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.
Files changed (175) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +510 -0
  3. package/dist/advisor/advisor.d.ts +50 -0
  4. package/dist/advisor/advisor.js +178 -0
  5. package/dist/agents/base.d.ts +62 -0
  6. package/dist/agents/base.js +215 -0
  7. package/dist/agents/orchestrator.d.ts +46 -0
  8. package/dist/agents/orchestrator.js +192 -0
  9. package/dist/agents/team.d.ts +51 -0
  10. package/dist/agents/team.js +210 -0
  11. package/dist/api/models.d.ts +23 -0
  12. package/dist/api/models.js +514 -0
  13. package/dist/api/ollama.d.ts +153 -0
  14. package/dist/api/ollama.js +751 -0
  15. package/dist/api/openrouter.d.ts +55 -0
  16. package/dist/api/openrouter.js +223 -0
  17. package/dist/commands/commands.d.ts +43 -0
  18. package/dist/commands/commands.js +308 -0
  19. package/dist/config/config.d.ts +8 -0
  20. package/dist/config/config.js +311 -0
  21. package/dist/context/compaction.d.ts +54 -0
  22. package/dist/context/compaction.js +251 -0
  23. package/dist/context/custom-agents.d.ts +107 -0
  24. package/dist/context/custom-agents.js +397 -0
  25. package/dist/context/custom-tools.d.ts +120 -0
  26. package/dist/context/custom-tools.js +564 -0
  27. package/dist/context/git-checkpoint.d.ts +52 -0
  28. package/dist/context/git-checkpoint.js +240 -0
  29. package/dist/context/neuro-md.d.ts +48 -0
  30. package/dist/context/neuro-md.js +202 -0
  31. package/dist/context/neuroignore.d.ts +102 -0
  32. package/dist/context/neuroignore.js +441 -0
  33. package/dist/context/repo-map.d.ts +38 -0
  34. package/dist/context/repo-map.js +220 -0
  35. package/dist/context/skill-standard.d.ts +262 -0
  36. package/dist/context/skill-standard.js +1156 -0
  37. package/dist/context/skill-system.d.ts +75 -0
  38. package/dist/context/skill-system.js +578 -0
  39. package/dist/context/tree-sitter.d.ts +99 -0
  40. package/dist/context/tree-sitter.js +1956 -0
  41. package/dist/core/acp.d.ts +325 -0
  42. package/dist/core/acp.js +1498 -0
  43. package/dist/core/api-server.d.ts +143 -0
  44. package/dist/core/api-server.js +550 -0
  45. package/dist/core/approval.d.ts +81 -0
  46. package/dist/core/approval.js +432 -0
  47. package/dist/core/auto-compact.d.ts +127 -0
  48. package/dist/core/auto-compact.js +436 -0
  49. package/dist/core/auto-mode.d.ts +232 -0
  50. package/dist/core/auto-mode.js +831 -0
  51. package/dist/core/background-session.d.ts +166 -0
  52. package/dist/core/background-session.js +696 -0
  53. package/dist/core/cicd.d.ts +257 -0
  54. package/dist/core/cicd.js +1443 -0
  55. package/dist/core/cloud-sync.d.ts +156 -0
  56. package/dist/core/cloud-sync.js +582 -0
  57. package/dist/core/code-review.d.ts +132 -0
  58. package/dist/core/code-review.js +1191 -0
  59. package/dist/core/completion.d.ts +49 -0
  60. package/dist/core/completion.js +384 -0
  61. package/dist/core/context.d.ts +38 -0
  62. package/dist/core/context.js +144 -0
  63. package/dist/core/diff-preview.d.ts +35 -0
  64. package/dist/core/diff-preview.js +173 -0
  65. package/dist/core/doom-loop.d.ts +51 -0
  66. package/dist/core/doom-loop.js +179 -0
  67. package/dist/core/engine.d.ts +183 -0
  68. package/dist/core/engine.js +942 -0
  69. package/dist/core/extended-thinking.d.ts +103 -0
  70. package/dist/core/extended-thinking.js +269 -0
  71. package/dist/core/fallback.d.ts +54 -0
  72. package/dist/core/fallback.js +104 -0
  73. package/dist/core/git-worktree.d.ts +139 -0
  74. package/dist/core/git-worktree.js +614 -0
  75. package/dist/core/headless.d.ts +30 -0
  76. package/dist/core/headless.js +101 -0
  77. package/dist/core/i18n.d.ts +83 -0
  78. package/dist/core/i18n.js +739 -0
  79. package/dist/core/linting.d.ts +129 -0
  80. package/dist/core/linting.js +699 -0
  81. package/dist/core/model-router.d.ts +109 -0
  82. package/dist/core/model-router.js +581 -0
  83. package/dist/core/multi-model.d.ts +243 -0
  84. package/dist/core/multi-model.js +1099 -0
  85. package/dist/core/multi-session.d.ts +144 -0
  86. package/dist/core/multi-session.js +442 -0
  87. package/dist/core/multimodal.d.ts +125 -0
  88. package/dist/core/multimodal.js +286 -0
  89. package/dist/core/observability.d.ts +93 -0
  90. package/dist/core/observability.js +737 -0
  91. package/dist/core/os-sandbox.d.ts +122 -0
  92. package/dist/core/os-sandbox.js +1193 -0
  93. package/dist/core/outcome-grading.d.ts +228 -0
  94. package/dist/core/outcome-grading.js +1123 -0
  95. package/dist/core/output-styles.d.ts +57 -0
  96. package/dist/core/output-styles.js +382 -0
  97. package/dist/core/parallel-agents.d.ts +183 -0
  98. package/dist/core/parallel-agents.js +563 -0
  99. package/dist/core/plugin-bundle.d.ts +236 -0
  100. package/dist/core/plugin-bundle.js +887 -0
  101. package/dist/core/plugin-sdk.d.ts +139 -0
  102. package/dist/core/plugin-sdk.js +273 -0
  103. package/dist/core/prompt-cache.d.ts +163 -0
  104. package/dist/core/prompt-cache.js +599 -0
  105. package/dist/core/sandbox.d.ts +127 -0
  106. package/dist/core/sandbox.js +369 -0
  107. package/dist/core/scheduled-tasks.d.ts +151 -0
  108. package/dist/core/scheduled-tasks.js +502 -0
  109. package/dist/core/security-scanner.d.ts +160 -0
  110. package/dist/core/security-scanner.js +1494 -0
  111. package/dist/core/session.d.ts +83 -0
  112. package/dist/core/session.js +269 -0
  113. package/dist/core/shell-completion.d.ts +51 -0
  114. package/dist/core/shell-completion.js +674 -0
  115. package/dist/core/smart-monitor.d.ts +146 -0
  116. package/dist/core/smart-monitor.js +1199 -0
  117. package/dist/core/spec-driven.d.ts +233 -0
  118. package/dist/core/spec-driven.js +1485 -0
  119. package/dist/core/spending-warnings.d.ts +123 -0
  120. package/dist/core/spending-warnings.js +456 -0
  121. package/dist/core/sub-agent.d.ts +298 -0
  122. package/dist/core/sub-agent.js +1023 -0
  123. package/dist/core/telemetry.d.ts +157 -0
  124. package/dist/core/telemetry.js +412 -0
  125. package/dist/core/terminal-ux.d.ts +134 -0
  126. package/dist/core/terminal-ux.js +649 -0
  127. package/dist/core/testing.d.ts +146 -0
  128. package/dist/core/testing.js +1200 -0
  129. package/dist/core/types.d.ts +439 -0
  130. package/dist/core/types.js +6 -0
  131. package/dist/core/undo-redo.d.ts +112 -0
  132. package/dist/core/undo-redo.js +290 -0
  133. package/dist/core/updater.d.ts +159 -0
  134. package/dist/core/updater.js +608 -0
  135. package/dist/core/vim-mode.d.ts +151 -0
  136. package/dist/core/vim-mode.js +771 -0
  137. package/dist/core/voice.d.ts +137 -0
  138. package/dist/core/voice.js +538 -0
  139. package/dist/core/web-dashboard.d.ts +109 -0
  140. package/dist/core/web-dashboard.js +484 -0
  141. package/dist/hooks/hooks.d.ts +74 -0
  142. package/dist/hooks/hooks.js +160 -0
  143. package/dist/hooks/llm-evaluator.d.ts +165 -0
  144. package/dist/hooks/llm-evaluator.js +560 -0
  145. package/dist/index.d.ts +3 -0
  146. package/dist/index.js +1186 -0
  147. package/dist/lsp/lsp-manager.d.ts +63 -0
  148. package/dist/lsp/lsp-manager.js +351 -0
  149. package/dist/mcp/client.d.ts +133 -0
  150. package/dist/mcp/client.js +684 -0
  151. package/dist/mcp/mcp-apps.d.ts +70 -0
  152. package/dist/mcp/mcp-apps.js +1007 -0
  153. package/dist/tools/bash.d.ts +5 -0
  154. package/dist/tools/bash.js +195 -0
  155. package/dist/tools/browser.d.ts +92 -0
  156. package/dist/tools/browser.js +1570 -0
  157. package/dist/tools/extended.d.ts +6 -0
  158. package/dist/tools/extended.js +191 -0
  159. package/dist/tools/file.d.ts +10 -0
  160. package/dist/tools/file.js +382 -0
  161. package/dist/tools/github.d.ts +389 -0
  162. package/dist/tools/github.js +759 -0
  163. package/dist/tools/index.d.ts +9 -0
  164. package/dist/tools/index.js +40 -0
  165. package/dist/tools/memory.d.ts +6 -0
  166. package/dist/tools/memory.js +197 -0
  167. package/dist/tools/registry.d.ts +29 -0
  168. package/dist/tools/registry.js +64 -0
  169. package/dist/tools/web.d.ts +6 -0
  170. package/dist/tools/web.js +150 -0
  171. package/dist/ui/renderer.d.ts +97 -0
  172. package/dist/ui/renderer.js +279 -0
  173. package/dist/ui/theme.d.ts +27 -0
  174. package/dist/ui/theme.js +106 -0
  175. 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