feat: add file_diff orchestrator tool
Runs diff -u on two project-scoped files. Low risk, no admin required. Covers code review, config comparison, and before/after verification. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -339,6 +339,45 @@ def _sync_file_grep(path_str: str, pattern: str, context_lines: int, recursive:
|
||||
return header + "\n\n" + "\n\n".join(sections)
|
||||
|
||||
|
||||
async def file_diff(path_a: str, path_b: str) -> str:
|
||||
"""Compare two files and return a unified diff."""
|
||||
return await asyncio.to_thread(_sync_file_diff, path_a, path_b)
|
||||
|
||||
|
||||
def _sync_file_diff(path_a: str, path_b: str) -> str:
|
||||
try:
|
||||
resolved_a = Path(path_a).expanduser().resolve()
|
||||
resolved_b = Path(path_b).expanduser().resolve()
|
||||
except Exception as e:
|
||||
return f"Invalid path: {e}"
|
||||
|
||||
for resolved in (resolved_a, resolved_b):
|
||||
if not _is_project_allowed(resolved):
|
||||
return f"Access denied: {resolved}"
|
||||
if not resolved.exists():
|
||||
return f"File not found: {resolved}"
|
||||
if not resolved.is_file():
|
||||
return f"Not a file: {resolved}"
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["diff", "-u", str(resolved_a), str(resolved_b)],
|
||||
capture_output=True, text=True, timeout=15,
|
||||
)
|
||||
if result.returncode == 0:
|
||||
return f"Files are identical: {resolved_a.name} vs {resolved_b.name}"
|
||||
output = result.stdout
|
||||
if not output:
|
||||
return f"diff returned no output (exit {result.returncode}): {result.stderr}"
|
||||
if len(output) > _MAX_BYTES:
|
||||
output = output[:_MAX_BYTES] + "\n… [truncated]"
|
||||
return output
|
||||
except subprocess.TimeoutExpired:
|
||||
return "Timeout running diff"
|
||||
except Exception as e:
|
||||
return f"Error: {e}"
|
||||
|
||||
|
||||
async def file_syntax_check(path: str) -> str:
|
||||
"""Check syntax of a Python (.py) or JSON (.json) file."""
|
||||
return await asyncio.to_thread(_sync_file_syntax_check, path)
|
||||
@@ -604,6 +643,30 @@ DECLARATIONS = [
|
||||
required=["path", "pattern"],
|
||||
),
|
||||
),
|
||||
types.FunctionDeclaration(
|
||||
name="file_diff",
|
||||
description=(
|
||||
"Compare two files and return a unified diff (diff -u). "
|
||||
"Use for code review, verifying what changed between two versions of a file, "
|
||||
"or comparing config files side-by-side. "
|
||||
"Returns 'Files are identical' if there are no differences. "
|
||||
"Restricted to the Cortex project directory."
|
||||
),
|
||||
parameters=types.Schema(
|
||||
type=types.Type.OBJECT,
|
||||
properties={
|
||||
"path_a": types.Schema(
|
||||
type=types.Type.STRING,
|
||||
description="Path to the first file (the 'before' or reference file)",
|
||||
),
|
||||
"path_b": types.Schema(
|
||||
type=types.Type.STRING,
|
||||
description="Path to the second file (the 'after' or comparison file)",
|
||||
),
|
||||
},
|
||||
required=["path_a", "path_b"],
|
||||
),
|
||||
),
|
||||
types.FunctionDeclaration(
|
||||
name="file_syntax_check",
|
||||
description=(
|
||||
|
||||
Reference in New Issue
Block a user