hanzo-mcp 0.8.11__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 +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 +10 -24
- hanzo_mcp/tools/agent/__init__.py +2 -1
- hanzo_mcp/tools/agent/agent.py +10 -30
- hanzo_mcp/tools/agent/agent_tool.py +5 -15
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +14 -41
- 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/swarm_tool.py +9 -29
- 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 +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/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 +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/knowledge_tools.py +7 -25
- hanzo_mcp/tools/memory/memory_tools.py +6 -18
- hanzo_mcp/tools/search/find_tool.py +10 -32
- hanzo_mcp/tools/search/unified_search.py +24 -78
- 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 +7 -27
- hanzo_mcp/tools/vector/mock_infinity.py +1 -3
- 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.11.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.11.dist-info/RECORD +0 -193
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.13.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.13.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.13.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.
|
|
@@ -191,9 +191,7 @@ class SwarmToolParams(TypedDict):
|
|
|
191
191
|
class SwarmState(State):
|
|
192
192
|
"""State for swarm execution."""
|
|
193
193
|
|
|
194
|
-
def __init__(
|
|
195
|
-
self, config: SwarmConfig, initial_query: str, context: Optional[str] = None
|
|
196
|
-
):
|
|
194
|
+
def __init__(self, config: SwarmConfig, initial_query: str, context: Optional[str] = None):
|
|
197
195
|
"""Initialize swarm state."""
|
|
198
196
|
super().__init__()
|
|
199
197
|
self.config = config
|
|
@@ -294,9 +292,7 @@ class SwarmAgent(MCPAgent):
|
|
|
294
292
|
# Default to anthropic
|
|
295
293
|
return f"model://anthropic/{model}"
|
|
296
294
|
|
|
297
|
-
async def run(
|
|
298
|
-
self, state: SwarmState, history: History, network: Network
|
|
299
|
-
) -> InferenceResult:
|
|
295
|
+
async def run(self, state: SwarmState, history: History, network: Network) -> InferenceResult:
|
|
300
296
|
"""Execute the swarm agent."""
|
|
301
297
|
# Build prompt with context
|
|
302
298
|
prompt_parts = []
|
|
@@ -418,9 +414,7 @@ class SwarmRouter(DeterministicRouter):
|
|
|
418
414
|
# No more agents to execute
|
|
419
415
|
return None
|
|
420
416
|
|
|
421
|
-
def _get_agent_class(
|
|
422
|
-
self, agent_id: str, agent_stack: List[type[Agent]]
|
|
423
|
-
) -> type[Agent]:
|
|
417
|
+
def _get_agent_class(self, agent_id: str, agent_stack: List[type[Agent]]) -> type[Agent]:
|
|
424
418
|
"""Get agent class for given agent ID."""
|
|
425
419
|
# Find matching agent by name
|
|
426
420
|
for agent_class in agent_stack:
|
|
@@ -495,11 +489,7 @@ Models can be specified as:
|
|
|
495
489
|
from hanzo_mcp.tools.agent.code_auth import get_latest_claude_model
|
|
496
490
|
|
|
497
491
|
self.model = model or f"anthropic/{get_latest_claude_model()}"
|
|
498
|
-
self.api_key = (
|
|
499
|
-
api_key
|
|
500
|
-
or os.environ.get("ANTHROPIC_API_KEY")
|
|
501
|
-
or os.environ.get("CLAUDE_API_KEY")
|
|
502
|
-
)
|
|
492
|
+
self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY")
|
|
503
493
|
self.base_url = base_url
|
|
504
494
|
self.max_tokens = max_tokens
|
|
505
495
|
self.agent_max_iterations = agent_max_iterations
|
|
@@ -507,21 +497,15 @@ Models can be specified as:
|
|
|
507
497
|
|
|
508
498
|
# Set up available tools for agents
|
|
509
499
|
self.available_tools: list[BaseTool] = []
|
|
510
|
-
self.available_tools.extend(
|
|
511
|
-
|
|
512
|
-
)
|
|
513
|
-
self.available_tools.extend(
|
|
514
|
-
get_read_only_jupyter_tools(self.permission_manager)
|
|
515
|
-
)
|
|
500
|
+
self.available_tools.extend(get_read_only_filesystem_tools(self.permission_manager))
|
|
501
|
+
self.available_tools.extend(get_read_only_jupyter_tools(self.permission_manager))
|
|
516
502
|
|
|
517
503
|
# Add edit tools
|
|
518
504
|
self.available_tools.append(Edit(self.permission_manager))
|
|
519
505
|
self.available_tools.append(MultiEdit(self.permission_manager))
|
|
520
506
|
|
|
521
507
|
# Add batch tool
|
|
522
|
-
self.available_tools.append(
|
|
523
|
-
BatchTool({t.name: t for t in self.available_tools})
|
|
524
|
-
)
|
|
508
|
+
self.available_tools.append(BatchTool({t.name: t for t in self.available_tools}))
|
|
525
509
|
|
|
526
510
|
@override
|
|
527
511
|
async def call(
|
|
@@ -549,9 +533,7 @@ Models can be specified as:
|
|
|
549
533
|
|
|
550
534
|
# hanzo-agents SDK is required (already imported above)
|
|
551
535
|
|
|
552
|
-
await tool_ctx.info(
|
|
553
|
-
f"Starting swarm execution with {len(agents_config)} agents using hanzo-agents SDK"
|
|
554
|
-
)
|
|
536
|
+
await tool_ctx.info(f"Starting swarm execution with {len(agents_config)} agents using hanzo-agents SDK")
|
|
555
537
|
|
|
556
538
|
# Create state
|
|
557
539
|
state = SwarmState(config=config, initial_query=initial_query, context=context)
|
|
@@ -648,9 +630,7 @@ Models can be specified as:
|
|
|
648
630
|
output.append("=" * 80)
|
|
649
631
|
output.append(f"Total agents: {len(agents_config)}")
|
|
650
632
|
output.append(f"Completed: {len(results)}")
|
|
651
|
-
output.append(
|
|
652
|
-
f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}"
|
|
653
|
-
)
|
|
633
|
+
output.append(f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}")
|
|
654
634
|
|
|
655
635
|
if entry_point:
|
|
656
636
|
output.append(f"Entry point: {entry_point}")
|
|
@@ -196,11 +196,7 @@ Models can be specified as:
|
|
|
196
196
|
from hanzo_mcp.tools.agent.code_auth import get_latest_claude_model
|
|
197
197
|
|
|
198
198
|
self.model = model or f"anthropic/{get_latest_claude_model()}"
|
|
199
|
-
self.api_key = (
|
|
200
|
-
api_key
|
|
201
|
-
or os.environ.get("ANTHROPIC_API_KEY")
|
|
202
|
-
or os.environ.get("CLAUDE_API_KEY")
|
|
203
|
-
)
|
|
199
|
+
self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY")
|
|
204
200
|
self.base_url = base_url
|
|
205
201
|
self.max_tokens = max_tokens
|
|
206
202
|
self.agent_max_iterations = agent_max_iterations
|
|
@@ -243,9 +239,7 @@ Models can be specified as:
|
|
|
243
239
|
agents_config = config.get("agents", {})
|
|
244
240
|
entry_point = config.get("entry_point")
|
|
245
241
|
|
|
246
|
-
await tool_ctx.info(
|
|
247
|
-
f"Starting swarm execution with {len(agents_config)} agents"
|
|
248
|
-
)
|
|
242
|
+
await tool_ctx.info(f"Starting swarm execution with {len(agents_config)} agents")
|
|
249
243
|
|
|
250
244
|
# Build agent network
|
|
251
245
|
agent_instances = {}
|
|
@@ -292,10 +286,7 @@ Models can be specified as:
|
|
|
292
286
|
# Check if any other agent connects to this one
|
|
293
287
|
has_incoming = False
|
|
294
288
|
for other_config in agents_config.values():
|
|
295
|
-
if (
|
|
296
|
-
other_config.get("connections")
|
|
297
|
-
and agent_id in other_config["connections"]
|
|
298
|
-
):
|
|
289
|
+
if other_config.get("connections") and agent_id in other_config["connections"]:
|
|
299
290
|
has_incoming = True
|
|
300
291
|
break
|
|
301
292
|
if not has_incoming:
|
|
@@ -309,18 +300,14 @@ Models can be specified as:
|
|
|
309
300
|
await execution_queue.put((root, initial_query, {}))
|
|
310
301
|
|
|
311
302
|
# Execute agents in network order
|
|
312
|
-
async def execute_agent(
|
|
313
|
-
agent_id: str, query: str, inputs: Dict[str, str]
|
|
314
|
-
) -> str:
|
|
303
|
+
async def execute_agent(agent_id: str, query: str, inputs: Dict[str, str]) -> str:
|
|
315
304
|
"""Execute a single agent in the network."""
|
|
316
305
|
async with semaphore:
|
|
317
306
|
try:
|
|
318
307
|
agent_config = agents_config[agent_id]
|
|
319
308
|
agent = agent_instances[agent_id]
|
|
320
309
|
|
|
321
|
-
await tool_ctx.info(
|
|
322
|
-
f"Executing agent: {agent_id} ({agent_config.get('role', 'Agent')})"
|
|
323
|
-
)
|
|
310
|
+
await tool_ctx.info(f"Executing agent: {agent_id} ({agent_config.get('role', 'Agent')})")
|
|
324
311
|
|
|
325
312
|
# Build prompt with context and inputs
|
|
326
313
|
prompt_parts = []
|
|
@@ -337,15 +324,11 @@ Models can be specified as:
|
|
|
337
324
|
if inputs:
|
|
338
325
|
prompt_parts.append("Input from previous agents:")
|
|
339
326
|
for input_agent, input_result in inputs.items():
|
|
340
|
-
prompt_parts.append(
|
|
341
|
-
f"\n--- From {input_agent} ---\n{input_result}"
|
|
342
|
-
)
|
|
327
|
+
prompt_parts.append(f"\n--- From {input_agent} ---\n{input_result}")
|
|
343
328
|
|
|
344
329
|
# Add file context if specified
|
|
345
330
|
if agent_config.get("file_path"):
|
|
346
|
-
prompt_parts.append(
|
|
347
|
-
f"\nFile to work on: {agent_config['file_path']}"
|
|
348
|
-
)
|
|
331
|
+
prompt_parts.append(f"\nFile to work on: {agent_config['file_path']}")
|
|
349
332
|
|
|
350
333
|
# Add the main query
|
|
351
334
|
prompt_parts.append(f"\nTask: {agent_config['query']}")
|
|
@@ -393,9 +376,7 @@ Models can be specified as:
|
|
|
393
376
|
|
|
394
377
|
if ready:
|
|
395
378
|
# Execute agent
|
|
396
|
-
task = asyncio.create_task(
|
|
397
|
-
execute_agent(agent_id, query, inputs)
|
|
398
|
-
)
|
|
379
|
+
task = asyncio.create_task(execute_agent(agent_id, query, inputs))
|
|
399
380
|
running_tasks.add(task)
|
|
400
381
|
|
|
401
382
|
async def handle_completion(task, agent_id=agent_id):
|
|
@@ -409,9 +390,7 @@ Models can be specified as:
|
|
|
409
390
|
connections = agent_config.get("connections", [])
|
|
410
391
|
for next_agent in connections:
|
|
411
392
|
if next_agent in agents_config:
|
|
412
|
-
await execution_queue.put(
|
|
413
|
-
(next_agent, "", {agent_id: result})
|
|
414
|
-
)
|
|
393
|
+
await execution_queue.put((next_agent, "", {agent_id: result}))
|
|
415
394
|
|
|
416
395
|
asyncio.create_task(handle_completion(task))
|
|
417
396
|
|
|
@@ -470,9 +449,7 @@ Models can be specified as:
|
|
|
470
449
|
output.append("=" * 80)
|
|
471
450
|
output.append(f"Total agents: {len(agents_config)}")
|
|
472
451
|
output.append(f"Completed: {len(results)}")
|
|
473
|
-
output.append(
|
|
474
|
-
f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}"
|
|
475
|
-
)
|
|
452
|
+
output.append(f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}")
|
|
476
453
|
|
|
477
454
|
if entry_point:
|
|
478
455
|
output.append(f"Entry point: {entry_point}")
|
|
@@ -490,12 +467,7 @@ Models can be specified as:
|
|
|
490
467
|
role = config.get("role", "Agent")
|
|
491
468
|
model = config.get("model", "default")
|
|
492
469
|
|
|
493
|
-
status = (
|
|
494
|
-
"✅"
|
|
495
|
-
if agent_id in results
|
|
496
|
-
and not results[agent_id].startswith("Error:")
|
|
497
|
-
else "❌"
|
|
498
|
-
)
|
|
470
|
+
status = "✅" if agent_id in results and not results[agent_id].startswith("Error:") else "❌"
|
|
499
471
|
lines.append(f"{indent}{status} {agent_id} ({role}) [{model}]")
|
|
500
472
|
|
|
501
473
|
# Show connections
|