# Copyright 2025 The EasyDeL/Calute Author @erfanzar (Erfan Zare Chavoshi).
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Comprehensive coding tools for file management, git operations, and code manipulation.
This module provides a suite of tools for file system operations, git version control,
and code analysis. It includes functions for reading, writing, copying, and deleting files,
as well as git operations like status, diff, log, and patch management. Additionally,
it offers code structure analysis capabilities for multiple programming languages.
"""
import difflib
import re
import shutil
import subprocess
from pathlib import Path
[docs]def read_file(
file_path: str, start_line: int = 1, end_line: int | None = None, context_variables: dict | None = None
) -> str:
"""Read a file or specific lines from a file.
Reads the contents of a file and returns it with line numbers.
Supports reading a specific range of lines for large files.
Args:
file_path: Path to the file to read.
start_line: Starting line number (1-based indexing).
end_line: Ending line number (inclusive). None reads to end of file.
context_variables: Optional context dictionary for tool execution.
Returns:
The file content with line numbers, or an error message if the file
cannot be read.
Example:
>>> content = read_file("example.py", start_line=1, end_line=10)
>>> print(content)
1 | import os
2 | import sys
"""
try:
path = Path(file_path)
if not path.exists():
return f"Error: File not found: {file_path}"
with open(path, "r", encoding="utf-8") as f:
lines = f.readlines()
if start_line < 1:
start_line = 1
if end_line is None:
end_line = len(lines)
else:
end_line = min(end_line, len(lines))
selected_lines = lines[start_line - 1 : end_line]
result = []
for i, line in enumerate(selected_lines, start=start_line):
result.append(f"{i:6d} | {line.rstrip()}")
return "\n".join(result) if result else "No content in specified range"
except Exception as e:
return f"Error reading file: {e!s}"
[docs]def write_file(file_path: str, content: str, create_dirs: bool = True, context_variables: dict | None = None) -> str:
"""Write content to a file, creating directories if needed.
Writes the specified content to a file at the given path. Can optionally
create parent directories if they don't exist.
Args:
file_path: Path to the file to write.
content: Content to write to the file.
create_dirs: Whether to create parent directories if they don't exist.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message with the number of characters and lines written,
or an error message if the write fails.
Example:
>>> result = write_file("output.txt", "Hello, World!")
>>> print(result)
Successfully wrote 13 characters (1 lines) to output.txt
"""
try:
path = Path(file_path)
if create_dirs:
path.parent.mkdir(parents=True, exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
lines = content.count("\n") + 1
return f"Successfully wrote {len(content)} characters ({lines} lines) to {file_path}"
except Exception as e:
return f"Error writing file: {e!s}"
[docs]def list_directory(
directory: str = ".",
pattern: str = "*",
recursive: bool = False,
show_hidden: bool = False,
max_depth: int = 3,
context_variables: dict | None = None,
) -> str:
"""List files and directories with filtering options.
Lists the contents of a directory with support for glob pattern filtering,
recursive traversal, and hidden file visibility control.
Args:
directory: Directory path to list. Defaults to current directory.
pattern: Glob pattern for filtering files (e.g., '*.py', '*.txt').
recursive: Whether to list contents recursively.
show_hidden: Whether to include hidden files (starting with '.').
max_depth: Maximum depth for recursive listing.
context_variables: Optional context dictionary for tool execution.
Returns:
A formatted directory listing with file sizes, or an error message
if the directory cannot be accessed.
Example:
>>> listing = list_directory("src", pattern="*.py", recursive=True)
>>> print(listing)
Directory listing for: /path/to/src
Pattern: *.py | Recursive: True | Hidden: False
------------------------------------------------------------
📄 main.py (1.2KB)
📁 utils/
📄 utils/helpers.py (0.5KB)
"""
try:
path = Path(directory)
if not path.exists():
return f"Error: Directory not found: {directory}"
if not path.is_dir():
return f"Error: Not a directory: {directory}"
items = []
if recursive:
def list_recursive(p: Path, depth: int = 0, prefix: str = ""):
if depth > max_depth:
return
try:
for item in sorted(p.glob(pattern)):
if not show_hidden and item.name.startswith("."):
continue
rel_path = item.relative_to(path)
indent = " " * depth
if item.is_dir():
items.append(f"{indent}📁 {rel_path}/")
if depth < max_depth:
list_recursive(item, depth + 1, prefix + " ")
else:
size = item.stat().st_size
size_str = format_size(size)
items.append(f"{indent}📄 {rel_path} ({size_str})")
except PermissionError:
items.append(f"{indent}❌ Permission denied")
list_recursive(path)
else:
for item in sorted(path.glob(pattern)):
if not show_hidden and item.name.startswith("."):
continue
if item.is_dir():
items.append(f"📁 {item.name}/")
else:
size = item.stat().st_size
size_str = format_size(size)
items.append(f"📄 {item.name} ({size_str})")
if not items:
return f"No items matching pattern '{pattern}' in {directory}"
header = f"Directory listing for: {path.absolute()}\n"
header += f"Pattern: {pattern} | Recursive: {recursive} | Hidden: {show_hidden}\n"
header += "-" * 60 + "\n"
return header + "\n".join(items)
except Exception as e:
return f"Error listing directory: {e!s}"
def format_size(size: int) -> str:
"""Format file size in human-readable format.
Converts a byte count to a human-readable string with appropriate
units (B, KB, MB, GB, TB).
Args:
size: File size in bytes.
Returns:
A formatted string with the size and appropriate unit.
Example:
>>> format_size(1536)
'1.5KB'
>>> format_size(1048576)
'1.0MB'
"""
for unit in ["B", "KB", "MB", "GB"]:
if size < 1024.0:
return f"{size:.1f}{unit}"
size /= 1024.0
return f"{size:.1f}TB"
[docs]def copy_file(source: str, destination: str, overwrite: bool = False, context_variables: dict | None = None) -> str:
"""Copy a file or directory to a new location.
Copies a file or directory from the source path to the destination path.
Supports both file and directory copying with optional overwrite behavior.
Args:
source: Path to the source file or directory.
destination: Path to the destination location.
overwrite: Whether to overwrite existing files at the destination.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message confirming the copy, or an error message if the
operation fails.
Example:
>>> result = copy_file("config.yaml", "backup/config.yaml")
>>> print(result)
Successfully copied file config.yaml to backup/config.yaml
"""
try:
src_path = Path(source)
dst_path = Path(destination)
if not src_path.exists():
return f"Error: Source not found: {source}"
if dst_path.exists() and not overwrite:
return f"Error: Destination exists: {destination}. Use overwrite=True to replace."
if src_path.is_dir():
shutil.copytree(src_path, dst_path, dirs_exist_ok=overwrite)
return f"Successfully copied directory {source} to {destination}"
else:
dst_path.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(src_path, dst_path)
return f"Successfully copied file {source} to {destination}"
except Exception as e:
return f"Error copying: {e!s}"
[docs]def move_file(source: str, destination: str, overwrite: bool = False, context_variables: dict | None = None) -> str:
"""Move a file or directory to a new location.
Moves a file or directory from the source path to the destination path.
This effectively renames or relocates the item.
Args:
source: Path to the source file or directory.
destination: Path to the destination location.
overwrite: Whether to overwrite existing files at the destination.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message confirming the move, or an error message if the
operation fails.
Example:
>>> result = move_file("old_name.py", "new_name.py")
>>> print(result)
Successfully moved old_name.py to new_name.py
"""
try:
src_path = Path(source)
dst_path = Path(destination)
if not src_path.exists():
return f"Error: Source not found: {source}"
if dst_path.exists() and not overwrite:
return f"Error: Destination exists: {destination}. Use overwrite=True to replace."
dst_path.parent.mkdir(parents=True, exist_ok=True)
shutil.move(str(src_path), str(dst_path))
return f"Successfully moved {source} to {destination}"
except Exception as e:
return f"Error moving: {e!s}"
[docs]def delete_file(path: str, force: bool = False, context_variables: dict | None = None) -> str:
"""Delete a file or directory.
Removes a file or directory from the filesystem. For directories with
many items, the force flag must be set to confirm the deletion.
Args:
path: Path to the file or directory to delete.
force: Force deletion of directories with more than 10 items.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message confirming the deletion, or an error message if
the operation fails or requires confirmation.
Example:
>>> result = delete_file("temp.txt")
>>> print(result)
Successfully deleted file: temp.txt
"""
try:
file_path = Path(path)
if not file_path.exists():
return f"Error: Path not found: {path}"
if not force and file_path.is_dir():
item_count = sum(1 for _ in file_path.rglob("*"))
if item_count > 10:
return f"Error: Directory contains {item_count} items. Use force=True to delete."
if file_path.is_dir():
shutil.rmtree(file_path)
return f"Successfully deleted directory: {path}"
else:
file_path.unlink()
return f"Successfully deleted file: {path}"
except Exception as e:
return f"Error deleting: {e!s}"
[docs]def git_status(repo_path: str = ".", context_variables: dict | None = None) -> str:
"""Get the current status of a git repository.
Retrieves the git status including branch information and file
modifications (staged, unstaged, untracked, etc.).
Args:
repo_path: Path to the git repository. Defaults to current directory.
context_variables: Optional context dictionary for tool execution.
Returns:
A formatted status output showing branch and file states, or an
error message if the repository is not accessible.
Example:
>>> status = git_status(".")
>>> print(status)
Branch: main
Modified (unstaged): src/main.py
Untracked: new_file.txt
"""
try:
result = subprocess.run(
["git", "status", "--porcelain", "-b"], cwd=repo_path, capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
return f"Error: {result.stderr}"
lines = result.stdout.strip().split("\n")
if not lines or not lines[0]:
return "Working directory clean"
output = []
for line in lines:
if line.startswith("##"):
branch_info = line[3:]
output.append(f"Branch: {branch_info}")
elif line:
status = line[:2]
file_path = line[3:]
status_map = {
"M ": "Modified (staged)",
" M": "Modified (unstaged)",
"MM": "Modified (staged and unstaged)",
"A ": "Added",
"D ": "Deleted",
"R ": "Renamed",
"C ": "Copied",
"??": "Untracked",
"!!": "Ignored",
}
status_desc = status_map.get(status, status)
output.append(f" {status_desc}: {file_path}")
return "\n".join(output)
except subprocess.TimeoutExpired:
return "Error: Git command timed out"
except Exception as e:
return f"Error getting git status: {e!s}"
[docs]def git_diff(
repo_path: str = ".",
file_path: str | None = None,
staged: bool = False,
context_lines: int = 3,
context_variables: dict | None = None,
) -> str:
"""Get the git diff showing changes in the repository.
Retrieves a unified diff of changes in the working directory or
staging area. Can be filtered to a specific file.
Args:
repo_path: Path to the git repository. Defaults to current directory.
file_path: Specific file to show diff for. None shows all changes.
staged: Whether to show staged changes instead of unstaged.
context_lines: Number of context lines to include around changes.
context_variables: Optional context dictionary for tool execution.
Returns:
The unified diff output showing changes, or "No changes detected"
if there are no modifications.
Example:
>>> diff = git_diff(".", file_path="src/main.py", staged=True)
>>> print(diff)
--- a/src/main.py
+++ b/src/main.py
@@ -1,3 +1,4 @@
import os
+import sys
"""
try:
cmd = ["git", "diff", f"-U{context_lines}"]
if staged:
cmd.append("--staged")
if file_path:
cmd.append(file_path)
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
return f"Error: {result.stderr}"
if not result.stdout:
return "No changes detected"
return result.stdout
except subprocess.TimeoutExpired:
return "Error: Git diff timed out"
except Exception as e:
return f"Error getting git diff: {e!s}"
[docs]def git_apply_patch(
patch_content: str, repo_path: str = ".", check_only: bool = False, context_variables: dict | None = None
) -> str:
"""Apply a git patch to the repository.
Applies a unified diff patch to the repository. Can optionally check
if the patch applies cleanly without actually applying it.
Args:
patch_content: The unified diff patch content to apply.
repo_path: Path to the git repository. Defaults to current directory.
check_only: If True, only verify the patch applies without applying.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message if the patch is applied or validated, or an error
message if the patch cannot be applied.
Example:
>>> patch = '''--- a/file.txt
... +++ b/file.txt
... @@ -1 +1 @@
... -old line
... +new line'''
>>> result = git_apply_patch(patch, check_only=True)
>>> print(result)
Patch can be applied cleanly
"""
try:
cmd = ["git", "apply"]
if check_only:
cmd.append("--check")
result = subprocess.run(cmd, cwd=repo_path, input=patch_content, capture_output=True, text=True, timeout=30)
if result.returncode != 0:
return f"Error applying patch: {result.stderr}"
if check_only:
return "Patch can be applied cleanly"
else:
return "Patch applied successfully"
except subprocess.TimeoutExpired:
return "Error: Git apply timed out"
except Exception as e:
return f"Error applying patch: {e!s}"
[docs]def git_log(
repo_path: str = ".",
max_commits: int = 10,
oneline: bool = True,
file_path: str | None = None,
context_variables: dict | None = None,
) -> str:
"""Get the git commit history.
Retrieves the commit log for the repository. Can be filtered to show
only commits affecting a specific file.
Args:
repo_path: Path to the git repository. Defaults to current directory.
max_commits: Maximum number of commits to display.
oneline: Whether to use compact one-line format per commit.
file_path: Specific file to show commit history for.
context_variables: Optional context dictionary for tool execution.
Returns:
The commit log output, or an error message if the repository
is not accessible.
Example:
>>> log = git_log(".", max_commits=5)
>>> print(log)
abc1234 Add new feature
def5678 Fix bug in parser
ghi9012 Update documentation
"""
try:
cmd = ["git", "log", f"-{max_commits}"]
if oneline:
cmd.append("--oneline")
else:
cmd.append("--pretty=format:%h - %an, %ar : %s")
if file_path:
cmd.extend(["--", file_path])
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True, timeout=10)
if result.returncode != 0:
return f"Error: {result.stderr}"
if not result.stdout:
return "No commit history found"
return result.stdout
except subprocess.TimeoutExpired:
return "Error: Git log timed out"
except Exception as e:
return f"Error getting git log: {e!s}"
[docs]def git_add(files: list[str], repo_path: str = ".", context_variables: dict | None = None) -> str:
"""Stage files for commit.
Adds the specified files to the git staging area in preparation
for a commit.
Args:
files: List of file paths to stage for commit.
repo_path: Path to the git repository. Defaults to current directory.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message with the count of staged files, or an error
message if the operation fails.
Example:
>>> result = git_add(["src/main.py", "README.md"])
>>> print(result)
Successfully staged 2 file(s)
"""
try:
if not files:
return "Error: No files specified"
cmd = ["git", "add", *files]
result = subprocess.run(cmd, cwd=repo_path, capture_output=True, text=True, timeout=10)
if result.returncode != 0:
return f"Error: {result.stderr}"
return f"Successfully staged {len(files)} file(s)"
except subprocess.TimeoutExpired:
return "Error: Git add timed out"
except Exception as e:
return f"Error staging files: {e!s}"
[docs]def create_diff(original: str, modified: str, file_name: str = "file.txt", context_variables: dict | None = None) -> str:
"""Create a unified diff between two text contents.
Generates a unified diff format showing the differences between
the original and modified content.
Args:
original: The original text content.
modified: The modified text content.
file_name: Name to use in the diff header.
context_variables: Optional context dictionary for tool execution.
Returns:
A unified diff string showing the differences.
Example:
>>> original = "line1\\nline2\\nline3"
>>> modified = "line1\\nmodified\\nline3"
>>> diff = create_diff(original, modified, "example.txt")
>>> print(diff)
--- a/example.txt
+++ b/example.txt
@@ -1,3 +1,3 @@
line1
-line2
+modified
line3
"""
try:
original_lines = original.splitlines(keepends=True)
modified_lines = modified.splitlines(keepends=True)
diff = difflib.unified_diff(
original_lines, modified_lines, fromfile=f"a/{file_name}", tofile=f"b/{file_name}", n=3
)
return "".join(diff)
except Exception as e:
return f"Error creating diff: {e!s}"
[docs]def apply_diff(original: str, diff: str, context_variables: dict | None = None) -> str:
"""Apply a unified diff to original content.
Parses a unified diff and applies the changes to the original content
to produce the modified result.
Args:
original: The original text content to modify.
diff: The unified diff to apply.
context_variables: Optional context dictionary for tool execution.
Returns:
The modified content after applying the diff, or an error message
if the diff cannot be applied.
Example:
>>> original = "line1\\nline2\\nline3"
>>> diff = '''--- a/file.txt
... +++ b/file.txt
... @@ -1,3 +1,3 @@
... line1
... -line2
... +modified
... line3'''
>>> result = apply_diff(original, diff)
>>> print(result)
line1
modified
line3
"""
try:
lines = original.splitlines(keepends=True)
diff_lines = diff.splitlines()
result = []
current_line = 0
for diff_line in diff_lines:
if diff_line.startswith("+++") or diff_line.startswith("---"):
continue
elif diff_line.startswith("@@"):
match = re.match(r"@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@", diff_line)
if match:
old_start = int(match.group(1)) - 1
while current_line < old_start and current_line < len(lines):
result.append(lines[current_line])
current_line += 1
elif diff_line.startswith("+") and not diff_line.startswith("+++"):
result.append(diff_line[1:] + "\n")
elif diff_line.startswith("-") and not diff_line.startswith("---"):
current_line += 1
elif diff_line.startswith(" "):
if current_line < len(lines):
result.append(lines[current_line])
current_line += 1
while current_line < len(lines):
result.append(lines[current_line])
current_line += 1
output = "".join(result)
if output.endswith("\n") and not original.endswith("\n"):
output = output[:-1]
elif not output.endswith("\n") and original.endswith("\n"):
output += "\n"
return output.rstrip("\n") if not original.endswith("\n") else output
except Exception as e:
return f"Error applying diff: {e!s}"
[docs]def find_and_replace(
file_path: str,
search: str,
replace: str,
regex: bool = False,
case_sensitive: bool = True,
backup: bool = True,
context_variables: dict | None = None,
) -> str:
"""Find and replace text in a file.
Searches for occurrences of a pattern in a file and replaces them
with the specified replacement text. Supports both literal and
regex-based search patterns.
Args:
file_path: Path to the file to modify.
search: Text or regex pattern to search for.
replace: Replacement text.
regex: Whether to interpret search as a regular expression.
case_sensitive: Whether the search should be case-sensitive.
backup: Whether to create a backup file before modifying.
context_variables: Optional context dictionary for tool execution.
Returns:
A success message with the count of replacements made, or an
error message if the operation fails.
Example:
>>> result = find_and_replace("config.py", "DEBUG=True", "DEBUG=False")
>>> print(result)
Replaced 1 occurrence(s) in config.py (backup saved as config.py.bak)
"""
try:
path = Path(file_path)
if not path.exists():
return f"Error: File not found: {file_path}"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
if backup:
backup_path = path.with_suffix(path.suffix + ".bak")
shutil.copy2(path, backup_path)
if regex:
flags = 0 if case_sensitive else re.IGNORECASE
pattern = re.compile(search, flags)
new_content, count = pattern.subn(replace, content)
else:
if case_sensitive:
new_content = content.replace(search, replace)
count = content.count(search)
else:
pattern = re.compile(re.escape(search), re.IGNORECASE)
new_content, count = pattern.subn(replace, content)
if count > 0:
with open(path, "w", encoding="utf-8") as f:
f.write(new_content)
backup_msg = f" (backup saved as {backup_path.name})" if backup else ""
return f"Replaced {count} occurrence(s) in {file_path}{backup_msg}"
except Exception as e:
return f"Error in find and replace: {e!s}"
[docs]def analyze_code_structure(file_path: str, context_variables: dict | None = None) -> str:
"""Analyze the structure of a code file.
Performs static analysis on a source code file to extract structural
information including imports, classes, functions, and code metrics.
Args:
file_path: Path to the code file to analyze.
context_variables: Optional context dictionary for tool execution.
Returns:
A formatted analysis report showing the file's structure including
language detection, line counts, imports, classes, and functions,
or an error message if the file cannot be analyzed.
Example:
>>> analysis = analyze_code_structure("src/main.py")
>>> print(analysis)
Code Structure Analysis: main.py
Language: Python
Total Lines: 150
Blank Lines: 20
Comment Lines: 15
Code Lines: 115
Imports (5):
• import os
• from typing import Dict
Classes (2):
• MyClass
• AnotherClass
Functions (8):
• main
• helper_function
"""
try:
path = Path(file_path)
if not path.exists():
return f"Error: File not found: {file_path}"
with open(path, "r", encoding="utf-8") as f:
content = f.read()
lines = content.splitlines()
ext = path.suffix.lower()
language = detect_language(ext)
analysis = {
"file": file_path,
"language": language,
"lines": len(lines),
"characters": len(content),
"functions": [],
"classes": [],
"imports": [],
"comments": 0,
"blank_lines": 0,
}
if language == "Python":
analyze_python(lines, analysis)
elif language == "JavaScript":
analyze_javascript(lines, analysis)
elif language == "Java":
analyze_java(lines, analysis)
output = [
f"Code Structure Analysis: {path.name}",
f"Language: {language}",
f"Total Lines: {analysis['lines']}",
f"Blank Lines: {analysis['blank_lines']}",
f"Comment Lines: {analysis['comments']}",
f"Code Lines: {analysis['lines'] - analysis['blank_lines'] - analysis['comments']}",
]
if analysis["imports"]:
output.append(f"\nImports ({len(analysis['imports'])}):")
for imp in analysis["imports"][:10]:
output.append(f" • {imp}")
if analysis["classes"]:
output.append(f"\nClasses ({len(analysis['classes'])}):")
for cls in analysis["classes"]:
output.append(f" • {cls}")
if analysis["functions"]:
output.append(f"\nFunctions ({len(analysis['functions'])}):")
for func in analysis["functions"][:20]:
output.append(f" • {func}")
return "\n".join(output)
except Exception as e:
return f"Error analyzing code structure: {e!s}"
def detect_language(extension: str) -> str:
"""Detect programming language from file extension.
Maps a file extension to its corresponding programming language name.
Args:
extension: The file extension including the dot (e.g., '.py', '.js').
Returns:
The name of the programming language, or 'Unknown' if the extension
is not recognized.
Example:
>>> detect_language(".py")
'Python'
>>> detect_language(".js")
'JavaScript'
"""
lang_map = {
".py": "Python",
".js": "JavaScript",
".ts": "TypeScript",
".java": "Java",
".cpp": "C++",
".c": "C",
".cs": "C#",
".go": "Go",
".rs": "Rust",
".rb": "Ruby",
".php": "PHP",
".swift": "Swift",
".kt": "Kotlin",
".scala": "Scala",
".r": "R",
".m": "MATLAB",
".jl": "Julia",
".sh": "Shell",
".bash": "Bash",
".sql": "SQL",
".html": "HTML",
".css": "CSS",
".scss": "SCSS",
".yaml": "YAML",
".yml": "YAML",
".json": "JSON",
".xml": "XML",
".md": "Markdown",
}
return lang_map.get(extension, "Unknown")
def analyze_python(lines: list[str], analysis: dict) -> None:
"""Analyze Python code structure.
Parses Python source code lines to extract structural information
including imports, class definitions, function definitions, and
comment/blank line counts.
Args:
lines: List of source code lines to analyze.
analysis: Dictionary to populate with analysis results. Modified in-place
with keys: 'imports', 'classes', 'functions', 'comments', 'blank_lines'.
"""
for _i, line in enumerate(lines):
stripped = line.strip()
if not stripped:
analysis["blank_lines"] += 1
elif stripped.startswith("#"):
analysis["comments"] += 1
elif stripped.startswith('"""') or stripped.startswith("'''"):
analysis["comments"] += 1
elif stripped.startswith("import ") or stripped.startswith("from "):
analysis["imports"].append(stripped)
elif stripped.startswith("def "):
match = re.match(r"def\s+(\w+)", stripped)
if match:
analysis["functions"].append(match.group(1))
elif stripped.startswith("class "):
match = re.match(r"class\s+(\w+)", stripped)
if match:
analysis["classes"].append(match.group(1))
def analyze_javascript(lines: list[str], analysis: dict) -> None:
"""Analyze JavaScript code structure.
Parses JavaScript source code lines to extract structural information
including imports/requires, class definitions, function definitions,
and comment/blank line counts.
Args:
lines: List of source code lines to analyze.
analysis: Dictionary to populate with analysis results. Modified in-place
with keys: 'imports', 'classes', 'functions', 'comments', 'blank_lines'.
"""
for line in lines:
stripped = line.strip()
if not stripped:
analysis["blank_lines"] += 1
elif stripped.startswith("//"):
analysis["comments"] += 1
elif stripped.startswith("/*"):
analysis["comments"] += 1
elif "import " in stripped or "require(" in stripped:
analysis["imports"].append(stripped)
elif "function " in stripped:
match = re.search(r"function\s+(\w+)", stripped)
if match:
analysis["functions"].append(match.group(1))
elif "class " in stripped:
match = re.match(r"class\s+(\w+)", stripped)
if match:
analysis["classes"].append(match.group(1))
elif " = function" in stripped or "= () =>" in stripped:
match = re.search(r"(\w+)\s*=\s*function|\(\w+\)\s*=>", stripped)
if match:
analysis["functions"].append(match.group(1) if match.group(1) else "anonymous")
def analyze_java(lines: list[str], analysis: dict) -> None:
"""Analyze Java code structure.
Parses Java source code lines to extract structural information
including imports, class definitions, method definitions, and
comment/blank line counts.
Args:
lines: List of source code lines to analyze.
analysis: Dictionary to populate with analysis results. Modified in-place
with keys: 'imports', 'classes', 'functions', 'comments', 'blank_lines'.
"""
for line in lines:
stripped = line.strip()
if not stripped:
analysis["blank_lines"] += 1
elif stripped.startswith("//"):
analysis["comments"] += 1
elif stripped.startswith("/*"):
analysis["comments"] += 1
elif stripped.startswith("import "):
analysis["imports"].append(stripped)
elif "class " in stripped:
match = re.search(r"class\s+(\w+)", stripped)
if match:
analysis["classes"].append(match.group(1))
elif re.search(r"(public|private|protected).*\s+\w+\s*\(", stripped):
match = re.search(r"(\w+)\s*\(", stripped)
if match and match.group(1) not in ["if", "while", "for", "switch", "catch"]:
analysis["functions"].append(match.group(1))
__all__ = [
"analyze_code_structure",
"apply_diff",
"copy_file",
"create_diff",
"delete_file",
"find_and_replace",
"git_add",
"git_apply_patch",
"git_diff",
"git_log",
"git_status",
"list_directory",
"move_file",
"read_file",
"write_file",
]