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