hanzo 0.3.11__py3-none-any.whl → 0.3.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 might be problematic. Click here for more details.

hanzo/cli.py CHANGED
@@ -1,9 +1,9 @@
1
1
  """Main CLI entry point for Hanzo."""
2
2
 
3
+ import os
3
4
  import sys
4
- import asyncio
5
5
  import signal
6
- import os
6
+ import asyncio
7
7
  import subprocess
8
8
  from typing import Optional
9
9
 
@@ -26,7 +26,7 @@ from .utils.output import console
26
26
  from .interactive.repl import HanzoREPL
27
27
 
28
28
  # Version
29
- __version__ = "0.2.10"
29
+ __version__ = "0.3.13"
30
30
 
31
31
 
32
32
  @click.group(invoke_without_command=True)
@@ -147,6 +147,140 @@ def node(ctx, name: str, port: int, network: str, models: tuple, max_jobs: int):
147
147
  pass
148
148
 
149
149
 
150
+ @cli.command()
151
+ @click.option("--workspace", default="~/.hanzo/dev", help="Workspace directory")
152
+ @click.option(
153
+ "--orchestrator",
154
+ default="gpt-5",
155
+ help="Orchestrator: gpt-5, router:gpt-4o, direct:claude, codex, gpt-5-pro-codex, cost-optimized",
156
+ )
157
+ @click.option(
158
+ "--orchestrator-mode",
159
+ type=click.Choice(["router", "direct", "codex", "hybrid", "local"]),
160
+ default=None,
161
+ help="Force orchestrator mode (router via hanzo-router, direct API, codex, hybrid, local)",
162
+ )
163
+ @click.option(
164
+ "--router-endpoint",
165
+ default=None,
166
+ help="Hanzo router endpoint (default: http://localhost:4000)",
167
+ )
168
+ @click.option("--claude-path", help="Path to Claude Code executable")
169
+ @click.option("--monitor", is_flag=True, help="Start in monitor mode")
170
+ @click.option("--repl", is_flag=True, help="Start REPL interface (default)")
171
+ @click.option("--instances", type=int, default=2, help="Number of worker agents")
172
+ @click.option("--mcp-tools", is_flag=True, default=True, help="Enable all MCP tools")
173
+ @click.option(
174
+ "--network-mode", is_flag=True, default=True, help="Network agents together"
175
+ )
176
+ @click.option(
177
+ "--guardrails", is_flag=True, default=True, help="Enable code quality guardrails"
178
+ )
179
+ @click.option(
180
+ "--use-network/--no-network", default=True, help="Use hanzo-network if available"
181
+ )
182
+ @click.option(
183
+ "--use-hanzo-net",
184
+ is_flag=True,
185
+ help="Use hanzo/net for local AI (auto-enabled with local: models)",
186
+ )
187
+ @click.option(
188
+ "--hanzo-net-port",
189
+ type=int,
190
+ default=52415,
191
+ help="Port for hanzo/net (default: 52415)",
192
+ )
193
+ @click.pass_context
194
+ def dev(
195
+ ctx,
196
+ workspace: str,
197
+ orchestrator: str,
198
+ orchestrator_mode: str,
199
+ router_endpoint: str,
200
+ claude_path: str,
201
+ monitor: bool,
202
+ repl: bool,
203
+ instances: int,
204
+ mcp_tools: bool,
205
+ network_mode: bool,
206
+ guardrails: bool,
207
+ use_network: bool,
208
+ use_hanzo_net: bool,
209
+ hanzo_net_port: int,
210
+ ):
211
+ """Start Hanzo Dev - AI Coding OS with configurable orchestrator.
212
+
213
+ This creates a multi-agent system where:
214
+ - Configurable orchestrator (GPT-5, GPT-4, Claude, or LOCAL) manages the network
215
+ - Local AI via hanzo/net for cost-effective orchestration
216
+ - Worker agents (Claude + local) handle code implementation
217
+ - Critic agents review and improve code (System 2 thinking)
218
+ - Cost-optimized routing (local models for simple tasks)
219
+ - All agents can use MCP tools
220
+ - Agents can call each other recursively
221
+ - Guardrails prevent code degradation
222
+ - Auto-recovery from failures
223
+
224
+ Examples:
225
+ hanzo dev # GPT-5 orchestrator (default)
226
+ hanzo dev --orchestrator gpt-4 # GPT-4 orchestrator
227
+ hanzo dev --orchestrator claude-3-5-sonnet # Claude orchestrator
228
+ hanzo dev --orchestrator local:llama3.2 # Local Llama 3.2 via hanzo/net
229
+ hanzo dev --use-hanzo-net # Enable local AI workers
230
+ hanzo dev --instances 4 # More worker agents
231
+ hanzo dev --monitor # Auto-monitor and restart mode
232
+ """
233
+ from .dev import run_dev_orchestrator
234
+ from .orchestrator_config import OrchestratorMode, get_orchestrator_config
235
+
236
+ # Get orchestrator configuration
237
+ orch_config = get_orchestrator_config(orchestrator)
238
+
239
+ # Override mode if specified
240
+ if orchestrator_mode:
241
+ orch_config.mode = OrchestratorMode(orchestrator_mode)
242
+
243
+ # Override router endpoint if specified
244
+ if router_endpoint and orch_config.router:
245
+ orch_config.router.endpoint = router_endpoint
246
+
247
+ # Auto-enable hanzo net if using local orchestrator
248
+ if orchestrator.startswith("local:") or orch_config.mode == OrchestratorMode.LOCAL:
249
+ use_hanzo_net = True
250
+
251
+ # Show configuration
252
+ console.print(f"[bold cyan]Orchestrator Configuration[/bold cyan]")
253
+ console.print(f" Mode: {orch_config.mode.value}")
254
+ console.print(f" Primary Model: {orch_config.primary_model}")
255
+ if orch_config.router:
256
+ console.print(f" Router Endpoint: {orch_config.router.endpoint}")
257
+ if orch_config.codex:
258
+ console.print(f" Codex Model: {orch_config.codex.model}")
259
+ console.print(
260
+ f" Cost Optimization: {'Enabled' if orch_config.enable_cost_optimization else 'Disabled'}"
261
+ )
262
+ console.print()
263
+
264
+ asyncio.run(
265
+ run_dev_orchestrator(
266
+ workspace=workspace,
267
+ orchestrator_model=orchestrator,
268
+ orchestrator_config=orch_config, # Pass the config
269
+ claude_path=claude_path,
270
+ monitor=monitor,
271
+ repl=repl or not monitor, # Default to REPL if not monitoring
272
+ instances=instances,
273
+ mcp_tools=mcp_tools,
274
+ network_mode=network_mode,
275
+ guardrails=guardrails,
276
+ use_network=use_network,
277
+ use_hanzo_net=use_hanzo_net,
278
+ hanzo_net_port=hanzo_net_port,
279
+ console=ctx.obj.get("console", console),
280
+ )
281
+ )
282
+
283
+
150
284
  async def start_compute_node(
151
285
  ctx,
152
286
  name: str = None,
@@ -217,25 +351,24 @@ async def start_compute_node(
217
351
 
218
352
  # Set up signal handlers for async version
219
353
  stop_event = asyncio.Event()
220
-
354
+
221
355
  def async_signal_handler(signum, frame):
222
356
  console.print("\n[yellow]Stopping hanzo net...[/yellow]")
223
357
  stop_event.set()
224
-
358
+
225
359
  signal.signal(signal.SIGINT, async_signal_handler)
226
360
  signal.signal(signal.SIGTERM, async_signal_handler)
227
-
361
+
228
362
  # Run net with proper signal handling
229
363
  try:
230
364
  net_task = asyncio.create_task(net_run())
231
365
  stop_task = asyncio.create_task(stop_event.wait())
232
-
366
+
233
367
  # Wait for either net to complete or stop signal
234
368
  done, pending = await asyncio.wait(
235
- [net_task, stop_task],
236
- return_when=asyncio.FIRST_COMPLETED
369
+ [net_task, stop_task], return_when=asyncio.FIRST_COMPLETED
237
370
  )
238
-
371
+
239
372
  # Cancel pending tasks
240
373
  for task in pending:
241
374
  task.cancel()
@@ -243,7 +376,7 @@ async def start_compute_node(
243
376
  await task
244
377
  except asyncio.CancelledError:
245
378
  pass
246
-
379
+
247
380
  # Check if we stopped due to signal
248
381
  if stop_task in done:
249
382
  console.print("[green]✓[/green] Node stopped gracefully")
@@ -298,25 +431,25 @@ async def start_compute_node(
298
431
  # Run net command with detected python in a more signal-friendly way
299
432
  # Create new process group for better signal handling
300
433
  process = subprocess.Popen(
301
- cmd_args,
434
+ cmd_args,
302
435
  env=env,
303
- preexec_fn=os.setsid if hasattr(os, 'setsid') else None
436
+ preexec_fn=os.setsid if hasattr(os, "setsid") else None,
304
437
  )
305
-
438
+
306
439
  # Set up signal handlers to forward to subprocess group
307
440
  def signal_handler(signum, frame):
308
441
  if process.poll() is None: # Process is still running
309
442
  console.print("\n[yellow]Stopping hanzo net...[/yellow]")
310
443
  try:
311
444
  # Send signal to entire process group
312
- if hasattr(os, 'killpg'):
445
+ if hasattr(os, "killpg"):
313
446
  os.killpg(os.getpgid(process.pid), signal.SIGTERM)
314
447
  else:
315
448
  process.terminate()
316
449
  process.wait(timeout=5) # Wait up to 5 seconds
317
450
  except subprocess.TimeoutExpired:
318
451
  console.print("[yellow]Force stopping...[/yellow]")
319
- if hasattr(os, 'killpg'):
452
+ if hasattr(os, "killpg"):
320
453
  os.killpg(os.getpgid(process.pid), signal.SIGKILL)
321
454
  else:
322
455
  process.kill()
@@ -324,18 +457,16 @@ async def start_compute_node(
324
457
  except ProcessLookupError:
325
458
  pass # Process already terminated
326
459
  raise KeyboardInterrupt
327
-
460
+
328
461
  # Register signal handlers
329
462
  signal.signal(signal.SIGINT, signal_handler)
330
463
  signal.signal(signal.SIGTERM, signal_handler)
331
-
464
+
332
465
  # Wait for process to complete
333
466
  returncode = process.wait()
334
-
467
+
335
468
  if returncode != 0 and returncode != -2: # -2 is Ctrl+C
336
- console.print(
337
- f"[red]Net exited with code {returncode}[/red]"
338
- )
469
+ console.print(f"[red]Net exited with code {returncode}[/red]")
339
470
 
340
471
  finally:
341
472
  os.chdir(original_cwd)