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,240 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - Git Auto-Commit & Checkpointing
|
|
3
|
+
// (Like Aider's auto-commit + Gemini CLI's shadow repo)
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { execSync } from 'child_process';
|
|
6
|
+
import { existsSync, mkdirSync, writeFileSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
export class GitCheckpointSystem {
|
|
10
|
+
workingDirectory;
|
|
11
|
+
shadowRepo;
|
|
12
|
+
autoCommit;
|
|
13
|
+
checkpoints = [];
|
|
14
|
+
constructor(workingDirectory, autoCommit = true) {
|
|
15
|
+
this.workingDirectory = workingDirectory;
|
|
16
|
+
this.autoCommit = autoCommit;
|
|
17
|
+
// Shadow repo for checkpointing (doesn't interfere with user's git)
|
|
18
|
+
const projectHash = this.hashString(workingDirectory);
|
|
19
|
+
this.shadowRepo = join(homedir(), '.neuro', 'checkpoints', projectHash);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Initialize the checkpoint system
|
|
23
|
+
*/
|
|
24
|
+
initialize() {
|
|
25
|
+
if (!this.isGitRepo())
|
|
26
|
+
return false;
|
|
27
|
+
this.ensureShadowRepo();
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Create a checkpoint before risky operations
|
|
32
|
+
*/
|
|
33
|
+
createCheckpoint(message) {
|
|
34
|
+
const changedFiles = this.getChangedFiles();
|
|
35
|
+
if (changedFiles.length === 0) {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
const checkpoint = {
|
|
39
|
+
id: `cp_${Date.now()}`,
|
|
40
|
+
timestamp: Date.now(),
|
|
41
|
+
message,
|
|
42
|
+
files: changedFiles,
|
|
43
|
+
};
|
|
44
|
+
// Option 1: Git stash-based checkpoint (quick)
|
|
45
|
+
if (this.autoCommit) {
|
|
46
|
+
try {
|
|
47
|
+
// Stage all changes
|
|
48
|
+
execSync('git add -A', { cwd: this.workingDirectory, encoding: 'utf-8' });
|
|
49
|
+
// Commit with checkpoint marker
|
|
50
|
+
const commitMsg = `neuro:checkpoint ${message}`;
|
|
51
|
+
execSync(`git commit -m "${commitMsg}" --no-gpg-sign`, {
|
|
52
|
+
cwd: this.workingDirectory,
|
|
53
|
+
encoding: 'utf-8',
|
|
54
|
+
});
|
|
55
|
+
const hash = execSync('git rev-parse HEAD', {
|
|
56
|
+
cwd: this.workingDirectory,
|
|
57
|
+
encoding: 'utf-8',
|
|
58
|
+
}).trim();
|
|
59
|
+
checkpoint.hash = hash;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Nothing to commit or commit failed
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// Option 2: Shadow repo snapshot
|
|
66
|
+
this.saveShadowSnapshot(checkpoint);
|
|
67
|
+
this.checkpoints.push(checkpoint);
|
|
68
|
+
return checkpoint;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Restore to a checkpoint
|
|
72
|
+
*/
|
|
73
|
+
restore(checkpointId) {
|
|
74
|
+
const checkpoint = this.checkpoints.find(c => c.id === checkpointId);
|
|
75
|
+
if (!checkpoint)
|
|
76
|
+
return false;
|
|
77
|
+
// Try git restore first
|
|
78
|
+
if (checkpoint.hash) {
|
|
79
|
+
try {
|
|
80
|
+
execSync(`git reset --hard ${checkpoint.hash}`, {
|
|
81
|
+
cwd: this.workingDirectory,
|
|
82
|
+
encoding: 'utf-8',
|
|
83
|
+
});
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch { }
|
|
87
|
+
}
|
|
88
|
+
// Fallback: restore from shadow repo
|
|
89
|
+
return this.restoreShadowSnapshot(checkpoint);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Undo the last checkpoint
|
|
93
|
+
*/
|
|
94
|
+
undo() {
|
|
95
|
+
if (this.checkpoints.length === 0)
|
|
96
|
+
return false;
|
|
97
|
+
const lastCheckpoint = this.checkpoints[this.checkpoints.length - 1];
|
|
98
|
+
return this.restore(lastCheckpoint.id);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* List all checkpoints
|
|
102
|
+
*/
|
|
103
|
+
listCheckpoints() {
|
|
104
|
+
return [...this.checkpoints];
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Get changed files since last commit
|
|
108
|
+
*/
|
|
109
|
+
getChangedFiles() {
|
|
110
|
+
try {
|
|
111
|
+
const status = execSync('git status --porcelain', {
|
|
112
|
+
cwd: this.workingDirectory,
|
|
113
|
+
encoding: 'utf-8',
|
|
114
|
+
});
|
|
115
|
+
return status.trim().split('\n')
|
|
116
|
+
.filter(l => l.trim())
|
|
117
|
+
.map(l => l.slice(3).trim());
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Get diff of changes
|
|
125
|
+
*/
|
|
126
|
+
getDiff() {
|
|
127
|
+
try {
|
|
128
|
+
return execSync('git diff', {
|
|
129
|
+
cwd: this.workingDirectory,
|
|
130
|
+
encoding: 'utf-8',
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return '';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Auto-commit changes with a message
|
|
139
|
+
*/
|
|
140
|
+
autoCommitChanges(message) {
|
|
141
|
+
if (!this.autoCommit)
|
|
142
|
+
return null;
|
|
143
|
+
if (!this.isGitRepo())
|
|
144
|
+
return null;
|
|
145
|
+
const changed = this.getChangedFiles();
|
|
146
|
+
if (changed.length === 0)
|
|
147
|
+
return null;
|
|
148
|
+
try {
|
|
149
|
+
execSync('git add -A', { cwd: this.workingDirectory, encoding: 'utf-8' });
|
|
150
|
+
execSync(`git commit -m "neuro: ${message}" --no-gpg-sign`, {
|
|
151
|
+
cwd: this.workingDirectory,
|
|
152
|
+
encoding: 'utf-8',
|
|
153
|
+
});
|
|
154
|
+
const hash = execSync('git rev-parse --short HEAD', {
|
|
155
|
+
cwd: this.workingDirectory,
|
|
156
|
+
encoding: 'utf-8',
|
|
157
|
+
}).trim();
|
|
158
|
+
return hash;
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// ---- Private ----
|
|
165
|
+
isGitRepo() {
|
|
166
|
+
try {
|
|
167
|
+
execSync('git rev-parse --is-inside-work-tree', {
|
|
168
|
+
cwd: this.workingDirectory,
|
|
169
|
+
encoding: 'utf-8',
|
|
170
|
+
stdio: 'pipe',
|
|
171
|
+
});
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
ensureShadowRepo() {
|
|
179
|
+
if (!existsSync(this.shadowRepo)) {
|
|
180
|
+
mkdirSync(this.shadowRepo, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
// Initialize shadow repo if needed
|
|
183
|
+
const gitDir = join(this.shadowRepo, '.git');
|
|
184
|
+
if (!existsSync(gitDir)) {
|
|
185
|
+
try {
|
|
186
|
+
execSync('git init', { cwd: this.shadowRepo, encoding: 'utf-8' });
|
|
187
|
+
}
|
|
188
|
+
catch { }
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
saveShadowSnapshot(checkpoint) {
|
|
192
|
+
const snapshotDir = join(this.shadowRepo, checkpoint.id);
|
|
193
|
+
if (!existsSync(snapshotDir)) {
|
|
194
|
+
mkdirSync(snapshotDir, { recursive: true });
|
|
195
|
+
}
|
|
196
|
+
// Save checkpoint metadata
|
|
197
|
+
writeFileSync(join(snapshotDir, 'meta.json'), JSON.stringify(checkpoint, null, 2), 'utf-8');
|
|
198
|
+
// Copy changed files to snapshot
|
|
199
|
+
for (const file of checkpoint.files) {
|
|
200
|
+
const src = join(this.workingDirectory, file);
|
|
201
|
+
const dst = join(snapshotDir, 'files', file);
|
|
202
|
+
if (existsSync(src)) {
|
|
203
|
+
try {
|
|
204
|
+
mkdirSync(join(dst, '..'), { recursive: true });
|
|
205
|
+
const { copyFileSync } = require('fs');
|
|
206
|
+
copyFileSync(src, dst);
|
|
207
|
+
}
|
|
208
|
+
catch { }
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
restoreShadowSnapshot(checkpoint) {
|
|
213
|
+
const snapshotDir = join(this.shadowRepo, checkpoint.id, 'files');
|
|
214
|
+
if (!existsSync(snapshotDir))
|
|
215
|
+
return false;
|
|
216
|
+
for (const file of checkpoint.files) {
|
|
217
|
+
const src = join(snapshotDir, file);
|
|
218
|
+
const dst = join(this.workingDirectory, file);
|
|
219
|
+
if (existsSync(src)) {
|
|
220
|
+
try {
|
|
221
|
+
const { copyFileSync } = require('fs');
|
|
222
|
+
mkdirSync(join(dst, '..'), { recursive: true });
|
|
223
|
+
copyFileSync(src, dst);
|
|
224
|
+
}
|
|
225
|
+
catch { }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
hashString(str) {
|
|
231
|
+
let hash = 0;
|
|
232
|
+
for (let i = 0; i < str.length; i++) {
|
|
233
|
+
const char = str.charCodeAt(i);
|
|
234
|
+
hash = ((hash << 5) - hash) + char;
|
|
235
|
+
hash |= 0;
|
|
236
|
+
}
|
|
237
|
+
return Math.abs(hash).toString(36);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=git-checkpoint.js.map
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export interface NeuroMdLayer {
|
|
2
|
+
scope: 'global' | 'user' | 'project' | 'local' | 'directory';
|
|
3
|
+
path: string;
|
|
4
|
+
content: string;
|
|
5
|
+
size: number;
|
|
6
|
+
}
|
|
7
|
+
export declare class NeuroMdSystem {
|
|
8
|
+
private workingDirectory;
|
|
9
|
+
private layers;
|
|
10
|
+
private watchers;
|
|
11
|
+
private onChange?;
|
|
12
|
+
constructor(workingDirectory: string, onChange?: () => void);
|
|
13
|
+
/**
|
|
14
|
+
* Load all NEURO.md layers in priority order
|
|
15
|
+
* Global → User → Project → Local → Directory
|
|
16
|
+
*/
|
|
17
|
+
load(): NeuroMdLayer[];
|
|
18
|
+
/**
|
|
19
|
+
* Get the combined context from all NEURO.md layers
|
|
20
|
+
*/
|
|
21
|
+
getCombinedContext(): string;
|
|
22
|
+
/**
|
|
23
|
+
* Get total size of all layers
|
|
24
|
+
*/
|
|
25
|
+
getTotalSize(): number;
|
|
26
|
+
/**
|
|
27
|
+
* Initialize a new NEURO.md in the project
|
|
28
|
+
*/
|
|
29
|
+
initProject(): string;
|
|
30
|
+
/**
|
|
31
|
+
* Watch NEURO.md files for changes
|
|
32
|
+
*/
|
|
33
|
+
startWatching(): void;
|
|
34
|
+
/**
|
|
35
|
+
* Stop watching NEURO.md files
|
|
36
|
+
*/
|
|
37
|
+
stopWatching(): void;
|
|
38
|
+
private tryLoadLayer;
|
|
39
|
+
private loadRulesDirectory;
|
|
40
|
+
/**
|
|
41
|
+
* Process @import references in NEURO.md
|
|
42
|
+
* e.g. @path/to/file.md → injects file contents
|
|
43
|
+
*/
|
|
44
|
+
private processImports;
|
|
45
|
+
private detectTechStack;
|
|
46
|
+
private generateStarterMd;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=neuro-md.d.ts.map
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// NeuroCLI - NEURO.md Persistent Context System
|
|
3
|
+
// Hierarchical project instructions (like CLAUDE.md / GEMINI.md)
|
|
4
|
+
// ============================================================
|
|
5
|
+
import { readFileSync, existsSync, watchFile, unwatchFile } from 'fs';
|
|
6
|
+
import { join } from 'path';
|
|
7
|
+
import { homedir } from 'os';
|
|
8
|
+
export class NeuroMdSystem {
|
|
9
|
+
workingDirectory;
|
|
10
|
+
layers = [];
|
|
11
|
+
watchers = [];
|
|
12
|
+
onChange;
|
|
13
|
+
constructor(workingDirectory, onChange) {
|
|
14
|
+
this.workingDirectory = workingDirectory;
|
|
15
|
+
this.onChange = onChange;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Load all NEURO.md layers in priority order
|
|
19
|
+
* Global → User → Project → Local → Directory
|
|
20
|
+
*/
|
|
21
|
+
load() {
|
|
22
|
+
this.layers = [];
|
|
23
|
+
// 1. Global: ~/.neuro/NEURO.md
|
|
24
|
+
this.tryLoadLayer('global', join(homedir(), '.neuro', 'NEURO.md'));
|
|
25
|
+
// 2. User: ~/.neuro/NEURO.md (same as global for now)
|
|
26
|
+
// Already loaded above
|
|
27
|
+
// 3. Project: NEURO.md in project root
|
|
28
|
+
this.tryLoadLayer('project', join(this.workingDirectory, 'NEURO.md'));
|
|
29
|
+
// 4. Project (alt): .neuro/NEURO.md
|
|
30
|
+
this.tryLoadLayer('project', join(this.workingDirectory, '.neuro', 'NEURO.md'));
|
|
31
|
+
// 5. Local: NEURO.local.md (gitignored)
|
|
32
|
+
this.tryLoadLayer('local', join(this.workingDirectory, 'NEURO.local.md'));
|
|
33
|
+
// 6. Rules: .neuro/rules/*.md
|
|
34
|
+
this.loadRulesDirectory();
|
|
35
|
+
return this.layers;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get the combined context from all NEURO.md layers
|
|
39
|
+
*/
|
|
40
|
+
getCombinedContext() {
|
|
41
|
+
if (this.layers.length === 0)
|
|
42
|
+
return '';
|
|
43
|
+
const parts = [];
|
|
44
|
+
parts.push('## Project Context (NEURO.md)\n');
|
|
45
|
+
for (const layer of this.layers) {
|
|
46
|
+
parts.push(`### [${layer.scope}] ${layer.path}\n${layer.content}\n`);
|
|
47
|
+
}
|
|
48
|
+
return parts.join('\n');
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Get total size of all layers
|
|
52
|
+
*/
|
|
53
|
+
getTotalSize() {
|
|
54
|
+
return this.layers.reduce((sum, l) => sum + l.size, 0);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Initialize a new NEURO.md in the project
|
|
58
|
+
*/
|
|
59
|
+
initProject() {
|
|
60
|
+
const { writeFileSync } = require('fs');
|
|
61
|
+
const projectMd = join(this.workingDirectory, 'NEURO.md');
|
|
62
|
+
if (existsSync(projectMd)) {
|
|
63
|
+
return `NEURO.md already exists at ${projectMd}`;
|
|
64
|
+
}
|
|
65
|
+
// Auto-detect tech stack
|
|
66
|
+
const techStack = this.detectTechStack();
|
|
67
|
+
const content = this.generateStarterMd(techStack);
|
|
68
|
+
writeFileSync(projectMd, content, 'utf-8');
|
|
69
|
+
return `Created NEURO.md at ${projectMd}`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Watch NEURO.md files for changes
|
|
73
|
+
*/
|
|
74
|
+
startWatching() {
|
|
75
|
+
this.stopWatching();
|
|
76
|
+
const paths = [
|
|
77
|
+
join(homedir(), '.neuro', 'NEURO.md'),
|
|
78
|
+
join(this.workingDirectory, 'NEURO.md'),
|
|
79
|
+
join(this.workingDirectory, '.neuro', 'NEURO.md'),
|
|
80
|
+
join(this.workingDirectory, 'NEURO.local.md'),
|
|
81
|
+
];
|
|
82
|
+
for (const path of paths) {
|
|
83
|
+
if (existsSync(path)) {
|
|
84
|
+
try {
|
|
85
|
+
watchFile(path, { interval: 5000 }, () => {
|
|
86
|
+
this.load(); // Reload on change
|
|
87
|
+
this.onChange?.();
|
|
88
|
+
});
|
|
89
|
+
this.watchers.push({ path, close: () => unwatchFile(path) });
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Stop watching NEURO.md files
|
|
97
|
+
*/
|
|
98
|
+
stopWatching() {
|
|
99
|
+
for (const w of this.watchers) {
|
|
100
|
+
w.close();
|
|
101
|
+
}
|
|
102
|
+
this.watchers = [];
|
|
103
|
+
}
|
|
104
|
+
// ---- Private Methods ----
|
|
105
|
+
tryLoadLayer(scope, path) {
|
|
106
|
+
if (!existsSync(path))
|
|
107
|
+
return;
|
|
108
|
+
try {
|
|
109
|
+
const content = readFileSync(path, 'utf-8');
|
|
110
|
+
// Process @import references
|
|
111
|
+
const processed = this.processImports(content, path);
|
|
112
|
+
this.layers.push({ scope, path, content: processed, size: content.length });
|
|
113
|
+
}
|
|
114
|
+
catch { }
|
|
115
|
+
}
|
|
116
|
+
loadRulesDirectory() {
|
|
117
|
+
const rulesDir = join(this.workingDirectory, '.neuro', 'rules');
|
|
118
|
+
if (!existsSync(rulesDir))
|
|
119
|
+
return;
|
|
120
|
+
try {
|
|
121
|
+
const { readdirSync } = require('fs');
|
|
122
|
+
const files = readdirSync(rulesDir).filter((f) => f.endsWith('.md'));
|
|
123
|
+
for (const file of files) {
|
|
124
|
+
this.tryLoadLayer('directory', join(rulesDir, file));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
catch { }
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Process @import references in NEURO.md
|
|
131
|
+
* e.g. @path/to/file.md → injects file contents
|
|
132
|
+
*/
|
|
133
|
+
processImports(content, basePath) {
|
|
134
|
+
const importRegex = /@([^\s\n]+\.(?:md|txt|json5?|yaml|yml))/g;
|
|
135
|
+
return content.replace(importRegex, (match, refPath) => {
|
|
136
|
+
const fullPath = refPath.startsWith('/')
|
|
137
|
+
? refPath
|
|
138
|
+
: join(basePath, '..', refPath);
|
|
139
|
+
if (existsSync(fullPath)) {
|
|
140
|
+
try {
|
|
141
|
+
return readFileSync(fullPath, 'utf-8');
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
return match;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return match;
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
detectTechStack() {
|
|
151
|
+
const stack = [];
|
|
152
|
+
const checks = [
|
|
153
|
+
{ file: 'package.json', tech: 'Node.js/JavaScript' },
|
|
154
|
+
{ file: 'tsconfig.json', tech: 'TypeScript' },
|
|
155
|
+
{ file: 'pyproject.toml', tech: 'Python' },
|
|
156
|
+
{ file: 'Cargo.toml', tech: 'Rust' },
|
|
157
|
+
{ file: 'go.mod', tech: 'Go' },
|
|
158
|
+
{ file: 'pom.xml', tech: 'Java/Maven' },
|
|
159
|
+
{ file: 'build.gradle', tech: 'Java/Gradle' },
|
|
160
|
+
{ file: 'Dockerfile', tech: 'Docker' },
|
|
161
|
+
{ file: 'docker-compose.yml', tech: 'Docker Compose' },
|
|
162
|
+
{ file: '.github/workflows', tech: 'GitHub Actions' },
|
|
163
|
+
{ file: 'Gemfile', tech: 'Ruby' },
|
|
164
|
+
{ file: 'mix.exs', tech: 'Elixir' },
|
|
165
|
+
{ file: 'composer.json', tech: 'PHP' },
|
|
166
|
+
];
|
|
167
|
+
for (const { file, tech } of checks) {
|
|
168
|
+
if (existsSync(join(this.workingDirectory, file))) {
|
|
169
|
+
stack.push(tech);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return stack;
|
|
173
|
+
}
|
|
174
|
+
generateStarterMd(techStack) {
|
|
175
|
+
return `# NeuroCLI Project Context
|
|
176
|
+
|
|
177
|
+
## Tech Stack
|
|
178
|
+
${techStack.map(t => `- ${t}`).join('\n') || '- Auto-detected on first run'}
|
|
179
|
+
|
|
180
|
+
## Project Structure
|
|
181
|
+
<!-- Describe your project structure here -->
|
|
182
|
+
|
|
183
|
+
## Conventions
|
|
184
|
+
<!-- Add your coding conventions and preferences -->
|
|
185
|
+
- Follow existing code style
|
|
186
|
+
- Write tests for new features
|
|
187
|
+
- Use meaningful variable names
|
|
188
|
+
|
|
189
|
+
## Common Commands
|
|
190
|
+
<!-- Add frequently used commands -->
|
|
191
|
+
\`\`\`bash
|
|
192
|
+
# Build
|
|
193
|
+
# Test
|
|
194
|
+
# Deploy
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
## Important Notes
|
|
198
|
+
<!-- Any important context for the AI assistant -->
|
|
199
|
+
`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
//# sourceMappingURL=neuro-md.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
export interface IgnoreRule {
|
|
2
|
+
pattern: string;
|
|
3
|
+
negated: boolean;
|
|
4
|
+
regex: RegExp;
|
|
5
|
+
source: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class NeuroIgnore {
|
|
8
|
+
private rules;
|
|
9
|
+
private cache;
|
|
10
|
+
private projectRoot;
|
|
11
|
+
private loaded;
|
|
12
|
+
constructor(projectRoot: string);
|
|
13
|
+
/**
|
|
14
|
+
* Load ignore rules from all sources:
|
|
15
|
+
* 1. Default patterns
|
|
16
|
+
* 2. ~/.neuro/ignore (global user rules)
|
|
17
|
+
* 3. .neuroignore (project-root rules)
|
|
18
|
+
*/
|
|
19
|
+
load(): void;
|
|
20
|
+
/**
|
|
21
|
+
* Check whether a given file path should be ignored.
|
|
22
|
+
* The path may be absolute or relative to projectRoot.
|
|
23
|
+
*
|
|
24
|
+
* Evaluation order matters: later rules override earlier ones.
|
|
25
|
+
* Negated patterns (prefixed with !) un-ignore previously matched paths.
|
|
26
|
+
*/
|
|
27
|
+
isIgnored(filePath: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Filter an array of paths, removing those that are ignored.
|
|
30
|
+
*/
|
|
31
|
+
filterPaths(paths: string[]): string[];
|
|
32
|
+
/**
|
|
33
|
+
* Dynamically add a rule at runtime.
|
|
34
|
+
*/
|
|
35
|
+
addRule(pattern: string, source?: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Remove a rule by its original pattern string.
|
|
38
|
+
* Returns true if a rule was found and removed.
|
|
39
|
+
*/
|
|
40
|
+
removeRule(pattern: string): boolean;
|
|
41
|
+
/**
|
|
42
|
+
* Return a shallow copy of the current rules list.
|
|
43
|
+
*/
|
|
44
|
+
getRules(): IgnoreRule[];
|
|
45
|
+
/**
|
|
46
|
+
* Clear the result cache.
|
|
47
|
+
*/
|
|
48
|
+
clearCache(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Print all active rules to stdout (useful for debugging).
|
|
51
|
+
*/
|
|
52
|
+
printRules(): void;
|
|
53
|
+
static readonly DEFAULT_IGNORED: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Load rules from a single file. Lines starting with # are comments.
|
|
56
|
+
* Blank lines are skipped. Trailing whitespace is trimmed.
|
|
57
|
+
*/
|
|
58
|
+
private loadFromFile;
|
|
59
|
+
/**
|
|
60
|
+
* Populate the default ignore rules.
|
|
61
|
+
*/
|
|
62
|
+
private loadDefaultRules;
|
|
63
|
+
/**
|
|
64
|
+
* Convert a gitignore-style glob pattern into a RegExp.
|
|
65
|
+
*
|
|
66
|
+
* Supported features:
|
|
67
|
+
* * matches anything except /
|
|
68
|
+
* ** matches anything including /
|
|
69
|
+
* ? matches any single character except /
|
|
70
|
+
* [abc] character class
|
|
71
|
+
* [a-z] character range
|
|
72
|
+
* {a,b} brace expansion (alternation)
|
|
73
|
+
* !prefix negation (handled separately before this method)
|
|
74
|
+
*
|
|
75
|
+
* A trailing / means the pattern only matches directories; we keep
|
|
76
|
+
* the regex flexible enough to match both for simplicity, but strip
|
|
77
|
+
* the trailing slash indicator.
|
|
78
|
+
*/
|
|
79
|
+
private patternToRegex;
|
|
80
|
+
/**
|
|
81
|
+
* Escape a string for use inside a RegExp.
|
|
82
|
+
*/
|
|
83
|
+
private escapeRegex;
|
|
84
|
+
/**
|
|
85
|
+
* Ensure rules have been loaded before answering queries.
|
|
86
|
+
*/
|
|
87
|
+
private ensureLoaded;
|
|
88
|
+
/**
|
|
89
|
+
* Convert a file path to a relative posix-style path from projectRoot.
|
|
90
|
+
* - Absolute paths are made relative to projectRoot.
|
|
91
|
+
* - Backslashes (Windows) are converted to forward slashes.
|
|
92
|
+
* - Leading ./ is stripped.
|
|
93
|
+
*/
|
|
94
|
+
private toRelativePosix;
|
|
95
|
+
/**
|
|
96
|
+
* Auto-detect common directories in the project root that should
|
|
97
|
+
* typically be ignored (e.g. a large "vendor" or "dist" directory
|
|
98
|
+
* that was not already covered by default patterns).
|
|
99
|
+
*/
|
|
100
|
+
private autoDetectIgnorableDirs;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=neuroignore.d.ts.map
|