hanzo-mcp 0.8.8__py3-none-any.whl → 0.8.13__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hanzo-mcp might be problematic. Click here for more details.
- hanzo_mcp/__init__.py +1 -3
- hanzo_mcp/analytics/posthog_analytics.py +4 -17
- hanzo_mcp/bridge.py +9 -25
- hanzo_mcp/cli.py +8 -17
- hanzo_mcp/cli_enhanced.py +5 -14
- hanzo_mcp/cli_plugin.py +3 -9
- hanzo_mcp/config/settings.py +6 -20
- hanzo_mcp/config/tool_config.py +2 -4
- hanzo_mcp/core/base_agent.py +88 -88
- hanzo_mcp/core/model_registry.py +238 -210
- hanzo_mcp/dev_server.py +5 -15
- hanzo_mcp/prompts/__init__.py +2 -6
- hanzo_mcp/prompts/project_todo_reminder.py +3 -9
- hanzo_mcp/prompts/tool_explorer.py +1 -3
- hanzo_mcp/prompts/utils.py +7 -21
- hanzo_mcp/server.py +6 -7
- hanzo_mcp/tools/__init__.py +13 -29
- hanzo_mcp/tools/agent/__init__.py +2 -1
- hanzo_mcp/tools/agent/agent.py +10 -30
- hanzo_mcp/tools/agent/agent_tool.py +6 -17
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +15 -42
- hanzo_mcp/tools/agent/claude_desktop_auth.py +3 -9
- hanzo_mcp/tools/agent/cli_agent_base.py +7 -24
- hanzo_mcp/tools/agent/cli_tools.py +76 -75
- hanzo_mcp/tools/agent/code_auth.py +1 -3
- hanzo_mcp/tools/agent/code_auth_tool.py +2 -6
- hanzo_mcp/tools/agent/critic_tool.py +8 -24
- hanzo_mcp/tools/agent/iching_tool.py +12 -36
- hanzo_mcp/tools/agent/network_tool.py +7 -18
- hanzo_mcp/tools/agent/prompt.py +1 -5
- hanzo_mcp/tools/agent/review_tool.py +10 -25
- hanzo_mcp/tools/agent/swarm_alias.py +1 -3
- hanzo_mcp/tools/agent/swarm_tool.py +16 -41
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +11 -39
- hanzo_mcp/tools/agent/unified_cli_tools.py +38 -38
- hanzo_mcp/tools/common/batch_tool.py +15 -45
- hanzo_mcp/tools/common/config_tool.py +9 -28
- hanzo_mcp/tools/common/context.py +1 -3
- hanzo_mcp/tools/common/critic_tool.py +1 -3
- hanzo_mcp/tools/common/decorators.py +2 -6
- hanzo_mcp/tools/common/enhanced_base.py +2 -6
- hanzo_mcp/tools/common/fastmcp_pagination.py +4 -12
- hanzo_mcp/tools/common/forgiving_edit.py +9 -28
- hanzo_mcp/tools/common/mode.py +1 -5
- hanzo_mcp/tools/common/paginated_base.py +3 -11
- hanzo_mcp/tools/common/paginated_response.py +10 -30
- hanzo_mcp/tools/common/pagination.py +3 -9
- hanzo_mcp/tools/common/permissions.py +3 -9
- hanzo_mcp/tools/common/personality.py +9 -34
- hanzo_mcp/tools/common/plugin_loader.py +3 -15
- hanzo_mcp/tools/common/stats.py +7 -19
- hanzo_mcp/tools/common/thinking_tool.py +1 -3
- hanzo_mcp/tools/common/tool_disable.py +2 -6
- hanzo_mcp/tools/common/tool_list.py +2 -6
- hanzo_mcp/tools/common/validation.py +1 -3
- hanzo_mcp/tools/config/config_tool.py +7 -13
- hanzo_mcp/tools/config/index_config.py +1 -3
- hanzo_mcp/tools/config/mode_tool.py +5 -15
- hanzo_mcp/tools/database/database_manager.py +3 -9
- hanzo_mcp/tools/database/graph.py +1 -3
- hanzo_mcp/tools/database/graph_add.py +3 -9
- hanzo_mcp/tools/database/graph_query.py +11 -34
- hanzo_mcp/tools/database/graph_remove.py +3 -9
- hanzo_mcp/tools/database/graph_search.py +6 -20
- hanzo_mcp/tools/database/graph_stats.py +11 -33
- hanzo_mcp/tools/database/sql.py +4 -12
- hanzo_mcp/tools/database/sql_query.py +6 -10
- hanzo_mcp/tools/database/sql_search.py +2 -6
- hanzo_mcp/tools/database/sql_stats.py +5 -15
- hanzo_mcp/tools/editor/neovim_command.py +1 -3
- hanzo_mcp/tools/editor/neovim_session.py +7 -13
- hanzo_mcp/tools/filesystem/__init__.py +2 -3
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
- hanzo_mcp/tools/filesystem/base.py +4 -12
- hanzo_mcp/tools/filesystem/batch_search.py +35 -115
- hanzo_mcp/tools/filesystem/content_replace.py +4 -12
- hanzo_mcp/tools/filesystem/diff.py +2 -10
- hanzo_mcp/tools/filesystem/directory_tree.py +9 -27
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +5 -15
- hanzo_mcp/tools/filesystem/edit.py +6 -18
- hanzo_mcp/tools/filesystem/find.py +3 -9
- hanzo_mcp/tools/filesystem/find_files.py +2 -6
- hanzo_mcp/tools/filesystem/git_search.py +9 -24
- hanzo_mcp/tools/filesystem/grep.py +9 -27
- hanzo_mcp/tools/filesystem/multi_edit.py +6 -18
- hanzo_mcp/tools/filesystem/read.py +8 -26
- hanzo_mcp/tools/filesystem/rules_tool.py +6 -17
- hanzo_mcp/tools/filesystem/search_tool.py +18 -62
- hanzo_mcp/tools/filesystem/symbols_tool.py +5 -15
- hanzo_mcp/tools/filesystem/tree.py +1 -3
- hanzo_mcp/tools/filesystem/watch.py +1 -3
- hanzo_mcp/tools/filesystem/write.py +1 -3
- hanzo_mcp/tools/jupyter/base.py +6 -20
- hanzo_mcp/tools/jupyter/jupyter.py +4 -12
- hanzo_mcp/tools/jupyter/notebook_edit.py +11 -35
- hanzo_mcp/tools/jupyter/notebook_read.py +2 -6
- hanzo_mcp/tools/llm/consensus_tool.py +8 -24
- hanzo_mcp/tools/llm/llm_manage.py +2 -6
- hanzo_mcp/tools/llm/llm_tool.py +17 -58
- hanzo_mcp/tools/llm/llm_unified.py +18 -59
- hanzo_mcp/tools/llm/provider_tools.py +1 -3
- hanzo_mcp/tools/lsp/lsp_tool.py +5 -17
- hanzo_mcp/tools/mcp/mcp_add.py +3 -5
- hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
- hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
- hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
- hanzo_mcp/tools/memory/__init__.py +33 -40
- hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
- hanzo_mcp/tools/memory/memory_tools.py +7 -19
- hanzo_mcp/tools/search/find_tool.py +10 -32
- hanzo_mcp/tools/search/unified_search.py +27 -81
- hanzo_mcp/tools/shell/__init__.py +2 -2
- hanzo_mcp/tools/shell/auto_background.py +2 -6
- hanzo_mcp/tools/shell/base.py +1 -5
- hanzo_mcp/tools/shell/base_process.py +5 -7
- hanzo_mcp/tools/shell/bash_session.py +7 -24
- hanzo_mcp/tools/shell/bash_session_executor.py +5 -15
- hanzo_mcp/tools/shell/bash_tool.py +3 -7
- hanzo_mcp/tools/shell/command_executor.py +26 -79
- hanzo_mcp/tools/shell/logs.py +4 -16
- hanzo_mcp/tools/shell/npx.py +2 -8
- hanzo_mcp/tools/shell/npx_tool.py +1 -3
- hanzo_mcp/tools/shell/pkill.py +4 -12
- hanzo_mcp/tools/shell/process_tool.py +2 -8
- hanzo_mcp/tools/shell/processes.py +5 -17
- hanzo_mcp/tools/shell/run_background.py +1 -3
- hanzo_mcp/tools/shell/run_command.py +1 -3
- hanzo_mcp/tools/shell/run_command_windows.py +1 -3
- hanzo_mcp/tools/shell/session_manager.py +2 -6
- hanzo_mcp/tools/shell/session_storage.py +2 -6
- hanzo_mcp/tools/shell/streaming_command.py +7 -23
- hanzo_mcp/tools/shell/uvx.py +4 -14
- hanzo_mcp/tools/shell/uvx_background.py +2 -6
- hanzo_mcp/tools/shell/uvx_tool.py +1 -3
- hanzo_mcp/tools/shell/zsh_tool.py +12 -20
- hanzo_mcp/tools/todo/todo.py +1 -3
- hanzo_mcp/tools/todo/todo_read.py +3 -9
- hanzo_mcp/tools/todo/todo_write.py +6 -18
- hanzo_mcp/tools/vector/__init__.py +3 -9
- hanzo_mcp/tools/vector/ast_analyzer.py +6 -20
- hanzo_mcp/tools/vector/git_ingester.py +10 -30
- hanzo_mcp/tools/vector/index_tool.py +3 -9
- hanzo_mcp/tools/vector/infinity_store.py +11 -30
- hanzo_mcp/tools/vector/mock_infinity.py +159 -0
- hanzo_mcp/tools/vector/project_manager.py +4 -12
- hanzo_mcp/tools/vector/vector.py +2 -6
- hanzo_mcp/tools/vector/vector_index.py +8 -8
- hanzo_mcp/tools/vector/vector_search.py +7 -21
- {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/METADATA +2 -2
- hanzo_mcp-0.8.13.dist-info/RECORD +193 -0
- hanzo_mcp-0.8.8.dist-info/RECORD +0 -192
- {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/top_level.txt +0 -0
|
@@ -21,9 +21,7 @@ class FilesystemBaseTool(FileSystemTool, ABC):
|
|
|
21
21
|
the base functionality in FileSystemTool.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
async def check_path_allowed(
|
|
25
|
-
self, path: str, tool_ctx: Any, error_prefix: str = "Error"
|
|
26
|
-
) -> tuple[bool, str]:
|
|
24
|
+
async def check_path_allowed(self, path: str, tool_ctx: Any, error_prefix: str = "Error") -> tuple[bool, str]:
|
|
27
25
|
"""Check if a path is allowed and log an error if not.
|
|
28
26
|
|
|
29
27
|
Args:
|
|
@@ -40,9 +38,7 @@ class FilesystemBaseTool(FileSystemTool, ABC):
|
|
|
40
38
|
return False, f"{error_prefix}: {message}"
|
|
41
39
|
return True, ""
|
|
42
40
|
|
|
43
|
-
async def check_path_exists(
|
|
44
|
-
self, path: str, tool_ctx: Any, error_prefix: str = "Error"
|
|
45
|
-
) -> tuple[bool, str]:
|
|
41
|
+
async def check_path_exists(self, path: str, tool_ctx: Any, error_prefix: str = "Error") -> tuple[bool, str]:
|
|
46
42
|
"""Check if a path exists and log an error if not.
|
|
47
43
|
|
|
48
44
|
Args:
|
|
@@ -60,9 +56,7 @@ class FilesystemBaseTool(FileSystemTool, ABC):
|
|
|
60
56
|
return False, f"{error_prefix}: {message}"
|
|
61
57
|
return True, ""
|
|
62
58
|
|
|
63
|
-
async def check_is_file(
|
|
64
|
-
self, path: str, tool_ctx: Any, error_prefix: str = "Error"
|
|
65
|
-
) -> tuple[bool, str]:
|
|
59
|
+
async def check_is_file(self, path: str, tool_ctx: Any, error_prefix: str = "Error") -> tuple[bool, str]:
|
|
66
60
|
"""Check if a path is a file and log an error if not.
|
|
67
61
|
|
|
68
62
|
Args:
|
|
@@ -80,9 +74,7 @@ class FilesystemBaseTool(FileSystemTool, ABC):
|
|
|
80
74
|
return False, f"{error_prefix}: {message}"
|
|
81
75
|
return True, ""
|
|
82
76
|
|
|
83
|
-
async def check_is_directory(
|
|
84
|
-
self, path: str, tool_ctx: Any, error_prefix: str = "Error"
|
|
85
|
-
) -> tuple[bool, str]:
|
|
77
|
+
async def check_is_directory(self, path: str, tool_ctx: Any, error_prefix: str = "Error") -> tuple[bool, str]:
|
|
86
78
|
"""Check if a path is a directory and log an error if not.
|
|
87
79
|
|
|
88
80
|
Args:
|
|
@@ -80,10 +80,7 @@ class BatchSearchResults:
|
|
|
80
80
|
return {
|
|
81
81
|
"query": self.query,
|
|
82
82
|
"total_results": self.total_results,
|
|
83
|
-
"results_by_type": {
|
|
84
|
-
k.value: [r.to_dict() for r in v]
|
|
85
|
-
for k, v in self.results_by_type.items()
|
|
86
|
-
},
|
|
83
|
+
"results_by_type": {k.value: [r.to_dict() for r in v] for k, v in self.results_by_type.items()},
|
|
87
84
|
"combined_results": [r.to_dict() for r in self.combined_results],
|
|
88
85
|
"search_time_ms": self.search_time_ms,
|
|
89
86
|
}
|
|
@@ -96,12 +93,8 @@ Queries = Annotated[
|
|
|
96
93
|
SearchPath = Annotated[str, Field(description="Path to search in", default=".")]
|
|
97
94
|
Include = Annotated[str, Field(description="File pattern to include", default="*")]
|
|
98
95
|
MaxResults = Annotated[int, Field(description="Maximum results per query", default=20)]
|
|
99
|
-
IncludeContext = Annotated[
|
|
100
|
-
|
|
101
|
-
]
|
|
102
|
-
CombineResults = Annotated[
|
|
103
|
-
bool, Field(description="Combine and deduplicate results", default=True)
|
|
104
|
-
]
|
|
96
|
+
IncludeContext = Annotated[bool, Field(description="Include function/method context", default=True)]
|
|
97
|
+
CombineResults = Annotated[bool, Field(description="Combine and deduplicate results", default=True)]
|
|
105
98
|
|
|
106
99
|
|
|
107
100
|
class BatchSearchParams(TypedDict):
|
|
@@ -212,9 +205,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
212
205
|
|
|
213
206
|
# If pattern contains natural language, prioritize vector search
|
|
214
207
|
words = pattern.split()
|
|
215
|
-
if len(words) > 2 and not any(
|
|
216
|
-
c in pattern for c in ["(", ")", "{", "}", "[", "]"]
|
|
217
|
-
):
|
|
208
|
+
if len(words) > 2 and not any(c in pattern for c in ["(", ")", "{", "}", "[", "]"]):
|
|
218
209
|
use_vector = True
|
|
219
210
|
|
|
220
211
|
return use_vector, use_ast, use_symbol
|
|
@@ -227,9 +218,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
227
218
|
|
|
228
219
|
try:
|
|
229
220
|
# Use the existing grep tool
|
|
230
|
-
grep_result = await self.grep_tool.call(
|
|
231
|
-
tool_ctx.mcp_context, pattern=pattern, path=path, include=include
|
|
232
|
-
)
|
|
221
|
+
grep_result = await self.grep_tool.call(tool_ctx.mcp_context, pattern=pattern, path=path, include=include)
|
|
233
222
|
|
|
234
223
|
results = []
|
|
235
224
|
if "Found" in grep_result and "matches" in grep_result:
|
|
@@ -265,9 +254,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
265
254
|
await tool_ctx.error(f"Grep search failed: {str(e)}")
|
|
266
255
|
return []
|
|
267
256
|
|
|
268
|
-
async def _run_vector_search(
|
|
269
|
-
self, pattern: str, path: str, tool_ctx, max_results: int
|
|
270
|
-
) -> List[SearchResult]:
|
|
257
|
+
async def _run_vector_search(self, pattern: str, path: str, tool_ctx, max_results: int) -> List[SearchResult]:
|
|
271
258
|
"""Run vector search and convert results."""
|
|
272
259
|
if not self.vector_tool:
|
|
273
260
|
return []
|
|
@@ -373,11 +360,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
373
360
|
content=content,
|
|
374
361
|
search_type=SearchType.AST,
|
|
375
362
|
score=0.9, # High score for AST matches
|
|
376
|
-
context=(
|
|
377
|
-
"\n".join(context_lines)
|
|
378
|
-
if context_lines
|
|
379
|
-
else None
|
|
380
|
-
),
|
|
363
|
+
context=("\n".join(context_lines) if context_lines else None),
|
|
381
364
|
)
|
|
382
365
|
results.append(result)
|
|
383
366
|
|
|
@@ -396,9 +379,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
396
379
|
await tool_ctx.error(f"AST search failed: {str(e)}")
|
|
397
380
|
return []
|
|
398
381
|
|
|
399
|
-
async def _run_symbol_search(
|
|
400
|
-
self, pattern: str, path: str, tool_ctx, max_results: int
|
|
401
|
-
) -> List[SearchResult]:
|
|
382
|
+
async def _run_symbol_search(self, pattern: str, path: str, tool_ctx, max_results: int) -> List[SearchResult]:
|
|
402
383
|
"""Run symbol search using AST analysis."""
|
|
403
384
|
await tool_ctx.info(f"Running symbol search for: {pattern}")
|
|
404
385
|
|
|
@@ -414,9 +395,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
414
395
|
# Look for source files
|
|
415
396
|
for ext in [".py", ".js", ".ts", ".java", ".cpp", ".c"]:
|
|
416
397
|
files_to_check.extend(path_obj.rglob(f"*{ext}"))
|
|
417
|
-
files_to_check = [
|
|
418
|
-
str(f) for f in files_to_check[:50]
|
|
419
|
-
] # Limit for performance
|
|
398
|
+
files_to_check = [str(f) for f in files_to_check[:50]] # Limit for performance
|
|
420
399
|
|
|
421
400
|
# Analyze files for symbols
|
|
422
401
|
for file_path in files_to_check:
|
|
@@ -439,11 +418,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
439
418
|
file_path=symbol.file_path,
|
|
440
419
|
line_number=symbol.line_start,
|
|
441
420
|
content=f"{symbol.type} {symbol.name}"
|
|
442
|
-
+ (
|
|
443
|
-
f" - {symbol.docstring[:100]}..."
|
|
444
|
-
if symbol.docstring
|
|
445
|
-
else ""
|
|
446
|
-
),
|
|
421
|
+
+ (f" - {symbol.docstring[:100]}..." if symbol.docstring else ""),
|
|
447
422
|
search_type=SearchType.SYMBOL,
|
|
448
423
|
score=0.95, # Very high score for symbol matches
|
|
449
424
|
symbol_info=symbol,
|
|
@@ -464,9 +439,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
464
439
|
await tool_ctx.error(f"Symbol search failed: {str(e)}")
|
|
465
440
|
return []
|
|
466
441
|
|
|
467
|
-
async def _add_function_context(
|
|
468
|
-
self, results: List[SearchResult], tool_ctx
|
|
469
|
-
) -> List[SearchResult]:
|
|
442
|
+
async def _add_function_context(self, results: List[SearchResult], tool_ctx) -> List[SearchResult]:
|
|
470
443
|
"""Add function/method context to results where relevant."""
|
|
471
444
|
enhanced_results = []
|
|
472
445
|
|
|
@@ -488,16 +461,10 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
488
461
|
if file_ast:
|
|
489
462
|
# Find symbol containing this line
|
|
490
463
|
for symbol in file_ast.symbols:
|
|
491
|
-
if
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
and symbol.type
|
|
496
|
-
in [
|
|
497
|
-
"function",
|
|
498
|
-
"method",
|
|
499
|
-
]
|
|
500
|
-
):
|
|
464
|
+
if symbol.line_start <= result.line_number <= symbol.line_end and symbol.type in [
|
|
465
|
+
"function",
|
|
466
|
+
"method",
|
|
467
|
+
]:
|
|
501
468
|
enhanced_result = SearchResult(
|
|
502
469
|
file_path=result.file_path,
|
|
503
470
|
line_number=result.line_number,
|
|
@@ -510,17 +477,13 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
510
477
|
)
|
|
511
478
|
break
|
|
512
479
|
except Exception as e:
|
|
513
|
-
await tool_ctx.warning(
|
|
514
|
-
f"Could not add context for {result.file_path}: {str(e)}"
|
|
515
|
-
)
|
|
480
|
+
await tool_ctx.warning(f"Could not add context for {result.file_path}: {str(e)}")
|
|
516
481
|
|
|
517
482
|
enhanced_results.append(enhanced_result)
|
|
518
483
|
|
|
519
484
|
return enhanced_results
|
|
520
485
|
|
|
521
|
-
def _combine_and_rank_results(
|
|
522
|
-
self, results_by_type: Dict[SearchType, List[SearchResult]]
|
|
523
|
-
) -> List[SearchResult]:
|
|
486
|
+
def _combine_and_rank_results(self, results_by_type: Dict[SearchType, List[SearchResult]]) -> List[SearchResult]:
|
|
524
487
|
"""Combine results from different search types and rank by relevance."""
|
|
525
488
|
all_results = []
|
|
526
489
|
seen_combinations = set()
|
|
@@ -552,8 +515,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
552
515
|
|
|
553
516
|
# Replace existing if: higher priority type, or same priority but higher score
|
|
554
517
|
if result_priority > existing_priority or (
|
|
555
|
-
result_priority == existing_priority
|
|
556
|
-
and result.score > existing.score
|
|
518
|
+
result_priority == existing_priority and result.score > existing.score
|
|
557
519
|
):
|
|
558
520
|
# Replace the entire result to preserve type
|
|
559
521
|
idx = all_results.index(existing)
|
|
@@ -561,9 +523,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
561
523
|
else:
|
|
562
524
|
# Still merge useful information
|
|
563
525
|
existing.context = existing.context or result.context
|
|
564
|
-
existing.symbol_info =
|
|
565
|
-
existing.symbol_info or result.symbol_info
|
|
566
|
-
)
|
|
526
|
+
existing.symbol_info = existing.symbol_info or result.symbol_info
|
|
567
527
|
break
|
|
568
528
|
|
|
569
529
|
# Sort by score (descending) then by search type priority
|
|
@@ -574,9 +534,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
574
534
|
SearchType.VECTOR: 1,
|
|
575
535
|
}
|
|
576
536
|
|
|
577
|
-
all_results.sort(
|
|
578
|
-
key=lambda r: (r.score, type_priority[r.search_type]), reverse=True
|
|
579
|
-
)
|
|
537
|
+
all_results.sort(key=lambda r: (r.score, type_priority[r.search_type]), reverse=True)
|
|
580
538
|
|
|
581
539
|
return all_results
|
|
582
540
|
|
|
@@ -612,9 +570,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
612
570
|
if not exists:
|
|
613
571
|
return error_msg
|
|
614
572
|
|
|
615
|
-
await tool_ctx.info(
|
|
616
|
-
f"Starting batch search with {len(queries)} queries in {path}"
|
|
617
|
-
)
|
|
573
|
+
await tool_ctx.info(f"Starting batch search with {len(queries)} queries in {path}")
|
|
618
574
|
|
|
619
575
|
# Run all queries in parallel
|
|
620
576
|
search_tasks = []
|
|
@@ -627,44 +583,26 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
627
583
|
if query_type == "grep":
|
|
628
584
|
pattern = query.get("pattern")
|
|
629
585
|
if pattern:
|
|
630
|
-
search_tasks.append(
|
|
631
|
-
self._run_grep_search(
|
|
632
|
-
pattern, path, include, tool_ctx, max_results
|
|
633
|
-
)
|
|
634
|
-
)
|
|
586
|
+
search_tasks.append(self._run_grep_search(pattern, path, include, tool_ctx, max_results))
|
|
635
587
|
|
|
636
588
|
elif query_type == "grep_ast":
|
|
637
589
|
pattern = query.get("pattern")
|
|
638
590
|
if pattern:
|
|
639
|
-
search_tasks.append(
|
|
640
|
-
self._run_ast_search(
|
|
641
|
-
pattern, path, include, tool_ctx, max_results
|
|
642
|
-
)
|
|
643
|
-
)
|
|
591
|
+
search_tasks.append(self._run_ast_search(pattern, path, include, tool_ctx, max_results))
|
|
644
592
|
|
|
645
593
|
elif query_type == "vector_search" and self.vector_tool:
|
|
646
594
|
search_query = query.get("query") or query.get("pattern")
|
|
647
595
|
if search_query:
|
|
648
|
-
search_tasks.append(
|
|
649
|
-
self._run_vector_search(
|
|
650
|
-
search_query, path, tool_ctx, max_results
|
|
651
|
-
)
|
|
652
|
-
)
|
|
596
|
+
search_tasks.append(self._run_vector_search(search_query, path, tool_ctx, max_results))
|
|
653
597
|
|
|
654
598
|
elif query_type == "git_search":
|
|
655
599
|
pattern = query.get("pattern")
|
|
656
600
|
search_type = query.get("search_type", "content")
|
|
657
601
|
if pattern:
|
|
658
|
-
search_tasks.append(
|
|
659
|
-
self._run_git_search(
|
|
660
|
-
pattern, path, search_type, tool_ctx, max_results
|
|
661
|
-
)
|
|
662
|
-
)
|
|
602
|
+
search_tasks.append(self._run_git_search(pattern, path, search_type, tool_ctx, max_results))
|
|
663
603
|
|
|
664
604
|
else:
|
|
665
|
-
await tool_ctx.warning(
|
|
666
|
-
f"Unknown or unavailable search type: {query_type}"
|
|
667
|
-
)
|
|
605
|
+
await tool_ctx.warning(f"Unknown or unavailable search type: {query_type}")
|
|
668
606
|
|
|
669
607
|
# Execute all searches in parallel
|
|
670
608
|
search_results = await asyncio.gather(*search_tasks, return_exceptions=True)
|
|
@@ -689,9 +627,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
689
627
|
|
|
690
628
|
# Add context if requested
|
|
691
629
|
if include_context:
|
|
692
|
-
combined_results = await self._add_context_to_results(
|
|
693
|
-
combined_results, tool_ctx
|
|
694
|
-
)
|
|
630
|
+
combined_results = await self._add_context_to_results(combined_results, tool_ctx)
|
|
695
631
|
|
|
696
632
|
end_time = time.time()
|
|
697
633
|
search_time_ms = (end_time - start_time) * 1000
|
|
@@ -700,27 +636,17 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
700
636
|
combined_results.sort(key=lambda r: r.score, reverse=True)
|
|
701
637
|
|
|
702
638
|
# Limit total results
|
|
703
|
-
combined_results = combined_results[
|
|
704
|
-
: max_results * 2
|
|
705
|
-
] # Allow more when combining
|
|
639
|
+
combined_results = combined_results[: max_results * 2] # Allow more when combining
|
|
706
640
|
|
|
707
641
|
# Create batch results object
|
|
708
642
|
batch_results = BatchSearchResults(
|
|
709
643
|
query=f"Batch search with {len(queries)} queries",
|
|
710
644
|
total_results=len(combined_results),
|
|
711
645
|
results_by_type={
|
|
712
|
-
SearchType.GREP: [
|
|
713
|
-
|
|
714
|
-
],
|
|
715
|
-
SearchType.
|
|
716
|
-
r for r in combined_results if r.search_type == SearchType.VECTOR
|
|
717
|
-
],
|
|
718
|
-
SearchType.AST: [
|
|
719
|
-
r for r in combined_results if r.search_type == SearchType.AST
|
|
720
|
-
],
|
|
721
|
-
SearchType.GIT: [
|
|
722
|
-
r for r in combined_results if r.search_type == SearchType.GIT
|
|
723
|
-
],
|
|
646
|
+
SearchType.GREP: [r for r in combined_results if r.search_type == SearchType.GREP],
|
|
647
|
+
SearchType.VECTOR: [r for r in combined_results if r.search_type == SearchType.VECTOR],
|
|
648
|
+
SearchType.AST: [r for r in combined_results if r.search_type == SearchType.AST],
|
|
649
|
+
SearchType.GIT: [r for r in combined_results if r.search_type == SearchType.GIT],
|
|
724
650
|
},
|
|
725
651
|
combined_results=combined_results,
|
|
726
652
|
search_time_ms=search_time_ms,
|
|
@@ -802,16 +728,12 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
802
728
|
|
|
803
729
|
return combined
|
|
804
730
|
|
|
805
|
-
async def _add_context_to_results(
|
|
806
|
-
self, results: List[SearchResult], tool_ctx
|
|
807
|
-
) -> List[SearchResult]:
|
|
731
|
+
async def _add_context_to_results(self, results: List[SearchResult], tool_ctx) -> List[SearchResult]:
|
|
808
732
|
"""Add function/method context to results."""
|
|
809
733
|
# This is a simplified version - you could enhance with full AST context
|
|
810
734
|
return await self._add_function_context(results, tool_ctx)
|
|
811
735
|
|
|
812
|
-
def _format_batch_results(
|
|
813
|
-
self, results: BatchSearchResults, query_info: List[Dict]
|
|
814
|
-
) -> str:
|
|
736
|
+
def _format_batch_results(self, results: BatchSearchResults, query_info: List[Dict]) -> str:
|
|
815
737
|
"""Format batch search results for display."""
|
|
816
738
|
output = []
|
|
817
739
|
|
|
@@ -859,9 +781,7 @@ Perfect for comprehensive code analysis and refactoring tasks."""
|
|
|
859
781
|
score_str = f"[{result.search_type.value} {result.score:.2f}]"
|
|
860
782
|
|
|
861
783
|
if result.line_number:
|
|
862
|
-
output.append(
|
|
863
|
-
f" {result.line_number:>4}: {score_str} {result.content}"
|
|
864
|
-
)
|
|
784
|
+
output.append(f" {result.line_number:>4}: {score_str} {result.content}")
|
|
865
785
|
else:
|
|
866
786
|
output.append(f" {score_str} {result.content}")
|
|
867
787
|
|
|
@@ -159,15 +159,11 @@ Only works within allowed directories."""
|
|
|
159
159
|
# Process based on whether path is a file or directory
|
|
160
160
|
if input_path.is_file():
|
|
161
161
|
# Single file search
|
|
162
|
-
if file_pattern == "*" or fnmatch.fnmatch(
|
|
163
|
-
input_path.name, file_pattern
|
|
164
|
-
):
|
|
162
|
+
if file_pattern == "*" or fnmatch.fnmatch(input_path.name, file_pattern):
|
|
165
163
|
matching_files.append(input_path)
|
|
166
164
|
await tool_ctx.info(f"Searching single file: {path}")
|
|
167
165
|
else:
|
|
168
|
-
await tool_ctx.info(
|
|
169
|
-
f"File does not match pattern '{file_pattern}': {path}"
|
|
170
|
-
)
|
|
166
|
+
await tool_ctx.info(f"File does not match pattern '{file_pattern}': {path}")
|
|
171
167
|
return f"File does not match pattern '{file_pattern}': {path}"
|
|
172
168
|
elif input_path.is_dir():
|
|
173
169
|
# Directory search - optimized file finding
|
|
@@ -186,9 +182,7 @@ Only works within allowed directories."""
|
|
|
186
182
|
for entry in input_path.rglob("*"):
|
|
187
183
|
entry_path = str(entry)
|
|
188
184
|
if entry_path in allowed_paths and entry.is_file():
|
|
189
|
-
if file_pattern == "*" or fnmatch.fnmatch(
|
|
190
|
-
entry.name, file_pattern
|
|
191
|
-
):
|
|
185
|
+
if file_pattern == "*" or fnmatch.fnmatch(entry.name, file_pattern):
|
|
192
186
|
matching_files.append(entry)
|
|
193
187
|
|
|
194
188
|
await tool_ctx.info(f"Found {len(matching_files)} matching files")
|
|
@@ -251,9 +245,7 @@ Only works within allowed directories."""
|
|
|
251
245
|
)
|
|
252
246
|
message = f"Dry run: {replacements_made} replacements of '{pattern}' with '{replacement}' would be made in {files_modified} files:"
|
|
253
247
|
else:
|
|
254
|
-
await tool_ctx.info(
|
|
255
|
-
f"Made {replacements_made} replacements in {files_modified} files"
|
|
256
|
-
)
|
|
248
|
+
await tool_ctx.info(f"Made {replacements_made} replacements in {files_modified} files")
|
|
257
249
|
message = f"Made {replacements_made} replacements of '{pattern}' with '{replacement}' in {files_modified} files:"
|
|
258
250
|
|
|
259
251
|
return message + "\n\n" + "\n".join(results)
|
|
@@ -166,16 +166,8 @@ diff a.json b.json --ignore-whitespace"""
|
|
|
166
166
|
output.extend(diff_lines)
|
|
167
167
|
|
|
168
168
|
# Add summary
|
|
169
|
-
additions = sum(
|
|
170
|
-
|
|
171
|
-
for line in diff_lines
|
|
172
|
-
if line.startswith("+") and not line.startswith("+++")
|
|
173
|
-
)
|
|
174
|
-
deletions = sum(
|
|
175
|
-
1
|
|
176
|
-
for line in diff_lines
|
|
177
|
-
if line.startswith("-") and not line.startswith("---")
|
|
178
|
-
)
|
|
169
|
+
additions = sum(1 for line in diff_lines if line.startswith("+") and not line.startswith("+++"))
|
|
170
|
+
deletions = sum(1 for line in diff_lines if line.startswith("-") and not line.startswith("---"))
|
|
179
171
|
|
|
180
172
|
output.append("")
|
|
181
173
|
output.append("=" * 60)
|
|
@@ -112,9 +112,7 @@ requested. Only works within allowed directories."""
|
|
|
112
112
|
await tool_ctx.error(path_validation.error_message)
|
|
113
113
|
return f"Error: {path_validation.error_message}"
|
|
114
114
|
|
|
115
|
-
await tool_ctx.info(
|
|
116
|
-
f"Getting directory tree: {path} (depth: {depth}, include_filtered: {include_filtered})"
|
|
117
|
-
)
|
|
115
|
+
await tool_ctx.info(f"Getting directory tree: {path} (depth: {depth}, include_filtered: {include_filtered})")
|
|
118
116
|
|
|
119
117
|
# Check if path is allowed
|
|
120
118
|
allowed, error_msg = await self.check_path_allowed(path, tool_ctx)
|
|
@@ -153,9 +151,7 @@ requested. Only works within allowed directories."""
|
|
|
153
151
|
}
|
|
154
152
|
|
|
155
153
|
# Log filtering settings
|
|
156
|
-
await tool_ctx.info(
|
|
157
|
-
f"Directory tree filtering: include_filtered={include_filtered}"
|
|
158
|
-
)
|
|
154
|
+
await tool_ctx.info(f"Directory tree filtering: include_filtered={include_filtered}")
|
|
159
155
|
|
|
160
156
|
# Check if a directory should be filtered
|
|
161
157
|
def should_filter(current_path: Path) -> bool:
|
|
@@ -165,9 +161,7 @@ requested. Only works within allowed directories."""
|
|
|
165
161
|
return False
|
|
166
162
|
|
|
167
163
|
# Filter based on directory name if filtering is enabled
|
|
168
|
-
return
|
|
169
|
-
current_path.name in FILTERED_DIRECTORIES and not include_filtered
|
|
170
|
-
)
|
|
164
|
+
return current_path.name in FILTERED_DIRECTORIES and not include_filtered
|
|
171
165
|
|
|
172
166
|
# Track stats for summary
|
|
173
167
|
stats = {
|
|
@@ -178,9 +172,7 @@ requested. Only works within allowed directories."""
|
|
|
178
172
|
}
|
|
179
173
|
|
|
180
174
|
# Build the tree recursively
|
|
181
|
-
async def build_tree(
|
|
182
|
-
current_path: Path, current_depth: int = 0
|
|
183
|
-
) -> list[dict[str, Any]]:
|
|
175
|
+
async def build_tree(current_path: Path, current_depth: int = 0) -> list[dict[str, Any]]:
|
|
184
176
|
result: list[dict[str, Any]] = []
|
|
185
177
|
|
|
186
178
|
# Skip processing if path isn't allowed
|
|
@@ -189,9 +181,7 @@ requested. Only works within allowed directories."""
|
|
|
189
181
|
|
|
190
182
|
try:
|
|
191
183
|
# Sort entries: directories first, then files alphabetically
|
|
192
|
-
entries = sorted(
|
|
193
|
-
current_path.iterdir(), key=lambda x: (not x.is_dir(), x.name)
|
|
194
|
-
)
|
|
184
|
+
entries = sorted(current_path.iterdir(), key=lambda x: (not x.is_dir(), x.name))
|
|
195
185
|
|
|
196
186
|
for entry in entries:
|
|
197
187
|
# Skip entries that aren't allowed
|
|
@@ -220,9 +210,7 @@ requested. Only works within allowed directories."""
|
|
|
220
210
|
continue
|
|
221
211
|
|
|
222
212
|
# Process children recursively with depth increment
|
|
223
|
-
entry_data["children"] = await build_tree(
|
|
224
|
-
entry, current_depth + 1
|
|
225
|
-
)
|
|
213
|
+
entry_data["children"] = await build_tree(entry, current_depth + 1)
|
|
226
214
|
result.append(entry_data)
|
|
227
215
|
else:
|
|
228
216
|
# Files should be at the same level check as directories
|
|
@@ -237,9 +225,7 @@ requested. Only works within allowed directories."""
|
|
|
237
225
|
return result
|
|
238
226
|
|
|
239
227
|
# Format the tree as a simple indented structure
|
|
240
|
-
def format_tree(
|
|
241
|
-
tree_data: list[dict[str, Any]], level: int = 0
|
|
242
|
-
) -> list[str]:
|
|
228
|
+
def format_tree(tree_data: list[dict[str, Any]], level: int = 0) -> list[str]:
|
|
243
229
|
lines = []
|
|
244
230
|
|
|
245
231
|
for item in tree_data:
|
|
@@ -249,9 +235,7 @@ requested. Only works within allowed directories."""
|
|
|
249
235
|
# Format based on type
|
|
250
236
|
if item["type"] == "directory":
|
|
251
237
|
if "skipped" in item:
|
|
252
|
-
lines.append(
|
|
253
|
-
f"{indent}{item['name']}/ [skipped - {item['skipped']}]"
|
|
254
|
-
)
|
|
238
|
+
lines.append(f"{indent}{item['name']}/ [skipped - {item['skipped']}]")
|
|
255
239
|
else:
|
|
256
240
|
lines.append(f"{indent}{item['name']}/")
|
|
257
241
|
# Add children with increased indentation if present
|
|
@@ -310,6 +294,4 @@ requested. Only works within allowed directories."""
|
|
|
310
294
|
depth: Depth = 3,
|
|
311
295
|
include_filtered: IncludeFiltered = False,
|
|
312
296
|
) -> str:
|
|
313
|
-
return await tool_self.call(
|
|
314
|
-
ctx, path=path, depth=depth, include_filtered=include_filtered
|
|
315
|
-
)
|
|
297
|
+
return await tool_self.call(ctx, path=path, depth=depth, include_filtered=include_filtered)
|
|
@@ -152,9 +152,7 @@ Returns nextCursor if more entries are available."""
|
|
|
152
152
|
await tool_ctx.error(path_validation.error_message)
|
|
153
153
|
return {"error": path_validation.error_message}
|
|
154
154
|
|
|
155
|
-
await tool_ctx.info(
|
|
156
|
-
f"Getting paginated directory tree: {path} (depth: {depth}, page_size: {page_size})"
|
|
157
|
-
)
|
|
155
|
+
await tool_ctx.info(f"Getting paginated directory tree: {path} (depth: {depth}, page_size: {page_size})")
|
|
158
156
|
|
|
159
157
|
# Check if path is allowed
|
|
160
158
|
allowed, error_msg = await self.check_path_allowed(path, tool_ctx)
|
|
@@ -196,35 +194,27 @@ Returns nextCursor if more entries are available."""
|
|
|
196
194
|
def should_filter(current_path: Path) -> bool:
|
|
197
195
|
if str(current_path.absolute()) == str(dir_path.absolute()):
|
|
198
196
|
return False
|
|
199
|
-
return
|
|
200
|
-
current_path.name in FILTERED_DIRECTORIES and not include_filtered
|
|
201
|
-
)
|
|
197
|
+
return current_path.name in FILTERED_DIRECTORIES and not include_filtered
|
|
202
198
|
|
|
203
199
|
# Collect all entries in a flat list for pagination
|
|
204
200
|
all_entries: List[Dict[str, Any]] = []
|
|
205
201
|
|
|
206
202
|
# Build the tree and collect entries
|
|
207
|
-
def collect_entries(
|
|
208
|
-
current_path: Path, current_depth: int = 0, parent_path: str = ""
|
|
209
|
-
) -> None:
|
|
203
|
+
def collect_entries(current_path: Path, current_depth: int = 0, parent_path: str = "") -> None:
|
|
210
204
|
"""Collect entries in a flat list for pagination."""
|
|
211
205
|
if not self.is_path_allowed(str(current_path)):
|
|
212
206
|
return
|
|
213
207
|
|
|
214
208
|
try:
|
|
215
209
|
# Sort entries: directories first, then files alphabetically
|
|
216
|
-
entries = sorted(
|
|
217
|
-
current_path.iterdir(), key=lambda x: (not x.is_dir(), x.name)
|
|
218
|
-
)
|
|
210
|
+
entries = sorted(current_path.iterdir(), key=lambda x: (not x.is_dir(), x.name))
|
|
219
211
|
|
|
220
212
|
for entry in entries:
|
|
221
213
|
if not self.is_path_allowed(str(entry)):
|
|
222
214
|
continue
|
|
223
215
|
|
|
224
216
|
# Calculate relative path for display
|
|
225
|
-
relative_path =
|
|
226
|
-
f"{parent_path}/{entry.name}" if parent_path else entry.name
|
|
227
|
-
)
|
|
217
|
+
relative_path = f"{parent_path}/{entry.name}" if parent_path else entry.name
|
|
228
218
|
|
|
229
219
|
if entry.is_dir():
|
|
230
220
|
entry_data: Dict[str, Any] = {
|
|
@@ -121,9 +121,7 @@ Usage:
|
|
|
121
121
|
# Empty old_string is valid when creating a new file
|
|
122
122
|
file_exists = Path(file_path).exists()
|
|
123
123
|
if file_exists and old_string.strip() == "":
|
|
124
|
-
await tool_ctx.error(
|
|
125
|
-
"Parameter 'old_string' cannot be empty for existing files"
|
|
126
|
-
)
|
|
124
|
+
await tool_ctx.error("Parameter 'old_string' cannot be empty for existing files")
|
|
127
125
|
return "Error: Parameter 'old_string' cannot be empty for existing files"
|
|
128
126
|
|
|
129
127
|
if (
|
|
@@ -131,12 +129,8 @@ Usage:
|
|
|
131
129
|
or not isinstance(expected_replacements, (int, float))
|
|
132
130
|
or expected_replacements < 0
|
|
133
131
|
):
|
|
134
|
-
await tool_ctx.error(
|
|
135
|
-
|
|
136
|
-
)
|
|
137
|
-
return (
|
|
138
|
-
"Error: Parameter 'expected_replacements' must be a non-negative number"
|
|
139
|
-
)
|
|
132
|
+
await tool_ctx.error("Parameter 'expected_replacements' must be a non-negative number")
|
|
133
|
+
return "Error: Parameter 'expected_replacements' must be a non-negative number"
|
|
140
134
|
|
|
141
135
|
await tool_ctx.info(f"Editing file: {file_path}")
|
|
142
136
|
|
|
@@ -164,9 +158,7 @@ Usage:
|
|
|
164
158
|
f.write(new_string)
|
|
165
159
|
|
|
166
160
|
await tool_ctx.info(f"Successfully created file: {file_path}")
|
|
167
|
-
return (
|
|
168
|
-
f"Successfully created file: {file_path} ({len(new_string)} bytes)"
|
|
169
|
-
)
|
|
161
|
+
return f"Successfully created file: {file_path} ({len(new_string)} bytes)"
|
|
170
162
|
|
|
171
163
|
# Check file exists for non-creation operations
|
|
172
164
|
exists, error_msg = await self.check_path_exists(file_path, tool_ctx)
|
|
@@ -199,9 +191,7 @@ Usage:
|
|
|
199
191
|
modified_content = original_content.replace(old_string, new_string)
|
|
200
192
|
else:
|
|
201
193
|
# If we can't find the exact string, report an error
|
|
202
|
-
await tool_ctx.error(
|
|
203
|
-
"The specified old_string was not found in the file content"
|
|
204
|
-
)
|
|
194
|
+
await tool_ctx.error("The specified old_string was not found in the file content")
|
|
205
195
|
return "Error: The specified old_string was not found in the file content. Please check that it matches exactly, including all whitespace and indentation."
|
|
206
196
|
|
|
207
197
|
# Generate diff
|
|
@@ -226,9 +216,7 @@ Usage:
|
|
|
226
216
|
num_backticks += 1
|
|
227
217
|
|
|
228
218
|
# Format diff with appropriate number of backticks
|
|
229
|
-
formatted_diff =
|
|
230
|
-
f"```{num_backticks}diff\n{diff_text}```{num_backticks}\n"
|
|
231
|
-
)
|
|
219
|
+
formatted_diff = f"```{num_backticks}diff\n{diff_text}```{num_backticks}\n"
|
|
232
220
|
|
|
233
221
|
# Write the file if there are changes
|
|
234
222
|
if diff_text:
|
|
@@ -199,9 +199,7 @@ Fast, intuitive file content search."""
|
|
|
199
199
|
# Fallback
|
|
200
200
|
selected_backend = "grep"
|
|
201
201
|
|
|
202
|
-
await tool_ctx.info(
|
|
203
|
-
f"Using {selected_backend} to search for '{pattern}' in {path}"
|
|
204
|
-
)
|
|
202
|
+
await tool_ctx.info(f"Using {selected_backend} to search for '{pattern}' in {path}")
|
|
205
203
|
|
|
206
204
|
# Execute search
|
|
207
205
|
if selected_backend == "rg":
|
|
@@ -459,9 +457,7 @@ Fast, intuitive file content search."""
|
|
|
459
457
|
context_lines = []
|
|
460
458
|
for j in range(start, end):
|
|
461
459
|
prefix = ":" if j + 1 == i else "-"
|
|
462
|
-
context_lines.append(
|
|
463
|
-
f"{file_path}:{j + 1}{prefix}{lines[j].rstrip()}"
|
|
464
|
-
)
|
|
460
|
+
context_lines.append(f"{file_path}:{j + 1}{prefix}{lines[j].rstrip()}")
|
|
465
461
|
results.extend(context_lines)
|
|
466
462
|
results.append("") # Separator
|
|
467
463
|
else:
|
|
@@ -482,9 +478,7 @@ Fast, intuitive file content search."""
|
|
|
482
478
|
await tool_ctx.error(f"Error in fallback grep: {str(e)}")
|
|
483
479
|
return f"Error in fallback grep: {str(e)}"
|
|
484
480
|
|
|
485
|
-
def _match_file_pattern(
|
|
486
|
-
self, filename: str, include: Optional[str], exclude: Optional[str]
|
|
487
|
-
) -> bool:
|
|
481
|
+
def _match_file_pattern(self, filename: str, include: Optional[str], exclude: Optional[str]) -> bool:
|
|
488
482
|
"""Check if filename matches include/exclude patterns."""
|
|
489
483
|
if include and not fnmatch.fnmatch(filename, include):
|
|
490
484
|
return False
|