neuro-cli 4.1.1 → 4.1.3
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/dist/agents/base.js +20 -8
- package/dist/agents/orchestrator.js +37 -11
- package/dist/api/openrouter.js +23 -4
- package/dist/core/approval.d.ts +7 -0
- package/dist/core/approval.js +32 -3
- package/dist/core/diff-preview.js +2 -1
- package/dist/core/engine.js +24 -7
- package/dist/index.js +688 -639
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// ============================================================
|
|
3
3
|
// NeuroCLI - Advanced AI Terminal Coding Assistant
|
|
4
|
-
// Main Entry Point - v4.1.
|
|
4
|
+
// Main Entry Point - v4.1.3 with robust error handling
|
|
5
5
|
// ============================================================
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { createInterface } from 'readline';
|
|
@@ -16,7 +16,20 @@ import { HeadlessMode } from './core/headless.js';
|
|
|
16
16
|
import { ShellCompletionGenerator } from './core/shell-completion.js';
|
|
17
17
|
import chalk from 'chalk';
|
|
18
18
|
import { AutoUpdater } from './core/updater.js';
|
|
19
|
-
const VERSION = '4.1.
|
|
19
|
+
const VERSION = '4.1.3';
|
|
20
|
+
// ---- Global Error Handlers (prevent crashes) ----
|
|
21
|
+
process.on('unhandledRejection', (reason) => {
|
|
22
|
+
console.error(chalk.red('\n⚠️ Unhandled promise rejection:'), reason);
|
|
23
|
+
// Don't exit — log and continue
|
|
24
|
+
});
|
|
25
|
+
process.on('uncaughtException', (error) => {
|
|
26
|
+
console.error(chalk.red('\n⚠️ Uncaught exception:'), error.message);
|
|
27
|
+
// Only exit for truly fatal errors
|
|
28
|
+
if (error.message.includes('ENOMEM') || error.message.includes('EADDRINUSE') || error.message.includes('EPERM')) {
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
31
|
+
// Otherwise, try to continue
|
|
32
|
+
});
|
|
20
33
|
// ---- CLI Setup ----
|
|
21
34
|
const program = new Command();
|
|
22
35
|
program
|
|
@@ -236,7 +249,13 @@ program
|
|
|
236
249
|
transport: transport,
|
|
237
250
|
command: transport === 'stdio' ? command : undefined,
|
|
238
251
|
url: isUrl ? command : undefined,
|
|
239
|
-
headers: opts.headers ?
|
|
252
|
+
headers: opts.headers ? (() => { try {
|
|
253
|
+
return JSON.parse(opts.headers);
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
console.log(chalk.red('Invalid JSON for --headers'));
|
|
257
|
+
return undefined;
|
|
258
|
+
} })() : undefined,
|
|
240
259
|
});
|
|
241
260
|
console.log(chalk.green(`MCP server "${name}" added (${transport})`));
|
|
242
261
|
}))
|
|
@@ -430,705 +449,735 @@ async function startInteractive(options) {
|
|
|
430
449
|
completer: completionEngine.complete,
|
|
431
450
|
});
|
|
432
451
|
rl.prompt();
|
|
452
|
+
// Wire the main readline to the approval system to prevent dual-readline bug
|
|
453
|
+
// (double character input when approval prompt and main rl both read stdin)
|
|
454
|
+
engine.approval.setMainReadline(rl);
|
|
433
455
|
let currentMode = 'auto';
|
|
434
456
|
let currentAgent;
|
|
457
|
+
let isProcessing = false;
|
|
435
458
|
rl.on('line', async (line) => {
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
engine
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
case 'agent':
|
|
460
|
-
if (args[0]) {
|
|
461
|
-
const agentNames = Array.from(engine.agents.keys());
|
|
462
|
-
const found = agentNames.find(n => n.toLowerCase() === args[0].toLowerCase());
|
|
463
|
-
if (found) {
|
|
464
|
-
currentAgent = found;
|
|
465
|
-
currentMode = 'direct';
|
|
466
|
-
engine.ui.success(`Switched to agent: ${found}`);
|
|
459
|
+
try {
|
|
460
|
+
const input = line.trim();
|
|
461
|
+
if (!input) {
|
|
462
|
+
rl.prompt();
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
// Prevent input while processing (approval prompt may be active)
|
|
466
|
+
if (isProcessing) {
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
// Add to history
|
|
470
|
+
completionEngine.addHistory(input);
|
|
471
|
+
// Handle slash commands
|
|
472
|
+
if (input.startsWith('/')) {
|
|
473
|
+
const [cmd, ...args] = input.slice(1).split(' ');
|
|
474
|
+
switch (cmd) {
|
|
475
|
+
case 'help':
|
|
476
|
+
printHelp(engine);
|
|
477
|
+
break;
|
|
478
|
+
case 'model':
|
|
479
|
+
if (args[0]) {
|
|
480
|
+
engine.switchModel(args[0]);
|
|
481
|
+
engine.fallback.addFallbackModel(config.defaultModel);
|
|
467
482
|
}
|
|
468
483
|
else {
|
|
469
|
-
engine.ui.
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
else {
|
|
473
|
-
const agents = Array.from(engine.agents.entries()).map(([name]) => ({ name, description: '', model: config.defaultModel }));
|
|
474
|
-
engine.ui.agentList(agents);
|
|
475
|
-
}
|
|
476
|
-
break;
|
|
477
|
-
case 'auto':
|
|
478
|
-
currentMode = 'auto';
|
|
479
|
-
currentAgent = undefined;
|
|
480
|
-
engine.ui.success('Mode: Auto (smart orchestration)');
|
|
481
|
-
break;
|
|
482
|
-
case 'orchestrate':
|
|
483
|
-
case 'plan':
|
|
484
|
-
currentMode = 'agent';
|
|
485
|
-
currentAgent = undefined;
|
|
486
|
-
engine.ui.success('Mode: Multi-agent orchestration');
|
|
487
|
-
break;
|
|
488
|
-
case 'direct':
|
|
489
|
-
currentMode = 'direct';
|
|
490
|
-
engine.ui.success('Mode: Direct (single agent)');
|
|
491
|
-
break;
|
|
492
|
-
case 'plan-mode':
|
|
493
|
-
engine.approval.setMode('plan');
|
|
494
|
-
engine.ui.success(`${engine.approval.getModeIcon()} Permission: plan (read-only)`);
|
|
495
|
-
break;
|
|
496
|
-
case 'stats':
|
|
497
|
-
const stats = engine.getSessionStats();
|
|
498
|
-
engine.ui.sessionStats(stats.inputTokens, stats.outputTokens, stats.cost);
|
|
499
|
-
break;
|
|
500
|
-
case 'clear':
|
|
501
|
-
console.clear();
|
|
502
|
-
engine.ui.banner();
|
|
503
|
-
break;
|
|
504
|
-
case 'theme':
|
|
505
|
-
if (args[0]) {
|
|
506
|
-
config.ui.theme = args[0];
|
|
507
|
-
engine.ui = new TerminalUI(config.ui.theme, config.ui.showTokenCount, config.ui.showCost);
|
|
508
|
-
engine.ui.success(`Theme: ${args[0]}`);
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
console.log('Available themes: dracula, dark, nord, light');
|
|
512
|
-
}
|
|
513
|
-
break;
|
|
514
|
-
case 'resume':
|
|
515
|
-
if (args[0]) {
|
|
516
|
-
const sessionId = args[0] === 'latest' ? engine.sessionManager.list()[0]?.id : args[0];
|
|
517
|
-
if (sessionId) {
|
|
518
|
-
const session = engine.sessionManager.load(sessionId);
|
|
519
|
-
if (session)
|
|
520
|
-
engine.ui.success(`Resumed session: ${sessionId.slice(0, 20)}...`);
|
|
521
|
-
else
|
|
522
|
-
engine.ui.error(`Session not found: ${sessionId}`);
|
|
484
|
+
engine.ui.modelList(config.defaultModel);
|
|
523
485
|
}
|
|
524
|
-
|
|
525
|
-
|
|
486
|
+
break;
|
|
487
|
+
case 'agent':
|
|
488
|
+
if (args[0]) {
|
|
489
|
+
const agentNames = Array.from(engine.agents.keys());
|
|
490
|
+
const found = agentNames.find(n => n.toLowerCase() === args[0].toLowerCase());
|
|
491
|
+
if (found) {
|
|
492
|
+
currentAgent = found;
|
|
493
|
+
currentMode = 'direct';
|
|
494
|
+
engine.ui.success(`Switched to agent: ${found}`);
|
|
495
|
+
}
|
|
496
|
+
else {
|
|
497
|
+
engine.ui.error(`Agent not found. Available: ${agentNames.join(', ')}`);
|
|
498
|
+
}
|
|
526
499
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
if (sessions.length === 0) {
|
|
531
|
-
engine.ui.info('No sessions found');
|
|
532
|
-
break;
|
|
500
|
+
else {
|
|
501
|
+
const agents = Array.from(engine.agents.entries()).map(([name]) => ({ name, description: '', model: config.defaultModel }));
|
|
502
|
+
engine.ui.agentList(agents);
|
|
533
503
|
}
|
|
534
|
-
console.log(chalk.bold('\nRecent sessions:\n'));
|
|
535
|
-
for (const s of sessions.slice(0, 10)) {
|
|
536
|
-
const date = new Date(s.createdAt).toLocaleString();
|
|
537
|
-
console.log(` ${chalk.cyan(s.id.slice(0, 20))} ${chalk.gray(date)} ${chalk.gray(`${s.messageCount} msgs`)}`);
|
|
538
|
-
}
|
|
539
|
-
console.log(chalk.gray('\nUse /resume <id> to resume a session'));
|
|
540
|
-
}
|
|
541
|
-
break;
|
|
542
|
-
case 'compact':
|
|
543
|
-
engine.ui.info('Compacting conversation context...');
|
|
544
|
-
engine.contextManager.manage(engine.sessionManager.getCurrent()?.messages || []);
|
|
545
|
-
engine.ui.success('Context compacted');
|
|
546
|
-
break;
|
|
547
|
-
case 'undo':
|
|
548
|
-
engine.ui.info('Undoing last change...');
|
|
549
|
-
const undoAction = engine.undoRedo.undo();
|
|
550
|
-
if (undoAction) {
|
|
551
|
-
engine.ui.success(`Undone: ${undoAction.description}`);
|
|
552
|
-
}
|
|
553
|
-
else {
|
|
554
|
-
engine.ui.warning('Nothing to undo');
|
|
555
|
-
}
|
|
556
|
-
break;
|
|
557
|
-
case 'redo':
|
|
558
|
-
engine.ui.info('Redoing...');
|
|
559
|
-
const redoAction = engine.undoRedo.redo();
|
|
560
|
-
if (redoAction) {
|
|
561
|
-
engine.ui.success(`Redone: ${redoAction.description}`);
|
|
562
|
-
}
|
|
563
|
-
else {
|
|
564
|
-
engine.ui.warning('Nothing to redo');
|
|
565
|
-
}
|
|
566
|
-
break;
|
|
567
|
-
case 'rewind':
|
|
568
|
-
const rewindN = args[0] ? parseInt(args[0]) : 1;
|
|
569
|
-
if (isNaN(rewindN) || rewindN < 1) {
|
|
570
|
-
engine.ui.error('Usage: /rewind <n>');
|
|
571
504
|
break;
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
505
|
+
case 'auto':
|
|
506
|
+
currentMode = 'auto';
|
|
507
|
+
currentAgent = undefined;
|
|
508
|
+
engine.ui.success('Mode: Auto (smart orchestration)');
|
|
509
|
+
break;
|
|
510
|
+
case 'orchestrate':
|
|
511
|
+
case 'plan':
|
|
512
|
+
currentMode = 'agent';
|
|
513
|
+
currentAgent = undefined;
|
|
514
|
+
engine.ui.success('Mode: Multi-agent orchestration');
|
|
515
|
+
break;
|
|
516
|
+
case 'direct':
|
|
517
|
+
currentMode = 'direct';
|
|
518
|
+
engine.ui.success('Mode: Direct (single agent)');
|
|
519
|
+
break;
|
|
520
|
+
case 'plan-mode':
|
|
521
|
+
engine.approval.setMode('plan');
|
|
522
|
+
engine.ui.success(`${engine.approval.getModeIcon()} Permission: plan (read-only)`);
|
|
523
|
+
break;
|
|
524
|
+
case 'stats':
|
|
525
|
+
const stats = engine.getSessionStats();
|
|
526
|
+
engine.ui.sessionStats(stats.inputTokens, stats.outputTokens, stats.cost);
|
|
527
|
+
break;
|
|
528
|
+
case 'clear':
|
|
529
|
+
console.clear();
|
|
530
|
+
engine.ui.banner();
|
|
531
|
+
break;
|
|
532
|
+
case 'theme':
|
|
533
|
+
if (args[0]) {
|
|
534
|
+
config.ui.theme = args[0];
|
|
535
|
+
engine.ui = new TerminalUI(config.ui.theme, config.ui.showTokenCount, config.ui.showCost);
|
|
536
|
+
engine.ui.success(`Theme: ${args[0]}`);
|
|
597
537
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
const status = s.connected ? chalk.green('connected') : chalk.gray('disconnected');
|
|
601
|
-
console.log(` ${chalk.cyan(s.name.padEnd(20))} ${status} ${chalk.gray(`${s.toolCount} tools`)} ${chalk.gray(s.config.transport)}`);
|
|
538
|
+
else {
|
|
539
|
+
console.log('Available themes: dracula, dark, nord, light');
|
|
602
540
|
}
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
const
|
|
607
|
-
if (
|
|
608
|
-
|
|
609
|
-
|
|
541
|
+
break;
|
|
542
|
+
case 'resume':
|
|
543
|
+
if (args[0]) {
|
|
544
|
+
const sessionId = args[0] === 'latest' ? engine.sessionManager.list()[0]?.id : args[0];
|
|
545
|
+
if (sessionId) {
|
|
546
|
+
const session = engine.sessionManager.load(sessionId);
|
|
547
|
+
if (session)
|
|
548
|
+
engine.ui.success(`Resumed session: ${sessionId.slice(0, 20)}...`);
|
|
549
|
+
else
|
|
550
|
+
engine.ui.error(`Session not found: ${sessionId}`);
|
|
610
551
|
}
|
|
611
552
|
else {
|
|
612
|
-
engine.ui.error(
|
|
553
|
+
engine.ui.error('No sessions found');
|
|
613
554
|
}
|
|
614
555
|
}
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
556
|
+
else {
|
|
557
|
+
const sessions = engine.sessionManager.list();
|
|
558
|
+
if (sessions.length === 0) {
|
|
559
|
+
engine.ui.info('No sessions found');
|
|
560
|
+
break;
|
|
561
|
+
}
|
|
562
|
+
console.log(chalk.bold('\nRecent sessions:\n'));
|
|
563
|
+
for (const s of sessions.slice(0, 10)) {
|
|
564
|
+
const date = new Date(s.createdAt).toLocaleString();
|
|
565
|
+
console.log(` ${chalk.cyan(s.id.slice(0, 20))} ${chalk.gray(date)} ${chalk.gray(`${s.messageCount} msgs`)}`);
|
|
566
|
+
}
|
|
567
|
+
console.log(chalk.gray('\nUse /resume <id> to resume a session'));
|
|
568
|
+
}
|
|
569
|
+
break;
|
|
570
|
+
case 'compact':
|
|
571
|
+
engine.ui.info('Compacting conversation context...');
|
|
572
|
+
engine.contextManager.manage(engine.sessionManager.getCurrent()?.messages || []);
|
|
573
|
+
engine.ui.success('Context compacted');
|
|
574
|
+
break;
|
|
575
|
+
case 'undo':
|
|
576
|
+
engine.ui.info('Undoing last change...');
|
|
577
|
+
const undoAction = engine.undoRedo.undo();
|
|
578
|
+
if (undoAction) {
|
|
579
|
+
engine.ui.success(`Undone: ${undoAction.description}`);
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
engine.ui.warning('Nothing to undo');
|
|
583
|
+
}
|
|
584
|
+
break;
|
|
585
|
+
case 'redo':
|
|
586
|
+
engine.ui.info('Redoing...');
|
|
587
|
+
const redoAction = engine.undoRedo.redo();
|
|
588
|
+
if (redoAction) {
|
|
589
|
+
engine.ui.success(`Redone: ${redoAction.description}`);
|
|
641
590
|
}
|
|
642
591
|
else {
|
|
643
|
-
engine.ui.
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
const
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
break;
|
|
651
|
-
case 'init':
|
|
652
|
-
engine.ui.info('Initializing NEURO.md for this project...');
|
|
653
|
-
try {
|
|
654
|
-
const { NeuroMdSystem } = await import('./context/neuro-md.js');
|
|
655
|
-
const nmd = new NeuroMdSystem(process.cwd());
|
|
656
|
-
nmd.load();
|
|
657
|
-
const content = `# Project Context\n\nThis file provides persistent context for NeuroCLI.\n\n## Tech Stack\n- [Detected automatically]\n\n## Conventions\n- Follow existing code patterns\n- Use TypeScript for all new files\n\n## Notes\n- This file is auto-generated by /init\n`;
|
|
658
|
-
const { writeFileSync } = await import('fs');
|
|
659
|
-
const { join } = await import('path');
|
|
660
|
-
writeFileSync(join(process.cwd(), 'NEURO.md'), content, 'utf-8');
|
|
661
|
-
engine.ui.success('NEURO.md created');
|
|
662
|
-
}
|
|
663
|
-
catch (e) {
|
|
664
|
-
engine.ui.error('Could not create NEURO.md: ' + (e instanceof Error ? e.message : String(e)));
|
|
665
|
-
}
|
|
666
|
-
break;
|
|
667
|
-
case 'unpause':
|
|
668
|
-
engine.doomLoop.unpause();
|
|
669
|
-
break;
|
|
670
|
-
case 'sandbox':
|
|
671
|
-
if (args[0] === 'on' || args[0] === 'enable') {
|
|
672
|
-
engine.sandbox.enable();
|
|
673
|
-
engine.ui.success('Sandbox mode enabled');
|
|
674
|
-
}
|
|
675
|
-
else if (args[0] === 'off' || args[0] === 'disable') {
|
|
676
|
-
engine.sandbox.disable();
|
|
677
|
-
engine.ui.success('Sandbox mode disabled');
|
|
678
|
-
}
|
|
679
|
-
else if (args[0] === 'status') {
|
|
680
|
-
engine.sandbox.printStatus();
|
|
681
|
-
}
|
|
682
|
-
else if (args[0] === 'undo') {
|
|
683
|
-
const undone = engine.sandbox.undoAll();
|
|
684
|
-
engine.ui.success(`Undone ${undone} file modifications`);
|
|
685
|
-
}
|
|
686
|
-
else {
|
|
687
|
-
const enabled = engine.sandbox.toggle();
|
|
688
|
-
engine.ui.success(enabled ? 'Sandbox mode enabled' : 'Sandbox mode disabled');
|
|
689
|
-
}
|
|
690
|
-
break;
|
|
691
|
-
case 'plugins':
|
|
692
|
-
case 'plugin':
|
|
693
|
-
const pluginSub = args[0];
|
|
694
|
-
if (pluginSub === 'list') {
|
|
695
|
-
const plugins = engine.pluginManager.listPlugins();
|
|
696
|
-
if (plugins.length === 0) {
|
|
697
|
-
engine.ui.info('No plugins loaded');
|
|
592
|
+
engine.ui.warning('Nothing to redo');
|
|
593
|
+
}
|
|
594
|
+
break;
|
|
595
|
+
case 'rewind':
|
|
596
|
+
const rewindN = args[0] ? parseInt(args[0]) : 1;
|
|
597
|
+
if (isNaN(rewindN) || rewindN < 1) {
|
|
598
|
+
engine.ui.error('Usage: /rewind <n>');
|
|
698
599
|
break;
|
|
699
600
|
}
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
601
|
+
const undone = engine.undoRedo.undoN(rewindN);
|
|
602
|
+
engine.ui.success(`Rewound ${undone.length} action(s)`);
|
|
603
|
+
break;
|
|
604
|
+
case 'fork':
|
|
605
|
+
engine.ui.info('Forking current session...');
|
|
606
|
+
const currentSession = engine.sessionManager.getCurrent();
|
|
607
|
+
if (currentSession) {
|
|
608
|
+
const forked = engine.sessionManager.create(process.cwd(), config.defaultModel);
|
|
609
|
+
forked.messages = [...currentSession.messages];
|
|
610
|
+
forked.forkedFrom = currentSession.id;
|
|
611
|
+
engine.sessionManager.save();
|
|
612
|
+
engine.ui.success(`Forked to new session: ${forked.id.slice(0, 20)}...`);
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
engine.ui.error('No active session to fork');
|
|
616
|
+
}
|
|
617
|
+
break;
|
|
618
|
+
case 'mcp':
|
|
619
|
+
const mcpSub = args[0];
|
|
620
|
+
if (mcpSub === 'list') {
|
|
621
|
+
const servers = engine.mcpClient.listServers();
|
|
622
|
+
if (servers.length === 0) {
|
|
623
|
+
engine.ui.info('No MCP servers configured');
|
|
624
|
+
break;
|
|
625
|
+
}
|
|
626
|
+
console.log(chalk.bold('\nMCP Servers:\n'));
|
|
627
|
+
for (const s of servers) {
|
|
628
|
+
const status = s.connected ? chalk.green('connected') : chalk.gray('disconnected');
|
|
629
|
+
console.log(` ${chalk.cyan(s.name.padEnd(20))} ${status} ${chalk.gray(`${s.toolCount} tools`)} ${chalk.gray(s.config.transport)}`);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
else if (mcpSub === 'connect' && args[1]) {
|
|
633
|
+
try {
|
|
634
|
+
const cfg = engine.mcpClient.loadConfig();
|
|
635
|
+
if (cfg.mcpServers[args[1]]) {
|
|
636
|
+
await engine.mcpClient.connect(args[1], cfg.mcpServers[args[1]]);
|
|
637
|
+
engine.ui.success(`Connected to MCP server: ${args[1]}`);
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
engine.ui.error(`MCP server "${args[1]}" not found`);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
catch (e) {
|
|
644
|
+
engine.ui.error(`Failed to connect: ${e instanceof Error ? e.message : String(e)}`);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
else if (mcpSub === 'disconnect' && args[1]) {
|
|
648
|
+
await engine.mcpClient.disconnect(args[1]);
|
|
649
|
+
engine.ui.success(`Disconnected from ${args[1]}`);
|
|
650
|
+
}
|
|
651
|
+
else if (mcpSub === 'health') {
|
|
652
|
+
engine.mcpClient.healthReport();
|
|
703
653
|
}
|
|
704
|
-
|
|
705
|
-
|
|
654
|
+
else {
|
|
655
|
+
console.log(chalk.bold('\nMCP Commands:\n'));
|
|
656
|
+
console.log(' /mcp list List MCP servers');
|
|
657
|
+
console.log(' /mcp connect <name> Connect to a server');
|
|
658
|
+
console.log(' /mcp disconnect <name> Disconnect from a server');
|
|
659
|
+
console.log(' /mcp health Show MCP health report');
|
|
660
|
+
}
|
|
661
|
+
break;
|
|
662
|
+
case 'permission':
|
|
663
|
+
case 'perm':
|
|
664
|
+
if (args[0]) {
|
|
665
|
+
const validModes = ['manual', 'auto', 'plan', 'yolo'];
|
|
666
|
+
if (validModes.includes(args[0])) {
|
|
667
|
+
engine.approval.setMode(args[0]);
|
|
668
|
+
engine.ui.success(`${engine.approval.getModeIcon()} Permission: ${args[0]} (${engine.approval.getModeDescription()})`);
|
|
669
|
+
}
|
|
670
|
+
else {
|
|
671
|
+
engine.ui.error(`Invalid mode. Use: ${validModes.join(', ')}`);
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
const newMode = engine.approval.cycleMode();
|
|
676
|
+
engine.ui.success(`${engine.approval.getModeIcon()} Permission: ${newMode} (${engine.approval.getModeDescription()})`);
|
|
677
|
+
}
|
|
678
|
+
break;
|
|
679
|
+
case 'init':
|
|
680
|
+
engine.ui.info('Initializing NEURO.md for this project...');
|
|
706
681
|
try {
|
|
707
|
-
await
|
|
708
|
-
|
|
682
|
+
const { NeuroMdSystem } = await import('./context/neuro-md.js');
|
|
683
|
+
const nmd = new NeuroMdSystem(process.cwd());
|
|
684
|
+
nmd.load();
|
|
685
|
+
const content = `# Project Context\n\nThis file provides persistent context for NeuroCLI.\n\n## Tech Stack\n- [Detected automatically]\n\n## Conventions\n- Follow existing code patterns\n- Use TypeScript for all new files\n\n## Notes\n- This file is auto-generated by /init\n`;
|
|
686
|
+
const { writeFileSync } = await import('fs');
|
|
687
|
+
const { join } = await import('path');
|
|
688
|
+
writeFileSync(join(process.cwd(), 'NEURO.md'), content, 'utf-8');
|
|
689
|
+
engine.ui.success('NEURO.md created');
|
|
709
690
|
}
|
|
710
691
|
catch (e) {
|
|
711
|
-
engine.ui.error(
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
for (const t of wl)
|
|
733
|
-
console.log(` ${chalk.green('+')} ${t}`);
|
|
734
|
-
}
|
|
735
|
-
else {
|
|
736
|
-
console.log('Usage: /whitelist add|remove|list <tool>');
|
|
737
|
-
}
|
|
738
|
-
break;
|
|
739
|
-
case 'blacklist':
|
|
740
|
-
if (args[0] === 'add' && args[1]) {
|
|
741
|
-
engine.approval.addToBlacklist(args[1]);
|
|
742
|
-
engine.ui.success(`Added "${args[1]}" to blacklist`);
|
|
743
|
-
}
|
|
744
|
-
else if (args[0] === 'remove' && args[1]) {
|
|
745
|
-
engine.approval.removeFromBlacklist(args[1]);
|
|
746
|
-
engine.ui.success(`Removed "${args[1]}" from blacklist`);
|
|
747
|
-
}
|
|
748
|
-
else if (args[0] === 'list') {
|
|
749
|
-
const bl = engine.approval.getBlacklist();
|
|
750
|
-
console.log(chalk.bold('\nBlacklisted tools:\n'));
|
|
751
|
-
for (const t of bl)
|
|
752
|
-
console.log(` ${chalk.red('-')} ${t}`);
|
|
753
|
-
}
|
|
754
|
-
else {
|
|
755
|
-
console.log('Usage: /blacklist add|remove|list <tool>');
|
|
756
|
-
}
|
|
757
|
-
break;
|
|
758
|
-
// --- v3.0 New Commands ---
|
|
759
|
-
case 'style':
|
|
760
|
-
if (args[0]) {
|
|
761
|
-
if (engine.styleManager.setStyle(args[0])) {
|
|
762
|
-
engine.ui.success(`Output style: ${args[0]}`);
|
|
692
|
+
engine.ui.error('Could not create NEURO.md: ' + (e instanceof Error ? e.message : String(e)));
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
case 'unpause':
|
|
696
|
+
engine.doomLoop.unpause();
|
|
697
|
+
break;
|
|
698
|
+
case 'sandbox':
|
|
699
|
+
if (args[0] === 'on' || args[0] === 'enable') {
|
|
700
|
+
engine.sandbox.enable();
|
|
701
|
+
engine.ui.success('Sandbox mode enabled');
|
|
702
|
+
}
|
|
703
|
+
else if (args[0] === 'off' || args[0] === 'disable') {
|
|
704
|
+
engine.sandbox.disable();
|
|
705
|
+
engine.ui.success('Sandbox mode disabled');
|
|
706
|
+
}
|
|
707
|
+
else if (args[0] === 'status') {
|
|
708
|
+
engine.sandbox.printStatus();
|
|
709
|
+
}
|
|
710
|
+
else if (args[0] === 'undo') {
|
|
711
|
+
const undone = engine.sandbox.undoAll();
|
|
712
|
+
engine.ui.success(`Undone ${undone} file modifications`);
|
|
763
713
|
}
|
|
764
714
|
else {
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
715
|
+
const enabled = engine.sandbox.toggle();
|
|
716
|
+
engine.ui.success(enabled ? 'Sandbox mode enabled' : 'Sandbox mode disabled');
|
|
717
|
+
}
|
|
718
|
+
break;
|
|
719
|
+
case 'plugins':
|
|
720
|
+
case 'plugin':
|
|
721
|
+
const pluginSub = args[0];
|
|
722
|
+
if (pluginSub === 'list') {
|
|
723
|
+
const plugins = engine.pluginManager.listPlugins();
|
|
724
|
+
if (plugins.length === 0) {
|
|
725
|
+
engine.ui.info('No plugins loaded');
|
|
726
|
+
break;
|
|
727
|
+
}
|
|
728
|
+
console.log(chalk.bold('\nPlugins:\n'));
|
|
729
|
+
for (const p of plugins) {
|
|
730
|
+
console.log(` ${chalk.cyan(p.name)} v${p.version} - ${chalk.gray(p.description)} ${chalk.green(`${p.toolCount} tools`)}`);
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
else if (pluginSub === 'load' && args[1]) {
|
|
734
|
+
try {
|
|
735
|
+
await engine.pluginManager.loadByName(args[1]);
|
|
736
|
+
engine.ui.success(`Plugin "${args[1]}" loaded`);
|
|
737
|
+
}
|
|
738
|
+
catch (e) {
|
|
739
|
+
engine.ui.error(`Failed to load plugin: ${e instanceof Error ? e.message : String(e)}`);
|
|
779
740
|
}
|
|
780
|
-
engine.ui.success(`Thinking mode: ${args[0]}`);
|
|
781
741
|
}
|
|
782
742
|
else {
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
if (activated) {
|
|
803
|
-
engine.ui.success(`Skill activated: ${args[1]}`);
|
|
743
|
+
console.log(chalk.bold('\nPlugin Commands:\n'));
|
|
744
|
+
console.log(' /plugins list List loaded plugins');
|
|
745
|
+
console.log(' /plugins load <name> Load a plugin');
|
|
746
|
+
}
|
|
747
|
+
break;
|
|
748
|
+
case 'whitelist':
|
|
749
|
+
if (args[0] === 'add' && args[1]) {
|
|
750
|
+
engine.approval.addToWhitelist(args[1]);
|
|
751
|
+
engine.ui.success(`Added "${args[1]}" to whitelist`);
|
|
752
|
+
}
|
|
753
|
+
else if (args[0] === 'remove' && args[1]) {
|
|
754
|
+
engine.approval.removeFromWhitelist(args[1]);
|
|
755
|
+
engine.ui.success(`Removed "${args[1]}" from whitelist`);
|
|
756
|
+
}
|
|
757
|
+
else if (args[0] === 'list') {
|
|
758
|
+
const wl = engine.approval.getWhitelist();
|
|
759
|
+
console.log(chalk.bold('\nWhitelisted tools:\n'));
|
|
760
|
+
for (const t of wl)
|
|
761
|
+
console.log(` ${chalk.green('+')} ${t}`);
|
|
804
762
|
}
|
|
805
763
|
else {
|
|
806
|
-
|
|
764
|
+
console.log('Usage: /whitelist add|remove|list <tool>');
|
|
765
|
+
}
|
|
766
|
+
break;
|
|
767
|
+
case 'blacklist':
|
|
768
|
+
if (args[0] === 'add' && args[1]) {
|
|
769
|
+
engine.approval.addToBlacklist(args[1]);
|
|
770
|
+
engine.ui.success(`Added "${args[1]}" to blacklist`);
|
|
771
|
+
}
|
|
772
|
+
else if (args[0] === 'remove' && args[1]) {
|
|
773
|
+
engine.approval.removeFromBlacklist(args[1]);
|
|
774
|
+
engine.ui.success(`Removed "${args[1]}" from blacklist`);
|
|
807
775
|
}
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
776
|
+
else if (args[0] === 'list') {
|
|
777
|
+
const bl = engine.approval.getBlacklist();
|
|
778
|
+
console.log(chalk.bold('\nBlacklisted tools:\n'));
|
|
779
|
+
for (const t of bl)
|
|
780
|
+
console.log(` ${chalk.red('-')} ${t}`);
|
|
812
781
|
}
|
|
813
782
|
else {
|
|
814
|
-
|
|
815
|
-
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
console.log(' /skills clear Deactivate all skills');
|
|
827
|
-
}
|
|
828
|
-
break;
|
|
829
|
-
case 'effort':
|
|
830
|
-
if (args[0]) {
|
|
831
|
-
const levels = ['low', 'medium', 'high', 'ultrathink'];
|
|
832
|
-
if (levels.includes(args[0])) {
|
|
833
|
-
engine.modelRouter.setEffort(args[0]);
|
|
834
|
-
engine.ui.success(`Effort level: ${args[0]}`);
|
|
783
|
+
console.log('Usage: /blacklist add|remove|list <tool>');
|
|
784
|
+
}
|
|
785
|
+
break;
|
|
786
|
+
// --- v3.0 New Commands ---
|
|
787
|
+
case 'style':
|
|
788
|
+
if (args[0]) {
|
|
789
|
+
if (engine.styleManager.setStyle(args[0])) {
|
|
790
|
+
engine.ui.success(`Output style: ${args[0]}`);
|
|
791
|
+
}
|
|
792
|
+
else {
|
|
793
|
+
engine.ui.error(`Unknown style. Available: ${engine.styleManager.listStyles().map(s => s.name).join(', ')}`);
|
|
794
|
+
}
|
|
835
795
|
}
|
|
836
796
|
else {
|
|
837
|
-
engine.
|
|
797
|
+
engine.styleManager.printStyles();
|
|
838
798
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
const
|
|
865
|
-
|
|
866
|
-
|
|
799
|
+
break;
|
|
800
|
+
case 'thinking':
|
|
801
|
+
if (args[0]) {
|
|
802
|
+
const modes = ['none', 'brief', 'full', 'ultrathink'];
|
|
803
|
+
if (modes.includes(args[0])) {
|
|
804
|
+
engine.extendedThinking.setMode(args[0]);
|
|
805
|
+
if (args[0] !== 'none' && !engine.extendedThinking.isDisplayEnabled()) {
|
|
806
|
+
engine.extendedThinking.toggleDisplay();
|
|
807
|
+
}
|
|
808
|
+
engine.ui.success(`Thinking mode: ${args[0]}`);
|
|
809
|
+
}
|
|
810
|
+
else {
|
|
811
|
+
engine.ui.error(`Invalid mode. Use: ${modes.join(', ')}`);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
const currentMode = engine.extendedThinking.getMode();
|
|
816
|
+
const showing = engine.extendedThinking.isDisplayEnabled() ? 'visible' : 'hidden';
|
|
817
|
+
console.log(chalk.bold(`\nThinking Mode: ${chalk.cyan(currentMode)} (display: ${showing})`));
|
|
818
|
+
console.log(chalk.gray(' Toggle display: /thinking toggle'));
|
|
819
|
+
console.log(chalk.gray(' Set mode: /thinking none|brief|full|ultrathink'));
|
|
820
|
+
console.log();
|
|
821
|
+
}
|
|
822
|
+
break;
|
|
823
|
+
case 'skills':
|
|
824
|
+
const skillSub = args[0];
|
|
825
|
+
if (skillSub === 'list') {
|
|
826
|
+
engine.skillSystem.listSkills();
|
|
827
|
+
}
|
|
828
|
+
else if (skillSub === 'activate' && args[1]) {
|
|
829
|
+
const activated = engine.skillSystem.activate(args[1]);
|
|
830
|
+
if (activated) {
|
|
831
|
+
engine.ui.success(`Skill activated: ${args[1]}`);
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
engine.ui.error(`Skill not found: ${args[1]}`);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
else if (skillSub === 'deactivate' && args[1]) {
|
|
838
|
+
if (engine.skillSystem.deactivate(args[1])) {
|
|
839
|
+
engine.ui.success(`Skill deactivated: ${args[1]}`);
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
engine.ui.error(`Skill not active: ${args[1]}`);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
else if (skillSub === 'clear') {
|
|
846
|
+
engine.skillSystem.deactivateAll();
|
|
847
|
+
engine.ui.success('All skills deactivated');
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
console.log(chalk.bold('\nSkill Commands:\n'));
|
|
851
|
+
console.log(' /skills list List all skills');
|
|
852
|
+
console.log(' /skills activate <name> Activate a skill');
|
|
853
|
+
console.log(' /skills deactivate <name> Deactivate a skill');
|
|
854
|
+
console.log(' /skills clear Deactivate all skills');
|
|
855
|
+
}
|
|
856
|
+
break;
|
|
857
|
+
case 'effort':
|
|
858
|
+
if (args[0]) {
|
|
859
|
+
const levels = ['low', 'medium', 'high', 'ultrathink'];
|
|
860
|
+
if (levels.includes(args[0])) {
|
|
861
|
+
engine.modelRouter.setEffort(args[0]);
|
|
862
|
+
engine.ui.success(`Effort level: ${args[0]}`);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
engine.ui.error(`Invalid level. Use: ${levels.join(', ')}`);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
else {
|
|
869
|
+
console.log(chalk.bold(`\nEffort Level: ${chalk.cyan(engine.modelRouter.getEffort())}`));
|
|
870
|
+
console.log(chalk.gray(' Set level: /effort low|medium|high|ultrathink'));
|
|
871
|
+
console.log();
|
|
872
|
+
}
|
|
873
|
+
break;
|
|
874
|
+
case 'cache':
|
|
875
|
+
const cacheSub = args[0];
|
|
876
|
+
if (cacheSub === 'on') {
|
|
877
|
+
config.promptCache.enabled = true;
|
|
878
|
+
engine.ui.success('Prompt cache enabled');
|
|
879
|
+
}
|
|
880
|
+
else if (cacheSub === 'off') {
|
|
881
|
+
config.promptCache.enabled = false;
|
|
882
|
+
engine.ui.success('Prompt cache disabled');
|
|
883
|
+
}
|
|
884
|
+
else if (cacheSub === 'clear') {
|
|
885
|
+
engine.promptCache.clear();
|
|
886
|
+
engine.ui.success('Cache cleared');
|
|
887
|
+
}
|
|
888
|
+
else if (cacheSub === 'stats') {
|
|
889
|
+
engine.promptCache.printStats();
|
|
890
|
+
}
|
|
891
|
+
else {
|
|
892
|
+
const status = config.promptCache.enabled ? chalk.green('enabled') : chalk.gray('disabled');
|
|
893
|
+
console.log(chalk.bold(`\nPrompt Cache: ${status}`));
|
|
894
|
+
console.log(chalk.gray(' /cache on|off|clear|stats'));
|
|
895
|
+
console.log();
|
|
896
|
+
}
|
|
897
|
+
break;
|
|
898
|
+
case 'spending':
|
|
899
|
+
engine.spendingMonitor.printReport();
|
|
900
|
+
break;
|
|
901
|
+
case 'ignore':
|
|
902
|
+
if (args[0] === 'list') {
|
|
903
|
+
engine.neuroIgnore.printRules();
|
|
904
|
+
}
|
|
905
|
+
else if (args[0] === 'add' && args[1]) {
|
|
906
|
+
engine.neuroIgnore.addRule(args[1], 'manual');
|
|
907
|
+
engine.ui.success(`Added ignore rule: ${args[1]}`);
|
|
908
|
+
}
|
|
909
|
+
else if (args[0] === 'check' && args[1]) {
|
|
910
|
+
const ignored = engine.neuroIgnore.isIgnored(args[1]);
|
|
911
|
+
console.log(` ${args[1]}: ${ignored ? chalk.red('ignored') : chalk.green('allowed')}`);
|
|
912
|
+
}
|
|
913
|
+
else {
|
|
914
|
+
console.log(chalk.bold('\nIgnore Commands:\n'));
|
|
915
|
+
console.log(' /ignore list List ignore rules');
|
|
916
|
+
console.log(' /ignore add <pattern> Add an ignore pattern');
|
|
917
|
+
console.log(' /ignore check <path> Check if a path is ignored');
|
|
918
|
+
}
|
|
919
|
+
break;
|
|
920
|
+
case 'ollama':
|
|
921
|
+
try {
|
|
922
|
+
const available = await engine.ollamaProvider.isAvailable();
|
|
923
|
+
if (!available) {
|
|
924
|
+
engine.ui.error('Ollama is not running. Start it with: ollama serve');
|
|
925
|
+
break;
|
|
926
|
+
}
|
|
927
|
+
const models = await engine.ollamaProvider.listModels();
|
|
928
|
+
console.log(chalk.bold('\nOllama Local Models:\n'));
|
|
929
|
+
for (const m of models) {
|
|
930
|
+
const isActive = config.defaultModel === m.name;
|
|
931
|
+
console.log(` ${isActive ? chalk.green('*') : ' '} ${chalk.cyan(m.name.padEnd(40))} ${chalk.gray(m.details?.parameter_size || '')} ${chalk.gray(m.details?.quantization_level || '')}`);
|
|
932
|
+
}
|
|
933
|
+
console.log(chalk.gray('\nSwitch model: /model <name>'));
|
|
934
|
+
}
|
|
935
|
+
catch (e) {
|
|
936
|
+
engine.ui.error('Could not connect to Ollama');
|
|
937
|
+
}
|
|
938
|
+
break;
|
|
939
|
+
case 'doctor':
|
|
940
|
+
console.log(chalk.bold('\nNeuroCLI v4.1.2 Health Check:\n'));
|
|
941
|
+
console.log(` API Key: ${config.apiKey ? chalk.green('configured') : chalk.red('MISSING')}`);
|
|
942
|
+
console.log(` Default Model: ${chalk.cyan(config.defaultModel)} ${MODELS[config.defaultModel] ? chalk.green('valid') : chalk.red('INVALID')}`);
|
|
943
|
+
console.log(` MCP Servers: ${chalk.cyan(String(engine.mcpClient.listServers().length))}`);
|
|
944
|
+
console.log(` Permission Mode: ${chalk.cyan(engine.approval.getMode())}`);
|
|
945
|
+
console.log(` Diff Preview: ${config.diffPreview ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
946
|
+
console.log(` Fallback Chain: ${config.fallbackChain.models.length > 0 ? chalk.green(config.fallbackChain.models.length + ' models') : chalk.yellow('none')}`);
|
|
947
|
+
console.log(` Doom Loop Protection: ${config.doomLoop.autoBreak ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
948
|
+
console.log(` Sandbox: ${engine.sandbox.isEnabled() ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
949
|
+
console.log(` Plugins: ${chalk.cyan(String(engine.pluginManager.listPlugins().length))}`);
|
|
950
|
+
console.log(` Custom Agents: ${chalk.cyan(String(engine.customAgentLoader.getAll().length))}`);
|
|
951
|
+
console.log(` Custom Tools: ${chalk.cyan(String(engine.customToolLoader.getAll().length))}`);
|
|
952
|
+
console.log(` Skills: ${chalk.cyan(String(engine.skillSystem.getAllSkills().length))} (${chalk.cyan(String(engine.skillSystem.getActiveSkills().length))} active)`);
|
|
953
|
+
console.log(` Prompt Cache: ${config.promptCache.enabled ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
954
|
+
console.log(` Output Style: ${chalk.cyan(engine.styleManager.getStyle().name)}`);
|
|
955
|
+
console.log(` Thinking Mode: ${chalk.cyan(engine.extendedThinking.getMode())}`);
|
|
956
|
+
console.log(` Effort Level: ${chalk.cyan(engine.modelRouter.getEffort())}`);
|
|
957
|
+
console.log(` Sessions: ${chalk.cyan(String(engine.sessionManager.list().length))}`);
|
|
958
|
+
console.log(` Spending Limit: ${config.spendingLimit > 0 ? chalk.cyan('$' + config.spendingLimit.toFixed(2)) : chalk.gray('unlimited')}`);
|
|
959
|
+
console.log(` Ignore Rules: ${chalk.cyan(String(engine.neuroIgnore.getRules().length))}`);
|
|
960
|
+
// Check Ollama availability
|
|
961
|
+
const ollamaAvail = await engine.ollamaProvider.isAvailable().catch(() => false);
|
|
962
|
+
console.log(` Ollama: ${ollamaAvail ? chalk.green('available') : chalk.gray('not running')}`);
|
|
963
|
+
// Update check status
|
|
964
|
+
const lastUpdateCheck = updater.getLastCheck();
|
|
965
|
+
const updateStatus = lastUpdateCheck?.hasUpdate ? chalk.yellow(`update available (v${lastUpdateCheck.latestVersion})`) : chalk.green('up to date');
|
|
966
|
+
console.log(` Auto-Update: ${updateStatus}`);
|
|
967
|
+
const nextCheck = updater.timeUntilNextCheck();
|
|
968
|
+
const nextCheckStr = nextCheck > 0 ? ` (next check in ${Math.floor(nextCheck / 3600000)}h)` : '';
|
|
969
|
+
console.log(` Update Check: ${chalk.gray('enabled' + nextCheckStr)}`);
|
|
867
970
|
console.log();
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
if (!available) {
|
|
896
|
-
engine.ui.error('Ollama is not running. Start it with: ollama serve');
|
|
971
|
+
break;
|
|
972
|
+
case 'export':
|
|
973
|
+
const exportSession = engine.sessionManager.getCurrent();
|
|
974
|
+
if (exportSession) {
|
|
975
|
+
const exportPath = args[0] || 'neuro-session-export.json';
|
|
976
|
+
const exportData = {
|
|
977
|
+
version: VERSION,
|
|
978
|
+
exportedAt: Date.now(),
|
|
979
|
+
session: exportSession,
|
|
980
|
+
neuroVersion: VERSION,
|
|
981
|
+
};
|
|
982
|
+
try {
|
|
983
|
+
writeFileSync(exportPath, JSON.stringify(exportData, null, 2), 'utf-8');
|
|
984
|
+
engine.ui.success(`Session exported to ${exportPath}`);
|
|
985
|
+
}
|
|
986
|
+
catch (e) {
|
|
987
|
+
console.log(JSON.stringify(exportSession, null, 2));
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
engine.ui.warning('No active session to export');
|
|
992
|
+
}
|
|
993
|
+
break;
|
|
994
|
+
case 'import':
|
|
995
|
+
const importPath = args[0];
|
|
996
|
+
if (!importPath) {
|
|
997
|
+
engine.ui.error('Usage: /import <path-to-json-file>');
|
|
897
998
|
break;
|
|
898
999
|
}
|
|
899
|
-
const models = await engine.ollamaProvider.listModels();
|
|
900
|
-
console.log(chalk.bold('\nOllama Local Models:\n'));
|
|
901
|
-
for (const m of models) {
|
|
902
|
-
const isActive = config.defaultModel === m.name;
|
|
903
|
-
console.log(` ${isActive ? chalk.green('*') : ' '} ${chalk.cyan(m.name.padEnd(40))} ${chalk.gray(m.details?.parameter_size || '')} ${chalk.gray(m.details?.quantization_level || '')}`);
|
|
904
|
-
}
|
|
905
|
-
console.log(chalk.gray('\nSwitch model: /model <name>'));
|
|
906
|
-
}
|
|
907
|
-
catch (e) {
|
|
908
|
-
engine.ui.error('Could not connect to Ollama');
|
|
909
|
-
}
|
|
910
|
-
break;
|
|
911
|
-
case 'doctor':
|
|
912
|
-
console.log(chalk.bold('\nNeuroCLI v4.1.1 Health Check:\n'));
|
|
913
|
-
console.log(` API Key: ${config.apiKey ? chalk.green('configured') : chalk.red('MISSING')}`);
|
|
914
|
-
console.log(` Default Model: ${chalk.cyan(config.defaultModel)} ${MODELS[config.defaultModel] ? chalk.green('valid') : chalk.red('INVALID')}`);
|
|
915
|
-
console.log(` MCP Servers: ${chalk.cyan(String(engine.mcpClient.listServers().length))}`);
|
|
916
|
-
console.log(` Permission Mode: ${chalk.cyan(engine.approval.getMode())}`);
|
|
917
|
-
console.log(` Diff Preview: ${config.diffPreview ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
918
|
-
console.log(` Fallback Chain: ${config.fallbackChain.models.length > 0 ? chalk.green(config.fallbackChain.models.length + ' models') : chalk.yellow('none')}`);
|
|
919
|
-
console.log(` Doom Loop Protection: ${config.doomLoop.autoBreak ? chalk.green('enabled') : chalk.yellow('disabled')}`);
|
|
920
|
-
console.log(` Sandbox: ${engine.sandbox.isEnabled() ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
921
|
-
console.log(` Plugins: ${chalk.cyan(String(engine.pluginManager.listPlugins().length))}`);
|
|
922
|
-
console.log(` Custom Agents: ${chalk.cyan(String(engine.customAgentLoader.getAll().length))}`);
|
|
923
|
-
console.log(` Custom Tools: ${chalk.cyan(String(engine.customToolLoader.getAll().length))}`);
|
|
924
|
-
console.log(` Skills: ${chalk.cyan(String(engine.skillSystem.getAllSkills().length))} (${chalk.cyan(String(engine.skillSystem.getActiveSkills().length))} active)`);
|
|
925
|
-
console.log(` Prompt Cache: ${config.promptCache.enabled ? chalk.green('enabled') : chalk.gray('disabled')}`);
|
|
926
|
-
console.log(` Output Style: ${chalk.cyan(engine.styleManager.getStyle().name)}`);
|
|
927
|
-
console.log(` Thinking Mode: ${chalk.cyan(engine.extendedThinking.getMode())}`);
|
|
928
|
-
console.log(` Effort Level: ${chalk.cyan(engine.modelRouter.getEffort())}`);
|
|
929
|
-
console.log(` Sessions: ${chalk.cyan(String(engine.sessionManager.list().length))}`);
|
|
930
|
-
console.log(` Spending Limit: ${config.spendingLimit > 0 ? chalk.cyan('$' + config.spendingLimit.toFixed(2)) : chalk.gray('unlimited')}`);
|
|
931
|
-
console.log(` Ignore Rules: ${chalk.cyan(String(engine.neuroIgnore.getRules().length))}`);
|
|
932
|
-
// Check Ollama availability
|
|
933
|
-
const ollamaAvail = await engine.ollamaProvider.isAvailable().catch(() => false);
|
|
934
|
-
console.log(` Ollama: ${ollamaAvail ? chalk.green('available') : chalk.gray('not running')}`);
|
|
935
|
-
// Update check status
|
|
936
|
-
const lastUpdateCheck = updater.getLastCheck();
|
|
937
|
-
const updateStatus = lastUpdateCheck?.hasUpdate ? chalk.yellow(`update available (v${lastUpdateCheck.latestVersion})`) : chalk.green('up to date');
|
|
938
|
-
console.log(` Auto-Update: ${updateStatus}`);
|
|
939
|
-
const nextCheck = updater.timeUntilNextCheck();
|
|
940
|
-
const nextCheckStr = nextCheck > 0 ? ` (next check in ${Math.floor(nextCheck / 3600000)}h)` : '';
|
|
941
|
-
console.log(` Update Check: ${chalk.gray('enabled' + nextCheckStr)}`);
|
|
942
|
-
console.log();
|
|
943
|
-
break;
|
|
944
|
-
case 'export':
|
|
945
|
-
const exportSession = engine.sessionManager.getCurrent();
|
|
946
|
-
if (exportSession) {
|
|
947
|
-
const exportPath = args[0] || 'neuro-session-export.json';
|
|
948
|
-
const exportData = {
|
|
949
|
-
version: VERSION,
|
|
950
|
-
exportedAt: Date.now(),
|
|
951
|
-
session: exportSession,
|
|
952
|
-
neuroVersion: VERSION,
|
|
953
|
-
};
|
|
954
1000
|
try {
|
|
955
|
-
|
|
956
|
-
|
|
1001
|
+
const importData = JSON.parse(readFileSync(importPath, 'utf-8'));
|
|
1002
|
+
const sessionData = importData.session || importData;
|
|
1003
|
+
const newSession = engine.sessionManager.create(process.cwd(), sessionData.model || config.defaultModel);
|
|
1004
|
+
if (sessionData.messages)
|
|
1005
|
+
newSession.messages = sessionData.messages;
|
|
1006
|
+
if (sessionData.totalInputTokens)
|
|
1007
|
+
newSession.totalInputTokens = sessionData.totalInputTokens;
|
|
1008
|
+
if (sessionData.totalOutputTokens)
|
|
1009
|
+
newSession.totalOutputTokens = sessionData.totalOutputTokens;
|
|
1010
|
+
if (sessionData.totalCost)
|
|
1011
|
+
newSession.totalCost = sessionData.totalCost;
|
|
1012
|
+
engine.sessionManager.save();
|
|
1013
|
+
engine.ui.success(`Session imported: ${newSession.id.slice(0, 20)}... (${newSession.messages.length} messages)`);
|
|
957
1014
|
}
|
|
958
1015
|
catch (e) {
|
|
959
|
-
|
|
960
|
-
}
|
|
961
|
-
}
|
|
962
|
-
else {
|
|
963
|
-
engine.ui.warning('No active session to export');
|
|
964
|
-
}
|
|
965
|
-
break;
|
|
966
|
-
case 'import':
|
|
967
|
-
const importPath = args[0];
|
|
968
|
-
if (!importPath) {
|
|
969
|
-
engine.ui.error('Usage: /import <path-to-json-file>');
|
|
1016
|
+
engine.ui.error(`Failed to import: ${e instanceof Error ? e.message : String(e)}`);
|
|
1017
|
+
}
|
|
970
1018
|
break;
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
const importData = JSON.parse(readFileSync(importPath, 'utf-8'));
|
|
974
|
-
const sessionData = importData.session || importData;
|
|
975
|
-
const newSession = engine.sessionManager.create(process.cwd(), sessionData.model || config.defaultModel);
|
|
976
|
-
if (sessionData.messages)
|
|
977
|
-
newSession.messages = sessionData.messages;
|
|
978
|
-
if (sessionData.totalInputTokens)
|
|
979
|
-
newSession.totalInputTokens = sessionData.totalInputTokens;
|
|
980
|
-
if (sessionData.totalOutputTokens)
|
|
981
|
-
newSession.totalOutputTokens = sessionData.totalOutputTokens;
|
|
982
|
-
if (sessionData.totalCost)
|
|
983
|
-
newSession.totalCost = sessionData.totalCost;
|
|
984
|
-
engine.sessionManager.save();
|
|
985
|
-
engine.ui.success(`Session imported: ${newSession.id.slice(0, 20)}... (${newSession.messages.length} messages)`);
|
|
986
|
-
}
|
|
987
|
-
catch (e) {
|
|
988
|
-
engine.ui.error(`Failed to import: ${e instanceof Error ? e.message : String(e)}`);
|
|
989
|
-
}
|
|
990
|
-
break;
|
|
991
|
-
case 'commit-push-pr':
|
|
992
|
-
engine.ui.info('Running commit + push + PR workflow...');
|
|
993
|
-
try {
|
|
994
|
-
const { execSync } = await import('child_process');
|
|
995
|
-
// Stage all
|
|
996
|
-
execSync('git add -A', { cwd: process.cwd(), encoding: 'utf-8' });
|
|
997
|
-
// Commit
|
|
998
|
-
const commitMsg = args.join(' ') || 'Update from NeuroCLI';
|
|
999
|
-
execSync(`git commit -m "${commitMsg}" --no-gpg-sign`, { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1000
|
-
// Push
|
|
1001
|
-
execSync('git push', { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1002
|
-
engine.ui.success('Changes committed and pushed');
|
|
1003
|
-
// Try to create PR with gh CLI
|
|
1019
|
+
case 'commit-push-pr':
|
|
1020
|
+
engine.ui.info('Running commit + push + PR workflow...');
|
|
1004
1021
|
try {
|
|
1005
|
-
const
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
engine.ui.error(`Code review failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1023
|
-
}
|
|
1024
|
-
break;
|
|
1025
|
-
case 'feedback':
|
|
1026
|
-
console.log(chalk.bold('\nFeedback:\n'));
|
|
1027
|
-
console.log(' Report issues: https://github.com/neuro-cli/neuro/issues');
|
|
1028
|
-
console.log(' Discussions: https://github.com/neuro-cli/neuro/discussions');
|
|
1029
|
-
console.log(chalk.gray('\n Your feedback helps make NeuroCLI better!'));
|
|
1030
|
-
console.log();
|
|
1031
|
-
break;
|
|
1032
|
-
case 'cost':
|
|
1033
|
-
engine.spendingMonitor.printReport();
|
|
1034
|
-
if (engine.promptCache) {
|
|
1035
|
-
console.log(chalk.bold('\nCache Savings:'));
|
|
1036
|
-
engine.promptCache.printStats();
|
|
1037
|
-
}
|
|
1038
|
-
break;
|
|
1039
|
-
case 'update':
|
|
1040
|
-
case 'upgrade':
|
|
1041
|
-
const updateSub = args[0];
|
|
1042
|
-
if (updateSub === 'now') {
|
|
1043
|
-
engine.ui.info('Updating NeuroCLI...');
|
|
1044
|
-
const updateResult = await updater.performUpdate();
|
|
1045
|
-
if (updateResult.success) {
|
|
1046
|
-
engine.ui.success(updateResult.message);
|
|
1047
|
-
console.log(chalk.yellow(' Please restart NeuroCLI to use the new version.'));
|
|
1022
|
+
const { execSync } = await import('child_process');
|
|
1023
|
+
// Stage all
|
|
1024
|
+
execSync('git add -A', { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1025
|
+
// Commit
|
|
1026
|
+
const commitMsg = args.join(' ') || 'Update from NeuroCLI';
|
|
1027
|
+
execSync(`git commit -m "${commitMsg}" --no-gpg-sign`, { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1028
|
+
// Push
|
|
1029
|
+
execSync('git push', { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1030
|
+
engine.ui.success('Changes committed and pushed');
|
|
1031
|
+
// Try to create PR with gh CLI
|
|
1032
|
+
try {
|
|
1033
|
+
const prResult = execSync(`gh pr create --title "${commitMsg}" --body "Auto-generated by NeuroCLI"`, { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1034
|
+
engine.ui.success(`PR created: ${prResult.trim()}`);
|
|
1035
|
+
}
|
|
1036
|
+
catch {
|
|
1037
|
+
engine.ui.info('Could not create PR (gh CLI not available or not a GitHub repo)');
|
|
1038
|
+
}
|
|
1048
1039
|
}
|
|
1049
|
-
|
|
1050
|
-
engine.ui.error(
|
|
1040
|
+
catch (e) {
|
|
1041
|
+
engine.ui.error(`Git operation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1051
1042
|
}
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
engine.ui.info('
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
updater.showUpdateDetails(checkResult);
|
|
1058
|
-
updater.showUpdateNotification(checkResult);
|
|
1043
|
+
break;
|
|
1044
|
+
case 'code-review':
|
|
1045
|
+
engine.ui.info('Starting multi-agent code review...');
|
|
1046
|
+
try {
|
|
1047
|
+
const reviewResult = await engine.processMessage('Perform a thorough code review of all recent changes in this repository. Check for: bugs, security issues, performance problems, code style, test coverage. Provide findings with severity levels (CRITICAL, WARNING, INFO).', 'agent');
|
|
1059
1048
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1049
|
+
catch (e) {
|
|
1050
|
+
engine.ui.error(`Code review failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1051
|
+
}
|
|
1052
|
+
break;
|
|
1053
|
+
case 'feedback':
|
|
1054
|
+
console.log(chalk.bold('\nFeedback:\n'));
|
|
1055
|
+
console.log(' Report issues: https://github.com/neuro-cli/neuro/issues');
|
|
1056
|
+
console.log(' Discussions: https://github.com/neuro-cli/neuro/discussions');
|
|
1057
|
+
console.log(chalk.gray('\n Your feedback helps make NeuroCLI better!'));
|
|
1058
|
+
console.log();
|
|
1059
|
+
break;
|
|
1060
|
+
case 'cost':
|
|
1061
|
+
engine.spendingMonitor.printReport();
|
|
1062
|
+
if (engine.promptCache) {
|
|
1063
|
+
console.log(chalk.bold('\nCache Savings:'));
|
|
1064
|
+
engine.promptCache.printStats();
|
|
1065
|
+
}
|
|
1066
|
+
break;
|
|
1067
|
+
case 'update':
|
|
1068
|
+
case 'upgrade':
|
|
1069
|
+
const updateSub = args[0];
|
|
1070
|
+
if (updateSub === 'now') {
|
|
1071
|
+
engine.ui.info('Updating NeuroCLI...');
|
|
1072
|
+
const updateResult = await updater.performUpdate();
|
|
1073
|
+
if (updateResult.success) {
|
|
1074
|
+
engine.ui.success(updateResult.message);
|
|
1075
|
+
console.log(chalk.yellow(' Please restart NeuroCLI to use the new version.'));
|
|
1076
|
+
}
|
|
1077
|
+
else {
|
|
1078
|
+
engine.ui.error(updateResult.message);
|
|
1079
|
+
}
|
|
1062
1080
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1081
|
+
else if (updateSub === 'check') {
|
|
1082
|
+
engine.ui.info('Checking for updates...');
|
|
1083
|
+
const checkResult = await updater.checkForUpdate(true);
|
|
1084
|
+
if (checkResult.hasUpdate) {
|
|
1085
|
+
updater.showUpdateDetails(checkResult);
|
|
1086
|
+
updater.showUpdateNotification(checkResult);
|
|
1087
|
+
}
|
|
1088
|
+
else {
|
|
1089
|
+
updater.showUpToDate();
|
|
1090
|
+
}
|
|
1069
1091
|
}
|
|
1070
|
-
else {
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1092
|
+
else if (updateSub === 'dismiss') {
|
|
1093
|
+
const checkResult = await updater.checkForUpdate(true);
|
|
1094
|
+
if (checkResult.hasUpdate) {
|
|
1095
|
+
updater.dismissVersion(checkResult.latestVersion);
|
|
1096
|
+
engine.ui.success(`Dismissed update notification for v${checkResult.latestVersion}`);
|
|
1097
|
+
}
|
|
1098
|
+
else {
|
|
1099
|
+
engine.ui.info('No update to dismiss');
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
else if (updateSub === 'auto') {
|
|
1103
|
+
const enableAuto = args[1] !== 'off';
|
|
1104
|
+
updater.setAutoUpdate(enableAuto);
|
|
1105
|
+
engine.ui.success(`Auto-update: ${enableAuto ? 'enabled' : 'disabled'}`);
|
|
1106
|
+
}
|
|
1107
|
+
else if (updateSub === 'interval') {
|
|
1108
|
+
const hours = args[1] ? parseFloat(args[1]) : 24;
|
|
1109
|
+
if (isNaN(hours) || hours < 1) {
|
|
1110
|
+
engine.ui.error('Interval must be at least 1 hour');
|
|
1111
|
+
}
|
|
1112
|
+
else {
|
|
1113
|
+
updater.setCheckInterval(hours);
|
|
1114
|
+
engine.ui.success(`Update check interval set to ${hours} hours`);
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
else if (updateSub === 'reset') {
|
|
1118
|
+
updater.resetDismissed();
|
|
1119
|
+
updater.forceNextCheck();
|
|
1120
|
+
engine.ui.success('Update preferences reset');
|
|
1083
1121
|
}
|
|
1084
1122
|
else {
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
engine.
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1123
|
+
// Default: interactive update flow
|
|
1124
|
+
await updater.interactiveUpdate();
|
|
1125
|
+
}
|
|
1126
|
+
break;
|
|
1127
|
+
case 'exit':
|
|
1128
|
+
case 'quit':
|
|
1129
|
+
case 'q':
|
|
1130
|
+
engine.mcpClient.disconnectAll().catch(() => { });
|
|
1131
|
+
engine.approval.close();
|
|
1132
|
+
engine.ui.info('Goodbye!');
|
|
1133
|
+
process.exit(0);
|
|
1134
|
+
break;
|
|
1135
|
+
default:
|
|
1136
|
+
engine.ui.error(`Unknown command: /${cmd}. Type /help for available commands.`);
|
|
1137
|
+
}
|
|
1138
|
+
rl.prompt();
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
// Process message with the engine
|
|
1142
|
+
try {
|
|
1143
|
+
isProcessing = true;
|
|
1144
|
+
await engine.processMessage(input, currentMode, currentAgent);
|
|
1145
|
+
}
|
|
1146
|
+
catch (error) {
|
|
1147
|
+
engine.ui.error(error instanceof Error ? error.message : String(error));
|
|
1148
|
+
}
|
|
1149
|
+
finally {
|
|
1150
|
+
isProcessing = false;
|
|
1109
1151
|
}
|
|
1110
1152
|
rl.prompt();
|
|
1111
|
-
return;
|
|
1112
|
-
}
|
|
1113
|
-
// Process message with the engine
|
|
1114
|
-
try {
|
|
1115
|
-
await engine.processMessage(input, currentMode, currentAgent);
|
|
1116
1153
|
}
|
|
1117
1154
|
catch (error) {
|
|
1118
|
-
|
|
1155
|
+
// Catch-all for any unhandled error in the line handler
|
|
1156
|
+
console.error(chalk.red(`\n⚠️ Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
1157
|
+
isProcessing = false;
|
|
1158
|
+
rl.prompt();
|
|
1119
1159
|
}
|
|
1120
|
-
rl.prompt();
|
|
1121
1160
|
});
|
|
1122
|
-
rl.on('close', () => {
|
|
1123
|
-
|
|
1124
|
-
|
|
1161
|
+
rl.on('close', async () => {
|
|
1162
|
+
try {
|
|
1163
|
+
await engine.mcpClient.disconnectAll();
|
|
1164
|
+
}
|
|
1165
|
+
catch { }
|
|
1166
|
+
try {
|
|
1167
|
+
engine.approval.close();
|
|
1168
|
+
}
|
|
1169
|
+
catch { }
|
|
1170
|
+
try {
|
|
1171
|
+
engine.sessionManager.save();
|
|
1172
|
+
}
|
|
1173
|
+
catch { }
|
|
1125
1174
|
engine.ui.info('Goodbye!');
|
|
1126
1175
|
process.exit(0);
|
|
1127
1176
|
});
|
|
1128
1177
|
}
|
|
1129
1178
|
function printHelp(engine) {
|
|
1130
1179
|
const t = engine.ui.theme;
|
|
1131
|
-
console.log(`\n ${t.bold('NeuroCLI v4.1.
|
|
1180
|
+
console.log(`\n ${t.bold('NeuroCLI v4.1.2 Commands:')}\n`);
|
|
1132
1181
|
console.log(` ${t.tool('/help')} Show this help message`);
|
|
1133
1182
|
console.log(` ${t.tool('/model [id]')} Switch or list models`);
|
|
1134
1183
|
console.log(` ${t.tool('/agent [name]')} Switch or list agents`);
|