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.

Files changed (81) hide show
  1. package/README.md +3 -2
  2. package/dist/index.js +172 -90
  3. package/dist/index.js.map +7 -0
  4. package/package.json +6 -1
  5. package/esbuild.config.js +0 -26
  6. package/src/cli/commands.ts +0 -573
  7. package/src/core/adr-exporter.ts +0 -253
  8. package/src/core/architecture/architecture-enforcement.ts +0 -228
  9. package/src/core/architecture/duplicate-detector.ts +0 -288
  10. package/src/core/architecture/index.ts +0 -6
  11. package/src/core/architecture/pattern-learner.ts +0 -306
  12. package/src/core/architecture/pattern-library.ts +0 -403
  13. package/src/core/architecture/pattern-validator.ts +0 -324
  14. package/src/core/change-intelligence/bug-correlator.ts +0 -544
  15. package/src/core/change-intelligence/change-intelligence.ts +0 -264
  16. package/src/core/change-intelligence/change-tracker.ts +0 -334
  17. package/src/core/change-intelligence/fix-suggester.ts +0 -340
  18. package/src/core/change-intelligence/index.ts +0 -5
  19. package/src/core/code-verifier.ts +0 -843
  20. package/src/core/confidence/confidence-scorer.ts +0 -251
  21. package/src/core/confidence/conflict-checker.ts +0 -289
  22. package/src/core/confidence/index.ts +0 -5
  23. package/src/core/confidence/source-tracker.ts +0 -263
  24. package/src/core/confidence/warning-detector.ts +0 -241
  25. package/src/core/context-rot/compaction.ts +0 -284
  26. package/src/core/context-rot/context-health.ts +0 -243
  27. package/src/core/context-rot/context-rot-prevention.ts +0 -213
  28. package/src/core/context-rot/critical-context.ts +0 -221
  29. package/src/core/context-rot/drift-detector.ts +0 -255
  30. package/src/core/context-rot/index.ts +0 -7
  31. package/src/core/context.ts +0 -263
  32. package/src/core/decision-extractor.ts +0 -339
  33. package/src/core/decisions.ts +0 -69
  34. package/src/core/deja-vu.ts +0 -421
  35. package/src/core/engine.ts +0 -1646
  36. package/src/core/feature-context.ts +0 -726
  37. package/src/core/ghost-mode.ts +0 -465
  38. package/src/core/learning.ts +0 -519
  39. package/src/core/living-docs/activity-tracker.ts +0 -296
  40. package/src/core/living-docs/architecture-generator.ts +0 -428
  41. package/src/core/living-docs/changelog-generator.ts +0 -348
  42. package/src/core/living-docs/component-generator.ts +0 -230
  43. package/src/core/living-docs/doc-engine.ts +0 -134
  44. package/src/core/living-docs/doc-validator.ts +0 -282
  45. package/src/core/living-docs/index.ts +0 -8
  46. package/src/core/project-manager.ts +0 -301
  47. package/src/core/refresh/activity-gate.ts +0 -256
  48. package/src/core/refresh/git-staleness-checker.ts +0 -108
  49. package/src/core/refresh/index.ts +0 -27
  50. package/src/core/summarizer.ts +0 -290
  51. package/src/core/test-awareness/change-validator.ts +0 -499
  52. package/src/core/test-awareness/index.ts +0 -5
  53. package/src/index.ts +0 -90
  54. package/src/indexing/ast.ts +0 -868
  55. package/src/indexing/embeddings.ts +0 -85
  56. package/src/indexing/indexer.ts +0 -270
  57. package/src/indexing/watcher.ts +0 -78
  58. package/src/server/gateways/aggregator.ts +0 -374
  59. package/src/server/gateways/index.ts +0 -473
  60. package/src/server/gateways/memory-ghost.ts +0 -343
  61. package/src/server/gateways/memory-query.ts +0 -452
  62. package/src/server/gateways/memory-record.ts +0 -346
  63. package/src/server/gateways/memory-review.ts +0 -410
  64. package/src/server/gateways/memory-status.ts +0 -517
  65. package/src/server/gateways/memory-verify.ts +0 -392
  66. package/src/server/gateways/router.ts +0 -434
  67. package/src/server/gateways/types.ts +0 -610
  68. package/src/server/http.ts +0 -228
  69. package/src/server/mcp.ts +0 -154
  70. package/src/server/resources.ts +0 -85
  71. package/src/server/tools.ts +0 -2460
  72. package/src/storage/database.ts +0 -271
  73. package/src/storage/tier1.ts +0 -135
  74. package/src/storage/tier2.ts +0 -972
  75. package/src/storage/tier3.ts +0 -123
  76. package/src/types/documentation.ts +0 -619
  77. package/src/types/index.ts +0 -222
  78. package/src/utils/config.ts +0 -194
  79. package/src/utils/files.ts +0 -117
  80. package/src/utils/time.ts +0 -37
  81. package/src/utils/tokens.ts +0 -52
@@ -1,421 +0,0 @@
1
- /**
2
- * Déjà Vu Detection - Similar Problem Recognition
3
- *
4
- * Detects when a query or code pattern is similar to something solved before.
5
- * Surfaces past solutions to prevent reinventing the wheel.
6
- * "You solved a similar problem 2 weeks ago in auth.ts"
7
- */
8
-
9
- import type Database from 'better-sqlite3';
10
- import type { EmbeddingGenerator } from '../indexing/embeddings.js';
11
- import type { Tier2Storage } from '../storage/tier2.js';
12
- import { createHash } from 'crypto';
13
- import { formatTimeAgoWithContext } from '../utils/time.js';
14
-
15
- export interface DejaVuMatch {
16
- type: 'query' | 'solution' | 'fix' | 'pattern';
17
- similarity: number;
18
- when: Date;
19
- file: string;
20
- snippet: string;
21
- message: string;
22
- context?: string;
23
- }
24
-
25
- export interface PastQuery {
26
- queryHash: string;
27
- queryText: string;
28
- resultFiles: string[];
29
- timestamp: Date;
30
- wasUseful: boolean;
31
- usefulness: number;
32
- }
33
-
34
- export interface PastSolution {
35
- file: string;
36
- snippet: string;
37
- query: string;
38
- timestamp: Date;
39
- similarity: number;
40
- }
41
-
42
- interface QueryPatternRow {
43
- id: number;
44
- query_hash: string;
45
- query_text: string;
46
- result_files: string;
47
- hit_count: number;
48
- avg_usefulness: number;
49
- last_used: number;
50
- }
51
-
52
- interface UsageEventRow {
53
- id: number;
54
- event_type: string;
55
- file_path: string | null;
56
- query: string | null;
57
- context_used: number;
58
- timestamp: number;
59
- }
60
-
61
- export class DejaVuDetector {
62
- private db: Database.Database;
63
- private tier2: Tier2Storage;
64
- private embeddingGenerator: EmbeddingGenerator;
65
-
66
- // Thresholds
67
- private readonly SIMILARITY_THRESHOLD = 0.7;
68
- private readonly MIN_USEFULNESS = 0.3;
69
- private readonly MAX_AGE_DAYS = 90;
70
-
71
- constructor(
72
- db: Database.Database,
73
- tier2: Tier2Storage,
74
- embeddingGenerator: EmbeddingGenerator
75
- ) {
76
- this.db = db;
77
- this.tier2 = tier2;
78
- this.embeddingGenerator = embeddingGenerator;
79
- }
80
-
81
- /**
82
- * Find similar past problems, solutions, and fixes
83
- */
84
- async findSimilar(query: string, limit: number = 3): Promise<DejaVuMatch[]> {
85
- const matches: DejaVuMatch[] = [];
86
-
87
- // Run searches in parallel
88
- const [pastQueries, pastSolutions, pastFixes] = await Promise.all([
89
- this.searchPastQueries(query),
90
- this.searchPastSolutions(query),
91
- this.searchPastFixes(query),
92
- ]);
93
-
94
- matches.push(...pastQueries, ...pastSolutions, ...pastFixes);
95
-
96
- // Sort by similarity and recency
97
- return matches
98
- .sort((a, b) => {
99
- // Weight similarity higher, but give bonus to more recent
100
- const recencyA = this.getRecencyScore(a.when);
101
- const recencyB = this.getRecencyScore(b.when);
102
- const scoreA = a.similarity * 0.7 + recencyA * 0.3;
103
- const scoreB = b.similarity * 0.7 + recencyB * 0.3;
104
- return scoreB - scoreA;
105
- })
106
- .slice(0, limit);
107
- }
108
-
109
- /**
110
- * Search for similar past queries with high usefulness
111
- */
112
- async searchPastQueries(query: string): Promise<DejaVuMatch[]> {
113
- const matches: DejaVuMatch[] = [];
114
-
115
- try {
116
- // Get query embedding
117
- const embedding = await this.embeddingGenerator.embed(query);
118
-
119
- // Get recent useful queries
120
- const stmt = this.db.prepare(`
121
- SELECT query_hash, query_text, result_files, avg_usefulness, last_used
122
- FROM query_patterns
123
- WHERE avg_usefulness >= ?
124
- AND last_used > unixepoch() - ? * 86400
125
- ORDER BY avg_usefulness DESC, last_used DESC
126
- LIMIT 50
127
- `);
128
-
129
- const rows = stmt.all(this.MIN_USEFULNESS, this.MAX_AGE_DAYS) as QueryPatternRow[];
130
-
131
- for (const row of rows) {
132
- // Calculate text similarity
133
- const similarity = this.calculateTextSimilarity(query, row.query_text);
134
-
135
- if (similarity >= this.SIMILARITY_THRESHOLD) {
136
- const resultFiles = this.parseJsonArray(row.result_files);
137
- const primaryFile = resultFiles[0] || 'unknown';
138
-
139
- matches.push({
140
- type: 'query',
141
- similarity,
142
- when: new Date(row.last_used * 1000),
143
- file: primaryFile,
144
- snippet: row.query_text.slice(0, 100),
145
- message: formatTimeAgoWithContext(new Date(row.last_used * 1000), 'asked a similar question', primaryFile),
146
- context: resultFiles.length > 1 ? `Also involved: ${resultFiles.slice(1, 4).join(', ')}` : undefined,
147
- });
148
- }
149
- }
150
- } catch (error) {
151
- console.error('Error searching past queries:', error);
152
- }
153
-
154
- return matches;
155
- }
156
-
157
- /**
158
- * Search for past solutions to similar problems
159
- */
160
- async searchPastSolutions(query: string): Promise<DejaVuMatch[]> {
161
- const matches: DejaVuMatch[] = [];
162
-
163
- try {
164
- // Search codebase for semantically similar content
165
- const embedding = await this.embeddingGenerator.embed(query);
166
- const searchResults = this.tier2.search(embedding, 10);
167
-
168
- // Find results that were previously part of useful context
169
- for (const result of searchResults) {
170
- if (result.similarity < this.SIMILARITY_THRESHOLD) continue;
171
-
172
- // Check if this file was used in a useful query before
173
- const usageStmt = this.db.prepare(`
174
- SELECT ue.query, ue.timestamp, qp.avg_usefulness
175
- FROM usage_events ue
176
- LEFT JOIN query_patterns qp ON ue.query = qp.query_text
177
- WHERE ue.file_path = ?
178
- AND ue.event_type = 'context_used'
179
- AND ue.timestamp > unixepoch() - ? * 86400
180
- AND (qp.avg_usefulness IS NULL OR qp.avg_usefulness >= ?)
181
- ORDER BY ue.timestamp DESC
182
- LIMIT 1
183
- `);
184
-
185
- const usage = usageStmt.get(result.file, this.MAX_AGE_DAYS, this.MIN_USEFULNESS) as {
186
- query: string;
187
- timestamp: number;
188
- avg_usefulness: number | null;
189
- } | undefined;
190
-
191
- if (usage) {
192
- matches.push({
193
- type: 'solution',
194
- similarity: result.similarity,
195
- when: new Date(usage.timestamp * 1000),
196
- file: result.file,
197
- snippet: result.preview.slice(0, 150),
198
- message: formatTimeAgoWithContext(new Date(usage.timestamp * 1000), 'worked on this', result.file),
199
- context: usage.query ? `For: "${usage.query.slice(0, 50)}..."` : undefined,
200
- });
201
- }
202
- }
203
- } catch (error) {
204
- console.error('Error searching past solutions:', error);
205
- }
206
-
207
- return matches;
208
- }
209
-
210
- /**
211
- * Search for past bug fixes with similar error patterns
212
- */
213
- async searchPastFixes(query: string): Promise<DejaVuMatch[]> {
214
- const matches: DejaVuMatch[] = [];
215
-
216
- // Check if query looks like an error
217
- if (!this.looksLikeError(query)) {
218
- return matches;
219
- }
220
-
221
- try {
222
- // Search for similar error queries
223
- const stmt = this.db.prepare(`
224
- SELECT ue.query, ue.file_path, ue.timestamp, qp.result_files
225
- FROM usage_events ue
226
- LEFT JOIN query_patterns qp ON ue.query = qp.query_text
227
- WHERE ue.event_type = 'query'
228
- AND (ue.query LIKE '%error%' OR ue.query LIKE '%fix%' OR ue.query LIKE '%bug%')
229
- AND ue.timestamp > unixepoch() - ? * 86400
230
- ORDER BY ue.timestamp DESC
231
- LIMIT 100
232
- `);
233
-
234
- const rows = stmt.all(this.MAX_AGE_DAYS) as Array<{
235
- query: string;
236
- file_path: string | null;
237
- timestamp: number;
238
- result_files: string | null;
239
- }>;
240
-
241
- for (const row of rows) {
242
- if (!row.query) continue;
243
-
244
- const similarity = this.calculateTextSimilarity(query, row.query);
245
-
246
- if (similarity >= this.SIMILARITY_THRESHOLD * 0.8) { // Slightly lower threshold for errors
247
- const resultFiles = row.result_files ? this.parseJsonArray(row.result_files) : [];
248
- const file = row.file_path || resultFiles[0] || 'unknown';
249
-
250
- matches.push({
251
- type: 'fix',
252
- similarity,
253
- when: new Date(row.timestamp * 1000),
254
- file,
255
- snippet: row.query.slice(0, 100),
256
- message: formatTimeAgoWithContext(new Date(row.timestamp * 1000), 'encountered a similar issue', file),
257
- });
258
- }
259
- }
260
- } catch (error) {
261
- console.error('Error searching past fixes:', error);
262
- }
263
-
264
- return matches;
265
- }
266
-
267
- /**
268
- * Record that a query was made (for future déjà vu detection)
269
- */
270
- recordQuery(query: string, files: string[], wasUseful?: boolean): void {
271
- try {
272
- const queryHash = this.hashQuery(query);
273
-
274
- const stmt = this.db.prepare(`
275
- INSERT INTO query_patterns (query_hash, query_text, result_files, hit_count, last_used)
276
- VALUES (?, ?, ?, 1, unixepoch())
277
- ON CONFLICT(query_hash) DO UPDATE SET
278
- hit_count = hit_count + 1,
279
- last_used = unixepoch()
280
- `);
281
-
282
- stmt.run(queryHash, query, JSON.stringify(files));
283
-
284
- // Update usefulness if provided
285
- if (wasUseful !== undefined) {
286
- this.updateUsefulness(queryHash, wasUseful);
287
- }
288
- } catch (error) {
289
- console.error('Error recording query:', error);
290
- }
291
- }
292
-
293
- /**
294
- * Update usefulness score for a query
295
- */
296
- updateUsefulness(queryHashOrText: string, wasUseful: boolean): void {
297
- try {
298
- const queryHash = queryHashOrText.length === 16
299
- ? queryHashOrText
300
- : this.hashQuery(queryHashOrText);
301
-
302
- // Exponential moving average
303
- const alpha = 0.3;
304
- const newScore = wasUseful ? 1.0 : 0.0;
305
-
306
- const stmt = this.db.prepare(`
307
- UPDATE query_patterns
308
- SET avg_usefulness = avg_usefulness * (1 - ?) + ? * ?
309
- WHERE query_hash = ?
310
- `);
311
-
312
- stmt.run(alpha, alpha, newScore, queryHash);
313
- } catch (error) {
314
- console.error('Error updating usefulness:', error);
315
- }
316
- }
317
-
318
- /**
319
- * Get déjà vu statistics
320
- */
321
- getStats(): { totalQueries: number; usefulQueries: number; avgUsefulness: number } {
322
- try {
323
- const stmt = this.db.prepare(`
324
- SELECT
325
- COUNT(*) as total,
326
- SUM(CASE WHEN avg_usefulness >= ? THEN 1 ELSE 0 END) as useful,
327
- AVG(avg_usefulness) as avg_useful
328
- FROM query_patterns
329
- WHERE last_used > unixepoch() - ? * 86400
330
- `);
331
-
332
- const result = stmt.get(this.MIN_USEFULNESS, this.MAX_AGE_DAYS) as {
333
- total: number;
334
- useful: number;
335
- avg_useful: number;
336
- };
337
-
338
- return {
339
- totalQueries: result.total || 0,
340
- usefulQueries: result.useful || 0,
341
- avgUsefulness: result.avg_useful || 0,
342
- };
343
- } catch {
344
- return { totalQueries: 0, usefulQueries: 0, avgUsefulness: 0 };
345
- }
346
- }
347
-
348
- // ========== Private Methods ==========
349
-
350
- private calculateTextSimilarity(a: string, b: string): number {
351
- // Normalize texts
352
- const normalizeText = (text: string) =>
353
- text.toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/).filter(w => w.length > 2);
354
-
355
- const wordsA = new Set(normalizeText(a));
356
- const wordsB = new Set(normalizeText(b));
357
-
358
- if (wordsA.size === 0 || wordsB.size === 0) {
359
- return 0;
360
- }
361
-
362
- // Jaccard similarity
363
- let intersection = 0;
364
- for (const word of wordsA) {
365
- if (wordsB.has(word)) {
366
- intersection++;
367
- }
368
- }
369
-
370
- const union = wordsA.size + wordsB.size - intersection;
371
- return intersection / union;
372
- }
373
-
374
- private looksLikeError(query: string): boolean {
375
- const errorPatterns = [
376
- /error/i,
377
- /exception/i,
378
- /failed/i,
379
- /failing/i,
380
- /broken/i,
381
- /fix/i,
382
- /bug/i,
383
- /issue/i,
384
- /not working/i,
385
- /doesn't work/i,
386
- /crash/i,
387
- /undefined/i,
388
- /null/i,
389
- /NaN/i,
390
- /TypeError/i,
391
- /ReferenceError/i,
392
- /SyntaxError/i,
393
- ];
394
-
395
- return errorPatterns.some(pattern => pattern.test(query));
396
- }
397
-
398
- private getRecencyScore(date: Date): number {
399
- const now = new Date();
400
- const diffMs = now.getTime() - date.getTime();
401
- const diffDays = diffMs / (1000 * 60 * 60 * 24);
402
-
403
- // Exponential decay: score of 1 for today, approaching 0 at MAX_AGE_DAYS
404
- return Math.exp(-diffDays / (this.MAX_AGE_DAYS / 3));
405
- }
406
-
407
- private parseJsonArray(json: string | null): string[] {
408
- if (!json) return [];
409
- try {
410
- const parsed = JSON.parse(json);
411
- return Array.isArray(parsed) ? parsed : [];
412
- } catch {
413
- return [];
414
- }
415
- }
416
-
417
- private hashQuery(query: string): string {
418
- const normalized = query.toLowerCase().trim().replace(/\s+/g, ' ');
419
- return createHash('md5').update(normalized).digest('hex').slice(0, 16);
420
- }
421
- }