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,311 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Configuration System
|
|
3
|
+
// ============================================================
|
|
4
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { join } from 'path';
|
|
6
|
+
import { homedir } from 'os';
|
|
7
|
+
const CONFIG_DIR = join(homedir(), '.neuro');
|
|
8
|
+
const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
|
|
9
|
+
export const DEFAULT_CONFIG = {
|
|
10
|
+
apiKey: '',
|
|
11
|
+
baseUrl: 'https://openrouter.ai/api/v1',
|
|
12
|
+
defaultModel: 'qwen/qwen3-coder:free',
|
|
13
|
+
agents: {
|
|
14
|
+
planner: {
|
|
15
|
+
name: 'Planner',
|
|
16
|
+
description: 'Task decomposition and planning specialist',
|
|
17
|
+
systemPrompt: `You are an expert software architect and planner. Your job is to break down complex tasks into clear, actionable steps. For each step, specify which agent should handle it and what tools they should use. Be thorough but concise.`,
|
|
18
|
+
model: 'qwen/qwen3-coder:free',
|
|
19
|
+
temperature: 0.7,
|
|
20
|
+
maxTokens: 4096,
|
|
21
|
+
tools: ['read_file', 'search_files', 'list_directory'],
|
|
22
|
+
maxIterations: 3,
|
|
23
|
+
},
|
|
24
|
+
coder: {
|
|
25
|
+
name: 'Coder',
|
|
26
|
+
description: 'Code generation and modification specialist',
|
|
27
|
+
systemPrompt: `You are an expert software developer. You write clean, efficient, well-documented code. You understand design patterns, best practices, and can work with any programming language or framework. When modifying existing code, you make minimal, targeted changes. Always consider edge cases and error handling.`,
|
|
28
|
+
model: 'qwen/qwen3-coder:free',
|
|
29
|
+
temperature: 0.4,
|
|
30
|
+
maxTokens: 16384,
|
|
31
|
+
tools: ['read_file', 'write_file', 'edit_file', 'search_files', 'list_directory', 'run_command', 'apply_diff'],
|
|
32
|
+
maxIterations: 15,
|
|
33
|
+
},
|
|
34
|
+
reviewer: {
|
|
35
|
+
name: 'Reviewer',
|
|
36
|
+
description: 'Code review and quality assurance specialist',
|
|
37
|
+
systemPrompt: `You are an expert code reviewer. You analyze code for bugs, security vulnerabilities, performance issues, and adherence to best practices. You provide specific, actionable feedback with line references. You categorize issues by severity (critical, warning, suggestion). You also check for test coverage and documentation.`,
|
|
38
|
+
model: 'nvidia/nemotron-3-super-120b-a12b:free',
|
|
39
|
+
temperature: 0.3,
|
|
40
|
+
maxTokens: 8192,
|
|
41
|
+
tools: ['read_file', 'search_files', 'list_directory'],
|
|
42
|
+
maxIterations: 5,
|
|
43
|
+
},
|
|
44
|
+
researcher: {
|
|
45
|
+
name: 'Researcher',
|
|
46
|
+
description: 'Information gathering and analysis specialist',
|
|
47
|
+
systemPrompt: `You are an expert researcher and analyst. You gather information from files, web resources, and codebases. You synthesize findings into clear, structured reports. You can explain complex technical concepts and find relevant examples or documentation.`,
|
|
48
|
+
model: 'google/gemma-4-31b-it:free',
|
|
49
|
+
temperature: 0.5,
|
|
50
|
+
maxTokens: 8192,
|
|
51
|
+
tools: ['read_file', 'search_files', 'list_directory', 'web_search', 'web_fetch'],
|
|
52
|
+
maxIterations: 8,
|
|
53
|
+
},
|
|
54
|
+
tester: {
|
|
55
|
+
name: 'Tester',
|
|
56
|
+
description: 'Test generation and execution specialist',
|
|
57
|
+
systemPrompt: `You are an expert QA engineer. You write comprehensive test suites covering unit tests, integration tests, and edge cases. You run tests and analyze failures to identify root causes. You ensure code meets quality standards before it's considered complete.`,
|
|
58
|
+
model: 'qwen/qwen3-coder:free',
|
|
59
|
+
temperature: 0.3,
|
|
60
|
+
maxTokens: 8192,
|
|
61
|
+
tools: ['read_file', 'write_file', 'search_files', 'run_command', 'list_directory'],
|
|
62
|
+
maxIterations: 10,
|
|
63
|
+
},
|
|
64
|
+
debugger: {
|
|
65
|
+
name: 'Debugger',
|
|
66
|
+
description: 'Bug investigation and fixing specialist',
|
|
67
|
+
systemPrompt: `You are an expert debugger. You systematically investigate bugs by reading code, running diagnostic commands, analyzing error messages and stack traces. You identify root causes and implement targeted fixes. You verify fixes work before reporting completion.`,
|
|
68
|
+
model: 'qwen/qwen3-coder:free',
|
|
69
|
+
temperature: 0.2,
|
|
70
|
+
maxTokens: 8192,
|
|
71
|
+
tools: ['read_file', 'edit_file', 'search_files', 'run_command', 'list_directory', 'apply_diff'],
|
|
72
|
+
maxIterations: 12,
|
|
73
|
+
},
|
|
74
|
+
architect: {
|
|
75
|
+
name: 'Architect',
|
|
76
|
+
description: 'System design and architecture specialist',
|
|
77
|
+
systemPrompt: `You are an expert software architect. You design scalable, maintainable system architectures. You make technology stack decisions, define component boundaries, and establish coding standards. You create detailed technical specifications that other agents can implement.`,
|
|
78
|
+
model: 'nvidia/nemotron-3-ultra-550b-a55b:free',
|
|
79
|
+
temperature: 0.7,
|
|
80
|
+
maxTokens: 8192,
|
|
81
|
+
tools: ['read_file', 'search_files', 'list_directory'],
|
|
82
|
+
maxIterations: 5,
|
|
83
|
+
},
|
|
84
|
+
devops: {
|
|
85
|
+
name: 'DevOps',
|
|
86
|
+
description: 'Deployment and infrastructure specialist',
|
|
87
|
+
systemPrompt: `You are an expert DevOps engineer. You handle CI/CD pipelines, Docker configurations, cloud deployments, and infrastructure as code. You optimize build processes and ensure reliable deployments.`,
|
|
88
|
+
model: 'cohere/north-mini-code:free',
|
|
89
|
+
temperature: 0.3,
|
|
90
|
+
maxTokens: 8192,
|
|
91
|
+
tools: ['read_file', 'write_file', 'edit_file', 'run_command', 'search_files', 'list_directory'],
|
|
92
|
+
maxIterations: 10,
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
tools: {
|
|
96
|
+
autoApprove: ['read_file', 'search_files', 'list_directory', 'web_search'],
|
|
97
|
+
requireApproval: ['write_file', 'edit_file', 'apply_diff', 'run_command', 'delete_file'],
|
|
98
|
+
denied: [],
|
|
99
|
+
},
|
|
100
|
+
context: {
|
|
101
|
+
maxTokens: 180000,
|
|
102
|
+
systemPromptRatio: 0.15,
|
|
103
|
+
},
|
|
104
|
+
session: {
|
|
105
|
+
autoSave: true,
|
|
106
|
+
maxHistory: 100,
|
|
107
|
+
},
|
|
108
|
+
ui: {
|
|
109
|
+
theme: 'dracula',
|
|
110
|
+
showTokenCount: true,
|
|
111
|
+
showCost: true,
|
|
112
|
+
streaming: true,
|
|
113
|
+
syntaxHighlight: true,
|
|
114
|
+
},
|
|
115
|
+
permissionMode: 'auto',
|
|
116
|
+
fallbackChain: {
|
|
117
|
+
models: ['qwen/qwen3-coder:free', 'nvidia/nemotron-3-super-120b-a12b:free', 'google/gemma-4-31b-it:free', 'cohere/north-mini-code:free'],
|
|
118
|
+
maxRetries: 2,
|
|
119
|
+
retryDelayMs: 3000,
|
|
120
|
+
},
|
|
121
|
+
doomLoop: {
|
|
122
|
+
maxConsecutiveErrors: 3,
|
|
123
|
+
maxRepetitiveActions: 3,
|
|
124
|
+
similarityThreshold: 0.7,
|
|
125
|
+
autoBreak: true,
|
|
126
|
+
},
|
|
127
|
+
mcp: {
|
|
128
|
+
servers: {},
|
|
129
|
+
autoConnect: true,
|
|
130
|
+
},
|
|
131
|
+
diffPreview: true,
|
|
132
|
+
sandbox: {
|
|
133
|
+
enabled: false,
|
|
134
|
+
rootDir: process.cwd(),
|
|
135
|
+
allowedDirs: [],
|
|
136
|
+
deniedDirs: ['node_modules', '.git', '__pycache__', '.env'],
|
|
137
|
+
deniedPatterns: ['**/.env', '**/.env.*', '**/*.pem', '**/*.key', '**/credentials.json'],
|
|
138
|
+
allowCommands: true,
|
|
139
|
+
allowedCommands: [],
|
|
140
|
+
deniedCommands: ['rm -rf /', 'sudo rm', 'mkfs', 'dd if='],
|
|
141
|
+
backupOnModify: true,
|
|
142
|
+
backupDir: '.neuro/backups',
|
|
143
|
+
maxFileSize: 10 * 1024 * 1024,
|
|
144
|
+
allowNetwork: true,
|
|
145
|
+
allowEnvAccess: false,
|
|
146
|
+
readOnly: false,
|
|
147
|
+
},
|
|
148
|
+
spendingLimit: 0,
|
|
149
|
+
promptCache: {
|
|
150
|
+
enabled: false,
|
|
151
|
+
cacheDir: join(homedir(), '.neuro', 'cache'),
|
|
152
|
+
maxEntries: 100,
|
|
153
|
+
ttlMs: 3600000,
|
|
154
|
+
similarityThreshold: 0.9,
|
|
155
|
+
},
|
|
156
|
+
customAgents: {},
|
|
157
|
+
// P2/P3 new config sections
|
|
158
|
+
telemetry: {
|
|
159
|
+
enabled: false,
|
|
160
|
+
trackModelPerformance: true,
|
|
161
|
+
trackToolUsage: true,
|
|
162
|
+
trackSessionMetrics: true,
|
|
163
|
+
retentionDays: 90,
|
|
164
|
+
},
|
|
165
|
+
vimMode: {
|
|
166
|
+
enabled: false,
|
|
167
|
+
showModeIndicator: true,
|
|
168
|
+
bellOnError: true,
|
|
169
|
+
},
|
|
170
|
+
i18n: {
|
|
171
|
+
locale: 'en',
|
|
172
|
+
fallbackLocale: 'en',
|
|
173
|
+
autoDetect: true,
|
|
174
|
+
},
|
|
175
|
+
multimodal: {
|
|
176
|
+
enabled: true,
|
|
177
|
+
maxImageSize: 20 * 1024 * 1024,
|
|
178
|
+
autoDetectImages: true,
|
|
179
|
+
},
|
|
180
|
+
voice: {
|
|
181
|
+
enabled: false,
|
|
182
|
+
ttsEngine: 'auto',
|
|
183
|
+
sttEngine: 'auto',
|
|
184
|
+
autoSpeak: false,
|
|
185
|
+
language: 'en',
|
|
186
|
+
},
|
|
187
|
+
apiServer: {
|
|
188
|
+
enabled: false,
|
|
189
|
+
host: '127.0.0.1',
|
|
190
|
+
port: 3141,
|
|
191
|
+
requireAuth: true,
|
|
192
|
+
enableWebSocket: true,
|
|
193
|
+
},
|
|
194
|
+
cloudSync: {
|
|
195
|
+
enabled: false,
|
|
196
|
+
backend: 'gist',
|
|
197
|
+
autoSync: false,
|
|
198
|
+
includeContent: true,
|
|
199
|
+
},
|
|
200
|
+
dashboard: {
|
|
201
|
+
enabled: false,
|
|
202
|
+
host: '127.0.0.1',
|
|
203
|
+
port: 3142,
|
|
204
|
+
autoOpen: true,
|
|
205
|
+
refreshInterval: 5000,
|
|
206
|
+
},
|
|
207
|
+
// v4.0 new configs
|
|
208
|
+
autoMode: {
|
|
209
|
+
enabled: false,
|
|
210
|
+
safetyLevel: 'conservative',
|
|
211
|
+
maxIterations: 50,
|
|
212
|
+
maxCost: 0,
|
|
213
|
+
maxTimeMs: 0,
|
|
214
|
+
blockedCommands: ['rm -rf /', 'mkfs', 'dd if=/dev/zero'],
|
|
215
|
+
blockedPatterns: ['/etc/passwd', '/etc/shadow'],
|
|
216
|
+
autoCommit: false,
|
|
217
|
+
autoTest: false,
|
|
218
|
+
pauseOnError: true,
|
|
219
|
+
},
|
|
220
|
+
scheduledTasks: {
|
|
221
|
+
enabled: true,
|
|
222
|
+
maxConcurrent: 5,
|
|
223
|
+
defaultIntervalUnit: 'hours',
|
|
224
|
+
persistTasks: true,
|
|
225
|
+
tasksDir: '',
|
|
226
|
+
},
|
|
227
|
+
linting: {
|
|
228
|
+
enabled: true,
|
|
229
|
+
autoRunOnChange: false,
|
|
230
|
+
autoFix: false,
|
|
231
|
+
failOnError: false,
|
|
232
|
+
timeout: 30000,
|
|
233
|
+
excludePatterns: ['node_modules', '.git', 'dist'],
|
|
234
|
+
},
|
|
235
|
+
testing: {
|
|
236
|
+
enabled: true,
|
|
237
|
+
autoRunOnChange: false,
|
|
238
|
+
runOnSave: false,
|
|
239
|
+
coverageThreshold: 80,
|
|
240
|
+
timeout: 60000,
|
|
241
|
+
relatedTestsOnly: false,
|
|
242
|
+
},
|
|
243
|
+
codeReview: {
|
|
244
|
+
enabled: true,
|
|
245
|
+
autoReviewOnChange: false,
|
|
246
|
+
focusAreas: ['security', 'performance', 'correctness', 'style'],
|
|
247
|
+
severityThreshold: 'minor',
|
|
248
|
+
excludePatterns: ['node_modules', '.git', 'dist'],
|
|
249
|
+
},
|
|
250
|
+
securityScanner: {
|
|
251
|
+
enabled: true,
|
|
252
|
+
autoScanOnChange: false,
|
|
253
|
+
failOnSeverity: 'high',
|
|
254
|
+
excludePatterns: ['node_modules', '.git', 'dist', 'coverage'],
|
|
255
|
+
customRules: [],
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
export function loadConfig() {
|
|
259
|
+
if (existsSync(CONFIG_FILE)) {
|
|
260
|
+
try {
|
|
261
|
+
const data = readFileSync(CONFIG_FILE, 'utf-8');
|
|
262
|
+
const saved = JSON.parse(data);
|
|
263
|
+
return { ...DEFAULT_CONFIG, ...saved };
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return { ...DEFAULT_CONFIG };
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return { ...DEFAULT_CONFIG };
|
|
270
|
+
}
|
|
271
|
+
export function saveConfig(config) {
|
|
272
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
273
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
274
|
+
}
|
|
275
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
276
|
+
}
|
|
277
|
+
export function getProjectConfigPath() {
|
|
278
|
+
return join(process.cwd(), '.neuro.json');
|
|
279
|
+
}
|
|
280
|
+
export function loadProjectConfig() {
|
|
281
|
+
const projectConfigPath = getProjectConfigPath();
|
|
282
|
+
if (existsSync(projectConfigPath)) {
|
|
283
|
+
try {
|
|
284
|
+
const data = readFileSync(projectConfigPath, 'utf-8');
|
|
285
|
+
return JSON.parse(data);
|
|
286
|
+
}
|
|
287
|
+
catch {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
export function initConfig(apiKey) {
|
|
294
|
+
const config = loadConfig();
|
|
295
|
+
if (apiKey) {
|
|
296
|
+
config.apiKey = apiKey;
|
|
297
|
+
}
|
|
298
|
+
if (!config.apiKey) {
|
|
299
|
+
const envKey = process.env.OPENROUTER_API_KEY;
|
|
300
|
+
if (envKey) {
|
|
301
|
+
config.apiKey = envKey;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// Merge project-level config
|
|
305
|
+
const projectConfig = loadProjectConfig();
|
|
306
|
+
if (projectConfig) {
|
|
307
|
+
Object.assign(config, projectConfig);
|
|
308
|
+
}
|
|
309
|
+
return config;
|
|
310
|
+
}
|
|
311
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Message } from '../core/types.js';
|
|
2
|
+
import { ContextManager } from '../core/context.js';
|
|
3
|
+
import { OpenRouterClient } from '../api/openrouter.js';
|
|
4
|
+
export type CompactionLayer = 'tool_budget' | 'snip' | 'micro' | 'session_memory' | 'full_collapse';
|
|
5
|
+
export interface CompactionResult {
|
|
6
|
+
messages: Message[];
|
|
7
|
+
layer: CompactionLayer;
|
|
8
|
+
originalTokens: number;
|
|
9
|
+
compactedTokens: number;
|
|
10
|
+
savings: number;
|
|
11
|
+
memories?: string[];
|
|
12
|
+
}
|
|
13
|
+
export declare class ContextCompactor {
|
|
14
|
+
private contextManager;
|
|
15
|
+
private client;
|
|
16
|
+
private model;
|
|
17
|
+
private maxToolOutputTokens;
|
|
18
|
+
private compactionThreshold;
|
|
19
|
+
constructor(contextManager: ContextManager, client: OpenRouterClient, model: string);
|
|
20
|
+
/**
|
|
21
|
+
* Check if compaction is needed
|
|
22
|
+
*/
|
|
23
|
+
needsCompaction(messages: Message[]): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Apply 5-layer compaction strategy
|
|
26
|
+
*/
|
|
27
|
+
compact(messages: Message[]): Promise<CompactionResult>;
|
|
28
|
+
/**
|
|
29
|
+
* Layer 1: Tool Budget
|
|
30
|
+
* Limit the size of tool outputs to prevent context bloat
|
|
31
|
+
*/
|
|
32
|
+
private layerToolBudget;
|
|
33
|
+
/**
|
|
34
|
+
* Layer 2: Snip
|
|
35
|
+
* Remove old conversation turns, keeping system + recent messages
|
|
36
|
+
*/
|
|
37
|
+
private layerSnip;
|
|
38
|
+
/**
|
|
39
|
+
* Layer 3: Micro-compaction
|
|
40
|
+
* Compress each message to its essential content
|
|
41
|
+
*/
|
|
42
|
+
private layerMicroCompact;
|
|
43
|
+
/**
|
|
44
|
+
* Layer 4: Session Memory Compaction
|
|
45
|
+
* Extract key memories from conversation, replace with summary
|
|
46
|
+
*/
|
|
47
|
+
private layerSessionMemory;
|
|
48
|
+
/**
|
|
49
|
+
* Layer 5: Full Collapse
|
|
50
|
+
* Nuclear option - completely summarize the conversation
|
|
51
|
+
*/
|
|
52
|
+
private layerFullCollapse;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=compaction.d.ts.map
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - 5-Layer Context Compaction
|
|
3
|
+
// (Like Claude Code's sophisticated context management)
|
|
4
|
+
// ============================================================
|
|
5
|
+
export class ContextCompactor {
|
|
6
|
+
contextManager;
|
|
7
|
+
client;
|
|
8
|
+
model;
|
|
9
|
+
maxToolOutputTokens = 5000;
|
|
10
|
+
compactionThreshold = 0.85; // 85% of context window
|
|
11
|
+
constructor(contextManager, client, model) {
|
|
12
|
+
this.contextManager = contextManager;
|
|
13
|
+
this.client = client;
|
|
14
|
+
this.model = model;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Check if compaction is needed
|
|
18
|
+
*/
|
|
19
|
+
needsCompaction(messages) {
|
|
20
|
+
const summary = this.contextManager.analyze(messages);
|
|
21
|
+
const ratio = summary.totalTokens / this.contextManager.maxInputTokens;
|
|
22
|
+
return ratio >= this.compactionThreshold;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Apply 5-layer compaction strategy
|
|
26
|
+
*/
|
|
27
|
+
async compact(messages) {
|
|
28
|
+
const originalTokens = this.contextManager.countTokens(messages);
|
|
29
|
+
// Layer 1: Tool Budget - Limit tool output sizes
|
|
30
|
+
let result = this.layerToolBudget(messages);
|
|
31
|
+
if (this.contextManager.countTokens(result.messages) < this.contextManager.maxInputTokens * 0.9) {
|
|
32
|
+
return { ...result, originalTokens };
|
|
33
|
+
}
|
|
34
|
+
// Layer 2: Snip - Remove old conversation turns
|
|
35
|
+
result = this.layerSnip(result.messages);
|
|
36
|
+
if (this.contextManager.countTokens(result.messages) < this.contextManager.maxInputTokens * 0.9) {
|
|
37
|
+
return { ...result, originalTokens };
|
|
38
|
+
}
|
|
39
|
+
// Layer 3: Micro-compaction - Compress each turn
|
|
40
|
+
result = this.layerMicroCompact(result.messages);
|
|
41
|
+
if (this.contextManager.countTokens(result.messages) < this.contextManager.maxInputTokens * 0.9) {
|
|
42
|
+
return { ...result, originalTokens };
|
|
43
|
+
}
|
|
44
|
+
// Layer 4: Session Memory - Extract key memories
|
|
45
|
+
result = await this.layerSessionMemory(result.messages);
|
|
46
|
+
if (this.contextManager.countTokens(result.messages) < this.contextManager.maxInputTokens * 0.9) {
|
|
47
|
+
return { ...result, originalTokens };
|
|
48
|
+
}
|
|
49
|
+
// Layer 5: Full Collapse - Nuclear option
|
|
50
|
+
result = await this.layerFullCollapse(result.messages);
|
|
51
|
+
return { ...result, originalTokens };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Layer 1: Tool Budget
|
|
55
|
+
* Limit the size of tool outputs to prevent context bloat
|
|
56
|
+
*/
|
|
57
|
+
layerToolBudget(messages) {
|
|
58
|
+
const maxOutput = this.maxToolOutputTokens * 4; // ~4 chars per token
|
|
59
|
+
const compacted = messages.map(msg => {
|
|
60
|
+
if (msg.role === 'tool' && msg.content.length > maxOutput) {
|
|
61
|
+
const truncated = msg.content.slice(0, maxOutput) +
|
|
62
|
+
`\n\n... [output truncated, ${msg.content.length - maxOutput} chars removed]`;
|
|
63
|
+
return { ...msg, content: truncated };
|
|
64
|
+
}
|
|
65
|
+
return msg;
|
|
66
|
+
});
|
|
67
|
+
const compactedTokens = this.contextManager.countTokens(compacted);
|
|
68
|
+
return {
|
|
69
|
+
messages: compacted,
|
|
70
|
+
layer: 'tool_budget',
|
|
71
|
+
originalTokens: compactedTokens,
|
|
72
|
+
compactedTokens,
|
|
73
|
+
savings: 0,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Layer 2: Snip
|
|
78
|
+
* Remove old conversation turns, keeping system + recent messages
|
|
79
|
+
*/
|
|
80
|
+
layerSnip(messages) {
|
|
81
|
+
const systemMessages = messages.filter(m => m.role === 'system');
|
|
82
|
+
const nonSystemMessages = messages.filter(m => m.role !== 'system');
|
|
83
|
+
// Keep last N messages (roughly last 20 turns)
|
|
84
|
+
const keepCount = Math.min(40, nonSystemMessages.length);
|
|
85
|
+
const kept = nonSystemMessages.slice(-keepCount);
|
|
86
|
+
const removed = nonSystemMessages.slice(0, -keepCount);
|
|
87
|
+
// Create a summary of removed messages
|
|
88
|
+
const summaryParts = [];
|
|
89
|
+
for (const msg of removed) {
|
|
90
|
+
const preview = msg.content.slice(0, 100);
|
|
91
|
+
switch (msg.role) {
|
|
92
|
+
case 'user':
|
|
93
|
+
summaryParts.push(`[User asked]: ${preview}`);
|
|
94
|
+
break;
|
|
95
|
+
case 'assistant':
|
|
96
|
+
if (msg.toolCalls?.length) {
|
|
97
|
+
summaryParts.push(`[AI called: ${msg.toolCalls.map(tc => tc.function.name).join(', ')}]`);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
summaryParts.push(`[AI responded]: ${preview}`);
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case 'tool':
|
|
104
|
+
summaryParts.push(`[Tool result]: ${preview.slice(0, 50)}`);
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const contextSummary = {
|
|
109
|
+
role: 'system',
|
|
110
|
+
content: `## Earlier Conversation Summary\n${summaryParts.join('\n')}`,
|
|
111
|
+
timestamp: Date.now(),
|
|
112
|
+
};
|
|
113
|
+
const compacted = [...systemMessages, contextSummary, ...kept];
|
|
114
|
+
const compactedTokens = this.contextManager.countTokens(compacted);
|
|
115
|
+
const originalTokens = this.contextManager.countTokens(messages);
|
|
116
|
+
return {
|
|
117
|
+
messages: compacted,
|
|
118
|
+
layer: 'snip',
|
|
119
|
+
originalTokens,
|
|
120
|
+
compactedTokens,
|
|
121
|
+
savings: originalTokens - compactedTokens,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Layer 3: Micro-compaction
|
|
126
|
+
* Compress each message to its essential content
|
|
127
|
+
*/
|
|
128
|
+
layerMicroCompact(messages) {
|
|
129
|
+
const compacted = messages.map(msg => {
|
|
130
|
+
// Only compress long messages
|
|
131
|
+
if (msg.content.length < 500)
|
|
132
|
+
return msg;
|
|
133
|
+
// For tool results, keep first and last portions
|
|
134
|
+
if (msg.role === 'tool') {
|
|
135
|
+
const half = Math.floor(1000 / 2);
|
|
136
|
+
const compressed = msg.content.slice(0, half) +
|
|
137
|
+
'\n...[compressed]...\n' +
|
|
138
|
+
msg.content.slice(-half);
|
|
139
|
+
return { ...msg, content: compressed };
|
|
140
|
+
}
|
|
141
|
+
// For assistant messages, keep first part
|
|
142
|
+
if (msg.role === 'assistant' && msg.content.length > 1000) {
|
|
143
|
+
return { ...msg, content: msg.content.slice(0, 800) + '\n...[compressed]' };
|
|
144
|
+
}
|
|
145
|
+
return msg;
|
|
146
|
+
});
|
|
147
|
+
const compactedTokens = this.contextManager.countTokens(compacted);
|
|
148
|
+
const originalTokens = this.contextManager.countTokens(messages);
|
|
149
|
+
return {
|
|
150
|
+
messages: compacted,
|
|
151
|
+
layer: 'micro',
|
|
152
|
+
originalTokens,
|
|
153
|
+
compactedTokens,
|
|
154
|
+
savings: originalTokens - compactedTokens,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Layer 4: Session Memory Compaction
|
|
159
|
+
* Extract key memories from conversation, replace with summary
|
|
160
|
+
*/
|
|
161
|
+
async layerSessionMemory(messages) {
|
|
162
|
+
// Build a prompt to extract key information
|
|
163
|
+
const conversation = messages
|
|
164
|
+
.filter(m => m.role !== 'system')
|
|
165
|
+
.map(m => `[${m.role}]: ${m.content.slice(0, 200)}`)
|
|
166
|
+
.join('\n');
|
|
167
|
+
try {
|
|
168
|
+
const response = await this.client.quickChat(this.model, [
|
|
169
|
+
{ role: 'system', content: 'Extract the key facts, decisions, and context from this conversation. Return a concise list of important points.', timestamp: Date.now() },
|
|
170
|
+
{ role: 'user', content: conversation, timestamp: Date.now() },
|
|
171
|
+
]);
|
|
172
|
+
const memories = response.content.split('\n').filter(l => l.trim());
|
|
173
|
+
// Keep system messages + memory summary + last 10 messages
|
|
174
|
+
const systemMessages = messages.filter(m => m.role === 'system');
|
|
175
|
+
const recentMessages = messages.slice(-10);
|
|
176
|
+
const memoryMessage = {
|
|
177
|
+
role: 'system',
|
|
178
|
+
content: `## Session Memories\n${response.content}`,
|
|
179
|
+
timestamp: Date.now(),
|
|
180
|
+
};
|
|
181
|
+
const compacted = [...systemMessages, memoryMessage, ...recentMessages];
|
|
182
|
+
const compactedTokens = this.contextManager.countTokens(compacted);
|
|
183
|
+
const originalTokens = this.contextManager.countTokens(messages);
|
|
184
|
+
return {
|
|
185
|
+
messages: compacted,
|
|
186
|
+
layer: 'session_memory',
|
|
187
|
+
originalTokens,
|
|
188
|
+
compactedTokens,
|
|
189
|
+
savings: originalTokens - compactedTokens,
|
|
190
|
+
memories,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
// Fallback to snip
|
|
195
|
+
return this.layerSnip(messages);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Layer 5: Full Collapse
|
|
200
|
+
* Nuclear option - completely summarize the conversation
|
|
201
|
+
*/
|
|
202
|
+
async layerFullCollapse(messages) {
|
|
203
|
+
const systemMessages = messages.filter(m => m.role === 'system');
|
|
204
|
+
// Get the user's original request
|
|
205
|
+
const userMessages = messages.filter(m => m.role === 'user');
|
|
206
|
+
const originalRequest = userMessages[0]?.content || '';
|
|
207
|
+
// Get the latest state
|
|
208
|
+
const lastAssistant = [...messages].reverse().find(m => m.role === 'assistant');
|
|
209
|
+
const latestState = lastAssistant?.content || '';
|
|
210
|
+
try {
|
|
211
|
+
const response = await this.client.quickChat(this.model, [
|
|
212
|
+
{
|
|
213
|
+
role: 'system',
|
|
214
|
+
content: 'You are a context compactor. Create a comprehensive but concise summary of the conversation that preserves all critical context for continuing the task.',
|
|
215
|
+
timestamp: Date.now(),
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
role: 'user',
|
|
219
|
+
content: `Original request: ${originalRequest}\n\nCurrent state: ${latestState}\n\nFull conversation:\n${messages.map(m => `[${m.role}]: ${m.content.slice(0, 300)}`).join('\n')}`,
|
|
220
|
+
timestamp: Date.now(),
|
|
221
|
+
},
|
|
222
|
+
]);
|
|
223
|
+
const collapsedMessage = {
|
|
224
|
+
role: 'system',
|
|
225
|
+
content: `## Collapsed Context\n${response.content}`,
|
|
226
|
+
timestamp: Date.now(),
|
|
227
|
+
};
|
|
228
|
+
// Keep system + collapsed context + last user message + last assistant message
|
|
229
|
+
const compacted = [
|
|
230
|
+
...systemMessages,
|
|
231
|
+
collapsedMessage,
|
|
232
|
+
...(userMessages.length > 0 ? [userMessages[userMessages.length - 1]] : []),
|
|
233
|
+
...(lastAssistant ? [lastAssistant] : []),
|
|
234
|
+
];
|
|
235
|
+
const compactedTokens = this.contextManager.countTokens(compacted);
|
|
236
|
+
const originalTokens = this.contextManager.countTokens(messages);
|
|
237
|
+
return {
|
|
238
|
+
messages: compacted,
|
|
239
|
+
layer: 'full_collapse',
|
|
240
|
+
originalTokens,
|
|
241
|
+
compactedTokens,
|
|
242
|
+
savings: originalTokens - compactedTokens,
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
catch {
|
|
246
|
+
// Ultimate fallback
|
|
247
|
+
return this.layerSnip(messages);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=compaction.js.map
|