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,465 +0,0 @@
1
- /**
2
- * Ghost Mode - Silent Intelligence Layer
3
- *
4
- * Silently tracks what files Claude reads/writes. When code is written that
5
- * touches a file with recorded decisions, automatically checks for conflicts.
6
- * Makes NeuronLayer feel "telepathic" by surfacing relevant context proactively.
7
- */
8
-
9
- import type { Decision } from '../types/index.js';
10
- import type { Tier2Storage } from '../storage/tier2.js';
11
- import type { EmbeddingGenerator } from '../indexing/embeddings.js';
12
-
13
- export interface ConflictWarning {
14
- decision: Decision;
15
- warning: string;
16
- severity: 'low' | 'medium' | 'high';
17
- matchedTerms: string[];
18
- }
19
-
20
- export interface FileContext {
21
- path: string;
22
- accessedAt: Date;
23
- relatedDecisions: Decision[];
24
- relatedPatterns: string[];
25
- accessCount: number;
26
- }
27
-
28
- export interface GhostInsight {
29
- activeFiles: string[];
30
- recentDecisions: Decision[];
31
- potentialConflicts: ConflictWarning[];
32
- suggestions: string[];
33
- }
34
-
35
- // Decision keywords that indicate strong stances
36
- const DECISION_INDICATORS = [
37
- 'use', 'always', 'never', 'prefer', 'avoid', 'must', 'should',
38
- 'instead of', 'rather than', 'not', 'don\'t', 'do not',
39
- ];
40
-
41
- // Technology/pattern keywords for matching
42
- const TECH_PATTERNS = [
43
- // Auth
44
- { pattern: /\b(jwt|json\s*web\s*token)\b/i, category: 'auth', term: 'JWT' },
45
- { pattern: /\b(session|cookie)\b/i, category: 'auth', term: 'session' },
46
- { pattern: /\b(oauth|o-?auth)\b/i, category: 'auth', term: 'OAuth' },
47
- // Database
48
- { pattern: /\b(sql|mysql|postgres|postgresql)\b/i, category: 'database', term: 'SQL' },
49
- { pattern: /\b(mongo|mongodb|nosql)\b/i, category: 'database', term: 'MongoDB' },
50
- { pattern: /\b(redis|memcache)\b/i, category: 'cache', term: 'Redis' },
51
- // State
52
- { pattern: /\b(redux|zustand|mobx)\b/i, category: 'state', term: 'state-management' },
53
- { pattern: /\b(context\s*api|useContext)\b/i, category: 'state', term: 'Context API' },
54
- // Testing
55
- { pattern: /\b(jest|vitest|mocha)\b/i, category: 'testing', term: 'testing-framework' },
56
- { pattern: /\b(enzyme|testing-library|rtl)\b/i, category: 'testing', term: 'testing-library' },
57
- // API
58
- { pattern: /\b(rest|restful)\b/i, category: 'api', term: 'REST' },
59
- { pattern: /\b(graphql|gql)\b/i, category: 'api', term: 'GraphQL' },
60
- { pattern: /\b(grpc|protobuf)\b/i, category: 'api', term: 'gRPC' },
61
- // Style
62
- { pattern: /\b(tailwind|tailwindcss)\b/i, category: 'styling', term: 'Tailwind' },
63
- { pattern: /\b(styled-components|emotion)\b/i, category: 'styling', term: 'CSS-in-JS' },
64
- { pattern: /\b(sass|scss|less)\b/i, category: 'styling', term: 'CSS preprocessor' },
65
- ];
66
-
67
- export interface FileImpact {
68
- changedFile: string;
69
- affectedFiles: string[];
70
- timestamp: Date;
71
- }
72
-
73
- export class GhostMode {
74
- private activeFiles: Map<string, FileContext> = new Map();
75
- private recentDecisions: Map<string, Decision[]> = new Map();
76
- private recentImpacts: Map<string, FileImpact> = new Map(); // Track file impacts
77
- private tier2: Tier2Storage;
78
- private embeddingGenerator: EmbeddingGenerator;
79
-
80
- // Ghost mode settings
81
- private readonly MAX_ACTIVE_FILES = 20;
82
- private readonly FILE_TTL_MS = 60 * 60 * 1000; // 1 hour
83
- private readonly DECISION_CACHE_SIZE = 50;
84
- private readonly IMPACT_TTL_MS = 30 * 60 * 1000; // 30 minutes
85
-
86
- constructor(tier2: Tier2Storage, embeddingGenerator: EmbeddingGenerator) {
87
- this.tier2 = tier2;
88
- this.embeddingGenerator = embeddingGenerator;
89
- }
90
-
91
- /**
92
- * Called when a file change impacts other files
93
- */
94
- onFileImpact(changedFile: string, affectedFiles: string[]): void {
95
- const impact: FileImpact = {
96
- changedFile,
97
- affectedFiles,
98
- timestamp: new Date()
99
- };
100
-
101
- // Store impact for each affected file
102
- for (const file of affectedFiles) {
103
- this.recentImpacts.set(file, impact);
104
- }
105
-
106
- // Evict old impacts
107
- this.evictStaleImpacts();
108
- }
109
-
110
- /**
111
- * Check if a file was recently impacted by changes to another file
112
- */
113
- getImpactWarning(filePath: string): FileImpact | null {
114
- const impact = this.recentImpacts.get(filePath);
115
- if (!impact) return null;
116
-
117
- // Check if still within TTL
118
- const age = Date.now() - impact.timestamp.getTime();
119
- if (age > this.IMPACT_TTL_MS) {
120
- this.recentImpacts.delete(filePath);
121
- return null;
122
- }
123
-
124
- return impact;
125
- }
126
-
127
- private evictStaleImpacts(): void {
128
- const now = Date.now();
129
- for (const [file, impact] of this.recentImpacts) {
130
- if (now - impact.timestamp.getTime() > this.IMPACT_TTL_MS) {
131
- this.recentImpacts.delete(file);
132
- }
133
- }
134
- }
135
-
136
- /**
137
- * Called when any file is read - silently track and pre-fetch decisions
138
- */
139
- async onFileAccess(filePath: string): Promise<void> {
140
- const now = new Date();
141
-
142
- // Update or create file context
143
- const existing = this.activeFiles.get(filePath);
144
- if (existing) {
145
- existing.accessedAt = now;
146
- existing.accessCount++;
147
- } else {
148
- // Pre-fetch related decisions for this file
149
- const relatedDecisions = await this.findRelatedDecisions(filePath);
150
- const relatedPatterns = await this.findRelatedPatterns(filePath);
151
-
152
- this.activeFiles.set(filePath, {
153
- path: filePath,
154
- accessedAt: now,
155
- relatedDecisions,
156
- relatedPatterns,
157
- accessCount: 1,
158
- });
159
-
160
- // Cache decisions for quick conflict checking
161
- this.recentDecisions.set(filePath, relatedDecisions);
162
- }
163
-
164
- // Evict stale entries
165
- this.evictStaleFiles();
166
- }
167
-
168
- /**
169
- * Called before code is written - returns potential conflicts
170
- */
171
- checkConflicts(code: string, targetFile?: string): ConflictWarning[] {
172
- const warnings: ConflictWarning[] = [];
173
- const codeTerms = this.extractTerms(code);
174
-
175
- // Get decisions to check against
176
- let decisionsToCheck: Decision[] = [];
177
-
178
- if (targetFile && this.recentDecisions.has(targetFile)) {
179
- decisionsToCheck = this.recentDecisions.get(targetFile) || [];
180
- } else {
181
- // Check against all cached decisions
182
- for (const decisions of this.recentDecisions.values()) {
183
- decisionsToCheck.push(...decisions);
184
- }
185
- // Deduplicate
186
- decisionsToCheck = this.deduplicateDecisions(decisionsToCheck);
187
- }
188
-
189
- // Check each decision for conflicts
190
- for (const decision of decisionsToCheck) {
191
- const conflict = this.detectConflict(code, codeTerms, decision);
192
- if (conflict) {
193
- warnings.push(conflict);
194
- }
195
- }
196
-
197
- // Sort by severity
198
- warnings.sort((a, b) => {
199
- const severityOrder = { high: 0, medium: 1, low: 2 };
200
- return severityOrder[a.severity] - severityOrder[b.severity];
201
- });
202
-
203
- return warnings;
204
- }
205
-
206
- /**
207
- * Get current ghost insight - what the system knows about current work
208
- */
209
- getInsight(): GhostInsight {
210
- const activeFiles = Array.from(this.activeFiles.keys());
211
- const recentDecisions = this.getRecentUniqueDecisions();
212
- const suggestions = this.generateSuggestions();
213
-
214
- return {
215
- activeFiles,
216
- recentDecisions,
217
- potentialConflicts: [],
218
- suggestions,
219
- };
220
- }
221
-
222
- /**
223
- * Get ghost insight with conflict check for specific code
224
- */
225
- getInsightForCode(code: string, targetFile?: string): GhostInsight {
226
- const insight = this.getInsight();
227
- insight.potentialConflicts = this.checkConflicts(code, targetFile);
228
- return insight;
229
- }
230
-
231
- /**
232
- * Clear ghost mode state
233
- */
234
- clear(): void {
235
- this.activeFiles.clear();
236
- this.recentDecisions.clear();
237
- }
238
-
239
- /**
240
- * Get files most recently accessed
241
- */
242
- getRecentFiles(limit: number = 10): string[] {
243
- return Array.from(this.activeFiles.entries())
244
- .sort((a, b) => b[1].accessedAt.getTime() - a[1].accessedAt.getTime())
245
- .slice(0, limit)
246
- .map(([path]) => path);
247
- }
248
-
249
- /**
250
- * Get decisions related to recently accessed files
251
- */
252
- getRecentUniqueDecisions(limit: number = 10): Decision[] {
253
- const allDecisions: Decision[] = [];
254
- for (const decisions of this.recentDecisions.values()) {
255
- allDecisions.push(...decisions);
256
- }
257
- return this.deduplicateDecisions(allDecisions).slice(0, limit);
258
- }
259
-
260
- // ========== Private Methods ==========
261
-
262
- private async findRelatedDecisions(filePath: string): Promise<Decision[]> {
263
- try {
264
- // Search decisions by file path
265
- const pathParts = filePath.split(/[/\\]/);
266
- const fileName = pathParts[pathParts.length - 1] || '';
267
- const dirName = pathParts[pathParts.length - 2] || '';
268
-
269
- // Create a search query from file context
270
- const searchQuery = `${dirName} ${fileName.replace(/\.[^.]+$/, '')}`;
271
- const embedding = await this.embeddingGenerator.embed(searchQuery);
272
-
273
- return this.tier2.searchDecisions(embedding, 5);
274
- } catch {
275
- return [];
276
- }
277
- }
278
-
279
- private findRelatedPatterns(filePath: string): string[] {
280
- // Extract patterns from file extension and path
281
- const patterns: string[] = [];
282
-
283
- if (filePath.includes('test') || filePath.includes('spec')) {
284
- patterns.push('testing');
285
- }
286
- if (filePath.includes('api') || filePath.includes('route')) {
287
- patterns.push('api');
288
- }
289
- if (filePath.includes('component') || filePath.includes('ui')) {
290
- patterns.push('ui');
291
- }
292
- if (filePath.includes('model') || filePath.includes('schema')) {
293
- patterns.push('data-model');
294
- }
295
- if (filePath.includes('auth')) {
296
- patterns.push('authentication');
297
- }
298
-
299
- return patterns;
300
- }
301
-
302
- private extractTerms(text: string): Set<string> {
303
- const terms = new Set<string>();
304
-
305
- // Extract technology/pattern terms
306
- for (const { pattern, term } of TECH_PATTERNS) {
307
- if (pattern.test(text)) {
308
- terms.add(term.toLowerCase());
309
- }
310
- }
311
-
312
- // Extract common programming terms
313
- const words = text.toLowerCase().match(/\b[a-z]+\b/g) || [];
314
- for (const word of words) {
315
- if (word.length > 3) {
316
- terms.add(word);
317
- }
318
- }
319
-
320
- return terms;
321
- }
322
-
323
- private detectConflict(
324
- code: string,
325
- codeTerms: Set<string>,
326
- decision: Decision
327
- ): ConflictWarning | null {
328
- const decisionText = `${decision.title} ${decision.description}`.toLowerCase();
329
- const decisionTerms = this.extractTerms(decisionText);
330
-
331
- // Check for technology conflicts
332
- const matchedTerms: string[] = [];
333
- for (const term of codeTerms) {
334
- if (decisionTerms.has(term)) {
335
- matchedTerms.push(term);
336
- }
337
- }
338
-
339
- if (matchedTerms.length === 0) {
340
- return null;
341
- }
342
-
343
- // Check if decision opposes this technology
344
- const negativePattern = new RegExp(
345
- `(don't|do not|never|avoid|not)\\s+.{0,30}\\b(${matchedTerms.join('|')})\\b`,
346
- 'i'
347
- );
348
- const preferOtherPattern = new RegExp(
349
- `(instead of|rather than)\\s+.{0,30}\\b(${matchedTerms.join('|')})\\b`,
350
- 'i'
351
- );
352
- const isNegative = negativePattern.test(decisionText) || preferOtherPattern.test(decisionText);
353
-
354
- if (!isNegative) {
355
- // Check if decision uses a different technology in the same category
356
- for (const { pattern, category, term } of TECH_PATTERNS) {
357
- if (matchedTerms.some(m => m.toLowerCase() === term.toLowerCase())) {
358
- continue; // Skip the term we're using
359
- }
360
-
361
- if (pattern.test(decisionText)) {
362
- // Decision mentions a different tech in same category
363
- const codeUsesCategory = matchedTerms.some(m => {
364
- const match = TECH_PATTERNS.find(p => p.term.toLowerCase() === m.toLowerCase());
365
- return match && match.category === category;
366
- });
367
-
368
- if (codeUsesCategory) {
369
- return {
370
- decision,
371
- warning: `This code uses ${matchedTerms.join(', ')} but decision "${decision.title}" suggests using ${term}`,
372
- severity: 'medium',
373
- matchedTerms,
374
- };
375
- }
376
- }
377
- }
378
-
379
- return null;
380
- }
381
-
382
- // Determine severity
383
- let severity: 'low' | 'medium' | 'high' = 'low';
384
- if (decisionText.includes('must') || decisionText.includes('never') || decisionText.includes('always')) {
385
- severity = 'high';
386
- } else if (decisionText.includes('should') || decisionText.includes('prefer')) {
387
- severity = 'medium';
388
- }
389
-
390
- return {
391
- decision,
392
- warning: `This code may conflict with decision: "${decision.title}"`,
393
- severity,
394
- matchedTerms,
395
- };
396
- }
397
-
398
- private generateSuggestions(): string[] {
399
- const suggestions: string[] = [];
400
-
401
- // Suggest based on active files
402
- const recentFiles = this.getRecentFiles(5);
403
- if (recentFiles.length > 0) {
404
- const categories = new Set<string>();
405
- for (const file of recentFiles) {
406
- const ctx = this.activeFiles.get(file);
407
- if (ctx) {
408
- ctx.relatedPatterns.forEach(p => categories.add(p));
409
- }
410
- }
411
-
412
- if (categories.size > 0) {
413
- suggestions.push(`Working on: ${Array.from(categories).join(', ')}`);
414
- }
415
- }
416
-
417
- // Suggest based on decisions
418
- const decisions = this.getRecentUniqueDecisions(3);
419
- if (decisions.length > 0) {
420
- suggestions.push(`Relevant decisions: ${decisions.map(d => d.title).join(', ')}`);
421
- }
422
-
423
- return suggestions;
424
- }
425
-
426
- private deduplicateDecisions(decisions: Decision[]): Decision[] {
427
- const seen = new Set<string>();
428
- return decisions.filter(d => {
429
- if (seen.has(d.id)) {
430
- return false;
431
- }
432
- seen.add(d.id);
433
- return true;
434
- });
435
- }
436
-
437
- private evictStaleFiles(): void {
438
- const now = Date.now();
439
- const toEvict: string[] = [];
440
-
441
- for (const [path, context] of this.activeFiles.entries()) {
442
- if (now - context.accessedAt.getTime() > this.FILE_TTL_MS) {
443
- toEvict.push(path);
444
- }
445
- }
446
-
447
- // Evict stale files
448
- for (const path of toEvict) {
449
- this.activeFiles.delete(path);
450
- this.recentDecisions.delete(path);
451
- }
452
-
453
- // If still too many, evict oldest
454
- if (this.activeFiles.size > this.MAX_ACTIVE_FILES) {
455
- const entries = Array.from(this.activeFiles.entries())
456
- .sort((a, b) => a[1].accessedAt.getTime() - b[1].accessedAt.getTime());
457
-
458
- const toRemove = entries.slice(0, entries.length - this.MAX_ACTIVE_FILES);
459
- for (const [path] of toRemove) {
460
- this.activeFiles.delete(path);
461
- this.recentDecisions.delete(path);
462
- }
463
- }
464
- }
465
- }