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
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Message, ToolCall, ToolDefinition, ToolResult } from '../core/types.js';
|
|
2
|
+
export interface StreamCallbacks {
|
|
3
|
+
onToken?: (token: string) => void;
|
|
4
|
+
onToolCall?: (toolCall: ToolCall) => void;
|
|
5
|
+
onToolResult?: (result: ToolResult) => void;
|
|
6
|
+
onThinking?: (thinking: string) => void;
|
|
7
|
+
onComplete?: (fullResponse: string, usage: TokenUsage) => void;
|
|
8
|
+
onError?: (error: Error) => void;
|
|
9
|
+
}
|
|
10
|
+
export interface TokenUsage {
|
|
11
|
+
inputTokens: number;
|
|
12
|
+
outputTokens: number;
|
|
13
|
+
cost: number;
|
|
14
|
+
}
|
|
15
|
+
export interface ChatRequest {
|
|
16
|
+
model: string;
|
|
17
|
+
messages: Message[];
|
|
18
|
+
tools?: ToolDefinition[];
|
|
19
|
+
temperature?: number;
|
|
20
|
+
maxTokens?: number;
|
|
21
|
+
stream?: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare class OpenRouterClient {
|
|
24
|
+
private apiKey;
|
|
25
|
+
private baseUrl;
|
|
26
|
+
private totalUsage;
|
|
27
|
+
constructor(apiKey: string, baseUrl?: string);
|
|
28
|
+
get usage(): TokenUsage;
|
|
29
|
+
resetUsage(): void;
|
|
30
|
+
/**
|
|
31
|
+
* Main chat completion with streaming
|
|
32
|
+
*/
|
|
33
|
+
chat(request: ChatRequest, callbacks?: StreamCallbacks): Promise<{
|
|
34
|
+
content: string;
|
|
35
|
+
toolCalls: ToolCall[];
|
|
36
|
+
usage: TokenUsage;
|
|
37
|
+
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Handle SSE streaming response
|
|
40
|
+
*/
|
|
41
|
+
private handleStreamingResponse;
|
|
42
|
+
/**
|
|
43
|
+
* Handle non-streaming response
|
|
44
|
+
*/
|
|
45
|
+
private handleNonStreamingResponse;
|
|
46
|
+
/**
|
|
47
|
+
* Quick completion without streaming (for sub-agents)
|
|
48
|
+
*/
|
|
49
|
+
quickChat(model: string, messages: Message[], tools?: ToolDefinition[], temperature?: number): Promise<{
|
|
50
|
+
content: string;
|
|
51
|
+
toolCalls: ToolCall[];
|
|
52
|
+
usage: TokenUsage;
|
|
53
|
+
}>;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=openrouter.d.ts.map
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - OpenRouter API Client
|
|
3
|
+
// Streaming + Tool Use + Multi-model support
|
|
4
|
+
// ============================================================
|
|
5
|
+
// @ts-ignore
|
|
6
|
+
import { createParser } from 'eventsource-parser';
|
|
7
|
+
import { MODELS, calculateCost } from './models.js';
|
|
8
|
+
export class OpenRouterClient {
|
|
9
|
+
apiKey;
|
|
10
|
+
baseUrl;
|
|
11
|
+
totalUsage = { inputTokens: 0, outputTokens: 0, cost: 0 };
|
|
12
|
+
constructor(apiKey, baseUrl = 'https://openrouter.ai/api/v1') {
|
|
13
|
+
this.apiKey = apiKey;
|
|
14
|
+
this.baseUrl = baseUrl;
|
|
15
|
+
}
|
|
16
|
+
get usage() {
|
|
17
|
+
return { ...this.totalUsage };
|
|
18
|
+
}
|
|
19
|
+
resetUsage() {
|
|
20
|
+
this.totalUsage = { inputTokens: 0, outputTokens: 0, cost: 0 };
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Main chat completion with streaming
|
|
24
|
+
*/
|
|
25
|
+
async chat(request, callbacks) {
|
|
26
|
+
const model = MODELS[request.model];
|
|
27
|
+
if (!model) {
|
|
28
|
+
throw new Error(`Unknown model: ${request.model}`);
|
|
29
|
+
}
|
|
30
|
+
const body = {
|
|
31
|
+
model: request.model,
|
|
32
|
+
messages: request.messages.map(m => ({
|
|
33
|
+
role: m.role,
|
|
34
|
+
content: m.content,
|
|
35
|
+
...(m.toolCalls ? { tool_calls: m.toolCalls } : {}),
|
|
36
|
+
...(m.toolCallId ? { tool_call_id: m.toolCallId } : {}),
|
|
37
|
+
...(m.name ? { name: m.name } : {}),
|
|
38
|
+
})),
|
|
39
|
+
stream: request.stream ?? true,
|
|
40
|
+
temperature: request.temperature ?? 0.7,
|
|
41
|
+
max_tokens: request.maxTokens ?? model.maxOutput,
|
|
42
|
+
};
|
|
43
|
+
if (request.tools && request.tools.length > 0 && model.supportsTools) {
|
|
44
|
+
body.tools = request.tools.map(t => ({
|
|
45
|
+
type: 'function',
|
|
46
|
+
function: {
|
|
47
|
+
name: t.name,
|
|
48
|
+
description: t.description,
|
|
49
|
+
parameters: t.parameters,
|
|
50
|
+
},
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
const maxRetries = 3;
|
|
54
|
+
let lastError = null;
|
|
55
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
56
|
+
if (attempt > 0) {
|
|
57
|
+
// Exponential backoff: 5s, 15s, 30s
|
|
58
|
+
const waitTime = Math.min(5000 * Math.pow(3, attempt - 1), 30000);
|
|
59
|
+
callbacks?.onThinking?.(`⏳ Rate limited, retrying in ${waitTime / 1000}s (attempt ${attempt + 1}/${maxRetries + 1})...`);
|
|
60
|
+
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
61
|
+
}
|
|
62
|
+
const response = await fetch(`${this.baseUrl}/chat/completions`, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: {
|
|
65
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
66
|
+
'Content-Type': 'application/json',
|
|
67
|
+
'HTTP-Referer': 'https://neurocli.dev',
|
|
68
|
+
'X-Title': 'NeuroCLI',
|
|
69
|
+
},
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
const errorBody = await response.text();
|
|
74
|
+
// Parse retry_after from 429 errors
|
|
75
|
+
if (response.status === 429) {
|
|
76
|
+
try {
|
|
77
|
+
const errorData = JSON.parse(errorBody);
|
|
78
|
+
const retryAfter = errorData?.error?.metadata?.retry_after_seconds;
|
|
79
|
+
if (retryAfter && attempt < maxRetries) {
|
|
80
|
+
lastError = new Error(`Rate limited, retry after ${retryAfter}s`);
|
|
81
|
+
continue; // Retry
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch { }
|
|
85
|
+
// If we can't parse retry_after or max retries reached, throw
|
|
86
|
+
if (attempt < maxRetries) {
|
|
87
|
+
lastError = new Error(`Rate limited (429): ${errorBody}`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
throw new Error(`OpenRouter API error (${response.status}): ${errorBody}`);
|
|
92
|
+
}
|
|
93
|
+
if (request.stream && callbacks) {
|
|
94
|
+
return this.handleStreamingResponse(response, request.model, callbacks);
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
return this.handleNonStreamingResponse(response, request.model);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
throw lastError || new Error('Max retries exceeded');
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Handle SSE streaming response
|
|
104
|
+
*/
|
|
105
|
+
async handleStreamingResponse(response, modelId, callbacks) {
|
|
106
|
+
const fullContent = [];
|
|
107
|
+
const toolCalls = new Map();
|
|
108
|
+
let inputTokens = 0;
|
|
109
|
+
let outputTokens = 0;
|
|
110
|
+
const parser = createParser((event) => {
|
|
111
|
+
if (event.type === 'event') {
|
|
112
|
+
if (event.data === '[DONE]')
|
|
113
|
+
return;
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(event.data);
|
|
116
|
+
// Extract usage if available
|
|
117
|
+
if (parsed.usage) {
|
|
118
|
+
inputTokens = parsed.usage.prompt_tokens || 0;
|
|
119
|
+
outputTokens = parsed.usage.completion_tokens || 0;
|
|
120
|
+
}
|
|
121
|
+
const delta = parsed.choices?.[0]?.delta;
|
|
122
|
+
if (!delta)
|
|
123
|
+
return;
|
|
124
|
+
// Handle thinking/reasoning tokens
|
|
125
|
+
if (delta.reasoning_content) {
|
|
126
|
+
callbacks.onThinking?.(delta.reasoning_content);
|
|
127
|
+
}
|
|
128
|
+
// Handle content tokens
|
|
129
|
+
if (delta.content) {
|
|
130
|
+
fullContent.push(delta.content);
|
|
131
|
+
callbacks.onToken?.(delta.content);
|
|
132
|
+
}
|
|
133
|
+
// Handle tool calls
|
|
134
|
+
if (delta.tool_calls) {
|
|
135
|
+
for (const tc of delta.tool_calls) {
|
|
136
|
+
const existing = toolCalls.get(tc.index);
|
|
137
|
+
if (existing) {
|
|
138
|
+
if (tc.function?.arguments) {
|
|
139
|
+
existing.function.arguments += tc.function.arguments;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
toolCalls.set(tc.index, {
|
|
144
|
+
id: tc.id || `tc_${tc.index}`,
|
|
145
|
+
type: 'function',
|
|
146
|
+
function: {
|
|
147
|
+
name: tc.function?.name || '',
|
|
148
|
+
arguments: tc.function?.arguments || '',
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Handle finish
|
|
155
|
+
if (parsed.choices?.[0]?.finish_reason === 'tool_calls') {
|
|
156
|
+
const toolCallList = Array.from(toolCalls.values());
|
|
157
|
+
for (const tc of toolCallList) {
|
|
158
|
+
callbacks.onToolCall?.(tc);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
// Skip malformed JSON chunks
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
const reader = response.body?.getReader();
|
|
168
|
+
if (!reader)
|
|
169
|
+
throw new Error('No response body');
|
|
170
|
+
const decoder = new TextDecoder();
|
|
171
|
+
try {
|
|
172
|
+
while (true) {
|
|
173
|
+
const { done, value } = await reader.read();
|
|
174
|
+
if (done)
|
|
175
|
+
break;
|
|
176
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
177
|
+
parser.feed(chunk);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
catch (error) {
|
|
181
|
+
callbacks.onError?.(error);
|
|
182
|
+
throw error;
|
|
183
|
+
}
|
|
184
|
+
const content = fullContent.join('');
|
|
185
|
+
const toolCallList = Array.from(toolCalls.values());
|
|
186
|
+
const cost = calculateCost(modelId, inputTokens, outputTokens);
|
|
187
|
+
const usage = { inputTokens, outputTokens, cost };
|
|
188
|
+
this.totalUsage.inputTokens += inputTokens;
|
|
189
|
+
this.totalUsage.outputTokens += outputTokens;
|
|
190
|
+
this.totalUsage.cost += cost;
|
|
191
|
+
callbacks.onComplete?.(content, usage);
|
|
192
|
+
return { content, toolCalls: toolCallList, usage };
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Handle non-streaming response
|
|
196
|
+
*/
|
|
197
|
+
async handleNonStreamingResponse(response, modelId) {
|
|
198
|
+
const data = await response.json();
|
|
199
|
+
const content = data.choices?.[0]?.message?.content || '';
|
|
200
|
+
const toolCalls = data.choices?.[0]?.message?.tool_calls || [];
|
|
201
|
+
const inputTokens = data.usage?.prompt_tokens || 0;
|
|
202
|
+
const outputTokens = data.usage?.completion_tokens || 0;
|
|
203
|
+
const cost = calculateCost(modelId, inputTokens, outputTokens);
|
|
204
|
+
const usage = { inputTokens, outputTokens, cost };
|
|
205
|
+
this.totalUsage.inputTokens += inputTokens;
|
|
206
|
+
this.totalUsage.outputTokens += outputTokens;
|
|
207
|
+
this.totalUsage.cost += cost;
|
|
208
|
+
return { content, toolCalls, usage };
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Quick completion without streaming (for sub-agents)
|
|
212
|
+
*/
|
|
213
|
+
async quickChat(model, messages, tools, temperature) {
|
|
214
|
+
return this.chat({
|
|
215
|
+
model,
|
|
216
|
+
messages,
|
|
217
|
+
tools,
|
|
218
|
+
temperature: temperature ?? 0.5,
|
|
219
|
+
stream: false,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
//# sourceMappingURL=openrouter.js.map
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export interface CustomCommand {
|
|
2
|
+
name: string;
|
|
3
|
+
description: string;
|
|
4
|
+
prompt: string;
|
|
5
|
+
args?: string[];
|
|
6
|
+
model?: string;
|
|
7
|
+
tools?: string[];
|
|
8
|
+
autoApprove?: boolean;
|
|
9
|
+
subagent?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare class CommandSystem {
|
|
12
|
+
private commands;
|
|
13
|
+
private workingDirectory;
|
|
14
|
+
constructor(workingDirectory: string);
|
|
15
|
+
/**
|
|
16
|
+
* Discover and load custom commands
|
|
17
|
+
*/
|
|
18
|
+
discover(): CustomCommand[];
|
|
19
|
+
/**
|
|
20
|
+
* Get a command by name
|
|
21
|
+
*/
|
|
22
|
+
get(name: string): CustomCommand | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Get all commands
|
|
25
|
+
*/
|
|
26
|
+
getAll(): CustomCommand[];
|
|
27
|
+
/**
|
|
28
|
+
* Parse a slash command input
|
|
29
|
+
*/
|
|
30
|
+
parse(input: string): {
|
|
31
|
+
command: CustomCommand;
|
|
32
|
+
args: string;
|
|
33
|
+
} | null;
|
|
34
|
+
/**
|
|
35
|
+
* Build the final prompt from a command template and args
|
|
36
|
+
*/
|
|
37
|
+
buildPrompt(command: CustomCommand, args: string): string;
|
|
38
|
+
private discoverFromDirectory;
|
|
39
|
+
private parseCommandFile;
|
|
40
|
+
private parseSimpleYaml;
|
|
41
|
+
private loadBundledCommands;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Custom Slash Commands
|
|
3
|
+
// (Like Gemini CLI + OpenCode custom commands)
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { readFileSync, existsSync, readdirSync } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
export class CommandSystem {
|
|
9
|
+
commands = new Map();
|
|
10
|
+
workingDirectory;
|
|
11
|
+
constructor(workingDirectory) {
|
|
12
|
+
this.workingDirectory = workingDirectory;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Discover and load custom commands
|
|
16
|
+
*/
|
|
17
|
+
discover() {
|
|
18
|
+
this.commands.clear();
|
|
19
|
+
// Global commands: ~/.neuro/commands/
|
|
20
|
+
this.discoverFromDirectory(join(homedir(), '.neuro', 'commands'));
|
|
21
|
+
// Project commands: .neuro/commands/
|
|
22
|
+
this.discoverFromDirectory(join(this.workingDirectory, '.neuro', 'commands'));
|
|
23
|
+
// Load bundled commands
|
|
24
|
+
this.loadBundledCommands();
|
|
25
|
+
return Array.from(this.commands.values());
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get a command by name
|
|
29
|
+
*/
|
|
30
|
+
get(name) {
|
|
31
|
+
return this.commands.get(name);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Get all commands
|
|
35
|
+
*/
|
|
36
|
+
getAll() {
|
|
37
|
+
return Array.from(this.commands.values());
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Parse a slash command input
|
|
41
|
+
*/
|
|
42
|
+
parse(input) {
|
|
43
|
+
if (!input.startsWith('/'))
|
|
44
|
+
return null;
|
|
45
|
+
const parts = input.slice(1).split(' ');
|
|
46
|
+
const cmdName = parts[0];
|
|
47
|
+
const args = parts.slice(1).join(' ');
|
|
48
|
+
const cmd = this.commands.get(cmdName);
|
|
49
|
+
if (!cmd)
|
|
50
|
+
return null;
|
|
51
|
+
return { command: cmd, args };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build the final prompt from a command template and args
|
|
55
|
+
*/
|
|
56
|
+
buildPrompt(command, args) {
|
|
57
|
+
let prompt = command.prompt;
|
|
58
|
+
// Replace {args} placeholder
|
|
59
|
+
prompt = prompt.replace(/\{args\}/g, args);
|
|
60
|
+
// Replace {cwd} placeholder
|
|
61
|
+
prompt = prompt.replace(/\{cwd\}/g, this.workingDirectory);
|
|
62
|
+
// Replace {date} placeholder
|
|
63
|
+
prompt = prompt.replace(/\{date\}/g, new Date().toISOString().split('T')[0]);
|
|
64
|
+
// If no args placeholder but args provided, append them
|
|
65
|
+
if (args && !command.prompt.includes('{args}')) {
|
|
66
|
+
prompt += `\n\nUser input: ${args}`;
|
|
67
|
+
}
|
|
68
|
+
return prompt;
|
|
69
|
+
}
|
|
70
|
+
// ---- Private ----
|
|
71
|
+
discoverFromDirectory(dir) {
|
|
72
|
+
if (!existsSync(dir))
|
|
73
|
+
return;
|
|
74
|
+
try {
|
|
75
|
+
for (const file of readdirSync(dir)) {
|
|
76
|
+
if (file.endsWith('.md')) {
|
|
77
|
+
const cmd = this.parseCommandFile(join(dir, file));
|
|
78
|
+
if (cmd)
|
|
79
|
+
this.commands.set(cmd.name, cmd);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch { }
|
|
84
|
+
}
|
|
85
|
+
parseCommandFile(filePath) {
|
|
86
|
+
try {
|
|
87
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
88
|
+
const name = filePath.split('/').pop()?.replace('.md', '') || 'unknown';
|
|
89
|
+
// Parse frontmatter
|
|
90
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
91
|
+
if (!fmMatch) {
|
|
92
|
+
return {
|
|
93
|
+
name,
|
|
94
|
+
description: '',
|
|
95
|
+
prompt: content,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
const frontmatter = this.parseSimpleYaml(fmMatch[1]);
|
|
99
|
+
return {
|
|
100
|
+
name: frontmatter.name || name,
|
|
101
|
+
description: frontmatter.description || '',
|
|
102
|
+
prompt: fmMatch[2],
|
|
103
|
+
model: frontmatter.model,
|
|
104
|
+
tools: frontmatter.tools,
|
|
105
|
+
autoApprove: frontmatter.auto_approve,
|
|
106
|
+
subagent: frontmatter.subagent,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
parseSimpleYaml(yaml) {
|
|
114
|
+
const result = {};
|
|
115
|
+
for (const line of yaml.split('\n')) {
|
|
116
|
+
const match = line.match(/^(\w+):\s*(.*)$/);
|
|
117
|
+
if (match) {
|
|
118
|
+
result[match[1]] = match[2].trim().replace(/^["']|["']$/g, '');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return result;
|
|
122
|
+
}
|
|
123
|
+
loadBundledCommands() {
|
|
124
|
+
const bundled = [
|
|
125
|
+
{
|
|
126
|
+
name: 'init',
|
|
127
|
+
description: 'Initialize NEURO.md project context',
|
|
128
|
+
prompt: 'Analyze the current project and create a NEURO.md file with tech stack, conventions, and project structure. Use the project_context tool first.',
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
name: 'memory',
|
|
132
|
+
description: 'Show loaded context and saved memories',
|
|
133
|
+
prompt: 'Show me all loaded context (NEURO.md files) and saved memories. Use recall_memory to check.',
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: 'compact',
|
|
137
|
+
description: 'Compress conversation context',
|
|
138
|
+
prompt: 'Compress the current conversation context. Summarize key information and remove redundancy.',
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: 'review',
|
|
142
|
+
description: 'Code review workflow',
|
|
143
|
+
prompt: 'Perform a thorough code review of the recent changes. Check for bugs, security issues, performance problems, and style issues.',
|
|
144
|
+
tools: ['read_file', 'search_files', 'run_command'],
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
name: 'debug',
|
|
148
|
+
description: 'Systematic debugging workflow',
|
|
149
|
+
prompt: 'I need to debug an issue. Help me systematically investigate and fix it. Start by understanding the error, then form hypotheses, investigate, and fix.',
|
|
150
|
+
tools: ['read_file', 'search_files', 'run_command', 'edit_file'],
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
name: 'verify',
|
|
154
|
+
description: 'Build and verify changes',
|
|
155
|
+
prompt: 'Verify the recent code changes by building the project and running tests. Report any issues found.',
|
|
156
|
+
tools: ['read_file', 'run_command', 'search_files'],
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
name: 'test',
|
|
160
|
+
description: 'Generate and run tests',
|
|
161
|
+
prompt: 'Generate comprehensive tests for the current codebase. Focus on unit tests and edge cases. Run the tests and fix any failures.',
|
|
162
|
+
tools: ['read_file', 'write_file', 'run_command', 'search_files'],
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'refactor',
|
|
166
|
+
description: 'Refactor code with safety',
|
|
167
|
+
prompt: 'Refactor the specified code. Make changes incrementally, run tests after each change, and ensure nothing breaks. {args}',
|
|
168
|
+
tools: ['read_file', 'edit_file', 'run_command', 'search_files'],
|
|
169
|
+
autoApprove: false,
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: 'explain',
|
|
173
|
+
description: 'Explain code in detail',
|
|
174
|
+
prompt: 'Explain the following code in detail. Cover the architecture, data flow, key algorithms, and any non-obvious patterns. {args}',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
name: 'security',
|
|
178
|
+
description: 'Security audit',
|
|
179
|
+
prompt: 'Perform a security audit of the codebase. Check for common vulnerabilities: SQL injection, XSS, CSRF, insecure deserialization, hardcoded secrets, improper error handling, and missing authentication checks.',
|
|
180
|
+
tools: ['read_file', 'search_files', 'run_command'],
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
name: 'perf',
|
|
184
|
+
description: 'Performance analysis',
|
|
185
|
+
prompt: 'Analyze the codebase for performance issues. Look for: N+1 queries, memory leaks, inefficient algorithms, unnecessary re-renders, large bundle sizes, and slow database queries.',
|
|
186
|
+
tools: ['read_file', 'search_files', 'run_command'],
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'migrate',
|
|
190
|
+
description: 'Migrate code or dependencies',
|
|
191
|
+
prompt: 'Help me migrate the following. Make changes carefully, update all references, run tests to verify, and document any breaking changes. {args}',
|
|
192
|
+
tools: ['read_file', 'edit_file', 'write_file', 'run_command', 'search_files'],
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
name: 'doctor',
|
|
196
|
+
description: 'Diagnose environment issues',
|
|
197
|
+
prompt: 'Run a diagnostic check of the development environment. Check: required tools, dependencies, configuration, environment variables, and build system. Report status for each and suggest fixes.',
|
|
198
|
+
tools: ['run_command', 'read_file', 'search_files'],
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
name: 'stats',
|
|
202
|
+
description: 'Show session statistics',
|
|
203
|
+
prompt: 'Show current session statistics including: tokens used, cost, number of messages, tools called, and duration.',
|
|
204
|
+
},
|
|
205
|
+
// --- v4.0 New Commands ---
|
|
206
|
+
{
|
|
207
|
+
name: 'auto',
|
|
208
|
+
description: 'Toggle autonomous mode (skip approvals)',
|
|
209
|
+
prompt: 'Toggle auto mode. When enabled, I will execute without asking for approval on each step. Use safety checks instead. {args}',
|
|
210
|
+
autoApprove: true,
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
name: 'goal',
|
|
214
|
+
description: 'Set a high-level goal for autonomous execution',
|
|
215
|
+
prompt: 'Set a goal for me to work towards autonomously. I will break it down into steps and execute them. {args}',
|
|
216
|
+
autoApprove: false,
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'routine',
|
|
220
|
+
description: 'Save and replay command sequences',
|
|
221
|
+
prompt: 'Manage routines. List, create, or execute saved command sequences. {args}',
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
name: 'loop',
|
|
225
|
+
description: 'Schedule a recurring task',
|
|
226
|
+
prompt: 'Schedule a recurring task. Usage: /loop <interval> <prompt> where interval is like 5m, 1h, 1d. {args}',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
name: 'bg',
|
|
230
|
+
description: 'Run a task in the background',
|
|
231
|
+
prompt: 'Start this task in the background. I will work on it independently and you can check status later. {args}',
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: 'skills',
|
|
235
|
+
description: 'Manage SKILL.md skills (install/search/list)',
|
|
236
|
+
prompt: 'Manage skills. Use: /skills list, /skills install <name>, /skills search <query>, /skills activate <name>. {args}',
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
name: 'parallel',
|
|
240
|
+
description: 'Spawn parallel agents for concurrent work',
|
|
241
|
+
prompt: 'Spawn parallel agents to work on multiple tasks concurrently. {args}',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
name: 'repomap',
|
|
245
|
+
description: 'Build and show repository map (tree-sitter)',
|
|
246
|
+
prompt: 'Analyze the repository structure and build a map of all symbols, classes, functions, and their relationships. Show the repo map. {args}',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
name: 'lint',
|
|
250
|
+
description: 'Run linters on the project',
|
|
251
|
+
prompt: 'Run configured linters on the project. Auto-detect ESLint, Prettier, Ruff, etc. {args}',
|
|
252
|
+
tools: ['run_command', 'read_file'],
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
name: 'test',
|
|
256
|
+
description: 'Run tests with auto-detection',
|
|
257
|
+
prompt: 'Detect and run the test framework. Support Jest, Vitest, pytest, Go test, cargo test. {args}',
|
|
258
|
+
tools: ['run_command', 'read_file'],
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
name: 'review',
|
|
262
|
+
description: 'Code review with security/performance checks',
|
|
263
|
+
prompt: 'Perform a thorough code review checking for security vulnerabilities, performance issues, code style, and correctness. {args}',
|
|
264
|
+
tools: ['read_file', 'search_files', 'run_command'],
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'scan',
|
|
268
|
+
description: 'Security vulnerability scan',
|
|
269
|
+
prompt: 'Scan the codebase for security vulnerabilities: hardcoded secrets, injection risks, weak crypto, and more. {args}',
|
|
270
|
+
tools: ['read_file', 'search_files'],
|
|
271
|
+
},
|
|
272
|
+
{
|
|
273
|
+
name: 'bundle',
|
|
274
|
+
description: 'Manage plugin bundles (install/create/publish)',
|
|
275
|
+
prompt: 'Manage plugin bundles. Use: /bundle install <source>, /bundle list, /bundle create <name>, /bundle publish. {args}',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
name: 'pr',
|
|
279
|
+
description: 'GitHub PR operations',
|
|
280
|
+
prompt: 'Create or manage GitHub pull requests. {args}',
|
|
281
|
+
tools: ['run_command', 'read_file'],
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
name: 'issue',
|
|
285
|
+
description: 'GitHub issue operations',
|
|
286
|
+
prompt: 'Create or manage GitHub issues. {args}',
|
|
287
|
+
tools: ['run_command'],
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: 'ci',
|
|
291
|
+
description: 'CI/CD pipeline operations',
|
|
292
|
+
prompt: 'Run, monitor, or manage CI/CD pipelines. {args}',
|
|
293
|
+
tools: ['run_command'],
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
name: 'browser',
|
|
297
|
+
description: 'Browser automation (navigate/screenshot/click)',
|
|
298
|
+
prompt: 'Use the browser to navigate, take screenshots, click elements, or extract content from web pages. {args}',
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
for (const cmd of bundled) {
|
|
302
|
+
if (!this.commands.has(cmd.name)) {
|
|
303
|
+
this.commands.set(cmd.name, cmd);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
//# sourceMappingURL=commands.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NeuroConfig } from '../core/types.js';
|
|
2
|
+
export declare const DEFAULT_CONFIG: NeuroConfig;
|
|
3
|
+
export declare function loadConfig(): NeuroConfig;
|
|
4
|
+
export declare function saveConfig(config: NeuroConfig): void;
|
|
5
|
+
export declare function getProjectConfigPath(): string;
|
|
6
|
+
export declare function loadProjectConfig(): Partial<NeuroConfig> | null;
|
|
7
|
+
export declare function initConfig(apiKey?: string): NeuroConfig;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|