87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
import re
|
|
import logging
|
|
from pathlib import Path
|
|
from typing import Any, Dict, List, Set
|
|
from tools.utils import ToolError, parse_indices
|
|
|
|
logger = logging.getLogger("MooseCP")
|
|
|
|
async def handle(args: Dict[str, Any]):
|
|
"""
|
|
Extracts specific sections of a file with absolute line numbers and surrounding context.
|
|
This tool MUST be called immediately before edit_file to verify the exact line numbers
|
|
and content of the block being replaced, preventing misalignment errors.
|
|
"""
|
|
path_str = args.get("path")
|
|
pattern = args.get("pattern")
|
|
lines_str = args.get("lines")
|
|
context = int(args.get("context", 1))
|
|
|
|
if not path_str:
|
|
raise ToolError("Missing path argument")
|
|
|
|
path = Path(path_str)
|
|
if not path.is_file():
|
|
raise ToolError(f"{path_str} is not a file.")
|
|
|
|
try:
|
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
|
all_lines = f.readlines()
|
|
except Exception as e:
|
|
raise ToolError(f"Error reading file {path_str}: {e}")
|
|
|
|
total_lines = len(all_lines)
|
|
|
|
# Case 1: No pattern and no lines provided -> Dump whole file
|
|
if not pattern and not lines_str:
|
|
output = [f"{i+1}: {line}" for i, line in enumerate(all_lines)]
|
|
return {"text": "".join(output)}
|
|
|
|
# Determine target line numbers (1-based)
|
|
target_lines: Set[int] = set()
|
|
|
|
if pattern:
|
|
try:
|
|
regex = re.compile(pattern)
|
|
for i, line in enumerate(all_lines):
|
|
if regex.search(line):
|
|
target_lines.add(i + 1)
|
|
except re.error as e:
|
|
raise ToolError(f"Invalid regex pattern: {e}")
|
|
|
|
if lines_str:
|
|
try:
|
|
indices = parse_indices(lines_str)
|
|
for idx in indices:
|
|
if 1 <= idx <= total_lines:
|
|
target_lines.add(idx)
|
|
except ToolError as e:
|
|
raise e
|
|
except Exception as e:
|
|
raise ToolError(f"Error parsing line indices: {e}")
|
|
|
|
if not target_lines:
|
|
return {"text": "No matching lines found."}
|
|
|
|
# Expand targets with context
|
|
lines_to_show: Set[int] = set()
|
|
for line in target_lines:
|
|
for offset in range(-context, context + 1):
|
|
ln = line + offset
|
|
if 1 <= ln <= total_lines:
|
|
lines_to_show.add(ln)
|
|
|
|
# Sort and format output
|
|
sorted_lines = sorted(list(lines_to_show))
|
|
output = []
|
|
last_line = None
|
|
|
|
for ln in sorted_lines:
|
|
if last_line is not None and ln > last_line + 1:
|
|
output.append("...\n")
|
|
|
|
output.append(f"{ln}: {all_lines[ln-1]}")
|
|
last_line = ln
|
|
|
|
return {"text": "".join(output)}
|