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,434 @@
1
+ /**
2
+ * Intent Detection and Query Routing
3
+ *
4
+ * Provides auto-detection of user intent to route to appropriate internal tools.
5
+ */
6
+
7
+ import type {
8
+ MemoryQueryInput,
9
+ MemoryQueryAction,
10
+ MemoryRecordInput,
11
+ MemoryRecordType,
12
+ MemoryReviewInput,
13
+ MemoryReviewAction,
14
+ MemoryStatusInput,
15
+ MemoryStatusAction,
16
+ } from './types.js';
17
+
18
+ // ============================================================================
19
+ // Pattern Detection
20
+ // ============================================================================
21
+
22
+ /**
23
+ * Detects if a string looks like a file path
24
+ */
25
+ export function isFilePath(str: string): boolean {
26
+ // Common file path patterns
27
+ const filePatterns = [
28
+ /^\.?\/?[\w-]+(?:\/[\w-]+)*\.\w+$/, // src/foo/bar.ts
29
+ /^[a-zA-Z]:\\/, // Windows absolute path
30
+ /^\//, // Unix absolute path
31
+ /\.(ts|js|tsx|jsx|py|go|rs|java|cpp|c|h|md|json|yaml|yml|toml)$/i, // File extensions
32
+ ];
33
+ return filePatterns.some(pattern => pattern.test(str));
34
+ }
35
+
36
+ /**
37
+ * Detects if a string looks like a symbol name (function, class, etc.)
38
+ */
39
+ export function isSymbolName(str: string): boolean {
40
+ // PascalCase or camelCase identifiers without spaces
41
+ const symbolPattern = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
42
+ // Should not be a common word
43
+ const commonWords = ['the', 'and', 'for', 'with', 'how', 'what', 'why', 'when', 'where', 'is', 'are', 'was', 'were'];
44
+ return symbolPattern.test(str) && !commonWords.includes(str.toLowerCase()) && str.length > 1;
45
+ }
46
+
47
+ /**
48
+ * Detects if a string contains code
49
+ */
50
+ export function containsCode(str: string): boolean {
51
+ // Look for code-like patterns
52
+ const codePatterns = [
53
+ /function\s+\w+\s*\(/, // function declarations
54
+ /const\s+\w+\s*=/, // const declarations
55
+ /let\s+\w+\s*=/, // let declarations
56
+ /class\s+\w+/, // class declarations
57
+ /interface\s+\w+/, // interface declarations
58
+ /=>/, // arrow functions
59
+ /\{\s*\n/, // code blocks
60
+ /import\s+.*from/, // imports
61
+ /export\s+(default\s+)?/, // exports
62
+ /if\s*\(.+\)\s*\{/, // if statements
63
+ /for\s*\(.+\)\s*\{/, // for loops
64
+ /async\s+function/, // async functions
65
+ /await\s+/, // await expressions
66
+ /try\s*\{/, // try blocks
67
+ /catch\s*\(/, // catch blocks
68
+ /return\s+/, // return statements
69
+ ];
70
+ return codePatterns.some(pattern => pattern.test(str));
71
+ }
72
+
73
+ /**
74
+ * Detects if a string looks like an error message
75
+ */
76
+ export function isErrorMessage(str: string): boolean {
77
+ const errorPatterns = [
78
+ /error:/i,
79
+ /exception:/i,
80
+ /failed:/i,
81
+ /cannot\s+/i,
82
+ /unable\s+to/i,
83
+ /TypeError/i,
84
+ /ReferenceError/i,
85
+ /SyntaxError/i,
86
+ /undefined\s+is\s+not/i,
87
+ /is\s+not\s+defined/i,
88
+ /not\s+a\s+function/i,
89
+ /cannot\s+read\s+property/i,
90
+ ];
91
+ return errorPatterns.some(pattern => pattern.test(str));
92
+ }
93
+
94
+ // ============================================================================
95
+ // Query Router
96
+ // ============================================================================
97
+
98
+ /**
99
+ * Auto-detect the best action for a memory query
100
+ */
101
+ export function detectQueryAction(input: MemoryQueryInput): MemoryQueryAction {
102
+ // Explicit action takes priority
103
+ if (input.action) return input.action;
104
+
105
+ // Code provided → confidence/sources/existing check
106
+ if (input.code && input.code.length > 20) {
107
+ if (containsCode(input.code)) {
108
+ return 'confidence';
109
+ }
110
+ }
111
+
112
+ // File path provided → file context
113
+ if (input.file && isFilePath(input.file)) {
114
+ return 'file';
115
+ }
116
+
117
+ // Symbol name provided → symbol lookup
118
+ if (input.symbol && isSymbolName(input.symbol)) {
119
+ return 'symbol';
120
+ }
121
+
122
+ // Query looks like a file path → file context
123
+ if (isFilePath(input.query)) {
124
+ return 'file';
125
+ }
126
+
127
+ // Query is a single symbol-like word → symbol lookup
128
+ if (isSymbolName(input.query) && !input.query.includes(' ')) {
129
+ return 'symbol';
130
+ }
131
+
132
+ // Default: combined context + search
133
+ return 'context';
134
+ }
135
+
136
+ /**
137
+ * Parse the query to extract file paths, symbols, and search terms
138
+ */
139
+ export function parseQuery(query: string): {
140
+ files: string[];
141
+ symbols: string[];
142
+ searchTerms: string;
143
+ } {
144
+ const words = query.split(/\s+/);
145
+ const files: string[] = [];
146
+ const symbols: string[] = [];
147
+ const searchTerms: string[] = [];
148
+
149
+ for (const word of words) {
150
+ if (isFilePath(word)) {
151
+ files.push(word);
152
+ } else if (isSymbolName(word) && word.length > 3) {
153
+ // Longer identifiers are likely symbols
154
+ symbols.push(word);
155
+ searchTerms.push(word); // Also include as search term
156
+ } else {
157
+ searchTerms.push(word);
158
+ }
159
+ }
160
+
161
+ return {
162
+ files,
163
+ symbols,
164
+ searchTerms: searchTerms.join(' '),
165
+ };
166
+ }
167
+
168
+ // ============================================================================
169
+ // Record Router
170
+ // ============================================================================
171
+
172
+ /**
173
+ * Auto-detect the type of record to create
174
+ */
175
+ export function detectRecordType(input: MemoryRecordInput): MemoryRecordType {
176
+ // Explicit type/action takes priority
177
+ if (input.type) return input.type;
178
+ if (input.action) return input.action;
179
+
180
+ // Pattern-related inputs
181
+ if (input.pattern_id && input.code) {
182
+ return 'example';
183
+ }
184
+ if (input.pattern_name || (input.code && input.category)) {
185
+ return 'pattern';
186
+ }
187
+
188
+ // Feedback input
189
+ if (input.query !== undefined && input.was_useful !== undefined) {
190
+ return 'feedback';
191
+ }
192
+
193
+ // Critical content
194
+ if (input.critical_type || input.reason) {
195
+ return 'critical';
196
+ }
197
+
198
+ // Feature context (content is a feature name, no title)
199
+ if (!input.title && input.content && input.content.length < 50 && !input.code) {
200
+ return 'feature';
201
+ }
202
+
203
+ // Default to decision
204
+ return 'decision';
205
+ }
206
+
207
+ /**
208
+ * Validate record input has required fields for the detected type
209
+ */
210
+ export function validateRecordInput(input: MemoryRecordInput): {
211
+ valid: boolean;
212
+ error?: string;
213
+ type: MemoryRecordType;
214
+ } {
215
+ const type = detectRecordType(input);
216
+
217
+ switch (type) {
218
+ case 'decision':
219
+ if (!input.title) {
220
+ return { valid: false, error: 'Decision requires a title', type };
221
+ }
222
+ if (!input.content) {
223
+ return { valid: false, error: 'Decision requires content/description', type };
224
+ }
225
+ break;
226
+
227
+ case 'pattern':
228
+ if (!input.code) {
229
+ return { valid: false, error: 'Pattern requires code example', type };
230
+ }
231
+ if (!input.pattern_name && !input.title) {
232
+ return { valid: false, error: 'Pattern requires a name', type };
233
+ }
234
+ break;
235
+
236
+ case 'example':
237
+ if (!input.pattern_id) {
238
+ return { valid: false, error: 'Example requires pattern_id', type };
239
+ }
240
+ if (!input.code) {
241
+ return { valid: false, error: 'Example requires code', type };
242
+ }
243
+ if (!input.explanation && !input.content) {
244
+ return { valid: false, error: 'Example requires explanation', type };
245
+ }
246
+ break;
247
+
248
+ case 'feedback':
249
+ if (input.was_useful === undefined) {
250
+ return { valid: false, error: 'Feedback requires was_useful boolean', type };
251
+ }
252
+ break;
253
+
254
+ case 'feature':
255
+ if (!input.content) {
256
+ return { valid: false, error: 'Feature requires a name (in content field)', type };
257
+ }
258
+ break;
259
+
260
+ case 'critical':
261
+ if (!input.content) {
262
+ return { valid: false, error: 'Critical content requires content', type };
263
+ }
264
+ break;
265
+ }
266
+
267
+ return { valid: true, type };
268
+ }
269
+
270
+ // ============================================================================
271
+ // Review Router
272
+ // ============================================================================
273
+
274
+ /**
275
+ * Auto-detect the best action for a memory review
276
+ */
277
+ export function detectReviewAction(input: MemoryReviewInput): MemoryReviewAction {
278
+ // Explicit action takes priority
279
+ if (input.action) return input.action;
280
+
281
+ // Error message provided → bug search
282
+ if (input.error && isErrorMessage(input.error)) {
283
+ return 'bugs';
284
+ }
285
+
286
+ // File provided with code → full review including tests
287
+ if (input.file && input.code) {
288
+ return 'full';
289
+ }
290
+
291
+ // Just code → pattern validation + conflicts
292
+ if (input.code && !input.file) {
293
+ return 'pattern';
294
+ }
295
+
296
+ // Default to full review
297
+ return 'full';
298
+ }
299
+
300
+ /**
301
+ * Determine which checks to run for a review
302
+ */
303
+ export function getReviewChecks(input: MemoryReviewInput): {
304
+ runPatterns: boolean;
305
+ runConflicts: boolean;
306
+ runConfidence: boolean;
307
+ runExisting: boolean;
308
+ runTests: boolean;
309
+ runBugs: boolean;
310
+ runCoverage: boolean;
311
+ } {
312
+ const action = detectReviewAction(input);
313
+
314
+ // Full review runs everything applicable
315
+ if (action === 'full') {
316
+ return {
317
+ runPatterns: input.include_patterns !== false,
318
+ runConflicts: true,
319
+ runConfidence: true,
320
+ runExisting: !!input.intent,
321
+ runTests: !!input.file && input.include_tests !== false,
322
+ runBugs: !!input.error,
323
+ runCoverage: false, // Only on explicit request
324
+ };
325
+ }
326
+
327
+ // Specific actions
328
+ return {
329
+ runPatterns: action === 'pattern',
330
+ runConflicts: action === 'conflicts',
331
+ runConfidence: action === 'confidence',
332
+ runExisting: false,
333
+ runTests: action === 'tests',
334
+ runBugs: action === 'bugs',
335
+ runCoverage: action === 'coverage',
336
+ };
337
+ }
338
+
339
+ // ============================================================================
340
+ // Status Router
341
+ // ============================================================================
342
+
343
+ /**
344
+ * Auto-detect the best action for a memory status query
345
+ */
346
+ export function detectStatusAction(input: MemoryStatusInput): MemoryStatusAction {
347
+ // Explicit action takes priority
348
+ if (input.action) return input.action;
349
+
350
+ // Time-based queries
351
+ if (input.since) {
352
+ if (input.author || input.file) {
353
+ return 'changed';
354
+ }
355
+ return 'happened';
356
+ }
357
+
358
+ // Category provided → patterns
359
+ if (input.category) {
360
+ return 'patterns';
361
+ }
362
+
363
+ // History requested → health
364
+ if (input.include_history) {
365
+ return 'health';
366
+ }
367
+
368
+ // Scope-based routing
369
+ switch (input.scope) {
370
+ case 'architecture':
371
+ return 'architecture';
372
+ case 'changes':
373
+ return 'changed';
374
+ case 'docs':
375
+ return 'undocumented';
376
+ case 'health':
377
+ return 'health';
378
+ case 'patterns':
379
+ return 'patterns';
380
+ default:
381
+ return 'summary';
382
+ }
383
+ }
384
+
385
+ /**
386
+ * Determine which status information to gather
387
+ */
388
+ export function getStatusGathers(input: MemoryStatusInput): {
389
+ gatherProject: boolean;
390
+ gatherArchitecture: boolean;
391
+ gatherChanges: boolean;
392
+ gatherActivity: boolean;
393
+ gatherDocs: boolean;
394
+ gatherHealth: boolean;
395
+ gatherPatterns: boolean;
396
+ gatherStats: boolean;
397
+ gatherCritical: boolean;
398
+ gatherLearning: boolean;
399
+ gatherUndocumented: boolean;
400
+ } {
401
+ const scope = input.scope || 'project';
402
+
403
+ // 'all' scope gathers everything
404
+ if (scope === 'all') {
405
+ return {
406
+ gatherProject: true,
407
+ gatherArchitecture: true,
408
+ gatherChanges: !!input.since,
409
+ gatherActivity: !!input.since,
410
+ gatherDocs: true,
411
+ gatherHealth: true,
412
+ gatherPatterns: true,
413
+ gatherStats: true,
414
+ gatherCritical: true,
415
+ gatherLearning: true,
416
+ gatherUndocumented: false, // Can be large, skip for 'all'
417
+ };
418
+ }
419
+
420
+ // Scope-specific gathering
421
+ return {
422
+ gatherProject: scope === 'project',
423
+ gatherArchitecture: scope === 'architecture',
424
+ gatherChanges: scope === 'changes',
425
+ gatherActivity: scope === 'changes' && !!input.since,
426
+ gatherDocs: scope === 'docs',
427
+ gatherHealth: scope === 'health',
428
+ gatherPatterns: scope === 'patterns',
429
+ gatherStats: scope === 'architecture',
430
+ gatherCritical: scope === 'health',
431
+ gatherLearning: scope === 'project',
432
+ gatherUndocumented: scope === 'docs',
433
+ };
434
+ }