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.
- package/CONTRIBUTING.md +127 -0
- package/LICENSE +21 -0
- package/README.md +305 -0
- package/dist/index.js +38016 -0
- package/esbuild.config.js +26 -0
- package/package.json +63 -0
- package/src/cli/commands.ts +382 -0
- package/src/core/adr-exporter.ts +253 -0
- package/src/core/architecture/architecture-enforcement.ts +228 -0
- package/src/core/architecture/duplicate-detector.ts +288 -0
- package/src/core/architecture/index.ts +6 -0
- package/src/core/architecture/pattern-learner.ts +306 -0
- package/src/core/architecture/pattern-library.ts +403 -0
- package/src/core/architecture/pattern-validator.ts +324 -0
- package/src/core/change-intelligence/bug-correlator.ts +444 -0
- package/src/core/change-intelligence/change-intelligence.ts +221 -0
- package/src/core/change-intelligence/change-tracker.ts +334 -0
- package/src/core/change-intelligence/fix-suggester.ts +340 -0
- package/src/core/change-intelligence/index.ts +5 -0
- package/src/core/code-verifier.ts +843 -0
- package/src/core/confidence/confidence-scorer.ts +251 -0
- package/src/core/confidence/conflict-checker.ts +289 -0
- package/src/core/confidence/index.ts +5 -0
- package/src/core/confidence/source-tracker.ts +263 -0
- package/src/core/confidence/warning-detector.ts +241 -0
- package/src/core/context-rot/compaction.ts +284 -0
- package/src/core/context-rot/context-health.ts +243 -0
- package/src/core/context-rot/context-rot-prevention.ts +213 -0
- package/src/core/context-rot/critical-context.ts +221 -0
- package/src/core/context-rot/drift-detector.ts +255 -0
- package/src/core/context-rot/index.ts +7 -0
- package/src/core/context.ts +263 -0
- package/src/core/decision-extractor.ts +339 -0
- package/src/core/decisions.ts +69 -0
- package/src/core/deja-vu.ts +421 -0
- package/src/core/engine.ts +1455 -0
- package/src/core/feature-context.ts +726 -0
- package/src/core/ghost-mode.ts +412 -0
- package/src/core/learning.ts +485 -0
- package/src/core/living-docs/activity-tracker.ts +296 -0
- package/src/core/living-docs/architecture-generator.ts +428 -0
- package/src/core/living-docs/changelog-generator.ts +348 -0
- package/src/core/living-docs/component-generator.ts +230 -0
- package/src/core/living-docs/doc-engine.ts +110 -0
- package/src/core/living-docs/doc-validator.ts +282 -0
- package/src/core/living-docs/index.ts +8 -0
- package/src/core/project-manager.ts +297 -0
- package/src/core/summarizer.ts +267 -0
- package/src/core/test-awareness/change-validator.ts +499 -0
- package/src/core/test-awareness/index.ts +5 -0
- package/src/index.ts +49 -0
- package/src/indexing/ast.ts +563 -0
- package/src/indexing/embeddings.ts +85 -0
- package/src/indexing/indexer.ts +245 -0
- package/src/indexing/watcher.ts +78 -0
- package/src/server/gateways/aggregator.ts +374 -0
- package/src/server/gateways/index.ts +473 -0
- package/src/server/gateways/memory-ghost.ts +343 -0
- package/src/server/gateways/memory-query.ts +452 -0
- package/src/server/gateways/memory-record.ts +346 -0
- package/src/server/gateways/memory-review.ts +410 -0
- package/src/server/gateways/memory-status.ts +517 -0
- package/src/server/gateways/memory-verify.ts +392 -0
- package/src/server/gateways/router.ts +434 -0
- package/src/server/gateways/types.ts +610 -0
- package/src/server/mcp.ts +154 -0
- package/src/server/resources.ts +85 -0
- package/src/server/tools.ts +2261 -0
- package/src/storage/database.ts +262 -0
- package/src/storage/tier1.ts +135 -0
- package/src/storage/tier2.ts +764 -0
- package/src/storage/tier3.ts +123 -0
- package/src/types/documentation.ts +619 -0
- package/src/types/index.ts +222 -0
- package/src/utils/config.ts +193 -0
- package/src/utils/files.ts +117 -0
- package/src/utils/time.ts +37 -0
- 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
|
+
}
|