@hanzo/dev 2.1.1 → 3.0.2
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.
- package/README.md +278 -279
- package/bin/dev.js +413 -0
- package/package.json +32 -61
- package/postinstall.js +513 -0
- package/scripts/preinstall.js +69 -0
- package/scripts/windows-cleanup.ps1 +31 -0
- package/.eslintrc.json +0 -24
- package/dist/cli/dev.js +0 -24746
- package/src/cli/dev.ts +0 -946
- package/src/lib/agent-loop.ts +0 -552
- package/src/lib/benchmark-runner.ts +0 -431
- package/src/lib/code-act-agent.ts +0 -378
- package/src/lib/config.ts +0 -163
- package/src/lib/editor.ts +0 -395
- package/src/lib/function-calling.ts +0 -318
- package/src/lib/mcp-client.ts +0 -259
- package/src/lib/peer-agent-network.ts +0 -584
- package/src/lib/swarm-runner.ts +0 -389
- package/src/lib/unified-workspace.ts +0 -435
- package/test-swarm/file1.js +0 -6
- package/test-swarm/file2.ts +0 -12
- package/test-swarm/file3.py +0 -15
- package/test-swarm/file4.md +0 -13
- package/test-swarm/file5.json +0 -12
- package/test-swarm-demo.sh +0 -22
- package/tests/browser-integration.test.ts +0 -242
- package/tests/code-act-agent.test.ts +0 -305
- package/tests/editor.test.ts +0 -223
- package/tests/fixtures/sample-code.js +0 -13
- package/tests/fixtures/sample-code.py +0 -28
- package/tests/fixtures/sample-code.ts +0 -22
- package/tests/mcp-client.test.ts +0 -238
- package/tests/peer-agent-network.test.ts +0 -340
- package/tests/swarm-runner.test.ts +0 -301
- package/tests/swe-bench.test.ts +0 -357
- package/tsconfig.cli.json +0 -25
- package/tsconfig.json +0 -35
- package/vitest.config.ts +0 -37
package/src/cli/dev.ts
DELETED
|
@@ -1,946 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import chalk from 'chalk';
|
|
4
|
-
import inquirer from 'inquirer';
|
|
5
|
-
import { spawn, execSync } from 'child_process';
|
|
6
|
-
import * as fs from 'fs';
|
|
7
|
-
import * as path from 'path';
|
|
8
|
-
import * as os from 'os';
|
|
9
|
-
import { FileEditor } from '../lib/editor';
|
|
10
|
-
import { MCPClient, DEFAULT_MCP_SERVERS } from '../lib/mcp-client';
|
|
11
|
-
import { FunctionCallingSystem } from '../lib/function-calling';
|
|
12
|
-
import { ConfigManager } from '../lib/config';
|
|
13
|
-
import { CodeActAgent } from '../lib/code-act-agent';
|
|
14
|
-
import { UnifiedWorkspace, WorkspaceSession } from '../lib/unified-workspace';
|
|
15
|
-
import { PeerAgentNetwork } from '../lib/peer-agent-network';
|
|
16
|
-
import { BenchmarkRunner, BenchmarkConfig } from '../lib/benchmark-runner';
|
|
17
|
-
import { ConfigurableAgentLoop, LLMProvider } from '../lib/agent-loop';
|
|
18
|
-
import { SwarmRunner, SwarmOptions } from '../lib/swarm-runner';
|
|
19
|
-
|
|
20
|
-
const program = new Command();
|
|
21
|
-
|
|
22
|
-
// Load environment variables from .env files
|
|
23
|
-
function loadEnvFiles(): void {
|
|
24
|
-
const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
|
|
25
|
-
const cwd = process.cwd();
|
|
26
|
-
|
|
27
|
-
envFiles.forEach(file => {
|
|
28
|
-
const filePath = path.join(cwd, file);
|
|
29
|
-
if (fs.existsSync(filePath)) {
|
|
30
|
-
const content = fs.readFileSync(filePath, 'utf-8');
|
|
31
|
-
content.split('\n').forEach(line => {
|
|
32
|
-
const match = line.match(/^([^=]+)=(.*)$/);
|
|
33
|
-
if (match) {
|
|
34
|
-
const key = match[1].trim();
|
|
35
|
-
const value = match[2].trim();
|
|
36
|
-
if (!process.env[key]) {
|
|
37
|
-
process.env[key] = value;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Load env files on startup
|
|
46
|
-
loadEnvFiles();
|
|
47
|
-
|
|
48
|
-
// Check if uvx is available
|
|
49
|
-
function hasUvx(): boolean {
|
|
50
|
-
try {
|
|
51
|
-
execSync('which uvx', { stdio: 'ignore' });
|
|
52
|
-
return true;
|
|
53
|
-
} catch {
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Available tools configuration
|
|
59
|
-
const TOOLS = {
|
|
60
|
-
'hanzo-dev': {
|
|
61
|
-
name: 'Hanzo Dev (OpenHands)',
|
|
62
|
-
command: hasUvx() ? 'uvx hanzo-dev' : 'hanzo-dev',
|
|
63
|
-
checkCommand: hasUvx() ? 'which uvx' : 'which hanzo-dev',
|
|
64
|
-
description: 'Hanzo AI software development agent - Full featured dev environment',
|
|
65
|
-
color: chalk.magenta,
|
|
66
|
-
apiKeys: ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'LLM_API_KEY', 'HANZO_API_KEY'],
|
|
67
|
-
priority: 1,
|
|
68
|
-
isDefault: true
|
|
69
|
-
},
|
|
70
|
-
claude: {
|
|
71
|
-
name: 'Claude (Anthropic)',
|
|
72
|
-
command: 'claude-code',
|
|
73
|
-
checkCommand: 'which claude-code',
|
|
74
|
-
description: 'Claude Code - AI coding assistant',
|
|
75
|
-
color: chalk.blue,
|
|
76
|
-
apiKeys: ['ANTHROPIC_API_KEY', 'CLAUDE_API_KEY'],
|
|
77
|
-
priority: 2
|
|
78
|
-
},
|
|
79
|
-
aider: {
|
|
80
|
-
name: 'Aider',
|
|
81
|
-
command: 'aider',
|
|
82
|
-
checkCommand: 'which aider',
|
|
83
|
-
description: 'AI pair programming in your terminal',
|
|
84
|
-
color: chalk.green,
|
|
85
|
-
apiKeys: ['OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'CLAUDE_API_KEY'],
|
|
86
|
-
priority: 3
|
|
87
|
-
},
|
|
88
|
-
gemini: {
|
|
89
|
-
name: 'Gemini (Google)',
|
|
90
|
-
command: 'gemini',
|
|
91
|
-
checkCommand: 'which gemini',
|
|
92
|
-
description: 'Google Gemini AI assistant',
|
|
93
|
-
color: chalk.yellow,
|
|
94
|
-
apiKeys: ['GOOGLE_API_KEY', 'GEMINI_API_KEY'],
|
|
95
|
-
priority: 4
|
|
96
|
-
},
|
|
97
|
-
codex: {
|
|
98
|
-
name: 'OpenAI Codex',
|
|
99
|
-
command: 'codex',
|
|
100
|
-
checkCommand: 'which codex',
|
|
101
|
-
description: 'OpenAI coding assistant',
|
|
102
|
-
color: chalk.cyan,
|
|
103
|
-
apiKeys: ['OPENAI_API_KEY'],
|
|
104
|
-
priority: 5
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
// Check if a tool has API keys configured
|
|
109
|
-
function hasApiKey(tool: string): boolean {
|
|
110
|
-
const toolConfig = TOOLS[tool as keyof typeof TOOLS];
|
|
111
|
-
if (!toolConfig || !toolConfig.apiKeys) return false;
|
|
112
|
-
|
|
113
|
-
return toolConfig.apiKeys.some(key => !!process.env[key]);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Check if a tool is installed
|
|
117
|
-
async function isToolInstalled(tool: string): Promise<boolean> {
|
|
118
|
-
return new Promise((resolve) => {
|
|
119
|
-
const checkCmd = TOOLS[tool as keyof typeof TOOLS]?.checkCommand || `which ${tool}`;
|
|
120
|
-
const check = spawn('sh', ['-c', checkCmd]);
|
|
121
|
-
check.on('close', (code) => {
|
|
122
|
-
resolve(code === 0);
|
|
123
|
-
});
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Get list of available tools
|
|
128
|
-
async function getAvailableTools(): Promise<string[]> {
|
|
129
|
-
const available: string[] = [];
|
|
130
|
-
for (const toolKey of Object.keys(TOOLS)) {
|
|
131
|
-
const isInstalled = await isToolInstalled(toolKey);
|
|
132
|
-
const hasKey = hasApiKey(toolKey);
|
|
133
|
-
if (isInstalled || hasKey) {
|
|
134
|
-
available.push(toolKey);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return available.sort((a, b) => {
|
|
138
|
-
const priorityA = TOOLS[a as keyof typeof TOOLS].priority;
|
|
139
|
-
const priorityB = TOOLS[b as keyof typeof TOOLS].priority;
|
|
140
|
-
return priorityA - priorityB;
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// Get default tool
|
|
145
|
-
async function getDefaultTool(): Promise<string | null> {
|
|
146
|
-
const availableTools = await getAvailableTools();
|
|
147
|
-
if (availableTools.length === 0) return null;
|
|
148
|
-
|
|
149
|
-
if (availableTools.includes('hanzo-dev')) {
|
|
150
|
-
return 'hanzo-dev';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
for (const tool of availableTools) {
|
|
154
|
-
if (await isToolInstalled(tool) && hasApiKey(tool)) {
|
|
155
|
-
return tool;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
return availableTools[0];
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Run a tool
|
|
163
|
-
function runTool(tool: string, args: string[] = []): void {
|
|
164
|
-
const toolConfig = TOOLS[tool as keyof typeof TOOLS];
|
|
165
|
-
if (!toolConfig) {
|
|
166
|
-
console.error(chalk.red(`Unknown tool: ${tool}`));
|
|
167
|
-
process.exit(1);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
console.log(toolConfig.color(`\n🚀 Launching ${toolConfig.name}...\n`));
|
|
171
|
-
|
|
172
|
-
if (tool === 'hanzo-dev' && hasUvx()) {
|
|
173
|
-
console.log(chalk.gray('Using uvx to run hanzo-dev...'));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const child = spawn(toolConfig.command, args, {
|
|
177
|
-
stdio: 'inherit',
|
|
178
|
-
shell: true,
|
|
179
|
-
env: process.env
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
child.on('error', (error) => {
|
|
183
|
-
console.error(chalk.red(`Failed to start ${toolConfig.name}: ${error.message}`));
|
|
184
|
-
|
|
185
|
-
if (tool === 'hanzo-dev') {
|
|
186
|
-
console.log(chalk.yellow('\nTo install hanzo-dev:'));
|
|
187
|
-
console.log(chalk.gray(' pip install hanzo-dev'));
|
|
188
|
-
console.log(chalk.gray(' # or'));
|
|
189
|
-
console.log(chalk.gray(' uvx hanzo-dev # (if you have uv installed)'));
|
|
190
|
-
}
|
|
191
|
-
process.exit(1);
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
child.on('exit', (code) => {
|
|
195
|
-
if (code !== 0) {
|
|
196
|
-
console.error(chalk.red(`${toolConfig.name} exited with code ${code}`));
|
|
197
|
-
}
|
|
198
|
-
process.exit(code || 0);
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Interactive editing mode using built-in editor
|
|
203
|
-
async function interactiveEditMode(): Promise<void> {
|
|
204
|
-
const editor = new FileEditor();
|
|
205
|
-
const functionCalling = new FunctionCallingSystem();
|
|
206
|
-
const mcpClient = new MCPClient();
|
|
207
|
-
|
|
208
|
-
console.log(chalk.bold.cyan('\n📝 Hanzo Dev Editor - Interactive Mode\n'));
|
|
209
|
-
console.log(chalk.gray('Commands: view, create, str_replace, insert, undo_edit, run, list, search, mcp, help, exit\n'));
|
|
210
|
-
|
|
211
|
-
// Connect to default MCP servers if available
|
|
212
|
-
for (const serverConfig of DEFAULT_MCP_SERVERS) {
|
|
213
|
-
try {
|
|
214
|
-
console.log(chalk.gray(`Connecting to MCP server: ${serverConfig.name}...`));
|
|
215
|
-
const session = await mcpClient.connect(serverConfig);
|
|
216
|
-
await functionCalling.registerMCPServer(serverConfig.name, session);
|
|
217
|
-
console.log(chalk.green(`✓ Connected to ${serverConfig.name}`));
|
|
218
|
-
} catch (error) {
|
|
219
|
-
console.log(chalk.yellow(`⚠ Could not connect to ${serverConfig.name}`));
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
while (true) {
|
|
224
|
-
const { command } = await inquirer.prompt([{
|
|
225
|
-
type: 'input',
|
|
226
|
-
name: 'command',
|
|
227
|
-
message: chalk.green('editor>'),
|
|
228
|
-
prefix: ''
|
|
229
|
-
}]);
|
|
230
|
-
|
|
231
|
-
if (command === 'exit' || command === 'quit') {
|
|
232
|
-
break;
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
if (command === 'help') {
|
|
236
|
-
console.log(chalk.cyan('\nAvailable commands:'));
|
|
237
|
-
console.log(' view <file> [start] [end] - View file contents');
|
|
238
|
-
console.log(' create <file> - Create new file');
|
|
239
|
-
console.log(' str_replace <file> - Replace string in file');
|
|
240
|
-
console.log(' insert <file> <line> - Insert line in file');
|
|
241
|
-
console.log(' undo_edit <file> - Undo last edit');
|
|
242
|
-
console.log(' run <command> - Run shell command');
|
|
243
|
-
console.log(' list <directory> - List directory contents');
|
|
244
|
-
console.log(' search <pattern> [path] - Search for files');
|
|
245
|
-
console.log(' mcp - List MCP tools');
|
|
246
|
-
console.log(' tools - List all available tools');
|
|
247
|
-
console.log(' help - Show this help');
|
|
248
|
-
console.log(' exit - Exit editor\n');
|
|
249
|
-
continue;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
if (command === 'tools') {
|
|
253
|
-
const tools = functionCalling.getAvailableTools();
|
|
254
|
-
console.log(chalk.cyan('\nAvailable tools:'));
|
|
255
|
-
tools.forEach(tool => {
|
|
256
|
-
console.log(` ${chalk.yellow(tool.name)} - ${tool.description}`);
|
|
257
|
-
});
|
|
258
|
-
console.log();
|
|
259
|
-
continue;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
if (command === 'mcp') {
|
|
263
|
-
const sessions = mcpClient.getAllSessions();
|
|
264
|
-
console.log(chalk.cyan('\nMCP Sessions:'));
|
|
265
|
-
sessions.forEach(session => {
|
|
266
|
-
console.log(` ${chalk.yellow(session.id)}:`);
|
|
267
|
-
session.tools.forEach(tool => {
|
|
268
|
-
console.log(` - ${tool.name}: ${tool.description}`);
|
|
269
|
-
});
|
|
270
|
-
});
|
|
271
|
-
console.log();
|
|
272
|
-
continue;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// Parse and execute commands
|
|
276
|
-
const parts = command.split(' ');
|
|
277
|
-
const cmd = parts[0];
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
let result;
|
|
281
|
-
|
|
282
|
-
switch (cmd) {
|
|
283
|
-
case 'view':
|
|
284
|
-
result = await editor.execute({
|
|
285
|
-
command: 'view',
|
|
286
|
-
path: parts[1],
|
|
287
|
-
startLine: parts[2] ? parseInt(parts[2]) : undefined,
|
|
288
|
-
endLine: parts[3] ? parseInt(parts[3]) : undefined
|
|
289
|
-
});
|
|
290
|
-
break;
|
|
291
|
-
|
|
292
|
-
case 'create':
|
|
293
|
-
const { content } = await inquirer.prompt([{
|
|
294
|
-
type: 'editor',
|
|
295
|
-
name: 'content',
|
|
296
|
-
message: 'Enter file content:'
|
|
297
|
-
}]);
|
|
298
|
-
result = await editor.execute({
|
|
299
|
-
command: 'create',
|
|
300
|
-
path: parts[1],
|
|
301
|
-
content
|
|
302
|
-
});
|
|
303
|
-
break;
|
|
304
|
-
|
|
305
|
-
case 'str_replace':
|
|
306
|
-
const { oldStr } = await inquirer.prompt([{
|
|
307
|
-
type: 'input',
|
|
308
|
-
name: 'oldStr',
|
|
309
|
-
message: 'String to replace:'
|
|
310
|
-
}]);
|
|
311
|
-
const { newStr } = await inquirer.prompt([{
|
|
312
|
-
type: 'input',
|
|
313
|
-
name: 'newStr',
|
|
314
|
-
message: 'Replacement string:'
|
|
315
|
-
}]);
|
|
316
|
-
result = await editor.execute({
|
|
317
|
-
command: 'str_replace',
|
|
318
|
-
path: parts[1],
|
|
319
|
-
oldStr,
|
|
320
|
-
newStr
|
|
321
|
-
});
|
|
322
|
-
break;
|
|
323
|
-
|
|
324
|
-
case 'insert':
|
|
325
|
-
const { lineContent } = await inquirer.prompt([{
|
|
326
|
-
type: 'input',
|
|
327
|
-
name: 'lineContent',
|
|
328
|
-
message: 'Line content:'
|
|
329
|
-
}]);
|
|
330
|
-
result = await editor.execute({
|
|
331
|
-
command: 'insert',
|
|
332
|
-
path: parts[1],
|
|
333
|
-
lineNumber: parseInt(parts[2]),
|
|
334
|
-
content: lineContent
|
|
335
|
-
});
|
|
336
|
-
break;
|
|
337
|
-
|
|
338
|
-
case 'undo_edit':
|
|
339
|
-
result = await editor.execute({
|
|
340
|
-
command: 'undo_edit',
|
|
341
|
-
path: parts[1]
|
|
342
|
-
});
|
|
343
|
-
break;
|
|
344
|
-
|
|
345
|
-
case 'run':
|
|
346
|
-
const runCommand = parts.slice(1).join(' ');
|
|
347
|
-
result = await functionCalling.callFunction({
|
|
348
|
-
id: Date.now().toString(),
|
|
349
|
-
name: 'run_command',
|
|
350
|
-
arguments: { command: runCommand }
|
|
351
|
-
});
|
|
352
|
-
break;
|
|
353
|
-
|
|
354
|
-
case 'list':
|
|
355
|
-
result = await functionCalling.callFunction({
|
|
356
|
-
id: Date.now().toString(),
|
|
357
|
-
name: 'list_directory',
|
|
358
|
-
arguments: { path: parts[1] || '.' }
|
|
359
|
-
});
|
|
360
|
-
break;
|
|
361
|
-
|
|
362
|
-
case 'search':
|
|
363
|
-
result = await functionCalling.callFunction({
|
|
364
|
-
id: Date.now().toString(),
|
|
365
|
-
name: 'search_files',
|
|
366
|
-
arguments: {
|
|
367
|
-
pattern: parts[1],
|
|
368
|
-
path: parts[2] || '.',
|
|
369
|
-
regex: false
|
|
370
|
-
}
|
|
371
|
-
});
|
|
372
|
-
break;
|
|
373
|
-
|
|
374
|
-
default:
|
|
375
|
-
console.log(chalk.red(`Unknown command: ${cmd}`));
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
if (result) {
|
|
380
|
-
if (result.success || result.result?.success) {
|
|
381
|
-
console.log(chalk.green('✓'), result.message || 'Success');
|
|
382
|
-
if (result.content || result.result?.stdout) {
|
|
383
|
-
console.log(result.content || result.result.stdout);
|
|
384
|
-
}
|
|
385
|
-
if (result.result?.files) {
|
|
386
|
-
result.result.files.forEach((file: any) => {
|
|
387
|
-
const icon = file.type === 'directory' ? '📁' : '📄';
|
|
388
|
-
console.log(` ${icon} ${file.name}`);
|
|
389
|
-
});
|
|
390
|
-
}
|
|
391
|
-
if (result.result?.matches) {
|
|
392
|
-
console.log(`Found ${result.result.total} matches:`);
|
|
393
|
-
result.result.matches.forEach((match: string) => {
|
|
394
|
-
console.log(` 📄 ${match}`);
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
} else {
|
|
398
|
-
console.log(chalk.red('✗'), result.message || result.error || 'Error');
|
|
399
|
-
if (result.result?.stderr) {
|
|
400
|
-
console.log(chalk.red(result.result.stderr));
|
|
401
|
-
}
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
} catch (error) {
|
|
405
|
-
console.log(chalk.red('Error:'), error);
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
console.log(chalk.gray('\nExiting editor mode...'));
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
// Interactive mode for tool selection
|
|
413
|
-
async function interactiveMode(): Promise<void> {
|
|
414
|
-
console.log(chalk.bold.cyan('\n🤖 Hanzo Dev - AI Development Assistant\n'));
|
|
415
|
-
|
|
416
|
-
const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
|
|
417
|
-
const detectedEnvFiles = envFiles.filter(file => fs.existsSync(path.join(process.cwd(), file)));
|
|
418
|
-
if (detectedEnvFiles.length > 0) {
|
|
419
|
-
console.log(chalk.gray('📄 Detected environment files:'));
|
|
420
|
-
detectedEnvFiles.forEach(file => {
|
|
421
|
-
console.log(chalk.gray(` - ${file}`));
|
|
422
|
-
});
|
|
423
|
-
console.log();
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const availableTools = await getAvailableTools();
|
|
427
|
-
const defaultTool = await getDefaultTool();
|
|
428
|
-
|
|
429
|
-
if (availableTools.length === 0 && !hasApiKey('hanzo-dev')) {
|
|
430
|
-
console.log(chalk.yellow('No AI tools available. Please either:'));
|
|
431
|
-
console.log(chalk.yellow('\n1. Install hanzo-dev (recommended):'));
|
|
432
|
-
console.log(chalk.gray(' pip install hanzo-dev'));
|
|
433
|
-
console.log(chalk.gray(' # or'));
|
|
434
|
-
console.log(chalk.gray(' uvx hanzo-dev'));
|
|
435
|
-
|
|
436
|
-
console.log(chalk.yellow('\n2. Install other tools:'));
|
|
437
|
-
console.log(chalk.gray(' npm install -g @hanzo/claude-code'));
|
|
438
|
-
console.log(chalk.gray(' pip install aider-chat'));
|
|
439
|
-
|
|
440
|
-
console.log(chalk.yellow('\n3. Or configure API keys in your .env file:'));
|
|
441
|
-
console.log(chalk.gray(' ANTHROPIC_API_KEY=sk-ant-...'));
|
|
442
|
-
console.log(chalk.gray(' OPENAI_API_KEY=sk-...'));
|
|
443
|
-
process.exit(1);
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
// Add built-in editor option
|
|
447
|
-
const choices = [
|
|
448
|
-
{
|
|
449
|
-
name: chalk.bold.yellow('🔧 Built-in Editor - Interactive file editing and MCP tools'),
|
|
450
|
-
value: 'builtin-editor',
|
|
451
|
-
short: 'Built-in Editor'
|
|
452
|
-
}
|
|
453
|
-
];
|
|
454
|
-
|
|
455
|
-
// Add external tools
|
|
456
|
-
for (const tool of availableTools) {
|
|
457
|
-
const toolConfig = TOOLS[tool as keyof typeof TOOLS];
|
|
458
|
-
const isInstalled = await isToolInstalled(tool);
|
|
459
|
-
const hasKey = hasApiKey(tool);
|
|
460
|
-
|
|
461
|
-
let status = '';
|
|
462
|
-
if (isInstalled && hasKey) {
|
|
463
|
-
status = chalk.green(' [Installed + API Key]');
|
|
464
|
-
} else if (isInstalled) {
|
|
465
|
-
status = chalk.yellow(' [Installed]');
|
|
466
|
-
} else if (hasKey) {
|
|
467
|
-
status = chalk.cyan(' [API Key Only]');
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
if (toolConfig.isDefault) {
|
|
471
|
-
status += chalk.bold.magenta(' ★ DEFAULT');
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
choices.push({
|
|
475
|
-
name: `${toolConfig.name} - ${toolConfig.description}${status}`,
|
|
476
|
-
value: tool,
|
|
477
|
-
short: toolConfig.name
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
const { selectedTool } = await inquirer.prompt([
|
|
482
|
-
{
|
|
483
|
-
type: 'list',
|
|
484
|
-
name: 'selectedTool',
|
|
485
|
-
message: 'Select a tool to launch:',
|
|
486
|
-
choices: choices,
|
|
487
|
-
default: defaultTool || 'builtin-editor'
|
|
488
|
-
}
|
|
489
|
-
]);
|
|
490
|
-
|
|
491
|
-
if (selectedTool === 'builtin-editor') {
|
|
492
|
-
await interactiveEditMode();
|
|
493
|
-
return;
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
const { passDirectory } = await inquirer.prompt([
|
|
497
|
-
{
|
|
498
|
-
type: 'confirm',
|
|
499
|
-
name: 'passDirectory',
|
|
500
|
-
message: `Open in current directory (${process.cwd()})?`,
|
|
501
|
-
default: true
|
|
502
|
-
}
|
|
503
|
-
]);
|
|
504
|
-
|
|
505
|
-
const args = passDirectory ? ['.'] : [];
|
|
506
|
-
runTool(selectedTool, args);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
// Setup version
|
|
510
|
-
const packagePath = path.join(__dirname, '../../package.json');
|
|
511
|
-
let version = '2.0.0';
|
|
512
|
-
try {
|
|
513
|
-
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
|
|
514
|
-
version = packageJson.version;
|
|
515
|
-
} catch (error) {
|
|
516
|
-
// Use default version
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
program
|
|
520
|
-
.name('dev')
|
|
521
|
-
.description('Hanzo Dev - Meta AI development CLI with built-in editor and tool orchestration')
|
|
522
|
-
.version(version);
|
|
523
|
-
|
|
524
|
-
// Built-in editor command
|
|
525
|
-
program
|
|
526
|
-
.command('edit [path]')
|
|
527
|
-
.description('Launch built-in editor with file editing and MCP tools')
|
|
528
|
-
.action(async (path) => {
|
|
529
|
-
if (path && fs.existsSync(path)) {
|
|
530
|
-
process.chdir(path);
|
|
531
|
-
}
|
|
532
|
-
await interactiveEditMode();
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
// External tool commands
|
|
536
|
-
Object.entries(TOOLS).forEach(([toolKey, toolConfig]) => {
|
|
537
|
-
if (toolKey === 'hanzo-dev') {
|
|
538
|
-
// Special alias for hanzo-dev
|
|
539
|
-
program
|
|
540
|
-
.command('python [args...]')
|
|
541
|
-
.description('Launch Python hanzo-dev (OpenHands)')
|
|
542
|
-
.action(async (args) => {
|
|
543
|
-
const isInstalled = await isToolInstalled('hanzo-dev');
|
|
544
|
-
if (!isInstalled && !hasUvx()) {
|
|
545
|
-
console.error(chalk.red('Hanzo Dev is not installed.'));
|
|
546
|
-
console.log(chalk.yellow('\nTo install:'));
|
|
547
|
-
console.log(chalk.gray(' pip install hanzo-dev'));
|
|
548
|
-
console.log(chalk.gray(' # or'));
|
|
549
|
-
console.log(chalk.gray(' pip install uv && uvx hanzo-dev'));
|
|
550
|
-
process.exit(1);
|
|
551
|
-
}
|
|
552
|
-
runTool('hanzo-dev', args);
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
program
|
|
557
|
-
.command(`${toolKey} [args...]`)
|
|
558
|
-
.description(`Launch ${toolConfig.name}`)
|
|
559
|
-
.action(async (args) => {
|
|
560
|
-
const isInstalled = await isToolInstalled(toolKey);
|
|
561
|
-
const hasKey = hasApiKey(toolKey);
|
|
562
|
-
|
|
563
|
-
if (!isInstalled && !hasKey) {
|
|
564
|
-
console.error(chalk.red(`${toolConfig.name} is not available.`));
|
|
565
|
-
process.exit(1);
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
runTool(toolKey, args);
|
|
569
|
-
});
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
// List command
|
|
573
|
-
program
|
|
574
|
-
.command('list')
|
|
575
|
-
.alias('ls')
|
|
576
|
-
.description('List all available AI tools and API keys')
|
|
577
|
-
.action(async () => {
|
|
578
|
-
console.log(chalk.bold.cyan('\n📋 AI Tools Status:\n'));
|
|
579
|
-
|
|
580
|
-
const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
|
|
581
|
-
const detectedEnvFiles = envFiles.filter(file => fs.existsSync(path.join(process.cwd(), file)));
|
|
582
|
-
if (detectedEnvFiles.length > 0) {
|
|
583
|
-
console.log(chalk.bold('Environment files:'));
|
|
584
|
-
detectedEnvFiles.forEach(file => {
|
|
585
|
-
console.log(chalk.gray(` 📄 ${file}`));
|
|
586
|
-
});
|
|
587
|
-
console.log();
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
console.log(chalk.bold('Built-in Features:'));
|
|
591
|
-
console.log(chalk.green(' ✓ Interactive Editor') + chalk.gray(' - File editing with view, create, str_replace'));
|
|
592
|
-
console.log(chalk.green(' ✓ MCP Client') + chalk.gray(' - Model Context Protocol tool integration'));
|
|
593
|
-
console.log(chalk.green(' ✓ Function Calling') + chalk.gray(' - Unified tool interface'));
|
|
594
|
-
console.log();
|
|
595
|
-
|
|
596
|
-
if (hasUvx()) {
|
|
597
|
-
console.log(chalk.bold('Package Manager:'));
|
|
598
|
-
console.log(chalk.green(' ✓ uvx available (can run Python tools without installation)'));
|
|
599
|
-
console.log();
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
console.log(chalk.bold('External Tools:'));
|
|
603
|
-
for (const [toolKey, toolConfig] of Object.entries(TOOLS)) {
|
|
604
|
-
const isInstalled = await isToolInstalled(toolKey);
|
|
605
|
-
const hasKey = hasApiKey(toolKey);
|
|
606
|
-
|
|
607
|
-
let status = chalk.red('✗ Not Available');
|
|
608
|
-
if (toolKey === 'hanzo-dev' && hasUvx()) {
|
|
609
|
-
status = chalk.green('✓ Ready (via uvx)');
|
|
610
|
-
} else if (isInstalled && hasKey) {
|
|
611
|
-
status = chalk.green('✓ Ready (Installed + API Key)');
|
|
612
|
-
} else if (isInstalled) {
|
|
613
|
-
status = chalk.yellow('⚠ Installed (No API Key)');
|
|
614
|
-
} else if (hasKey) {
|
|
615
|
-
status = chalk.cyan('☁ API Mode (Not Installed)');
|
|
616
|
-
}
|
|
617
|
-
|
|
618
|
-
let displayName = toolConfig.color(toolConfig.name);
|
|
619
|
-
if (toolConfig.isDefault) {
|
|
620
|
-
displayName += chalk.bold.magenta(' ★');
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
console.log(` ${status} ${displayName}`);
|
|
624
|
-
console.log(chalk.gray(` ${toolConfig.description}`));
|
|
625
|
-
}
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
// Status command
|
|
629
|
-
program
|
|
630
|
-
.command('status')
|
|
631
|
-
.description('Show current working directory and environment')
|
|
632
|
-
.action(() => {
|
|
633
|
-
const config = new ConfigManager();
|
|
634
|
-
|
|
635
|
-
console.log(chalk.bold.cyan('\n📊 Hanzo Dev Status\n'));
|
|
636
|
-
console.log(`Current Directory: ${chalk.green(process.cwd())}`);
|
|
637
|
-
console.log(`User: ${chalk.green(os.userInfo().username)}`);
|
|
638
|
-
console.log(`Node Version: ${chalk.green(process.version)}`);
|
|
639
|
-
console.log(`Platform: ${chalk.green(os.platform())}`);
|
|
640
|
-
console.log(`Dev Version: ${chalk.green(version)}`);
|
|
641
|
-
|
|
642
|
-
if (hasUvx()) {
|
|
643
|
-
console.log(`UV/UVX: ${chalk.green('✓ Available')}`);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
const envFiles = ['.env', '.env.local', '.env.development', '.env.production'];
|
|
647
|
-
const detectedEnvFiles = envFiles.filter(file => fs.existsSync(path.join(process.cwd(), file)));
|
|
648
|
-
if (detectedEnvFiles.length > 0) {
|
|
649
|
-
console.log(`\nEnvironment Files:`);
|
|
650
|
-
detectedEnvFiles.forEach(file => {
|
|
651
|
-
console.log(chalk.green(` ✓ ${file}`));
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
console.log(`\nConfiguration:`);
|
|
656
|
-
const cfg = config.getConfig();
|
|
657
|
-
console.log(` Default Agent: ${chalk.yellow(cfg.defaultAgent)}`);
|
|
658
|
-
console.log(` Runtime: ${chalk.yellow(cfg.runtime || 'cli')}`);
|
|
659
|
-
console.log(` Confirmation Mode: ${chalk.yellow(cfg.security.confirmationMode ? 'On' : 'Off')}`);
|
|
660
|
-
});
|
|
661
|
-
|
|
662
|
-
// Workspace command - unified workspace with shell, editor, browser, planner
|
|
663
|
-
program
|
|
664
|
-
.command('workspace')
|
|
665
|
-
.alias('ws')
|
|
666
|
-
.description('Launch unified workspace with shell, editor, browser, and planner')
|
|
667
|
-
.action(async () => {
|
|
668
|
-
const session = new WorkspaceSession();
|
|
669
|
-
await session.start();
|
|
670
|
-
});
|
|
671
|
-
|
|
672
|
-
// Swarm command - spawn multiple agents for parallel work
|
|
673
|
-
program
|
|
674
|
-
.command('swarm [path]')
|
|
675
|
-
.description('Spawn agent swarm for codebase (one agent per file/directory)')
|
|
676
|
-
.option('-t, --type <type>', 'Agent type (claude-code, aider, openhands)', 'claude-code')
|
|
677
|
-
.option('-s, --strategy <strategy>', 'Assignment strategy (one-per-file, one-per-directory, by-complexity)', 'one-per-file')
|
|
678
|
-
.action(async (path, options) => {
|
|
679
|
-
const targetPath = path || process.cwd();
|
|
680
|
-
const network = new PeerAgentNetwork();
|
|
681
|
-
|
|
682
|
-
try {
|
|
683
|
-
await network.spawnAgentsForCodebase(targetPath, options.type, options.strategy);
|
|
684
|
-
|
|
685
|
-
// Show network status
|
|
686
|
-
const status = network.getNetworkStatus();
|
|
687
|
-
console.log(chalk.cyan('\n📊 Network Status:'));
|
|
688
|
-
console.log(` Agents: ${status.totalAgents} (${status.activeAgents} active)`);
|
|
689
|
-
console.log(` Connections: ${status.totalConnections}`);
|
|
690
|
-
|
|
691
|
-
// Interactive swarm control
|
|
692
|
-
const { action } = await inquirer.prompt([{
|
|
693
|
-
type: 'list',
|
|
694
|
-
name: 'action',
|
|
695
|
-
message: 'What would you like to do?',
|
|
696
|
-
choices: [
|
|
697
|
-
{ name: 'Execute task on swarm', value: 'task' },
|
|
698
|
-
{ name: 'Run parallel tasks', value: 'parallel' },
|
|
699
|
-
{ name: 'Show network status', value: 'status' },
|
|
700
|
-
{ name: 'Exit', value: 'exit' }
|
|
701
|
-
]
|
|
702
|
-
}]);
|
|
703
|
-
|
|
704
|
-
if (action === 'task') {
|
|
705
|
-
const { task } = await inquirer.prompt([{
|
|
706
|
-
type: 'input',
|
|
707
|
-
name: 'task',
|
|
708
|
-
message: 'Enter task for swarm:'
|
|
709
|
-
}]);
|
|
710
|
-
|
|
711
|
-
await network.coordinateSwarm(task);
|
|
712
|
-
} else if (action === 'parallel') {
|
|
713
|
-
console.log(chalk.yellow('Enter tasks (one per line, empty line to finish):'));
|
|
714
|
-
const tasks: Array<{task: string}> = [];
|
|
715
|
-
|
|
716
|
-
while (true) {
|
|
717
|
-
const { task } = await inquirer.prompt([{
|
|
718
|
-
type: 'input',
|
|
719
|
-
name: 'task',
|
|
720
|
-
message: '>'
|
|
721
|
-
}]);
|
|
722
|
-
|
|
723
|
-
if (!task) break;
|
|
724
|
-
tasks.push({ task });
|
|
725
|
-
}
|
|
726
|
-
|
|
727
|
-
if (tasks.length > 0) {
|
|
728
|
-
await network.executeParallelTasks(tasks);
|
|
729
|
-
}
|
|
730
|
-
} else if (action === 'status') {
|
|
731
|
-
const status = network.getNetworkStatus();
|
|
732
|
-
console.log(chalk.cyan('\n📊 Detailed Network Status:'));
|
|
733
|
-
console.log(JSON.stringify(status, null, 2));
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
await network.cleanup();
|
|
737
|
-
} catch (error) {
|
|
738
|
-
console.error(chalk.red(`Swarm error: ${error}`));
|
|
739
|
-
await network.cleanup();
|
|
740
|
-
}
|
|
741
|
-
});
|
|
742
|
-
|
|
743
|
-
// Agent command - run a task with CodeAct agent
|
|
744
|
-
program
|
|
745
|
-
.command('agent <task>')
|
|
746
|
-
.description('Execute a task using CodeAct agent with automatic planning and error correction')
|
|
747
|
-
.action(async (task) => {
|
|
748
|
-
const agent = new CodeActAgent();
|
|
749
|
-
try {
|
|
750
|
-
await agent.executeTask(task);
|
|
751
|
-
} catch (error) {
|
|
752
|
-
console.error(chalk.red(`Agent error: ${error}`));
|
|
753
|
-
}
|
|
754
|
-
});
|
|
755
|
-
|
|
756
|
-
// Benchmark command - run SWE-bench evaluation
|
|
757
|
-
program
|
|
758
|
-
.command('benchmark')
|
|
759
|
-
.alias('bench')
|
|
760
|
-
.description('Run SWE-bench evaluation to measure performance')
|
|
761
|
-
.option('-d, --dataset <dataset>', 'Dataset to use (swe-bench, swe-bench-lite, custom)', 'swe-bench-lite')
|
|
762
|
-
.option('-a, --agents <number>', 'Number of agents for parallel execution', '5')
|
|
763
|
-
.option('-p, --parallel', 'Run tasks in parallel', true)
|
|
764
|
-
.option('-t, --timeout <ms>', 'Timeout per task in milliseconds', '300000')
|
|
765
|
-
.option('-o, --output <file>', 'Output file for results', 'benchmark-results.json')
|
|
766
|
-
.option('--provider <provider>', 'LLM provider (claude, openai, gemini, local)')
|
|
767
|
-
.option('--max-tasks <number>', 'Maximum number of tasks to run')
|
|
768
|
-
.action(async (options) => {
|
|
769
|
-
console.log(chalk.bold.cyan('\n🏃 Starting Hanzo Dev Benchmark\n'));
|
|
770
|
-
|
|
771
|
-
// Parse options
|
|
772
|
-
const config: BenchmarkConfig = {
|
|
773
|
-
dataset: options.dataset as any,
|
|
774
|
-
agents: parseInt(options.agents),
|
|
775
|
-
parallel: options.parallel !== 'false',
|
|
776
|
-
timeout: parseInt(options.timeout),
|
|
777
|
-
output: options.output,
|
|
778
|
-
maxTasks: options.maxTasks ? parseInt(options.maxTasks) : undefined
|
|
779
|
-
};
|
|
780
|
-
|
|
781
|
-
// Set provider if specified
|
|
782
|
-
if (options.provider) {
|
|
783
|
-
const providers = ConfigurableAgentLoop.getAvailableProviders();
|
|
784
|
-
const provider = providers.find(p =>
|
|
785
|
-
p.type === options.provider ||
|
|
786
|
-
p.name.toLowerCase().includes(options.provider.toLowerCase())
|
|
787
|
-
);
|
|
788
|
-
|
|
789
|
-
if (provider) {
|
|
790
|
-
config.provider = provider;
|
|
791
|
-
} else {
|
|
792
|
-
console.error(chalk.red(`Provider '${options.provider}' not found or not configured`));
|
|
793
|
-
console.log(chalk.yellow('\nAvailable providers:'));
|
|
794
|
-
providers.forEach(p => {
|
|
795
|
-
console.log(` - ${p.name} (${p.type})`);
|
|
796
|
-
});
|
|
797
|
-
process.exit(1);
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
// Run benchmark
|
|
802
|
-
const runner = new BenchmarkRunner(config);
|
|
803
|
-
|
|
804
|
-
try {
|
|
805
|
-
await runner.run();
|
|
806
|
-
console.log(chalk.green('\n✅ Benchmark completed successfully'));
|
|
807
|
-
} catch (error) {
|
|
808
|
-
console.error(chalk.red(`\n❌ Benchmark failed: ${error}`));
|
|
809
|
-
process.exit(1);
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
|
|
813
|
-
// Add global options for provider and swarm
|
|
814
|
-
program
|
|
815
|
-
.option('--claude', 'Use Claude AI provider')
|
|
816
|
-
.option('--openai', 'Use OpenAI provider')
|
|
817
|
-
.option('--gemini', 'Use Gemini provider')
|
|
818
|
-
.option('--grok', 'Use Grok provider')
|
|
819
|
-
.option('--local', 'Use local AI provider')
|
|
820
|
-
.option('--swarm <count>', 'Launch swarm of agents (up to 100)')
|
|
821
|
-
.option('-p, --prompt <prompt>', 'Task prompt for agents');
|
|
822
|
-
|
|
823
|
-
// Swarm mode function
|
|
824
|
-
async function runSwarmMode(options: any): Promise<void> {
|
|
825
|
-
// Determine provider
|
|
826
|
-
let provider: SwarmOptions['provider'] = 'claude';
|
|
827
|
-
if (options.claude) provider = 'claude';
|
|
828
|
-
else if (options.openai) provider = 'openai';
|
|
829
|
-
else if (options.gemini) provider = 'gemini';
|
|
830
|
-
else if (options.grok) provider = 'grok';
|
|
831
|
-
else if (options.local) provider = 'local';
|
|
832
|
-
|
|
833
|
-
// Parse swarm count
|
|
834
|
-
const count = Math.min(parseInt(options.swarm) || 5, 100);
|
|
835
|
-
|
|
836
|
-
if (!options.prompt) {
|
|
837
|
-
console.error(chalk.red('Error: --prompt is required when using --swarm'));
|
|
838
|
-
process.exit(1);
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
const swarmOptions: SwarmOptions = {
|
|
842
|
-
provider,
|
|
843
|
-
count,
|
|
844
|
-
prompt: options.prompt,
|
|
845
|
-
cwd: process.cwd(),
|
|
846
|
-
autoLogin: true
|
|
847
|
-
};
|
|
848
|
-
|
|
849
|
-
console.log(chalk.bold.cyan(`\n🐝 Hanzo Dev Swarm Mode\n`));
|
|
850
|
-
console.log(chalk.gray(`Provider: ${provider}`));
|
|
851
|
-
console.log(chalk.gray(`Agents: ${count}`));
|
|
852
|
-
console.log(chalk.gray(`Prompt: ${options.prompt}\n`));
|
|
853
|
-
|
|
854
|
-
const runner = new SwarmRunner(swarmOptions);
|
|
855
|
-
|
|
856
|
-
// Check authentication
|
|
857
|
-
const hasAuth = await runner.ensureProviderAuth();
|
|
858
|
-
if (!hasAuth) {
|
|
859
|
-
console.error(chalk.red(`\nError: ${provider} is not authenticated`));
|
|
860
|
-
console.log(chalk.yellow('\nTo authenticate:'));
|
|
861
|
-
|
|
862
|
-
switch (provider) {
|
|
863
|
-
case 'claude':
|
|
864
|
-
console.log(chalk.gray(' 1. Set ANTHROPIC_API_KEY environment variable'));
|
|
865
|
-
console.log(chalk.gray(' 2. Run: claude login'));
|
|
866
|
-
break;
|
|
867
|
-
case 'openai':
|
|
868
|
-
console.log(chalk.gray(' Set OPENAI_API_KEY environment variable'));
|
|
869
|
-
break;
|
|
870
|
-
case 'gemini':
|
|
871
|
-
console.log(chalk.gray(' Set GOOGLE_API_KEY or GEMINI_API_KEY environment variable'));
|
|
872
|
-
break;
|
|
873
|
-
case 'grok':
|
|
874
|
-
console.log(chalk.gray(' Set GROK_API_KEY environment variable'));
|
|
875
|
-
break;
|
|
876
|
-
}
|
|
877
|
-
process.exit(1);
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
try {
|
|
881
|
-
await runner.run();
|
|
882
|
-
} catch (error) {
|
|
883
|
-
console.error(chalk.red(`\nSwarm error: ${error}`));
|
|
884
|
-
process.exit(1);
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
// Default action
|
|
889
|
-
program
|
|
890
|
-
.action(async (options) => {
|
|
891
|
-
// Check if swarm mode is requested
|
|
892
|
-
if (options.swarm) {
|
|
893
|
-
await runSwarmMode(options);
|
|
894
|
-
return;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
// Check if a specific provider is requested
|
|
898
|
-
if (options.claude || options.openai || options.gemini || options.grok || options.local) {
|
|
899
|
-
let provider = 'claude';
|
|
900
|
-
if (options.claude) provider = 'claude';
|
|
901
|
-
else if (options.openai) provider = 'openai';
|
|
902
|
-
else if (options.gemini) provider = 'gemini';
|
|
903
|
-
else if (options.grok) provider = 'grok';
|
|
904
|
-
else if (options.local) provider = 'local';
|
|
905
|
-
|
|
906
|
-
// Map provider to tool name
|
|
907
|
-
const toolMap: Record<string, string> = {
|
|
908
|
-
claude: 'claude',
|
|
909
|
-
openai: 'codex',
|
|
910
|
-
gemini: 'gemini',
|
|
911
|
-
grok: 'grok',
|
|
912
|
-
local: 'hanzo-dev'
|
|
913
|
-
};
|
|
914
|
-
|
|
915
|
-
const toolName = toolMap[provider];
|
|
916
|
-
if (toolName && TOOLS[toolName as keyof typeof TOOLS]) {
|
|
917
|
-
console.log(chalk.gray(`Launching ${TOOLS[toolName as keyof typeof TOOLS].name}...`));
|
|
918
|
-
runTool(toolName, options.prompt ? [options.prompt] : ['.']);
|
|
919
|
-
return;
|
|
920
|
-
}
|
|
921
|
-
}
|
|
922
|
-
|
|
923
|
-
const defaultTool = await getDefaultTool();
|
|
924
|
-
if (defaultTool && process.argv.length === 2) {
|
|
925
|
-
console.log(chalk.gray(`Auto-launching ${TOOLS[defaultTool as keyof typeof TOOLS].name}...`));
|
|
926
|
-
runTool(defaultTool, ['.']);
|
|
927
|
-
} else {
|
|
928
|
-
interactiveMode();
|
|
929
|
-
}
|
|
930
|
-
});
|
|
931
|
-
|
|
932
|
-
// Parse arguments
|
|
933
|
-
program.parse();
|
|
934
|
-
|
|
935
|
-
// If no arguments, run interactive mode
|
|
936
|
-
if (process.argv.length === 2) {
|
|
937
|
-
(async () => {
|
|
938
|
-
const defaultTool = await getDefaultTool();
|
|
939
|
-
if (defaultTool) {
|
|
940
|
-
console.log(chalk.gray(`Auto-launching ${TOOLS[defaultTool as keyof typeof TOOLS].name}...`));
|
|
941
|
-
runTool(defaultTool, ['.']);
|
|
942
|
-
} else {
|
|
943
|
-
interactiveMode();
|
|
944
|
-
}
|
|
945
|
-
})();
|
|
946
|
-
}
|