driftdetect 0.9.39 → 0.9.41

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.
@@ -0,0 +1,1833 @@
1
+ /**
2
+ * Memory Command - drift memory
3
+ *
4
+ * Enterprise-grade CLI for managing Cortex V2 memories.
5
+ * Provides full CRUD operations, search, learning, health monitoring,
6
+ * validation, consolidation, and integration with the drift ecosystem.
7
+ *
8
+ * Memory Types:
9
+ * - core: Project identity and preferences (never decays)
10
+ * - tribal: Institutional knowledge, gotchas, warnings (365 day half-life)
11
+ * - procedural: How-to knowledge, step-by-step procedures (180 day half-life)
12
+ * - semantic: Consolidated knowledge from episodic memories (90 day half-life)
13
+ * - episodic: Interaction records, raw material for consolidation (7 day half-life)
14
+ * - pattern_rationale: Why patterns exist in the codebase (180 day half-life)
15
+ * - constraint_override: Approved exceptions to constraints (90 day half-life)
16
+ * - decision_context: Human context for architectural decisions (180 day half-life)
17
+ * - code_smell: Patterns to avoid, anti-patterns (90 day half-life)
18
+ */
19
+ import * as fs from 'node:fs/promises';
20
+ import * as path from 'node:path';
21
+ import chalk from 'chalk';
22
+ import { Command } from 'commander';
23
+ import { createSpinner } from '../ui/spinner.js';
24
+ const DRIFT_DIR = '.drift';
25
+ const MEMORY_DIR = 'memory';
26
+ const MEMORY_DB = 'cortex.db';
27
+ const MEMORY_TYPES = {
28
+ core: { name: 'core', icon: '🏠', description: 'Project identity and preferences', halfLife: '∞' },
29
+ tribal: { name: 'tribal', icon: '⚠️', description: 'Institutional knowledge, gotchas', halfLife: '365d' },
30
+ procedural: { name: 'procedural', icon: '📋', description: 'How-to knowledge, procedures', halfLife: '180d' },
31
+ semantic: { name: 'semantic', icon: '💡', description: 'Consolidated knowledge', halfLife: '90d' },
32
+ episodic: { name: 'episodic', icon: '💭', description: 'Interaction records', halfLife: '7d' },
33
+ pattern_rationale: { name: 'pattern_rationale', icon: '🎯', description: 'Why patterns exist', halfLife: '180d' },
34
+ constraint_override: { name: 'constraint_override', icon: '✅', description: 'Approved exceptions', halfLife: '90d' },
35
+ decision_context: { name: 'decision_context', icon: '📝', description: 'Decision context', halfLife: '180d' },
36
+ code_smell: { name: 'code_smell', icon: '🚫', description: 'Patterns to avoid', halfLife: '90d' },
37
+ };
38
+ // ============================================================================
39
+ // Cortex Integration
40
+ // ============================================================================
41
+ /**
42
+ * Get Cortex instance (lazy loaded)
43
+ */
44
+ async function getCortex(rootDir) {
45
+ try {
46
+ const { getCortex: getGlobalCortex } = await import('driftdetect-cortex');
47
+ // Ensure memory directory exists
48
+ const memoryDir = path.join(rootDir, DRIFT_DIR, MEMORY_DIR);
49
+ await fs.mkdir(memoryDir, { recursive: true });
50
+ // Configure cortex with the project's database
51
+ const dbPath = path.join(memoryDir, MEMORY_DB);
52
+ return await getGlobalCortex({
53
+ storage: { type: 'sqlite', sqlitePath: dbPath },
54
+ autoInitialize: true,
55
+ });
56
+ }
57
+ catch (error) {
58
+ throw new Error(`Failed to initialize Cortex: ${error}`);
59
+ }
60
+ }
61
+ /**
62
+ * Check if memory system is initialized
63
+ */
64
+ async function memoryExists(rootDir) {
65
+ try {
66
+ await fs.access(path.join(rootDir, DRIFT_DIR, MEMORY_DIR, MEMORY_DB));
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ }
73
+ /**
74
+ * Show helpful message when no memories exist
75
+ */
76
+ function showNoMemoriesMessage() {
77
+ console.log();
78
+ console.log(chalk.yellow('⚠️ Memory system not initialized.'));
79
+ console.log();
80
+ console.log(chalk.gray('Initialize the memory system:'));
81
+ console.log();
82
+ console.log(chalk.cyan(' drift memory init'));
83
+ console.log();
84
+ console.log(chalk.gray('Or add your first memory:'));
85
+ console.log();
86
+ console.log(chalk.cyan(' drift memory add tribal "Always use bcrypt for password hashing"'));
87
+ console.log();
88
+ }
89
+ // ============================================================================
90
+ // Formatters
91
+ // ============================================================================
92
+ function getTypeIcon(type) {
93
+ return MEMORY_TYPES[type]?.icon ?? '📦';
94
+ }
95
+ function getConfidenceColor(confidence) {
96
+ const percent = Math.round(confidence * 100);
97
+ if (confidence >= 0.8)
98
+ return chalk.green(`${percent}%`);
99
+ if (confidence >= 0.5)
100
+ return chalk.yellow(`${percent}%`);
101
+ return chalk.red(`${percent}%`);
102
+ }
103
+ function getHealthColor(score) {
104
+ if (score >= 80)
105
+ return chalk.green(`${score}/100 (healthy)`);
106
+ if (score >= 50)
107
+ return chalk.yellow(`${score}/100 (warning)`);
108
+ return chalk.red(`${score}/100 (critical)`);
109
+ }
110
+ function getImportanceColor(importance) {
111
+ switch (importance) {
112
+ case 'critical': return chalk.red(importance);
113
+ case 'high': return chalk.yellow(importance);
114
+ case 'normal': return chalk.white(importance);
115
+ case 'low': return chalk.gray(importance);
116
+ default: return importance;
117
+ }
118
+ }
119
+ function formatMemoryBrief(memory) {
120
+ const icon = getTypeIcon(memory.type);
121
+ const confidence = getConfidenceColor(memory.confidence ?? 1);
122
+ const id = memory.id ?? 'unknown';
123
+ console.log(` ${icon} ${chalk.cyan(id.slice(0, 8))}... ${confidence}`);
124
+ console.log(` ${chalk.white(memory.summary ?? 'No summary')}`);
125
+ if (memory.tags?.length > 0) {
126
+ console.log(chalk.gray(` Tags: ${memory.tags.join(', ')}`));
127
+ }
128
+ }
129
+ function formatMemoryDetailed(memory, related = []) {
130
+ const icon = getTypeIcon(memory.type);
131
+ console.log();
132
+ console.log(chalk.bold(`${icon} ${memory.type.toUpperCase()}`));
133
+ console.log(chalk.gray('═'.repeat(60)));
134
+ console.log();
135
+ // Basic info
136
+ console.log(chalk.bold('Details'));
137
+ console.log(chalk.gray('─'.repeat(50)));
138
+ console.log(` ID: ${chalk.cyan(memory.id)}`);
139
+ console.log(` Type: ${memory.type}`);
140
+ console.log(` Confidence: ${getConfidenceColor(memory.confidence)}`);
141
+ console.log(` Importance: ${getImportanceColor(memory.importance)}`);
142
+ console.log(` Created: ${new Date(memory.createdAt).toLocaleString()}`);
143
+ console.log(` Updated: ${new Date(memory.updatedAt).toLocaleString()}`);
144
+ console.log(` Accessed: ${memory.accessCount} times`);
145
+ console.log();
146
+ // Summary
147
+ console.log(chalk.bold('Summary'));
148
+ console.log(chalk.gray('─'.repeat(50)));
149
+ console.log(` ${memory.summary}`);
150
+ console.log();
151
+ // Type-specific content
152
+ switch (memory.type) {
153
+ case 'tribal':
154
+ console.log(chalk.bold('Knowledge'));
155
+ console.log(chalk.gray('─'.repeat(50)));
156
+ console.log(` Topic: ${memory.topic}`);
157
+ console.log(` Severity: ${memory.severity}`);
158
+ console.log(` ${memory.knowledge}`);
159
+ if (memory.warnings?.length > 0) {
160
+ console.log();
161
+ console.log(chalk.bold('Warnings'));
162
+ for (const w of memory.warnings) {
163
+ console.log(` ⚠️ ${w}`);
164
+ }
165
+ }
166
+ break;
167
+ case 'procedural':
168
+ console.log(chalk.bold('Procedure'));
169
+ console.log(chalk.gray('─'.repeat(50)));
170
+ console.log(` Name: ${memory.name}`);
171
+ if (memory.steps?.length > 0) {
172
+ console.log();
173
+ console.log(chalk.bold('Steps'));
174
+ for (const step of memory.steps) {
175
+ console.log(` ${step.order}. ${step.action}`);
176
+ if (step.details)
177
+ console.log(chalk.gray(` ${step.details}`));
178
+ }
179
+ }
180
+ break;
181
+ case 'pattern_rationale':
182
+ console.log(chalk.bold('Pattern'));
183
+ console.log(chalk.gray('─'.repeat(50)));
184
+ console.log(` Pattern: ${memory.patternName}`);
185
+ console.log(` Category: ${memory.patternCategory}`);
186
+ console.log();
187
+ console.log(chalk.bold('Rationale'));
188
+ console.log(` ${memory.rationale}`);
189
+ if (memory.businessContext) {
190
+ console.log();
191
+ console.log(chalk.bold('Business Context'));
192
+ console.log(` ${memory.businessContext}`);
193
+ }
194
+ break;
195
+ case 'code_smell':
196
+ console.log(chalk.bold('Code Smell'));
197
+ console.log(chalk.gray('─'.repeat(50)));
198
+ console.log(` Name: ${memory.name}`);
199
+ console.log(` Severity: ${memory.severity}`);
200
+ console.log(` Reason: ${memory.reason}`);
201
+ if (memory.suggestion) {
202
+ console.log();
203
+ console.log(chalk.bold('Suggestion'));
204
+ console.log(` ${memory.suggestion}`);
205
+ }
206
+ break;
207
+ case 'decision_context':
208
+ console.log(chalk.bold('Decision'));
209
+ console.log(chalk.gray('─'.repeat(50)));
210
+ console.log(` Summary: ${memory.decisionSummary}`);
211
+ console.log(` Still Valid: ${memory.stillValid ? chalk.green('Yes') : chalk.red('No')}`);
212
+ if (memory.businessContext) {
213
+ console.log();
214
+ console.log(chalk.bold('Business Context'));
215
+ console.log(` ${memory.businessContext}`);
216
+ }
217
+ break;
218
+ }
219
+ // Links
220
+ if (memory.linkedFiles?.length > 0 || memory.linkedPatterns?.length > 0) {
221
+ console.log();
222
+ console.log(chalk.bold('Links'));
223
+ console.log(chalk.gray('─'.repeat(50)));
224
+ if (memory.linkedFiles?.length > 0) {
225
+ console.log(` Files: ${memory.linkedFiles.join(', ')}`);
226
+ }
227
+ if (memory.linkedPatterns?.length > 0) {
228
+ console.log(` Patterns: ${memory.linkedPatterns.join(', ')}`);
229
+ }
230
+ }
231
+ // Tags
232
+ if (memory.tags?.length > 0) {
233
+ console.log();
234
+ console.log(chalk.bold('Tags'));
235
+ console.log(chalk.gray('─'.repeat(50)));
236
+ console.log(` ${memory.tags.join(', ')}`);
237
+ }
238
+ // Related memories
239
+ if (related.length > 0) {
240
+ console.log();
241
+ console.log(chalk.bold('Related Memories'));
242
+ console.log(chalk.gray('─'.repeat(50)));
243
+ for (const r of related.slice(0, 5)) {
244
+ console.log(` ${getTypeIcon(r.type)} ${chalk.cyan(r.id.slice(0, 8))}... ${r.summary}`);
245
+ }
246
+ if (related.length > 5) {
247
+ console.log(chalk.gray(` ... and ${related.length - 5} more`));
248
+ }
249
+ }
250
+ console.log();
251
+ }
252
+ // ============================================================================
253
+ // Subcommand Actions
254
+ // ============================================================================
255
+ /**
256
+ * Init subcommand - initialize memory system
257
+ */
258
+ async function initAction(options) {
259
+ const rootDir = process.cwd();
260
+ const format = options.format ?? 'text';
261
+ const isTextFormat = format === 'text';
262
+ if (isTextFormat) {
263
+ console.log();
264
+ console.log(chalk.bold('🧠 Initializing Memory System'));
265
+ console.log(chalk.gray('═'.repeat(50)));
266
+ }
267
+ const spinner = isTextFormat ? createSpinner('Creating memory database...') : null;
268
+ spinner?.start();
269
+ try {
270
+ const cortex = await getCortex(rootDir);
271
+ await cortex.storage.close();
272
+ spinner?.stop();
273
+ if (format === 'json') {
274
+ console.log(JSON.stringify({
275
+ success: true,
276
+ path: path.join(DRIFT_DIR, MEMORY_DIR, MEMORY_DB)
277
+ }));
278
+ return;
279
+ }
280
+ console.log();
281
+ console.log(chalk.green.bold('✓ Memory system initialized'));
282
+ console.log();
283
+ console.log(chalk.gray(`Database: ${path.join(DRIFT_DIR, MEMORY_DIR, MEMORY_DB)}`));
284
+ console.log();
285
+ console.log(chalk.gray('─'.repeat(50)));
286
+ console.log(chalk.bold('📌 Next Steps:'));
287
+ console.log(chalk.gray(` • drift memory add tribal "..." ${chalk.white('Add tribal knowledge')}`));
288
+ console.log(chalk.gray(` • drift memory status ${chalk.white('View memory statistics')}`));
289
+ console.log(chalk.gray(` • drift memory import <file> ${chalk.white('Import memories from file')}`));
290
+ console.log();
291
+ }
292
+ catch (error) {
293
+ spinner?.stop();
294
+ if (format === 'json') {
295
+ console.log(JSON.stringify({ error: String(error) }));
296
+ }
297
+ else {
298
+ console.log(chalk.red(`\n❌ Error: ${error}`));
299
+ }
300
+ }
301
+ }
302
+ /**
303
+ * Status subcommand - show memory system status
304
+ */
305
+ async function statusAction(options) {
306
+ const rootDir = process.cwd();
307
+ const format = options.format ?? 'text';
308
+ if (!(await memoryExists(rootDir))) {
309
+ if (format === 'json') {
310
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
311
+ }
312
+ else {
313
+ showNoMemoriesMessage();
314
+ }
315
+ return;
316
+ }
317
+ const spinner = format === 'text' ? createSpinner('Loading memory statistics...') : null;
318
+ spinner?.start();
319
+ try {
320
+ const cortex = await getCortex(rootDir);
321
+ // Get statistics
322
+ const countByType = await cortex.storage.countByType();
323
+ const total = Object.values(countByType).reduce((sum, count) => sum + count, 0);
324
+ // Get sample of memories for analysis
325
+ const memories = await cortex.storage.search({ limit: 500 });
326
+ let confidenceSum = 0;
327
+ let lowConfidenceCount = 0;
328
+ let recentlyAccessed = 0;
329
+ const oneWeekAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000);
330
+ for (const memory of memories) {
331
+ confidenceSum += memory.confidence ?? 1;
332
+ if ((memory.confidence ?? 1) < 0.5)
333
+ lowConfidenceCount++;
334
+ if (memory.lastAccessed && new Date(memory.lastAccessed) > oneWeekAgo) {
335
+ recentlyAccessed++;
336
+ }
337
+ }
338
+ const avgConfidence = memories.length > 0 ? confidenceSum / memories.length : 0;
339
+ // Get pending consolidation count
340
+ const pendingConsolidation = await cortex.storage.count({
341
+ types: ['episodic'],
342
+ consolidationStatus: 'pending',
343
+ });
344
+ // Calculate health score
345
+ let healthScore = 100;
346
+ if (avgConfidence < 0.5)
347
+ healthScore -= 20;
348
+ if (lowConfidenceCount > total * 0.3)
349
+ healthScore -= 15;
350
+ if (pendingConsolidation > 50)
351
+ healthScore -= 10;
352
+ if (total > 1000)
353
+ healthScore -= 5;
354
+ healthScore = Math.max(0, healthScore);
355
+ await cortex.storage.close();
356
+ spinner?.stop();
357
+ if (format === 'json') {
358
+ console.log(JSON.stringify({
359
+ total,
360
+ byType: countByType,
361
+ avgConfidence,
362
+ lowConfidenceCount,
363
+ recentlyAccessed,
364
+ pendingConsolidation,
365
+ healthScore,
366
+ }, null, 2));
367
+ return;
368
+ }
369
+ console.log();
370
+ console.log(chalk.bold('🧠 Memory System Status'));
371
+ console.log(chalk.gray('═'.repeat(60)));
372
+ console.log();
373
+ // Overview
374
+ console.log(chalk.bold('📊 Overview'));
375
+ console.log(chalk.gray('─'.repeat(50)));
376
+ console.log(` Total Memories: ${chalk.cyan.bold(total)}`);
377
+ console.log(` Avg Confidence: ${getConfidenceColor(avgConfidence)}`);
378
+ console.log(` Low Confidence: ${lowConfidenceCount > 0 ? chalk.yellow(lowConfidenceCount) : chalk.green('0')}`);
379
+ console.log(` Recently Accessed: ${chalk.cyan(recentlyAccessed)} (last 7 days)`);
380
+ console.log(` Pending Consolidation: ${pendingConsolidation > 0 ? chalk.yellow(pendingConsolidation) : chalk.green('0')}`);
381
+ console.log();
382
+ // By Type
383
+ console.log(chalk.bold('📋 By Type'));
384
+ console.log(chalk.gray('─'.repeat(50)));
385
+ for (const [type, count] of Object.entries(countByType)) {
386
+ if (count > 0) {
387
+ const typeInfo = MEMORY_TYPES[type];
388
+ console.log(` ${typeInfo?.icon ?? '📦'} ${type.padEnd(20)} ${chalk.cyan(count)} ${chalk.gray(`(${typeInfo?.halfLife ?? '?'} half-life)`)}`);
389
+ }
390
+ }
391
+ console.log();
392
+ // Health
393
+ console.log(chalk.bold('💚 Health'));
394
+ console.log(chalk.gray('─'.repeat(50)));
395
+ console.log(` Score: ${getHealthColor(healthScore)}`);
396
+ if (lowConfidenceCount > total * 0.3) {
397
+ console.log(chalk.yellow(` ⚠️ ${lowConfidenceCount} memories have low confidence`));
398
+ }
399
+ if (pendingConsolidation > 50) {
400
+ console.log(chalk.yellow(` ⚠️ ${pendingConsolidation} episodic memories pending consolidation`));
401
+ }
402
+ if (total > 1000) {
403
+ console.log(chalk.yellow(` ⚠️ Large memory count - consider running consolidation`));
404
+ }
405
+ console.log();
406
+ }
407
+ catch (error) {
408
+ spinner?.stop();
409
+ if (format === 'json') {
410
+ console.log(JSON.stringify({ error: String(error) }));
411
+ }
412
+ else {
413
+ console.log(chalk.red(`Error: ${error}`));
414
+ }
415
+ }
416
+ }
417
+ /**
418
+ * Add subcommand - add a new memory
419
+ */
420
+ async function addAction(type, content, options) {
421
+ const rootDir = process.cwd();
422
+ const format = options.format ?? 'text';
423
+ // Validate type
424
+ if (!MEMORY_TYPES[type]) {
425
+ const validTypes = Object.keys(MEMORY_TYPES).join(', ');
426
+ if (format === 'json') {
427
+ console.log(JSON.stringify({ error: `Invalid type. Valid types: ${validTypes}` }));
428
+ }
429
+ else {
430
+ console.log(chalk.red(`Invalid memory type: ${type}`));
431
+ console.log(chalk.gray(`Valid types: ${validTypes}`));
432
+ }
433
+ return;
434
+ }
435
+ // Some types shouldn't be manually added
436
+ const manuallyAddableTypes = ['tribal', 'procedural', 'pattern_rationale', 'code_smell', 'decision_context', 'constraint_override'];
437
+ if (!manuallyAddableTypes.includes(type)) {
438
+ if (format === 'json') {
439
+ console.log(JSON.stringify({ error: `Type '${type}' cannot be manually added. Use: ${manuallyAddableTypes.join(', ')}` }));
440
+ }
441
+ else {
442
+ console.log(chalk.red(`Type '${type}' cannot be manually added.`));
443
+ console.log(chalk.gray(`Manually addable types: ${manuallyAddableTypes.join(', ')}`));
444
+ if (type === 'episodic') {
445
+ console.log(chalk.gray('Episodic memories are created automatically from interactions.'));
446
+ }
447
+ else if (type === 'semantic') {
448
+ console.log(chalk.gray('Semantic memories are created through consolidation.'));
449
+ }
450
+ else if (type === 'core') {
451
+ console.log(chalk.gray('Core memory is created during initialization.'));
452
+ }
453
+ }
454
+ return;
455
+ }
456
+ // Validate content
457
+ if (!content || content.trim().length === 0) {
458
+ if (format === 'json') {
459
+ console.log(JSON.stringify({ error: 'Content cannot be empty' }));
460
+ }
461
+ else {
462
+ console.log(chalk.red('Content cannot be empty'));
463
+ }
464
+ return;
465
+ }
466
+ try {
467
+ const cortex = await getCortex(rootDir);
468
+ // Parse tags
469
+ const tags = options.tags ? options.tags.split(',').map(t => t.trim()) : undefined;
470
+ const importance = (options.importance ?? 'normal');
471
+ // Build memory object based on type
472
+ const memory = {
473
+ type,
474
+ confidence: 1.0,
475
+ importance,
476
+ tags,
477
+ summary: content.length > 100 ? content.slice(0, 97) + '...' : content,
478
+ };
479
+ // Type-specific fields
480
+ switch (type) {
481
+ case 'tribal':
482
+ memory.topic = options.topic ?? extractTopic(content);
483
+ memory.knowledge = content;
484
+ memory.severity = options.severity ?? 'warning';
485
+ memory.source = { type: 'manual' };
486
+ break;
487
+ case 'procedural':
488
+ memory.name = options.topic ?? extractTopic(content);
489
+ memory.description = content;
490
+ memory.triggers = [options.topic ?? extractTopic(content)];
491
+ memory.steps = [{ order: 1, action: content }];
492
+ memory.usageCount = 0;
493
+ break;
494
+ case 'semantic':
495
+ memory.topic = options.topic ?? extractTopic(content);
496
+ memory.knowledge = content;
497
+ memory.supportingEvidence = 1;
498
+ memory.contradictingEvidence = 0;
499
+ break;
500
+ case 'pattern_rationale':
501
+ memory.patternId = options.pattern ?? 'manual';
502
+ memory.patternName = options.topic ?? extractTopic(content);
503
+ memory.patternCategory = 'manual';
504
+ memory.rationale = content;
505
+ break;
506
+ case 'code_smell':
507
+ memory.name = options.topic ?? extractTopic(content);
508
+ memory.description = content;
509
+ memory.reason = content;
510
+ memory.suggestion = 'Review and fix';
511
+ memory.severity = options.severity ?? 'warning';
512
+ memory.autoDetect = false;
513
+ break;
514
+ case 'decision_context':
515
+ memory.decisionId = 'manual';
516
+ memory.decisionSummary = options.topic ?? extractTopic(content);
517
+ memory.businessContext = content;
518
+ memory.stillValid = true;
519
+ break;
520
+ case 'constraint_override':
521
+ memory.constraintId = options.pattern ?? 'manual';
522
+ memory.constraintName = options.topic ?? extractTopic(content);
523
+ memory.scope = { type: 'global', target: '*' };
524
+ memory.reason = content;
525
+ memory.permanent = false;
526
+ memory.usageCount = 0;
527
+ break;
528
+ default:
529
+ memory.content = content;
530
+ }
531
+ const id = await cortex.add(memory);
532
+ // Link to file if specified
533
+ if (options.file) {
534
+ await cortex.storage.linkToFile(id, options.file);
535
+ }
536
+ // Link to pattern if specified
537
+ if (options.pattern) {
538
+ await cortex.storage.linkToPattern(id, options.pattern);
539
+ }
540
+ await cortex.storage.close();
541
+ if (format === 'json') {
542
+ console.log(JSON.stringify({ success: true, id, type }));
543
+ return;
544
+ }
545
+ console.log();
546
+ console.log(chalk.green.bold('✓ Memory added'));
547
+ console.log();
548
+ console.log(` ${getTypeIcon(type)} ${chalk.bold('ID:')} ${chalk.cyan(id)}`);
549
+ console.log(` ${chalk.bold('Type:')} ${type}`);
550
+ console.log(` ${chalk.bold('Importance:')} ${importance}`);
551
+ if (tags) {
552
+ console.log(` ${chalk.bold('Tags:')} ${tags.join(', ')}`);
553
+ }
554
+ if (options.file) {
555
+ console.log(` ${chalk.bold('Linked to:')} ${options.file}`);
556
+ }
557
+ console.log();
558
+ }
559
+ catch (error) {
560
+ if (format === 'json') {
561
+ console.log(JSON.stringify({ error: String(error) }));
562
+ }
563
+ else {
564
+ console.log(chalk.red(`Error: ${error}`));
565
+ }
566
+ }
567
+ }
568
+ /**
569
+ * List subcommand - list memories
570
+ */
571
+ async function listAction(options) {
572
+ const rootDir = process.cwd();
573
+ const format = options.format ?? 'text';
574
+ const limit = parseInt(options.limit ?? '20', 10);
575
+ if (!(await memoryExists(rootDir))) {
576
+ if (format === 'json') {
577
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
578
+ }
579
+ else {
580
+ showNoMemoriesMessage();
581
+ }
582
+ return;
583
+ }
584
+ try {
585
+ const cortex = await getCortex(rootDir);
586
+ const query = { limit };
587
+ if (options.type) {
588
+ query.types = [options.type];
589
+ }
590
+ if (options.minConfidence) {
591
+ query.minConfidence = parseFloat(options.minConfidence);
592
+ }
593
+ if (options.importance) {
594
+ query.importance = [options.importance];
595
+ }
596
+ const memories = await cortex.storage.search(query);
597
+ await cortex.storage.close();
598
+ if (format === 'json') {
599
+ console.log(JSON.stringify({ memories, total: memories.length }, null, 2));
600
+ return;
601
+ }
602
+ console.log();
603
+ console.log(chalk.bold('🧠 Memories'));
604
+ console.log(chalk.gray('─'.repeat(60)));
605
+ console.log();
606
+ if (memories.length === 0) {
607
+ console.log(chalk.yellow('No memories match the filters.'));
608
+ console.log();
609
+ return;
610
+ }
611
+ // Group by type
612
+ const byType = new Map();
613
+ for (const m of memories) {
614
+ const list = byType.get(m.type) ?? [];
615
+ list.push(m);
616
+ byType.set(m.type, list);
617
+ }
618
+ for (const [type, mems] of byType) {
619
+ const typeInfo = MEMORY_TYPES[type];
620
+ console.log(chalk.bold(`${typeInfo?.icon ?? '📦'} ${type.toUpperCase()}`));
621
+ console.log(chalk.gray('─'.repeat(40)));
622
+ for (const m of mems.slice(0, 10)) {
623
+ formatMemoryBrief(m);
624
+ }
625
+ if (mems.length > 10) {
626
+ console.log(chalk.gray(` ... and ${mems.length - 10} more`));
627
+ }
628
+ console.log();
629
+ }
630
+ console.log(chalk.gray(`Showing ${memories.length} memories`));
631
+ console.log();
632
+ }
633
+ catch (error) {
634
+ if (format === 'json') {
635
+ console.log(JSON.stringify({ error: String(error) }));
636
+ }
637
+ else {
638
+ console.log(chalk.red(`Error: ${error}`));
639
+ }
640
+ }
641
+ }
642
+ /**
643
+ * Show subcommand - show memory details
644
+ */
645
+ async function showAction(id, options) {
646
+ const rootDir = process.cwd();
647
+ const format = options.format ?? 'text';
648
+ try {
649
+ const cortex = await getCortex(rootDir);
650
+ const memory = await cortex.get(id);
651
+ if (!memory) {
652
+ await cortex.storage.close();
653
+ if (format === 'json') {
654
+ console.log(JSON.stringify({ error: 'Memory not found' }));
655
+ }
656
+ else {
657
+ console.log(chalk.red(`Memory not found: ${id}`));
658
+ }
659
+ return;
660
+ }
661
+ // Get related memories
662
+ const related = await cortex.storage.getRelated(id);
663
+ // Get decay factors
664
+ const decay = cortex.calculateDecay(memory);
665
+ await cortex.storage.close();
666
+ if (format === 'json') {
667
+ console.log(JSON.stringify({ memory, related, decay }, null, 2));
668
+ return;
669
+ }
670
+ formatMemoryDetailed(memory, related);
671
+ // Show decay info
672
+ console.log(chalk.bold('📉 Decay'));
673
+ console.log(chalk.gray('─'.repeat(50)));
674
+ console.log(` Current Confidence: ${getConfidenceColor(memory.confidence)}`);
675
+ const effectiveConf = isNaN(decay.finalConfidence) ? memory.confidence : decay.finalConfidence;
676
+ const ageFactor = isNaN(decay.temporalDecay) ? 1 : decay.temporalDecay;
677
+ const usageFactor = isNaN(decay.usageBoost) ? 1 : decay.usageBoost;
678
+ console.log(` Effective Confidence: ${getConfidenceColor(effectiveConf)}`);
679
+ console.log(` Age Factor: ${(ageFactor * 100).toFixed(1)}%`);
680
+ console.log(` Usage Factor: ${(usageFactor * 100).toFixed(1)}%`);
681
+ console.log();
682
+ }
683
+ catch (error) {
684
+ if (format === 'json') {
685
+ console.log(JSON.stringify({ error: String(error) }));
686
+ }
687
+ else {
688
+ console.log(chalk.red(`Error: ${error}`));
689
+ }
690
+ }
691
+ }
692
+ /**
693
+ * Search subcommand - search memories
694
+ */
695
+ async function searchAction(query, options) {
696
+ const rootDir = process.cwd();
697
+ const format = options.format ?? 'text';
698
+ const limit = parseInt(options.limit ?? '20', 10);
699
+ if (!(await memoryExists(rootDir))) {
700
+ if (format === 'json') {
701
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
702
+ }
703
+ else {
704
+ showNoMemoriesMessage();
705
+ }
706
+ return;
707
+ }
708
+ const spinner = format === 'text' ? createSpinner('Searching...') : null;
709
+ spinner?.start();
710
+ try {
711
+ const cortex = await getCortex(rootDir);
712
+ // Try semantic search first, fall back to text search
713
+ let results = [];
714
+ try {
715
+ const embedding = await cortex.embeddings.embed(query);
716
+ results = await cortex.storage.similaritySearch(embedding, limit);
717
+ }
718
+ catch {
719
+ // Similarity search failed
720
+ }
721
+ // If semantic search returned no results, fall back to text-based search
722
+ if (results.length === 0) {
723
+ const allMemories = await cortex.storage.search({ limit: 1000 });
724
+ const queryLower = query.toLowerCase();
725
+ results = allMemories.filter((m) => {
726
+ const searchText = `${m.summary} ${m.type} ${m.knowledge ?? ''} ${m.topic ?? ''}`.toLowerCase();
727
+ return searchText.includes(queryLower);
728
+ }).slice(0, limit);
729
+ }
730
+ // Filter by type if specified
731
+ if (options.type) {
732
+ results = results.filter(m => m.type === options.type);
733
+ }
734
+ await cortex.storage.close();
735
+ spinner?.stop();
736
+ if (format === 'json') {
737
+ console.log(JSON.stringify({ query, results, total: results.length }, null, 2));
738
+ return;
739
+ }
740
+ console.log();
741
+ console.log(chalk.bold(`🔍 Search Results for "${query}"`));
742
+ console.log(chalk.gray('─'.repeat(60)));
743
+ console.log();
744
+ if (results.length === 0) {
745
+ console.log(chalk.yellow('No memories found matching your query.'));
746
+ console.log();
747
+ return;
748
+ }
749
+ for (const m of results) {
750
+ formatMemoryBrief(m);
751
+ }
752
+ console.log();
753
+ console.log(chalk.gray(`Found ${results.length} memories`));
754
+ console.log();
755
+ }
756
+ catch (error) {
757
+ spinner?.stop();
758
+ if (format === 'json') {
759
+ console.log(JSON.stringify({ error: String(error) }));
760
+ }
761
+ else {
762
+ console.log(chalk.red(`Error: ${error}`));
763
+ }
764
+ }
765
+ }
766
+ /**
767
+ * Extract topic from content
768
+ */
769
+ function extractTopic(content) {
770
+ // Extract first few words as topic
771
+ const words = content.split(/\s+/).slice(0, 3);
772
+ return words.join(' ');
773
+ }
774
+ /**
775
+ * Update subcommand - update a memory
776
+ */
777
+ async function updateAction(id, options) {
778
+ const rootDir = process.cwd();
779
+ const format = options.format ?? 'text';
780
+ try {
781
+ const cortex = await getCortex(rootDir);
782
+ // Verify memory exists
783
+ const existing = await cortex.get(id);
784
+ if (!existing) {
785
+ await cortex.storage.close();
786
+ if (format === 'json') {
787
+ console.log(JSON.stringify({ error: 'Memory not found' }));
788
+ }
789
+ else {
790
+ console.log(chalk.red(`Memory not found: ${id}`));
791
+ }
792
+ return;
793
+ }
794
+ // Build updates
795
+ const updates = {};
796
+ if (options.confidence) {
797
+ updates.confidence = parseFloat(options.confidence);
798
+ }
799
+ if (options.importance) {
800
+ updates.importance = options.importance;
801
+ }
802
+ if (options.tags) {
803
+ updates.tags = options.tags.split(',').map(t => t.trim());
804
+ }
805
+ if (options.summary) {
806
+ updates.summary = options.summary;
807
+ }
808
+ await cortex.update(id, updates);
809
+ await cortex.storage.close();
810
+ if (format === 'json') {
811
+ console.log(JSON.stringify({ success: true, id, updates }));
812
+ return;
813
+ }
814
+ console.log();
815
+ console.log(chalk.green.bold('✓ Memory updated'));
816
+ console.log();
817
+ console.log(` ID: ${chalk.cyan(id)}`);
818
+ for (const [key, value] of Object.entries(updates)) {
819
+ console.log(` ${key}: ${value}`);
820
+ }
821
+ console.log();
822
+ }
823
+ catch (error) {
824
+ if (format === 'json') {
825
+ console.log(JSON.stringify({ error: String(error) }));
826
+ }
827
+ else {
828
+ console.log(chalk.red(`Error: ${error}`));
829
+ }
830
+ }
831
+ }
832
+ /**
833
+ * Delete subcommand - delete a memory
834
+ */
835
+ async function deleteAction(id, options) {
836
+ const rootDir = process.cwd();
837
+ const format = options.format ?? 'text';
838
+ try {
839
+ const cortex = await getCortex(rootDir);
840
+ // Verify memory exists
841
+ const existing = await cortex.get(id);
842
+ if (!existing) {
843
+ await cortex.storage.close();
844
+ if (format === 'json') {
845
+ console.log(JSON.stringify({ error: 'Memory not found' }));
846
+ }
847
+ else {
848
+ console.log(chalk.red(`Memory not found: ${id}`));
849
+ }
850
+ return;
851
+ }
852
+ await cortex.delete(id);
853
+ await cortex.storage.close();
854
+ if (format === 'json') {
855
+ console.log(JSON.stringify({ success: true, id }));
856
+ return;
857
+ }
858
+ console.log();
859
+ console.log(chalk.green.bold('✓ Memory deleted'));
860
+ console.log(` ID: ${chalk.cyan(id)}`);
861
+ console.log();
862
+ }
863
+ catch (error) {
864
+ if (format === 'json') {
865
+ console.log(JSON.stringify({ error: String(error) }));
866
+ }
867
+ else {
868
+ console.log(chalk.red(`Error: ${error}`));
869
+ }
870
+ }
871
+ }
872
+ /**
873
+ * Learn subcommand - learn from a correction
874
+ */
875
+ async function learnAction(correction, options) {
876
+ const rootDir = process.cwd();
877
+ const format = options.format ?? 'text';
878
+ // The correction is the main argument, original is optional context
879
+ const feedback = correction;
880
+ const original = options.original ?? 'Previous approach';
881
+ const spinner = format === 'text' ? createSpinner('Learning from correction...') : null;
882
+ spinner?.start();
883
+ try {
884
+ const cortex = await getCortex(rootDir);
885
+ // Check if CortexV2 learn method is available
886
+ let result;
887
+ if ('learn' in cortex) {
888
+ result = await cortex.learn(original, feedback, options.code, { activeFile: options.file });
889
+ }
890
+ else {
891
+ // Fallback: create a tribal memory
892
+ const id = await cortex.add({
893
+ type: 'tribal',
894
+ topic: 'Learned correction',
895
+ knowledge: original !== 'Previous approach'
896
+ ? `Original: ${original}\nCorrection: ${feedback}`
897
+ : feedback,
898
+ severity: 'warning',
899
+ source: { type: 'manual' },
900
+ summary: feedback.length > 50 ? `${feedback.slice(0, 47)}...` : feedback,
901
+ confidence: 0.8,
902
+ importance: 'normal',
903
+ });
904
+ result = {
905
+ success: true,
906
+ createdMemories: [id],
907
+ principles: [{ statement: feedback }],
908
+ category: 'correction',
909
+ };
910
+ }
911
+ await cortex.storage.close();
912
+ spinner?.stop();
913
+ if (format === 'json') {
914
+ console.log(JSON.stringify(result, null, 2));
915
+ return;
916
+ }
917
+ console.log();
918
+ console.log(chalk.green.bold('✓ Learned from correction'));
919
+ console.log();
920
+ if (result.createdMemories?.length > 0) {
921
+ console.log(chalk.bold('📝 Memories Created:'));
922
+ for (const memId of result.createdMemories) {
923
+ console.log(` ${chalk.cyan(memId)}`);
924
+ }
925
+ console.log();
926
+ }
927
+ if (result.principles?.length > 0) {
928
+ console.log(chalk.bold('💡 Extracted Principles:'));
929
+ for (const p of result.principles) {
930
+ console.log(` • ${p.statement}`);
931
+ }
932
+ console.log();
933
+ }
934
+ console.log(`Category: ${result.category}`);
935
+ console.log();
936
+ }
937
+ catch (error) {
938
+ spinner?.stop();
939
+ if (format === 'json') {
940
+ console.log(JSON.stringify({ error: String(error) }));
941
+ }
942
+ else {
943
+ console.log(chalk.red(`Error: ${error}`));
944
+ }
945
+ }
946
+ }
947
+ /**
948
+ * Feedback subcommand - provide feedback on a memory
949
+ */
950
+ async function feedbackAction(id, action, options) {
951
+ const rootDir = process.cwd();
952
+ const format = options.format ?? 'text';
953
+ if (!['confirm', 'reject', 'modify'].includes(action)) {
954
+ if (format === 'json') {
955
+ console.log(JSON.stringify({ error: 'Action must be: confirm, reject, or modify' }));
956
+ }
957
+ else {
958
+ console.log(chalk.red('Action must be: confirm, reject, or modify'));
959
+ }
960
+ return;
961
+ }
962
+ try {
963
+ const cortex = await getCortex(rootDir);
964
+ const memory = await cortex.get(id);
965
+ if (!memory) {
966
+ await cortex.storage.close();
967
+ if (format === 'json') {
968
+ console.log(JSON.stringify({ error: 'Memory not found' }));
969
+ }
970
+ else {
971
+ console.log(chalk.red(`Memory not found: ${id}`));
972
+ }
973
+ return;
974
+ }
975
+ const previousConfidence = memory.confidence;
976
+ let newConfidence;
977
+ switch (action) {
978
+ case 'confirm':
979
+ newConfidence = Math.min(1.0, previousConfidence + 0.1);
980
+ break;
981
+ case 'reject':
982
+ newConfidence = Math.max(0.1, previousConfidence - 0.3);
983
+ break;
984
+ case 'modify':
985
+ newConfidence = Math.max(0.3, previousConfidence - 0.1);
986
+ break;
987
+ default:
988
+ newConfidence = previousConfidence;
989
+ }
990
+ await cortex.update(id, {
991
+ confidence: newConfidence,
992
+ lastAccessed: new Date().toISOString(),
993
+ accessCount: memory.accessCount + 1,
994
+ });
995
+ await cortex.storage.close();
996
+ if (format === 'json') {
997
+ console.log(JSON.stringify({
998
+ success: true,
999
+ id,
1000
+ action,
1001
+ previousConfidence,
1002
+ newConfidence,
1003
+ }));
1004
+ return;
1005
+ }
1006
+ console.log();
1007
+ console.log(chalk.green.bold(`✓ Feedback recorded: ${action}`));
1008
+ console.log();
1009
+ console.log(` Memory: ${chalk.cyan(id)}`);
1010
+ console.log(` Previous Confidence: ${getConfidenceColor(previousConfidence)}`);
1011
+ console.log(` New Confidence: ${getConfidenceColor(newConfidence)}`);
1012
+ console.log();
1013
+ }
1014
+ catch (error) {
1015
+ if (format === 'json') {
1016
+ console.log(JSON.stringify({ error: String(error) }));
1017
+ }
1018
+ else {
1019
+ console.log(chalk.red(`Error: ${error}`));
1020
+ }
1021
+ }
1022
+ }
1023
+ /**
1024
+ * Validate subcommand - validate memories
1025
+ */
1026
+ async function validateAction(options) {
1027
+ const rootDir = process.cwd();
1028
+ const format = options.format ?? 'text';
1029
+ const autoHeal = options.autoHeal !== false;
1030
+ const minConfidence = parseFloat(options.minConfidence ?? '0.2');
1031
+ if (!(await memoryExists(rootDir))) {
1032
+ if (format === 'json') {
1033
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1034
+ }
1035
+ else {
1036
+ showNoMemoriesMessage();
1037
+ }
1038
+ return;
1039
+ }
1040
+ const spinner = format === 'text' ? createSpinner('Validating memories...') : null;
1041
+ spinner?.start();
1042
+ try {
1043
+ const cortex = await getCortex(rootDir);
1044
+ const startTime = Date.now();
1045
+ // Get memories based on scope
1046
+ let memories;
1047
+ switch (options.scope) {
1048
+ case 'all':
1049
+ memories = await cortex.storage.search({ limit: 1000 });
1050
+ break;
1051
+ case 'recent':
1052
+ memories = await cortex.storage.search({ limit: 100 });
1053
+ break;
1054
+ case 'high_importance':
1055
+ memories = await cortex.storage.search({
1056
+ importance: ['high', 'critical'],
1057
+ limit: 500,
1058
+ });
1059
+ break;
1060
+ case 'stale':
1061
+ default:
1062
+ memories = await cortex.storage.search({
1063
+ maxConfidence: 0.5,
1064
+ limit: 500,
1065
+ });
1066
+ break;
1067
+ }
1068
+ const healingStats = {
1069
+ confidenceAdjusted: 0,
1070
+ summariesFixed: 0,
1071
+ memoriesRemoved: 0,
1072
+ };
1073
+ const issues = [];
1074
+ let valid = 0;
1075
+ let stale = 0;
1076
+ let healed = 0;
1077
+ for (const memory of memories) {
1078
+ let hasIssue = false;
1079
+ // Check for missing summary
1080
+ if (!memory.summary || memory.summary.trim() === '') {
1081
+ hasIssue = true;
1082
+ if (autoHeal) {
1083
+ await cortex.update(memory.id, { summary: `Memory ${memory.id.slice(0, 8)}...` });
1084
+ healingStats.summariesFixed++;
1085
+ healed++;
1086
+ }
1087
+ issues.push({ memoryId: memory.id, issue: 'Missing summary', healed: autoHeal });
1088
+ }
1089
+ // Check for invalid confidence
1090
+ if (memory.confidence < 0 || memory.confidence > 1) {
1091
+ hasIssue = true;
1092
+ if (autoHeal) {
1093
+ const fixed = Math.max(0, Math.min(1, memory.confidence));
1094
+ await cortex.update(memory.id, { confidence: fixed });
1095
+ healingStats.confidenceAdjusted++;
1096
+ healed++;
1097
+ }
1098
+ issues.push({ memoryId: memory.id, issue: 'Invalid confidence', healed: autoHeal });
1099
+ }
1100
+ // Check for very low confidence
1101
+ if (memory.confidence < minConfidence) {
1102
+ hasIssue = true;
1103
+ stale++;
1104
+ if (options.removeInvalid) {
1105
+ await cortex.delete(memory.id);
1106
+ healingStats.memoriesRemoved++;
1107
+ healed++;
1108
+ }
1109
+ issues.push({ memoryId: memory.id, issue: 'Very low confidence', healed: options.removeInvalid ?? false });
1110
+ }
1111
+ if (!hasIssue) {
1112
+ valid++;
1113
+ }
1114
+ }
1115
+ const duration = Date.now() - startTime;
1116
+ await cortex.storage.close();
1117
+ spinner?.stop();
1118
+ if (format === 'json') {
1119
+ console.log(JSON.stringify({
1120
+ summary: { total: memories.length, valid, stale, healed },
1121
+ healingStats,
1122
+ duration,
1123
+ issues: issues.slice(0, 50),
1124
+ }, null, 2));
1125
+ return;
1126
+ }
1127
+ console.log();
1128
+ console.log(chalk.bold('🔍 Validation Results'));
1129
+ console.log(chalk.gray('═'.repeat(60)));
1130
+ console.log();
1131
+ console.log(chalk.bold('📊 Summary'));
1132
+ console.log(chalk.gray('─'.repeat(50)));
1133
+ console.log(` Total Validated: ${chalk.cyan(memories.length)}`);
1134
+ console.log(` Valid: ${chalk.green(valid)}`);
1135
+ console.log(` Stale: ${stale > 0 ? chalk.yellow(stale) : chalk.green('0')}`);
1136
+ console.log(` Healed: ${healed > 0 ? chalk.cyan(healed) : '0'}`);
1137
+ console.log(` Duration: ${duration}ms`);
1138
+ console.log();
1139
+ if (healingStats.summariesFixed > 0 || healingStats.confidenceAdjusted > 0 || healingStats.memoriesRemoved > 0) {
1140
+ console.log(chalk.bold('🔧 Healing Stats'));
1141
+ console.log(chalk.gray('─'.repeat(50)));
1142
+ if (healingStats.summariesFixed > 0) {
1143
+ console.log(` Summaries Fixed: ${healingStats.summariesFixed}`);
1144
+ }
1145
+ if (healingStats.confidenceAdjusted > 0) {
1146
+ console.log(` Confidence Adjusted: ${healingStats.confidenceAdjusted}`);
1147
+ }
1148
+ if (healingStats.memoriesRemoved > 0) {
1149
+ console.log(` Memories Removed: ${healingStats.memoriesRemoved}`);
1150
+ }
1151
+ console.log();
1152
+ }
1153
+ if (issues.length > 0 && options.verbose) {
1154
+ console.log(chalk.bold('⚠️ Issues'));
1155
+ console.log(chalk.gray('─'.repeat(50)));
1156
+ for (const issue of issues.slice(0, 10)) {
1157
+ const icon = issue.healed ? chalk.green('✓') : chalk.yellow('⚠');
1158
+ console.log(` ${icon} ${chalk.cyan(issue.memoryId.slice(0, 8))}... ${issue.issue}`);
1159
+ }
1160
+ if (issues.length > 10) {
1161
+ console.log(chalk.gray(` ... and ${issues.length - 10} more`));
1162
+ }
1163
+ console.log();
1164
+ }
1165
+ }
1166
+ catch (error) {
1167
+ spinner?.stop();
1168
+ if (format === 'json') {
1169
+ console.log(JSON.stringify({ error: String(error) }));
1170
+ }
1171
+ else {
1172
+ console.log(chalk.red(`Error: ${error}`));
1173
+ }
1174
+ }
1175
+ }
1176
+ /**
1177
+ * Consolidate subcommand - consolidate episodic memories
1178
+ */
1179
+ async function consolidateAction(options) {
1180
+ const rootDir = process.cwd();
1181
+ const format = options.format ?? 'text';
1182
+ const dryRun = options.dryRun ?? false;
1183
+ if (!(await memoryExists(rootDir))) {
1184
+ if (format === 'json') {
1185
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1186
+ }
1187
+ else {
1188
+ showNoMemoriesMessage();
1189
+ }
1190
+ return;
1191
+ }
1192
+ const spinner = format === 'text' ? createSpinner(dryRun ? 'Analyzing consolidation...' : 'Consolidating memories...') : null;
1193
+ spinner?.start();
1194
+ try {
1195
+ const cortex = await getCortex(rootDir);
1196
+ const result = await cortex.consolidate(dryRun);
1197
+ await cortex.storage.close();
1198
+ spinner?.stop();
1199
+ if (format === 'json') {
1200
+ console.log(JSON.stringify({ dryRun, ...result }, null, 2));
1201
+ return;
1202
+ }
1203
+ console.log();
1204
+ console.log(chalk.bold(dryRun ? '🔍 Consolidation Preview' : '✓ Consolidation Complete'));
1205
+ console.log(chalk.gray('═'.repeat(60)));
1206
+ console.log();
1207
+ console.log(chalk.bold('📊 Results'));
1208
+ console.log(chalk.gray('─'.repeat(50)));
1209
+ console.log(` Episodes Processed: ${chalk.cyan(result.episodesProcessed)}`);
1210
+ console.log(` Memories Created: ${chalk.green(result.memoriesCreated)}`);
1211
+ console.log(` Memories Updated: ${chalk.cyan(result.memoriesUpdated)}`);
1212
+ console.log(` Memories Pruned: ${result.memoriesPruned > 0 ? chalk.yellow(result.memoriesPruned) : '0'}`);
1213
+ console.log(` Tokens Freed: ${chalk.cyan(result.tokensFreed)}`);
1214
+ console.log(` Duration: ${result.duration}ms`);
1215
+ console.log();
1216
+ if (dryRun) {
1217
+ console.log(chalk.gray('This was a dry run. Run without --dry-run to apply changes.'));
1218
+ console.log();
1219
+ }
1220
+ }
1221
+ catch (error) {
1222
+ spinner?.stop();
1223
+ if (format === 'json') {
1224
+ console.log(JSON.stringify({ error: String(error) }));
1225
+ }
1226
+ else {
1227
+ console.log(chalk.red(`Error: ${error}`));
1228
+ }
1229
+ }
1230
+ }
1231
+ /**
1232
+ * Warnings subcommand - show active warnings
1233
+ */
1234
+ async function warningsAction(options) {
1235
+ const rootDir = process.cwd();
1236
+ const format = options.format ?? 'text';
1237
+ if (!(await memoryExists(rootDir))) {
1238
+ if (format === 'json') {
1239
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1240
+ }
1241
+ else {
1242
+ showNoMemoriesMessage();
1243
+ }
1244
+ return;
1245
+ }
1246
+ try {
1247
+ const cortex = await getCortex(rootDir);
1248
+ // Get tribal warnings
1249
+ const tribal = await cortex.search({
1250
+ types: ['tribal'],
1251
+ importance: ['high', 'critical'],
1252
+ limit: 50,
1253
+ });
1254
+ // Get code smells
1255
+ const smells = await cortex.search({
1256
+ types: ['code_smell'],
1257
+ limit: 50,
1258
+ });
1259
+ await cortex.storage.close();
1260
+ const warnings = [];
1261
+ // Process tribal warnings
1262
+ for (const mem of tribal) {
1263
+ if (options.severity === 'critical' && mem.severity !== 'critical')
1264
+ continue;
1265
+ if (options.focus && !mem.topic?.toLowerCase().includes(options.focus.toLowerCase()))
1266
+ continue;
1267
+ warnings.push({
1268
+ type: 'tribal',
1269
+ severity: mem.severity,
1270
+ message: mem.knowledge,
1271
+ source: mem.topic,
1272
+ confidence: mem.confidence,
1273
+ });
1274
+ }
1275
+ // Process code smells
1276
+ for (const mem of smells) {
1277
+ if (options.focus && !mem.name?.toLowerCase().includes(options.focus.toLowerCase()))
1278
+ continue;
1279
+ warnings.push({
1280
+ type: 'code_smell',
1281
+ severity: mem.severity,
1282
+ message: `${mem.name}: ${mem.reason}`,
1283
+ source: mem.name,
1284
+ confidence: mem.confidence,
1285
+ });
1286
+ }
1287
+ // Sort by severity
1288
+ const severityOrder = { critical: 0, error: 1, warning: 2, info: 3 };
1289
+ warnings.sort((a, b) => (severityOrder[a.severity] ?? 4) - (severityOrder[b.severity] ?? 4));
1290
+ if (format === 'json') {
1291
+ console.log(JSON.stringify({
1292
+ warnings,
1293
+ total: warnings.length,
1294
+ bySeverity: {
1295
+ critical: warnings.filter(w => w.severity === 'critical').length,
1296
+ warning: warnings.filter(w => w.severity === 'warning').length,
1297
+ info: warnings.filter(w => w.severity === 'info').length,
1298
+ },
1299
+ }, null, 2));
1300
+ return;
1301
+ }
1302
+ console.log();
1303
+ console.log(chalk.bold('⚠️ Active Warnings'));
1304
+ console.log(chalk.gray('═'.repeat(60)));
1305
+ console.log();
1306
+ if (warnings.length === 0) {
1307
+ console.log(chalk.green('No active warnings.'));
1308
+ console.log();
1309
+ return;
1310
+ }
1311
+ for (const w of warnings) {
1312
+ const icon = w.severity === 'critical' ? chalk.red('🚨') :
1313
+ w.severity === 'warning' ? chalk.yellow('⚠️') :
1314
+ chalk.blue('ℹ️');
1315
+ const severityColor = w.severity === 'critical' ? chalk.red :
1316
+ w.severity === 'warning' ? chalk.yellow :
1317
+ chalk.gray;
1318
+ console.log(`${icon} ${severityColor(`[${w.severity.toUpperCase()}]`)} ${w.source}`);
1319
+ console.log(` ${w.message}`);
1320
+ console.log(chalk.gray(` Confidence: ${getConfidenceColor(w.confidence)}`));
1321
+ console.log();
1322
+ }
1323
+ console.log(chalk.gray(`Total: ${warnings.length} warnings`));
1324
+ console.log();
1325
+ }
1326
+ catch (error) {
1327
+ if (format === 'json') {
1328
+ console.log(JSON.stringify({ error: String(error) }));
1329
+ }
1330
+ else {
1331
+ console.log(chalk.red(`Error: ${error}`));
1332
+ }
1333
+ }
1334
+ }
1335
+ /**
1336
+ * Why subcommand - get context for a task
1337
+ */
1338
+ async function whyAction(focus, options) {
1339
+ const rootDir = process.cwd();
1340
+ const format = options.format ?? 'text';
1341
+ const intent = (options.intent ?? 'understand_code');
1342
+ const maxTokens = parseInt(options.maxTokens ?? '2000', 10);
1343
+ if (!(await memoryExists(rootDir))) {
1344
+ if (format === 'json') {
1345
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1346
+ }
1347
+ else {
1348
+ showNoMemoriesMessage();
1349
+ }
1350
+ return;
1351
+ }
1352
+ const spinner = format === 'text' ? createSpinner('Gathering context...') : null;
1353
+ spinner?.start();
1354
+ try {
1355
+ const cortex = await getCortex(rootDir);
1356
+ const result = await cortex.retrieval.retrieve({
1357
+ intent,
1358
+ focus,
1359
+ maxTokens,
1360
+ });
1361
+ await cortex.storage.close();
1362
+ spinner?.stop();
1363
+ // Organize by type
1364
+ const byType = {};
1365
+ for (const m of result.memories) {
1366
+ const type = m.memory.type;
1367
+ if (!byType[type])
1368
+ byType[type] = [];
1369
+ byType[type].push(m);
1370
+ }
1371
+ if (format === 'json') {
1372
+ console.log(JSON.stringify({
1373
+ focus,
1374
+ intent,
1375
+ tokensUsed: result.tokensUsed,
1376
+ totalCandidates: result.totalCandidates,
1377
+ retrievalTime: result.retrievalTime,
1378
+ memories: result.memories.map((m) => ({
1379
+ id: m.memory.id,
1380
+ type: m.memory.type,
1381
+ summary: m.memory.summary,
1382
+ relevanceScore: m.relevanceScore,
1383
+ })),
1384
+ }, null, 2));
1385
+ return;
1386
+ }
1387
+ console.log();
1388
+ console.log(chalk.bold(`🔍 Context for "${focus}"`));
1389
+ console.log(chalk.gray('═'.repeat(60)));
1390
+ console.log();
1391
+ console.log(chalk.gray(`Intent: ${intent} | Tokens: ${result.tokensUsed}/${maxTokens} | Time: ${result.retrievalTime}ms`));
1392
+ console.log();
1393
+ if (result.memories.length === 0) {
1394
+ console.log(chalk.yellow('No relevant memories found.'));
1395
+ console.log();
1396
+ return;
1397
+ }
1398
+ // Show by type
1399
+ for (const [type, memories] of Object.entries(byType)) {
1400
+ const typeInfo = MEMORY_TYPES[type];
1401
+ console.log(chalk.bold(`${typeInfo?.icon ?? '📦'} ${type.toUpperCase()}`));
1402
+ console.log(chalk.gray('─'.repeat(40)));
1403
+ for (const m of memories.slice(0, 5)) {
1404
+ console.log(` ${chalk.cyan(m.memory.id.slice(0, 8))}... ${m.memory.summary}`);
1405
+ console.log(chalk.gray(` Relevance: ${(m.relevanceScore * 100).toFixed(0)}%`));
1406
+ }
1407
+ if (memories.length > 5) {
1408
+ console.log(chalk.gray(` ... and ${memories.length - 5} more`));
1409
+ }
1410
+ console.log();
1411
+ }
1412
+ }
1413
+ catch (error) {
1414
+ spinner?.stop();
1415
+ if (format === 'json') {
1416
+ console.log(JSON.stringify({ error: String(error) }));
1417
+ }
1418
+ else {
1419
+ console.log(chalk.red(`Error: ${error}`));
1420
+ }
1421
+ }
1422
+ }
1423
+ /**
1424
+ * Export subcommand - export memories to JSON
1425
+ */
1426
+ async function exportAction(output, options) {
1427
+ const rootDir = process.cwd();
1428
+ const format = options.format ?? 'text';
1429
+ if (!(await memoryExists(rootDir))) {
1430
+ if (format === 'json') {
1431
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1432
+ }
1433
+ else {
1434
+ showNoMemoriesMessage();
1435
+ }
1436
+ return;
1437
+ }
1438
+ const spinner = format === 'text' ? createSpinner('Exporting memories...') : null;
1439
+ spinner?.start();
1440
+ try {
1441
+ const cortex = await getCortex(rootDir);
1442
+ const query = { limit: 10000 };
1443
+ if (options.type) {
1444
+ query.types = [options.type];
1445
+ }
1446
+ if (options.minConfidence) {
1447
+ query.minConfidence = parseFloat(options.minConfidence);
1448
+ }
1449
+ if (options.includeArchived) {
1450
+ query.includeArchived = true;
1451
+ }
1452
+ const memories = await cortex.search(query);
1453
+ await cortex.storage.close();
1454
+ const exportData = {
1455
+ exportedAt: new Date().toISOString(),
1456
+ version: '1.0.0',
1457
+ count: memories.length,
1458
+ memories,
1459
+ };
1460
+ await fs.writeFile(output, JSON.stringify(exportData, null, 2));
1461
+ spinner?.stop();
1462
+ if (format === 'json') {
1463
+ console.log(JSON.stringify({ success: true, count: memories.length, output }));
1464
+ return;
1465
+ }
1466
+ console.log();
1467
+ console.log(chalk.green.bold(`✓ Exported ${memories.length} memories to ${output}`));
1468
+ console.log();
1469
+ }
1470
+ catch (error) {
1471
+ spinner?.stop();
1472
+ if (format === 'json') {
1473
+ console.log(JSON.stringify({ error: String(error) }));
1474
+ }
1475
+ else {
1476
+ console.log(chalk.red(`Error: ${error}`));
1477
+ }
1478
+ }
1479
+ }
1480
+ /**
1481
+ * Import subcommand - import memories from JSON
1482
+ */
1483
+ async function importAction(input, options) {
1484
+ const rootDir = process.cwd();
1485
+ const format = options.format ?? 'text';
1486
+ const overwrite = options.overwrite ?? false;
1487
+ const spinner = format === 'text' ? createSpinner('Importing memories...') : null;
1488
+ spinner?.start();
1489
+ try {
1490
+ const cortex = await getCortex(rootDir);
1491
+ const content = await fs.readFile(input, 'utf-8');
1492
+ const data = JSON.parse(content);
1493
+ const memories = data.memories ?? data;
1494
+ let imported = 0;
1495
+ let skipped = 0;
1496
+ let errors = 0;
1497
+ for (const memory of memories) {
1498
+ try {
1499
+ const existing = await cortex.get(memory.id);
1500
+ if (existing && !overwrite) {
1501
+ skipped++;
1502
+ continue;
1503
+ }
1504
+ if (existing) {
1505
+ await cortex.update(memory.id, memory);
1506
+ }
1507
+ else {
1508
+ await cortex.add(memory);
1509
+ }
1510
+ imported++;
1511
+ }
1512
+ catch {
1513
+ errors++;
1514
+ }
1515
+ }
1516
+ await cortex.storage.close();
1517
+ spinner?.stop();
1518
+ if (format === 'json') {
1519
+ console.log(JSON.stringify({ imported, skipped, errors, total: memories.length }));
1520
+ return;
1521
+ }
1522
+ console.log();
1523
+ console.log(chalk.green.bold('✓ Import complete'));
1524
+ console.log();
1525
+ console.log(` Imported: ${chalk.green(imported)}`);
1526
+ console.log(` Skipped: ${skipped > 0 ? chalk.yellow(skipped) : '0'}`);
1527
+ console.log(` Errors: ${errors > 0 ? chalk.red(errors) : '0'}`);
1528
+ console.log(` Total: ${memories.length}`);
1529
+ console.log();
1530
+ }
1531
+ catch (error) {
1532
+ spinner?.stop();
1533
+ if (format === 'json') {
1534
+ console.log(JSON.stringify({ error: String(error) }));
1535
+ }
1536
+ else {
1537
+ console.log(chalk.red(`Error: ${error}`));
1538
+ }
1539
+ }
1540
+ }
1541
+ /**
1542
+ * Health subcommand - comprehensive health report
1543
+ */
1544
+ async function healthAction(options) {
1545
+ const rootDir = process.cwd();
1546
+ const format = options.format ?? 'text';
1547
+ if (!(await memoryExists(rootDir))) {
1548
+ if (format === 'json') {
1549
+ console.log(JSON.stringify({ error: 'Memory system not initialized' }));
1550
+ }
1551
+ else {
1552
+ showNoMemoriesMessage();
1553
+ }
1554
+ return;
1555
+ }
1556
+ const spinner = format === 'text' ? createSpinner('Analyzing memory health...') : null;
1557
+ spinner?.start();
1558
+ try {
1559
+ const cortex = await getCortex(rootDir);
1560
+ // Get memory statistics
1561
+ const countByType = await cortex.storage.countByType();
1562
+ const total = Object.values(countByType).reduce((sum, count) => sum + count, 0);
1563
+ // Get sample of memories for analysis
1564
+ const memories = await cortex.storage.search({ limit: 500 });
1565
+ let confidenceSum = 0;
1566
+ let lowConfidenceCount = 0;
1567
+ let recentlyAccessed = 0;
1568
+ const oneWeekAgo = new Date();
1569
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
1570
+ for (const memory of memories) {
1571
+ confidenceSum += memory.confidence;
1572
+ if (memory.confidence < 0.5) {
1573
+ lowConfidenceCount++;
1574
+ }
1575
+ if (memory.lastAccessed && new Date(memory.lastAccessed) > oneWeekAgo) {
1576
+ recentlyAccessed++;
1577
+ }
1578
+ }
1579
+ const avgConfidence = memories.length > 0 ? confidenceSum / memories.length : 0;
1580
+ // Identify issues
1581
+ const issues = [];
1582
+ const recommendations = [];
1583
+ if (avgConfidence < 0.5) {
1584
+ issues.push({
1585
+ severity: 'high',
1586
+ message: `Average memory confidence is low (${Math.round(avgConfidence * 100)}%)`,
1587
+ recommendation: 'Run validation to confirm or remove low-confidence memories',
1588
+ });
1589
+ }
1590
+ if (lowConfidenceCount > memories.length * 0.3) {
1591
+ issues.push({
1592
+ severity: 'medium',
1593
+ message: `${lowConfidenceCount} memories (${Math.round(lowConfidenceCount / memories.length * 100)}%) have low confidence`,
1594
+ recommendation: 'Review and validate these memories',
1595
+ });
1596
+ }
1597
+ if (total > 1000) {
1598
+ issues.push({
1599
+ severity: 'low',
1600
+ message: `Large memory count (${total}) may impact performance`,
1601
+ recommendation: 'Consider running consolidation to merge similar memories',
1602
+ });
1603
+ }
1604
+ if (recentlyAccessed < memories.length * 0.1) {
1605
+ issues.push({
1606
+ severity: 'low',
1607
+ message: 'Most memories have not been accessed recently',
1608
+ recommendation: 'Consider pruning unused memories',
1609
+ });
1610
+ }
1611
+ // Generate recommendations
1612
+ if (lowConfidenceCount > 10) {
1613
+ recommendations.push('Run `drift memory validate` to clean up low-confidence memories');
1614
+ }
1615
+ if (total > 500) {
1616
+ recommendations.push('Run `drift memory consolidate` to merge similar memories');
1617
+ }
1618
+ if (avgConfidence < 0.7) {
1619
+ recommendations.push('Use `drift memory feedback` to confirm accurate memories');
1620
+ }
1621
+ if (recommendations.length === 0) {
1622
+ recommendations.push('Memory system is healthy. Continue using as normal.');
1623
+ }
1624
+ // Calculate overall score
1625
+ let overallScore = 100;
1626
+ for (const issue of issues) {
1627
+ switch (issue.severity) {
1628
+ case 'critical':
1629
+ overallScore -= 30;
1630
+ break;
1631
+ case 'high':
1632
+ overallScore -= 20;
1633
+ break;
1634
+ case 'medium':
1635
+ overallScore -= 10;
1636
+ break;
1637
+ case 'low':
1638
+ overallScore -= 5;
1639
+ break;
1640
+ }
1641
+ }
1642
+ overallScore = Math.max(0, overallScore);
1643
+ await cortex.storage.close();
1644
+ spinner?.stop();
1645
+ if (format === 'json') {
1646
+ console.log(JSON.stringify({
1647
+ overallScore,
1648
+ status: overallScore >= 80 ? 'healthy' : overallScore >= 50 ? 'warning' : 'critical',
1649
+ memoryStats: {
1650
+ total,
1651
+ byType: countByType,
1652
+ avgConfidence,
1653
+ lowConfidenceCount,
1654
+ recentlyAccessed,
1655
+ },
1656
+ issues,
1657
+ recommendations,
1658
+ }, null, 2));
1659
+ return;
1660
+ }
1661
+ console.log();
1662
+ console.log(chalk.bold('🏥 Memory Health Report'));
1663
+ console.log(chalk.gray('═'.repeat(60)));
1664
+ console.log();
1665
+ console.log(chalk.bold('📊 Overall Health'));
1666
+ console.log(chalk.gray('─'.repeat(50)));
1667
+ console.log(` Score: ${getHealthColor(overallScore)}`);
1668
+ console.log();
1669
+ console.log(chalk.bold('📈 Statistics'));
1670
+ console.log(chalk.gray('─'.repeat(50)));
1671
+ console.log(` Total Memories: ${chalk.cyan(total)}`);
1672
+ console.log(` Avg Confidence: ${getConfidenceColor(avgConfidence)}`);
1673
+ console.log(` Low Confidence: ${lowConfidenceCount > 0 ? chalk.yellow(lowConfidenceCount) : chalk.green('0')}`);
1674
+ console.log(` Recently Accessed: ${chalk.cyan(recentlyAccessed)}`);
1675
+ console.log();
1676
+ if (issues.length > 0) {
1677
+ console.log(chalk.bold('⚠️ Issues'));
1678
+ console.log(chalk.gray('─'.repeat(50)));
1679
+ for (const issue of issues) {
1680
+ const icon = issue.severity === 'high' ? chalk.red('●') :
1681
+ issue.severity === 'medium' ? chalk.yellow('●') :
1682
+ chalk.gray('●');
1683
+ console.log(` ${icon} ${issue.message}`);
1684
+ console.log(chalk.gray(` → ${issue.recommendation}`));
1685
+ }
1686
+ console.log();
1687
+ }
1688
+ console.log(chalk.bold('💡 Recommendations'));
1689
+ console.log(chalk.gray('─'.repeat(50)));
1690
+ for (const rec of recommendations) {
1691
+ console.log(` • ${rec}`);
1692
+ }
1693
+ console.log();
1694
+ }
1695
+ catch (error) {
1696
+ spinner?.stop();
1697
+ if (format === 'json') {
1698
+ console.log(JSON.stringify({ error: String(error) }));
1699
+ }
1700
+ else {
1701
+ console.log(chalk.red(`Error: ${error}`));
1702
+ }
1703
+ }
1704
+ }
1705
+ // ============================================================================
1706
+ // Command Registration
1707
+ // ============================================================================
1708
+ export function createMemoryCommand() {
1709
+ const cmd = new Command('memory')
1710
+ .description('Manage Cortex V2 memories - institutional knowledge, procedures, patterns, and more')
1711
+ .option('-f, --format <format>', 'Output format (text, json)', 'text')
1712
+ .option('-v, --verbose', 'Enable verbose output');
1713
+ // Initialize
1714
+ cmd
1715
+ .command('init')
1716
+ .description('Initialize the memory system')
1717
+ .action(() => initAction(cmd.opts()));
1718
+ // Status
1719
+ cmd
1720
+ .command('status')
1721
+ .description('Show memory system status and health')
1722
+ .action(() => statusAction(cmd.opts()));
1723
+ // Add
1724
+ cmd
1725
+ .command('add <type> <content>')
1726
+ .description('Add a new memory (types: tribal, procedural, pattern_rationale, code_smell, decision_context, constraint_override)')
1727
+ .option('-t, --topic <topic>', 'Topic or name for the memory')
1728
+ .option('-s, --severity <severity>', 'Severity level (info, warning, critical)', 'warning')
1729
+ .option('-i, --importance <importance>', 'Importance level (low, normal, high, critical)', 'normal')
1730
+ .option('--tags <tags>', 'Comma-separated tags')
1731
+ .option('--file <file>', 'Link to a file')
1732
+ .option('--pattern <pattern>', 'Link to a pattern ID')
1733
+ .action((type, content, opts) => addAction(type, content, { ...cmd.opts(), ...opts }));
1734
+ // List
1735
+ cmd
1736
+ .command('list')
1737
+ .description('List memories')
1738
+ .option('-t, --type <type>', 'Filter by memory type')
1739
+ .option('-i, --importance <importance>', 'Filter by importance')
1740
+ .option('-l, --limit <number>', 'Maximum results', '20')
1741
+ .option('--min-confidence <number>', 'Minimum confidence threshold')
1742
+ .action((opts) => listAction({ ...cmd.opts(), ...opts }));
1743
+ // Show
1744
+ cmd
1745
+ .command('show <id>')
1746
+ .description('Show memory details')
1747
+ .action((id) => showAction(id, cmd.opts()));
1748
+ // Search
1749
+ cmd
1750
+ .command('search <query>')
1751
+ .description('Search memories')
1752
+ .option('-t, --type <type>', 'Filter by memory type')
1753
+ .option('-l, --limit <number>', 'Maximum results', '20')
1754
+ .action((query, opts) => searchAction(query, { ...cmd.opts(), ...opts }));
1755
+ // Update
1756
+ cmd
1757
+ .command('update <id>')
1758
+ .description('Update a memory')
1759
+ .option('-c, --confidence <number>', 'New confidence value (0-1)')
1760
+ .option('-i, --importance <importance>', 'New importance level')
1761
+ .option('--tags <tags>', 'New comma-separated tags')
1762
+ .option('--summary <summary>', 'New summary')
1763
+ .action((id, opts) => updateAction(id, { ...cmd.opts(), ...opts }));
1764
+ // Delete
1765
+ cmd
1766
+ .command('delete <id>')
1767
+ .description('Delete a memory (soft delete)')
1768
+ .action((id) => deleteAction(id, cmd.opts()));
1769
+ // Learn
1770
+ cmd
1771
+ .command('learn <correction>')
1772
+ .description('Learn from a correction (e.g., "Always use bcrypt with cost factor 12")')
1773
+ .option('-o, --original <text>', 'What was originally done (optional context)')
1774
+ .option('-c, --code <code>', 'Corrected code example')
1775
+ .option('--file <file>', 'Related file')
1776
+ .action((correction, opts) => learnAction(correction, { ...cmd.opts(), ...opts }));
1777
+ // Feedback
1778
+ cmd
1779
+ .command('feedback <id> <action>')
1780
+ .description('Provide feedback on a memory (actions: confirm, reject, modify)')
1781
+ .option('-d, --details <text>', 'Additional details')
1782
+ .action((id, action, opts) => feedbackAction(id, action, { ...cmd.opts(), ...opts }));
1783
+ // Validate
1784
+ cmd
1785
+ .command('validate')
1786
+ .description('Validate memories and optionally heal issues')
1787
+ .option('-s, --scope <scope>', 'Scope: all, stale, recent, high_importance', 'stale')
1788
+ .option('--auto-heal', 'Automatically heal minor issues', true)
1789
+ .option('--remove-invalid', 'Remove memories that cannot be healed')
1790
+ .option('--min-confidence <number>', 'Minimum confidence to keep', '0.2')
1791
+ .action((opts) => validateAction({ ...cmd.opts(), ...opts }));
1792
+ // Consolidate
1793
+ cmd
1794
+ .command('consolidate')
1795
+ .description('Consolidate episodic memories into semantic knowledge')
1796
+ .option('--dry-run', 'Preview without making changes')
1797
+ .action((opts) => consolidateAction({ ...cmd.opts(), ...opts }));
1798
+ // Warnings
1799
+ cmd
1800
+ .command('warnings')
1801
+ .description('Show active warnings from tribal knowledge and code smells')
1802
+ .option('--focus <focus>', 'Filter by focus area')
1803
+ .option('--severity <severity>', 'Filter by severity (all, critical, warning)', 'all')
1804
+ .action((opts) => warningsAction({ ...cmd.opts(), ...opts }));
1805
+ // Why
1806
+ cmd
1807
+ .command('why <focus>')
1808
+ .description('Get context for a task - patterns, decisions, tribal knowledge')
1809
+ .option('-i, --intent <intent>', 'Intent: add_feature, fix_bug, refactor, security_audit, understand_code, add_test', 'understand_code')
1810
+ .option('--max-tokens <number>', 'Maximum tokens to use', '2000')
1811
+ .action((focus, opts) => whyAction(focus, { ...cmd.opts(), ...opts }));
1812
+ // Export
1813
+ cmd
1814
+ .command('export <output>')
1815
+ .description('Export memories to JSON file')
1816
+ .option('-t, --type <type>', 'Filter by memory type')
1817
+ .option('--min-confidence <number>', 'Minimum confidence threshold')
1818
+ .option('--include-archived', 'Include archived memories')
1819
+ .action((output, opts) => exportAction(output, { ...cmd.opts(), ...opts }));
1820
+ // Import
1821
+ cmd
1822
+ .command('import <input>')
1823
+ .description('Import memories from JSON file')
1824
+ .option('--overwrite', 'Overwrite existing memories with same ID')
1825
+ .action((input, opts) => importAction(input, { ...cmd.opts(), ...opts }));
1826
+ // Health
1827
+ cmd
1828
+ .command('health')
1829
+ .description('Get comprehensive health report')
1830
+ .action(() => healthAction(cmd.opts()));
1831
+ return cmd;
1832
+ }
1833
+ //# sourceMappingURL=memory.js.map