hanzo-mcp 0.8.11__py3-none-any.whl → 0.8.14__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 +2 -4
  2. hanzo_mcp/analytics/posthog_analytics.py +3 -9
  3. hanzo_mcp/bridge.py +9 -25
  4. hanzo_mcp/cli.py +6 -15
  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 +1 -3
  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 +13 -6
  17. hanzo_mcp/tools/__init__.py +10 -24
  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 +5 -15
  21. hanzo_mcp/tools/agent/agent_tool_v1_deprecated.py +14 -41
  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 +75 -74
  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 +9 -29
  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 +38 -11
  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 +6 -18
  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_edit.py +2 -2
  72. hanzo_mcp/tools/editor/neovim_session.py +7 -13
  73. hanzo_mcp/tools/filesystem/__init__.py +2 -3
  74. hanzo_mcp/tools/filesystem/ast_multi_edit.py +14 -43
  75. hanzo_mcp/tools/filesystem/base.py +4 -12
  76. hanzo_mcp/tools/filesystem/batch_search.py +35 -115
  77. hanzo_mcp/tools/filesystem/content_replace.py +4 -12
  78. hanzo_mcp/tools/filesystem/diff.py +2 -10
  79. hanzo_mcp/tools/filesystem/directory_tree.py +9 -27
  80. hanzo_mcp/tools/filesystem/directory_tree_paginated.py +5 -15
  81. hanzo_mcp/tools/filesystem/edit.py +6 -18
  82. hanzo_mcp/tools/filesystem/find.py +3 -9
  83. hanzo_mcp/tools/filesystem/find_files.py +2 -6
  84. hanzo_mcp/tools/filesystem/git_search.py +9 -24
  85. hanzo_mcp/tools/filesystem/grep.py +9 -27
  86. hanzo_mcp/tools/filesystem/multi_edit.py +6 -18
  87. hanzo_mcp/tools/filesystem/read.py +8 -26
  88. hanzo_mcp/tools/filesystem/rules_tool.py +6 -17
  89. hanzo_mcp/tools/filesystem/search_tool.py +18 -62
  90. hanzo_mcp/tools/filesystem/symbols_tool.py +5 -15
  91. hanzo_mcp/tools/filesystem/tree.py +1 -3
  92. hanzo_mcp/tools/filesystem/watch.py +1 -3
  93. hanzo_mcp/tools/filesystem/write.py +1 -3
  94. hanzo_mcp/tools/jupyter/base.py +6 -20
  95. hanzo_mcp/tools/jupyter/jupyter.py +4 -12
  96. hanzo_mcp/tools/jupyter/notebook_edit.py +11 -35
  97. hanzo_mcp/tools/jupyter/notebook_read.py +2 -6
  98. hanzo_mcp/tools/llm/consensus_tool.py +8 -24
  99. hanzo_mcp/tools/llm/llm_manage.py +2 -6
  100. hanzo_mcp/tools/llm/llm_tool.py +17 -58
  101. hanzo_mcp/tools/llm/llm_unified.py +18 -59
  102. hanzo_mcp/tools/llm/provider_tools.py +1 -3
  103. hanzo_mcp/tools/lsp/lsp_tool.py +5 -17
  104. hanzo_mcp/tools/mcp/mcp_add.py +1 -3
  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 +10 -27
  108. hanzo_mcp/tools/memory/knowledge_tools.py +7 -25
  109. hanzo_mcp/tools/memory/memory_tools.py +6 -18
  110. hanzo_mcp/tools/search/find_tool.py +10 -32
  111. hanzo_mcp/tools/search/unified_search.py +24 -78
  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 +33 -86
  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 +7 -27
  144. hanzo_mcp/tools/vector/mock_infinity.py +1 -3
  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.11.dist-info → hanzo_mcp-0.8.14.dist-info}/METADATA +2 -2
  150. hanzo_mcp-0.8.14.dist-info/RECORD +193 -0
  151. hanzo_mcp-0.8.11.dist-info/RECORD +0 -193
  152. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.14.dist-info}/WHEEL +0 -0
  153. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.14.dist-info}/entry_points.txt +0 -0
  154. {hanzo_mcp-0.8.11.dist-info → hanzo_mcp-0.8.14.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.
@@ -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
- get_read_only_filesystem_tools(self.permission_manager)
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