hanzo-mcp 0.7.7__py3-none-any.whl → 0.8.1__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 +6 -0
- hanzo_mcp/__main__.py +1 -1
- hanzo_mcp/analytics/__init__.py +2 -2
- hanzo_mcp/analytics/posthog_analytics.py +76 -82
- hanzo_mcp/cli.py +31 -36
- hanzo_mcp/cli_enhanced.py +94 -72
- hanzo_mcp/cli_plugin.py +27 -17
- hanzo_mcp/config/__init__.py +2 -2
- hanzo_mcp/config/settings.py +112 -88
- hanzo_mcp/config/tool_config.py +32 -34
- hanzo_mcp/dev_server.py +66 -67
- hanzo_mcp/prompts/__init__.py +94 -12
- hanzo_mcp/prompts/enhanced_prompts.py +809 -0
- hanzo_mcp/prompts/example_custom_prompt.py +6 -5
- hanzo_mcp/prompts/project_todo_reminder.py +0 -1
- hanzo_mcp/prompts/tool_explorer.py +10 -7
- hanzo_mcp/server.py +17 -21
- hanzo_mcp/server_enhanced.py +15 -22
- hanzo_mcp/tools/__init__.py +56 -28
- hanzo_mcp/tools/agent/__init__.py +16 -19
- hanzo_mcp/tools/agent/agent.py +82 -65
- hanzo_mcp/tools/agent/agent_tool.py +152 -122
- hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +66 -62
- hanzo_mcp/tools/agent/clarification_protocol.py +55 -50
- hanzo_mcp/tools/agent/clarification_tool.py +11 -10
- hanzo_mcp/tools/agent/claude_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/claude_desktop_auth.py +130 -144
- hanzo_mcp/tools/agent/cli_agent_base.py +59 -53
- hanzo_mcp/tools/agent/code_auth.py +102 -107
- hanzo_mcp/tools/agent/code_auth_tool.py +28 -27
- hanzo_mcp/tools/agent/codex_cli_tool.py +20 -19
- hanzo_mcp/tools/agent/critic_tool.py +86 -73
- hanzo_mcp/tools/agent/gemini_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/grok_cli_tool.py +21 -20
- hanzo_mcp/tools/agent/iching_tool.py +404 -139
- hanzo_mcp/tools/agent/network_tool.py +89 -73
- hanzo_mcp/tools/agent/prompt.py +2 -1
- hanzo_mcp/tools/agent/review_tool.py +101 -98
- hanzo_mcp/tools/agent/swarm_alias.py +87 -0
- hanzo_mcp/tools/agent/swarm_tool.py +246 -161
- hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +134 -92
- hanzo_mcp/tools/agent/tool_adapter.py +21 -11
- hanzo_mcp/tools/common/__init__.py +1 -1
- hanzo_mcp/tools/common/base.py +3 -5
- hanzo_mcp/tools/common/batch_tool.py +46 -39
- hanzo_mcp/tools/common/config_tool.py +120 -84
- hanzo_mcp/tools/common/context.py +1 -5
- hanzo_mcp/tools/common/context_fix.py +5 -3
- hanzo_mcp/tools/common/critic_tool.py +4 -8
- hanzo_mcp/tools/common/decorators.py +58 -56
- hanzo_mcp/tools/common/enhanced_base.py +29 -32
- hanzo_mcp/tools/common/fastmcp_pagination.py +91 -94
- hanzo_mcp/tools/common/forgiving_edit.py +91 -87
- hanzo_mcp/tools/common/mode.py +15 -17
- hanzo_mcp/tools/common/mode_loader.py +27 -24
- hanzo_mcp/tools/common/paginated_base.py +61 -53
- hanzo_mcp/tools/common/paginated_response.py +72 -79
- hanzo_mcp/tools/common/pagination.py +50 -53
- hanzo_mcp/tools/common/permissions.py +4 -4
- hanzo_mcp/tools/common/personality.py +186 -138
- hanzo_mcp/tools/common/plugin_loader.py +54 -54
- hanzo_mcp/tools/common/stats.py +65 -47
- hanzo_mcp/tools/common/test_helpers.py +31 -0
- hanzo_mcp/tools/common/thinking_tool.py +4 -8
- hanzo_mcp/tools/common/tool_disable.py +17 -12
- hanzo_mcp/tools/common/tool_enable.py +13 -14
- hanzo_mcp/tools/common/tool_list.py +36 -28
- hanzo_mcp/tools/common/truncate.py +23 -23
- hanzo_mcp/tools/config/__init__.py +4 -4
- hanzo_mcp/tools/config/config_tool.py +42 -29
- hanzo_mcp/tools/config/index_config.py +37 -34
- hanzo_mcp/tools/config/mode_tool.py +175 -55
- hanzo_mcp/tools/database/__init__.py +15 -12
- hanzo_mcp/tools/database/database_manager.py +77 -75
- hanzo_mcp/tools/database/graph.py +137 -91
- hanzo_mcp/tools/database/graph_add.py +30 -18
- hanzo_mcp/tools/database/graph_query.py +178 -102
- hanzo_mcp/tools/database/graph_remove.py +33 -28
- hanzo_mcp/tools/database/graph_search.py +97 -75
- hanzo_mcp/tools/database/graph_stats.py +91 -59
- hanzo_mcp/tools/database/sql.py +107 -79
- hanzo_mcp/tools/database/sql_query.py +30 -24
- hanzo_mcp/tools/database/sql_search.py +29 -25
- hanzo_mcp/tools/database/sql_stats.py +47 -35
- hanzo_mcp/tools/editor/neovim_command.py +25 -28
- hanzo_mcp/tools/editor/neovim_edit.py +21 -23
- hanzo_mcp/tools/editor/neovim_session.py +60 -54
- hanzo_mcp/tools/filesystem/__init__.py +31 -30
- hanzo_mcp/tools/filesystem/ast_multi_edit.py +329 -249
- hanzo_mcp/tools/filesystem/ast_tool.py +4 -4
- hanzo_mcp/tools/filesystem/base.py +1 -1
- hanzo_mcp/tools/filesystem/batch_search.py +316 -224
- hanzo_mcp/tools/filesystem/content_replace.py +4 -4
- hanzo_mcp/tools/filesystem/diff.py +71 -59
- hanzo_mcp/tools/filesystem/directory_tree.py +7 -7
- hanzo_mcp/tools/filesystem/directory_tree_paginated.py +49 -37
- hanzo_mcp/tools/filesystem/edit.py +4 -4
- hanzo_mcp/tools/filesystem/find.py +173 -80
- hanzo_mcp/tools/filesystem/find_files.py +73 -52
- hanzo_mcp/tools/filesystem/git_search.py +157 -104
- hanzo_mcp/tools/filesystem/grep.py +8 -8
- hanzo_mcp/tools/filesystem/multi_edit.py +4 -8
- hanzo_mcp/tools/filesystem/read.py +12 -10
- hanzo_mcp/tools/filesystem/rules_tool.py +59 -43
- hanzo_mcp/tools/filesystem/search_tool.py +263 -207
- hanzo_mcp/tools/filesystem/symbols_tool.py +94 -54
- hanzo_mcp/tools/filesystem/tree.py +35 -33
- hanzo_mcp/tools/filesystem/unix_aliases.py +13 -18
- hanzo_mcp/tools/filesystem/watch.py +37 -36
- hanzo_mcp/tools/filesystem/write.py +4 -8
- hanzo_mcp/tools/jupyter/__init__.py +4 -4
- hanzo_mcp/tools/jupyter/base.py +4 -5
- hanzo_mcp/tools/jupyter/jupyter.py +67 -47
- hanzo_mcp/tools/jupyter/notebook_edit.py +4 -4
- hanzo_mcp/tools/jupyter/notebook_read.py +4 -7
- hanzo_mcp/tools/llm/__init__.py +5 -7
- hanzo_mcp/tools/llm/consensus_tool.py +72 -52
- hanzo_mcp/tools/llm/llm_manage.py +101 -60
- hanzo_mcp/tools/llm/llm_tool.py +226 -166
- hanzo_mcp/tools/llm/provider_tools.py +25 -26
- hanzo_mcp/tools/lsp/__init__.py +1 -1
- hanzo_mcp/tools/lsp/lsp_tool.py +228 -143
- hanzo_mcp/tools/mcp/__init__.py +2 -3
- hanzo_mcp/tools/mcp/mcp_add.py +27 -25
- hanzo_mcp/tools/mcp/mcp_remove.py +7 -8
- hanzo_mcp/tools/mcp/mcp_stats.py +23 -22
- hanzo_mcp/tools/mcp/mcp_tool.py +129 -98
- hanzo_mcp/tools/memory/__init__.py +39 -21
- hanzo_mcp/tools/memory/knowledge_tools.py +124 -99
- hanzo_mcp/tools/memory/memory_tools.py +90 -108
- hanzo_mcp/tools/search/__init__.py +7 -2
- hanzo_mcp/tools/search/find_tool.py +297 -212
- hanzo_mcp/tools/search/unified_search.py +366 -314
- hanzo_mcp/tools/shell/__init__.py +8 -7
- hanzo_mcp/tools/shell/auto_background.py +56 -49
- hanzo_mcp/tools/shell/base.py +1 -1
- hanzo_mcp/tools/shell/base_process.py +75 -75
- hanzo_mcp/tools/shell/bash_session.py +2 -2
- hanzo_mcp/tools/shell/bash_session_executor.py +4 -4
- hanzo_mcp/tools/shell/bash_tool.py +24 -31
- hanzo_mcp/tools/shell/command_executor.py +12 -12
- hanzo_mcp/tools/shell/logs.py +43 -33
- hanzo_mcp/tools/shell/npx.py +13 -13
- hanzo_mcp/tools/shell/npx_background.py +24 -21
- hanzo_mcp/tools/shell/npx_tool.py +18 -22
- hanzo_mcp/tools/shell/open.py +19 -21
- hanzo_mcp/tools/shell/pkill.py +31 -26
- hanzo_mcp/tools/shell/process_tool.py +32 -32
- hanzo_mcp/tools/shell/processes.py +57 -58
- hanzo_mcp/tools/shell/run_background.py +24 -25
- hanzo_mcp/tools/shell/run_command.py +5 -5
- hanzo_mcp/tools/shell/run_command_windows.py +5 -5
- hanzo_mcp/tools/shell/session_storage.py +3 -3
- hanzo_mcp/tools/shell/streaming_command.py +141 -126
- hanzo_mcp/tools/shell/uvx.py +24 -25
- hanzo_mcp/tools/shell/uvx_background.py +35 -33
- hanzo_mcp/tools/shell/uvx_tool.py +18 -22
- hanzo_mcp/tools/todo/__init__.py +6 -2
- hanzo_mcp/tools/todo/todo.py +50 -37
- hanzo_mcp/tools/todo/todo_read.py +5 -8
- hanzo_mcp/tools/todo/todo_write.py +5 -7
- hanzo_mcp/tools/vector/__init__.py +40 -28
- hanzo_mcp/tools/vector/ast_analyzer.py +176 -143
- hanzo_mcp/tools/vector/git_ingester.py +170 -179
- hanzo_mcp/tools/vector/index_tool.py +96 -44
- hanzo_mcp/tools/vector/infinity_store.py +283 -228
- hanzo_mcp/tools/vector/mock_infinity.py +39 -40
- hanzo_mcp/tools/vector/project_manager.py +88 -78
- hanzo_mcp/tools/vector/vector.py +59 -42
- hanzo_mcp/tools/vector/vector_index.py +30 -27
- hanzo_mcp/tools/vector/vector_search.py +64 -45
- hanzo_mcp/types.py +6 -4
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/METADATA +1 -1
- hanzo_mcp-0.8.1.dist-info/RECORD +185 -0
- hanzo_mcp-0.7.7.dist-info/RECORD +0 -182
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/WHEEL +0 -0
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/entry_points.txt +0 -0
- {hanzo_mcp-0.7.7.dist-info → hanzo_mcp-0.8.1.dist-info}/top_level.txt +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"""Review tool for agents to request balanced code review from main loop."""
|
|
2
2
|
|
|
3
|
-
import json
|
|
4
|
-
from typing import Any, Dict, List, Optional, override
|
|
5
3
|
from enum import Enum
|
|
4
|
+
from typing import List, Optional, override
|
|
6
5
|
|
|
7
|
-
from hanzo_mcp.tools.common.base import BaseTool
|
|
8
|
-
from mcp.server.fastmcp import Context as MCPContext
|
|
9
6
|
from mcp.server import FastMCP
|
|
7
|
+
from mcp.server.fastmcp import Context as MCPContext
|
|
8
|
+
|
|
9
|
+
from hanzo_mcp.tools.common.base import BaseTool
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class ReviewFocus(Enum):
|
|
13
13
|
"""Types of review focus areas."""
|
|
14
|
+
|
|
14
15
|
GENERAL = "general"
|
|
15
16
|
FUNCTIONALITY = "functionality"
|
|
16
17
|
READABILITY = "readability"
|
|
@@ -22,9 +23,9 @@ class ReviewFocus(Enum):
|
|
|
22
23
|
|
|
23
24
|
class ReviewTool(BaseTool):
|
|
24
25
|
"""Tool for agents to request balanced code review from the main loop."""
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
name = "review"
|
|
27
|
-
|
|
28
|
+
|
|
28
29
|
@property
|
|
29
30
|
@override
|
|
30
31
|
def description(self) -> str:
|
|
@@ -55,7 +56,7 @@ review(
|
|
|
55
56
|
file_paths=["/path/to/import_handler.go"],
|
|
56
57
|
context="This will be used to automatically fix missing imports in Go files"
|
|
57
58
|
)"""
|
|
58
|
-
|
|
59
|
+
|
|
59
60
|
async def call(
|
|
60
61
|
self,
|
|
61
62
|
ctx: MCPContext,
|
|
@@ -63,16 +64,16 @@ review(
|
|
|
63
64
|
work_description: str,
|
|
64
65
|
code_snippets: Optional[List[str]] = None,
|
|
65
66
|
file_paths: Optional[List[str]] = None,
|
|
66
|
-
context: Optional[str] = None
|
|
67
|
+
context: Optional[str] = None,
|
|
67
68
|
) -> str:
|
|
68
69
|
"""This is a placeholder - actual implementation happens in AgentTool."""
|
|
69
70
|
# This tool is handled specially in the agent execution
|
|
70
71
|
return f"Review requested for: {work_description}"
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
def register(self, server: FastMCP) -> None:
|
|
73
74
|
"""Register the tool with the MCP server."""
|
|
74
75
|
tool_self = self
|
|
75
|
-
|
|
76
|
+
|
|
76
77
|
@server.tool(name=self.name, description=self.description)
|
|
77
78
|
async def review(
|
|
78
79
|
ctx: MCPContext,
|
|
@@ -80,21 +81,16 @@ review(
|
|
|
80
81
|
work_description: str,
|
|
81
82
|
code_snippets: Optional[List[str]] = None,
|
|
82
83
|
file_paths: Optional[List[str]] = None,
|
|
83
|
-
context: Optional[str] = None
|
|
84
|
+
context: Optional[str] = None,
|
|
84
85
|
) -> str:
|
|
85
86
|
return await tool_self.call(
|
|
86
|
-
ctx,
|
|
87
|
-
focus,
|
|
88
|
-
work_description,
|
|
89
|
-
code_snippets,
|
|
90
|
-
file_paths,
|
|
91
|
-
context
|
|
87
|
+
ctx, focus, work_description, code_snippets, file_paths, context
|
|
92
88
|
)
|
|
93
89
|
|
|
94
90
|
|
|
95
91
|
class BalancedReviewer:
|
|
96
92
|
"""Provides balanced, constructive code reviews."""
|
|
97
|
-
|
|
93
|
+
|
|
98
94
|
def __init__(self):
|
|
99
95
|
self.review_handlers = {
|
|
100
96
|
ReviewFocus.GENERAL: self._review_general,
|
|
@@ -105,30 +101,30 @@ class BalancedReviewer:
|
|
|
105
101
|
ReviewFocus.DOCUMENTATION: self._review_documentation,
|
|
106
102
|
ReviewFocus.ARCHITECTURE: self._review_architecture,
|
|
107
103
|
}
|
|
108
|
-
|
|
104
|
+
|
|
109
105
|
def review(
|
|
110
106
|
self,
|
|
111
107
|
focus: ReviewFocus,
|
|
112
108
|
work_description: str,
|
|
113
109
|
code_snippets: Optional[List[str]] = None,
|
|
114
110
|
file_paths: Optional[List[str]] = None,
|
|
115
|
-
context: Optional[str] = None
|
|
111
|
+
context: Optional[str] = None,
|
|
116
112
|
) -> str:
|
|
117
113
|
"""Perform a balanced code review."""
|
|
118
114
|
review_func = self.review_handlers.get(focus, self._review_general)
|
|
119
115
|
return review_func(work_description, code_snippets, file_paths, context)
|
|
120
|
-
|
|
116
|
+
|
|
121
117
|
def _review_general(
|
|
122
118
|
self,
|
|
123
119
|
work_description: str,
|
|
124
120
|
code_snippets: Optional[List[str]],
|
|
125
121
|
file_paths: Optional[List[str]],
|
|
126
|
-
context: Optional[str]
|
|
122
|
+
context: Optional[str],
|
|
127
123
|
) -> str:
|
|
128
124
|
"""Provide a general balanced review."""
|
|
129
125
|
response = "📋 GENERAL CODE REVIEW:\n\n"
|
|
130
126
|
response += f"**Work Reviewed:** {work_description}\n\n"
|
|
131
|
-
|
|
127
|
+
|
|
132
128
|
# Positive observations
|
|
133
129
|
response += "**Positive Aspects:**\n"
|
|
134
130
|
if "fix" in work_description.lower():
|
|
@@ -141,293 +137,300 @@ class BalancedReviewer:
|
|
|
141
137
|
response += "✓ Focused changes in a single file (good for reviewability)\n"
|
|
142
138
|
elif file_paths and len(file_paths) > 1:
|
|
143
139
|
response += "✓ Comprehensive approach across multiple files\n"
|
|
144
|
-
|
|
140
|
+
|
|
145
141
|
# Constructive suggestions
|
|
146
142
|
response += "\n**Suggestions for Consideration:**\n"
|
|
147
143
|
response += "• Ensure all edge cases are handled appropriately\n"
|
|
148
144
|
response += "• Consider adding unit tests if not already present\n"
|
|
149
145
|
response += "• Verify the changes integrate well with existing code\n"
|
|
150
|
-
|
|
146
|
+
|
|
151
147
|
if context:
|
|
152
148
|
response += f"\n**Context Consideration:**\n{context}\n"
|
|
153
149
|
response += "→ This context helps understand the implementation choices.\n"
|
|
154
|
-
|
|
150
|
+
|
|
155
151
|
# Summary
|
|
156
152
|
response += "\n**Summary:**\n"
|
|
157
153
|
response += "The implementation appears sound. Consider the suggestions above to further strengthen the code."
|
|
158
|
-
|
|
154
|
+
|
|
159
155
|
return response
|
|
160
|
-
|
|
156
|
+
|
|
161
157
|
def _review_functionality(
|
|
162
158
|
self,
|
|
163
159
|
work_description: str,
|
|
164
160
|
code_snippets: Optional[List[str]],
|
|
165
161
|
file_paths: Optional[List[str]],
|
|
166
|
-
context: Optional[str]
|
|
162
|
+
context: Optional[str],
|
|
167
163
|
) -> str:
|
|
168
164
|
"""Review functionality aspects."""
|
|
169
165
|
response = "📋 FUNCTIONALITY REVIEW:\n\n"
|
|
170
166
|
response += f"**Implementation:** {work_description}\n\n"
|
|
171
|
-
|
|
167
|
+
|
|
172
168
|
response += "**Functional Assessment:**\n"
|
|
173
|
-
|
|
169
|
+
|
|
174
170
|
# Analyze code snippets if provided
|
|
175
171
|
if code_snippets:
|
|
176
172
|
for i, snippet in enumerate(code_snippets, 1):
|
|
177
173
|
response += f"\nCode Snippet {i}:\n"
|
|
178
|
-
|
|
174
|
+
|
|
179
175
|
# Check for function definitions
|
|
180
176
|
if "func " in snippet or "def " in snippet or "function " in snippet:
|
|
181
177
|
response += "✓ Function definition looks properly structured\n"
|
|
182
|
-
|
|
178
|
+
|
|
183
179
|
# Check for error handling
|
|
184
180
|
if "error" in snippet or "err" in snippet or "try" in snippet:
|
|
185
181
|
response += "✓ Error handling is present\n"
|
|
186
182
|
elif "return" in snippet:
|
|
187
183
|
response += "• Consider adding error handling if applicable\n"
|
|
188
|
-
|
|
184
|
+
|
|
189
185
|
# Check for input validation
|
|
190
186
|
if "if " in snippet or "check" in snippet.lower():
|
|
191
187
|
response += "✓ Input validation appears to be present\n"
|
|
192
|
-
|
|
188
|
+
|
|
193
189
|
response += "\n**Functional Considerations:**\n"
|
|
194
190
|
response += "• Does the implementation handle all expected inputs?\n"
|
|
195
191
|
response += "• Are return values meaningful and consistent?\n"
|
|
196
192
|
response += "• Is the functionality easily testable?\n"
|
|
197
193
|
response += "• Does it integrate well with existing features?\n"
|
|
198
|
-
|
|
194
|
+
|
|
199
195
|
response += "\n**Overall:** The functionality appears to meet the described requirements."
|
|
200
|
-
|
|
196
|
+
|
|
201
197
|
return response
|
|
202
|
-
|
|
198
|
+
|
|
203
199
|
def _review_readability(
|
|
204
200
|
self,
|
|
205
201
|
work_description: str,
|
|
206
202
|
code_snippets: Optional[List[str]],
|
|
207
203
|
file_paths: Optional[List[str]],
|
|
208
|
-
context: Optional[str]
|
|
204
|
+
context: Optional[str],
|
|
209
205
|
) -> str:
|
|
210
206
|
"""Review code readability."""
|
|
211
207
|
response = "📋 READABILITY REVIEW:\n\n"
|
|
212
|
-
|
|
208
|
+
|
|
213
209
|
response += "**Readability Factors:**\n"
|
|
214
|
-
|
|
210
|
+
|
|
215
211
|
if code_snippets:
|
|
216
|
-
total_lines = sum(snippet.count(
|
|
217
|
-
avg_line_length = sum(
|
|
218
|
-
|
|
212
|
+
total_lines = sum(snippet.count("\n") + 1 for snippet in code_snippets)
|
|
213
|
+
avg_line_length = sum(
|
|
214
|
+
len(line) for snippet in code_snippets for line in snippet.split("\n")
|
|
215
|
+
) / max(total_lines, 1)
|
|
216
|
+
|
|
219
217
|
if avg_line_length < 80:
|
|
220
218
|
response += "✓ Line lengths are reasonable\n"
|
|
221
219
|
else:
|
|
222
|
-
response +=
|
|
223
|
-
|
|
220
|
+
response += (
|
|
221
|
+
"• Some lines might be too long, consider breaking them up\n"
|
|
222
|
+
)
|
|
223
|
+
|
|
224
224
|
# Check naming
|
|
225
225
|
has_good_names = any(
|
|
226
|
-
any(
|
|
226
|
+
any(
|
|
227
|
+
word in snippet
|
|
228
|
+
for word in ["Add", "Get", "Set", "Create", "Update", "Delete"]
|
|
229
|
+
)
|
|
227
230
|
for snippet in code_snippets
|
|
228
231
|
)
|
|
229
232
|
if has_good_names:
|
|
230
233
|
response += "✓ Function/method names appear descriptive\n"
|
|
231
|
-
|
|
234
|
+
|
|
232
235
|
response += "\n**Readability Suggestions:**\n"
|
|
233
236
|
response += "• Use meaningful variable and function names\n"
|
|
234
237
|
response += "• Keep functions focused on a single responsibility\n"
|
|
235
238
|
response += "• Add comments for complex logic sections\n"
|
|
236
239
|
response += "• Maintain consistent indentation and formatting\n"
|
|
237
|
-
|
|
240
|
+
|
|
238
241
|
response += "\n**Overall:** Code readability appears acceptable with room for minor improvements."
|
|
239
|
-
|
|
242
|
+
|
|
240
243
|
return response
|
|
241
|
-
|
|
244
|
+
|
|
242
245
|
def _review_maintainability(
|
|
243
246
|
self,
|
|
244
247
|
work_description: str,
|
|
245
248
|
code_snippets: Optional[List[str]],
|
|
246
249
|
file_paths: Optional[List[str]],
|
|
247
|
-
context: Optional[str]
|
|
250
|
+
context: Optional[str],
|
|
248
251
|
) -> str:
|
|
249
252
|
"""Review maintainability aspects."""
|
|
250
253
|
response = "📋 MAINTAINABILITY REVIEW:\n\n"
|
|
251
|
-
|
|
254
|
+
|
|
252
255
|
response += "**Maintainability Factors:**\n"
|
|
253
|
-
|
|
256
|
+
|
|
254
257
|
# Check file organization
|
|
255
258
|
if file_paths:
|
|
256
259
|
if len(file_paths) == 1:
|
|
257
260
|
response += "✓ Changes are localized to a single file\n"
|
|
258
261
|
else:
|
|
259
262
|
response += "✓ Changes are logically distributed across files\n"
|
|
260
|
-
|
|
263
|
+
|
|
261
264
|
# Check for modularity in code
|
|
262
265
|
if code_snippets:
|
|
263
266
|
function_count = sum(
|
|
264
|
-
snippet.count(
|
|
267
|
+
snippet.count("func ")
|
|
268
|
+
+ snippet.count("def ")
|
|
269
|
+
+ snippet.count("function ")
|
|
265
270
|
for snippet in code_snippets
|
|
266
271
|
)
|
|
267
272
|
if function_count > 0:
|
|
268
273
|
response += "✓ Code is broken into functions/methods\n"
|
|
269
|
-
|
|
274
|
+
|
|
270
275
|
response += "\n**Maintainability Considerations:**\n"
|
|
271
276
|
response += "• Is the code modular and reusable?\n"
|
|
272
277
|
response += "• Are dependencies clearly defined?\n"
|
|
273
278
|
response += "• Will future developers understand the intent?\n"
|
|
274
279
|
response += "• Is the code structured to allow easy updates?\n"
|
|
275
|
-
|
|
280
|
+
|
|
276
281
|
response += "\n**Recommendations:**\n"
|
|
277
282
|
response += "• Consider extracting common patterns into utilities\n"
|
|
278
283
|
response += "• Ensure consistent patterns across the codebase\n"
|
|
279
284
|
response += "• Document any non-obvious design decisions\n"
|
|
280
|
-
|
|
285
|
+
|
|
281
286
|
return response
|
|
282
|
-
|
|
287
|
+
|
|
283
288
|
def _review_testing(
|
|
284
289
|
self,
|
|
285
290
|
work_description: str,
|
|
286
291
|
code_snippets: Optional[List[str]],
|
|
287
292
|
file_paths: Optional[List[str]],
|
|
288
|
-
context: Optional[str]
|
|
293
|
+
context: Optional[str],
|
|
289
294
|
) -> str:
|
|
290
295
|
"""Review testing aspects."""
|
|
291
296
|
response = "📋 TESTING REVIEW:\n\n"
|
|
292
|
-
|
|
297
|
+
|
|
293
298
|
has_test_files = any("test" in str(path).lower() for path in (file_paths or []))
|
|
294
|
-
|
|
299
|
+
|
|
295
300
|
if has_test_files:
|
|
296
301
|
response += "✓ Test files are included with the changes\n\n"
|
|
297
302
|
else:
|
|
298
303
|
response += "⚠️ No test files detected in the changes\n\n"
|
|
299
|
-
|
|
304
|
+
|
|
300
305
|
response += "**Testing Checklist:**\n"
|
|
301
306
|
response += "□ Unit tests for new functions\n"
|
|
302
307
|
response += "□ Integration tests for feature interactions\n"
|
|
303
308
|
response += "□ Edge case coverage\n"
|
|
304
309
|
response += "□ Error condition testing\n"
|
|
305
310
|
response += "□ Performance tests (if applicable)\n"
|
|
306
|
-
|
|
311
|
+
|
|
307
312
|
response += "\n**Testing Recommendations:**\n"
|
|
308
313
|
response += "• Write tests that document expected behavior\n"
|
|
309
314
|
response += "• Include both positive and negative test cases\n"
|
|
310
315
|
response += "• Ensure tests are maintainable and clear\n"
|
|
311
316
|
response += "• Aim for good coverage of critical paths\n"
|
|
312
|
-
|
|
317
|
+
|
|
313
318
|
if not has_test_files:
|
|
314
319
|
response += "\n💡 Consider adding tests to ensure reliability and prevent regressions."
|
|
315
|
-
|
|
320
|
+
|
|
316
321
|
return response
|
|
317
|
-
|
|
322
|
+
|
|
318
323
|
def _review_documentation(
|
|
319
324
|
self,
|
|
320
325
|
work_description: str,
|
|
321
326
|
code_snippets: Optional[List[str]],
|
|
322
327
|
file_paths: Optional[List[str]],
|
|
323
|
-
context: Optional[str]
|
|
328
|
+
context: Optional[str],
|
|
324
329
|
) -> str:
|
|
325
330
|
"""Review documentation aspects."""
|
|
326
331
|
response = "📋 DOCUMENTATION REVIEW:\n\n"
|
|
327
|
-
|
|
332
|
+
|
|
328
333
|
# Check for documentation in code
|
|
329
334
|
has_comments = False
|
|
330
335
|
if code_snippets:
|
|
331
336
|
has_comments = any(
|
|
332
|
-
|
|
337
|
+
"//" in snippet or "/*" in snippet or "#" in snippet or '"""' in snippet
|
|
333
338
|
for snippet in code_snippets
|
|
334
339
|
)
|
|
335
|
-
|
|
340
|
+
|
|
336
341
|
if has_comments:
|
|
337
342
|
response += "✓ Code includes some documentation\n"
|
|
338
343
|
else:
|
|
339
344
|
response += "• Consider adding documentation comments\n"
|
|
340
|
-
|
|
345
|
+
|
|
341
346
|
response += "\n**Documentation Guidelines:**\n"
|
|
342
347
|
response += "• Document the 'why' not just the 'what'\n"
|
|
343
348
|
response += "• Include examples for complex functions\n"
|
|
344
349
|
response += "• Document any assumptions or limitations\n"
|
|
345
350
|
response += "• Keep documentation up-to-date with code changes\n"
|
|
346
|
-
|
|
351
|
+
|
|
347
352
|
response += "\n**Recommended Documentation:**\n"
|
|
348
353
|
response += "• Function/method purpose and parameters\n"
|
|
349
354
|
response += "• Complex algorithm explanations\n"
|
|
350
355
|
response += "• API usage examples\n"
|
|
351
356
|
response += "• Configuration requirements\n"
|
|
352
|
-
|
|
357
|
+
|
|
353
358
|
return response
|
|
354
|
-
|
|
359
|
+
|
|
355
360
|
def _review_architecture(
|
|
356
361
|
self,
|
|
357
362
|
work_description: str,
|
|
358
363
|
code_snippets: Optional[List[str]],
|
|
359
364
|
file_paths: Optional[List[str]],
|
|
360
|
-
context: Optional[str]
|
|
365
|
+
context: Optional[str],
|
|
361
366
|
) -> str:
|
|
362
367
|
"""Review architectural aspects."""
|
|
363
368
|
response = "📋 ARCHITECTURE REVIEW:\n\n"
|
|
364
|
-
|
|
369
|
+
|
|
365
370
|
response += "**Architectural Considerations:**\n"
|
|
366
|
-
|
|
371
|
+
|
|
367
372
|
# Analyze file structure
|
|
368
373
|
if file_paths:
|
|
369
374
|
# Check for separation of concerns
|
|
370
|
-
has_separation =
|
|
375
|
+
has_separation = (
|
|
376
|
+
len(set(str(p).split("/")[-2] for p in file_paths if "/" in str(p))) > 1
|
|
377
|
+
)
|
|
371
378
|
if has_separation:
|
|
372
379
|
response += "✓ Changes span multiple modules (good separation)\n"
|
|
373
380
|
else:
|
|
374
381
|
response += "✓ Changes are cohesive within a module\n"
|
|
375
|
-
|
|
382
|
+
|
|
376
383
|
response += "\n**Architectural Principles:**\n"
|
|
377
384
|
response += "• Single Responsibility - Each component has one clear purpose\n"
|
|
378
385
|
response += "• Open/Closed - Open for extension, closed for modification\n"
|
|
379
386
|
response += "• Dependency Inversion - Depend on abstractions, not concretions\n"
|
|
380
387
|
response += "• Interface Segregation - Keep interfaces focused and minimal\n"
|
|
381
|
-
|
|
388
|
+
|
|
382
389
|
response += "\n**Questions to Consider:**\n"
|
|
383
390
|
response += "• Does this fit well with the existing architecture?\n"
|
|
384
391
|
response += "• Are the right abstractions in place?\n"
|
|
385
392
|
response += "• Is the coupling between components appropriate?\n"
|
|
386
393
|
response += "• Will this scale as requirements grow?\n"
|
|
387
|
-
|
|
394
|
+
|
|
388
395
|
if context:
|
|
389
396
|
response += f"\n**Context Impact:**\n{context}\n"
|
|
390
397
|
response += "→ Ensure the architectural choices align with this context.\n"
|
|
391
|
-
|
|
398
|
+
|
|
392
399
|
return response
|
|
393
400
|
|
|
394
401
|
|
|
395
402
|
class ReviewProtocol:
|
|
396
403
|
"""Protocol for review interactions."""
|
|
397
|
-
|
|
404
|
+
|
|
398
405
|
def __init__(self):
|
|
399
406
|
self.reviewer = BalancedReviewer()
|
|
400
407
|
self.review_count = 0
|
|
401
408
|
self.max_reviews = 3 # Allow up to 3 reviews per task
|
|
402
|
-
|
|
409
|
+
|
|
403
410
|
def request_review(
|
|
404
411
|
self,
|
|
405
412
|
focus: str,
|
|
406
413
|
work_description: str,
|
|
407
414
|
code_snippets: Optional[List[str]] = None,
|
|
408
415
|
file_paths: Optional[List[str]] = None,
|
|
409
|
-
context: Optional[str] = None
|
|
416
|
+
context: Optional[str] = None,
|
|
410
417
|
) -> str:
|
|
411
418
|
"""Request a balanced review."""
|
|
412
419
|
if self.review_count >= self.max_reviews:
|
|
413
420
|
return "📋 Review limit reached. You've received comprehensive feedback - time to finalize your implementation."
|
|
414
|
-
|
|
421
|
+
|
|
415
422
|
self.review_count += 1
|
|
416
|
-
|
|
423
|
+
|
|
417
424
|
try:
|
|
418
425
|
focus_enum = ReviewFocus[focus.upper()]
|
|
419
426
|
except KeyError:
|
|
420
427
|
focus_enum = ReviewFocus.GENERAL
|
|
421
|
-
|
|
428
|
+
|
|
422
429
|
review = self.reviewer.review(
|
|
423
|
-
focus_enum,
|
|
424
|
-
work_description,
|
|
425
|
-
code_snippets,
|
|
426
|
-
file_paths,
|
|
427
|
-
context
|
|
430
|
+
focus_enum, work_description, code_snippets, file_paths, context
|
|
428
431
|
)
|
|
429
|
-
|
|
432
|
+
|
|
430
433
|
header = f"Review {self.review_count}/{self.max_reviews} (Focus: {focus_enum.value}):\n\n"
|
|
431
434
|
footer = "\n\n💡 This is a balanced review - consider both strengths and suggestions."
|
|
432
|
-
|
|
433
|
-
return header + review + footer
|
|
435
|
+
|
|
436
|
+
return header + review + footer
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Swarm tool as an alias to Network tool for backward compatibility.
|
|
2
|
+
|
|
3
|
+
This module makes swarm an alias to the network tool, as network is the
|
|
4
|
+
evolution of swarm with better distributed execution capabilities.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from hanzo_mcp.tools.agent.network_tool import NetworkTool
|
|
8
|
+
from hanzo_mcp.tools.common.permissions import PermissionManager
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SwarmTool(NetworkTool):
|
|
12
|
+
"""Swarm tool - alias to Network tool for backward compatibility.
|
|
13
|
+
|
|
14
|
+
The swarm tool is now an alias to the network tool, which provides
|
|
15
|
+
all the same functionality plus additional distributed execution modes.
|
|
16
|
+
Use 'network' for new code, 'swarm' is maintained for compatibility.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self) -> str:
|
|
21
|
+
"""Get the tool name."""
|
|
22
|
+
return "swarm"
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def description(self) -> str:
|
|
26
|
+
"""Get the tool description."""
|
|
27
|
+
return """Execute a network of AI agents (alias to 'network' tool).
|
|
28
|
+
|
|
29
|
+
The 'swarm' tool is now an alias to the 'network' tool for backward compatibility.
|
|
30
|
+
All swarm functionality is available through network, which additionally provides:
|
|
31
|
+
|
|
32
|
+
- Local-first execution with privacy preservation
|
|
33
|
+
- Distributed compute across devices
|
|
34
|
+
- Hybrid mode with cloud fallback
|
|
35
|
+
- Integration with hanzo-network for MCP-connected agents
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
```python
|
|
39
|
+
# These are equivalent:
|
|
40
|
+
swarm(task="Analyze code", agents=["analyzer", "reviewer"])
|
|
41
|
+
network(task="Analyze code", agents=["analyzer", "reviewer"])
|
|
42
|
+
|
|
43
|
+
# Network adds new modes:
|
|
44
|
+
network(task="Process data", mode="local") # Privacy-first
|
|
45
|
+
network(task="Large analysis", mode="distributed") # Scale out
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
For new code, prefer using 'network' directly."""
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
permission_manager: PermissionManager,
|
|
53
|
+
default_mode: str = "hybrid",
|
|
54
|
+
**kwargs,
|
|
55
|
+
):
|
|
56
|
+
"""Initialize swarm as an alias to network.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
permission_manager: Permission manager
|
|
60
|
+
default_mode: Default execution mode (hybrid/local/distributed)
|
|
61
|
+
**kwargs: Additional arguments passed to NetworkTool
|
|
62
|
+
"""
|
|
63
|
+
# Just pass through to NetworkTool
|
|
64
|
+
super().__init__(
|
|
65
|
+
permission_manager=permission_manager, default_mode=default_mode, **kwargs
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
async def call(self, **kwargs) -> str:
|
|
69
|
+
"""Execute swarm via network tool.
|
|
70
|
+
|
|
71
|
+
All parameters are passed through to the network tool.
|
|
72
|
+
"""
|
|
73
|
+
# For backward compatibility, rename some parameters if needed
|
|
74
|
+
if "config" in kwargs and "agents" not in kwargs:
|
|
75
|
+
# Old swarm used 'config' for agent definitions
|
|
76
|
+
config = kwargs.pop("config")
|
|
77
|
+
if isinstance(config, dict) and "agents" in config:
|
|
78
|
+
kwargs["agents"] = config["agents"]
|
|
79
|
+
if "topology" in config:
|
|
80
|
+
kwargs["routing"] = config["topology"]
|
|
81
|
+
|
|
82
|
+
# Pass through to network tool
|
|
83
|
+
return await super().call(**kwargs)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
# For backward compatibility exports
|
|
87
|
+
__all__ = ["SwarmTool"]
|