hanzo-mcp 0.8.8__py3-none-any.whl → 0.8.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of hanzo-mcp might be problematic. Click here for more details.

Files changed (154) hide show
  1. hanzo_mcp/__init__.py +1 -3
  2. hanzo_mcp/analytics/posthog_analytics.py +4 -17
  3. hanzo_mcp/bridge.py +9 -25
  4. hanzo_mcp/cli.py +8 -17
  5. hanzo_mcp/cli_enhanced.py +5 -14
  6. hanzo_mcp/cli_plugin.py +3 -9
  7. hanzo_mcp/config/settings.py +6 -20
  8. hanzo_mcp/config/tool_config.py +2 -4
  9. hanzo_mcp/core/base_agent.py +88 -88
  10. hanzo_mcp/core/model_registry.py +238 -210
  11. hanzo_mcp/dev_server.py +5 -15
  12. hanzo_mcp/prompts/__init__.py +2 -6
  13. hanzo_mcp/prompts/project_todo_reminder.py +3 -9
  14. hanzo_mcp/prompts/tool_explorer.py +1 -3
  15. hanzo_mcp/prompts/utils.py +7 -21
  16. hanzo_mcp/server.py +6 -7
  17. hanzo_mcp/tools/__init__.py +13 -29
  18. hanzo_mcp/tools/agent/__init__.py +2 -1
  19. hanzo_mcp/tools/agent/agent.py +10 -30
  20. hanzo_mcp/tools/agent/agent_tool.py +6 -17
  21. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +15 -42
  22. hanzo_mcp/tools/agent/claude_desktop_auth.py +3 -9
  23. hanzo_mcp/tools/agent/cli_agent_base.py +7 -24
  24. hanzo_mcp/tools/agent/cli_tools.py +76 -75
  25. hanzo_mcp/tools/agent/code_auth.py +1 -3
  26. hanzo_mcp/tools/agent/code_auth_tool.py +2 -6
  27. hanzo_mcp/tools/agent/critic_tool.py +8 -24
  28. hanzo_mcp/tools/agent/iching_tool.py +12 -36
  29. hanzo_mcp/tools/agent/network_tool.py +7 -18
  30. hanzo_mcp/tools/agent/prompt.py +1 -5
  31. hanzo_mcp/tools/agent/review_tool.py +10 -25
  32. hanzo_mcp/tools/agent/swarm_alias.py +1 -3
  33. hanzo_mcp/tools/agent/swarm_tool.py +16 -41
  34. hanzo_mcp/tools/agent/swarm_tool_v1_deprecated.py +11 -39
  35. hanzo_mcp/tools/agent/unified_cli_tools.py +38 -38
  36. hanzo_mcp/tools/common/batch_tool.py +15 -45
  37. hanzo_mcp/tools/common/config_tool.py +9 -28
  38. hanzo_mcp/tools/common/context.py +1 -3
  39. hanzo_mcp/tools/common/critic_tool.py +1 -3
  40. hanzo_mcp/tools/common/decorators.py +2 -6
  41. hanzo_mcp/tools/common/enhanced_base.py +2 -6
  42. hanzo_mcp/tools/common/fastmcp_pagination.py +4 -12
  43. hanzo_mcp/tools/common/forgiving_edit.py +9 -28
  44. hanzo_mcp/tools/common/mode.py +1 -5
  45. hanzo_mcp/tools/common/paginated_base.py +3 -11
  46. hanzo_mcp/tools/common/paginated_response.py +10 -30
  47. hanzo_mcp/tools/common/pagination.py +3 -9
  48. hanzo_mcp/tools/common/permissions.py +3 -9
  49. hanzo_mcp/tools/common/personality.py +9 -34
  50. hanzo_mcp/tools/common/plugin_loader.py +3 -15
  51. hanzo_mcp/tools/common/stats.py +7 -19
  52. hanzo_mcp/tools/common/thinking_tool.py +1 -3
  53. hanzo_mcp/tools/common/tool_disable.py +2 -6
  54. hanzo_mcp/tools/common/tool_list.py +2 -6
  55. hanzo_mcp/tools/common/validation.py +1 -3
  56. hanzo_mcp/tools/config/config_tool.py +7 -13
  57. hanzo_mcp/tools/config/index_config.py +1 -3
  58. hanzo_mcp/tools/config/mode_tool.py +5 -15
  59. hanzo_mcp/tools/database/database_manager.py +3 -9
  60. hanzo_mcp/tools/database/graph.py +1 -3
  61. hanzo_mcp/tools/database/graph_add.py +3 -9
  62. hanzo_mcp/tools/database/graph_query.py +11 -34
  63. hanzo_mcp/tools/database/graph_remove.py +3 -9
  64. hanzo_mcp/tools/database/graph_search.py +6 -20
  65. hanzo_mcp/tools/database/graph_stats.py +11 -33
  66. hanzo_mcp/tools/database/sql.py +4 -12
  67. hanzo_mcp/tools/database/sql_query.py +6 -10
  68. hanzo_mcp/tools/database/sql_search.py +2 -6
  69. hanzo_mcp/tools/database/sql_stats.py +5 -15
  70. hanzo_mcp/tools/editor/neovim_command.py +1 -3
  71. hanzo_mcp/tools/editor/neovim_session.py +7 -13
  72. hanzo_mcp/tools/filesystem/__init__.py +2 -3
  73. hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
  74. hanzo_mcp/tools/filesystem/base.py +4 -12
  75. hanzo_mcp/tools/filesystem/batch_search.py +35 -115
  76. hanzo_mcp/tools/filesystem/content_replace.py +4 -12
  77. hanzo_mcp/tools/filesystem/diff.py +2 -10
  78. hanzo_mcp/tools/filesystem/directory_tree.py +9 -27
  79. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +5 -15
  80. hanzo_mcp/tools/filesystem/edit.py +6 -18
  81. hanzo_mcp/tools/filesystem/find.py +3 -9
  82. hanzo_mcp/tools/filesystem/find_files.py +2 -6
  83. hanzo_mcp/tools/filesystem/git_search.py +9 -24
  84. hanzo_mcp/tools/filesystem/grep.py +9 -27
  85. hanzo_mcp/tools/filesystem/multi_edit.py +6 -18
  86. hanzo_mcp/tools/filesystem/read.py +8 -26
  87. hanzo_mcp/tools/filesystem/rules_tool.py +6 -17
  88. hanzo_mcp/tools/filesystem/search_tool.py +18 -62
  89. hanzo_mcp/tools/filesystem/symbols_tool.py +5 -15
  90. hanzo_mcp/tools/filesystem/tree.py +1 -3
  91. hanzo_mcp/tools/filesystem/watch.py +1 -3
  92. hanzo_mcp/tools/filesystem/write.py +1 -3
  93. hanzo_mcp/tools/jupyter/base.py +6 -20
  94. hanzo_mcp/tools/jupyter/jupyter.py +4 -12
  95. hanzo_mcp/tools/jupyter/notebook_edit.py +11 -35
  96. hanzo_mcp/tools/jupyter/notebook_read.py +2 -6
  97. hanzo_mcp/tools/llm/consensus_tool.py +8 -24
  98. hanzo_mcp/tools/llm/llm_manage.py +2 -6
  99. hanzo_mcp/tools/llm/llm_tool.py +17 -58
  100. hanzo_mcp/tools/llm/llm_unified.py +18 -59
  101. hanzo_mcp/tools/llm/provider_tools.py +1 -3
  102. hanzo_mcp/tools/lsp/lsp_tool.py +5 -17
  103. hanzo_mcp/tools/mcp/mcp_add.py +3 -5
  104. hanzo_mcp/tools/mcp/mcp_remove.py +1 -1
  105. hanzo_mcp/tools/mcp/mcp_stats.py +1 -3
  106. hanzo_mcp/tools/mcp/mcp_tool.py +9 -23
  107. hanzo_mcp/tools/memory/__init__.py +33 -40
  108. hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
  109. hanzo_mcp/tools/memory/memory_tools.py +7 -19
  110. hanzo_mcp/tools/search/find_tool.py +10 -32
  111. hanzo_mcp/tools/search/unified_search.py +27 -81
  112. hanzo_mcp/tools/shell/__init__.py +2 -2
  113. hanzo_mcp/tools/shell/auto_background.py +2 -6
  114. hanzo_mcp/tools/shell/base.py +1 -5
  115. hanzo_mcp/tools/shell/base_process.py +5 -7
  116. hanzo_mcp/tools/shell/bash_session.py +7 -24
  117. hanzo_mcp/tools/shell/bash_session_executor.py +5 -15
  118. hanzo_mcp/tools/shell/bash_tool.py +3 -7
  119. hanzo_mcp/tools/shell/command_executor.py +26 -79
  120. hanzo_mcp/tools/shell/logs.py +4 -16
  121. hanzo_mcp/tools/shell/npx.py +2 -8
  122. hanzo_mcp/tools/shell/npx_tool.py +1 -3
  123. hanzo_mcp/tools/shell/pkill.py +4 -12
  124. hanzo_mcp/tools/shell/process_tool.py +2 -8
  125. hanzo_mcp/tools/shell/processes.py +5 -17
  126. hanzo_mcp/tools/shell/run_background.py +1 -3
  127. hanzo_mcp/tools/shell/run_command.py +1 -3
  128. hanzo_mcp/tools/shell/run_command_windows.py +1 -3
  129. hanzo_mcp/tools/shell/session_manager.py +2 -6
  130. hanzo_mcp/tools/shell/session_storage.py +2 -6
  131. hanzo_mcp/tools/shell/streaming_command.py +7 -23
  132. hanzo_mcp/tools/shell/uvx.py +4 -14
  133. hanzo_mcp/tools/shell/uvx_background.py +2 -6
  134. hanzo_mcp/tools/shell/uvx_tool.py +1 -3
  135. hanzo_mcp/tools/shell/zsh_tool.py +12 -20
  136. hanzo_mcp/tools/todo/todo.py +1 -3
  137. hanzo_mcp/tools/todo/todo_read.py +3 -9
  138. hanzo_mcp/tools/todo/todo_write.py +6 -18
  139. hanzo_mcp/tools/vector/__init__.py +3 -9
  140. hanzo_mcp/tools/vector/ast_analyzer.py +6 -20
  141. hanzo_mcp/tools/vector/git_ingester.py +10 -30
  142. hanzo_mcp/tools/vector/index_tool.py +3 -9
  143. hanzo_mcp/tools/vector/infinity_store.py +11 -30
  144. hanzo_mcp/tools/vector/mock_infinity.py +159 -0
  145. hanzo_mcp/tools/vector/project_manager.py +4 -12
  146. hanzo_mcp/tools/vector/vector.py +2 -6
  147. hanzo_mcp/tools/vector/vector_index.py +8 -8
  148. hanzo_mcp/tools/vector/vector_search.py +7 -21
  149. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/METADATA +2 -2
  150. hanzo_mcp-0.8.13.dist-info/RECORD +193 -0
  151. hanzo_mcp-0.8.8.dist-info/RECORD +0 -192
  152. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/WHEEL +0 -0
  153. {hanzo_mcp-0.8.8.dist-info → hanzo_mcp-0.8.13.dist-info}/entry_points.txt +0 -0
  154. {hanzo_mcp-0.8.8.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(
@@ -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
- len(line) for snippet in code_snippets for line in snippet.split("\n")
215
- ) / max(total_lines, 1)
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.
@@ -96,14 +96,12 @@ except ImportError:
96
96
  # Try core module import
97
97
  from hanzo_agents.core.memory import create_memory_kv, create_memory_vector
98
98
  except ImportError:
99
- # Define fallback functions if not available
99
+ # Define stubs if not available
100
100
  def create_memory_kv(*args, **kwargs):
101
- """Fallback: Key-value memory not available."""
102
- return {}
101
+ pass
103
102
 
104
103
  def create_memory_vector(*args, **kwargs):
105
- """Fallback: Vector memory not available."""
106
- return {}
104
+ pass
107
105
 
108
106
 
109
107
  try:
@@ -117,18 +115,15 @@ except ImportError:
117
115
  state_based_router,
118
116
  )
119
117
  except ImportError:
120
- # Define fallback functions if not available
118
+ # Define stubs if not available
121
119
  def sequential_router(*args, **kwargs):
122
- """Fallback: Sequential router not available."""
123
- return lambda agents, task: None
120
+ pass
124
121
 
125
122
  def conditional_router(*args, **kwargs):
126
- """Fallback: Conditional router not available."""
127
- return lambda agents, task: None
123
+ pass
128
124
 
129
125
  def state_based_router(*args, **kwargs):
130
- """Fallback: State-based router not available."""
131
- return lambda agents, task: None
126
+ pass
132
127
 
133
128
 
134
129
  try:
@@ -196,9 +191,7 @@ class SwarmToolParams(TypedDict):
196
191
  class SwarmState(State):
197
192
  """State for swarm execution."""
198
193
 
199
- def __init__(
200
- self, config: SwarmConfig, initial_query: str, context: Optional[str] = None
201
- ):
194
+ def __init__(self, config: SwarmConfig, initial_query: str, context: Optional[str] = None):
202
195
  """Initialize swarm state."""
203
196
  super().__init__()
204
197
  self.config = config
@@ -299,9 +292,7 @@ class SwarmAgent(MCPAgent):
299
292
  # Default to anthropic
300
293
  return f"model://anthropic/{model}"
301
294
 
302
- async def run(
303
- self, state: SwarmState, history: History, network: Network
304
- ) -> InferenceResult:
295
+ async def run(self, state: SwarmState, history: History, network: Network) -> InferenceResult:
305
296
  """Execute the swarm agent."""
306
297
  # Build prompt with context
307
298
  prompt_parts = []
@@ -423,9 +414,7 @@ class SwarmRouter(DeterministicRouter):
423
414
  # No more agents to execute
424
415
  return None
425
416
 
426
- def _get_agent_class(
427
- self, agent_id: str, agent_stack: List[type[Agent]]
428
- ) -> type[Agent]:
417
+ def _get_agent_class(self, agent_id: str, agent_stack: List[type[Agent]]) -> type[Agent]:
429
418
  """Get agent class for given agent ID."""
430
419
  # Find matching agent by name
431
420
  for agent_class in agent_stack:
@@ -500,11 +489,7 @@ Models can be specified as:
500
489
  from hanzo_mcp.tools.agent.code_auth import get_latest_claude_model
501
490
 
502
491
  self.model = model or f"anthropic/{get_latest_claude_model()}"
503
- self.api_key = (
504
- api_key
505
- or os.environ.get("ANTHROPIC_API_KEY")
506
- or os.environ.get("CLAUDE_API_KEY")
507
- )
492
+ self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY") or os.environ.get("CLAUDE_API_KEY")
508
493
  self.base_url = base_url
509
494
  self.max_tokens = max_tokens
510
495
  self.agent_max_iterations = agent_max_iterations
@@ -512,21 +497,15 @@ Models can be specified as:
512
497
 
513
498
  # Set up available tools for agents
514
499
  self.available_tools: list[BaseTool] = []
515
- self.available_tools.extend(
516
- get_read_only_filesystem_tools(self.permission_manager)
517
- )
518
- self.available_tools.extend(
519
- get_read_only_jupyter_tools(self.permission_manager)
520
- )
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))
521
502
 
522
503
  # Add edit tools
523
504
  self.available_tools.append(Edit(self.permission_manager))
524
505
  self.available_tools.append(MultiEdit(self.permission_manager))
525
506
 
526
507
  # Add batch tool
527
- self.available_tools.append(
528
- BatchTool({t.name: t for t in self.available_tools})
529
- )
508
+ self.available_tools.append(BatchTool({t.name: t for t in self.available_tools}))
530
509
 
531
510
  @override
532
511
  async def call(
@@ -554,9 +533,7 @@ Models can be specified as:
554
533
 
555
534
  # hanzo-agents SDK is required (already imported above)
556
535
 
557
- await tool_ctx.info(
558
- f"Starting swarm execution with {len(agents_config)} agents using hanzo-agents SDK"
559
- )
536
+ await tool_ctx.info(f"Starting swarm execution with {len(agents_config)} agents using hanzo-agents SDK")
560
537
 
561
538
  # Create state
562
539
  state = SwarmState(config=config, initial_query=initial_query, context=context)
@@ -653,9 +630,7 @@ Models can be specified as:
653
630
  output.append("=" * 80)
654
631
  output.append(f"Total agents: {len(agents_config)}")
655
632
  output.append(f"Completed: {len(results)}")
656
- output.append(
657
- f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}"
658
- )
633
+ output.append(f"Failed: {len([r for r in results.values() if r.startswith('Error:')])}")
659
634
 
660
635
  if entry_point:
661
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