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,1455 @@
1
+ import { join, basename } from 'path';
2
+ import { existsSync, mkdirSync, readFileSync, statSync } from 'fs';
3
+ import { initializeDatabase, closeDatabase } from '../storage/database.js';
4
+ import { Tier1Storage } from '../storage/tier1.js';
5
+ import { Tier2Storage } from '../storage/tier2.js';
6
+ import { Tier3Storage } from '../storage/tier3.js';
7
+ import { Indexer } from '../indexing/indexer.js';
8
+ import { ContextAssembler } from './context.js';
9
+ import { DecisionTracker } from './decisions.js';
10
+ import { DecisionExtractor } from './decision-extractor.js';
11
+ import { LearningEngine } from './learning.js';
12
+ import { FileSummarizer } from './summarizer.js';
13
+ import { ProjectManager, type ProjectInfo } from './project-manager.js';
14
+ import { ADRExporter, type ADRExportOptions } from './adr-exporter.js';
15
+ import { FeatureContextManager, type ResurrectedContext, type ContextResurrectionOptions } from './feature-context.js';
16
+ import { LivingDocumentationEngine } from './living-docs/index.js';
17
+ import { ContextRotPrevention } from './context-rot/index.js';
18
+ import { ConfidenceScorer } from './confidence/index.js';
19
+ import { ChangeIntelligence } from './change-intelligence/index.js';
20
+ import { ArchitectureEnforcement } from './architecture/index.js';
21
+ import { TestAwareness } from './test-awareness/index.js';
22
+ import { GhostMode, type GhostInsight, type ConflictWarning } from './ghost-mode.js';
23
+ import { DejaVuDetector, type DejaVuMatch } from './deja-vu.js';
24
+ import { CodeVerifier, type VerificationResult, type VerificationCheck, type ImportVerification, type SecurityScanResult, type DependencyCheckResult } from './code-verifier.js';
25
+ import { detectLanguage, getPreview, countLines } from '../utils/files.js';
26
+ import type { MemoryLayerConfig, AssembledContext, Decision, ProjectSummary, SearchResult, CodeSymbol, SymbolKind, ActiveFeatureContext, HotContext } from '../types/index.js';
27
+ import type { ArchitectureDoc, ComponentDoc, DailyChangelog, ChangelogOptions, ValidationResult, ActivityResult, UndocumentedItem, ContextHealth, CompactionResult, CompactionOptions, CriticalContext, DriftResult, ConfidenceResult, ConfidenceLevel, ConfidenceSources, ConflictResult, ChangeQueryResult, ChangeQueryOptions, Diagnosis, PastBug, FixSuggestion, Change, Pattern, PatternCategory, PatternValidationResult, ExistingFunction, TestInfo, TestFramework, TestValidationResult, TestUpdate, TestCoverage } from '../types/documentation.js';
28
+ import type Database from 'better-sqlite3';
29
+
30
+ // Re-export types for external use
31
+ export type { GhostInsight, ConflictWarning, DejaVuMatch, ResurrectedContext, VerificationResult, VerificationCheck, ImportVerification, SecurityScanResult, DependencyCheckResult };
32
+
33
+ export class MemoryLayerEngine {
34
+ private config: MemoryLayerConfig;
35
+ private db: Database.Database;
36
+ private tier1: Tier1Storage;
37
+ private tier2: Tier2Storage;
38
+ private tier3: Tier3Storage;
39
+ private indexer: Indexer;
40
+ private contextAssembler: ContextAssembler;
41
+ private decisionTracker: DecisionTracker;
42
+ private learningEngine: LearningEngine;
43
+ private summarizer: FileSummarizer;
44
+ private projectManager: ProjectManager;
45
+ private adrExporter: ADRExporter;
46
+ private featureContextManager: FeatureContextManager;
47
+ private livingDocs: LivingDocumentationEngine;
48
+ private contextRotPrevention: ContextRotPrevention;
49
+ private confidenceScorer: ConfidenceScorer;
50
+ private changeIntelligence: ChangeIntelligence;
51
+ private architectureEnforcement: ArchitectureEnforcement;
52
+ private testAwareness: TestAwareness;
53
+ private ghostMode: GhostMode;
54
+ private dejaVu: DejaVuDetector;
55
+ private codeVerifier: CodeVerifier;
56
+ private backgroundInterval: NodeJS.Timeout | null = null;
57
+ private initialized = false;
58
+ private initializationStatus: 'pending' | 'indexing' | 'ready' | 'error' = 'pending';
59
+ private indexingProgress: { indexed: number; total: number } = { indexed: 0, total: 0 };
60
+
61
+ // Background intelligence settings
62
+ private readonly BACKGROUND_REFRESH_INTERVAL_MS = 5 * 60 * 1000; // 5 minutes
63
+
64
+ constructor(config: MemoryLayerConfig) {
65
+ this.config = config;
66
+
67
+ // Ensure data directory exists
68
+ if (!existsSync(config.dataDir)) {
69
+ mkdirSync(config.dataDir, { recursive: true });
70
+ }
71
+
72
+ // Initialize database
73
+ const dbPath = join(config.dataDir, 'memorylayer.db');
74
+ this.db = initializeDatabase(dbPath);
75
+
76
+ // Initialize storage tiers
77
+ this.tier1 = new Tier1Storage(config.dataDir);
78
+ this.tier2 = new Tier2Storage(this.db);
79
+ this.tier3 = new Tier3Storage(this.db);
80
+
81
+ // Initialize indexer
82
+ this.indexer = new Indexer(config, this.tier2);
83
+
84
+ // Initialize context assembler
85
+ this.contextAssembler = new ContextAssembler(
86
+ this.tier1,
87
+ this.tier2,
88
+ this.tier3,
89
+ this.indexer.getEmbeddingGenerator()
90
+ );
91
+
92
+ // Initialize decision tracker
93
+ this.decisionTracker = new DecisionTracker(
94
+ this.tier1,
95
+ this.tier2,
96
+ this.indexer.getEmbeddingGenerator()
97
+ );
98
+
99
+ // Phase 3: Initialize learning engine and summarizer
100
+ this.learningEngine = new LearningEngine(this.db);
101
+ this.summarizer = new FileSummarizer(this.db);
102
+
103
+ // Phase 4: Initialize project manager and ADR exporter
104
+ this.projectManager = new ProjectManager();
105
+ this.adrExporter = new ADRExporter(config.projectPath);
106
+
107
+ // Phase 5: Initialize feature context manager
108
+ this.featureContextManager = new FeatureContextManager(config.projectPath, config.dataDir);
109
+
110
+ // Wire up feature context manager to context assembler
111
+ this.contextAssembler.setFeatureContextManager(this.featureContextManager);
112
+
113
+ // Phase 6: Initialize living documentation engine
114
+ this.livingDocs = new LivingDocumentationEngine(
115
+ config.projectPath,
116
+ config.dataDir,
117
+ this.db,
118
+ this.tier2
119
+ );
120
+
121
+ // Phase 7: Initialize context rot prevention
122
+ this.contextRotPrevention = new ContextRotPrevention(this.db, config.maxTokens);
123
+
124
+ // Phase 8: Initialize confidence scorer
125
+ this.confidenceScorer = new ConfidenceScorer(
126
+ this.tier2,
127
+ this.indexer.getEmbeddingGenerator()
128
+ );
129
+
130
+ // Phase 9: Initialize change intelligence
131
+ this.changeIntelligence = new ChangeIntelligence(
132
+ config.projectPath,
133
+ this.db,
134
+ this.tier2,
135
+ this.indexer.getEmbeddingGenerator()
136
+ );
137
+
138
+ // Phase 10: Initialize architecture enforcement
139
+ this.architectureEnforcement = new ArchitectureEnforcement(
140
+ this.db,
141
+ this.tier2,
142
+ this.indexer.getEmbeddingGenerator()
143
+ );
144
+
145
+ // Phase 11: Initialize test awareness
146
+ this.testAwareness = new TestAwareness(
147
+ config.projectPath,
148
+ this.db,
149
+ this.tier2
150
+ );
151
+
152
+ // Phase 12: Initialize Ghost Mode (silent intelligence layer)
153
+ this.ghostMode = new GhostMode(
154
+ this.tier2,
155
+ this.indexer.getEmbeddingGenerator()
156
+ );
157
+
158
+ // Phase 12: Initialize Déjà Vu Detector
159
+ this.dejaVu = new DejaVuDetector(
160
+ this.db,
161
+ this.tier2,
162
+ this.indexer.getEmbeddingGenerator()
163
+ );
164
+
165
+ // Phase 13: Initialize Code Verifier (pre-commit quality gate)
166
+ this.codeVerifier = new CodeVerifier(config.projectPath);
167
+
168
+ // Register this project
169
+ const projectInfo = this.projectManager.registerProject(config.projectPath);
170
+ this.projectManager.setActiveProject(projectInfo.id);
171
+
172
+ this.setupIndexerEvents();
173
+ }
174
+
175
+ private setupIndexerEvents(): void {
176
+ this.indexer.on('indexingStarted', () => {
177
+ // Silent start - only show if files need indexing
178
+ this.indexingProgress = { indexed: 0, total: 0 };
179
+ });
180
+
181
+ this.indexer.on('progress', (progress) => {
182
+ // Track progress for status visibility
183
+ this.indexingProgress = { indexed: progress.indexed, total: progress.total || 0 };
184
+
185
+ // Only show progress when actually indexing files
186
+ if (progress.indexed === 1) {
187
+ console.error('Indexing new/changed files...');
188
+ }
189
+ if (progress.indexed % 10 === 0) {
190
+ console.error(` ${progress.indexed} files indexed`);
191
+ }
192
+ });
193
+
194
+ this.indexer.on('indexingComplete', (stats: { total: number; indexed: number; skipped?: number }) => {
195
+ // Update final progress
196
+ this.indexingProgress = { indexed: stats.indexed, total: stats.total };
197
+
198
+ if (stats.indexed > 0) {
199
+ console.error(`Indexing complete: ${stats.indexed} files indexed`);
200
+ } else {
201
+ console.error(`Index up to date (${stats.total} files)`);
202
+ }
203
+ this.updateProjectSummary();
204
+ this.updateProjectStats();
205
+ // Extract decisions from git and comments
206
+ this.extractDecisions().catch(err => console.error('Decision extraction error:', err));
207
+ });
208
+
209
+ this.indexer.on('fileIndexed', (path) => {
210
+ // Track file in feature context
211
+ this.featureContextManager.onFileOpened(path);
212
+ });
213
+
214
+ this.indexer.on('error', (error) => {
215
+ console.error('Indexer error:', error);
216
+ });
217
+ }
218
+
219
+ async initialize(): Promise<void> {
220
+ if (this.initialized) return;
221
+
222
+ console.error(`Initializing MemoryLayer for: ${this.config.projectPath}`);
223
+
224
+ try {
225
+ // Perform initial indexing
226
+ this.initializationStatus = 'indexing';
227
+ await this.indexer.performInitialIndex();
228
+
229
+ // Start watching for changes
230
+ this.indexer.startWatching();
231
+
232
+ // Sync change intelligence from git
233
+ const synced = this.changeIntelligence.initialize();
234
+ if (synced > 0) {
235
+ console.error(`Synced ${synced} changes from git history`);
236
+ }
237
+
238
+ // Initialize architecture enforcement (learn patterns from codebase)
239
+ const archResult = this.architectureEnforcement.initialize();
240
+ if (archResult.patternsLearned > 0 || archResult.examplesAdded > 0) {
241
+ console.error(`Architecture enforcement: ${archResult.patternsLearned} patterns learned, ${archResult.examplesAdded} examples added`);
242
+ }
243
+
244
+ // Initialize test awareness (index tests)
245
+ const testResult = this.testAwareness.initialize();
246
+ if (testResult.testsIndexed > 0) {
247
+ console.error(`Test awareness: ${testResult.testsIndexed} tests indexed (${testResult.framework})`);
248
+ }
249
+
250
+ // Start background intelligence loop
251
+ this.startBackgroundIntelligence();
252
+
253
+ this.initialized = true;
254
+ this.initializationStatus = 'ready';
255
+ console.error('MemoryLayer initialized');
256
+ } catch (error) {
257
+ this.initializationStatus = 'error';
258
+ throw error;
259
+ }
260
+ }
261
+
262
+ /**
263
+ * Get the current engine status for visibility
264
+ */
265
+ getEngineStatus(): { status: string; ready: boolean; indexing: { indexed: number; total: number } } {
266
+ return {
267
+ status: this.initializationStatus,
268
+ ready: this.initialized,
269
+ indexing: this.indexingProgress
270
+ };
271
+ }
272
+
273
+ /**
274
+ * Background Intelligence Loop - continuously learn and update without user intervention
275
+ */
276
+ private startBackgroundIntelligence(): void {
277
+ // Clear any existing interval
278
+ if (this.backgroundInterval) {
279
+ clearInterval(this.backgroundInterval);
280
+ }
281
+
282
+ // Refresh git changes every 5 minutes
283
+ this.backgroundInterval = setInterval(() => {
284
+ try {
285
+ // Sync recent git changes
286
+ const synced = this.changeIntelligence.syncFromGit(20);
287
+ if (synced > 0) {
288
+ console.error(`[Background] Synced ${synced} recent changes from git`);
289
+ }
290
+
291
+ // Update importance scores for recently accessed files
292
+ this.learningEngine.updateImportanceScores();
293
+ } catch (error) {
294
+ // Silent fail for background tasks
295
+ console.error('[Background] Error in intelligence loop:', error);
296
+ }
297
+ }, this.BACKGROUND_REFRESH_INTERVAL_MS);
298
+ }
299
+
300
+ /**
301
+ * Record AI feedback - learn from what suggestions were actually used
302
+ */
303
+ recordAIFeedback(suggestion: string, wasUsed: boolean, correction?: string): void {
304
+ this.learningEngine.trackEvent({
305
+ eventType: wasUsed ? 'context_used' : 'context_ignored',
306
+ query: suggestion,
307
+ });
308
+
309
+ // If there was a correction, record it for learning
310
+ if (correction) {
311
+ this.dejaVu.recordQuery(correction, [], true);
312
+ }
313
+ }
314
+
315
+ async getContext(query: string, currentFile?: string, maxTokens?: number): Promise<AssembledContext> {
316
+ // Track the query
317
+ this.learningEngine.trackEvent({ eventType: 'query', query });
318
+
319
+ // Get expanded queries for better retrieval
320
+ const expandedQueries = this.learningEngine.expandQuery(query);
321
+
322
+ const result = await this.contextAssembler.assemble(query, {
323
+ currentFile,
324
+ maxTokens: maxTokens || this.config.maxTokens
325
+ });
326
+
327
+ // Track which files were included in context
328
+ for (const source of result.sources) {
329
+ this.learningEngine.trackEvent({ eventType: 'context_used', filePath: source, query });
330
+ }
331
+
332
+ // Track query pattern for future predictions
333
+ this.learningEngine.trackQuery(query, result.sources);
334
+
335
+ // Track in feature context
336
+ this.featureContextManager.onQuery(query, result.sources);
337
+
338
+ return result;
339
+ }
340
+
341
+ async searchCodebase(query: string, limit: number = 10): Promise<SearchResult[]> {
342
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(query);
343
+ let results = this.tier2.search(embedding, limit * 2); // Get more for re-ranking
344
+
345
+ // Apply personalized ranking
346
+ results = this.learningEngine.applyPersonalizedRanking(results);
347
+
348
+ return results.slice(0, limit);
349
+ }
350
+
351
+ async recordDecision(
352
+ title: string,
353
+ description: string,
354
+ files?: string[],
355
+ tags?: string[]
356
+ ): Promise<Decision> {
357
+ return this.decisionTracker.recordDecision(title, description, files || [], tags || []);
358
+ }
359
+
360
+ getRecentDecisions(limit: number = 10): Decision[] {
361
+ return this.decisionTracker.getRecentDecisions(limit);
362
+ }
363
+
364
+ /**
365
+ * Search decisions in current project by query
366
+ */
367
+ async searchDecisions(query: string, limit: number = 5): Promise<Decision[]> {
368
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(query);
369
+ return this.tier2.searchDecisions(embedding, limit);
370
+ }
371
+
372
+ async getFileContext(filePath: string): Promise<{ content: string; language: string; lines: number } | null> {
373
+ const absolutePath = join(this.config.projectPath, filePath);
374
+
375
+ if (!existsSync(absolutePath)) {
376
+ return null;
377
+ }
378
+
379
+ try {
380
+ // Check hot cache first
381
+ let content = this.learningEngine.getFromHotCache(filePath);
382
+
383
+ if (!content) {
384
+ content = readFileSync(absolutePath, 'utf-8');
385
+ // Add to hot cache for faster future access
386
+ this.learningEngine.addToHotCache(filePath, content);
387
+ }
388
+
389
+ const language = detectLanguage(filePath);
390
+ const lines = countLines(content);
391
+
392
+ // Track file view
393
+ this.learningEngine.trackEvent({ eventType: 'file_view', filePath });
394
+
395
+ // Track in feature context
396
+ this.featureContextManager.onFileOpened(filePath);
397
+
398
+ // Update Tier 1 with this as the active file
399
+ this.tier1.setActiveFile({
400
+ path: filePath,
401
+ content: getPreview(content, 2000),
402
+ language
403
+ });
404
+
405
+ return { content, language, lines };
406
+ } catch (error) {
407
+ console.error(`Error reading file ${filePath}:`, error);
408
+ return null;
409
+ }
410
+ }
411
+
412
+ getProjectSummary(): ProjectSummary {
413
+ const savedSummary = this.tier2.getProjectSummary();
414
+ const languages = this.tier2.getLanguages();
415
+ const totalFiles = this.tier2.getFileCount();
416
+ const totalLines = this.tier2.getTotalLines();
417
+ const recentDecisions = this.decisionTracker.getRecentDecisions(5);
418
+
419
+ // Try to detect dependencies from package.json or similar
420
+ const dependencies = this.detectDependencies();
421
+
422
+ return {
423
+ name: savedSummary?.name || basename(this.config.projectPath),
424
+ description: savedSummary?.description || 'No description available',
425
+ languages,
426
+ totalFiles,
427
+ totalLines,
428
+ keyDirectories: savedSummary?.keyDirectories || this.detectKeyDirectories(),
429
+ recentDecisions,
430
+ dependencies,
431
+ architectureNotes: savedSummary?.architectureNotes || ''
432
+ };
433
+ }
434
+
435
+ private updateProjectSummary(): void {
436
+ const languages = this.tier2.getLanguages();
437
+ const keyDirs = this.detectKeyDirectories();
438
+
439
+ this.tier2.updateProjectSummary(
440
+ basename(this.config.projectPath),
441
+ '',
442
+ languages,
443
+ keyDirs,
444
+ ''
445
+ );
446
+ }
447
+
448
+ // Phase 2: Auto-extract decisions from git commits and code comments
449
+ private async extractDecisions(): Promise<void> {
450
+ try {
451
+ const extractor = new DecisionExtractor(this.config.projectPath);
452
+ const extracted = await extractor.extractAll();
453
+
454
+ if (extracted.length === 0) {
455
+ return;
456
+ }
457
+
458
+ console.error(`Found ${extracted.length} potential decisions from git/comments`);
459
+
460
+ // Convert and store decisions (limit to avoid flooding)
461
+ const decisions = extractor.toDecisions(extracted.slice(0, 10));
462
+
463
+ for (const decision of decisions) {
464
+ // Check if we already have a similar decision
465
+ const existing = this.tier2.getRecentDecisions(50);
466
+ const isDuplicate = existing.some(d =>
467
+ d.title.toLowerCase() === decision.title.toLowerCase() ||
468
+ d.description.includes(decision.description.slice(0, 50))
469
+ );
470
+
471
+ if (!isDuplicate) {
472
+ // Generate embedding and store
473
+ const textToEmbed = `${decision.title}\n${decision.description}`;
474
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(textToEmbed);
475
+ this.tier2.upsertDecision(decision, embedding);
476
+ this.tier1.addDecision(decision);
477
+ }
478
+ }
479
+
480
+ console.error('Decision extraction complete');
481
+ } catch (error) {
482
+ console.error('Error extracting decisions:', error);
483
+ }
484
+ }
485
+
486
+ private detectKeyDirectories(): string[] {
487
+ const commonDirs = ['src', 'lib', 'app', 'pages', 'components', 'api', 'server', 'client', 'core'];
488
+ const found: string[] = [];
489
+
490
+ for (const dir of commonDirs) {
491
+ if (existsSync(join(this.config.projectPath, dir))) {
492
+ found.push(dir);
493
+ }
494
+ }
495
+
496
+ return found;
497
+ }
498
+
499
+ private detectDependencies(): string[] {
500
+ const deps: string[] = [];
501
+
502
+ // Check package.json
503
+ const packageJsonPath = join(this.config.projectPath, 'package.json');
504
+ if (existsSync(packageJsonPath)) {
505
+ try {
506
+ const pkg = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
507
+ const allDeps = {
508
+ ...pkg.dependencies,
509
+ ...pkg.devDependencies
510
+ };
511
+ deps.push(...Object.keys(allDeps).slice(0, 20)); // Limit to 20
512
+ } catch {
513
+ // Ignore parse errors
514
+ }
515
+ }
516
+
517
+ // Check requirements.txt
518
+ const requirementsPath = join(this.config.projectPath, 'requirements.txt');
519
+ if (existsSync(requirementsPath)) {
520
+ try {
521
+ const content = readFileSync(requirementsPath, 'utf-8');
522
+ const lines = content.split('\n')
523
+ .map(l => l.trim())
524
+ .filter(l => l && !l.startsWith('#'))
525
+ .map(l => l.split(/[=<>]/)[0]?.trim())
526
+ .filter((l): l is string => !!l);
527
+ deps.push(...lines.slice(0, 20));
528
+ } catch {
529
+ // Ignore
530
+ }
531
+ }
532
+
533
+ return deps;
534
+ }
535
+
536
+ setCurrentGoal(goal: string): void {
537
+ this.tier1.setCurrentGoal(goal);
538
+ }
539
+
540
+ // Phase 2: Symbol search
541
+ async searchSymbols(name: string, kind?: string, limit: number = 10): Promise<CodeSymbol[]> {
542
+ return this.tier2.searchSymbols(name, kind as SymbolKind | undefined, limit);
543
+ }
544
+
545
+ // Phase 2: Get file dependencies
546
+ getFileDependencies(filePath: string): {
547
+ imports: Array<{ file: string; symbols: string[] }>;
548
+ importedBy: Array<{ file: string; symbols: string[] }>;
549
+ symbols: Array<{ name: string; kind: string; line: number; exported: boolean }>;
550
+ } {
551
+ const file = this.tier2.getFile(filePath);
552
+
553
+ if (!file) {
554
+ return { imports: [], importedBy: [], symbols: [] };
555
+ }
556
+
557
+ // Get what this file imports
558
+ const fileImports = this.tier2.getImportsByFile(file.id);
559
+ const imports = fileImports.map(i => ({
560
+ file: i.importedFrom,
561
+ symbols: i.importedSymbols
562
+ }));
563
+
564
+ // Get files that import this file
565
+ const dependents = this.tier2.getFileDependents(filePath);
566
+ const importedBy = dependents.map(d => ({
567
+ file: d.file,
568
+ symbols: d.imports
569
+ }));
570
+
571
+ // Get symbols defined in this file
572
+ const fileSymbols = this.tier2.getSymbolsByFile(file.id);
573
+ const symbols = fileSymbols.map(s => ({
574
+ name: s.name,
575
+ kind: s.kind,
576
+ line: s.lineStart,
577
+ exported: s.exported
578
+ }));
579
+
580
+ return { imports, importedBy, symbols };
581
+ }
582
+
583
+ // Phase 2: Get symbol count
584
+ getSymbolCount(): number {
585
+ return this.tier2.getSymbolCount();
586
+ }
587
+
588
+ // Phase 3: Get predicted files for pre-fetching
589
+ getPredictedFiles(currentFile: string, query: string): string[] {
590
+ return this.learningEngine.predictNeededFiles(currentFile, query);
591
+ }
592
+
593
+ // Phase 3: Pre-fetch predicted files into hot cache
594
+ async preFetchFiles(currentFile: string, query: string): Promise<number> {
595
+ const predicted = this.learningEngine.predictNeededFiles(currentFile, query);
596
+ let fetched = 0;
597
+
598
+ for (const filePath of predicted) {
599
+ if (!this.learningEngine.isInHotCache(filePath)) {
600
+ const absolutePath = join(this.config.projectPath, filePath);
601
+ if (existsSync(absolutePath)) {
602
+ try {
603
+ const content = readFileSync(absolutePath, 'utf-8');
604
+ this.learningEngine.addToHotCache(filePath, content);
605
+ fetched++;
606
+ } catch {
607
+ // Skip files that can't be read
608
+ }
609
+ }
610
+ }
611
+ }
612
+
613
+ return fetched;
614
+ }
615
+
616
+ // Phase 3: Get file summary (compressed representation)
617
+ getFileSummary(filePath: string): string | null {
618
+ const file = this.tier2.getFile(filePath);
619
+ if (!file) return null;
620
+
621
+ // Check if we have a cached summary
622
+ const cached = this.summarizer.getSummary(file.id);
623
+ if (cached && !this.summarizer.needsRegeneration(file.id, file.lastModified)) {
624
+ return cached.summary;
625
+ }
626
+
627
+ // Generate new summary
628
+ const symbols = this.tier2.getSymbolsByFile(file.id);
629
+ const imports = this.tier2.getImportsByFile(file.id);
630
+ const exports = this.tier2.getExportsByFile(file.id);
631
+
632
+ const summary = this.summarizer.generateSummary(
633
+ filePath,
634
+ file.preview,
635
+ symbols,
636
+ imports.map(i => ({ importedFrom: i.importedFrom, importedSymbols: i.importedSymbols })),
637
+ exports.map(e => ({ exportedName: e.exportedName }))
638
+ );
639
+
640
+ // Store for future use
641
+ this.summarizer.storeSummary(file.id, summary);
642
+
643
+ return summary;
644
+ }
645
+
646
+ // Phase 3: Get summaries for multiple files (for compressed context)
647
+ getFileSummaries(filePaths: string[]): Map<string, string> {
648
+ const result = new Map<string, string>();
649
+
650
+ for (const filePath of filePaths) {
651
+ const summary = this.getFileSummary(filePath);
652
+ if (summary) {
653
+ result.set(filePath, summary);
654
+ }
655
+ }
656
+
657
+ return result;
658
+ }
659
+
660
+ // Phase 3: Get learning/usage statistics
661
+ getLearningStats(): {
662
+ usageStats: ReturnType<LearningEngine['getUsageStats']>;
663
+ compressionStats: ReturnType<FileSummarizer['getCompressionStats']>;
664
+ hotCacheStats: ReturnType<LearningEngine['getHotCacheStats']>;
665
+ } {
666
+ return {
667
+ usageStats: this.learningEngine.getUsageStats(),
668
+ compressionStats: this.summarizer.getCompressionStats(),
669
+ hotCacheStats: this.learningEngine.getHotCacheStats()
670
+ };
671
+ }
672
+
673
+ // Phase 3: Mark context as useful/not useful for learning
674
+ markContextUsefulness(query: string, wasUseful: boolean): void {
675
+ this.learningEngine.updateQueryUsefulness(query, wasUseful);
676
+ }
677
+
678
+ // Phase 3: Get frequently accessed files
679
+ getFrequentFiles(limit: number = 20): string[] {
680
+ return this.learningEngine.getFrequentFiles(limit);
681
+ }
682
+
683
+ // Phase 3: Expand query for better search
684
+ expandQuery(query: string): string[] {
685
+ return this.learningEngine.expandQuery(query);
686
+ }
687
+
688
+ // ========== Phase 4: Multi-Project & Team Features ==========
689
+
690
+ // Get all registered projects
691
+ listProjects(): ProjectInfo[] {
692
+ return this.projectManager.listProjects();
693
+ }
694
+
695
+ // Get current active project
696
+ getActiveProject(): ProjectInfo | null {
697
+ return this.projectManager.getActiveProject();
698
+ }
699
+
700
+ // Get project by ID
701
+ getProject(projectId: string): ProjectInfo | null {
702
+ return this.projectManager.getProject(projectId);
703
+ }
704
+
705
+ // Switch to a different project
706
+ switchProject(projectId: string): boolean {
707
+ return this.projectManager.setActiveProject(projectId);
708
+ }
709
+
710
+ // Discover projects in common locations
711
+ discoverProjects(): string[] {
712
+ return this.projectManager.discoverProjects();
713
+ }
714
+
715
+ // Cross-project search - search across all registered projects
716
+ async searchAllProjects(query: string, limit: number = 10): Promise<Array<{
717
+ project: string;
718
+ projectId: string;
719
+ results: SearchResult[];
720
+ }>> {
721
+ const allResults: Array<{
722
+ project: string;
723
+ projectId: string;
724
+ results: SearchResult[];
725
+ }> = [];
726
+
727
+ const projectDbs = this.projectManager.getProjectDatabases();
728
+
729
+ try {
730
+ // Generate embedding for query
731
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(query);
732
+
733
+ for (const { project, db } of projectDbs) {
734
+ try {
735
+ // Search each project's database
736
+ const tempTier2 = new Tier2Storage(db);
737
+ const results = tempTier2.search(embedding, limit);
738
+
739
+ if (results.length > 0) {
740
+ allResults.push({
741
+ project: project.name,
742
+ projectId: project.id,
743
+ results
744
+ });
745
+ }
746
+ } catch (err) {
747
+ console.error(`Error searching project ${project.name}:`, err);
748
+ }
749
+ }
750
+ } finally {
751
+ // Close all database connections
752
+ this.projectManager.closeAllDatabases(projectDbs);
753
+ }
754
+
755
+ // Sort by best match across projects
756
+ allResults.sort((a, b) => {
757
+ const maxA = Math.max(...a.results.map(r => r.similarity));
758
+ const maxB = Math.max(...b.results.map(r => r.similarity));
759
+ return maxB - maxA;
760
+ });
761
+
762
+ return allResults;
763
+ }
764
+
765
+ // Cross-project decision search
766
+ async searchAllDecisions(query: string, limit: number = 10): Promise<Array<{
767
+ project: string;
768
+ projectId: string;
769
+ decisions: Decision[];
770
+ }>> {
771
+ const allResults: Array<{
772
+ project: string;
773
+ projectId: string;
774
+ decisions: Decision[];
775
+ }> = [];
776
+
777
+ const projectDbs = this.projectManager.getProjectDatabases();
778
+
779
+ try {
780
+ // Generate embedding for query
781
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(query);
782
+
783
+ for (const { project, db } of projectDbs) {
784
+ try {
785
+ const tempTier2 = new Tier2Storage(db);
786
+ const decisions = tempTier2.searchDecisions(embedding, limit);
787
+
788
+ if (decisions.length > 0) {
789
+ allResults.push({
790
+ project: project.name,
791
+ projectId: project.id,
792
+ decisions
793
+ });
794
+ }
795
+ } catch (err) {
796
+ console.error(`Error searching decisions in ${project.name}:`, err);
797
+ }
798
+ }
799
+ } finally {
800
+ this.projectManager.closeAllDatabases(projectDbs);
801
+ }
802
+
803
+ return allResults;
804
+ }
805
+
806
+ // Record decision with author attribution
807
+ async recordDecisionWithAuthor(
808
+ title: string,
809
+ description: string,
810
+ author: string,
811
+ files?: string[],
812
+ tags?: string[],
813
+ status: 'proposed' | 'accepted' | 'deprecated' | 'superseded' = 'accepted'
814
+ ): Promise<Decision> {
815
+ const decision: Decision = {
816
+ id: crypto.randomUUID(),
817
+ title,
818
+ description,
819
+ files: files || [],
820
+ tags: tags || [],
821
+ createdAt: new Date(),
822
+ author,
823
+ status
824
+ };
825
+
826
+ // Generate embedding
827
+ const textToEmbed = `${title}\n${description}`;
828
+ const embedding = await this.indexer.getEmbeddingGenerator().embed(textToEmbed);
829
+
830
+ // Store in tier2
831
+ this.tier2.upsertDecision(decision, embedding);
832
+
833
+ // Add to tier1 for recent decisions
834
+ this.tier1.addDecision(decision);
835
+
836
+ return decision;
837
+ }
838
+
839
+ // Update decision status
840
+ updateDecisionStatus(
841
+ decisionId: string,
842
+ status: 'proposed' | 'accepted' | 'deprecated' | 'superseded',
843
+ supersededBy?: string
844
+ ): boolean {
845
+ return this.tier2.updateDecisionStatus(decisionId, status, supersededBy);
846
+ }
847
+
848
+ // Get all decisions (for export)
849
+ getAllDecisions(): Decision[] {
850
+ return this.tier2.getAllDecisions();
851
+ }
852
+
853
+ // Export single decision to ADR file
854
+ exportDecisionToADR(decisionId: string, options?: ADRExportOptions): string | null {
855
+ const decisions = this.getAllDecisions();
856
+ const decision = decisions.find(d => d.id === decisionId);
857
+
858
+ if (!decision) {
859
+ return null;
860
+ }
861
+
862
+ return this.adrExporter.exportDecision(decision, options);
863
+ }
864
+
865
+ // Export all decisions to ADR files
866
+ exportAllDecisionsToADR(options?: ADRExportOptions): string[] {
867
+ const decisions = this.getAllDecisions();
868
+ return this.adrExporter.exportAllDecisions(decisions, options);
869
+ }
870
+
871
+ // Update project stats (called after indexing)
872
+ private updateProjectStats(): void {
873
+ const project = this.projectManager.getActiveProject();
874
+ if (project) {
875
+ this.projectManager.updateProjectStats(project.id, {
876
+ totalFiles: this.tier2.getFileCount(),
877
+ totalDecisions: this.getAllDecisions().length,
878
+ languages: this.tier2.getLanguages()
879
+ });
880
+ }
881
+ }
882
+
883
+ // ========== Phase 5: Active Feature Context ==========
884
+
885
+ // Get the hot context for current feature
886
+ getHotContext(): HotContext {
887
+ return this.featureContextManager.getHotContext();
888
+ }
889
+
890
+ // Get current active feature context
891
+ getActiveFeatureContext(): ActiveFeatureContext | null {
892
+ return this.featureContextManager.getCurrentContext();
893
+ }
894
+
895
+ // Get summary of current feature context
896
+ getActiveContextSummary(): { name: string; files: number; changes: number; duration: number } | null {
897
+ return this.featureContextManager.getCurrentSummary();
898
+ }
899
+
900
+ // Start a new feature context
901
+ startFeatureContext(name?: string): ActiveFeatureContext {
902
+ return this.featureContextManager.startNewContext(name);
903
+ }
904
+
905
+ // Set feature context name
906
+ setFeatureContextName(name: string): boolean {
907
+ return this.featureContextManager.setContextName(name);
908
+ }
909
+
910
+ // Get recent feature contexts
911
+ getRecentFeatureContexts(): ActiveFeatureContext[] {
912
+ return this.featureContextManager.getRecentContexts();
913
+ }
914
+
915
+ // Switch to a previous feature context
916
+ switchFeatureContext(contextId: string): boolean {
917
+ return this.featureContextManager.switchToRecent(contextId);
918
+ }
919
+
920
+ // Complete current feature context
921
+ completeFeatureContext(): boolean {
922
+ return this.featureContextManager.completeContext();
923
+ }
924
+
925
+ // Track a file being opened (for external triggers)
926
+ trackFileOpened(filePath: string): void {
927
+ this.featureContextManager.onFileOpened(filePath);
928
+ }
929
+
930
+ // Track a file being edited
931
+ trackFileEdited(filePath: string, diff: string, linesChanged?: number[]): void {
932
+ this.featureContextManager.onFileEdited(filePath, diff, linesChanged || []);
933
+ }
934
+
935
+ // Track a query with files used
936
+ trackQuery(query: string, filesUsed: string[]): void {
937
+ this.featureContextManager.onQuery(query, filesUsed);
938
+ }
939
+
940
+ // Get feature context manager for direct access
941
+ getFeatureContextManager(): FeatureContextManager {
942
+ return this.featureContextManager;
943
+ }
944
+
945
+ // ========== Phase 6: Living Documentation ==========
946
+
947
+ // Get project architecture overview
948
+ async getArchitecture(): Promise<ArchitectureDoc> {
949
+ return this.livingDocs.generateArchitectureDocs();
950
+ }
951
+
952
+ // Get detailed documentation for a component/file
953
+ async getComponentDoc(path: string): Promise<ComponentDoc> {
954
+ return this.livingDocs.generateComponentDoc(path);
955
+ }
956
+
957
+ // Get changelog of recent changes
958
+ async getChangelog(options?: ChangelogOptions): Promise<DailyChangelog[]> {
959
+ return this.livingDocs.generateChangelog(options || {});
960
+ }
961
+
962
+ // Validate documentation status
963
+ async validateDocs(): Promise<ValidationResult> {
964
+ return this.livingDocs.validateDocs();
965
+ }
966
+
967
+ // Query recent project activity
968
+ async whatHappened(since: string, scope?: string): Promise<ActivityResult> {
969
+ return this.livingDocs.whatHappened(since, scope);
970
+ }
971
+
972
+ // Find undocumented code
973
+ async findUndocumented(options?: {
974
+ importance?: 'low' | 'medium' | 'high' | 'all';
975
+ type?: 'file' | 'function' | 'class' | 'interface' | 'all';
976
+ }): Promise<UndocumentedItem[]> {
977
+ return this.livingDocs.findUndocumented(options);
978
+ }
979
+
980
+ // ========== Phase 7: Context Rot Prevention ==========
981
+
982
+ // Get context health status
983
+ getContextHealth(): ContextHealth {
984
+ return this.contextRotPrevention.getContextHealth();
985
+ }
986
+
987
+ // Set current token count (for external tracking)
988
+ setContextTokens(tokens: number): void {
989
+ this.contextRotPrevention.setCurrentTokens(tokens);
990
+ }
991
+
992
+ // Detect drift from initial requirements
993
+ detectDrift(): DriftResult {
994
+ return this.contextRotPrevention.detectDrift();
995
+ }
996
+
997
+ // Mark content as critical (never compress)
998
+ markCritical(
999
+ content: string,
1000
+ options?: {
1001
+ type?: CriticalContext['type'];
1002
+ reason?: string;
1003
+ source?: string;
1004
+ }
1005
+ ): CriticalContext {
1006
+ return this.contextRotPrevention.markCritical(content, options);
1007
+ }
1008
+
1009
+ // Get all critical context
1010
+ getCriticalContext(type?: CriticalContext['type']): CriticalContext[] {
1011
+ return this.contextRotPrevention.getCriticalContext(type);
1012
+ }
1013
+
1014
+ // Remove a critical context item
1015
+ removeCriticalContext(id: string): boolean {
1016
+ return this.contextRotPrevention.removeCritical(id);
1017
+ }
1018
+
1019
+ // Trigger context compaction
1020
+ triggerCompaction(options: CompactionOptions): CompactionResult {
1021
+ return this.contextRotPrevention.triggerCompaction(options);
1022
+ }
1023
+
1024
+ // Auto-compact based on current health
1025
+ autoCompact(): CompactionResult {
1026
+ return this.contextRotPrevention.autoCompact();
1027
+ }
1028
+
1029
+ // Add a message to conversation tracking
1030
+ addConversationMessage(role: 'user' | 'assistant' | 'system', content: string): void {
1031
+ this.contextRotPrevention.addMessage({ role, content });
1032
+ }
1033
+
1034
+ // Clear conversation history
1035
+ clearConversation(): void {
1036
+ this.contextRotPrevention.clearConversation();
1037
+ }
1038
+
1039
+ // Get context summary for AI (includes critical context and drift warnings)
1040
+ getContextSummaryForAI(): string {
1041
+ return this.contextRotPrevention.getContextSummaryForAI();
1042
+ }
1043
+
1044
+ // ========== Phase 8: Confidence Scoring ==========
1045
+
1046
+ // Get confidence score for code
1047
+ async getConfidence(code: string, context?: string): Promise<ConfidenceResult> {
1048
+ return this.confidenceScorer.getConfidence(code, context);
1049
+ }
1050
+
1051
+ // List sources for code suggestion
1052
+ async listConfidenceSources(code: string, context?: string, includeSnippets?: boolean): Promise<ConfidenceSources> {
1053
+ return this.confidenceScorer.listSources(code, context, includeSnippets);
1054
+ }
1055
+
1056
+ // Check for conflicts with past decisions
1057
+ async checkCodeConflicts(code: string): Promise<ConflictResult> {
1058
+ return this.confidenceScorer.checkConflicts(code);
1059
+ }
1060
+
1061
+ // Get confidence level indicator emoji
1062
+ getConfidenceIndicator(level: ConfidenceLevel): string {
1063
+ return ConfidenceScorer.getIndicator(level);
1064
+ }
1065
+
1066
+ // Format confidence result for display
1067
+ formatConfidenceResult(result: ConfidenceResult): string {
1068
+ return ConfidenceScorer.formatResult(result);
1069
+ }
1070
+
1071
+ // ========== Phase 9: Change Intelligence ==========
1072
+
1073
+ // Query what changed
1074
+ whatChanged(options: ChangeQueryOptions = {}): ChangeQueryResult {
1075
+ return this.changeIntelligence.whatChanged(options);
1076
+ }
1077
+
1078
+ // Get changes for a specific file
1079
+ whatChangedIn(file: string, limit?: number): Change[] {
1080
+ return this.changeIntelligence.whatChangedIn(file, limit);
1081
+ }
1082
+
1083
+ // Diagnose why something broke
1084
+ whyBroke(error: string, options?: { file?: string; line?: number }): Diagnosis {
1085
+ return this.changeIntelligence.whyBroke(error, options);
1086
+ }
1087
+
1088
+ // Find similar bugs from history
1089
+ findSimilarBugs(error: string, limit?: number): PastBug[] {
1090
+ return this.changeIntelligence.findSimilarBugs(error, limit);
1091
+ }
1092
+
1093
+ // Suggest fixes for an error
1094
+ suggestFix(error: string, context?: string): FixSuggestion[] {
1095
+ return this.changeIntelligence.suggestFix(error, context);
1096
+ }
1097
+
1098
+ // Get recent changes
1099
+ getRecentChanges(hours: number = 24): Change[] {
1100
+ return this.changeIntelligence.getRecentChanges(hours);
1101
+ }
1102
+
1103
+ // Format changes for display
1104
+ formatChanges(result: ChangeQueryResult): string {
1105
+ return ChangeIntelligence.formatChanges(result);
1106
+ }
1107
+
1108
+ // Format diagnosis for display
1109
+ formatDiagnosis(diagnosis: Diagnosis): string {
1110
+ return ChangeIntelligence.formatDiagnosis(diagnosis);
1111
+ }
1112
+
1113
+ // Format fix suggestions for display
1114
+ formatFixSuggestions(suggestions: FixSuggestion[]): string {
1115
+ return ChangeIntelligence.formatFixSuggestions(suggestions);
1116
+ }
1117
+
1118
+ // ========== Phase 10: Architecture Enforcement ==========
1119
+
1120
+ // Validate code against patterns
1121
+ validatePattern(code: string, type?: string): PatternValidationResult {
1122
+ const category = type === 'auto' || !type ? undefined : type as PatternCategory;
1123
+ return this.architectureEnforcement.validatePattern(code, category);
1124
+ }
1125
+
1126
+ // Suggest existing functions for an intent
1127
+ suggestExisting(intent: string, limit?: number): ExistingFunction[] {
1128
+ return this.architectureEnforcement.suggestExisting(intent, limit);
1129
+ }
1130
+
1131
+ // Learn a new pattern
1132
+ learnPattern(
1133
+ code: string,
1134
+ name: string,
1135
+ description?: string,
1136
+ category?: string
1137
+ ): { success: boolean; patternId?: string; message: string } {
1138
+ return this.architectureEnforcement.learnPattern(
1139
+ code,
1140
+ name,
1141
+ description,
1142
+ category as PatternCategory | undefined
1143
+ );
1144
+ }
1145
+
1146
+ // List all patterns
1147
+ listPatterns(category?: string): Pattern[] {
1148
+ return this.architectureEnforcement.listPatterns(category as PatternCategory | undefined);
1149
+ }
1150
+
1151
+ // Get a specific pattern
1152
+ getPattern(id: string): Pattern | null {
1153
+ return this.architectureEnforcement.getPattern(id);
1154
+ }
1155
+
1156
+ // Add example to existing pattern
1157
+ addPatternExample(
1158
+ patternId: string,
1159
+ code: string,
1160
+ explanation: string,
1161
+ isAntiPattern: boolean = false
1162
+ ): boolean {
1163
+ return this.architectureEnforcement.addExample(patternId, code, explanation, isAntiPattern);
1164
+ }
1165
+
1166
+ // Add rule to existing pattern
1167
+ addPatternRule(
1168
+ patternId: string,
1169
+ rule: string,
1170
+ severity: 'info' | 'warning' | 'critical'
1171
+ ): boolean {
1172
+ return this.architectureEnforcement.addRule(patternId, rule, severity);
1173
+ }
1174
+
1175
+ // Search patterns
1176
+ searchPatterns(query: string): Pattern[] {
1177
+ return this.architectureEnforcement.searchPatterns(query);
1178
+ }
1179
+
1180
+ // Delete a pattern
1181
+ deletePattern(id: string): boolean {
1182
+ return this.architectureEnforcement.deletePattern(id);
1183
+ }
1184
+
1185
+ // Get architecture statistics
1186
+ getArchitectureStats(): {
1187
+ patterns: {
1188
+ total: number;
1189
+ byCategory: Record<string, number>;
1190
+ topPatterns: Array<{ name: string; usageCount: number }>;
1191
+ };
1192
+ functions: {
1193
+ total: number;
1194
+ exported: number;
1195
+ byPurpose: Record<string, number>;
1196
+ };
1197
+ } {
1198
+ return this.architectureEnforcement.getStats();
1199
+ }
1200
+
1201
+ // Refresh the function index
1202
+ refreshArchitectureIndex(): void {
1203
+ this.architectureEnforcement.refreshIndex();
1204
+ }
1205
+
1206
+ // Format validation result for display
1207
+ formatValidationResult(result: PatternValidationResult): string {
1208
+ return ArchitectureEnforcement.formatValidationResult(result);
1209
+ }
1210
+
1211
+ // Format pattern list for display
1212
+ formatPatternList(patterns: Pattern[]): string {
1213
+ return ArchitectureEnforcement.formatPatternList(patterns);
1214
+ }
1215
+
1216
+ // Format existing suggestions for display
1217
+ formatExistingSuggestions(suggestions: ExistingFunction[]): string {
1218
+ return ArchitectureEnforcement.formatSuggestions(suggestions);
1219
+ }
1220
+
1221
+ // ========== Phase 11: Test-Aware Suggestions ==========
1222
+
1223
+ // Get tests related to a file or function
1224
+ getRelatedTests(file: string, fn?: string): TestInfo[] {
1225
+ return this.testAwareness.getRelatedTests(file, fn);
1226
+ }
1227
+
1228
+ // Get tests for a specific file
1229
+ getTestsForFile(file: string): TestInfo[] {
1230
+ return this.testAwareness.getTestsForFile(file);
1231
+ }
1232
+
1233
+ // Get all tests in the project
1234
+ getAllTests(): TestInfo[] {
1235
+ return this.testAwareness.getAllTests();
1236
+ }
1237
+
1238
+ // Check if a code change would break tests
1239
+ checkTests(code: string, file: string): TestValidationResult {
1240
+ return this.testAwareness.checkTests(code, file);
1241
+ }
1242
+
1243
+ // Suggest test updates for a change
1244
+ suggestTestUpdate(change: string, failingTests?: string[]): TestUpdate[] {
1245
+ return this.testAwareness.suggestTestUpdate(change, failingTests);
1246
+ }
1247
+
1248
+ // Get test coverage for a file
1249
+ getTestCoverage(file: string): TestCoverage {
1250
+ return this.testAwareness.getCoverage(file);
1251
+ }
1252
+
1253
+ // Get detected test framework
1254
+ getTestFramework(): TestFramework {
1255
+ return this.testAwareness.getFramework();
1256
+ }
1257
+
1258
+ // Get total test count
1259
+ getTestCount(): number {
1260
+ return this.testAwareness.getTestCount();
1261
+ }
1262
+
1263
+ // Generate test template for a function
1264
+ generateTestTemplate(file: string, functionName: string): string {
1265
+ return this.testAwareness.generateTestTemplate(file, functionName);
1266
+ }
1267
+
1268
+ // Suggest new tests for uncovered functions
1269
+ suggestNewTests(file: string): Array<{ function: string; template: string; priority: 'high' | 'medium' | 'low' }> {
1270
+ return this.testAwareness.suggestNewTests(file);
1271
+ }
1272
+
1273
+ // Refresh test index
1274
+ refreshTestIndex(): { testsIndexed: number; framework: TestFramework } {
1275
+ return this.testAwareness.refreshIndex();
1276
+ }
1277
+
1278
+ // Format test validation result for display
1279
+ formatTestValidationResult(result: TestValidationResult): string {
1280
+ return this.testAwareness.formatValidationResult(result);
1281
+ }
1282
+
1283
+ // Format test coverage for display
1284
+ formatTestCoverage(coverage: TestCoverage): string {
1285
+ return this.testAwareness.formatCoverage(coverage);
1286
+ }
1287
+
1288
+ // Format test list for display
1289
+ formatTestList(tests: TestInfo[]): string {
1290
+ return this.testAwareness.formatTestList(tests);
1291
+ }
1292
+
1293
+ // Format test updates for display
1294
+ formatTestUpdates(updates: TestUpdate[]): string {
1295
+ return this.testAwareness['testSuggester'].formatTestUpdates(updates);
1296
+ }
1297
+
1298
+ // ========== Phase 12: Ghost Mode + Déjà Vu ==========
1299
+
1300
+ /**
1301
+ * Get ghost insight - what the system knows about current work
1302
+ */
1303
+ getGhostInsight(): GhostInsight {
1304
+ return this.ghostMode.getInsight();
1305
+ }
1306
+
1307
+ /**
1308
+ * Get ghost insight with conflict check for specific code
1309
+ */
1310
+ getGhostInsightForCode(code: string, targetFile?: string): GhostInsight {
1311
+ return this.ghostMode.getInsightForCode(code, targetFile);
1312
+ }
1313
+
1314
+ /**
1315
+ * Check for conflicts with past decisions
1316
+ */
1317
+ checkGhostConflicts(code: string, targetFile?: string): ConflictWarning[] {
1318
+ return this.ghostMode.checkConflicts(code, targetFile);
1319
+ }
1320
+
1321
+ /**
1322
+ * Notify ghost mode of file access (for silent tracking)
1323
+ */
1324
+ async notifyFileAccess(filePath: string): Promise<void> {
1325
+ await this.ghostMode.onFileAccess(filePath);
1326
+ }
1327
+
1328
+ /**
1329
+ * Find similar past problems (déjà vu detection)
1330
+ */
1331
+ async findDejaVu(query: string, limit?: number): Promise<DejaVuMatch[]> {
1332
+ return this.dejaVu.findSimilar(query, limit);
1333
+ }
1334
+
1335
+ /**
1336
+ * Record query for future déjà vu detection
1337
+ */
1338
+ recordQueryForDejaVu(query: string, files: string[], wasUseful?: boolean): void {
1339
+ this.dejaVu.recordQuery(query, files, wasUseful);
1340
+ }
1341
+
1342
+ /**
1343
+ * Resurrect context from last session
1344
+ * "Welcome back! Last time you were working on X, stuck on Y"
1345
+ */
1346
+ resurrectContext(options?: ContextResurrectionOptions): ResurrectedContext {
1347
+ return this.featureContextManager.resurrectContext(options);
1348
+ }
1349
+
1350
+ /**
1351
+ * Get all contexts that can be resurrected
1352
+ */
1353
+ getResurrectableContexts(): Array<{ id: string; name: string; lastActive: Date; summary: string }> {
1354
+ return this.featureContextManager.getResurrectableContexts();
1355
+ }
1356
+
1357
+ /**
1358
+ * Get comprehensive ghost mode data
1359
+ * Combines ghost insight, déjà vu matches, and resurrection data
1360
+ */
1361
+ async getFullGhostData(
1362
+ mode: 'full' | 'conflicts' | 'dejavu' | 'resurrect' = 'full',
1363
+ options?: { code?: string; file?: string; query?: string }
1364
+ ): Promise<{
1365
+ ghost?: GhostInsight;
1366
+ dejaVu?: DejaVuMatch[];
1367
+ resurrection?: ResurrectedContext;
1368
+ conflicts?: ConflictWarning[];
1369
+ }> {
1370
+ const result: {
1371
+ ghost?: GhostInsight;
1372
+ dejaVu?: DejaVuMatch[];
1373
+ resurrection?: ResurrectedContext;
1374
+ conflicts?: ConflictWarning[];
1375
+ } = {};
1376
+
1377
+ if (mode === 'full' || mode === 'conflicts') {
1378
+ if (options?.code) {
1379
+ result.ghost = this.ghostMode.getInsightForCode(options.code, options.file);
1380
+ result.conflicts = result.ghost.potentialConflicts;
1381
+ } else {
1382
+ result.ghost = this.ghostMode.getInsight();
1383
+ }
1384
+ }
1385
+
1386
+ if (mode === 'full' || mode === 'dejavu') {
1387
+ if (options?.query || options?.code) {
1388
+ result.dejaVu = await this.dejaVu.findSimilar(options.query || options.code || '', 5);
1389
+ }
1390
+ }
1391
+
1392
+ if (mode === 'full' || mode === 'resurrect') {
1393
+ result.resurrection = this.featureContextManager.resurrectContext();
1394
+ }
1395
+
1396
+ return result;
1397
+ }
1398
+
1399
+ /**
1400
+ * Get déjà vu statistics
1401
+ */
1402
+ getDejaVuStats(): { totalQueries: number; usefulQueries: number; avgUsefulness: number } {
1403
+ return this.dejaVu.getStats();
1404
+ }
1405
+
1406
+ // ========== Phase 13: Code Verification (Pre-Commit Quality Gate) ==========
1407
+
1408
+ /**
1409
+ * Verify code for common AI-generated issues
1410
+ * Catches: hallucinated imports, security vulnerabilities, dependency issues
1411
+ */
1412
+ async verifyCode(
1413
+ code: string,
1414
+ file?: string,
1415
+ checks: VerificationCheck[] = ['all']
1416
+ ): Promise<VerificationResult> {
1417
+ return this.codeVerifier.verify(code, file, checks);
1418
+ }
1419
+
1420
+ /**
1421
+ * Quick security scan without full verification
1422
+ */
1423
+ quickSecurityScan(code: string, language?: string): SecurityScanResult {
1424
+ return this.codeVerifier.scanSecurity(code, language);
1425
+ }
1426
+
1427
+ /**
1428
+ * Verify imports only
1429
+ */
1430
+ verifyImports(code: string, file?: string): ImportVerification {
1431
+ return this.codeVerifier.verifyImports(code, file);
1432
+ }
1433
+
1434
+ /**
1435
+ * Check dependencies only
1436
+ */
1437
+ checkCodeDependencies(code: string): DependencyCheckResult {
1438
+ return this.codeVerifier.checkDependencies(code);
1439
+ }
1440
+
1441
+ shutdown(): void {
1442
+ console.error('Shutting down MemoryLayer...');
1443
+
1444
+ // Stop background intelligence
1445
+ if (this.backgroundInterval) {
1446
+ clearInterval(this.backgroundInterval);
1447
+ this.backgroundInterval = null;
1448
+ }
1449
+
1450
+ this.indexer.stopWatching();
1451
+ this.tier1.save();
1452
+ this.featureContextManager.shutdown();
1453
+ closeDatabase(this.db);
1454
+ }
1455
+ }