@hbarefoot/engram 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/engram.js CHANGED
@@ -16,19 +16,35 @@ import { fileURLToPath } from 'url';
16
16
  import { dirname, join } from 'path';
17
17
  import fs from 'fs';
18
18
 
19
- // Read version from package.json
19
+ // Read version from package.json (may not exist when running as bundled sidecar)
20
20
  const __filename = fileURLToPath(import.meta.url);
21
21
  const __dirname = dirname(__filename);
22
- const packageJson = JSON.parse(
23
- readFileSync(join(__dirname, '../package.json'), 'utf-8')
24
- );
22
+ let version = '1.1.0';
23
+ try {
24
+ const packageJson = JSON.parse(
25
+ readFileSync(join(__dirname, '../package.json'), 'utf-8')
26
+ );
27
+ version = packageJson.version;
28
+ } catch {
29
+ // Running as bundled sidecar — package.json not available
30
+ }
25
31
 
26
32
  const program = new Command();
27
33
 
28
34
  program
29
35
  .name('engram')
30
36
  .description('Persistent memory for AI agents - SQLite for agent state')
31
- .version(packageJson.version);
37
+ .version(version);
38
+
39
+ // ── Lazy format helpers (only loaded for non-MCP commands) ───────────
40
+
41
+ let fmt;
42
+ async function loadFormat() {
43
+ if (!fmt) {
44
+ fmt = await import('../src/utils/format.js');
45
+ }
46
+ return fmt;
47
+ }
32
48
 
33
49
  // Start server command
34
50
  program
@@ -39,28 +55,32 @@ program
39
55
  .option('--config <path>', 'Path to config file')
40
56
  .action(async (options) => {
41
57
  if (options.mcpOnly) {
42
- // Start MCP server only (stdio mode)
58
+ // Start MCP server only (stdio mode) — no formatting imports
43
59
  logger.info('Starting Engram MCP server (stdio mode)...');
44
60
  await startMCPServer(options.config);
45
61
  } else {
46
- // Start REST API server
62
+ const f = await loadFormat();
63
+ const chalk = (await import('chalk')).default;
47
64
  const config = loadConfig(options.config);
48
65
  const port = parseInt(options.port);
49
66
 
50
- logger.info('Starting Engram REST API server...');
51
- logger.info(`Server available at http://localhost:${port}`);
52
- logger.info(`Dashboard available at http://localhost:${port}`);
67
+ f.printHeader(version);
68
+
69
+ f.info(`REST API ${chalk.cyan(`http://localhost:${port}`)}`);
70
+ f.info(`Dashboard ${chalk.cyan(`http://localhost:${port}`)}`);
71
+ console.log('');
53
72
 
54
73
  await startRESTServer(config, port);
55
74
 
56
75
  // Keep process alive
57
76
  process.on('SIGINT', () => {
58
- logger.info('Received SIGINT, shutting down...');
77
+ console.log('');
78
+ f.info('Shutting down...');
59
79
  process.exit(0);
60
80
  });
61
81
 
62
82
  process.on('SIGTERM', () => {
63
- logger.info('Received SIGTERM, shutting down...');
83
+ f.info('Shutting down...');
64
84
  process.exit(0);
65
85
  });
66
86
  }
@@ -76,6 +96,7 @@ program
76
96
  .option('-n, --namespace <name>', 'Project/scope namespace', 'default')
77
97
  .option('--config <path>', 'Path to config file')
78
98
  .action(async (content, options) => {
99
+ const f = await loadFormat();
79
100
  try {
80
101
  const config = loadConfig(options.config);
81
102
  const db = initDatabase(getDatabasePath(config));
@@ -84,7 +105,7 @@ program
84
105
  const validation = validateContent(content, { autoRedact: config.security?.secretDetection !== false });
85
106
 
86
107
  if (!validation.valid) {
87
- console.error('❌ Cannot store memory:', validation.errors.join(', '));
108
+ f.error(`Cannot store memory: ${validation.errors.join(', ')}`);
88
109
  process.exit(1);
89
110
  }
90
111
 
@@ -103,31 +124,40 @@ program
103
124
  memoryData.entity = extracted.entity;
104
125
  }
105
126
 
106
- // Generate embedding
127
+ // Generate embedding with spinner
128
+ const spin = f.spinner('Generating embedding...');
129
+ spin.start();
107
130
  try {
108
131
  const { generateEmbedding } = await import('../src/embed/index.js');
109
132
  const embedding = await generateEmbedding(validation.content, getModelsPath(config));
110
133
  memoryData.embedding = embedding;
134
+ spin.succeed('Embedding generated');
111
135
  } catch (error) {
112
- logger.warn('Failed to generate embedding', { error: error.message });
136
+ spin.warn('Embedding skipped (model unavailable)');
113
137
  }
114
138
 
115
139
  const memory = createMemory(db, memoryData);
116
140
 
117
- console.log('✅ Memory stored successfully!');
118
- console.log(`ID: ${memory.id}`);
119
- console.log(`Category: ${memory.category}`);
120
- console.log(`Entity: ${memory.entity || 'none'}`);
121
- console.log(`Confidence: ${memory.confidence}`);
122
- console.log(`Namespace: ${memory.namespace}`);
141
+ console.log('');
142
+ f.success('Memory stored');
143
+ console.log('');
144
+ f.printKeyValue([
145
+ ['ID', memory.id],
146
+ ['Category', f.categoryBadge(memory.category)],
147
+ ['Entity', memory.entity || 'none'],
148
+ ['Confidence', f.confidenceColor(memory.confidence)],
149
+ ['Namespace', memory.namespace]
150
+ ]);
123
151
 
124
152
  if (validation.warnings?.length > 0) {
125
- console.log(`\n⚠️ Warnings: ${validation.warnings.join(', ')}`);
153
+ console.log('');
154
+ f.warning(`Warnings: ${validation.warnings.join(', ')}`);
126
155
  }
156
+ console.log('');
127
157
 
128
158
  db.close();
129
159
  } catch (error) {
130
- console.error('❌ Error:', error.message);
160
+ f.error(error.message);
131
161
  process.exit(1);
132
162
  }
133
163
  });
@@ -136,12 +166,14 @@ program
136
166
  program
137
167
  .command('recall <query>')
138
168
  .description('Recall memories matching a query')
139
- .option('-l, --limit <n>', 'Max results (default 5)', parseInt, 5)
169
+ .option('-l, --limit <n>', 'Max results (default 5)', v => parseInt(v, 10), 5)
140
170
  .option('-c, --category <type>', 'Filter by category')
141
171
  .option('-n, --namespace <name>', 'Filter by namespace')
142
172
  .option('--threshold <score>', 'Minimum relevance score (0-1)', parseFloat, 0.3)
143
173
  .option('--config <path>', 'Path to config file')
144
174
  .action(async (query, options) => {
175
+ const f = await loadFormat();
176
+ const chalk = (await import('chalk')).default;
145
177
  try {
146
178
  const config = loadConfig(options.config);
147
179
  const db = initDatabase(getDatabasePath(config));
@@ -159,12 +191,42 @@ program
159
191
  modelsPath
160
192
  );
161
193
 
162
- const formatted = formatRecallResults(memories);
163
- console.log(formatted);
194
+ if (memories.length === 0) {
195
+ console.log('');
196
+ f.info('No relevant memories found.');
197
+ console.log('');
198
+ db.close();
199
+ return;
200
+ }
201
+
202
+ f.printSection(`${memories.length} result${memories.length === 1 ? '' : 's'}`);
203
+ console.log('');
204
+
205
+ memories.forEach((memory, index) => {
206
+ const num = chalk.bold(`#${index + 1}`);
207
+ const cat = f.categoryBadge(memory.category);
208
+ const id = f.shortId(memory.id);
209
+ const score = memory.score ? f.scoreDisplay(memory.score, { showBar: true }) : '';
210
+
211
+ console.log(`${num} ${cat} ${id} ${score}`);
212
+ console.log(` ${memory.content}`);
213
+
214
+ if (memory.scoreBreakdown) {
215
+ const bd = memory.scoreBreakdown;
216
+ const parts = [
217
+ `sim=${chalk.dim(bd.similarity.toFixed(2))}`,
218
+ `rec=${chalk.dim(bd.recency.toFixed(2))}`,
219
+ `conf=${chalk.dim(bd.confidence.toFixed(2))}`,
220
+ `fts=${chalk.dim(bd.ftsBoost.toFixed(2))}`
221
+ ];
222
+ console.log(` ${chalk.dim(parts.join(' '))}`);
223
+ }
224
+ console.log('');
225
+ });
164
226
 
165
227
  db.close();
166
228
  } catch (error) {
167
- console.error('❌ Error:', error.message);
229
+ f.error(error.message);
168
230
  process.exit(1);
169
231
  }
170
232
  });
@@ -174,7 +236,9 @@ program
174
236
  .command('forget <id>')
175
237
  .description('Delete a memory by ID')
176
238
  .option('--config <path>', 'Path to config file')
177
- .action((id, options) => {
239
+ .action(async (id, options) => {
240
+ const f = await loadFormat();
241
+ const chalk = (await import('chalk')).default;
178
242
  try {
179
243
  const config = loadConfig(options.config);
180
244
  const db = initDatabase(getDatabasePath(config));
@@ -182,23 +246,32 @@ program
182
246
  const memory = getMemory(db, id);
183
247
 
184
248
  if (!memory) {
185
- console.error(`❌ Memory not found: ${id}`);
249
+ f.error(`Memory not found: ${id}`);
186
250
  db.close();
187
251
  process.exit(1);
188
252
  }
189
253
 
254
+ // Show preview before deletion
255
+ console.log('');
256
+ console.log(f.box(
257
+ `${chalk.bold('Deleting memory')} ${f.shortId(memory.id)}\n` +
258
+ `${f.categoryBadge(memory.category)} conf ${f.confidenceColor(memory.confidence)}\n` +
259
+ `${f.truncate(memory.content, 60)}`
260
+ ));
261
+
190
262
  const deleted = deleteMemory(db, id);
191
263
 
264
+ console.log('');
192
265
  if (deleted) {
193
- console.log(`✅ Memory deleted: ${id}`);
194
- console.log(`Content: ${memory.content}`);
266
+ f.success(`Memory deleted: ${id}`);
195
267
  } else {
196
- console.error(`❌ Failed to delete memory: ${id}`);
268
+ f.error(`Failed to delete memory: ${id}`);
197
269
  }
270
+ console.log('');
198
271
 
199
272
  db.close();
200
273
  } catch (error) {
201
- console.error('❌ Error:', error.message);
274
+ f.error(error.message);
202
275
  process.exit(1);
203
276
  }
204
277
  });
@@ -207,12 +280,13 @@ program
207
280
  program
208
281
  .command('list')
209
282
  .description('List all memories (paginated)')
210
- .option('-l, --limit <n>', 'Max results', parseInt, 50)
211
- .option('--offset <n>', 'Offset for pagination', parseInt, 0)
283
+ .option('-l, --limit <n>', 'Max results', v => parseInt(v, 10), 50)
284
+ .option('--offset <n>', 'Offset for pagination', v => parseInt(v, 10), 0)
212
285
  .option('-c, --category <type>', 'Filter by category')
213
286
  .option('-n, --namespace <name>', 'Filter by namespace')
214
287
  .option('--config <path>', 'Path to config file')
215
- .action((options) => {
288
+ .action(async (options) => {
289
+ const f = await loadFormat();
216
290
  try {
217
291
  const config = loadConfig(options.config);
218
292
  const db = initDatabase(getDatabasePath(config));
@@ -224,19 +298,37 @@ program
224
298
  namespace: options.namespace
225
299
  });
226
300
 
227
- console.log(`\nFound ${memories.length} memories:\n`);
301
+ f.printSection(`${memories.length} memories`);
228
302
 
229
- memories.forEach((memory, index) => {
230
- console.log(`[${index + 1}] ${memory.id.substring(0, 8)}`);
231
- console.log(` ${memory.content}`);
232
- console.log(` Category: ${memory.category} | Entity: ${memory.entity || 'none'} | Confidence: ${memory.confidence}`);
233
- console.log(` Namespace: ${memory.namespace} | Accessed: ${memory.access_count} times`);
303
+ if (memories.length === 0) {
234
304
  console.log('');
305
+ f.info('No memories found.');
306
+ console.log('');
307
+ db.close();
308
+ return;
309
+ }
310
+
311
+ const table = f.createTable({
312
+ head: ['ID', 'Content', 'Category', 'Conf', 'Access', 'Namespace']
235
313
  });
236
314
 
315
+ for (const memory of memories) {
316
+ table.push([
317
+ f.shortId(memory.id),
318
+ f.truncate(memory.content, 50),
319
+ f.categoryBadge(memory.category),
320
+ f.confidenceColor(memory.confidence),
321
+ String(memory.access_count),
322
+ memory.namespace
323
+ ]);
324
+ }
325
+
326
+ console.log(table.toString());
327
+ console.log('');
328
+
237
329
  db.close();
238
330
  } catch (error) {
239
- console.error('❌ Error:', error.message);
331
+ f.error(error.message);
240
332
  process.exit(1);
241
333
  }
242
334
  });
@@ -247,50 +339,86 @@ program
247
339
  .description('Show Engram status and statistics')
248
340
  .option('--config <path>', 'Path to config file')
249
341
  .action(async (options) => {
342
+ const f = await loadFormat();
343
+ const chalk = (await import('chalk')).default;
250
344
  try {
251
345
  const config = loadConfig(options.config);
252
346
  const db = initDatabase(getDatabasePath(config));
253
347
  const stats = getStats(db);
254
348
 
255
- console.log('\n📊 Engram Status\n');
349
+ f.printHeader(version);
256
350
 
257
- console.log('Memory Statistics:');
258
- console.log(` Total memories: ${stats.total}`);
259
- console.log(` With embeddings: ${stats.withEmbeddings}`);
260
- console.log(` By category:`);
261
- for (const [category, count] of Object.entries(stats.byCategory)) {
262
- console.log(` - ${category}: ${count}`);
351
+ // Memory statistics
352
+ f.printSection('Memory Statistics');
353
+ console.log('');
354
+ f.printKeyValue([
355
+ ['Total memories', chalk.bold(String(stats.total))],
356
+ ['With embeddings', String(stats.withEmbeddings)]
357
+ ]);
358
+
359
+ // Categories table
360
+ if (Object.keys(stats.byCategory).length > 0) {
361
+ console.log('');
362
+ const catTable = f.createTable({ head: ['Category', 'Count'] });
363
+ for (const [category, count] of Object.entries(stats.byCategory)) {
364
+ catTable.push([f.categoryBadge(category), String(count)]);
365
+ }
366
+ console.log(catTable.toString());
263
367
  }
264
- console.log(` By namespace:`);
265
- for (const [namespace, count] of Object.entries(stats.byNamespace)) {
266
- console.log(` - ${namespace}: ${count}`);
368
+
369
+ // Namespaces table
370
+ if (Object.keys(stats.byNamespace).length > 0) {
371
+ console.log('');
372
+ const nsTable = f.createTable({ head: ['Namespace', 'Count'] });
373
+ for (const [namespace, count] of Object.entries(stats.byNamespace)) {
374
+ nsTable.push([namespace, String(count)]);
375
+ }
376
+ console.log(nsTable.toString());
267
377
  }
268
378
 
269
379
  // Model info
380
+ f.printSection('Embedding Model');
381
+ console.log('');
270
382
  try {
271
383
  const { getModelInfo } = await import('../src/embed/index.js');
272
384
  const modelInfo = getModelInfo(getModelsPath(config));
273
385
 
274
- console.log('\n🤖 Embedding Model:');
275
- console.log(` Name: ${modelInfo.name}`);
276
- console.log(` Available: ${modelInfo.available ? '' : '❌'}`);
277
- console.log(` Cached: ${modelInfo.cached ? '✅' : '❌'}`);
278
- console.log(` Size: ${modelInfo.sizeMB} MB`);
279
- console.log(` Path: ${modelInfo.path}`);
280
- } catch (error) {
281
- console.log('\n🤖 Embedding Model: ❌ Not available');
386
+ let statusText;
387
+ if (modelInfo.cached) {
388
+ statusText = chalk.green('Ready');
389
+ } else if (modelInfo.loading) {
390
+ statusText = chalk.yellow('Loading...');
391
+ } else if (modelInfo.available) {
392
+ statusText = chalk.green('Available');
393
+ } else {
394
+ statusText = chalk.red('Not available');
395
+ }
396
+
397
+ f.printKeyValue([
398
+ ['Name', modelInfo.name],
399
+ ['Status', statusText],
400
+ ['Size', `${modelInfo.sizeMB} MB`],
401
+ ['Path', chalk.dim(modelInfo.path)]
402
+ ]);
403
+ } catch {
404
+ f.printKeyValue([['Status', chalk.red('Not available')]]);
282
405
  }
283
406
 
284
- console.log('\n⚙️ Configuration:');
285
- console.log(` Data directory: ${config.dataDir}`);
286
- console.log(` Default namespace: ${config.defaults.namespace}`);
287
- console.log(` Recall limit: ${config.defaults.recallLimit}`);
288
- console.log(` Secret detection: ${config.security.secretDetection ? '✅' : '❌'}`);
407
+ // Configuration
408
+ f.printSection('Configuration');
409
+ console.log('');
410
+ f.printKeyValue([
411
+ ['Data directory', chalk.dim(config.dataDir)],
412
+ ['Namespace', config.defaults.namespace],
413
+ ['Recall limit', String(config.defaults.recallLimit)],
414
+ ['Secret detection', config.security.secretDetection ? chalk.green('on') : chalk.red('off')]
415
+ ]);
289
416
  console.log('');
290
417
 
291
418
  db.close();
292
419
  } catch (error) {
293
- console.error('❌ Error:', error.message);
420
+ const f2 = await loadFormat();
421
+ f2.error(error.message);
294
422
  process.exit(1);
295
423
  }
296
424
  });
@@ -305,11 +433,13 @@ program
305
433
  .option('--cleanup-stale', 'Enable stale memory cleanup')
306
434
  .option('--config <path>', 'Path to config file')
307
435
  .action(async (options) => {
436
+ const f = await loadFormat();
308
437
  try {
309
438
  const config = loadConfig(options.config);
310
439
  const db = initDatabase(getDatabasePath(config));
311
440
 
312
- console.log('🔄 Running consolidation...\n');
441
+ const spin = f.spinner('Running consolidation...');
442
+ spin.start();
313
443
 
314
444
  const results = await consolidate(db, {
315
445
  detectDuplicates: options.duplicates !== false,
@@ -318,17 +448,23 @@ program
318
448
  cleanupStale: options.cleanupStale === true
319
449
  });
320
450
 
321
- console.log('Consolidation complete!');
322
- console.log(` Duplicates removed: ${results.duplicatesRemoved}`);
323
- console.log(` Contradictions detected: ${results.contradictionsDetected}`);
324
- console.log(` Memories decayed: ${results.memoriesDecayed}`);
325
- console.log(` Stale memories cleaned: ${results.staleMemoriesCleaned}`);
326
- console.log(` Duration: ${results.duration}ms`);
451
+ spin.succeed('Consolidation complete');
452
+ console.log('');
453
+
454
+ const table = f.createTable({ head: ['Metric', 'Count'] });
455
+ table.push(
456
+ ['Duplicates removed', String(results.duplicatesRemoved)],
457
+ ['Contradictions detected', String(results.contradictionsDetected)],
458
+ ['Memories decayed', String(results.memoriesDecayed)],
459
+ ['Stale cleaned', String(results.staleMemoriesCleaned)],
460
+ ['Duration', `${results.duration}ms`]
461
+ );
462
+ console.log(table.toString());
327
463
  console.log('');
328
464
 
329
465
  db.close();
330
466
  } catch (error) {
331
- console.error('❌ Error:', error.message);
467
+ f.error(error.message);
332
468
  process.exit(1);
333
469
  }
334
470
  });
@@ -338,7 +474,9 @@ program
338
474
  .command('conflicts')
339
475
  .description('Show detected contradictions')
340
476
  .option('--config <path>', 'Path to config file')
341
- .action((options) => {
477
+ .action(async (options) => {
478
+ const f = await loadFormat();
479
+ const chalk = (await import('chalk')).default;
342
480
  try {
343
481
  const config = loadConfig(options.config);
344
482
  const db = initDatabase(getDatabasePath(config));
@@ -346,22 +484,28 @@ program
346
484
  const conflicts = getConflicts(db);
347
485
 
348
486
  if (conflicts.length === 0) {
349
- console.log('✅ No conflicts detected');
487
+ console.log('');
488
+ f.success('No conflicts detected');
489
+ console.log('');
350
490
  } else {
351
- console.log(`\n⚠️ Found ${conflicts.length} conflict(s):\n`);
491
+ f.printSection(`${conflicts.length} conflict${conflicts.length === 1 ? '' : 's'}`);
492
+ console.log('');
352
493
 
353
494
  conflicts.forEach((conflict, index) => {
354
- console.log(`Conflict ${index + 1}: ${conflict.conflictId}`);
355
- conflict.memories.forEach((memory, mIndex) => {
356
- console.log(` [${String.fromCharCode(65 + mIndex)}] ${memory.id.substring(0, 8)}: ${memory.content}`);
357
- });
495
+ const header = `${chalk.bold(`Conflict ${index + 1}`)} ${chalk.dim(conflict.conflictId)}`;
496
+ const memLines = conflict.memories.map((memory, mIndex) => {
497
+ const label = chalk.bold(String.fromCharCode(65 + mIndex));
498
+ return `${label} ${f.shortId(memory.id)} ${f.categoryBadge(memory.category || 'fact')}\n ${f.truncate(memory.content, 56)}`;
499
+ }).join('\n');
500
+
501
+ console.log(f.box(`${header}\n${memLines}`));
358
502
  console.log('');
359
503
  });
360
504
  }
361
505
 
362
506
  db.close();
363
507
  } catch (error) {
364
- console.error('❌ Error:', error.message);
508
+ f.error(error.message);
365
509
  process.exit(1);
366
510
  }
367
511
  });
@@ -375,13 +519,15 @@ program
375
519
  .option('-f, --format <type>', 'Format: markdown, claude, txt, json', 'markdown')
376
520
  .option('-c, --categories <list>', 'Comma-separated list of categories')
377
521
  .option('--min-confidence <score>', 'Minimum confidence (0-1)', parseFloat, 0.5)
378
- .option('--min-access <count>', 'Minimum access count', parseInt, 0)
522
+ .option('--min-access <count>', 'Minimum access count', v => parseInt(v, 10), 0)
379
523
  .option('--include-low-feedback', 'Include memories with negative feedback', false)
380
524
  .option('--group-by <field>', 'Group by: category, entity, none', 'category')
381
525
  .option('--header <text>', 'Custom header text')
382
526
  .option('--footer <text>', 'Custom footer text')
383
527
  .option('--config <path>', 'Path to config file')
384
- .action((options) => {
528
+ .action(async (options) => {
529
+ const f = await loadFormat();
530
+ const chalk = (await import('chalk')).default;
385
531
  try {
386
532
  const config = loadConfig(options.config);
387
533
  const db = initDatabase(getDatabasePath(config));
@@ -403,17 +549,56 @@ program
403
549
  if (options.output) {
404
550
  // Write to file
405
551
  fs.writeFileSync(options.output, result.content, 'utf8');
406
- console.log(`✅ Exported ${result.stats.totalExported} memories to ${options.output}`);
407
- console.log(` Size: ${result.stats.sizeKB} KB`);
408
- console.log(` By category:`, result.stats.byCategory);
552
+ console.log('');
553
+ f.success(`Exported to ${chalk.bold(options.output)}`);
554
+ console.log('');
555
+ f.printKeyValue([
556
+ ['Memories', String(result.stats.totalExported)],
557
+ ['Size', `${result.stats.sizeKB} KB`],
558
+ ['Format', options.format]
559
+ ]);
560
+
561
+ if (result.stats.byCategory && Object.keys(result.stats.byCategory).length > 0) {
562
+ console.log('');
563
+ const catTable = f.createTable({ head: ['Category', 'Count'] });
564
+ for (const [cat, count] of Object.entries(result.stats.byCategory)) {
565
+ catTable.push([f.categoryBadge(cat), String(count)]);
566
+ }
567
+ console.log(catTable.toString());
568
+ }
569
+ console.log('');
409
570
  } else {
410
- // Output to stdout
571
+ // Output to stdout (raw, no formatting)
411
572
  console.log(result.content);
412
573
  }
413
574
 
414
575
  db.close();
415
576
  } catch (error) {
416
- console.error('❌ Error:', error.message);
577
+ f.error(error.message);
578
+ process.exit(1);
579
+ }
580
+ });
581
+
582
+ // Import wizard command
583
+ program
584
+ .command('import')
585
+ .description('Import memories from developer artifacts (smart import wizard)')
586
+ .option('-s, --source <type>', 'Single source: cursorrules, claude, package, git, ssh, shell, obsidian, env')
587
+ .option('--dry-run', 'Preview without committing')
588
+ .option('-n, --namespace <name>', 'Override namespace for imported memories')
589
+ .option('--config <path>', 'Path to config file')
590
+ .action(async (options) => {
591
+ try {
592
+ const { runWizard } = await import('../src/import/wizard.js');
593
+ await runWizard({
594
+ source: options.source,
595
+ dryRun: options.dryRun,
596
+ namespace: options.namespace,
597
+ config: options.config
598
+ });
599
+ } catch (error) {
600
+ const f = await loadFormat();
601
+ f.error(`Import error: ${error.message}`);
417
602
  process.exit(1);
418
603
  }
419
604
  });