neuronlayer 0.1.9 → 0.2.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.
Potentially problematic release.
This version of neuronlayer might be problematic. Click here for more details.
- package/README.md +3 -2
- package/dist/index.js +172 -90
- package/dist/index.js.map +7 -0
- package/package.json +6 -1
- package/esbuild.config.js +0 -26
- package/src/cli/commands.ts +0 -573
- package/src/core/adr-exporter.ts +0 -253
- package/src/core/architecture/architecture-enforcement.ts +0 -228
- package/src/core/architecture/duplicate-detector.ts +0 -288
- package/src/core/architecture/index.ts +0 -6
- package/src/core/architecture/pattern-learner.ts +0 -306
- package/src/core/architecture/pattern-library.ts +0 -403
- package/src/core/architecture/pattern-validator.ts +0 -324
- package/src/core/change-intelligence/bug-correlator.ts +0 -544
- package/src/core/change-intelligence/change-intelligence.ts +0 -264
- package/src/core/change-intelligence/change-tracker.ts +0 -334
- package/src/core/change-intelligence/fix-suggester.ts +0 -340
- package/src/core/change-intelligence/index.ts +0 -5
- package/src/core/code-verifier.ts +0 -843
- package/src/core/confidence/confidence-scorer.ts +0 -251
- package/src/core/confidence/conflict-checker.ts +0 -289
- package/src/core/confidence/index.ts +0 -5
- package/src/core/confidence/source-tracker.ts +0 -263
- package/src/core/confidence/warning-detector.ts +0 -241
- package/src/core/context-rot/compaction.ts +0 -284
- package/src/core/context-rot/context-health.ts +0 -243
- package/src/core/context-rot/context-rot-prevention.ts +0 -213
- package/src/core/context-rot/critical-context.ts +0 -221
- package/src/core/context-rot/drift-detector.ts +0 -255
- package/src/core/context-rot/index.ts +0 -7
- package/src/core/context.ts +0 -263
- package/src/core/decision-extractor.ts +0 -339
- package/src/core/decisions.ts +0 -69
- package/src/core/deja-vu.ts +0 -421
- package/src/core/engine.ts +0 -1646
- package/src/core/feature-context.ts +0 -726
- package/src/core/ghost-mode.ts +0 -465
- package/src/core/learning.ts +0 -519
- package/src/core/living-docs/activity-tracker.ts +0 -296
- package/src/core/living-docs/architecture-generator.ts +0 -428
- package/src/core/living-docs/changelog-generator.ts +0 -348
- package/src/core/living-docs/component-generator.ts +0 -230
- package/src/core/living-docs/doc-engine.ts +0 -134
- package/src/core/living-docs/doc-validator.ts +0 -282
- package/src/core/living-docs/index.ts +0 -8
- package/src/core/project-manager.ts +0 -301
- package/src/core/refresh/activity-gate.ts +0 -256
- package/src/core/refresh/git-staleness-checker.ts +0 -108
- package/src/core/refresh/index.ts +0 -27
- package/src/core/summarizer.ts +0 -290
- package/src/core/test-awareness/change-validator.ts +0 -499
- package/src/core/test-awareness/index.ts +0 -5
- package/src/index.ts +0 -90
- package/src/indexing/ast.ts +0 -868
- package/src/indexing/embeddings.ts +0 -85
- package/src/indexing/indexer.ts +0 -270
- package/src/indexing/watcher.ts +0 -78
- package/src/server/gateways/aggregator.ts +0 -374
- package/src/server/gateways/index.ts +0 -473
- package/src/server/gateways/memory-ghost.ts +0 -343
- package/src/server/gateways/memory-query.ts +0 -452
- package/src/server/gateways/memory-record.ts +0 -346
- package/src/server/gateways/memory-review.ts +0 -410
- package/src/server/gateways/memory-status.ts +0 -517
- package/src/server/gateways/memory-verify.ts +0 -392
- package/src/server/gateways/router.ts +0 -434
- package/src/server/gateways/types.ts +0 -610
- package/src/server/http.ts +0 -228
- package/src/server/mcp.ts +0 -154
- package/src/server/resources.ts +0 -85
- package/src/server/tools.ts +0 -2460
- package/src/storage/database.ts +0 -271
- package/src/storage/tier1.ts +0 -135
- package/src/storage/tier2.ts +0 -972
- package/src/storage/tier3.ts +0 -123
- package/src/types/documentation.ts +0 -619
- package/src/types/index.ts +0 -222
- package/src/utils/config.ts +0 -194
- package/src/utils/files.ts +0 -117
- package/src/utils/time.ts +0 -37
- package/src/utils/tokens.ts +0 -52
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neuronlayer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server that gives AI assistants persistent understanding of your codebase",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"neuronlayer": "dist/index.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist/**",
|
|
12
|
+
"wasm/**"
|
|
13
|
+
],
|
|
10
14
|
"scripts": {
|
|
11
15
|
"build": "node esbuild.config.js",
|
|
12
16
|
"dev": "node --watch dist/index.js",
|
|
@@ -40,6 +44,7 @@
|
|
|
40
44
|
"better-sqlite3": "^11.8.0",
|
|
41
45
|
"chokidar": "^4.0.0",
|
|
42
46
|
"glob": "^13.0.0",
|
|
47
|
+
"tree-sitter-wasms": "^0.1.13",
|
|
43
48
|
"web-tree-sitter": "^0.24.0"
|
|
44
49
|
},
|
|
45
50
|
"devDependencies": {
|
package/esbuild.config.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { build } from 'esbuild';
|
|
2
|
-
|
|
3
|
-
const commonOptions = {
|
|
4
|
-
bundle: true,
|
|
5
|
-
platform: 'node',
|
|
6
|
-
target: 'node18',
|
|
7
|
-
format: 'esm',
|
|
8
|
-
sourcemap: true,
|
|
9
|
-
external: [
|
|
10
|
-
'better-sqlite3', // Native module
|
|
11
|
-
'@xenova/transformers', // Large, keep external
|
|
12
|
-
'chokidar', // Uses CommonJS require
|
|
13
|
-
'glob', // Keep external
|
|
14
|
-
'web-tree-sitter' // WASM module
|
|
15
|
-
]
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// Build main MCP server
|
|
19
|
-
await build({
|
|
20
|
-
...commonOptions,
|
|
21
|
-
entryPoints: ['src/index.ts'],
|
|
22
|
-
outfile: 'dist/index.js',
|
|
23
|
-
banner: { js: '#!/usr/bin/env node' },
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
console.log('Build complete!');
|
package/src/cli/commands.ts
DELETED
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
import { ProjectManager, type ProjectInfo } from '../core/project-manager.js';
|
|
2
|
-
import { ADRExporter } from '../core/adr-exporter.js';
|
|
3
|
-
import { initializeDatabase } from '../storage/database.js';
|
|
4
|
-
import { Tier2Storage } from '../storage/tier2.js';
|
|
5
|
-
import { join } from 'path';
|
|
6
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
7
|
-
import { homedir } from 'os';
|
|
8
|
-
|
|
9
|
-
const projectManager = new ProjectManager();
|
|
10
|
-
|
|
11
|
-
interface CommandResult {
|
|
12
|
-
success: boolean;
|
|
13
|
-
message: string;
|
|
14
|
-
data?: unknown;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
// List all projects
|
|
18
|
-
export function listProjects(): CommandResult {
|
|
19
|
-
const projects = projectManager.listProjects();
|
|
20
|
-
const activeProject = projectManager.getActiveProject();
|
|
21
|
-
|
|
22
|
-
if (projects.length === 0) {
|
|
23
|
-
return {
|
|
24
|
-
success: true,
|
|
25
|
-
message: 'No projects registered. Use "neuronlayer projects add <path>" to add one.'
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
const lines = ['Registered Projects:', ''];
|
|
30
|
-
for (const project of projects) {
|
|
31
|
-
const isActive = activeProject?.id === project.id ? ' (active)' : '';
|
|
32
|
-
lines.push(` ${project.name}${isActive}`);
|
|
33
|
-
lines.push(` ID: ${project.id}`);
|
|
34
|
-
lines.push(` Path: ${project.path}`);
|
|
35
|
-
lines.push(` Files: ${project.totalFiles}, Decisions: ${project.totalDecisions}`);
|
|
36
|
-
lines.push(` Languages: ${project.languages.join(', ') || 'N/A'}`);
|
|
37
|
-
lines.push('');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return {
|
|
41
|
-
success: true,
|
|
42
|
-
message: lines.join('\n'),
|
|
43
|
-
data: projects
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Add a project
|
|
48
|
-
export function addProject(projectPath: string): CommandResult {
|
|
49
|
-
try {
|
|
50
|
-
const projectInfo = projectManager.registerProject(projectPath);
|
|
51
|
-
projectManager.setActiveProject(projectInfo.id);
|
|
52
|
-
|
|
53
|
-
return {
|
|
54
|
-
success: true,
|
|
55
|
-
message: `Project "${projectInfo.name}" registered and set as active.\nID: ${projectInfo.id}\nData directory: ${projectInfo.dataDir}`,
|
|
56
|
-
data: projectInfo
|
|
57
|
-
};
|
|
58
|
-
} catch (error) {
|
|
59
|
-
return {
|
|
60
|
-
success: false,
|
|
61
|
-
message: `Failed to add project: ${error instanceof Error ? error.message : String(error)}`
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Remove a project
|
|
67
|
-
export function removeProject(projectId: string): CommandResult {
|
|
68
|
-
const project = projectManager.getProject(projectId);
|
|
69
|
-
|
|
70
|
-
if (!project) {
|
|
71
|
-
return {
|
|
72
|
-
success: false,
|
|
73
|
-
message: `Project not found: ${projectId}`
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const removed = projectManager.removeProject(projectId);
|
|
78
|
-
|
|
79
|
-
return {
|
|
80
|
-
success: removed,
|
|
81
|
-
message: removed
|
|
82
|
-
? `Project "${project.name}" removed from registry.`
|
|
83
|
-
: `Failed to remove project.`
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Switch to a project
|
|
88
|
-
export function switchProject(projectId: string): CommandResult {
|
|
89
|
-
const project = projectManager.getProject(projectId);
|
|
90
|
-
|
|
91
|
-
if (!project) {
|
|
92
|
-
return {
|
|
93
|
-
success: false,
|
|
94
|
-
message: `Project not found: ${projectId}`
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const switched = projectManager.setActiveProject(projectId);
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
success: switched,
|
|
102
|
-
message: switched
|
|
103
|
-
? `Switched to project: ${project.name}`
|
|
104
|
-
: `Failed to switch project.`
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Discover projects
|
|
109
|
-
export function discoverProjects(): CommandResult {
|
|
110
|
-
const discovered = projectManager.discoverProjects();
|
|
111
|
-
|
|
112
|
-
if (discovered.length === 0) {
|
|
113
|
-
return {
|
|
114
|
-
success: true,
|
|
115
|
-
message: 'No projects discovered in common locations.'
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const lines = [`Discovered ${discovered.length} potential projects:`, ''];
|
|
120
|
-
for (const path of discovered) {
|
|
121
|
-
const name = path.split(/[/\\]/).pop();
|
|
122
|
-
lines.push(` ${name}`);
|
|
123
|
-
lines.push(` ${path}`);
|
|
124
|
-
lines.push('');
|
|
125
|
-
}
|
|
126
|
-
lines.push('Use "neuronlayer projects add <path>" to register a project.');
|
|
127
|
-
|
|
128
|
-
return {
|
|
129
|
-
success: true,
|
|
130
|
-
message: lines.join('\n'),
|
|
131
|
-
data: discovered
|
|
132
|
-
};
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// Export decisions to ADR
|
|
136
|
-
export function exportDecisions(
|
|
137
|
-
projectPath?: string,
|
|
138
|
-
options: { outputDir?: string; format?: 'madr' | 'nygard' | 'simple' } = {}
|
|
139
|
-
): CommandResult {
|
|
140
|
-
// Determine project path
|
|
141
|
-
let targetPath = projectPath;
|
|
142
|
-
if (!targetPath) {
|
|
143
|
-
const activeProject = projectManager.getActiveProject();
|
|
144
|
-
if (!activeProject) {
|
|
145
|
-
return {
|
|
146
|
-
success: false,
|
|
147
|
-
message: 'No project specified and no active project. Use "neuronlayer projects switch <id>" first.'
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
targetPath = activeProject.path;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Get project info
|
|
154
|
-
const projectInfo = projectManager.getProjectByPath(targetPath);
|
|
155
|
-
if (!projectInfo) {
|
|
156
|
-
return {
|
|
157
|
-
success: false,
|
|
158
|
-
message: `Project not registered: ${targetPath}. Use "neuronlayer projects add ${targetPath}" first.`
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Open database and get decisions (check both new and old names)
|
|
163
|
-
let dbPath = join(projectInfo.dataDir, 'neuronlayer.db');
|
|
164
|
-
if (!existsSync(dbPath)) {
|
|
165
|
-
// Fall back to old name for backwards compatibility
|
|
166
|
-
const oldDbPath = join(projectInfo.dataDir, 'memorylayer.db');
|
|
167
|
-
if (existsSync(oldDbPath)) {
|
|
168
|
-
dbPath = oldDbPath;
|
|
169
|
-
} else {
|
|
170
|
-
return {
|
|
171
|
-
success: false,
|
|
172
|
-
message: `Project database not found. Has the project been indexed?`
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const db = initializeDatabase(dbPath);
|
|
178
|
-
const tier2 = new Tier2Storage(db);
|
|
179
|
-
const decisions = tier2.getAllDecisions();
|
|
180
|
-
db.close();
|
|
181
|
-
|
|
182
|
-
if (decisions.length === 0) {
|
|
183
|
-
return {
|
|
184
|
-
success: true,
|
|
185
|
-
message: 'No decisions to export.'
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// Export
|
|
190
|
-
const exporter = new ADRExporter(targetPath);
|
|
191
|
-
const exportedFiles = exporter.exportAllDecisions(decisions, {
|
|
192
|
-
outputDir: options.outputDir,
|
|
193
|
-
format: options.format,
|
|
194
|
-
includeIndex: true
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
success: true,
|
|
199
|
-
message: `Exported ${exportedFiles.length} ADR files to ${options.outputDir || join(targetPath, 'docs', 'decisions')}`,
|
|
200
|
-
data: exportedFiles
|
|
201
|
-
};
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// Show project info
|
|
205
|
-
export function showProject(projectId?: string): CommandResult {
|
|
206
|
-
let project: ProjectInfo | null;
|
|
207
|
-
|
|
208
|
-
if (projectId) {
|
|
209
|
-
project = projectManager.getProject(projectId);
|
|
210
|
-
} else {
|
|
211
|
-
project = projectManager.getActiveProject();
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
if (!project) {
|
|
215
|
-
return {
|
|
216
|
-
success: false,
|
|
217
|
-
message: projectId
|
|
218
|
-
? `Project not found: ${projectId}`
|
|
219
|
-
: 'No active project. Use "neuronlayer projects switch <id>" first.'
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const lines = [
|
|
224
|
-
`Project: ${project.name}`,
|
|
225
|
-
`ID: ${project.id}`,
|
|
226
|
-
`Path: ${project.path}`,
|
|
227
|
-
`Data Directory: ${project.dataDir}`,
|
|
228
|
-
`Files Indexed: ${project.totalFiles}`,
|
|
229
|
-
`Decisions: ${project.totalDecisions}`,
|
|
230
|
-
`Languages: ${project.languages.join(', ') || 'N/A'}`,
|
|
231
|
-
`Last Accessed: ${new Date(project.lastAccessed).toLocaleString()}`
|
|
232
|
-
];
|
|
233
|
-
|
|
234
|
-
return {
|
|
235
|
-
success: true,
|
|
236
|
-
message: lines.join('\n'),
|
|
237
|
-
data: project
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// Helper to configure an MCP client
|
|
242
|
-
function configureMCPClient(
|
|
243
|
-
clientName: string,
|
|
244
|
-
configPath: string,
|
|
245
|
-
serverName: string,
|
|
246
|
-
projectPath: string
|
|
247
|
-
): { success: boolean; message: string } {
|
|
248
|
-
let config: { mcpServers?: Record<string, unknown> } = { mcpServers: {} };
|
|
249
|
-
|
|
250
|
-
try {
|
|
251
|
-
if (existsSync(configPath)) {
|
|
252
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
253
|
-
config = JSON.parse(content);
|
|
254
|
-
} else {
|
|
255
|
-
// Create directory if needed
|
|
256
|
-
const sep = process.platform === 'win32' ? '\\' : '/';
|
|
257
|
-
const configDir = configPath.substring(0, configPath.lastIndexOf(sep));
|
|
258
|
-
mkdirSync(configDir, { recursive: true });
|
|
259
|
-
}
|
|
260
|
-
} catch {
|
|
261
|
-
// Config doesn't exist or is invalid, start fresh
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
if (!config.mcpServers) {
|
|
265
|
-
config.mcpServers = {};
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
config.mcpServers[serverName] = {
|
|
269
|
-
command: 'npx',
|
|
270
|
-
args: ['-y', 'neuronlayer', '--project', projectPath]
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
275
|
-
return { success: true, message: `${clientName}: ${configPath}` };
|
|
276
|
-
} catch (err) {
|
|
277
|
-
return { success: false, message: `${clientName}: Failed - ${err instanceof Error ? err.message : String(err)}` };
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// Helper to configure project-local .mcp.json for Claude Code
|
|
282
|
-
function configureProjectMCP(
|
|
283
|
-
configPath: string,
|
|
284
|
-
projectPath: string
|
|
285
|
-
): { success: boolean; message: string } {
|
|
286
|
-
let config: { mcpServers?: Record<string, unknown> } = { mcpServers: {} };
|
|
287
|
-
|
|
288
|
-
try {
|
|
289
|
-
if (existsSync(configPath)) {
|
|
290
|
-
const content = readFileSync(configPath, 'utf-8');
|
|
291
|
-
config = JSON.parse(content);
|
|
292
|
-
}
|
|
293
|
-
} catch {
|
|
294
|
-
// Config doesn't exist or is invalid, start fresh
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (!config.mcpServers) {
|
|
298
|
-
config.mcpServers = {};
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// Use simple name since this is project-specific
|
|
302
|
-
config.mcpServers['neuronlayer'] = {
|
|
303
|
-
command: 'npx',
|
|
304
|
-
args: ['-y', 'neuronlayer', '--project', '.']
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
try {
|
|
308
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
309
|
-
return { success: true, message: `Claude Code: ${configPath} (project-local)` };
|
|
310
|
-
} catch (err) {
|
|
311
|
-
return { success: false, message: `Claude Code: Failed - ${err instanceof Error ? err.message : String(err)}` };
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Initialize neuronlayer for current project + auto-configure Claude Desktop & OpenCode
|
|
316
|
-
export function initProject(projectPath?: string): CommandResult {
|
|
317
|
-
const targetPath = projectPath || process.cwd();
|
|
318
|
-
|
|
319
|
-
// 1. Register the project
|
|
320
|
-
const addResult = addProject(targetPath);
|
|
321
|
-
if (!addResult.success) {
|
|
322
|
-
return addResult;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
const projectInfo = addResult.data as ProjectInfo;
|
|
326
|
-
const serverName = `neuronlayer-${projectInfo.name.toLowerCase().replace(/[^a-z0-9]/g, '-')}`;
|
|
327
|
-
const platform = process.platform;
|
|
328
|
-
|
|
329
|
-
const configuredClients: string[] = [];
|
|
330
|
-
const failedClients: string[] = [];
|
|
331
|
-
|
|
332
|
-
// 2. Configure Claude Desktop
|
|
333
|
-
let claudeConfigPath: string;
|
|
334
|
-
if (platform === 'win32') {
|
|
335
|
-
claudeConfigPath = join(homedir(), 'AppData', 'Roaming', 'Claude', 'claude_desktop_config.json');
|
|
336
|
-
} else if (platform === 'darwin') {
|
|
337
|
-
claudeConfigPath = join(homedir(), 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json');
|
|
338
|
-
} else {
|
|
339
|
-
claudeConfigPath = join(homedir(), '.config', 'claude', 'claude_desktop_config.json');
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
const claudeResult = configureMCPClient('Claude Desktop', claudeConfigPath, serverName, targetPath);
|
|
343
|
-
if (claudeResult.success) {
|
|
344
|
-
configuredClients.push(claudeResult.message);
|
|
345
|
-
} else {
|
|
346
|
-
failedClients.push(claudeResult.message);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// 3. Configure OpenCode
|
|
350
|
-
const openCodeConfigPath = join(homedir(), '.opencode', 'config.json');
|
|
351
|
-
const openCodeResult = configureMCPClient('OpenCode', openCodeConfigPath, serverName, targetPath);
|
|
352
|
-
if (openCodeResult.success) {
|
|
353
|
-
configuredClients.push(openCodeResult.message);
|
|
354
|
-
} else {
|
|
355
|
-
failedClients.push(openCodeResult.message);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// 4. Configure Claude Code (CLI) - use project-local .mcp.json
|
|
359
|
-
// This ensures only the current project's NeuronLayer connects
|
|
360
|
-
const claudeCodeConfigPath = join(targetPath, '.mcp.json');
|
|
361
|
-
const claudeCodeResult = configureProjectMCP(claudeCodeConfigPath, targetPath);
|
|
362
|
-
if (claudeCodeResult.success) {
|
|
363
|
-
configuredClients.push(claudeCodeResult.message);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
// 5. Configure Cursor
|
|
367
|
-
let cursorConfigPath: string;
|
|
368
|
-
if (platform === 'win32') {
|
|
369
|
-
cursorConfigPath = join(homedir(), 'AppData', 'Roaming', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
370
|
-
} else if (platform === 'darwin') {
|
|
371
|
-
cursorConfigPath = join(homedir(), 'Library', 'Application Support', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
372
|
-
} else {
|
|
373
|
-
cursorConfigPath = join(homedir(), '.config', 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'mcp.json');
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
const cursorResult = configureMCPClient('Cursor', cursorConfigPath, serverName, targetPath);
|
|
377
|
-
if (cursorResult.success) {
|
|
378
|
-
configuredClients.push(cursorResult.message);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
// Build result message
|
|
382
|
-
let message = `
|
|
383
|
-
NeuronLayer initialized!
|
|
384
|
-
|
|
385
|
-
Project: ${projectInfo.name}
|
|
386
|
-
Path: ${targetPath}
|
|
387
|
-
Data: ${projectInfo.dataDir}
|
|
388
|
-
|
|
389
|
-
Configured MCP Clients:
|
|
390
|
-
${configuredClients.map(c => ' ✓ ' + c).join('\n')}
|
|
391
|
-
`;
|
|
392
|
-
|
|
393
|
-
if (failedClients.length > 0) {
|
|
394
|
-
message += `\nFailed:\n${failedClients.map(c => ' ✗ ' + c).join('\n')}`;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
message += `\n\nRestart your AI tools to activate.`;
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
success: true,
|
|
401
|
-
message: message.trim(),
|
|
402
|
-
data: { projectInfo, serverName, configuredClients }
|
|
403
|
-
};
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
// Print help
|
|
407
|
-
export function printHelp(): void {
|
|
408
|
-
console.log(`
|
|
409
|
-
NeuronLayer CLI - Code Intelligence for AI Coding Assistants
|
|
410
|
-
|
|
411
|
-
USAGE:
|
|
412
|
-
neuronlayer [command] [options]
|
|
413
|
-
|
|
414
|
-
COMMANDS:
|
|
415
|
-
init [path] Initialize project + auto-configure AI tools
|
|
416
|
-
serve [options] Start HTTP API server (for non-MCP tools)
|
|
417
|
-
(no command) Start MCP server
|
|
418
|
-
projects list List all registered projects
|
|
419
|
-
projects add <path> Add a project to the registry
|
|
420
|
-
projects remove <id> Remove a project from the registry
|
|
421
|
-
projects switch <id> Set a project as active
|
|
422
|
-
projects show [id] Show project details
|
|
423
|
-
projects discover Discover projects in common locations
|
|
424
|
-
export [options] Export decisions to ADR files
|
|
425
|
-
help Show this help message
|
|
426
|
-
|
|
427
|
-
OPTIONS:
|
|
428
|
-
--project, -p <path> Path to the project directory
|
|
429
|
-
--port <number> Port for HTTP server (default: 3333)
|
|
430
|
-
--output, -o <dir> Output directory for exports
|
|
431
|
-
--format <type> ADR format: madr, nygard, simple
|
|
432
|
-
|
|
433
|
-
EXAMPLES:
|
|
434
|
-
# Quick setup (auto-configures Claude Desktop)
|
|
435
|
-
cd /path/to/project
|
|
436
|
-
neuronlayer init
|
|
437
|
-
|
|
438
|
-
# Start MCP server
|
|
439
|
-
neuronlayer --project /path/to/project
|
|
440
|
-
|
|
441
|
-
# List all projects
|
|
442
|
-
neuronlayer projects list
|
|
443
|
-
|
|
444
|
-
# Add a new project
|
|
445
|
-
neuronlayer projects add /path/to/my-project
|
|
446
|
-
|
|
447
|
-
# Switch active project
|
|
448
|
-
neuronlayer projects switch abc123
|
|
449
|
-
|
|
450
|
-
# Export decisions to ADR files
|
|
451
|
-
neuronlayer export --format madr
|
|
452
|
-
|
|
453
|
-
# Discover projects
|
|
454
|
-
neuronlayer projects discover
|
|
455
|
-
|
|
456
|
-
# Start HTTP API server (for tools without MCP support)
|
|
457
|
-
neuronlayer serve --project /path/to/project
|
|
458
|
-
neuronlayer serve --port 8080
|
|
459
|
-
|
|
460
|
-
For more information, visit: https://github.com/abhisavakar/neuronlayer
|
|
461
|
-
`);
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// Parse and execute CLI commands
|
|
465
|
-
export function executeCLI(args: string[]): void {
|
|
466
|
-
const command = args[0];
|
|
467
|
-
const subcommand = args[1];
|
|
468
|
-
|
|
469
|
-
switch (command) {
|
|
470
|
-
case 'help':
|
|
471
|
-
case '--help':
|
|
472
|
-
case '-h':
|
|
473
|
-
printHelp();
|
|
474
|
-
break;
|
|
475
|
-
|
|
476
|
-
case 'init': {
|
|
477
|
-
const path = args[1];
|
|
478
|
-
const result = initProject(path);
|
|
479
|
-
console.log(result.message);
|
|
480
|
-
if (!result.success) process.exit(1);
|
|
481
|
-
break;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
case 'projects': {
|
|
485
|
-
switch (subcommand) {
|
|
486
|
-
case 'list':
|
|
487
|
-
console.log(listProjects().message);
|
|
488
|
-
break;
|
|
489
|
-
case 'add': {
|
|
490
|
-
const path = args[2];
|
|
491
|
-
if (!path) {
|
|
492
|
-
console.error('Error: Project path required.');
|
|
493
|
-
console.error('Usage: neuronlayer projects add <path>');
|
|
494
|
-
process.exit(1);
|
|
495
|
-
}
|
|
496
|
-
const result = addProject(path);
|
|
497
|
-
console.log(result.message);
|
|
498
|
-
if (!result.success) process.exit(1);
|
|
499
|
-
break;
|
|
500
|
-
}
|
|
501
|
-
case 'remove': {
|
|
502
|
-
const id = args[2];
|
|
503
|
-
if (!id) {
|
|
504
|
-
console.error('Error: Project ID required.');
|
|
505
|
-
console.error('Usage: neuronlayer projects remove <id>');
|
|
506
|
-
process.exit(1);
|
|
507
|
-
}
|
|
508
|
-
const result = removeProject(id);
|
|
509
|
-
console.log(result.message);
|
|
510
|
-
if (!result.success) process.exit(1);
|
|
511
|
-
break;
|
|
512
|
-
}
|
|
513
|
-
case 'switch': {
|
|
514
|
-
const id = args[2];
|
|
515
|
-
if (!id) {
|
|
516
|
-
console.error('Error: Project ID required.');
|
|
517
|
-
console.error('Usage: neuronlayer projects switch <id>');
|
|
518
|
-
process.exit(1);
|
|
519
|
-
}
|
|
520
|
-
const result = switchProject(id);
|
|
521
|
-
console.log(result.message);
|
|
522
|
-
if (!result.success) process.exit(1);
|
|
523
|
-
break;
|
|
524
|
-
}
|
|
525
|
-
case 'show': {
|
|
526
|
-
const id = args[2];
|
|
527
|
-
const result = showProject(id);
|
|
528
|
-
console.log(result.message);
|
|
529
|
-
if (!result.success) process.exit(1);
|
|
530
|
-
break;
|
|
531
|
-
}
|
|
532
|
-
case 'discover':
|
|
533
|
-
console.log(discoverProjects().message);
|
|
534
|
-
break;
|
|
535
|
-
default:
|
|
536
|
-
console.error(`Unknown subcommand: ${subcommand}`);
|
|
537
|
-
console.error('Available: list, add, remove, switch, show, discover');
|
|
538
|
-
process.exit(1);
|
|
539
|
-
}
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
case 'export': {
|
|
544
|
-
// Parse export options
|
|
545
|
-
let outputDir: string | undefined;
|
|
546
|
-
let format: 'madr' | 'nygard' | 'simple' | undefined;
|
|
547
|
-
|
|
548
|
-
for (let i = 1; i < args.length; i++) {
|
|
549
|
-
const arg = args[i];
|
|
550
|
-
const nextArg = args[i + 1];
|
|
551
|
-
if ((arg === '--output' || arg === '-o') && nextArg) {
|
|
552
|
-
outputDir = nextArg;
|
|
553
|
-
i++;
|
|
554
|
-
} else if (arg === '--format' && nextArg) {
|
|
555
|
-
format = nextArg as 'madr' | 'nygard' | 'simple';
|
|
556
|
-
i++;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
const result = exportDecisions(undefined, { outputDir, format });
|
|
561
|
-
console.log(result.message);
|
|
562
|
-
if (!result.success) process.exit(1);
|
|
563
|
-
break;
|
|
564
|
-
}
|
|
565
|
-
|
|
566
|
-
default:
|
|
567
|
-
// If no command matches, it might be the default MCP server mode
|
|
568
|
-
// Return without handling - let main() handle it
|
|
569
|
-
return;
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
process.exit(0);
|
|
573
|
-
}
|