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,173 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Diff Preview System
|
|
3
|
+
// Shows file changes before applying them
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { readFileSync, existsSync } from 'fs';
|
|
6
|
+
import chalk from 'chalk';
|
|
7
|
+
export class DiffPreview {
|
|
8
|
+
/**
|
|
9
|
+
* Create a diff preview between old content and new content
|
|
10
|
+
*/
|
|
11
|
+
static createDiff(oldContent, newContent, filePath) {
|
|
12
|
+
const oldLines = oldContent.split('\n');
|
|
13
|
+
const newLines = newContent.split('\n');
|
|
14
|
+
const diffLines = [];
|
|
15
|
+
// Simple LCS-based diff
|
|
16
|
+
const lcs = DiffPreview.lcs(oldLines, newLines);
|
|
17
|
+
let oi = 0, ni = 0, li = 0;
|
|
18
|
+
let added = 0, removed = 0;
|
|
19
|
+
while (oi < oldLines.length || ni < newLines.length) {
|
|
20
|
+
if (li < lcs.length && oi < oldLines.length && ni < newLines.length &&
|
|
21
|
+
oldLines[oi] === lcs[li] && newLines[ni] === lcs[li]) {
|
|
22
|
+
diffLines.push({ type: 'context', content: oldLines[oi], lineNumber: oi + 1 });
|
|
23
|
+
oi++;
|
|
24
|
+
ni++;
|
|
25
|
+
li++;
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
// Check for removals
|
|
29
|
+
while (oi < oldLines.length && (li >= lcs.length || oldLines[oi] !== lcs[li])) {
|
|
30
|
+
diffLines.push({ type: 'remove', content: oldLines[oi], lineNumber: oi + 1 });
|
|
31
|
+
removed++;
|
|
32
|
+
oi++;
|
|
33
|
+
}
|
|
34
|
+
// Check for additions
|
|
35
|
+
while (ni < newLines.length && (li >= lcs.length || newLines[ni] !== lcs[li])) {
|
|
36
|
+
diffLines.push({ type: 'add', content: newLines[ni], lineNumber: ni + 1 });
|
|
37
|
+
added++;
|
|
38
|
+
ni++;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return { filePath, added, removed, lines: diffLines };
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Create diff for edit_file operation (old_text -> new_text replacement)
|
|
46
|
+
*/
|
|
47
|
+
static createEditDiff(filePath, oldText, newText) {
|
|
48
|
+
let content = '';
|
|
49
|
+
if (existsSync(filePath)) {
|
|
50
|
+
content = readFileSync(filePath, 'utf-8');
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
// Find the old text in the file
|
|
56
|
+
const idx = content.indexOf(oldText);
|
|
57
|
+
if (idx === -1)
|
|
58
|
+
return null;
|
|
59
|
+
const newContent = content.slice(0, idx) + newText + content.slice(idx + oldText.length);
|
|
60
|
+
return DiffPreview.createDiff(content, newContent, filePath);
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Render diff to terminal with colors
|
|
64
|
+
*/
|
|
65
|
+
static renderDiff(diff, contextLines = 3) {
|
|
66
|
+
const { filePath, added, removed, lines } = diff;
|
|
67
|
+
console.log();
|
|
68
|
+
console.log(` ${chalk.bold.cyan('━━━ Diff Preview ━━━')}`);
|
|
69
|
+
console.log(` ${chalk.white(filePath)} ${chalk.green(`+${added}`)} ${chalk.red(`-${removed}`)}`);
|
|
70
|
+
console.log(` ${chalk.gray('─'.repeat(50))}`);
|
|
71
|
+
let inChange = false;
|
|
72
|
+
let contextCount = 0;
|
|
73
|
+
for (const line of lines) {
|
|
74
|
+
switch (line.type) {
|
|
75
|
+
case 'add':
|
|
76
|
+
contextCount = 0;
|
|
77
|
+
inChange = true;
|
|
78
|
+
console.log(` ${chalk.green('+')} ${chalk.green(line.content)}`);
|
|
79
|
+
break;
|
|
80
|
+
case 'remove':
|
|
81
|
+
contextCount = 0;
|
|
82
|
+
inChange = true;
|
|
83
|
+
console.log(` ${chalk.red('-')} ${chalk.red(line.content)}`);
|
|
84
|
+
break;
|
|
85
|
+
case 'context':
|
|
86
|
+
contextCount++;
|
|
87
|
+
if (inChange) {
|
|
88
|
+
// Show a few context lines after a change
|
|
89
|
+
if (contextCount <= contextLines) {
|
|
90
|
+
console.log(` ${chalk.gray(' ')} ${chalk.gray(line.content)}`);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
inChange = false;
|
|
94
|
+
console.log(` ${chalk.gray(' ...')}`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else if (contextCount <= 1) {
|
|
98
|
+
// Show one context line before a change
|
|
99
|
+
console.log(` ${chalk.gray(' ')} ${chalk.gray(line.content)}`);
|
|
100
|
+
}
|
|
101
|
+
break;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
console.log(` ${chalk.gray('─'.repeat(50))}`);
|
|
105
|
+
console.log(` ${chalk.green(`+${added} additions`)} ${chalk.red(`-${removed} deletions`)}`);
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Render a compact summary of multiple diffs
|
|
110
|
+
*/
|
|
111
|
+
static renderSummary(diffs) {
|
|
112
|
+
console.log();
|
|
113
|
+
console.log(` ${chalk.bold.cyan('━━━ Changes Summary ━━━')}`);
|
|
114
|
+
for (const diff of diffs) {
|
|
115
|
+
const statusLabel = diff.added > 0 && diff.removed === 0 ? chalk.green('created') :
|
|
116
|
+
diff.added === 0 && diff.removed > 0 ? chalk.red('deleted') :
|
|
117
|
+
chalk.yellow('modified');
|
|
118
|
+
console.log(` ${statusLabel} ${diff.filePath.padEnd(40)} ${chalk.green(`+${diff.added}`)} ${chalk.red(`-${diff.removed}`)}`);
|
|
119
|
+
}
|
|
120
|
+
const totalAdded = diffs.reduce((s, d) => s + d.added, 0);
|
|
121
|
+
const totalRemoved = diffs.reduce((s, d) => s + d.removed, 0);
|
|
122
|
+
console.log(` ${chalk.gray('─'.repeat(50))}`);
|
|
123
|
+
console.log(` ${chalk.bold('Total:')} ${chalk.green(`+${totalAdded}`)} ${chalk.red(`-${totalRemoved}`)}`);
|
|
124
|
+
console.log();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Ask user to confirm diff changes
|
|
128
|
+
*/
|
|
129
|
+
static async confirmDiff(diff) {
|
|
130
|
+
DiffPreview.renderDiff(diff);
|
|
131
|
+
const rl = require('readline').createInterface({ input: process.stdin, output: process.stdout });
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
rl.question(chalk.cyan(' Apply these changes? [y/n]: '), (answer) => {
|
|
134
|
+
rl.close();
|
|
135
|
+
resolve(answer.toLowerCase().trim() === 'y' || answer.toLowerCase().trim() === 'yes');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
// Simple Longest Common Subsequence
|
|
140
|
+
static lcs(a, b) {
|
|
141
|
+
const m = a.length;
|
|
142
|
+
const n = b.length;
|
|
143
|
+
const dp = Array.from({ length: m + 1 }, () => Array(n + 1).fill(0));
|
|
144
|
+
for (let i = 1; i <= m; i++) {
|
|
145
|
+
for (let j = 1; j <= n; j++) {
|
|
146
|
+
if (a[i - 1] === b[j - 1]) {
|
|
147
|
+
dp[i][j] = dp[i - 1][j - 1] + 1;
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Backtrack
|
|
155
|
+
const result = [];
|
|
156
|
+
let i = m, j = n;
|
|
157
|
+
while (i > 0 && j > 0) {
|
|
158
|
+
if (a[i - 1] === b[j - 1]) {
|
|
159
|
+
result.unshift(a[i - 1]);
|
|
160
|
+
i--;
|
|
161
|
+
j--;
|
|
162
|
+
}
|
|
163
|
+
else if (dp[i - 1][j] > dp[i][j - 1]) {
|
|
164
|
+
i--;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
j--;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=diff-preview.js.map
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export interface DoomLoopConfig {
|
|
2
|
+
maxConsecutiveErrors: number;
|
|
3
|
+
maxRepetitiveActions: number;
|
|
4
|
+
similarityThreshold: number;
|
|
5
|
+
cooldownMs: number;
|
|
6
|
+
autoBreak: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface DoomLoopState {
|
|
9
|
+
consecutiveErrors: number;
|
|
10
|
+
lastActions: Array<{
|
|
11
|
+
tool: string;
|
|
12
|
+
args: string;
|
|
13
|
+
result: string;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}>;
|
|
16
|
+
isPaused: boolean;
|
|
17
|
+
pauseReason?: string;
|
|
18
|
+
totalLoopsDetected: number;
|
|
19
|
+
}
|
|
20
|
+
export declare class DoomLoopProtection {
|
|
21
|
+
private config;
|
|
22
|
+
private state;
|
|
23
|
+
private onLoopDetected?;
|
|
24
|
+
constructor(config?: Partial<DoomLoopConfig>, onLoopDetected?: (reason: string, state: DoomLoopState) => Promise<boolean>);
|
|
25
|
+
/**
|
|
26
|
+
* Record a tool execution and check for doom loops
|
|
27
|
+
* Returns true if the action should proceed, false if blocked
|
|
28
|
+
*/
|
|
29
|
+
recordAction(toolName: string, args: Record<string, unknown>, result: string, isError: boolean): Promise<boolean>;
|
|
30
|
+
/**
|
|
31
|
+
* Reset the state (e.g., after user intervention)
|
|
32
|
+
*/
|
|
33
|
+
reset(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Unpause after user intervention
|
|
36
|
+
*/
|
|
37
|
+
unpause(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Get current state
|
|
40
|
+
*/
|
|
41
|
+
getState(): DoomLoopState;
|
|
42
|
+
/**
|
|
43
|
+
* Check if currently paused
|
|
44
|
+
*/
|
|
45
|
+
isPaused(): boolean;
|
|
46
|
+
private handleLoop;
|
|
47
|
+
private countRepetitiveActions;
|
|
48
|
+
private calculateErrorSimilarity;
|
|
49
|
+
private summarizeArgs;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=doom-loop.d.ts.map
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Doom Loop Protection
|
|
3
|
+
// Detects and prevents agent stuck loops
|
|
4
|
+
// ============================================================
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
const DEFAULT_CONFIG = {
|
|
7
|
+
maxConsecutiveErrors: 3,
|
|
8
|
+
maxRepetitiveActions: 3,
|
|
9
|
+
similarityThreshold: 0.7,
|
|
10
|
+
cooldownMs: 5000,
|
|
11
|
+
autoBreak: true,
|
|
12
|
+
};
|
|
13
|
+
export class DoomLoopProtection {
|
|
14
|
+
config;
|
|
15
|
+
state;
|
|
16
|
+
onLoopDetected;
|
|
17
|
+
constructor(config, onLoopDetected) {
|
|
18
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
19
|
+
this.onLoopDetected = onLoopDetected;
|
|
20
|
+
this.state = {
|
|
21
|
+
consecutiveErrors: 0,
|
|
22
|
+
lastActions: [],
|
|
23
|
+
isPaused: false,
|
|
24
|
+
totalLoopsDetected: 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Record a tool execution and check for doom loops
|
|
29
|
+
* Returns true if the action should proceed, false if blocked
|
|
30
|
+
*/
|
|
31
|
+
async recordAction(toolName, args, result, isError) {
|
|
32
|
+
if (this.state.isPaused) {
|
|
33
|
+
console.log(chalk.yellow(`\n ⚠ Doom loop protection: Agent is paused. Reason: ${this.state.pauseReason}`));
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Track errors
|
|
37
|
+
if (isError) {
|
|
38
|
+
this.state.consecutiveErrors++;
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
this.state.consecutiveErrors = 0;
|
|
42
|
+
}
|
|
43
|
+
// Record action
|
|
44
|
+
const actionStr = JSON.stringify({ tool: toolName, args: this.summarizeArgs(args) });
|
|
45
|
+
this.state.lastActions.push({
|
|
46
|
+
tool: toolName,
|
|
47
|
+
args: actionStr,
|
|
48
|
+
result: isError ? 'error' : 'success',
|
|
49
|
+
timestamp: Date.now(),
|
|
50
|
+
});
|
|
51
|
+
// Keep only last 20 actions
|
|
52
|
+
if (this.state.lastActions.length > 20) {
|
|
53
|
+
this.state.lastActions = this.state.lastActions.slice(-20);
|
|
54
|
+
}
|
|
55
|
+
// Check for consecutive errors
|
|
56
|
+
if (this.state.consecutiveErrors >= this.config.maxConsecutiveErrors) {
|
|
57
|
+
const reason = `${this.state.consecutiveErrors} consecutive errors detected`;
|
|
58
|
+
return this.handleLoop(reason);
|
|
59
|
+
}
|
|
60
|
+
// Check for repetitive actions
|
|
61
|
+
const recentActions = this.state.lastActions.slice(-this.config.maxRepetitiveActions * 2);
|
|
62
|
+
const repetitiveCount = this.countRepetitiveActions(recentActions, toolName, args);
|
|
63
|
+
if (repetitiveCount >= this.config.maxRepetitiveActions) {
|
|
64
|
+
const reason = `${repetitiveCount} repetitive actions detected for ${toolName}`;
|
|
65
|
+
return this.handleLoop(reason);
|
|
66
|
+
}
|
|
67
|
+
// Check for similar error patterns
|
|
68
|
+
const recentErrors = this.state.lastActions.filter(a => a.result === 'error').slice(-5);
|
|
69
|
+
if (recentErrors.length >= 3) {
|
|
70
|
+
const similarity = this.calculateErrorSimilarity(recentErrors);
|
|
71
|
+
if (similarity >= this.config.similarityThreshold) {
|
|
72
|
+
const reason = `Similar errors detected (${(similarity * 100).toFixed(0)}% similarity)`;
|
|
73
|
+
return this.handleLoop(reason);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Reset the state (e.g., after user intervention)
|
|
80
|
+
*/
|
|
81
|
+
reset() {
|
|
82
|
+
this.state = {
|
|
83
|
+
consecutiveErrors: 0,
|
|
84
|
+
lastActions: [],
|
|
85
|
+
isPaused: false,
|
|
86
|
+
totalLoopsDetected: 0,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Unpause after user intervention
|
|
91
|
+
*/
|
|
92
|
+
unpause() {
|
|
93
|
+
this.state.isPaused = false;
|
|
94
|
+
this.state.pauseReason = undefined;
|
|
95
|
+
this.state.consecutiveErrors = 0;
|
|
96
|
+
this.state.lastActions = [];
|
|
97
|
+
console.log(chalk.green(' ✓ Doom loop protection: Resumed'));
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get current state
|
|
101
|
+
*/
|
|
102
|
+
getState() {
|
|
103
|
+
return { ...this.state };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Check if currently paused
|
|
107
|
+
*/
|
|
108
|
+
isPaused() {
|
|
109
|
+
return this.state.isPaused;
|
|
110
|
+
}
|
|
111
|
+
async handleLoop(reason) {
|
|
112
|
+
this.state.totalLoopsDetected++;
|
|
113
|
+
this.state.isPaused = true;
|
|
114
|
+
this.state.pauseReason = reason;
|
|
115
|
+
console.log(chalk.red.bold(`\n 🚨 Doom Loop Detected: ${reason}`));
|
|
116
|
+
console.log(chalk.yellow(' The agent appears to be stuck in a loop.'));
|
|
117
|
+
console.log(chalk.gray(' Use /unpause to resume or /reset to start fresh.'));
|
|
118
|
+
if (this.onLoopDetected) {
|
|
119
|
+
const shouldContinue = await this.onLoopDetected(reason, this.state);
|
|
120
|
+
if (shouldContinue) {
|
|
121
|
+
this.unpause();
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
countRepetitiveActions(actions, toolName, args) {
|
|
128
|
+
const currentArgs = this.summarizeArgs(args);
|
|
129
|
+
let count = 0;
|
|
130
|
+
for (const action of actions) {
|
|
131
|
+
if (action.tool === toolName) {
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(action.args);
|
|
134
|
+
if (parsed.args === currentArgs) {
|
|
135
|
+
count++;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
catch { }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return count;
|
|
142
|
+
}
|
|
143
|
+
calculateErrorSimilarity(errors) {
|
|
144
|
+
if (errors.length < 2)
|
|
145
|
+
return 0;
|
|
146
|
+
// Simple word-overlap similarity
|
|
147
|
+
const allWords = errors.map(e => {
|
|
148
|
+
try {
|
|
149
|
+
const parsed = JSON.parse(e.args);
|
|
150
|
+
return new Set((parsed.args || '').split(/\s+/));
|
|
151
|
+
}
|
|
152
|
+
catch {
|
|
153
|
+
return new Set();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
let totalSimilarity = 0;
|
|
157
|
+
let comparisons = 0;
|
|
158
|
+
for (let i = 0; i < allWords.length - 1; i++) {
|
|
159
|
+
for (let j = i + 1; j < allWords.length; j++) {
|
|
160
|
+
const intersection = new Set([...allWords[i]].filter(x => allWords[j].has(x)));
|
|
161
|
+
const union = new Set([...allWords[i], ...allWords[j]]);
|
|
162
|
+
const similarity = union.size > 0 ? intersection.size / union.size : 0;
|
|
163
|
+
totalSimilarity += similarity;
|
|
164
|
+
comparisons++;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return comparisons > 0 ? totalSimilarity / comparisons : 0;
|
|
168
|
+
}
|
|
169
|
+
summarizeArgs(args) {
|
|
170
|
+
const entries = Object.entries(args);
|
|
171
|
+
if (entries.length === 0)
|
|
172
|
+
return '';
|
|
173
|
+
return entries.map(([k, v]) => {
|
|
174
|
+
const val = typeof v === 'string' ? v.slice(0, 100) : JSON.stringify(v).slice(0, 100);
|
|
175
|
+
return `${k}=${val}`;
|
|
176
|
+
}).join(' ');
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
//# sourceMappingURL=doom-loop.js.map
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { NeuroConfig, AgentExecution } from '../core/types.js';
|
|
2
|
+
import { OpenRouterClient, TokenUsage } from '../api/openrouter.js';
|
|
3
|
+
import { ToolRegistry } from '../tools/registry.js';
|
|
4
|
+
import { BaseAgent } from '../agents/base.js';
|
|
5
|
+
import { Orchestrator } from '../agents/orchestrator.js';
|
|
6
|
+
import { ContextManager } from '../core/context.js';
|
|
7
|
+
import { SessionManager } from '../core/session.js';
|
|
8
|
+
import { TerminalUI } from '../ui/renderer.js';
|
|
9
|
+
import { ApprovalSystem } from '../core/approval.js';
|
|
10
|
+
import { MCPClient } from '../mcp/client.js';
|
|
11
|
+
import { DoomLoopProtection } from '../core/doom-loop.js';
|
|
12
|
+
import { FallbackChain } from '../core/fallback.js';
|
|
13
|
+
import { Sandbox } from '../core/sandbox.js';
|
|
14
|
+
import { PluginManager } from '../core/plugin-sdk.js';
|
|
15
|
+
import { UndoRedoSystem } from './undo-redo.js';
|
|
16
|
+
import { PromptCache } from './prompt-cache.js';
|
|
17
|
+
import { ModelRouter } from './model-router.js';
|
|
18
|
+
import { StyleManager } from './output-styles.js';
|
|
19
|
+
import { ExtendedThinking } from './extended-thinking.js';
|
|
20
|
+
import { SpendingMonitor } from './spending-warnings.js';
|
|
21
|
+
import { SkillSystem } from '../context/skill-system.js';
|
|
22
|
+
import { CustomAgentLoader } from '../context/custom-agents.js';
|
|
23
|
+
import { CustomToolLoader } from '../context/custom-tools.js';
|
|
24
|
+
import { NeuroIgnore } from '../context/neuroignore.js';
|
|
25
|
+
import { OllamaProvider } from '../api/ollama.js';
|
|
26
|
+
import { TelemetrySystem } from './telemetry.js';
|
|
27
|
+
import { VimModeManager } from './vim-mode.js';
|
|
28
|
+
import { I18nSystem } from './i18n.js';
|
|
29
|
+
import { MultimodalSupport } from './multimodal.js';
|
|
30
|
+
import { VoiceIO } from './voice.js';
|
|
31
|
+
import { APIServer } from './api-server.js';
|
|
32
|
+
import { CloudSync } from './cloud-sync.js';
|
|
33
|
+
import { WebDashboard } from './web-dashboard.js';
|
|
34
|
+
import { SkillStandard } from '../context/skill-standard.js';
|
|
35
|
+
import { AutoMode } from './auto-mode.js';
|
|
36
|
+
import { ScheduledTaskManager } from './scheduled-tasks.js';
|
|
37
|
+
import { ParallelAgentManager } from './parallel-agents.js';
|
|
38
|
+
import { BackgroundSessionManager } from './background-session.js';
|
|
39
|
+
import { TreeSitterIntegration } from '../context/tree-sitter.js';
|
|
40
|
+
import { LintingIntegration } from './linting.js';
|
|
41
|
+
import { TestingIntegration } from './testing.js';
|
|
42
|
+
import { CodeReviewSystem } from './code-review.js';
|
|
43
|
+
import { SecurityScanner } from './security-scanner.js';
|
|
44
|
+
import { PluginBundleManager } from './plugin-bundle.js';
|
|
45
|
+
import { SubAgentManager } from './sub-agent.js';
|
|
46
|
+
import { ACPServer } from './acp.js';
|
|
47
|
+
import { OSSandboxManager } from './os-sandbox.js';
|
|
48
|
+
import { SpecDrivenPipeline } from './spec-driven.js';
|
|
49
|
+
import { LLMEvaluatorManager } from '../hooks/llm-evaluator.js';
|
|
50
|
+
import { MCPAppManager } from '../mcp/mcp-apps.js';
|
|
51
|
+
import { MultiModelOrchestrator } from './multi-model.js';
|
|
52
|
+
import { SmartMonitor } from './smart-monitor.js';
|
|
53
|
+
import { OutcomeGrader } from './outcome-grading.js';
|
|
54
|
+
import { ObservabilityManager } from './observability.js';
|
|
55
|
+
import { AutoCompactManager } from './auto-compact.js';
|
|
56
|
+
import { TerminalUX } from './terminal-ux.js';
|
|
57
|
+
import { MultiSessionManager } from './multi-session.js';
|
|
58
|
+
import { GitWorktreeManager } from './git-worktree.js';
|
|
59
|
+
import { AutoUpdater } from './updater.js';
|
|
60
|
+
export declare class NeuroEngine {
|
|
61
|
+
config: NeuroConfig;
|
|
62
|
+
client: OpenRouterClient;
|
|
63
|
+
registry: ToolRegistry;
|
|
64
|
+
orchestrator: Orchestrator;
|
|
65
|
+
contextManager: ContextManager;
|
|
66
|
+
sessionManager: SessionManager;
|
|
67
|
+
ui: TerminalUI;
|
|
68
|
+
agents: Map<string, BaseAgent>;
|
|
69
|
+
mcpClient: MCPClient;
|
|
70
|
+
approval: ApprovalSystem;
|
|
71
|
+
doomLoop: DoomLoopProtection;
|
|
72
|
+
fallback: FallbackChain;
|
|
73
|
+
sandbox: Sandbox;
|
|
74
|
+
pluginManager: PluginManager;
|
|
75
|
+
undoRedo: UndoRedoSystem;
|
|
76
|
+
promptCache: PromptCache;
|
|
77
|
+
modelRouter: ModelRouter;
|
|
78
|
+
styleManager: StyleManager;
|
|
79
|
+
extendedThinking: ExtendedThinking;
|
|
80
|
+
spendingMonitor: SpendingMonitor;
|
|
81
|
+
skillSystem: SkillSystem;
|
|
82
|
+
customAgentLoader: CustomAgentLoader;
|
|
83
|
+
customToolLoader: CustomToolLoader;
|
|
84
|
+
neuroIgnore: NeuroIgnore;
|
|
85
|
+
ollamaProvider: OllamaProvider;
|
|
86
|
+
telemetry: TelemetrySystem;
|
|
87
|
+
vimMode: VimModeManager;
|
|
88
|
+
i18n: I18nSystem;
|
|
89
|
+
multimodal: MultimodalSupport;
|
|
90
|
+
voice: VoiceIO;
|
|
91
|
+
apiServer: APIServer;
|
|
92
|
+
cloudSync: CloudSync;
|
|
93
|
+
dashboard: WebDashboard;
|
|
94
|
+
skillStandard: SkillStandard;
|
|
95
|
+
autoMode: AutoMode;
|
|
96
|
+
scheduledTasks: ScheduledTaskManager;
|
|
97
|
+
parallelAgents: ParallelAgentManager;
|
|
98
|
+
backgroundSessions: BackgroundSessionManager;
|
|
99
|
+
treeSitter: TreeSitterIntegration;
|
|
100
|
+
linting: LintingIntegration;
|
|
101
|
+
testing: TestingIntegration;
|
|
102
|
+
codeReview: CodeReviewSystem;
|
|
103
|
+
securityScanner: SecurityScanner;
|
|
104
|
+
pluginBundles: PluginBundleManager;
|
|
105
|
+
subAgentSpawner: SubAgentManager;
|
|
106
|
+
acp: ACPServer;
|
|
107
|
+
osSandbox: OSSandboxManager;
|
|
108
|
+
specDriven: SpecDrivenPipeline;
|
|
109
|
+
llmEvaluator: LLMEvaluatorManager;
|
|
110
|
+
mcpApps: MCPAppManager;
|
|
111
|
+
multiModelOrchestrator: MultiModelOrchestrator;
|
|
112
|
+
smartMonitor: SmartMonitor;
|
|
113
|
+
outcomeGrading: OutcomeGrader;
|
|
114
|
+
observability: ObservabilityManager;
|
|
115
|
+
autoCompact: AutoCompactManager;
|
|
116
|
+
terminalUX: TerminalUX;
|
|
117
|
+
multiSession: MultiSessionManager;
|
|
118
|
+
gitWorktree: GitWorktreeManager;
|
|
119
|
+
updater: AutoUpdater;
|
|
120
|
+
private autoApproveSet;
|
|
121
|
+
private requireApprovalSet;
|
|
122
|
+
constructor(config: NeuroConfig);
|
|
123
|
+
/**
|
|
124
|
+
* Register plugin tools with the tool registry
|
|
125
|
+
*/
|
|
126
|
+
private registerPluginTools;
|
|
127
|
+
/**
|
|
128
|
+
* Register custom tools from .neuro/tools/
|
|
129
|
+
*/
|
|
130
|
+
private registerCustomTools;
|
|
131
|
+
/**
|
|
132
|
+
* Load custom agents from .neuro/agents/
|
|
133
|
+
*/
|
|
134
|
+
private loadCustomAgents;
|
|
135
|
+
/**
|
|
136
|
+
* Initialize all agents from config
|
|
137
|
+
*/
|
|
138
|
+
private initializeAgents;
|
|
139
|
+
/**
|
|
140
|
+
* Process a user message
|
|
141
|
+
*/
|
|
142
|
+
processMessage(message: string, mode?: 'auto' | 'agent' | 'direct', targetAgent?: string): Promise<{
|
|
143
|
+
content: string;
|
|
144
|
+
usage: TokenUsage;
|
|
145
|
+
execution?: AgentExecution;
|
|
146
|
+
}>;
|
|
147
|
+
/**
|
|
148
|
+
* Check sandbox permissions for a tool call
|
|
149
|
+
*/
|
|
150
|
+
private checkSandboxForTool;
|
|
151
|
+
/**
|
|
152
|
+
* Handle tool approval using the enhanced ApprovalSystem
|
|
153
|
+
*/
|
|
154
|
+
private handleApproval;
|
|
155
|
+
/**
|
|
156
|
+
* Assess task complexity to decide execution mode
|
|
157
|
+
* Now delegates to ModelRouter for more sophisticated analysis
|
|
158
|
+
*/
|
|
159
|
+
private assessComplexity;
|
|
160
|
+
/**
|
|
161
|
+
* Switch the active model
|
|
162
|
+
*/
|
|
163
|
+
switchModel(modelId: string): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Get current session stats
|
|
166
|
+
*/
|
|
167
|
+
getSessionStats(): {
|
|
168
|
+
inputTokens: number;
|
|
169
|
+
outputTokens: number;
|
|
170
|
+
cost: number;
|
|
171
|
+
messages: number;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Register a custom agent
|
|
175
|
+
*/
|
|
176
|
+
registerCustomAgent(name: string, config: {
|
|
177
|
+
description: string;
|
|
178
|
+
systemPrompt: string;
|
|
179
|
+
tools?: string[];
|
|
180
|
+
maxIterations?: number;
|
|
181
|
+
}): void;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=engine.d.ts.map
|