neuro-cli 4.1.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/LICENSE +21 -0
- package/README.md +510 -0
- package/dist/advisor/advisor.d.ts +50 -0
- package/dist/advisor/advisor.js +178 -0
- package/dist/agents/base.d.ts +62 -0
- package/dist/agents/base.js +215 -0
- package/dist/agents/orchestrator.d.ts +46 -0
- package/dist/agents/orchestrator.js +192 -0
- package/dist/agents/team.d.ts +51 -0
- package/dist/agents/team.js +210 -0
- package/dist/api/models.d.ts +23 -0
- package/dist/api/models.js +514 -0
- package/dist/api/ollama.d.ts +153 -0
- package/dist/api/ollama.js +751 -0
- package/dist/api/openrouter.d.ts +55 -0
- package/dist/api/openrouter.js +223 -0
- package/dist/commands/commands.d.ts +43 -0
- package/dist/commands/commands.js +308 -0
- package/dist/config/config.d.ts +8 -0
- package/dist/config/config.js +311 -0
- package/dist/context/compaction.d.ts +54 -0
- package/dist/context/compaction.js +251 -0
- package/dist/context/custom-agents.d.ts +107 -0
- package/dist/context/custom-agents.js +397 -0
- package/dist/context/custom-tools.d.ts +120 -0
- package/dist/context/custom-tools.js +564 -0
- package/dist/context/git-checkpoint.d.ts +52 -0
- package/dist/context/git-checkpoint.js +240 -0
- package/dist/context/neuro-md.d.ts +48 -0
- package/dist/context/neuro-md.js +202 -0
- package/dist/context/neuroignore.d.ts +102 -0
- package/dist/context/neuroignore.js +441 -0
- package/dist/context/repo-map.d.ts +38 -0
- package/dist/context/repo-map.js +220 -0
- package/dist/context/skill-standard.d.ts +262 -0
- package/dist/context/skill-standard.js +1156 -0
- package/dist/context/skill-system.d.ts +75 -0
- package/dist/context/skill-system.js +578 -0
- package/dist/context/tree-sitter.d.ts +99 -0
- package/dist/context/tree-sitter.js +1956 -0
- package/dist/core/acp.d.ts +325 -0
- package/dist/core/acp.js +1498 -0
- package/dist/core/api-server.d.ts +143 -0
- package/dist/core/api-server.js +550 -0
- package/dist/core/approval.d.ts +81 -0
- package/dist/core/approval.js +432 -0
- package/dist/core/auto-compact.d.ts +127 -0
- package/dist/core/auto-compact.js +436 -0
- package/dist/core/auto-mode.d.ts +232 -0
- package/dist/core/auto-mode.js +831 -0
- package/dist/core/background-session.d.ts +166 -0
- package/dist/core/background-session.js +696 -0
- package/dist/core/cicd.d.ts +257 -0
- package/dist/core/cicd.js +1443 -0
- package/dist/core/cloud-sync.d.ts +156 -0
- package/dist/core/cloud-sync.js +582 -0
- package/dist/core/code-review.d.ts +132 -0
- package/dist/core/code-review.js +1191 -0
- package/dist/core/completion.d.ts +49 -0
- package/dist/core/completion.js +384 -0
- package/dist/core/context.d.ts +38 -0
- package/dist/core/context.js +144 -0
- package/dist/core/diff-preview.d.ts +35 -0
- package/dist/core/diff-preview.js +173 -0
- package/dist/core/doom-loop.d.ts +51 -0
- package/dist/core/doom-loop.js +179 -0
- package/dist/core/engine.d.ts +183 -0
- package/dist/core/engine.js +942 -0
- package/dist/core/extended-thinking.d.ts +103 -0
- package/dist/core/extended-thinking.js +269 -0
- package/dist/core/fallback.d.ts +54 -0
- package/dist/core/fallback.js +104 -0
- package/dist/core/git-worktree.d.ts +139 -0
- package/dist/core/git-worktree.js +614 -0
- package/dist/core/headless.d.ts +30 -0
- package/dist/core/headless.js +101 -0
- package/dist/core/i18n.d.ts +83 -0
- package/dist/core/i18n.js +739 -0
- package/dist/core/linting.d.ts +129 -0
- package/dist/core/linting.js +699 -0
- package/dist/core/model-router.d.ts +109 -0
- package/dist/core/model-router.js +581 -0
- package/dist/core/multi-model.d.ts +243 -0
- package/dist/core/multi-model.js +1099 -0
- package/dist/core/multi-session.d.ts +144 -0
- package/dist/core/multi-session.js +442 -0
- package/dist/core/multimodal.d.ts +125 -0
- package/dist/core/multimodal.js +286 -0
- package/dist/core/observability.d.ts +93 -0
- package/dist/core/observability.js +737 -0
- package/dist/core/os-sandbox.d.ts +122 -0
- package/dist/core/os-sandbox.js +1193 -0
- package/dist/core/outcome-grading.d.ts +228 -0
- package/dist/core/outcome-grading.js +1123 -0
- package/dist/core/output-styles.d.ts +57 -0
- package/dist/core/output-styles.js +382 -0
- package/dist/core/parallel-agents.d.ts +183 -0
- package/dist/core/parallel-agents.js +563 -0
- package/dist/core/plugin-bundle.d.ts +236 -0
- package/dist/core/plugin-bundle.js +887 -0
- package/dist/core/plugin-sdk.d.ts +139 -0
- package/dist/core/plugin-sdk.js +273 -0
- package/dist/core/prompt-cache.d.ts +163 -0
- package/dist/core/prompt-cache.js +599 -0
- package/dist/core/sandbox.d.ts +127 -0
- package/dist/core/sandbox.js +369 -0
- package/dist/core/scheduled-tasks.d.ts +151 -0
- package/dist/core/scheduled-tasks.js +502 -0
- package/dist/core/security-scanner.d.ts +160 -0
- package/dist/core/security-scanner.js +1494 -0
- package/dist/core/session.d.ts +83 -0
- package/dist/core/session.js +269 -0
- package/dist/core/shell-completion.d.ts +51 -0
- package/dist/core/shell-completion.js +674 -0
- package/dist/core/smart-monitor.d.ts +146 -0
- package/dist/core/smart-monitor.js +1199 -0
- package/dist/core/spec-driven.d.ts +233 -0
- package/dist/core/spec-driven.js +1485 -0
- package/dist/core/spending-warnings.d.ts +123 -0
- package/dist/core/spending-warnings.js +456 -0
- package/dist/core/sub-agent.d.ts +298 -0
- package/dist/core/sub-agent.js +1023 -0
- package/dist/core/telemetry.d.ts +157 -0
- package/dist/core/telemetry.js +412 -0
- package/dist/core/terminal-ux.d.ts +134 -0
- package/dist/core/terminal-ux.js +649 -0
- package/dist/core/testing.d.ts +146 -0
- package/dist/core/testing.js +1200 -0
- package/dist/core/types.d.ts +439 -0
- package/dist/core/types.js +6 -0
- package/dist/core/undo-redo.d.ts +112 -0
- package/dist/core/undo-redo.js +290 -0
- package/dist/core/updater.d.ts +159 -0
- package/dist/core/updater.js +608 -0
- package/dist/core/vim-mode.d.ts +151 -0
- package/dist/core/vim-mode.js +771 -0
- package/dist/core/voice.d.ts +137 -0
- package/dist/core/voice.js +538 -0
- package/dist/core/web-dashboard.d.ts +109 -0
- package/dist/core/web-dashboard.js +484 -0
- package/dist/hooks/hooks.d.ts +74 -0
- package/dist/hooks/hooks.js +160 -0
- package/dist/hooks/llm-evaluator.d.ts +165 -0
- package/dist/hooks/llm-evaluator.js +560 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1186 -0
- package/dist/lsp/lsp-manager.d.ts +63 -0
- package/dist/lsp/lsp-manager.js +351 -0
- package/dist/mcp/client.d.ts +133 -0
- package/dist/mcp/client.js +684 -0
- package/dist/mcp/mcp-apps.d.ts +70 -0
- package/dist/mcp/mcp-apps.js +1007 -0
- package/dist/tools/bash.d.ts +5 -0
- package/dist/tools/bash.js +195 -0
- package/dist/tools/browser.d.ts +92 -0
- package/dist/tools/browser.js +1570 -0
- package/dist/tools/extended.d.ts +6 -0
- package/dist/tools/extended.js +191 -0
- package/dist/tools/file.d.ts +10 -0
- package/dist/tools/file.js +382 -0
- package/dist/tools/github.d.ts +389 -0
- package/dist/tools/github.js +759 -0
- package/dist/tools/index.d.ts +9 -0
- package/dist/tools/index.js +40 -0
- package/dist/tools/memory.d.ts +6 -0
- package/dist/tools/memory.js +197 -0
- package/dist/tools/registry.d.ts +29 -0
- package/dist/tools/registry.js +64 -0
- package/dist/tools/web.d.ts +6 -0
- package/dist/tools/web.js +150 -0
- package/dist/ui/renderer.d.ts +97 -0
- package/dist/ui/renderer.js +279 -0
- package/dist/ui/theme.d.ts +27 -0
- package/dist/ui/theme.js +106 -0
- package/package.json +83 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,1186 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// ============================================================
|
|
3
|
+
// NeuroCLI - Advanced AI Terminal Coding Assistant
|
|
4
|
+
// Main Entry Point - v4.1.0 with auto-updater
|
|
5
|
+
// ============================================================
|
|
6
|
+
import { Command } from 'commander';
|
|
7
|
+
import { createInterface } from 'readline';
|
|
8
|
+
import { writeFileSync, readFileSync } from 'fs';
|
|
9
|
+
import { NeuroEngine } from './core/engine.js';
|
|
10
|
+
import { initConfig, saveConfig } from './config/config.js';
|
|
11
|
+
import { MODELS } from './api/models.js';
|
|
12
|
+
import { TerminalUI } from './ui/renderer.js';
|
|
13
|
+
import { getTheme } from './ui/theme.js';
|
|
14
|
+
import { CompletionEngine } from './core/completion.js';
|
|
15
|
+
import { HeadlessMode } from './core/headless.js';
|
|
16
|
+
import { ShellCompletionGenerator } from './core/shell-completion.js';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
import { AutoUpdater } from './core/updater.js';
|
|
19
|
+
const VERSION = '4.1.0';
|
|
20
|
+
// ---- CLI Setup ----
|
|
21
|
+
const program = new Command();
|
|
22
|
+
program
|
|
23
|
+
.name('neuro')
|
|
24
|
+
.description('NeuroCLI - Advanced AI Terminal Coding Assistant')
|
|
25
|
+
.version(VERSION)
|
|
26
|
+
.option('-k, --api-key <key>', 'OpenRouter API key')
|
|
27
|
+
.option('-m, --model <model>', 'Default model to use')
|
|
28
|
+
.option('-t, --theme <theme>', 'UI theme (dracula, dark, nord, light)')
|
|
29
|
+
.option('--no-streaming', 'Disable streaming output')
|
|
30
|
+
.option('--auto-approve', 'Auto-approve all tool calls')
|
|
31
|
+
.option('-c, --continue', 'Continue most recent session')
|
|
32
|
+
.option('-r, --resume <sessionId>', 'Resume specific session')
|
|
33
|
+
.option('--fork', 'Fork the resumed session instead of continuing it')
|
|
34
|
+
.option('--permission-mode <mode>', 'Permission mode: manual, auto, plan, yolo')
|
|
35
|
+
.option('--diff-preview', 'Enable diff preview before applying changes')
|
|
36
|
+
.option('--no-diff-preview', 'Disable diff preview')
|
|
37
|
+
.option('--effort <level>', 'Effort level: low, medium, high, ultrathink')
|
|
38
|
+
.option('--style <style>', 'Output style: concise, explanatory, learning, etc.')
|
|
39
|
+
.option('--thinking', 'Enable extended thinking display')
|
|
40
|
+
.option('--cache', 'Enable prompt caching')
|
|
41
|
+
.option('--no-cache', 'Disable prompt caching')
|
|
42
|
+
.option('--sandbox', 'Enable sandbox mode')
|
|
43
|
+
.option('--spending-limit <usd>', 'Set daily spending limit in USD', parseFloat)
|
|
44
|
+
.option('--ollama', 'Use Ollama local models instead of OpenRouter')
|
|
45
|
+
.action(async (options) => {
|
|
46
|
+
await startInteractive(options);
|
|
47
|
+
});
|
|
48
|
+
// ---- Headless/CI Mode ----
|
|
49
|
+
program
|
|
50
|
+
.command('run <prompt>')
|
|
51
|
+
.description('Run a task in headless/CI mode')
|
|
52
|
+
.option('-m, --model <model>', 'Model to use')
|
|
53
|
+
.option('-a, --agent <agent>', 'Agent to use')
|
|
54
|
+
.option('--max-turns <n>', 'Max agent iterations', parseInt)
|
|
55
|
+
.option('--allowed-tools <tools>', 'Comma-separated list of allowed tools')
|
|
56
|
+
.option('-f, --format <format>', 'Output format: text, json, stream-json')
|
|
57
|
+
.option('--auto', 'Auto-approve all tool calls')
|
|
58
|
+
.option('--continue <sessionId>', 'Continue a specific session')
|
|
59
|
+
.action(async (prompt, opts) => {
|
|
60
|
+
const result = await HeadlessMode.run({
|
|
61
|
+
prompt,
|
|
62
|
+
model: opts.model,
|
|
63
|
+
agent: opts.agent,
|
|
64
|
+
maxTurns: opts.maxTurns,
|
|
65
|
+
allowedTools: opts.allowedTools?.split(','),
|
|
66
|
+
outputFormat: opts.format || 'text',
|
|
67
|
+
autoApprove: opts.auto ?? true,
|
|
68
|
+
continueSession: opts.continue,
|
|
69
|
+
});
|
|
70
|
+
process.exit(result.exitCode);
|
|
71
|
+
});
|
|
72
|
+
// ---- Non-interactive ask ----
|
|
73
|
+
program
|
|
74
|
+
.command('ask <prompt>')
|
|
75
|
+
.description('Ask a single question and exit')
|
|
76
|
+
.option('-m, --model <model>', 'Model to use')
|
|
77
|
+
.option('-a, --agent <agent>', 'Agent to use')
|
|
78
|
+
.option('-f, --format <format>', 'Output format: text, json')
|
|
79
|
+
.action(async (prompt, opts) => {
|
|
80
|
+
const config = initConfig();
|
|
81
|
+
if (opts.model)
|
|
82
|
+
config.defaultModel = opts.model;
|
|
83
|
+
const engine = new NeuroEngine(config);
|
|
84
|
+
const result = await engine.processMessage(prompt, 'direct', opts.agent || 'Coder');
|
|
85
|
+
if (opts.format === 'json') {
|
|
86
|
+
console.log(JSON.stringify({ content: result.content, usage: result.usage }, null, 2));
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log(result.content);
|
|
90
|
+
}
|
|
91
|
+
process.exit(0);
|
|
92
|
+
});
|
|
93
|
+
// ---- Models list ----
|
|
94
|
+
program
|
|
95
|
+
.command('models')
|
|
96
|
+
.description('List available models')
|
|
97
|
+
.option('--ollama', 'List Ollama local models')
|
|
98
|
+
.action(async (opts) => {
|
|
99
|
+
if (opts.ollama) {
|
|
100
|
+
const { OllamaProvider } = await import('./api/ollama.js');
|
|
101
|
+
const provider = new OllamaProvider();
|
|
102
|
+
try {
|
|
103
|
+
const models = await provider.listModels();
|
|
104
|
+
if (models.length === 0) {
|
|
105
|
+
console.log(chalk.yellow('No local models found. Is Ollama running?'));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
console.log(chalk.bold('\nOllama Local Models:\n'));
|
|
109
|
+
for (const m of models) {
|
|
110
|
+
console.log(` ${chalk.cyan(m.name.padEnd(40))} ${chalk.gray(m.details.parameter_size || '')} ${chalk.gray(m.details.quantization_level || '')}`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
console.log(chalk.red('Could not connect to Ollama. Is it running?'));
|
|
115
|
+
}
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
const config = initConfig();
|
|
119
|
+
const ui = new TerminalUI(config.ui.theme);
|
|
120
|
+
ui.modelList(config.defaultModel);
|
|
121
|
+
});
|
|
122
|
+
// ---- Agents list ----
|
|
123
|
+
program
|
|
124
|
+
.command('agents')
|
|
125
|
+
.description('List available agents')
|
|
126
|
+
.action(() => {
|
|
127
|
+
const config = initConfig();
|
|
128
|
+
const ui = new TerminalUI(config.ui.theme);
|
|
129
|
+
const agents = Object.values(config.agents).map(a => ({
|
|
130
|
+
name: a.name,
|
|
131
|
+
description: a.description,
|
|
132
|
+
model: a.model || config.defaultModel,
|
|
133
|
+
}));
|
|
134
|
+
ui.agentList(agents);
|
|
135
|
+
});
|
|
136
|
+
// ---- Config ----
|
|
137
|
+
program
|
|
138
|
+
.command('config')
|
|
139
|
+
.description('Show or modify configuration')
|
|
140
|
+
.option('--set-key <key>', 'Set API key')
|
|
141
|
+
.option('--set-model <model>', 'Set default model')
|
|
142
|
+
.option('--set-theme <theme>', 'Set UI theme')
|
|
143
|
+
.option('--set-permission <mode>', 'Set permission mode (manual, auto, plan, yolo)')
|
|
144
|
+
.option('--set-spending-limit <usd>', 'Set daily spending limit in USD', parseFloat)
|
|
145
|
+
.option('--show', 'Show current config')
|
|
146
|
+
.action(async (opts) => {
|
|
147
|
+
const config = initConfig();
|
|
148
|
+
const theme = getTheme(config.ui.theme);
|
|
149
|
+
if (opts.setKey) {
|
|
150
|
+
config.apiKey = opts.setKey;
|
|
151
|
+
saveConfig(config);
|
|
152
|
+
console.log(chalk.green('API key updated'));
|
|
153
|
+
}
|
|
154
|
+
if (opts.setModel) {
|
|
155
|
+
if (!MODELS[opts.setModel]) {
|
|
156
|
+
console.log(chalk.red(`Unknown model: ${opts.setModel}`));
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
config.defaultModel = opts.setModel;
|
|
160
|
+
saveConfig(config);
|
|
161
|
+
console.log(chalk.green(`Default model set to ${MODELS[opts.setModel].name}`));
|
|
162
|
+
}
|
|
163
|
+
if (opts.setTheme) {
|
|
164
|
+
config.ui.theme = opts.setTheme;
|
|
165
|
+
saveConfig(config);
|
|
166
|
+
console.log(chalk.green(`Theme set to ${opts.setTheme}`));
|
|
167
|
+
}
|
|
168
|
+
if (opts.setPermission) {
|
|
169
|
+
config.permissionMode = opts.setPermission;
|
|
170
|
+
saveConfig(config);
|
|
171
|
+
console.log(chalk.green(`Permission mode set to ${opts.setPermission}`));
|
|
172
|
+
}
|
|
173
|
+
if (opts.setSpendingLimit !== undefined) {
|
|
174
|
+
config.spendingLimit = opts.setSpendingLimit;
|
|
175
|
+
saveConfig(config);
|
|
176
|
+
console.log(chalk.green(`Spending limit set to $${opts.setSpendingLimit}`));
|
|
177
|
+
}
|
|
178
|
+
if (opts.show || (!opts.setKey && !opts.setModel && !opts.setTheme && !opts.setPermission && opts.setSpendingLimit === undefined)) {
|
|
179
|
+
console.log(chalk.bold('\nCurrent Configuration:\n'));
|
|
180
|
+
console.log(` API Key: ${config.apiKey ? chalk.green('configured') : chalk.red('not set')}`);
|
|
181
|
+
console.log(` Base URL: ${config.baseUrl}`);
|
|
182
|
+
console.log(` Default Model: ${config.defaultModel}`);
|
|
183
|
+
console.log(` Theme: ${config.ui.theme}`);
|
|
184
|
+
console.log(` Permission Mode: ${config.permissionMode}`);
|
|
185
|
+
console.log(` Diff Preview: ${config.diffPreview ? 'enabled' : 'disabled'}`);
|
|
186
|
+
console.log(` Fallback Chain: ${config.fallbackChain.models.join(' -> ')}`);
|
|
187
|
+
console.log(` Doom Loop Protection: ${config.doomLoop.autoBreak ? 'enabled' : 'disabled'}`);
|
|
188
|
+
console.log(` MCP Auto-Connect: ${config.mcp.autoConnect ? 'enabled' : 'disabled'}`);
|
|
189
|
+
console.log(` Streaming: ${config.ui.streaming ? 'enabled' : 'disabled'}`);
|
|
190
|
+
console.log(` Prompt Cache: ${config.promptCache.enabled ? 'enabled' : 'disabled'}`);
|
|
191
|
+
console.log(` Spending Limit: ${config.spendingLimit > 0 ? '$' + config.spendingLimit.toFixed(2) : 'unlimited'}`);
|
|
192
|
+
console.log();
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
// ---- Sessions ----
|
|
196
|
+
program
|
|
197
|
+
.command('sessions')
|
|
198
|
+
.description('List or manage sessions')
|
|
199
|
+
.option('--clear', 'Clear all sessions')
|
|
200
|
+
.action(async (opts) => {
|
|
201
|
+
const { SessionManager } = await import('./core/session.js');
|
|
202
|
+
const sm = new SessionManager();
|
|
203
|
+
if (opts.clear) {
|
|
204
|
+
console.log(chalk.yellow('Clearing all sessions...'));
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const sessions = sm.list();
|
|
208
|
+
if (sessions.length === 0) {
|
|
209
|
+
console.log(chalk.gray('No sessions found.'));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.log(chalk.bold('\nSessions:\n'));
|
|
213
|
+
for (const session of sessions.slice(0, 20)) {
|
|
214
|
+
const date = new Date(session.createdAt).toLocaleString();
|
|
215
|
+
console.log(` ${chalk.cyan(session.id.slice(0, 20))} ${chalk.gray(date)} ${chalk.gray(`${session.messageCount} msgs`)} ${chalk.gray(`$${session.cost.toFixed(4)}`)}`);
|
|
216
|
+
}
|
|
217
|
+
console.log();
|
|
218
|
+
});
|
|
219
|
+
// ---- MCP Management ----
|
|
220
|
+
program
|
|
221
|
+
.command('mcp')
|
|
222
|
+
.description('Manage MCP (Model Context Protocol) servers')
|
|
223
|
+
.addCommand(new Command('add')
|
|
224
|
+
.description('Add an MCP server')
|
|
225
|
+
.argument('<name>', 'Server name')
|
|
226
|
+
.argument('<command>', 'Command to run (for stdio) or URL (for http/sse)')
|
|
227
|
+
.option('-t, --transport <type>', 'Transport type: stdio, sse, http', 'stdio')
|
|
228
|
+
.option('--headers <json>', 'HTTP headers as JSON string')
|
|
229
|
+
.action(async (name, command, opts) => {
|
|
230
|
+
const { MCPClient } = await import('./mcp/client.js');
|
|
231
|
+
const client = new MCPClient();
|
|
232
|
+
const isUrl = command.startsWith('http://') || command.startsWith('https://');
|
|
233
|
+
const transport = isUrl ? (opts.transport === 'http' ? 'http' : 'sse') : 'stdio';
|
|
234
|
+
client.addServer(name, {
|
|
235
|
+
name,
|
|
236
|
+
transport: transport,
|
|
237
|
+
command: transport === 'stdio' ? command : undefined,
|
|
238
|
+
url: isUrl ? command : undefined,
|
|
239
|
+
headers: opts.headers ? JSON.parse(opts.headers) : undefined,
|
|
240
|
+
});
|
|
241
|
+
console.log(chalk.green(`MCP server "${name}" added (${transport})`));
|
|
242
|
+
}))
|
|
243
|
+
.addCommand(new Command('list').description('List configured MCP servers').action(async () => {
|
|
244
|
+
const { MCPClient } = await import('./mcp/client.js');
|
|
245
|
+
const client = new MCPClient();
|
|
246
|
+
const servers = client.listServers();
|
|
247
|
+
if (servers.length === 0) {
|
|
248
|
+
console.log(chalk.gray('No MCP servers configured.'));
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
console.log(chalk.bold('\nMCP Servers:\n'));
|
|
252
|
+
for (const s of servers) {
|
|
253
|
+
const status = s.connected ? chalk.green('connected') : chalk.gray('disconnected');
|
|
254
|
+
console.log(` ${chalk.cyan(s.name)} ${status} ${chalk.gray(`${s.toolCount} tools`)} ${chalk.gray(s.config.transport)}`);
|
|
255
|
+
}
|
|
256
|
+
console.log();
|
|
257
|
+
}))
|
|
258
|
+
.addCommand(new Command('remove').description('Remove an MCP server').argument('<name>').action(async (name) => {
|
|
259
|
+
const { MCPClient } = await import('./mcp/client.js');
|
|
260
|
+
const client = new MCPClient();
|
|
261
|
+
if (client.removeServer(name))
|
|
262
|
+
console.log(chalk.green(`MCP server "${name}" removed`));
|
|
263
|
+
else
|
|
264
|
+
console.log(chalk.red(`MCP server "${name}" not found`));
|
|
265
|
+
}));
|
|
266
|
+
// ---- Update Command ----
|
|
267
|
+
program
|
|
268
|
+
.command('update')
|
|
269
|
+
.description('Check for updates and optionally self-update')
|
|
270
|
+
.option('--force', 'Force check even if recently checked')
|
|
271
|
+
.option('--auto', 'Auto-update without prompting')
|
|
272
|
+
.option('--check-only', 'Only check, do not update')
|
|
273
|
+
.option('--dismiss', 'Dismiss the current available update')
|
|
274
|
+
.action(async (opts) => {
|
|
275
|
+
const updater = new AutoUpdater({ currentVersion: VERSION });
|
|
276
|
+
if (opts.dismiss) {
|
|
277
|
+
const result = await updater.checkForUpdate(true);
|
|
278
|
+
if (result.hasUpdate) {
|
|
279
|
+
updater.dismissVersion(result.latestVersion);
|
|
280
|
+
console.log(chalk.gray(`Dismissed update notification for v${result.latestVersion}`));
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
console.log(chalk.gray('No update to dismiss'));
|
|
284
|
+
}
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (opts.auto) {
|
|
288
|
+
updater.setAutoUpdate(true);
|
|
289
|
+
}
|
|
290
|
+
if (opts.checkOnly) {
|
|
291
|
+
console.log(chalk.cyan('Checking for updates...'));
|
|
292
|
+
const result = await updater.checkForUpdate(opts.force);
|
|
293
|
+
if (result.hasUpdate) {
|
|
294
|
+
updater.showUpdateDetails(result);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
updater.showUpToDate();
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
// Interactive update flow
|
|
302
|
+
await updater.interactiveUpdate();
|
|
303
|
+
});
|
|
304
|
+
// ---- Shell Completion ----
|
|
305
|
+
program
|
|
306
|
+
.command('completion <shell>')
|
|
307
|
+
.description('Generate shell completion script (bash, zsh, fish)')
|
|
308
|
+
.action((shell) => {
|
|
309
|
+
const generator = new ShellCompletionGenerator(ShellCompletionGenerator.getDefaultOptions());
|
|
310
|
+
const script = generator.generate(shell);
|
|
311
|
+
console.log(script);
|
|
312
|
+
});
|
|
313
|
+
// ---- Interactive Mode ----
|
|
314
|
+
async function startInteractive(options) {
|
|
315
|
+
const config = initConfig(options.apiKey);
|
|
316
|
+
if (options.model)
|
|
317
|
+
config.defaultModel = options.model;
|
|
318
|
+
if (options.theme)
|
|
319
|
+
config.ui.theme = options.theme;
|
|
320
|
+
if (options.noStreaming)
|
|
321
|
+
config.ui.streaming = false;
|
|
322
|
+
if (options.permissionMode)
|
|
323
|
+
config.permissionMode = options.permissionMode;
|
|
324
|
+
if (options.diffPreview === true)
|
|
325
|
+
config.diffPreview = true;
|
|
326
|
+
if (options.diffPreview === false)
|
|
327
|
+
config.diffPreview = false;
|
|
328
|
+
if (options.cache === true)
|
|
329
|
+
config.promptCache.enabled = true;
|
|
330
|
+
if (options.cache === false)
|
|
331
|
+
config.promptCache.enabled = false;
|
|
332
|
+
if (options.spendingLimit)
|
|
333
|
+
config.spendingLimit = options.spendingLimit;
|
|
334
|
+
if (options.autoApprove) {
|
|
335
|
+
config.tools.autoApprove = [...config.tools.autoApprove, ...config.tools.requireApproval];
|
|
336
|
+
config.tools.requireApproval = [];
|
|
337
|
+
config.permissionMode = 'yolo';
|
|
338
|
+
}
|
|
339
|
+
// Check API key
|
|
340
|
+
if (!config.apiKey && !options.ollama) {
|
|
341
|
+
console.log(chalk.red('\nOpenRouter API key not configured!'));
|
|
342
|
+
console.log(chalk.yellow('\nSet it with:'));
|
|
343
|
+
console.log(` ${chalk.cyan('neuro config --set-key YOUR_API_KEY')}`);
|
|
344
|
+
console.log(` ${chalk.cyan('export OPENROUTER_API_KEY=YOUR_API_KEY')}`);
|
|
345
|
+
console.log(` ${chalk.cyan('neuro -k YOUR_API_KEY')}`);
|
|
346
|
+
console.log(chalk.gray('\nGet your key at: https://openrouter.ai/keys\n'));
|
|
347
|
+
process.exit(1);
|
|
348
|
+
}
|
|
349
|
+
// Initialize engine
|
|
350
|
+
const engine = new NeuroEngine(config);
|
|
351
|
+
// Set effort level if specified
|
|
352
|
+
if (options.effort) {
|
|
353
|
+
engine.modelRouter.setEffort(options.effort);
|
|
354
|
+
}
|
|
355
|
+
// Set output style if specified
|
|
356
|
+
if (options.style) {
|
|
357
|
+
engine.styleManager.setStyle(options.style);
|
|
358
|
+
}
|
|
359
|
+
// Enable thinking if specified
|
|
360
|
+
if (options.thinking) {
|
|
361
|
+
engine.extendedThinking.setMode('full');
|
|
362
|
+
engine.extendedThinking.toggleDisplay();
|
|
363
|
+
}
|
|
364
|
+
// Enable sandbox if specified
|
|
365
|
+
if (options.sandbox) {
|
|
366
|
+
engine.sandbox.enable();
|
|
367
|
+
}
|
|
368
|
+
// Enable prompt cache if specified
|
|
369
|
+
if (options.cache) {
|
|
370
|
+
config.promptCache.enabled = true;
|
|
371
|
+
}
|
|
372
|
+
// Resume session if requested
|
|
373
|
+
if (options.resume) {
|
|
374
|
+
const session = engine.sessionManager.load(options.resume);
|
|
375
|
+
if (session) {
|
|
376
|
+
engine.ui.success(`Resumed session: ${session.id.slice(0, 20)}...`);
|
|
377
|
+
if (options.fork) {
|
|
378
|
+
const forked = engine.sessionManager.create(process.cwd(), config.defaultModel);
|
|
379
|
+
forked.messages = [...session.messages];
|
|
380
|
+
engine.sessionManager.save();
|
|
381
|
+
engine.ui.success(`Forked to new session: ${forked.id.slice(0, 20)}...`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
else {
|
|
385
|
+
engine.ui.error(`Session not found: ${options.resume}`);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
else if (options.continue) {
|
|
389
|
+
const sessions = engine.sessionManager.list();
|
|
390
|
+
if (sessions.length > 0) {
|
|
391
|
+
const session = engine.sessionManager.load(sessions[0].id);
|
|
392
|
+
if (session) {
|
|
393
|
+
engine.ui.success(`Continued session: ${session.id.slice(0, 20)}...`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
engine.ui.warning('No sessions found to continue');
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
// Initialize auto-updater and check for updates in background
|
|
401
|
+
const updater = new AutoUpdater({ currentVersion: VERSION });
|
|
402
|
+
const updateCheck = updater.checkOnStartup(); // Fire and forget — don't block startup
|
|
403
|
+
// Print banner
|
|
404
|
+
engine.ui.banner();
|
|
405
|
+
const theme = engine.ui.theme;
|
|
406
|
+
const permMode = engine.approval.getMode();
|
|
407
|
+
const permIcon = engine.approval.getModeIcon();
|
|
408
|
+
const activeStyle = engine.styleManager.getStyle();
|
|
409
|
+
console.log(theme.muted(` Model: ${MODELS[config.defaultModel]?.name || config.defaultModel}`));
|
|
410
|
+
console.log(theme.muted(` Permission: ${permIcon} ${permMode}`));
|
|
411
|
+
console.log(theme.muted(` Style: ${activeStyle.name}`));
|
|
412
|
+
console.log(theme.muted(` Thinking: ${engine.extendedThinking.getMode()}`));
|
|
413
|
+
console.log(theme.muted(` Cache: ${config.promptCache.enabled ? 'on' : 'off'}`));
|
|
414
|
+
console.log(theme.muted(` Working Dir: ${process.cwd()}`));
|
|
415
|
+
console.log(theme.muted(` Type /help for commands, Tab for completion, Ctrl+C to exit\n`));
|
|
416
|
+
// Show update notification if available (after banner)
|
|
417
|
+
updateCheck.then((result) => {
|
|
418
|
+
if (result && result.hasUpdate) {
|
|
419
|
+
updater.showUpdateNotification(result);
|
|
420
|
+
}
|
|
421
|
+
}).catch(() => { });
|
|
422
|
+
// Create readline with tab completion
|
|
423
|
+
const completionEngine = new CompletionEngine(process.cwd());
|
|
424
|
+
completionEngine.setAgentNames(Array.from(engine.agents.keys()));
|
|
425
|
+
const rl = createInterface({
|
|
426
|
+
input: process.stdin,
|
|
427
|
+
output: process.stdout,
|
|
428
|
+
prompt: chalk.cyan('❯ '),
|
|
429
|
+
historySize: 100,
|
|
430
|
+
completer: completionEngine.complete,
|
|
431
|
+
});
|
|
432
|
+
rl.prompt();
|
|
433
|
+
let currentMode = 'auto';
|
|
434
|
+
let currentAgent;
|
|
435
|
+
rl.on('line', async (line) => {
|
|
436
|
+
const input = line.trim();
|
|
437
|
+
if (!input) {
|
|
438
|
+
rl.prompt();
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// Add to history
|
|
442
|
+
completionEngine.addHistory(input);
|
|
443
|
+
// Handle slash commands
|
|
444
|
+
if (input.startsWith('/')) {
|
|
445
|
+
const [cmd, ...args] = input.slice(1).split(' ');
|
|
446
|
+
switch (cmd) {
|
|
447
|
+
case 'help':
|
|
448
|
+
printHelp(engine);
|
|
449
|
+
break;
|
|
450
|
+
case 'model':
|
|
451
|
+
if (args[0]) {
|
|
452
|
+
engine.switchModel(args[0]);
|
|
453
|
+
engine.fallback.addFallbackModel(config.defaultModel);
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
engine.ui.modelList(config.defaultModel);
|
|
457
|
+
}
|
|
458
|
+
break;
|
|
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}`);
|
|
467
|
+
}
|
|
468
|
+
else {
|
|
469
|
+
engine.ui.error(`Agent not found. Available: ${agentNames.join(', ')}`);
|
|
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}`);
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
engine.ui.error('No sessions found');
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
const sessions = engine.sessionManager.list();
|
|
530
|
+
if (sessions.length === 0) {
|
|
531
|
+
engine.ui.info('No sessions found');
|
|
532
|
+
break;
|
|
533
|
+
}
|
|
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
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
const undone = engine.undoRedo.undoN(rewindN);
|
|
574
|
+
engine.ui.success(`Rewound ${undone.length} action(s)`);
|
|
575
|
+
break;
|
|
576
|
+
case 'fork':
|
|
577
|
+
engine.ui.info('Forking current session...');
|
|
578
|
+
const currentSession = engine.sessionManager.getCurrent();
|
|
579
|
+
if (currentSession) {
|
|
580
|
+
const forked = engine.sessionManager.create(process.cwd(), config.defaultModel);
|
|
581
|
+
forked.messages = [...currentSession.messages];
|
|
582
|
+
forked.forkedFrom = currentSession.id;
|
|
583
|
+
engine.sessionManager.save();
|
|
584
|
+
engine.ui.success(`Forked to new session: ${forked.id.slice(0, 20)}...`);
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
engine.ui.error('No active session to fork');
|
|
588
|
+
}
|
|
589
|
+
break;
|
|
590
|
+
case 'mcp':
|
|
591
|
+
const mcpSub = args[0];
|
|
592
|
+
if (mcpSub === 'list') {
|
|
593
|
+
const servers = engine.mcpClient.listServers();
|
|
594
|
+
if (servers.length === 0) {
|
|
595
|
+
engine.ui.info('No MCP servers configured');
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
console.log(chalk.bold('\nMCP Servers:\n'));
|
|
599
|
+
for (const s of servers) {
|
|
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)}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
else if (mcpSub === 'connect' && args[1]) {
|
|
605
|
+
try {
|
|
606
|
+
const cfg = engine.mcpClient.loadConfig();
|
|
607
|
+
if (cfg.mcpServers[args[1]]) {
|
|
608
|
+
await engine.mcpClient.connect(args[1], cfg.mcpServers[args[1]]);
|
|
609
|
+
engine.ui.success(`Connected to MCP server: ${args[1]}`);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
engine.ui.error(`MCP server "${args[1]}" not found`);
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
catch (e) {
|
|
616
|
+
engine.ui.error(`Failed to connect: ${e instanceof Error ? e.message : String(e)}`);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
else if (mcpSub === 'disconnect' && args[1]) {
|
|
620
|
+
await engine.mcpClient.disconnect(args[1]);
|
|
621
|
+
engine.ui.success(`Disconnected from ${args[1]}`);
|
|
622
|
+
}
|
|
623
|
+
else if (mcpSub === 'health') {
|
|
624
|
+
engine.mcpClient.healthReport();
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
console.log(chalk.bold('\nMCP Commands:\n'));
|
|
628
|
+
console.log(' /mcp list List MCP servers');
|
|
629
|
+
console.log(' /mcp connect <name> Connect to a server');
|
|
630
|
+
console.log(' /mcp disconnect <name> Disconnect from a server');
|
|
631
|
+
console.log(' /mcp health Show MCP health report');
|
|
632
|
+
}
|
|
633
|
+
break;
|
|
634
|
+
case 'permission':
|
|
635
|
+
case 'perm':
|
|
636
|
+
if (args[0]) {
|
|
637
|
+
const validModes = ['manual', 'auto', 'plan', 'yolo'];
|
|
638
|
+
if (validModes.includes(args[0])) {
|
|
639
|
+
engine.approval.setMode(args[0]);
|
|
640
|
+
engine.ui.success(`${engine.approval.getModeIcon()} Permission: ${args[0]} (${engine.approval.getModeDescription()})`);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
engine.ui.error(`Invalid mode. Use: ${validModes.join(', ')}`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
else {
|
|
647
|
+
const newMode = engine.approval.cycleMode();
|
|
648
|
+
engine.ui.success(`${engine.approval.getModeIcon()} Permission: ${newMode} (${engine.approval.getModeDescription()})`);
|
|
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');
|
|
698
|
+
break;
|
|
699
|
+
}
|
|
700
|
+
console.log(chalk.bold('\nPlugins:\n'));
|
|
701
|
+
for (const p of plugins) {
|
|
702
|
+
console.log(` ${chalk.cyan(p.name)} v${p.version} - ${chalk.gray(p.description)} ${chalk.green(`${p.toolCount} tools`)}`);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
else if (pluginSub === 'load' && args[1]) {
|
|
706
|
+
try {
|
|
707
|
+
await engine.pluginManager.loadByName(args[1]);
|
|
708
|
+
engine.ui.success(`Plugin "${args[1]}" loaded`);
|
|
709
|
+
}
|
|
710
|
+
catch (e) {
|
|
711
|
+
engine.ui.error(`Failed to load plugin: ${e instanceof Error ? e.message : String(e)}`);
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
else {
|
|
715
|
+
console.log(chalk.bold('\nPlugin Commands:\n'));
|
|
716
|
+
console.log(' /plugins list List loaded plugins');
|
|
717
|
+
console.log(' /plugins load <name> Load a plugin');
|
|
718
|
+
}
|
|
719
|
+
break;
|
|
720
|
+
case 'whitelist':
|
|
721
|
+
if (args[0] === 'add' && args[1]) {
|
|
722
|
+
engine.approval.addToWhitelist(args[1]);
|
|
723
|
+
engine.ui.success(`Added "${args[1]}" to whitelist`);
|
|
724
|
+
}
|
|
725
|
+
else if (args[0] === 'remove' && args[1]) {
|
|
726
|
+
engine.approval.removeFromWhitelist(args[1]);
|
|
727
|
+
engine.ui.success(`Removed "${args[1]}" from whitelist`);
|
|
728
|
+
}
|
|
729
|
+
else if (args[0] === 'list') {
|
|
730
|
+
const wl = engine.approval.getWhitelist();
|
|
731
|
+
console.log(chalk.bold('\nWhitelisted tools:\n'));
|
|
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]}`);
|
|
763
|
+
}
|
|
764
|
+
else {
|
|
765
|
+
engine.ui.error(`Unknown style. Available: ${engine.styleManager.listStyles().map(s => s.name).join(', ')}`);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
else {
|
|
769
|
+
engine.styleManager.printStyles();
|
|
770
|
+
}
|
|
771
|
+
break;
|
|
772
|
+
case 'thinking':
|
|
773
|
+
if (args[0]) {
|
|
774
|
+
const modes = ['none', 'brief', 'full', 'ultrathink'];
|
|
775
|
+
if (modes.includes(args[0])) {
|
|
776
|
+
engine.extendedThinking.setMode(args[0]);
|
|
777
|
+
if (args[0] !== 'none' && !engine.extendedThinking.isDisplayEnabled()) {
|
|
778
|
+
engine.extendedThinking.toggleDisplay();
|
|
779
|
+
}
|
|
780
|
+
engine.ui.success(`Thinking mode: ${args[0]}`);
|
|
781
|
+
}
|
|
782
|
+
else {
|
|
783
|
+
engine.ui.error(`Invalid mode. Use: ${modes.join(', ')}`);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
const currentMode = engine.extendedThinking.getMode();
|
|
788
|
+
const showing = engine.extendedThinking.isDisplayEnabled() ? 'visible' : 'hidden';
|
|
789
|
+
console.log(chalk.bold(`\nThinking Mode: ${chalk.cyan(currentMode)} (display: ${showing})`));
|
|
790
|
+
console.log(chalk.gray(' Toggle display: /thinking toggle'));
|
|
791
|
+
console.log(chalk.gray(' Set mode: /thinking none|brief|full|ultrathink'));
|
|
792
|
+
console.log();
|
|
793
|
+
}
|
|
794
|
+
break;
|
|
795
|
+
case 'skills':
|
|
796
|
+
const skillSub = args[0];
|
|
797
|
+
if (skillSub === 'list') {
|
|
798
|
+
engine.skillSystem.listSkills();
|
|
799
|
+
}
|
|
800
|
+
else if (skillSub === 'activate' && args[1]) {
|
|
801
|
+
const activated = engine.skillSystem.activate(args[1]);
|
|
802
|
+
if (activated) {
|
|
803
|
+
engine.ui.success(`Skill activated: ${args[1]}`);
|
|
804
|
+
}
|
|
805
|
+
else {
|
|
806
|
+
engine.ui.error(`Skill not found: ${args[1]}`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
else if (skillSub === 'deactivate' && args[1]) {
|
|
810
|
+
if (engine.skillSystem.deactivate(args[1])) {
|
|
811
|
+
engine.ui.success(`Skill deactivated: ${args[1]}`);
|
|
812
|
+
}
|
|
813
|
+
else {
|
|
814
|
+
engine.ui.error(`Skill not active: ${args[1]}`);
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
else if (skillSub === 'clear') {
|
|
818
|
+
engine.skillSystem.deactivateAll();
|
|
819
|
+
engine.ui.success('All skills deactivated');
|
|
820
|
+
}
|
|
821
|
+
else {
|
|
822
|
+
console.log(chalk.bold('\nSkill Commands:\n'));
|
|
823
|
+
console.log(' /skills list List all skills');
|
|
824
|
+
console.log(' /skills activate <name> Activate a skill');
|
|
825
|
+
console.log(' /skills deactivate <name> Deactivate a skill');
|
|
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]}`);
|
|
835
|
+
}
|
|
836
|
+
else {
|
|
837
|
+
engine.ui.error(`Invalid level. Use: ${levels.join(', ')}`);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
else {
|
|
841
|
+
console.log(chalk.bold(`\nEffort Level: ${chalk.cyan(engine.modelRouter.getEffort())}`));
|
|
842
|
+
console.log(chalk.gray(' Set level: /effort low|medium|high|ultrathink'));
|
|
843
|
+
console.log();
|
|
844
|
+
}
|
|
845
|
+
break;
|
|
846
|
+
case 'cache':
|
|
847
|
+
const cacheSub = args[0];
|
|
848
|
+
if (cacheSub === 'on') {
|
|
849
|
+
config.promptCache.enabled = true;
|
|
850
|
+
engine.ui.success('Prompt cache enabled');
|
|
851
|
+
}
|
|
852
|
+
else if (cacheSub === 'off') {
|
|
853
|
+
config.promptCache.enabled = false;
|
|
854
|
+
engine.ui.success('Prompt cache disabled');
|
|
855
|
+
}
|
|
856
|
+
else if (cacheSub === 'clear') {
|
|
857
|
+
engine.promptCache.clear();
|
|
858
|
+
engine.ui.success('Cache cleared');
|
|
859
|
+
}
|
|
860
|
+
else if (cacheSub === 'stats') {
|
|
861
|
+
engine.promptCache.printStats();
|
|
862
|
+
}
|
|
863
|
+
else {
|
|
864
|
+
const status = config.promptCache.enabled ? chalk.green('enabled') : chalk.gray('disabled');
|
|
865
|
+
console.log(chalk.bold(`\nPrompt Cache: ${status}`));
|
|
866
|
+
console.log(chalk.gray(' /cache on|off|clear|stats'));
|
|
867
|
+
console.log();
|
|
868
|
+
}
|
|
869
|
+
break;
|
|
870
|
+
case 'spending':
|
|
871
|
+
engine.spendingMonitor.printReport();
|
|
872
|
+
break;
|
|
873
|
+
case 'ignore':
|
|
874
|
+
if (args[0] === 'list') {
|
|
875
|
+
engine.neuroIgnore.printRules();
|
|
876
|
+
}
|
|
877
|
+
else if (args[0] === 'add' && args[1]) {
|
|
878
|
+
engine.neuroIgnore.addRule(args[1], 'manual');
|
|
879
|
+
engine.ui.success(`Added ignore rule: ${args[1]}`);
|
|
880
|
+
}
|
|
881
|
+
else if (args[0] === 'check' && args[1]) {
|
|
882
|
+
const ignored = engine.neuroIgnore.isIgnored(args[1]);
|
|
883
|
+
console.log(` ${args[1]}: ${ignored ? chalk.red('ignored') : chalk.green('allowed')}`);
|
|
884
|
+
}
|
|
885
|
+
else {
|
|
886
|
+
console.log(chalk.bold('\nIgnore Commands:\n'));
|
|
887
|
+
console.log(' /ignore list List ignore rules');
|
|
888
|
+
console.log(' /ignore add <pattern> Add an ignore pattern');
|
|
889
|
+
console.log(' /ignore check <path> Check if a path is ignored');
|
|
890
|
+
}
|
|
891
|
+
break;
|
|
892
|
+
case 'ollama':
|
|
893
|
+
try {
|
|
894
|
+
const available = await engine.ollamaProvider.isAvailable();
|
|
895
|
+
if (!available) {
|
|
896
|
+
engine.ui.error('Ollama is not running. Start it with: ollama serve');
|
|
897
|
+
break;
|
|
898
|
+
}
|
|
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.0 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
|
+
try {
|
|
955
|
+
writeFileSync(exportPath, JSON.stringify(exportData, null, 2), 'utf-8');
|
|
956
|
+
engine.ui.success(`Session exported to ${exportPath}`);
|
|
957
|
+
}
|
|
958
|
+
catch (e) {
|
|
959
|
+
console.log(JSON.stringify(exportSession, null, 2));
|
|
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>');
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
try {
|
|
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
|
|
1004
|
+
try {
|
|
1005
|
+
const prResult = execSync(`gh pr create --title "${commitMsg}" --body "Auto-generated by NeuroCLI"`, { cwd: process.cwd(), encoding: 'utf-8' });
|
|
1006
|
+
engine.ui.success(`PR created: ${prResult.trim()}`);
|
|
1007
|
+
}
|
|
1008
|
+
catch {
|
|
1009
|
+
engine.ui.info('Could not create PR (gh CLI not available or not a GitHub repo)');
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
catch (e) {
|
|
1013
|
+
engine.ui.error(`Git operation failed: ${e instanceof Error ? e.message : String(e)}`);
|
|
1014
|
+
}
|
|
1015
|
+
break;
|
|
1016
|
+
case 'code-review':
|
|
1017
|
+
engine.ui.info('Starting multi-agent code review...');
|
|
1018
|
+
try {
|
|
1019
|
+
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');
|
|
1020
|
+
}
|
|
1021
|
+
catch (e) {
|
|
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.'));
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
engine.ui.error(updateResult.message);
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
else if (updateSub === 'check') {
|
|
1054
|
+
engine.ui.info('Checking for updates...');
|
|
1055
|
+
const checkResult = await updater.checkForUpdate(true);
|
|
1056
|
+
if (checkResult.hasUpdate) {
|
|
1057
|
+
updater.showUpdateDetails(checkResult);
|
|
1058
|
+
updater.showUpdateNotification(checkResult);
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
updater.showUpToDate();
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
else if (updateSub === 'dismiss') {
|
|
1065
|
+
const checkResult = await updater.checkForUpdate(true);
|
|
1066
|
+
if (checkResult.hasUpdate) {
|
|
1067
|
+
updater.dismissVersion(checkResult.latestVersion);
|
|
1068
|
+
engine.ui.success(`Dismissed update notification for v${checkResult.latestVersion}`);
|
|
1069
|
+
}
|
|
1070
|
+
else {
|
|
1071
|
+
engine.ui.info('No update to dismiss');
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
else if (updateSub === 'auto') {
|
|
1075
|
+
const enableAuto = args[1] !== 'off';
|
|
1076
|
+
updater.setAutoUpdate(enableAuto);
|
|
1077
|
+
engine.ui.success(`Auto-update: ${enableAuto ? 'enabled' : 'disabled'}`);
|
|
1078
|
+
}
|
|
1079
|
+
else if (updateSub === 'interval') {
|
|
1080
|
+
const hours = args[1] ? parseFloat(args[1]) : 24;
|
|
1081
|
+
if (isNaN(hours) || hours < 1) {
|
|
1082
|
+
engine.ui.error('Interval must be at least 1 hour');
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
updater.setCheckInterval(hours);
|
|
1086
|
+
engine.ui.success(`Update check interval set to ${hours} hours`);
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
else if (updateSub === 'reset') {
|
|
1090
|
+
updater.resetDismissed();
|
|
1091
|
+
updater.forceNextCheck();
|
|
1092
|
+
engine.ui.success('Update preferences reset');
|
|
1093
|
+
}
|
|
1094
|
+
else {
|
|
1095
|
+
// Default: interactive update flow
|
|
1096
|
+
await updater.interactiveUpdate();
|
|
1097
|
+
}
|
|
1098
|
+
break;
|
|
1099
|
+
case 'exit':
|
|
1100
|
+
case 'quit':
|
|
1101
|
+
case 'q':
|
|
1102
|
+
engine.mcpClient.disconnectAll().catch(() => { });
|
|
1103
|
+
engine.approval.close();
|
|
1104
|
+
engine.ui.info('Goodbye!');
|
|
1105
|
+
process.exit(0);
|
|
1106
|
+
break;
|
|
1107
|
+
default:
|
|
1108
|
+
engine.ui.error(`Unknown command: /${cmd}. Type /help for available commands.`);
|
|
1109
|
+
}
|
|
1110
|
+
rl.prompt();
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
// Process message with the engine
|
|
1114
|
+
try {
|
|
1115
|
+
await engine.processMessage(input, currentMode, currentAgent);
|
|
1116
|
+
}
|
|
1117
|
+
catch (error) {
|
|
1118
|
+
engine.ui.error(error instanceof Error ? error.message : String(error));
|
|
1119
|
+
}
|
|
1120
|
+
rl.prompt();
|
|
1121
|
+
});
|
|
1122
|
+
rl.on('close', () => {
|
|
1123
|
+
engine.mcpClient.disconnectAll().catch(() => { });
|
|
1124
|
+
engine.approval.close();
|
|
1125
|
+
engine.ui.info('Goodbye!');
|
|
1126
|
+
process.exit(0);
|
|
1127
|
+
});
|
|
1128
|
+
}
|
|
1129
|
+
function printHelp(engine) {
|
|
1130
|
+
const t = engine.ui.theme;
|
|
1131
|
+
console.log(`\n ${t.bold('NeuroCLI v4.1.0 Commands:')}\n`);
|
|
1132
|
+
console.log(` ${t.tool('/help')} Show this help message`);
|
|
1133
|
+
console.log(` ${t.tool('/model [id]')} Switch or list models`);
|
|
1134
|
+
console.log(` ${t.tool('/agent [name]')} Switch or list agents`);
|
|
1135
|
+
console.log(` ${t.tool('/auto')} Auto mode (smart orchestration)`);
|
|
1136
|
+
console.log(` ${t.tool('/orchestrate')} Multi-agent orchestration mode`);
|
|
1137
|
+
console.log(` ${t.tool('/direct')} Direct agent mode`);
|
|
1138
|
+
console.log(` ${t.tool('/plan-mode')} Plan mode (read-only, no modifications)`);
|
|
1139
|
+
console.log(` ${t.tool('/permission [m]')} Cycle or set permission mode`);
|
|
1140
|
+
console.log(` ${t.tool('/resume [id]')} Resume a previous session`);
|
|
1141
|
+
console.log(` ${t.tool('/fork')} Fork current session`);
|
|
1142
|
+
console.log(` ${t.tool('/compact')} Compact conversation context`);
|
|
1143
|
+
console.log(` ${t.tool('/undo')} Undo last change`);
|
|
1144
|
+
console.log(` ${t.tool('/redo')} Redo undone change`);
|
|
1145
|
+
console.log(` ${t.tool('/rewind [n]')} Rewind n changes`);
|
|
1146
|
+
console.log(` ${t.tool('/mcp [cmd]')} Manage MCP servers`);
|
|
1147
|
+
console.log(` ${t.tool('/init')} Initialize NEURO.md`);
|
|
1148
|
+
console.log(` ${t.tool('/sandbox')} Toggle sandbox mode`);
|
|
1149
|
+
console.log(` ${t.tool('/plugins')} Manage plugins`);
|
|
1150
|
+
console.log(` ${t.tool('/whitelist')} Manage tool whitelist`);
|
|
1151
|
+
console.log(` ${t.tool('/blacklist')} Manage tool blacklist`);
|
|
1152
|
+
console.log(` ${t.tool('/doctor')} Health check`);
|
|
1153
|
+
console.log(` ${t.tool('/export [path]')} Export session as JSON`);
|
|
1154
|
+
console.log(` ${t.tool('/import <path>')} Import a session from JSON`);
|
|
1155
|
+
console.log(` ${t.tool('/cost')} Show spending and cache report`);
|
|
1156
|
+
console.log(` ${t.tool('/spending')} Show detailed spending report`);
|
|
1157
|
+
console.log(` ${t.tool('/stats')} Show session statistics`);
|
|
1158
|
+
console.log(` ${t.tool('/theme [name]')} Switch UI theme`);
|
|
1159
|
+
console.log();
|
|
1160
|
+
console.log(` ${t.bold('v3.0 New Commands:')}\n`);
|
|
1161
|
+
console.log(` ${t.tool('/style [name]')} Switch output style`);
|
|
1162
|
+
console.log(` ${t.tool('/thinking [mode]')} Toggle thinking mode (none|brief|full|ultrathink)`);
|
|
1163
|
+
console.log(` ${t.tool('/effort [level]')} Set effort level (low|medium|high|ultrathink)`);
|
|
1164
|
+
console.log(` ${t.tool('/skills [cmd]')} Manage skills (list|activate|deactivate|clear)`);
|
|
1165
|
+
console.log(` ${t.tool('/cache [cmd]')} Manage prompt cache (on|off|clear|stats)`);
|
|
1166
|
+
console.log(` ${t.tool('/ignore [cmd]')} Manage .neuroignore rules`);
|
|
1167
|
+
console.log(` ${t.tool('/ollama')} List Ollama local models`);
|
|
1168
|
+
console.log(` ${t.tool('/commit-push-pr')} Commit + push + create PR`);
|
|
1169
|
+
console.log(` ${t.tool('/code-review')} Multi-agent code review`);
|
|
1170
|
+
console.log(` ${t.tool('/update [cmd]')} Check/update NeuroCLI (now|check|dismiss|auto|interval|reset)`);
|
|
1171
|
+
console.log(` ${t.tool('/feedback')} Give feedback`);
|
|
1172
|
+
console.log(` ${t.tool('/clear')} Clear terminal`);
|
|
1173
|
+
console.log(` ${t.tool('/exit')} Exit NeuroCLI`);
|
|
1174
|
+
console.log();
|
|
1175
|
+
console.log(` ${t.muted('Tab: auto-complete commands, models, files')}`);
|
|
1176
|
+
console.log(` ${t.muted('Shift+Tab: cycle permission modes')}`);
|
|
1177
|
+
console.log();
|
|
1178
|
+
console.log(` ${t.muted('Examples:')}`);
|
|
1179
|
+
console.log(` ${t.muted(' "Create a REST API with Express"')}`);
|
|
1180
|
+
console.log(` ${t.muted(' "Fix the bug in auth.ts"')}`);
|
|
1181
|
+
console.log(` ${t.muted(' "Explain how this codebase works"')}`);
|
|
1182
|
+
console.log();
|
|
1183
|
+
}
|
|
1184
|
+
// Parse and execute
|
|
1185
|
+
program.parse(process.argv);
|
|
1186
|
+
//# sourceMappingURL=index.js.map
|