hanzo-mcp 0.8.11__py3-none-any.whl → 0.9.0__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 +3 -9
- hanzo_mcp/bridge.py +9 -25
- hanzo_mcp/cli.py +6 -15
- 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 +1 -3
- 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 +2 -6
- hanzo_mcp/tools/__init__.py +26 -27
- hanzo_mcp/tools/agent/__init__.py +2 -1
- hanzo_mcp/tools/agent/agent.py +10 -30
- hanzo_mcp/tools/agent/agent_tool.py +22 -15
- 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 +75 -74
- 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/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/path_utils.py +34 -0
- hanzo_mcp/tools/common/permissions.py +14 -13
- hanzo_mcp/tools/common/personality.py +983 -701
- hanzo_mcp/tools/common/plugin_loader.py +3 -15
- hanzo_mcp/tools/common/stats.py +6 -18
- 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/compiler/__init__.py +8 -0
- hanzo_mcp/tools/compiler/sandboxed_compiler.py +681 -0
- 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/environment/__init__.py +8 -0
- hanzo_mcp/tools/environment/environment_detector.py +594 -0
- hanzo_mcp/tools/filesystem/__init__.py +28 -26
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
- hanzo_mcp/tools/filesystem/ast_tool.py +3 -0
- hanzo_mcp/tools/filesystem/base.py +20 -12
- hanzo_mcp/tools/filesystem/content_replace.py +7 -12
- hanzo_mcp/tools/filesystem/diff.py +2 -10
- hanzo_mcp/tools/filesystem/directory_tree.py +285 -51
- hanzo_mcp/tools/filesystem/edit.py +10 -18
- hanzo_mcp/tools/filesystem/find.py +312 -179
- hanzo_mcp/tools/filesystem/git_search.py +12 -24
- hanzo_mcp/tools/filesystem/multi_edit.py +10 -18
- hanzo_mcp/tools/filesystem/read.py +14 -30
- hanzo_mcp/tools/filesystem/rules_tool.py +9 -17
- hanzo_mcp/tools/filesystem/search.py +1160 -0
- hanzo_mcp/tools/filesystem/watch.py +2 -4
- hanzo_mcp/tools/filesystem/write.py +7 -10
- hanzo_mcp/tools/framework/__init__.py +8 -0
- hanzo_mcp/tools/framework/framework_modes.py +714 -0
- hanzo_mcp/tools/jupyter/base.py +6 -20
- hanzo_mcp/tools/jupyter/jupyter.py +4 -12
- 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 +621 -481
- hanzo_mcp/tools/mcp/mcp_add.py +1 -3
- hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
- hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
- hanzo_mcp/tools/memory/__init__.py +10 -27
- hanzo_mcp/tools/memory/conversation_memory.py +636 -0
- hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
- hanzo_mcp/tools/memory/memory_tools.py +6 -18
- hanzo_mcp/tools/search/find_tool.py +12 -34
- hanzo_mcp/tools/search/unified_search.py +24 -78
- hanzo_mcp/tools/shell/__init__.py +16 -4
- 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/run_tool.py +56 -0
- 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/vector/__init__.py +97 -50
- 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 +7 -27
- hanzo_mcp/tools/vector/mock_infinity.py +1 -3
- hanzo_mcp/tools/vector/node_tool.py +538 -0
- hanzo_mcp/tools/vector/project_manager.py +4 -12
- hanzo_mcp/tools/vector/unified_vector.py +384 -0
- 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.11.dist-info → hanzo_mcp-0.9.0.dist-info}/METADATA +2 -2
- hanzo_mcp-0.9.0.dist-info/RECORD +191 -0
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +0 -645
- hanzo_mcp/tools/agent/swarm_tool.py +0 -718
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +0 -577
- hanzo_mcp/tools/filesystem/batch_search.py +0 -900
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +0 -350
- hanzo_mcp/tools/filesystem/find_files.py +0 -369
- hanzo_mcp/tools/filesystem/grep.py +0 -467
- hanzo_mcp/tools/filesystem/search_tool.py +0 -767
- hanzo_mcp/tools/filesystem/symbols_tool.py +0 -515
- hanzo_mcp/tools/filesystem/tree.py +0 -270
- hanzo_mcp/tools/jupyter/notebook_edit.py +0 -317
- hanzo_mcp/tools/jupyter/notebook_read.py +0 -147
- hanzo_mcp/tools/todo/todo_read.py +0 -143
- hanzo_mcp/tools/todo/todo_write.py +0 -374
- hanzo_mcp-0.8.11.dist-info/RECORD +0 -193
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.9.0.dist-info}/top_level.txt +0 -0
|
@@ -423,17 +423,13 @@ class IChing:
|
|
|
423
423
|
lines += "1"
|
|
424
424
|
return Hexagram(lines)
|
|
425
425
|
|
|
426
|
-
def select_principles(
|
|
427
|
-
self, hexagram: Hexagram, challenge: str
|
|
428
|
-
) -> List[HanzoPrinciple]:
|
|
426
|
+
def select_principles(self, hexagram: Hexagram, challenge: str) -> List[HanzoPrinciple]:
|
|
429
427
|
"""Select relevant Hanzo principles based on hexagram and challenge."""
|
|
430
428
|
# Use hexagram pattern to deterministically but creatively select principles
|
|
431
429
|
selected = []
|
|
432
430
|
|
|
433
431
|
# Primary principle based on hexagram pattern
|
|
434
|
-
primary_index = sum(
|
|
435
|
-
int(bit) * (2**i) for i, bit in enumerate(hexagram.lines)
|
|
436
|
-
) % len(self.principles)
|
|
432
|
+
primary_index = sum(int(bit) * (2**i) for i, bit in enumerate(hexagram.lines)) % len(self.principles)
|
|
437
433
|
selected.append(self.principles[primary_index])
|
|
438
434
|
|
|
439
435
|
# Supporting principles based on challenge keywords
|
|
@@ -458,9 +454,7 @@ class IChing:
|
|
|
458
454
|
# Add complementary principle based on changing lines
|
|
459
455
|
changing_lines = hexagram.get_changing_lines()
|
|
460
456
|
if changing_lines:
|
|
461
|
-
complement_index = (primary_index + sum(changing_lines)) % len(
|
|
462
|
-
self.principles
|
|
463
|
-
)
|
|
457
|
+
complement_index = (primary_index + sum(changing_lines)) % len(self.principles)
|
|
464
458
|
selected.append(self.principles[complement_index])
|
|
465
459
|
|
|
466
460
|
# Ensure uniqueness and limit to 3-5 principles
|
|
@@ -473,9 +467,7 @@ class IChing:
|
|
|
473
467
|
|
|
474
468
|
return unique_selected[:5]
|
|
475
469
|
|
|
476
|
-
def generate_guidance(
|
|
477
|
-
self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str
|
|
478
|
-
) -> str:
|
|
470
|
+
def generate_guidance(self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str) -> str:
|
|
479
471
|
"""Generate creative guidance combining I Ching wisdom and Hanzo principles."""
|
|
480
472
|
guidance = f"☯️ I CHING GUIDANCE FOR ENGINEERING CHALLENGE ☯️\n\n"
|
|
481
473
|
guidance += f"**Your Challenge:** {challenge}\n\n"
|
|
@@ -498,21 +490,15 @@ class IChing:
|
|
|
498
490
|
if "Creative" in hexagram.title:
|
|
499
491
|
guidance += "• This is a time for bold innovation. Don't hold back on ambitious ideas.\n"
|
|
500
492
|
elif "Receptive" in hexagram.title:
|
|
501
|
-
guidance +=
|
|
502
|
-
"• Listen deeply to user needs and system constraints before acting.\n"
|
|
503
|
-
)
|
|
493
|
+
guidance += "• Listen deeply to user needs and system constraints before acting.\n"
|
|
504
494
|
elif "Difficulty" in hexagram.title:
|
|
505
|
-
guidance +=
|
|
506
|
-
"• Challenges are teachers. Each obstacle reveals the path forward.\n"
|
|
507
|
-
)
|
|
495
|
+
guidance += "• Challenges are teachers. Each obstacle reveals the path forward.\n"
|
|
508
496
|
elif "Waiting" in hexagram.title:
|
|
509
497
|
guidance += "• Strategic patience required. Prepare thoroughly before implementation.\n"
|
|
510
498
|
elif "Conflict" in hexagram.title:
|
|
511
499
|
guidance += "• Technical disagreements? Seek data-driven resolution.\n"
|
|
512
500
|
elif "Peace" in hexagram.title:
|
|
513
|
-
guidance +=
|
|
514
|
-
"• Harmony achieved. Now build sustainably on this foundation.\n"
|
|
515
|
-
)
|
|
501
|
+
guidance += "• Harmony achieved. Now build sustainably on this foundation.\n"
|
|
516
502
|
|
|
517
503
|
# Principle-specific actionable advice
|
|
518
504
|
principle_actions = {
|
|
@@ -534,9 +520,7 @@ class IChing:
|
|
|
534
520
|
changing_lines = hexagram.get_changing_lines()
|
|
535
521
|
if changing_lines:
|
|
536
522
|
guidance += f"\n**Lines in Transition:** {', '.join(str(i + 1) for i in changing_lines)}\n"
|
|
537
|
-
guidance +=
|
|
538
|
-
"• Change is imminent in these areas. Prepare for transformation.\n"
|
|
539
|
-
)
|
|
523
|
+
guidance += "• Change is imminent in these areas. Prepare for transformation.\n"
|
|
540
524
|
|
|
541
525
|
# Final synthesis
|
|
542
526
|
guidance += "\n**The Way Forward:**\n"
|
|
@@ -547,9 +531,7 @@ class IChing:
|
|
|
547
531
|
|
|
548
532
|
return guidance
|
|
549
533
|
|
|
550
|
-
def _synthesize_action_plan(
|
|
551
|
-
self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str
|
|
552
|
-
) -> str:
|
|
534
|
+
def _synthesize_action_plan(self, hexagram: Hexagram, principles: List[HanzoPrinciple], challenge: str) -> str:
|
|
553
535
|
"""Create a specific action plan based on the reading."""
|
|
554
536
|
plan = ""
|
|
555
537
|
|
|
@@ -558,21 +540,15 @@ class IChing:
|
|
|
558
540
|
plan += "1. **Diagnose systematically** - Use empirical debugging, not guesswork\n"
|
|
559
541
|
plan += "2. **Fix root cause** - Address the source, not just symptoms\n"
|
|
560
542
|
plan += "3. **Prevent recurrence** - Add tests and monitoring\n"
|
|
561
|
-
elif any(
|
|
562
|
-
word in challenge.lower() for word in ["scale", "performance", "slow"]
|
|
563
|
-
):
|
|
543
|
+
elif any(word in challenge.lower() for word in ["scale", "performance", "slow"]):
|
|
564
544
|
plan += "1. **Measure first** - Profile to find actual bottlenecks\n"
|
|
565
545
|
plan += "2. **Parallelize** - Use concurrency where possible\n"
|
|
566
546
|
plan += "3. **Simplify** - Remove complexity before optimizing\n"
|
|
567
|
-
elif any(
|
|
568
|
-
word in challenge.lower() for word in ["design", "architect", "structure"]
|
|
569
|
-
):
|
|
547
|
+
elif any(word in challenge.lower() for word in ["design", "architect", "structure"]):
|
|
570
548
|
plan += "1. **Start simple** - MVP first, elaborate later\n"
|
|
571
549
|
plan += "2. **Stay flexible** - Design for change\n"
|
|
572
550
|
plan += "3. **Think holistically** - Consider entire system\n"
|
|
573
|
-
elif any(
|
|
574
|
-
word in challenge.lower() for word in ["team", "collaborate", "people"]
|
|
575
|
-
):
|
|
551
|
+
elif any(word in challenge.lower() for word in ["team", "collaborate", "people"]):
|
|
576
552
|
plan += "1. **Enable autonomy** - Trust your team\n"
|
|
577
553
|
plan += "2. **Maintain balance** - Sustainable pace wins\n"
|
|
578
554
|
plan += "3. **Share knowledge** - Elevate everyone\n"
|
|
@@ -77,9 +77,7 @@ class NetworkTool(BaseTool):
|
|
|
77
77
|
"""
|
|
78
78
|
self.permission_manager = permission_manager
|
|
79
79
|
self.default_mode = default_mode
|
|
80
|
-
self.cluster_endpoint = cluster_endpoint or os.environ.get(
|
|
81
|
-
"HANZO_CLUSTER_ENDPOINT", "http://localhost:8000"
|
|
82
|
-
)
|
|
80
|
+
self.cluster_endpoint = cluster_endpoint or os.environ.get("HANZO_CLUSTER_ENDPOINT", "http://localhost:8000")
|
|
83
81
|
self._cluster = None
|
|
84
82
|
|
|
85
83
|
async def _ensure_cluster(self):
|
|
@@ -160,9 +158,7 @@ class NetworkTool(BaseTool):
|
|
|
160
158
|
results["results"].append(
|
|
161
159
|
{
|
|
162
160
|
"agent": "local-cluster",
|
|
163
|
-
"response": local_result.get("choices", [{}])[0].get(
|
|
164
|
-
"text", ""
|
|
165
|
-
),
|
|
161
|
+
"response": local_result.get("choices", [{}])[0].get("text", ""),
|
|
166
162
|
"local": True,
|
|
167
163
|
}
|
|
168
164
|
)
|
|
@@ -180,6 +176,7 @@ class NetworkTool(BaseTool):
|
|
|
180
176
|
# Agent-based execution with concurrency
|
|
181
177
|
if not results["success"] or mode in ["distributed", "hybrid"]:
|
|
182
178
|
from hanzo_mcp.tools.agent.agent_tool import AgentTool
|
|
179
|
+
|
|
183
180
|
agent = AgentTool(permission_manager=self.permission_manager, model=model_pref)
|
|
184
181
|
concurrency = max(1, len(agents_list)) if agents_list else 5 if routing == "parallel" else 1
|
|
185
182
|
agent_params = {"prompts": task, "concurrency": concurrency}
|
|
@@ -206,25 +203,17 @@ class NetworkTool(BaseTool):
|
|
|
206
203
|
async def network_handler(
|
|
207
204
|
ctx: MCPContext,
|
|
208
205
|
task: Annotated[str, Field(description="Task to execute on the network")],
|
|
209
|
-
agents: Annotated[
|
|
210
|
-
Optional[List[str]], Field(description="Specific agents to use")
|
|
211
|
-
] = None,
|
|
206
|
+
agents: Annotated[Optional[List[str]], Field(description="Specific agents to use")] = None,
|
|
212
207
|
mode: Annotated[
|
|
213
208
|
Optional[str],
|
|
214
209
|
Field(description="Execution mode: local, distributed, or hybrid"),
|
|
215
210
|
] = None,
|
|
216
|
-
model: Annotated[
|
|
217
|
-
Optional[str], Field(description="Model preference")
|
|
218
|
-
] = None,
|
|
211
|
+
model: Annotated[Optional[str], Field(description="Model preference")] = None,
|
|
219
212
|
routing: Annotated[
|
|
220
213
|
Optional[str],
|
|
221
|
-
Field(
|
|
222
|
-
description="Routing strategy: sequential, parallel, or consensus"
|
|
223
|
-
),
|
|
224
|
-
] = None,
|
|
225
|
-
require_local: Annotated[
|
|
226
|
-
Optional[bool], Field(description="Require local-only execution")
|
|
214
|
+
Field(description="Routing strategy: sequential, parallel, or consensus"),
|
|
227
215
|
] = None,
|
|
216
|
+
require_local: Annotated[Optional[bool], Field(description="Require local-only execution")] = None,
|
|
228
217
|
) -> str:
|
|
229
218
|
"""Dispatch work to agent networks."""
|
|
230
219
|
params = NetworkToolParams(
|
hanzo_mcp/tools/agent/prompt.py
CHANGED
|
@@ -139,11 +139,7 @@ def get_default_model(model_override: str | None = None) -> str:
|
|
|
139
139
|
model = os.environ.get("AGENT_MODEL", "claude-3-5-sonnet-20241022")
|
|
140
140
|
|
|
141
141
|
# Special cases for tests
|
|
142
|
-
if (
|
|
143
|
-
model.startswith("test-model")
|
|
144
|
-
or "TEST_MODE" in os.environ
|
|
145
|
-
and model == "claude-3-5-sonnet-20241022"
|
|
146
|
-
):
|
|
142
|
+
if model.startswith("test-model") or "TEST_MODE" in os.environ and model == "claude-3-5-sonnet-20241022":
|
|
147
143
|
return model
|
|
148
144
|
|
|
149
145
|
provider = os.environ.get("AGENT_PROVIDER", "anthropic")
|
|
@@ -83,9 +83,7 @@ review(
|
|
|
83
83
|
file_paths: Optional[List[str]] = None,
|
|
84
84
|
context: Optional[str] = None,
|
|
85
85
|
) -> str:
|
|
86
|
-
return await tool_self.call(
|
|
87
|
-
ctx, focus, work_description, code_snippets, file_paths, context
|
|
88
|
-
)
|
|
86
|
+
return await tool_self.call(ctx, focus, work_description, code_snippets, file_paths, context)
|
|
89
87
|
|
|
90
88
|
|
|
91
89
|
class BalancedReviewer:
|
|
@@ -210,23 +208,18 @@ class BalancedReviewer:
|
|
|
210
208
|
|
|
211
209
|
if code_snippets:
|
|
212
210
|
total_lines = sum(snippet.count("\n") + 1 for snippet in code_snippets)
|
|
213
|
-
avg_line_length = sum(
|
|
214
|
-
|
|
215
|
-
)
|
|
211
|
+
avg_line_length = sum(len(line) for snippet in code_snippets for line in snippet.split("\n")) / max(
|
|
212
|
+
total_lines, 1
|
|
213
|
+
)
|
|
216
214
|
|
|
217
215
|
if avg_line_length < 80:
|
|
218
216
|
response += "✓ Line lengths are reasonable\n"
|
|
219
217
|
else:
|
|
220
|
-
response +=
|
|
221
|
-
"• Some lines might be too long, consider breaking them up\n"
|
|
222
|
-
)
|
|
218
|
+
response += "• Some lines might be too long, consider breaking them up\n"
|
|
223
219
|
|
|
224
220
|
# Check naming
|
|
225
221
|
has_good_names = any(
|
|
226
|
-
any(
|
|
227
|
-
word in snippet
|
|
228
|
-
for word in ["Add", "Get", "Set", "Create", "Update", "Delete"]
|
|
229
|
-
)
|
|
222
|
+
any(word in snippet for word in ["Add", "Get", "Set", "Create", "Update", "Delete"])
|
|
230
223
|
for snippet in code_snippets
|
|
231
224
|
)
|
|
232
225
|
if has_good_names:
|
|
@@ -264,10 +257,7 @@ class BalancedReviewer:
|
|
|
264
257
|
# Check for modularity in code
|
|
265
258
|
if code_snippets:
|
|
266
259
|
function_count = sum(
|
|
267
|
-
snippet.count("func ")
|
|
268
|
-
+ snippet.count("def ")
|
|
269
|
-
+ snippet.count("function ")
|
|
270
|
-
for snippet in code_snippets
|
|
260
|
+
snippet.count("func ") + snippet.count("def ") + snippet.count("function ") for snippet in code_snippets
|
|
271
261
|
)
|
|
272
262
|
if function_count > 0:
|
|
273
263
|
response += "✓ Code is broken into functions/methods\n"
|
|
@@ -334,8 +324,7 @@ class BalancedReviewer:
|
|
|
334
324
|
has_comments = False
|
|
335
325
|
if code_snippets:
|
|
336
326
|
has_comments = any(
|
|
337
|
-
"//" in snippet or "/*" in snippet or "#" in snippet or '"""' in snippet
|
|
338
|
-
for snippet in code_snippets
|
|
327
|
+
"//" in snippet or "/*" in snippet or "#" in snippet or '"""' in snippet for snippet in code_snippets
|
|
339
328
|
)
|
|
340
329
|
|
|
341
330
|
if has_comments:
|
|
@@ -372,9 +361,7 @@ class BalancedReviewer:
|
|
|
372
361
|
# Analyze file structure
|
|
373
362
|
if file_paths:
|
|
374
363
|
# Check for separation of concerns
|
|
375
|
-
has_separation = (
|
|
376
|
-
len(set(str(p).split("/")[-2] for p in file_paths if "/" in str(p))) > 1
|
|
377
|
-
)
|
|
364
|
+
has_separation = len(set(str(p).split("/")[-2] for p in file_paths if "/" in str(p))) > 1
|
|
378
365
|
if has_separation:
|
|
379
366
|
response += "✓ Changes span multiple modules (good separation)\n"
|
|
380
367
|
else:
|
|
@@ -426,9 +413,7 @@ class ReviewProtocol:
|
|
|
426
413
|
except KeyError:
|
|
427
414
|
focus_enum = ReviewFocus.GENERAL
|
|
428
415
|
|
|
429
|
-
review = self.reviewer.review(
|
|
430
|
-
focus_enum, work_description, code_snippets, file_paths, context
|
|
431
|
-
)
|
|
416
|
+
review = self.reviewer.review(focus_enum, work_description, code_snippets, file_paths, context)
|
|
432
417
|
|
|
433
418
|
header = f"Review {self.review_count}/{self.max_reviews} (Focus: {focus_enum.value}):\n\n"
|
|
434
419
|
footer = "\n\n💡 This is a balanced review - consider both strengths and suggestions."
|
|
@@ -61,9 +61,7 @@ For new code, prefer using 'network' directly."""
|
|
|
61
61
|
**kwargs: Additional arguments passed to NetworkTool
|
|
62
62
|
"""
|
|
63
63
|
# Just pass through to NetworkTool
|
|
64
|
-
super().__init__(
|
|
65
|
-
permission_manager=permission_manager, default_mode=default_mode, **kwargs
|
|
66
|
-
)
|
|
64
|
+
super().__init__(permission_manager=permission_manager, default_mode=default_mode, **kwargs)
|
|
67
65
|
|
|
68
66
|
async def call(self, **kwargs) -> str:
|
|
69
67
|
"""Execute swarm via network tool.
|
|
@@ -21,10 +21,10 @@ from ...core.model_registry import registry
|
|
|
21
21
|
|
|
22
22
|
class UnifiedCLITool(BaseTool, CLIAgent):
|
|
23
23
|
"""Unified CLI tool that combines BaseTool and CLIAgent functionality.
|
|
24
|
-
|
|
24
|
+
|
|
25
25
|
MRO: BaseTool first for proper method resolution order.
|
|
26
26
|
"""
|
|
27
|
-
|
|
27
|
+
|
|
28
28
|
def __init__(
|
|
29
29
|
self,
|
|
30
30
|
name: str,
|
|
@@ -34,7 +34,7 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
34
34
|
permission_manager: Optional[PermissionManager] = None,
|
|
35
35
|
):
|
|
36
36
|
"""Initialize unified CLI tool.
|
|
37
|
-
|
|
37
|
+
|
|
38
38
|
Args:
|
|
39
39
|
name: Tool name
|
|
40
40
|
description: Tool description
|
|
@@ -45,47 +45,47 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
45
45
|
# Initialize CLIAgent with config
|
|
46
46
|
config = AgentConfig(model=default_model)
|
|
47
47
|
CLIAgent.__init__(self, config)
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
# Store tool metadata
|
|
50
50
|
self._name = name
|
|
51
51
|
self._description = description
|
|
52
52
|
self._cli_command = cli_command
|
|
53
53
|
self.permission_manager = permission_manager
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
@property
|
|
56
56
|
def name(self) -> str:
|
|
57
57
|
return self._name
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
@property
|
|
60
60
|
def description(self) -> str:
|
|
61
61
|
return self._description
|
|
62
|
-
|
|
62
|
+
|
|
63
63
|
@property
|
|
64
64
|
def cli_command(self) -> str:
|
|
65
65
|
return self._cli_command
|
|
66
|
-
|
|
66
|
+
|
|
67
67
|
def build_command(self, prompt: str, **kwargs: Any) -> List[str]:
|
|
68
68
|
"""Build the CLI command with model-specific formatting.
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
Args:
|
|
71
71
|
prompt: The prompt
|
|
72
72
|
**kwargs: Additional parameters
|
|
73
|
-
|
|
73
|
+
|
|
74
74
|
Returns:
|
|
75
75
|
Command arguments list
|
|
76
76
|
"""
|
|
77
77
|
command = [self.cli_command]
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
# Get model config from registry
|
|
80
80
|
model_config = registry.get(self.config.model)
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
# Handle different CLI tool formats
|
|
83
83
|
if self.cli_command == "claude":
|
|
84
84
|
if model_config:
|
|
85
85
|
command.extend(["--model", model_config.full_name])
|
|
86
86
|
# Claude takes prompt via stdin
|
|
87
87
|
return command
|
|
88
|
-
|
|
88
|
+
|
|
89
89
|
elif self.cli_command == "openai":
|
|
90
90
|
# OpenAI CLI format
|
|
91
91
|
command.extend(["api", "chat.completions.create"])
|
|
@@ -93,14 +93,14 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
93
93
|
command.extend(["-m", model_config.full_name])
|
|
94
94
|
command.extend(["-g", "user", prompt])
|
|
95
95
|
return command
|
|
96
|
-
|
|
96
|
+
|
|
97
97
|
elif self.cli_command in ["gemini", "grok"]:
|
|
98
98
|
# Simple format: command --model MODEL prompt
|
|
99
99
|
if model_config:
|
|
100
100
|
command.extend(["--model", model_config.full_name])
|
|
101
101
|
command.append(prompt)
|
|
102
102
|
return command
|
|
103
|
-
|
|
103
|
+
|
|
104
104
|
elif self.cli_command == "openhands":
|
|
105
105
|
# OpenHands format
|
|
106
106
|
command.extend(["run", prompt])
|
|
@@ -109,7 +109,7 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
109
109
|
if self.config.working_dir:
|
|
110
110
|
command.extend(["--workspace", str(self.config.working_dir)])
|
|
111
111
|
return command
|
|
112
|
-
|
|
112
|
+
|
|
113
113
|
elif self.cli_command == "hanzo":
|
|
114
114
|
# Hanzo dev format
|
|
115
115
|
command.append("dev")
|
|
@@ -117,13 +117,13 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
117
117
|
command.extend(["--model", model_config.full_name])
|
|
118
118
|
command.extend(["--prompt", prompt])
|
|
119
119
|
return command
|
|
120
|
-
|
|
120
|
+
|
|
121
121
|
elif self.cli_command == "cline":
|
|
122
122
|
# Cline format
|
|
123
123
|
command.append(prompt)
|
|
124
124
|
command.append("--no-interactive")
|
|
125
125
|
return command
|
|
126
|
-
|
|
126
|
+
|
|
127
127
|
elif self.cli_command == "aider":
|
|
128
128
|
# Aider format
|
|
129
129
|
if model_config:
|
|
@@ -131,24 +131,24 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
131
131
|
command.extend(["--message", prompt])
|
|
132
132
|
command.extend(["--yes", "--no-stream"])
|
|
133
133
|
return command
|
|
134
|
-
|
|
134
|
+
|
|
135
135
|
elif self.cli_command == "ollama":
|
|
136
136
|
# Ollama format for local models
|
|
137
137
|
command.extend(["run", self.config.model.replace("ollama/", "")])
|
|
138
138
|
command.append(prompt)
|
|
139
139
|
return command
|
|
140
|
-
|
|
140
|
+
|
|
141
141
|
# Default format
|
|
142
142
|
command.append(prompt)
|
|
143
143
|
return command
|
|
144
|
-
|
|
144
|
+
|
|
145
145
|
async def call(self, ctx: Context[Any, Any, Any], **params: Any) -> str:
|
|
146
146
|
"""Execute the CLI tool via MCP interface.
|
|
147
|
-
|
|
147
|
+
|
|
148
148
|
Args:
|
|
149
149
|
ctx: MCP context
|
|
150
150
|
**params: Tool parameters
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
Returns:
|
|
153
153
|
Execution result
|
|
154
154
|
"""
|
|
@@ -159,23 +159,23 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
159
159
|
self.config.working_dir = Path(params["working_dir"])
|
|
160
160
|
if params.get("timeout"):
|
|
161
161
|
self.config.timeout = params["timeout"]
|
|
162
|
-
|
|
162
|
+
|
|
163
163
|
# Execute using base agent
|
|
164
164
|
result = await self.execute(
|
|
165
165
|
params.get("prompt", ""),
|
|
166
166
|
context=ctx,
|
|
167
167
|
)
|
|
168
|
-
|
|
168
|
+
|
|
169
169
|
return result.content
|
|
170
|
-
|
|
170
|
+
|
|
171
171
|
def register(self, mcp_server: FastMCP) -> None:
|
|
172
172
|
"""Register this tool with the MCP server.
|
|
173
|
-
|
|
173
|
+
|
|
174
174
|
Args:
|
|
175
175
|
mcp_server: The FastMCP server instance
|
|
176
176
|
"""
|
|
177
177
|
tool_self = self
|
|
178
|
-
|
|
178
|
+
|
|
179
179
|
@mcp_server.tool(name=self.name, description=self.description)
|
|
180
180
|
async def tool_wrapper(
|
|
181
181
|
prompt: str,
|
|
@@ -195,15 +195,15 @@ class UnifiedCLITool(BaseTool, CLIAgent):
|
|
|
195
195
|
|
|
196
196
|
def create_cli_tools(permission_manager: Optional[PermissionManager] = None) -> Dict[str, UnifiedCLITool]:
|
|
197
197
|
"""Create all CLI tools with unified implementation.
|
|
198
|
-
|
|
198
|
+
|
|
199
199
|
Args:
|
|
200
200
|
permission_manager: Permission manager for access control
|
|
201
|
-
|
|
201
|
+
|
|
202
202
|
Returns:
|
|
203
203
|
Dictionary of tool name to tool instance
|
|
204
204
|
"""
|
|
205
205
|
tools = {}
|
|
206
|
-
|
|
206
|
+
|
|
207
207
|
# Define all tools with their configurations
|
|
208
208
|
tool_configs = [
|
|
209
209
|
("claude", "Execute Claude CLI for AI assistance", "claude", "claude"),
|
|
@@ -217,7 +217,7 @@ def create_cli_tools(permission_manager: Optional[PermissionManager] = None) ->
|
|
|
217
217
|
("cline", "Execute Cline for autonomous coding", "cline", "claude"),
|
|
218
218
|
("aider", "Execute Aider for AI pair programming", "aider", "gpt-4-turbo"),
|
|
219
219
|
]
|
|
220
|
-
|
|
220
|
+
|
|
221
221
|
for name, description, cli_command, default_model in tool_configs:
|
|
222
222
|
tools[name] = UnifiedCLITool(
|
|
223
223
|
name=name,
|
|
@@ -226,7 +226,7 @@ def create_cli_tools(permission_manager: Optional[PermissionManager] = None) ->
|
|
|
226
226
|
default_model=default_model,
|
|
227
227
|
permission_manager=permission_manager,
|
|
228
228
|
)
|
|
229
|
-
|
|
229
|
+
|
|
230
230
|
return tools
|
|
231
231
|
|
|
232
232
|
|
|
@@ -235,20 +235,20 @@ def register_cli_tools(
|
|
|
235
235
|
permission_manager: Optional[PermissionManager] = None,
|
|
236
236
|
) -> List[BaseTool]:
|
|
237
237
|
"""Register all CLI tools with the MCP server.
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
Args:
|
|
240
240
|
mcp_server: The FastMCP server instance
|
|
241
241
|
permission_manager: Permission manager for access control
|
|
242
|
-
|
|
242
|
+
|
|
243
243
|
Returns:
|
|
244
244
|
List of registered CLI tools
|
|
245
245
|
"""
|
|
246
246
|
tools = create_cli_tools(permission_manager)
|
|
247
|
-
|
|
247
|
+
|
|
248
248
|
# Register each tool
|
|
249
249
|
for tool in tools.values():
|
|
250
250
|
tool.register(mcp_server)
|
|
251
|
-
|
|
251
|
+
|
|
252
252
|
return list(tools.values())
|
|
253
253
|
|
|
254
254
|
|
|
@@ -256,4 +256,4 @@ __all__ = [
|
|
|
256
256
|
"UnifiedCLITool",
|
|
257
257
|
"create_cli_tools",
|
|
258
258
|
"register_cli_tools",
|
|
259
|
-
]
|
|
259
|
+
]
|
|
@@ -177,30 +177,22 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
177
177
|
|
|
178
178
|
# Validate required parameters
|
|
179
179
|
if not description:
|
|
180
|
-
await tool_ctx.error(
|
|
181
|
-
"Parameter 'description' is required but was None or empty"
|
|
182
|
-
)
|
|
180
|
+
await tool_ctx.error("Parameter 'description' is required but was None or empty")
|
|
183
181
|
return "Error: Parameter 'description' is required but was None or empty"
|
|
184
182
|
|
|
185
183
|
if not invocations:
|
|
186
|
-
await tool_ctx.error(
|
|
187
|
-
"Parameter 'invocations' is required but was None or empty"
|
|
188
|
-
)
|
|
184
|
+
await tool_ctx.error("Parameter 'invocations' is required but was None or empty")
|
|
189
185
|
return "Error: Parameter 'invocations' is required but was None or empty"
|
|
190
186
|
|
|
191
187
|
if not isinstance(invocations, list) or len(invocations) == 0:
|
|
192
188
|
await tool_ctx.error("Parameter 'invocations' must be a non-empty list")
|
|
193
189
|
return "Error: Parameter 'invocations' must be a non-empty list"
|
|
194
190
|
|
|
195
|
-
await tool_ctx.info(
|
|
196
|
-
f"Executing batch operation: {description} ({len(invocations)} invocations)"
|
|
197
|
-
)
|
|
191
|
+
await tool_ctx.info(f"Executing batch operation: {description} ({len(invocations)} invocations)")
|
|
198
192
|
|
|
199
193
|
# Execute all tool invocations in parallel
|
|
200
194
|
tasks: list[asyncio.Future[dict[str, Any]]] = []
|
|
201
|
-
invocation_map: dict[asyncio.Future[dict[str, Any]], dict[str, Any]] =
|
|
202
|
-
{}
|
|
203
|
-
) # Map task Future to invocation
|
|
195
|
+
invocation_map: dict[asyncio.Future[dict[str, Any]], dict[str, Any]] = {} # Map task Future to invocation
|
|
204
196
|
|
|
205
197
|
for i, invocation in enumerate(invocations):
|
|
206
198
|
# Extract tool name and input from invocation
|
|
@@ -213,9 +205,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
213
205
|
await tool_ctx.error(error_message)
|
|
214
206
|
# Add direct result for this invocation
|
|
215
207
|
tasks.append(asyncio.Future())
|
|
216
|
-
tasks[-1].set_result(
|
|
217
|
-
{"invocation": invocation, "result": f"Error: {error_message}"}
|
|
218
|
-
)
|
|
208
|
+
tasks[-1].set_result({"invocation": invocation, "result": f"Error: {error_message}"})
|
|
219
209
|
invocation_map[tasks[-1]] = invocation
|
|
220
210
|
continue
|
|
221
211
|
|
|
@@ -225,9 +215,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
225
215
|
await tool_ctx.error(error_message)
|
|
226
216
|
# Add direct result for this invocation
|
|
227
217
|
tasks.append(asyncio.Future())
|
|
228
|
-
tasks[-1].set_result(
|
|
229
|
-
{"invocation": invocation, "result": f"Error: {error_message}"}
|
|
230
|
-
)
|
|
218
|
+
tasks[-1].set_result({"invocation": invocation, "result": f"Error: {error_message}"})
|
|
231
219
|
invocation_map[tasks[-1]] = invocation
|
|
232
220
|
continue
|
|
233
221
|
|
|
@@ -237,9 +225,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
237
225
|
await tool_ctx.info(f"Creating task for tool: {tool_name}")
|
|
238
226
|
|
|
239
227
|
# Create coroutine for this tool execution
|
|
240
|
-
async def execute_tool(
|
|
241
|
-
tool_obj: BaseTool, tool_name: str, tool_input: dict[str, Any]
|
|
242
|
-
):
|
|
228
|
+
async def execute_tool(tool_obj: BaseTool, tool_name: str, tool_input: dict[str, Any]):
|
|
243
229
|
try:
|
|
244
230
|
await tool_ctx.info(f"Executing tool: {tool_name}")
|
|
245
231
|
result = await tool_obj.call(ctx, **tool_input)
|
|
@@ -265,9 +251,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
265
251
|
await tool_ctx.error(error_message)
|
|
266
252
|
# Add direct result for this invocation
|
|
267
253
|
tasks.append(asyncio.Future())
|
|
268
|
-
tasks[-1].set_result(
|
|
269
|
-
{"invocation": invocation, "result": f"Error: {error_message}"}
|
|
270
|
-
)
|
|
254
|
+
tasks[-1].set_result({"invocation": invocation, "result": f"Error: {error_message}"})
|
|
271
255
|
invocation_map[tasks[-1]] = invocation
|
|
272
256
|
|
|
273
257
|
# Wait for all tasks to complete
|
|
@@ -284,9 +268,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
284
268
|
tool_name: str = invocation.get("tool_name", "unknown")
|
|
285
269
|
error_message = f"Unexpected error in tool '{tool_name}': {str(e)}"
|
|
286
270
|
await tool_ctx.error(error_message)
|
|
287
|
-
results.append(
|
|
288
|
-
{"invocation": invocation, "result": f"Error: {error_message}"}
|
|
289
|
-
)
|
|
271
|
+
results.append({"invocation": invocation, "result": f"Error: {error_message}"})
|
|
290
272
|
|
|
291
273
|
# Extract cursor if provided
|
|
292
274
|
cursor = params.get("cursor")
|
|
@@ -314,9 +296,7 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
314
296
|
)
|
|
315
297
|
|
|
316
298
|
# Create paginated response with token awareness
|
|
317
|
-
paginated_response = create_paginated_response(
|
|
318
|
-
formatted_results, cursor=cursor, use_token_limit=True
|
|
319
|
-
)
|
|
299
|
+
paginated_response = create_paginated_response(formatted_results, cursor=cursor, use_token_limit=True)
|
|
320
300
|
|
|
321
301
|
# Convert paginated response to string format for MCP
|
|
322
302
|
if isinstance(paginated_response, dict) and "items" in paginated_response:
|
|
@@ -326,13 +306,9 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
326
306
|
# Add header
|
|
327
307
|
result_parts.append(f"=== Batch operation: {description} ===")
|
|
328
308
|
result_parts.append(f"Total invocations: {len(invocations)}")
|
|
329
|
-
result_parts.append(
|
|
330
|
-
f"Showing results: {len(paginated_response['items'])} of {len(results)}"
|
|
331
|
-
)
|
|
309
|
+
result_parts.append(f"Showing results: {len(paginated_response['items'])} of {len(results)}")
|
|
332
310
|
if paginated_response.get("hasMore"):
|
|
333
|
-
result_parts.append(
|
|
334
|
-
f"More results available - use cursor: {paginated_response.get('nextCursor')}"
|
|
335
|
-
)
|
|
311
|
+
result_parts.append(f"More results available - use cursor: {paginated_response.get('nextCursor')}")
|
|
336
312
|
result_parts.append("")
|
|
337
313
|
|
|
338
314
|
# Format each result
|
|
@@ -352,12 +328,8 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
352
328
|
|
|
353
329
|
# If there's a next cursor, we need to preserve it in the response
|
|
354
330
|
# For now, append it as a note at the end
|
|
355
|
-
if paginated_response.get("hasMore") and paginated_response.get(
|
|
356
|
-
"nextCursor"
|
|
357
|
-
):
|
|
358
|
-
formatted_output += (
|
|
359
|
-
f"\n\n[To continue, use cursor: {paginated_response['nextCursor']}]"
|
|
360
|
-
)
|
|
331
|
+
if paginated_response.get("hasMore") and paginated_response.get("nextCursor"):
|
|
332
|
+
formatted_output += f"\n\n[To continue, use cursor: {paginated_response['nextCursor']}]"
|
|
361
333
|
|
|
362
334
|
await tool_ctx.info(
|
|
363
335
|
f"Batch operation '{description}' completed with {len(paginated_response['items'])} results"
|
|
@@ -424,6 +396,4 @@ Not available: think,write,edit,multi_edit,notebook_edit
|
|
|
424
396
|
cursor: Cursor,
|
|
425
397
|
ctx: MCPContext,
|
|
426
398
|
) -> str:
|
|
427
|
-
return await tool_self.call(
|
|
428
|
-
ctx, description=description, invocations=invocations, cursor=cursor
|
|
429
|
-
)
|
|
399
|
+
return await tool_self.call(ctx, description=description, invocations=invocations, cursor=cursor)
|