neuronlayer 0.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 (78) hide show
  1. package/CONTRIBUTING.md +127 -0
  2. package/LICENSE +21 -0
  3. package/README.md +305 -0
  4. package/dist/index.js +38016 -0
  5. package/esbuild.config.js +26 -0
  6. package/package.json +63 -0
  7. package/src/cli/commands.ts +382 -0
  8. package/src/core/adr-exporter.ts +253 -0
  9. package/src/core/architecture/architecture-enforcement.ts +228 -0
  10. package/src/core/architecture/duplicate-detector.ts +288 -0
  11. package/src/core/architecture/index.ts +6 -0
  12. package/src/core/architecture/pattern-learner.ts +306 -0
  13. package/src/core/architecture/pattern-library.ts +403 -0
  14. package/src/core/architecture/pattern-validator.ts +324 -0
  15. package/src/core/change-intelligence/bug-correlator.ts +444 -0
  16. package/src/core/change-intelligence/change-intelligence.ts +221 -0
  17. package/src/core/change-intelligence/change-tracker.ts +334 -0
  18. package/src/core/change-intelligence/fix-suggester.ts +340 -0
  19. package/src/core/change-intelligence/index.ts +5 -0
  20. package/src/core/code-verifier.ts +843 -0
  21. package/src/core/confidence/confidence-scorer.ts +251 -0
  22. package/src/core/confidence/conflict-checker.ts +289 -0
  23. package/src/core/confidence/index.ts +5 -0
  24. package/src/core/confidence/source-tracker.ts +263 -0
  25. package/src/core/confidence/warning-detector.ts +241 -0
  26. package/src/core/context-rot/compaction.ts +284 -0
  27. package/src/core/context-rot/context-health.ts +243 -0
  28. package/src/core/context-rot/context-rot-prevention.ts +213 -0
  29. package/src/core/context-rot/critical-context.ts +221 -0
  30. package/src/core/context-rot/drift-detector.ts +255 -0
  31. package/src/core/context-rot/index.ts +7 -0
  32. package/src/core/context.ts +263 -0
  33. package/src/core/decision-extractor.ts +339 -0
  34. package/src/core/decisions.ts +69 -0
  35. package/src/core/deja-vu.ts +421 -0
  36. package/src/core/engine.ts +1455 -0
  37. package/src/core/feature-context.ts +726 -0
  38. package/src/core/ghost-mode.ts +412 -0
  39. package/src/core/learning.ts +485 -0
  40. package/src/core/living-docs/activity-tracker.ts +296 -0
  41. package/src/core/living-docs/architecture-generator.ts +428 -0
  42. package/src/core/living-docs/changelog-generator.ts +348 -0
  43. package/src/core/living-docs/component-generator.ts +230 -0
  44. package/src/core/living-docs/doc-engine.ts +110 -0
  45. package/src/core/living-docs/doc-validator.ts +282 -0
  46. package/src/core/living-docs/index.ts +8 -0
  47. package/src/core/project-manager.ts +297 -0
  48. package/src/core/summarizer.ts +267 -0
  49. package/src/core/test-awareness/change-validator.ts +499 -0
  50. package/src/core/test-awareness/index.ts +5 -0
  51. package/src/index.ts +49 -0
  52. package/src/indexing/ast.ts +563 -0
  53. package/src/indexing/embeddings.ts +85 -0
  54. package/src/indexing/indexer.ts +245 -0
  55. package/src/indexing/watcher.ts +78 -0
  56. package/src/server/gateways/aggregator.ts +374 -0
  57. package/src/server/gateways/index.ts +473 -0
  58. package/src/server/gateways/memory-ghost.ts +343 -0
  59. package/src/server/gateways/memory-query.ts +452 -0
  60. package/src/server/gateways/memory-record.ts +346 -0
  61. package/src/server/gateways/memory-review.ts +410 -0
  62. package/src/server/gateways/memory-status.ts +517 -0
  63. package/src/server/gateways/memory-verify.ts +392 -0
  64. package/src/server/gateways/router.ts +434 -0
  65. package/src/server/gateways/types.ts +610 -0
  66. package/src/server/mcp.ts +154 -0
  67. package/src/server/resources.ts +85 -0
  68. package/src/server/tools.ts +2261 -0
  69. package/src/storage/database.ts +262 -0
  70. package/src/storage/tier1.ts +135 -0
  71. package/src/storage/tier2.ts +764 -0
  72. package/src/storage/tier3.ts +123 -0
  73. package/src/types/documentation.ts +619 -0
  74. package/src/types/index.ts +222 -0
  75. package/src/utils/config.ts +193 -0
  76. package/src/utils/files.ts +117 -0
  77. package/src/utils/time.ts +37 -0
  78. package/src/utils/tokens.ts +52 -0
@@ -0,0 +1,499 @@
1
+ import type { TestInfo, ChangeAnalysis, TestValidationResult, PredictedFailure, Assertion } from '../../types/documentation.js';
2
+ import type { TestIndexer } from './test-indexer.js';
3
+ import type { Tier2Storage } from '../../storage/tier2.js';
4
+
5
+ interface FunctionChange {
6
+ name: string;
7
+ changeType: 'added' | 'removed' | 'modified' | 'signature_changed';
8
+ oldSignature?: string;
9
+ newSignature?: string;
10
+ }
11
+
12
+ interface ParsedChange {
13
+ addedLines: string[];
14
+ removedLines: string[];
15
+ modifiedFunctions: FunctionChange[];
16
+ exportChanges: { added: string[]; removed: string[] };
17
+ }
18
+
19
+ export class ChangeValidator {
20
+ private testIndexer: TestIndexer;
21
+ private tier2: Tier2Storage;
22
+
23
+ constructor(testIndexer: TestIndexer, tier2: Tier2Storage) {
24
+ this.testIndexer = testIndexer;
25
+ this.tier2 = tier2;
26
+ }
27
+
28
+ analyzeChange(code: string, file: string): ChangeAnalysis {
29
+ // Get existing file content from tier2
30
+ const existingFile = this.tier2.getFile(file);
31
+ const existingContent = existingFile?.preview || '';
32
+
33
+ // Parse changes
34
+ const parsed = this.parseCodeChange(existingContent, code);
35
+
36
+ // Find affected functions
37
+ const functions = parsed.modifiedFunctions.map(f => f.name);
38
+
39
+ // Get tests that cover this file
40
+ const testsForFile = this.testIndexer.getTestsForFile(file);
41
+
42
+ // Get tests that cover any of the modified functions
43
+ const testsForFunctions = functions.flatMap(fn => this.testIndexer.getTestsForFunction(fn));
44
+
45
+ // Combine and dedupe affected tests
46
+ const affectedTestIds = new Set<string>();
47
+ const affectedTests: TestInfo[] = [];
48
+
49
+ for (const test of [...testsForFile, ...testsForFunctions]) {
50
+ if (!affectedTestIds.has(test.id)) {
51
+ affectedTestIds.add(test.id);
52
+ affectedTests.push(test);
53
+ }
54
+ }
55
+
56
+ // Determine change type
57
+ const changeType = this.determineChangeType(parsed);
58
+
59
+ // Calculate risk level
60
+ const risk = this.calculateRisk(parsed, affectedTests);
61
+
62
+ // Calculate test coverage percentage
63
+ const coveredFunctions = new Set<string>();
64
+ for (const test of affectedTests) {
65
+ for (const fn of test.coversFunctions) {
66
+ coveredFunctions.add(fn);
67
+ }
68
+ }
69
+ const testCoverage = functions.length > 0
70
+ ? Math.round((coveredFunctions.size / functions.length) * 100)
71
+ : affectedTests.length > 0 ? 100 : 0;
72
+
73
+ // Generate reasoning
74
+ const reasoning = this.generateReasoning(parsed, affectedTests, risk);
75
+
76
+ return {
77
+ file,
78
+ functions,
79
+ type: changeType,
80
+ affectedTests,
81
+ testCoverage,
82
+ risk,
83
+ reasoning
84
+ };
85
+ }
86
+
87
+ predictFailures(analysis: ChangeAnalysis, newCode: string): PredictedFailure[] {
88
+ const failures: PredictedFailure[] = [];
89
+
90
+ for (const test of analysis.affectedTests) {
91
+ // Check each assertion in the test
92
+ for (const assertion of test.assertions) {
93
+ const prediction = this.predictAssertionFailure(assertion, newCode, analysis);
94
+ if (prediction) {
95
+ failures.push(prediction);
96
+ }
97
+ }
98
+
99
+ // If no specific assertion failures, check for general issues
100
+ if (!failures.some(f => f.test.id === test.id)) {
101
+ const generalPrediction = this.predictGeneralFailure(test, analysis, newCode);
102
+ if (generalPrediction) {
103
+ failures.push(generalPrediction);
104
+ }
105
+ }
106
+ }
107
+
108
+ return failures;
109
+ }
110
+
111
+ validateChange(code: string, file: string): TestValidationResult {
112
+ const analysis = this.analyzeChange(code, file);
113
+ const predictedFailures = this.predictFailures(analysis, code);
114
+
115
+ // Categorize tests
116
+ const wouldPass: TestInfo[] = [];
117
+ const wouldFail: PredictedFailure[] = predictedFailures;
118
+ const uncertain: TestInfo[] = [];
119
+
120
+ const failingTestIds = new Set(predictedFailures.map(f => f.test.id));
121
+
122
+ for (const test of analysis.affectedTests) {
123
+ if (failingTestIds.has(test.id)) {
124
+ continue; // Already in wouldFail
125
+ }
126
+
127
+ // Determine if test would likely pass or is uncertain
128
+ const coverage = this.assessTestCoverage(test, code, file);
129
+ if (coverage.confident) {
130
+ wouldPass.push(test);
131
+ } else {
132
+ uncertain.push(test);
133
+ }
134
+ }
135
+
136
+ // Generate suggested test updates
137
+ const suggestedTestUpdates = this.generateTestUpdates(predictedFailures, analysis);
138
+
139
+ // Determine if change is safe
140
+ const safe = predictedFailures.length === 0 &&
141
+ analysis.risk !== 'high' &&
142
+ (analysis.testCoverage >= 50 || analysis.affectedTests.length === 0);
143
+
144
+ return {
145
+ safe,
146
+ relatedTests: analysis.affectedTests,
147
+ wouldPass,
148
+ wouldFail: predictedFailures,
149
+ uncertain,
150
+ suggestedTestUpdates,
151
+ coveragePercent: analysis.testCoverage
152
+ };
153
+ }
154
+
155
+ private parseCodeChange(oldCode: string, newCode: string): ParsedChange {
156
+ const oldLines = oldCode.split('\n');
157
+ const newLines = newCode.split('\n');
158
+
159
+ const addedLines: string[] = [];
160
+ const removedLines: string[] = [];
161
+
162
+ // Simple diff: lines in new but not in old
163
+ const oldSet = new Set(oldLines.map(l => l.trim()));
164
+ const newSet = new Set(newLines.map(l => l.trim()));
165
+
166
+ for (const line of newLines) {
167
+ if (!oldSet.has(line.trim()) && line.trim()) {
168
+ addedLines.push(line);
169
+ }
170
+ }
171
+
172
+ for (const line of oldLines) {
173
+ if (!newSet.has(line.trim()) && line.trim()) {
174
+ removedLines.push(line);
175
+ }
176
+ }
177
+
178
+ // Detect function changes
179
+ const oldFunctions = this.extractFunctions(oldCode);
180
+ const newFunctions = this.extractFunctions(newCode);
181
+ const modifiedFunctions: FunctionChange[] = [];
182
+
183
+ const oldFuncMap = new Map(oldFunctions.map(f => [f.name, f]));
184
+ const newFuncMap = new Map(newFunctions.map(f => [f.name, f]));
185
+
186
+ // Find removed functions
187
+ for (const [name, func] of oldFuncMap) {
188
+ if (!newFuncMap.has(name)) {
189
+ modifiedFunctions.push({
190
+ name,
191
+ changeType: 'removed',
192
+ oldSignature: func.signature
193
+ });
194
+ }
195
+ }
196
+
197
+ // Find added and modified functions
198
+ for (const [name, func] of newFuncMap) {
199
+ const oldFunc = oldFuncMap.get(name);
200
+ if (!oldFunc) {
201
+ modifiedFunctions.push({
202
+ name,
203
+ changeType: 'added',
204
+ newSignature: func.signature
205
+ });
206
+ } else if (oldFunc.signature !== func.signature) {
207
+ modifiedFunctions.push({
208
+ name,
209
+ changeType: 'signature_changed',
210
+ oldSignature: oldFunc.signature,
211
+ newSignature: func.signature
212
+ });
213
+ } else if (oldFunc.body !== func.body) {
214
+ modifiedFunctions.push({
215
+ name,
216
+ changeType: 'modified',
217
+ oldSignature: oldFunc.signature,
218
+ newSignature: func.signature
219
+ });
220
+ }
221
+ }
222
+
223
+ // Detect export changes
224
+ const oldExports = this.extractExports(oldCode);
225
+ const newExports = this.extractExports(newCode);
226
+
227
+ const addedExports = newExports.filter(e => !oldExports.includes(e));
228
+ const removedExports = oldExports.filter(e => !newExports.includes(e));
229
+
230
+ return {
231
+ addedLines,
232
+ removedLines,
233
+ modifiedFunctions,
234
+ exportChanges: { added: addedExports, removed: removedExports }
235
+ };
236
+ }
237
+
238
+ private extractFunctions(code: string): Array<{ name: string; signature: string; body: string }> {
239
+ const functions: Array<{ name: string; signature: string; body: string }> = [];
240
+
241
+ // JavaScript/TypeScript function patterns
242
+ const patterns = [
243
+ // Function declaration
244
+ /function\s+(\w+)\s*\(([^)]*)\)[^{]*\{/g,
245
+ // Arrow function
246
+ /(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s*)?\(([^)]*)\)\s*(?::\s*\w+)?\s*=>/g,
247
+ // Method
248
+ /(\w+)\s*\(([^)]*)\)\s*(?::\s*\w+)?\s*\{/g,
249
+ // Python function
250
+ /def\s+(\w+)\s*\(([^)]*)\)\s*(?:->\s*\w+)?\s*:/g,
251
+ // Go function
252
+ /func\s+(\w+)\s*\(([^)]*)\)/g
253
+ ];
254
+
255
+ for (const pattern of patterns) {
256
+ let match;
257
+ while ((match = pattern.exec(code)) !== null) {
258
+ const name = match[1];
259
+ const params = match[2];
260
+ const signature = `${name}(${params})`;
261
+
262
+ // Extract a simple body representation (first 200 chars after signature)
263
+ const bodyStart = match.index + match[0].length;
264
+ const body = code.slice(bodyStart, bodyStart + 200);
265
+
266
+ functions.push({ name: name ?? '', signature, body });
267
+ }
268
+ }
269
+
270
+ return functions;
271
+ }
272
+
273
+ private extractExports(code: string): string[] {
274
+ const exports: string[] = [];
275
+
276
+ // ES6 exports
277
+ const exportPatterns = [
278
+ /export\s+(?:default\s+)?(?:function|class|const|let|var)\s+(\w+)/g,
279
+ /export\s*\{\s*([^}]+)\s*\}/g,
280
+ /module\.exports\s*=\s*\{\s*([^}]+)\s*\}/g,
281
+ /module\.exports\.(\w+)\s*=/g
282
+ ];
283
+
284
+ for (const pattern of exportPatterns) {
285
+ let match;
286
+ while ((match = pattern.exec(code)) !== null) {
287
+ const captured = match[1];
288
+ if (!captured) continue;
289
+ if (captured.includes(',')) {
290
+ // Multiple exports
291
+ const names = captured.split(',').map(s => {
292
+ const parts = s.trim().split(/\s+as\s+/);
293
+ return parts[0] ?? '';
294
+ }).filter(Boolean);
295
+ exports.push(...names);
296
+ } else {
297
+ exports.push(captured.trim());
298
+ }
299
+ }
300
+ }
301
+
302
+ return exports;
303
+ }
304
+
305
+ private determineChangeType(parsed: ParsedChange): 'refactor' | 'add' | 'delete' | 'modify' {
306
+ const hasAdditions = parsed.addedLines.length > 0 || parsed.modifiedFunctions.some(f => f.changeType === 'added');
307
+ const hasDeletions = parsed.removedLines.length > 0 || parsed.modifiedFunctions.some(f => f.changeType === 'removed');
308
+ const hasModifications = parsed.modifiedFunctions.some(f => f.changeType === 'modified' || f.changeType === 'signature_changed');
309
+
310
+ if (hasDeletions && !hasAdditions) return 'delete';
311
+ if (hasAdditions && !hasDeletions && !hasModifications) return 'add';
312
+ if (hasModifications || (hasAdditions && hasDeletions)) return 'modify';
313
+
314
+ return 'refactor';
315
+ }
316
+
317
+ private calculateRisk(parsed: ParsedChange, affectedTests: TestInfo[]): 'low' | 'medium' | 'high' {
318
+ let riskScore = 0;
319
+
320
+ // Removed exports are high risk
321
+ if (parsed.exportChanges.removed.length > 0) {
322
+ riskScore += 30;
323
+ }
324
+
325
+ // Signature changes are medium-high risk
326
+ const signatureChanges = parsed.modifiedFunctions.filter(f => f.changeType === 'signature_changed');
327
+ riskScore += signatureChanges.length * 20;
328
+
329
+ // Removed functions are high risk
330
+ const removedFunctions = parsed.modifiedFunctions.filter(f => f.changeType === 'removed');
331
+ riskScore += removedFunctions.length * 25;
332
+
333
+ // No test coverage is risky
334
+ if (affectedTests.length === 0 && parsed.modifiedFunctions.length > 0) {
335
+ riskScore += 20;
336
+ }
337
+
338
+ // Many affected tests might indicate broad impact
339
+ if (affectedTests.length > 10) {
340
+ riskScore += 15;
341
+ }
342
+
343
+ if (riskScore >= 50) return 'high';
344
+ if (riskScore >= 25) return 'medium';
345
+ return 'low';
346
+ }
347
+
348
+ private generateReasoning(parsed: ParsedChange, affectedTests: TestInfo[], risk: 'low' | 'medium' | 'high'): string {
349
+ const reasons: string[] = [];
350
+
351
+ if (parsed.modifiedFunctions.length > 0) {
352
+ const types = parsed.modifiedFunctions.map(f => f.changeType);
353
+ if (types.includes('removed')) {
354
+ reasons.push(`${types.filter(t => t === 'removed').length} function(s) removed`);
355
+ }
356
+ if (types.includes('signature_changed')) {
357
+ reasons.push(`${types.filter(t => t === 'signature_changed').length} function signature(s) changed`);
358
+ }
359
+ if (types.includes('modified')) {
360
+ reasons.push(`${types.filter(t => t === 'modified').length} function body(ies) modified`);
361
+ }
362
+ if (types.includes('added')) {
363
+ reasons.push(`${types.filter(t => t === 'added').length} function(s) added`);
364
+ }
365
+ }
366
+
367
+ if (parsed.exportChanges.removed.length > 0) {
368
+ reasons.push(`${parsed.exportChanges.removed.length} export(s) removed: ${parsed.exportChanges.removed.join(', ')}`);
369
+ }
370
+
371
+ if (affectedTests.length > 0) {
372
+ reasons.push(`${affectedTests.length} test(s) may be affected`);
373
+ } else {
374
+ reasons.push('No tests cover this code');
375
+ }
376
+
377
+ return reasons.join('. ') + '.';
378
+ }
379
+
380
+ private predictAssertionFailure(
381
+ assertion: Assertion,
382
+ newCode: string,
383
+ analysis: ChangeAnalysis
384
+ ): PredictedFailure | null {
385
+ // Check if the assertion subject is affected by the change
386
+ const subject = assertion.subject;
387
+
388
+ // Check if subject function was removed or signature changed
389
+ for (const func of analysis.functions) {
390
+ if (subject.includes(func)) {
391
+ // Check if this function had breaking changes
392
+ const funcInfo = this.tier2.searchSymbols(func, undefined, 1);
393
+ if (funcInfo.length === 0) {
394
+ // Function might have been removed
395
+ return {
396
+ test: analysis.affectedTests.find(t => t.assertions.some(a => a.code === assertion.code))!,
397
+ assertion,
398
+ reason: `Function '${func}' appears to be removed or renamed`,
399
+ confidence: 80,
400
+ suggestedFix: `Update test to use the new function name or remove the test if functionality is removed`
401
+ };
402
+ }
403
+ }
404
+ }
405
+
406
+ // Check if expected value might have changed
407
+ if (assertion.expected) {
408
+ // Look for the expected value in the old code but not in new
409
+ const expectedInCode = newCode.includes(assertion.expected);
410
+ if (!expectedInCode && assertion.type === 'equality') {
411
+ return {
412
+ test: analysis.affectedTests.find(t => t.assertions.some(a => a.code === assertion.code))!,
413
+ assertion,
414
+ reason: `Expected value '${assertion.expected}' may have changed`,
415
+ confidence: 60,
416
+ suggestedFix: `Review and update the expected value in the assertion`
417
+ };
418
+ }
419
+ }
420
+
421
+ return null;
422
+ }
423
+
424
+ private predictGeneralFailure(
425
+ test: TestInfo,
426
+ analysis: ChangeAnalysis,
427
+ newCode: string
428
+ ): PredictedFailure | null {
429
+ // Check if any covered functions were removed
430
+ for (const fn of test.coversFunctions) {
431
+ const inNewCode = newCode.includes(fn);
432
+ if (!inNewCode) {
433
+ return {
434
+ test,
435
+ reason: `Test calls '${fn}' which may have been removed or renamed`,
436
+ confidence: 70,
437
+ suggestedFix: `Update test to use the new function name or remove the test`
438
+ };
439
+ }
440
+ }
441
+
442
+ // Check for signature changes affecting this test
443
+ for (const fn of test.coversFunctions) {
444
+ if (analysis.functions.includes(fn)) {
445
+ // Function was modified
446
+ return {
447
+ test,
448
+ reason: `Test uses '${fn}' which has been modified`,
449
+ confidence: 40,
450
+ suggestedFix: `Review test assertions after code changes`
451
+ };
452
+ }
453
+ }
454
+
455
+ return null;
456
+ }
457
+
458
+ private assessTestCoverage(test: TestInfo, newCode: string, file: string): { confident: boolean } {
459
+ // Check if all covered functions still exist
460
+ let allFunctionsExist = true;
461
+ for (const fn of test.coversFunctions) {
462
+ if (!newCode.includes(fn)) {
463
+ allFunctionsExist = false;
464
+ break;
465
+ }
466
+ }
467
+
468
+ // Check if file is still covered
469
+ const coversFile = test.coversFiles.some(f =>
470
+ file.includes(f) || f.includes(file.replace(/\.[^.]+$/, ''))
471
+ );
472
+
473
+ return {
474
+ confident: allFunctionsExist && coversFile
475
+ };
476
+ }
477
+
478
+ private generateTestUpdates(
479
+ failures: PredictedFailure[],
480
+ analysis: ChangeAnalysis
481
+ ): Array<{ file: string; testName: string; line: number; before: string; after: string; reason: string }> {
482
+ const updates: Array<{ file: string; testName: string; line: number; before: string; after: string; reason: string }> = [];
483
+
484
+ for (const failure of failures) {
485
+ if (failure.assertion && failure.suggestedFix) {
486
+ updates.push({
487
+ file: failure.test.file,
488
+ testName: failure.test.name,
489
+ line: failure.assertion.line,
490
+ before: failure.assertion.code,
491
+ after: `// TODO: ${failure.suggestedFix}\n${failure.assertion.code}`,
492
+ reason: failure.reason
493
+ });
494
+ }
495
+ }
496
+
497
+ return updates;
498
+ }
499
+ }
@@ -0,0 +1,5 @@
1
+ export { TestAwareness } from './test-awareness.js';
2
+ export { TestIndexer } from './test-indexer.js';
3
+ export { TestParser } from './test-parser.js';
4
+ export { ChangeValidator } from './change-validator.js';
5
+ export { TestSuggester } from './test-suggester.js';
package/src/index.ts ADDED
@@ -0,0 +1,49 @@
1
+ import { MCPServer } from './server/mcp.js';
2
+ import { getDefaultConfig, parseArgs } from './utils/config.js';
3
+ import { executeCLI, printHelp } from './cli/commands.js';
4
+
5
+ async function main(): Promise<void> {
6
+ const args = process.argv.slice(2);
7
+
8
+ // Check for CLI commands first
9
+ const firstArg = args[0];
10
+ const cliCommands = ['projects', 'export', 'help', '--help', '-h'];
11
+
12
+ if (firstArg && cliCommands.includes(firstArg)) {
13
+ // Handle CLI commands
14
+ executeCLI(args);
15
+ return;
16
+ }
17
+
18
+ // No arguments and not piped - show help
19
+ if (args.length === 0 && process.stdin.isTTY) {
20
+ printHelp();
21
+ console.log('\nTo start as MCP server, use: memorylayer --project <path>\n');
22
+ return;
23
+ }
24
+
25
+ // Parse command line arguments for MCP server mode
26
+ const { projectPath } = parseArgs(args);
27
+
28
+ // Get configuration
29
+ const config = getDefaultConfig(projectPath);
30
+
31
+ console.error('MemoryLayer starting...');
32
+ console.error(`Project: ${config.projectPath}`);
33
+ console.error(`Data directory: ${config.dataDir}`);
34
+
35
+ // Create and start MCP server
36
+ const server = new MCPServer(config);
37
+
38
+ try {
39
+ await server.start();
40
+ } catch (error) {
41
+ console.error('Failed to start MemoryLayer:', error);
42
+ process.exit(1);
43
+ }
44
+ }
45
+
46
+ main().catch((error) => {
47
+ console.error('Unhandled error:', error);
48
+ process.exit(1);
49
+ });