vibe-weaver 1.0.0

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/src/index.ts ADDED
@@ -0,0 +1,797 @@
1
+ /**
2
+ * Vibe-Weaver CLI - Main Entry Point
3
+ * A production-ready terminal agent that beats Claude Code
4
+ */
5
+
6
+ import { Command } from 'commander';
7
+ import chalk from 'chalk';
8
+ import { login, logout, whoami, loginGithub, isLoggedIn } from './auth/index.js';
9
+ import { generateCode, getCreditCost } from './lib/vibe-api.js';
10
+ import { writeCodeBlocks } from './lib/code-parser.js';
11
+ import { readFile, writeFile, listDir, grep } from './tools/files.js';
12
+ import { runShell, getInstallCommand, getBuildCommand } from './tools/shell.js';
13
+ import {
14
+ getStatus, getBranch, commit, commitAll, push, pull,
15
+ getDiff, isRepo, fullPRWorkflow
16
+ } from './tools/git.js';
17
+ import inquirer from 'inquirer';
18
+ import path from 'path';
19
+
20
+ const program = new Command();
21
+
22
+ program
23
+ .name('vibe-weaver')
24
+ .description('Vibe-Weaver CLI - Production-ready terminal agent that beats Claude Code')
25
+ .version('1.0.0');
26
+
27
+ // ============================================
28
+ // Auth Commands
29
+ // ============================================
30
+
31
+ program
32
+ .command('login')
33
+ .description('Log in with your Vibe-Weaver API key')
34
+ .option('-k, --api-key <key>', 'API key (vw_...)')
35
+ .option('-o, --open', 'Open browser to create API key')
36
+ .option('-g, --github', 'Also configure GitHub token')
37
+ .action(async (options) => {
38
+ try {
39
+ await login({ apiKey: options.apiKey, openBrowser: options.open });
40
+
41
+ if (options.github) {
42
+ await loginGithub();
43
+ }
44
+ } catch (error) {
45
+ console.error(chalk.red('Login failed:'), error instanceof Error ? error.message : error);
46
+ process.exit(1);
47
+ }
48
+ });
49
+
50
+ program
51
+ .command('logout')
52
+ .description('Log out and clear stored credentials')
53
+ .action(async () => {
54
+ try {
55
+ await logout();
56
+ } catch (error) {
57
+ console.error(chalk.red('Logout failed:'), error instanceof Error ? error.message : error);
58
+ process.exit(1);
59
+ }
60
+ });
61
+
62
+ program
63
+ .command('whoami')
64
+ .description('Show current user info and credits')
65
+ .action(async () => {
66
+ try {
67
+ await whoami();
68
+ } catch (error) {
69
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
70
+ process.exit(1);
71
+ }
72
+ });
73
+
74
+ // ============================================
75
+ // Generate Commands
76
+ // ============================================
77
+
78
+ program
79
+ .command('generate')
80
+ .description('Generate code using Vibe-Weaver AI')
81
+ .option('-g, --goal <text>', 'Goal/prompt for code generation')
82
+ .option('-p, --platform <type>', 'Platform: web, roblox, mobile, desktop, api', 'web')
83
+ .option('-l, --language <lang>', 'Language: typescript, javascript, python, lua, rust', 'typescript')
84
+ .option('-m, --mode <mode>', 'Model: fast, balanced, deep_reasoning, refactor', 'balanced')
85
+ .option('-o, --out <dir>', 'Output directory', '.')
86
+ .option('-y, --yes', 'Auto-approve file writes')
87
+ .option('-d, --dry-run', 'Show what would be generated without writing')
88
+ .action(async (options) => {
89
+ if (!options.goal) {
90
+ console.error(chalk.red('Error: --goal is required'));
91
+ process.exit(1);
92
+ }
93
+
94
+ try {
95
+ const loggedIn = await isLoggedIn();
96
+ if (!loggedIn) {
97
+ console.error(chalk.red('Not logged in. Run "vibe-weaver login" first.'));
98
+ process.exit(1);
99
+ }
100
+
101
+ console.log(chalk.cyan('\n🎨 Vibe-Weaver Code Generation'));
102
+ console.log(chalk.gray(`Mode: ${options.mode} | Platform: ${options.platform} | Language: ${options.language}`));
103
+ console.log(chalk.gray(`Cost: ~${getCreditCost(options.mode)} credits\n`));
104
+
105
+ const result = await generateCode({
106
+ goal: options.goal,
107
+ platform: options.platform,
108
+ language: options.language,
109
+ modelId: options.mode
110
+ });
111
+
112
+ console.log(chalk.gray('Explanation:'));
113
+ console.log(result.explanation);
114
+
115
+ console.log(chalk.gray('\nQuality Score:'), `${result.quality.score}/100`);
116
+
117
+ if (result.parsed.blocks.length > 0) {
118
+ console.log(chalk.gray('\nGenerated Files:'));
119
+ for (const block of result.parsed.blocks) {
120
+ console.log(` - ${chalk.green(block.path)} (${block.language})`);
121
+ }
122
+
123
+ if (!options.dryRun) {
124
+ const writeResult = await writeCodeBlocks(result.parsed.blocks, path.resolve(options.out));
125
+
126
+ console.log(chalk.gray('\nWritten:'));
127
+ for (const file of writeResult.success) {
128
+ console.log(` ✓ ${file}`);
129
+ }
130
+
131
+ if (writeResult.failed.length > 0) {
132
+ console.log(chalk.red('\nFailed:'));
133
+ for (const file of writeResult.failed) {
134
+ console.log(` ✗ ${file.path}: ${file.error}`);
135
+ }
136
+ }
137
+ } else {
138
+ console.log(chalk.yellow('\n[DRY-RUN] Files not written'));
139
+ }
140
+ }
141
+
142
+ console.log(chalk.green('\n✓ Generation complete'));
143
+ } catch (error) {
144
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
145
+ process.exit(1);
146
+ }
147
+ });
148
+
149
+ // ============================================
150
+ // Agent Commands
151
+ // ============================================
152
+
153
+ program
154
+ .command('run')
155
+ .description('Run the interactive agent loop (REPL)')
156
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
157
+ .option('-y, --yes', 'Auto-approve all operations')
158
+ .option('-d, --dry-run', 'Preview changes without applying')
159
+ .action(async (options) => {
160
+ const loggedIn = await isLoggedIn();
161
+ if (!loggedIn) {
162
+ console.error(chalk.red('Not logged in. Run "vibe-weaver login" first.'));
163
+ process.exit(1);
164
+ }
165
+
166
+ console.log(chalk.cyan('🎯 Vibe-Weaver Interactive Agent'));
167
+ console.log(chalk.gray('Type your goal or task. Press Ctrl+C to exit.\n'));
168
+
169
+ // Interactive REPL
170
+ const readline = await import('readline');
171
+ const rl = readline.createInterface({
172
+ input: process.stdin,
173
+ output: process.stdout
174
+ });
175
+
176
+ const askQuestion = () => {
177
+ rl.question(chalk.green('\n> '), async (input) => {
178
+ if (!input.trim()) {
179
+ askQuestion();
180
+ return;
181
+ }
182
+
183
+ if (input.toLowerCase() === 'exit' || input.toLowerCase() === 'quit') {
184
+ rl.close();
185
+ return;
186
+ }
187
+
188
+ try {
189
+ console.log(chalk.gray('\nGenerating code...'));
190
+ const result = await generateCode({ goal: input });
191
+
192
+ if (result.parsed.blocks.length > 0) {
193
+ const writeResult = await writeCodeBlocks(result.parsed.blocks, options.cwd);
194
+
195
+ console.log(chalk.green('\n✓ Generated files:'));
196
+ for (const file of writeResult.success) {
197
+ console.log(` ✓ ${file}`);
198
+ }
199
+
200
+ if (writeResult.failed.length > 0) {
201
+ console.log(chalk.red('\n✗ Failed:'));
202
+ for (const file of writeResult.failed) {
203
+ console.log(` ✗ ${file.path}: ${file.error}`);
204
+ }
205
+ }
206
+ }
207
+ } catch (error) {
208
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
209
+ }
210
+
211
+ askQuestion();
212
+ });
213
+ };
214
+
215
+ askQuestion();
216
+ });
217
+
218
+ // ============================================
219
+ // File Commands
220
+ // ============================================
221
+
222
+ program
223
+ .command('read')
224
+ .description('Read a file')
225
+ .argument('<file>', 'File path to read')
226
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
227
+ .action(async (file, options) => {
228
+ try {
229
+ const result = await readFile(file, options.cwd);
230
+ if (result.success && result.content) {
231
+ console.log(result.content);
232
+ } else {
233
+ console.error(chalk.red(`Error: ${result.error}`));
234
+ process.exit(1);
235
+ }
236
+ } catch (error) {
237
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
238
+ process.exit(1);
239
+ }
240
+ });
241
+
242
+ program
243
+ .command('write')
244
+ .description('Write content to a file')
245
+ .argument('<file>', 'File path to write')
246
+ .argument('<content>', 'Content to write')
247
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
248
+ .option('-d, --dry-run', 'Preview without writing')
249
+ .action(async (file, content, options) => {
250
+ try {
251
+ const result = await writeFile(file, content, options.cwd, { dryRun: options.dryRun });
252
+ if (result.success) {
253
+ console.log(chalk.green(`✓ Written to ${file}`));
254
+ } else {
255
+ console.error(chalk.red(`Error: ${result.error}`));
256
+ process.exit(1);
257
+ }
258
+ } catch (error) {
259
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
260
+ process.exit(1);
261
+ }
262
+ });
263
+
264
+ program
265
+ .command('ls')
266
+ .description('List files in a directory')
267
+ .argument('[dir]', 'Directory to list', '.')
268
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
269
+ .action(async (dir, options) => {
270
+ try {
271
+ const entries = await listDir(dir, options.cwd);
272
+ for (const entry of entries) {
273
+ const prefix = entry.isDirectory ? chalk.blue('📁') : chalk.gray('📄');
274
+ console.log(`${prefix} ${entry.path}`);
275
+ }
276
+ } catch (error) {
277
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
278
+ process.exit(1);
279
+ }
280
+ });
281
+
282
+ program
283
+ .command('grep')
284
+ .description('Search for a pattern in files')
285
+ .argument('<pattern>', 'Pattern to search for')
286
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
287
+ .option('-r, --recursive', 'Search recursively', true)
288
+ .action(async (pattern, options) => {
289
+ try {
290
+ const results = await grep(pattern, options.cwd, { recursive: options.recursive });
291
+ if (results.length === 0) {
292
+ console.log(chalk.yellow('No matches found'));
293
+ return;
294
+ }
295
+
296
+ for (const result of results) {
297
+ console.log(`${chalk.gray(`${result.file}:${result.line}`)} ${result.content}`);
298
+ }
299
+ } catch (error) {
300
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
301
+ process.exit(1);
302
+ }
303
+ });
304
+
305
+ // ============================================
306
+ // Shell Commands
307
+ // ============================================
308
+
309
+ program
310
+ .command('exec')
311
+ .description('Execute a shell command')
312
+ .argument('<command>', 'Command to execute')
313
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
314
+ .option('-d, --dry-run', 'Preview without executing')
315
+ .action(async (command, options) => {
316
+ try {
317
+ if (options.dryRun) {
318
+ console.log(chalk.yellow(`[DRY-RUN] Would execute: ${command}`));
319
+ return;
320
+ }
321
+
322
+ console.log(chalk.gray(`$ ${command}`));
323
+ const result = await runShell(command, { cwd: options.cwd });
324
+
325
+ if (result.stdout) console.log(result.stdout);
326
+ if (result.stderr) console.error(chalk.red(result.stderr));
327
+
328
+ process.exit(result.exitCode);
329
+ } catch (error) {
330
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
331
+ process.exit(1);
332
+ }
333
+ });
334
+
335
+ // ============================================
336
+ // Git Commands
337
+ // ============================================
338
+
339
+ program
340
+ .command('status')
341
+ .description('Show git status')
342
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
343
+ .action(async (options) => {
344
+ try {
345
+ const status = await getStatus(options.cwd);
346
+
347
+ console.log(chalk.gray(`Branch: ${status.current || '(none)'}`));
348
+ if (status.tracking) {
349
+ console.log(chalk.gray(`Tracking: ${status.tracking}`));
350
+ }
351
+
352
+ if (status.staged.length > 0) {
353
+ console.log(chalk.green('\nStaged:'));
354
+ for (const file of status.staged) {
355
+ console.log(` ${chalk.green('+')} ${file}`);
356
+ }
357
+ }
358
+
359
+ if (status.modified.length > 0) {
360
+ console.log(chalk.yellow('\nModified:'));
361
+ for (const file of status.modified) {
362
+ console.log(` ${chalk.yellow('M')} ${file}`);
363
+ }
364
+ }
365
+
366
+ if (status.untracked.length > 0) {
367
+ console.log(chalk.gray('\nUntracked:'));
368
+ for (const file of status.untracked) {
369
+ console.log(` ${chalk.gray('?')} ${file}`);
370
+ }
371
+ }
372
+ } catch (error) {
373
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
374
+ process.exit(1);
375
+ }
376
+ });
377
+
378
+ program
379
+ .command('commit')
380
+ .description('Commit changes with a message')
381
+ .argument('<message>', 'Commit message')
382
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
383
+ .option('-a, --all', 'Stage all changes', false)
384
+ .action(async (message, options) => {
385
+ try {
386
+ if (options.all) {
387
+ const result = await commitAll(message, options.cwd);
388
+ if (result.success) {
389
+ console.log(chalk.green(`✓ Committed: ${result.message}`));
390
+ } else {
391
+ console.error(chalk.red(`Error: ${result.error}`));
392
+ process.exit(1);
393
+ }
394
+ } else {
395
+ const result = await commit(message, options.cwd);
396
+ if (result.success) {
397
+ console.log(chalk.green(`✓ Committed: ${result.message}`));
398
+ } else {
399
+ console.error(chalk.red(`Error: ${result.error}`));
400
+ process.exit(1);
401
+ }
402
+ }
403
+ } catch (error) {
404
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
405
+ process.exit(1);
406
+ }
407
+ });
408
+
409
+ program
410
+ .command('push')
411
+ .description('Push to remote')
412
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
413
+ .option('-f, --force', 'Force push', false)
414
+ .action(async (options) => {
415
+ try {
416
+ const result = await push({ force: options.force }, options.cwd);
417
+ if (result.success) {
418
+ console.log(chalk.green('✓ Pushed successfully'));
419
+ } else {
420
+ console.error(chalk.red(`Error: ${result.error}`));
421
+ process.exit(1);
422
+ }
423
+ } catch (error) {
424
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
425
+ process.exit(1);
426
+ }
427
+ });
428
+
429
+ program
430
+ .command('pull')
431
+ .description('Pull from remote')
432
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
433
+ .action(async (options) => {
434
+ try {
435
+ const result = await pull({}, options.cwd);
436
+ if (result.success) {
437
+ console.log(chalk.green('✓ Pulled successfully'));
438
+ } else {
439
+ console.error(chalk.red(`Error: ${result.error}`));
440
+ process.exit(1);
441
+ }
442
+ } catch (error) {
443
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
444
+ process.exit(1);
445
+ }
446
+ });
447
+
448
+ // ============================================
449
+ // PR Commands
450
+ // ============================================
451
+
452
+ program
453
+ .command('pr')
454
+ .description('Create a pull request with AI-generated summary')
455
+ .option('-t, --title <title>', 'PR title')
456
+ .option('-b, --body <body>', 'PR body')
457
+ .option('-o, --owner <owner>', 'Repository owner')
458
+ .option('-r, --repo <repo>', 'Repository name')
459
+ .option('-B, --base <branch>', 'Base branch', 'main')
460
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
461
+ .action(async (options) => {
462
+ try {
463
+ const isGitRepo = await isRepo(options.cwd);
464
+ if (!isGitRepo) {
465
+ console.error(chalk.red('Not a git repository'));
466
+ process.exit(1);
467
+ }
468
+
469
+ // Get current branch
470
+ const currentBranch = await getBranch(options.cwd);
471
+
472
+ // If no title/body provided, generate with AI
473
+ let prTitle = options.title;
474
+ let prBody = options.body;
475
+
476
+ if (!prTitle || !prBody) {
477
+ console.log(chalk.gray('Generating PR description with AI...'));
478
+ const result = await generateCode({
479
+ goal: `Create a descriptive pull request title and body for the changes in branch ${currentBranch}. Focus on what changed and why.`,
480
+ modelId: 'balanced'
481
+ });
482
+
483
+ // Parse the result
484
+ const lines = result.explanation.split('\n').filter(l => l.trim());
485
+ if (!prTitle) prTitle = lines[0] || `Update ${currentBranch}`;
486
+ if (!prBody) prBody = lines.slice(1).join('\n') || result.explanation;
487
+ }
488
+
489
+ // Prompt for required options if not provided
490
+ if (!options.owner || !options.repo) {
491
+ const remote = await import('./tools/git.js').then(m => m.getRemote(options.cwd));
492
+ if (remote) {
493
+ // Parse owner/repo from remote URL
494
+ const match = remote.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
495
+ if (match) {
496
+ options.owner = options.owner || match[1];
497
+ options.repo = options.repo || match[2];
498
+ }
499
+ }
500
+ }
501
+
502
+ if (!options.owner || !options.repo) {
503
+ const answers = await inquirer.prompt([
504
+ { type: 'input', name: 'owner', message: 'Repository owner:', when: !options.owner },
505
+ { type: 'input', name: 'repo', message: 'Repository name:', when: !options.repo }
506
+ ]);
507
+ options.owner = options.owner || answers.owner;
508
+ options.repo = options.repo || answers.repo;
509
+ }
510
+
511
+ // Get diff for PR
512
+ const diff = await getDiff({ from: options.base, to: currentBranch }, options.cwd);
513
+ console.log(chalk.gray(`Diff: ${diff.length} characters`));
514
+
515
+ const result = await fullPRWorkflow({
516
+ owner: options.owner,
517
+ repo: options.repo,
518
+ branchName: currentBranch,
519
+ baseBranch: options.base,
520
+ files: [], // Would need to parse diff into files
521
+ commitMessage: prTitle,
522
+ prTitle,
523
+ prBody
524
+ });
525
+
526
+ if (result.success) {
527
+ console.log(chalk.green(`\n✓ PR created: ${result.prUrl}`));
528
+ } else {
529
+ console.error(chalk.red(`Error: ${result.error}`));
530
+ process.exit(1);
531
+ }
532
+ } catch (error) {
533
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
534
+ process.exit(1);
535
+ }
536
+ });
537
+
538
+ // ============================================
539
+ // Build Commands
540
+ // ============================================
541
+
542
+ program
543
+ .command('build')
544
+ .description('Run build command')
545
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
546
+ .option('-d, --dry-run', 'Preview without executing')
547
+ .action(async (options) => {
548
+ try {
549
+ const buildCmd = getBuildCommand(options.cwd);
550
+
551
+ if (options.dryRun) {
552
+ console.log(chalk.yellow(`[DRY-RUN] Would execute: ${buildCmd}`));
553
+ return;
554
+ }
555
+
556
+ console.log(chalk.gray(`$ ${buildCmd}`));
557
+ const result = await runShell(buildCmd, { cwd: options.cwd });
558
+
559
+ if (result.stdout) console.log(result.stdout);
560
+ if (result.stderr) console.error(chalk.red(result.stderr));
561
+
562
+ if (result.success) {
563
+ console.log(chalk.green('\n✓ Build successful'));
564
+ } else {
565
+ console.log(chalk.red(`\n✗ Build failed (exit code ${result.exitCode})`));
566
+ process.exit(result.exitCode);
567
+ }
568
+ } catch (error) {
569
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
570
+ process.exit(1);
571
+ }
572
+ });
573
+
574
+ program
575
+ .command('install')
576
+ .description('Run install command')
577
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
578
+ .option('-d, --dry-run', 'Preview without executing')
579
+ .action(async (options) => {
580
+ try {
581
+ const installCmd = getInstallCommand(options.cwd);
582
+
583
+ if (options.dryRun) {
584
+ console.log(chalk.yellow(`[DRY-RUN] Would execute: ${installCmd}`));
585
+ return;
586
+ }
587
+
588
+ console.log(chalk.gray(`$ ${installCmd}`));
589
+ const result = await runShell(installCmd, { cwd: options.cwd, timeout: 300000 });
590
+
591
+ if (result.stdout) console.log(result.stdout);
592
+ if (result.stderr) console.error(chalk.red(result.stderr));
593
+
594
+ if (result.success) {
595
+ console.log(chalk.green('\n✓ Install successful'));
596
+ } else {
597
+ console.log(chalk.red(`\n✗ Install failed (exit code ${result.exitCode})`));
598
+ process.exit(result.exitCode);
599
+ }
600
+ } catch (error) {
601
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
602
+ process.exit(1);
603
+ }
604
+ });
605
+
606
+ // ============================================
607
+ // MCP Commands
608
+ // ============================================
609
+
610
+ program
611
+ .command('mcp')
612
+ .description('Manage MCP server connections')
613
+ .option('-l, --list', 'List available MCP servers')
614
+ .option('-c, --connect', 'Connect to all enabled MCP servers')
615
+ .option('-s, --status', 'Show MCP server status')
616
+ .action(async (options) => {
617
+ const { MCPManager, getDefaultMCPServers } = await import('./mcp/index.js');
618
+
619
+ if (options.list) {
620
+ const servers = getDefaultMCPServers();
621
+ console.log(chalk.cyan('\n📡 Available MCP Servers\n'));
622
+ for (const server of servers) {
623
+ const status = server.enabled ? chalk.green('enabled') : chalk.gray('disabled');
624
+ console.log(` ${server.name.padEnd(20)} ${status}`);
625
+ }
626
+ return;
627
+ }
628
+
629
+ if (options.connect) {
630
+ const manager = new MCPManager();
631
+ console.log(chalk.cyan('\n🔌 Connecting to MCP servers...\n'));
632
+ const result = await manager.initializeAll();
633
+ console.log(chalk.green(`\n✓ Connected to ${result.success.length} servers`));
634
+ if (result.failed.length > 0) {
635
+ console.log(chalk.red(`✗ Failed to connect to ${result.failed.length} servers`));
636
+ }
637
+ return;
638
+ }
639
+
640
+ if (options.status) {
641
+ const manager = new MCPManager();
642
+ await manager.initializeAll();
643
+ const status = manager.getStatus();
644
+ console.log(chalk.cyan('\n📡 MCP Server Status\n'));
645
+ for (const s of status) {
646
+ const icon = s.connected ? chalk.green('✓') : chalk.red('✗');
647
+ console.log(` ${icon} ${s.name}`);
648
+ }
649
+ return;
650
+ }
651
+
652
+ console.log(chalk.gray('Use --list, --connect, or --status'));
653
+ });
654
+
655
+ // ============================================
656
+ // Swarm Commands
657
+ // ============================================
658
+
659
+ program
660
+ .command('swarm')
661
+ .description('Run multiple agents in parallel for complex tasks')
662
+ .option('-g, --goal <text>', 'Goal for the swarm')
663
+ .option('-p, --parallel <num>', 'Max parallel agents', '3')
664
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
665
+ .action(async (options) => {
666
+ if (!options.goal) {
667
+ console.error(chalk.red('Error: --goal is required'));
668
+ process.exit(1);
669
+ }
670
+
671
+ const loggedIn = await isLoggedIn();
672
+ if (!loggedIn) {
673
+ console.error(chalk.red('Not logged in. Run "vibe-weaver login" first.'));
674
+ process.exit(1);
675
+ }
676
+
677
+ const { runSwarm } = await import('./agent/swarm.js');
678
+ await runSwarm(options.goal, {
679
+ maxParallel: parseInt(options.parallel),
680
+ baseDir: options.cwd
681
+ });
682
+ });
683
+
684
+ // ============================================
685
+ // Task Commands
686
+ // ============================================
687
+
688
+ const taskCmd = program
689
+ .command('task')
690
+ .description('Manage background tasks');
691
+
692
+ taskCmd
693
+ .command('submit')
694
+ .description('Submit a background task')
695
+ .option('-g, --goal <text>', 'Task goal')
696
+ .option('-p, --platform <type>', 'Platform', 'web')
697
+ .option('-m, --mode <mode>', 'Model mode', 'balanced')
698
+ .action(async (options) => {
699
+ if (!options.goal) {
700
+ console.error(chalk.red('Error: --goal is required'));
701
+ process.exit(1);
702
+ }
703
+
704
+ const { submitTask } = await import('./agent/tasks.js');
705
+ await submitTask({
706
+ goal: options.goal,
707
+ platform: options.platform,
708
+ modelId: options.mode
709
+ });
710
+ });
711
+
712
+ taskCmd
713
+ .command('status')
714
+ .description('Check task status')
715
+ .argument('<id>', 'Task ID')
716
+ .action(async (taskId) => {
717
+ const { getTaskStatus, displayTaskStatus } = await import('./agent/tasks.js');
718
+ const status = await getTaskStatus(taskId);
719
+ displayTaskStatus(status);
720
+ });
721
+
722
+ taskCmd
723
+ .command('logs')
724
+ .description('Stream task logs')
725
+ .argument('<id>', 'Task ID')
726
+ .action(async (taskId) => {
727
+ const { streamTaskLogs } = await import('./agent/tasks.js');
728
+ console.log(chalk.gray(`Streaming logs for task ${taskId}...\n`));
729
+
730
+ for await (const log of streamTaskLogs(taskId)) {
731
+ process.stdout.write(log);
732
+ }
733
+ });
734
+
735
+ taskCmd
736
+ .command('wait')
737
+ .description('Wait for task to complete')
738
+ .argument('<id>', 'Task ID')
739
+ .option('-t, --timeout <seconds>', 'Timeout in seconds', '300')
740
+ .action(async (taskId, options) => {
741
+ const { waitForTask, displayTaskStatus } = await import('./agent/tasks.js');
742
+
743
+ try {
744
+ const status = await waitForTask(taskId, {
745
+ pollInterval: 5000,
746
+ onProgress: (s) => {
747
+ process.stdout.write(chalk.gray(`\rStatus: ${s.status}`));
748
+ }
749
+ });
750
+ console.log();
751
+ displayTaskStatus(status);
752
+ } catch (error) {
753
+ console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
754
+ process.exit(1);
755
+ }
756
+ });
757
+
758
+ // ============================================
759
+ // Deploy Commands
760
+ // ============================================
761
+
762
+ program
763
+ .command('deploy')
764
+ .description('Deploy project and verify')
765
+ .option('-i, --project-id <id>', 'Project ID')
766
+ .option('-t, --platform <type>', 'Platform', 'web')
767
+ .option('-v, --verify', 'Verify deployment', true)
768
+ .option('--no-verify', 'Skip verification')
769
+ .option('-c, --cwd <dir>', 'Working directory', process.cwd())
770
+ .action(async (options) => {
771
+ const loggedIn = await isLoggedIn();
772
+ if (!loggedIn) {
773
+ console.error(chalk.red('Not logged in. Run "vibe-weaver login" first.'));
774
+ process.exit(1);
775
+ }
776
+
777
+ const { deploy } = await import('./agent/deploy.js');
778
+ const result = await deploy({
779
+ projectId: options.projectId,
780
+ platform: options.platform,
781
+ verify: options.verify,
782
+ cwd: options.cwd
783
+ });
784
+
785
+ if (result.success) {
786
+ console.log(chalk.green(`\n✓ Deployed to ${result.url}`));
787
+ } else {
788
+ console.error(chalk.red(`\n✗ Deployment failed: ${result.error}`));
789
+ process.exit(1);
790
+ }
791
+ });
792
+
793
+ // ============================================
794
+ // Main entry point
795
+ // ============================================
796
+
797
+ program.parse();