monomind 1.18.9 → 1.18.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/package.json +1 -1
  2. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.d.ts +1 -2
  3. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-commands.js +1 -2
  4. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.d.ts +1 -2
  5. package/packages/@monomind/cli/dist/src/commands/hooks-coverage-gaps.js +2 -117
  6. package/packages/@monomind/cli/dist/src/commands/hooks.js +1 -2
  7. package/packages/@monomind/cli/dist/src/commands/index.d.ts +0 -1
  8. package/packages/@monomind/cli/dist/src/commands/index.js +0 -1
  9. package/packages/@monomind/cli/dist/src/init/executor.d.ts +3 -0
  10. package/packages/@monomind/cli/dist/src/init/executor.js +20 -111
  11. package/packages/@monomind/cli/dist/src/mcp-client.js +0 -2
  12. package/packages/@monomind/cli/dist/src/mcp-tools/index.d.ts +0 -1
  13. package/packages/@monomind/cli/dist/src/mcp-tools/index.js +0 -1
  14. package/packages/@monomind/cli/dist/src/mcp-tools/quality/coverage-analysis/track-trends.d.ts +4 -4
  15. package/packages/@monomind/cli/dist/src/mcp-tools/quality/test-generation/suggest-tests.d.ts +4 -4
  16. package/packages/@monomind/cli/package.json +1 -1
  17. package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.d.ts +0 -134
  18. package/packages/@monomind/cli/dist/src/benchmarks/benchmark-runner.js +0 -340
  19. package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.d.ts +0 -36
  20. package/packages/@monomind/cli/dist/src/benchmarks/metric-evaluators.js +0 -125
  21. package/packages/@monomind/cli/dist/src/commands/benchmark.d.ts +0 -10
  22. package/packages/@monomind/cli/dist/src/commands/benchmark.js +0 -586
  23. package/packages/@monomind/cli/dist/src/commands/migrate.d.ts +0 -8
  24. package/packages/@monomind/cli/dist/src/commands/migrate.js +0 -789
  25. package/packages/@monomind/cli/dist/src/commands/monovector/backup.d.ts +0 -11
  26. package/packages/@monomind/cli/dist/src/commands/monovector/backup.js +0 -752
  27. package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.d.ts +0 -11
  28. package/packages/@monomind/cli/dist/src/commands/monovector/benchmark.js +0 -502
  29. package/packages/@monomind/cli/dist/src/commands/monovector/import.d.ts +0 -18
  30. package/packages/@monomind/cli/dist/src/commands/monovector/import.js +0 -374
  31. package/packages/@monomind/cli/dist/src/commands/monovector/index.d.ts +0 -29
  32. package/packages/@monomind/cli/dist/src/commands/monovector/index.js +0 -129
  33. package/packages/@monomind/cli/dist/src/commands/monovector/init.d.ts +0 -11
  34. package/packages/@monomind/cli/dist/src/commands/monovector/init.js +0 -481
  35. package/packages/@monomind/cli/dist/src/commands/monovector/migrate.d.ts +0 -11
  36. package/packages/@monomind/cli/dist/src/commands/monovector/migrate.js +0 -500
  37. package/packages/@monomind/cli/dist/src/commands/monovector/optimize.d.ts +0 -11
  38. package/packages/@monomind/cli/dist/src/commands/monovector/optimize.js +0 -515
  39. package/packages/@monomind/cli/dist/src/commands/monovector/setup.d.ts +0 -18
  40. package/packages/@monomind/cli/dist/src/commands/monovector/setup.js +0 -775
  41. package/packages/@monomind/cli/dist/src/commands/monovector/status.d.ts +0 -11
  42. package/packages/@monomind/cli/dist/src/commands/monovector/status.js +0 -491
  43. package/packages/@monomind/cli/dist/src/commands/progress.d.ts +0 -11
  44. package/packages/@monomind/cli/dist/src/commands/progress.js +0 -261
  45. package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.d.ts +0 -14
  46. package/packages/@monomind/cli/dist/src/mcp-tools/progress-tools.js +0 -353
@@ -1,789 +0,0 @@
1
- /**
2
- * CLI Migrate Command
3
- * Migration tools for V2 to v1 transition
4
- */
5
- import { output } from '../output.js';
6
- import * as fs from 'fs';
7
- import * as path from 'path';
8
- const MAX_MIGRATE_FILE_BYTES = 10 * 1024 * 1024; // 10 MB
9
- function sanitizeJsonObject(raw) {
10
- if (typeof raw !== 'object' || raw === null || Array.isArray(raw))
11
- return {};
12
- const safe = Object.create(null);
13
- for (const [k, v] of Object.entries(raw)) {
14
- if (k === '__proto__' || k === 'constructor' || k === 'prototype')
15
- continue;
16
- safe[k] = v;
17
- }
18
- return safe;
19
- }
20
- function safeFileCopy(src, dest) {
21
- const lstat = fs.lstatSync(src);
22
- if (lstat.isSymbolicLink()) {
23
- throw new Error(`Symlink not allowed in backup path: ${src}`);
24
- }
25
- fs.copyFileSync(src, dest);
26
- }
27
- // Migration targets
28
- const MIGRATION_TARGETS = [
29
- { value: 'config', label: 'Configuration', hint: 'Migrate configuration files' },
30
- { value: 'memory', label: 'Memory Data', hint: 'Migrate memory/database content' },
31
- { value: 'agents', label: 'Agent Configs', hint: 'Migrate agent configurations' },
32
- { value: 'hooks', label: 'Hooks', hint: 'Migrate hook definitions' },
33
- { value: 'workflows', label: 'Workflows', hint: 'Migrate workflow definitions' },
34
- { value: 'embeddings', label: 'Embeddings', hint: 'Migrate to ONNX with hyperbolic support' },
35
- { value: 'all', label: 'All', hint: 'Full migration' }
36
- ];
37
- // Status command
38
- const statusCommand = {
39
- name: 'status',
40
- description: 'Check migration status',
41
- action: async (ctx) => {
42
- const cwd = ctx.cwd || process.cwd();
43
- const components = [];
44
- // Check v2 config: monomind.config.json with version "2" or missing version
45
- const v2ConfigPath = path.join(cwd, 'monomind.config.json');
46
- const v1ConfigDir = path.join(cwd, '.monomind');
47
- let hasV2Config = false;
48
- let hasv1Config = false;
49
- try {
50
- if (fs.existsSync(v2ConfigPath) && fs.statSync(v2ConfigPath).size <= MAX_MIGRATE_FILE_BYTES) {
51
- const raw = fs.readFileSync(v2ConfigPath, 'utf-8');
52
- const parsed = JSON.parse(raw);
53
- if (parsed.version === '2' || parsed.version === 2 || !parsed.version) {
54
- hasV2Config = true;
55
- }
56
- }
57
- }
58
- catch { /* ignore parse errors */ }
59
- try {
60
- hasv1Config = fs.existsSync(v1ConfigDir) && fs.statSync(v1ConfigDir).isDirectory();
61
- }
62
- catch { /* ignore */ }
63
- if (hasV2Config && hasv1Config) {
64
- components.push({ component: 'Config', status: 'v2 + v1', migrationNeeded: 'no' });
65
- }
66
- else if (hasV2Config) {
67
- components.push({ component: 'Config', status: 'v2', migrationNeeded: 'yes' });
68
- }
69
- else if (hasv1Config) {
70
- components.push({ component: 'Config', status: 'v1', migrationNeeded: 'no' });
71
- }
72
- else {
73
- components.push({ component: 'Config', status: 'missing', migrationNeeded: 'no' });
74
- }
75
- // Check v2 memory: ./data/memory/*.json or memory.db
76
- const v2MemoryDir = path.join(cwd, 'data', 'memory');
77
- let hasV2MemoryJson = false;
78
- let hasV2MemoryDb = false;
79
- try {
80
- if (fs.existsSync(v2MemoryDir)) {
81
- const files = fs.readdirSync(v2MemoryDir);
82
- hasV2MemoryJson = files.some(f => f.endsWith('.json'));
83
- hasV2MemoryDb = files.includes('memory.db');
84
- }
85
- }
86
- catch { /* ignore */ }
87
- if (hasV2MemoryJson || hasV2MemoryDb) {
88
- components.push({ component: 'Memory', status: 'v2', migrationNeeded: 'yes' });
89
- }
90
- else {
91
- components.push({ component: 'Memory', status: 'missing', migrationNeeded: 'no' });
92
- }
93
- // Check v2 sessions: ./data/sessions/
94
- const v2SessionsDir = path.join(cwd, 'data', 'sessions');
95
- let hasV2Sessions = false;
96
- try {
97
- if (fs.existsSync(v2SessionsDir)) {
98
- const files = fs.readdirSync(v2SessionsDir);
99
- hasV2Sessions = files.length > 0;
100
- }
101
- }
102
- catch { /* ignore */ }
103
- if (hasV2Sessions) {
104
- components.push({ component: 'Sessions', status: 'v2', migrationNeeded: 'yes' });
105
- }
106
- else {
107
- components.push({ component: 'Sessions', status: 'missing', migrationNeeded: 'no' });
108
- }
109
- // Check migration state
110
- const migrationStatePath = path.join(cwd, '.monomind', 'migration-state.json');
111
- let migrationState = null;
112
- try {
113
- if (fs.existsSync(migrationStatePath) && fs.statSync(migrationStatePath).size <= MAX_MIGRATE_FILE_BYTES) {
114
- const raw = fs.readFileSync(migrationStatePath, 'utf-8');
115
- const parsed = JSON.parse(raw);
116
- migrationState = parsed.status || 'unknown';
117
- }
118
- }
119
- catch { /* ignore */ }
120
- if (migrationState) {
121
- components.push({ component: 'Migration State', status: migrationState, migrationNeeded: 'no' });
122
- }
123
- // Display results
124
- if (ctx.flags.format === 'json') {
125
- output.printJson({ components, migrationState });
126
- return { success: true, data: { components, migrationState } };
127
- }
128
- output.writeln();
129
- output.writeln(output.bold('Migration Status'));
130
- output.writeln();
131
- output.printTable({
132
- columns: [
133
- { key: 'component', header: 'Component', width: 20 },
134
- { key: 'status', header: 'Status', width: 15 },
135
- { key: 'migrationNeeded', header: 'Migration Needed', width: 20 }
136
- ],
137
- data: components.map(c => ({
138
- component: c.component,
139
- status: formatMigrationStatus(c.status),
140
- migrationNeeded: c.migrationNeeded === 'yes' ? output.warning('yes') : output.dim('no')
141
- })),
142
- border: false
143
- });
144
- const needsMigration = components.some(c => c.migrationNeeded === 'yes');
145
- output.writeln();
146
- if (needsMigration) {
147
- output.printInfo('V2 artifacts detected. Run "monomind migrate run" to migrate.');
148
- }
149
- else {
150
- output.printSuccess('No migration needed.');
151
- }
152
- return { success: true, data: { components, needsMigration } };
153
- }
154
- };
155
- // Run migration
156
- const runCommand = {
157
- name: 'run',
158
- description: 'Run migration',
159
- options: [
160
- {
161
- name: 'target',
162
- short: 't',
163
- description: 'Migration target',
164
- type: 'string',
165
- choices: MIGRATION_TARGETS.map(t => t.value)
166
- },
167
- {
168
- name: 'dry-run',
169
- description: 'Show what would be migrated without making changes',
170
- type: 'boolean',
171
- default: false
172
- },
173
- {
174
- name: 'backup',
175
- description: 'Create backup before migration',
176
- type: 'boolean',
177
- default: true
178
- },
179
- {
180
- name: 'force',
181
- short: 'f',
182
- description: 'Force migration (overwrite existing)',
183
- type: 'boolean',
184
- default: false
185
- }
186
- ],
187
- action: async (ctx) => {
188
- const cwd = ctx.cwd || process.cwd();
189
- const dryRun = ctx.flags['dry-run'] === true;
190
- const skipBackup = ctx.flags.backup === false;
191
- const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
192
- const v1Dir = path.join(cwd, '.monomind');
193
- const backupDir = path.join(v1Dir, 'backup', `v2-${timestamp}`);
194
- const migrationStatePath = path.join(v1Dir, 'migration-state.json');
195
- const target = ctx.flags.target;
196
- const migrated = [];
197
- const skipped = [];
198
- output.writeln();
199
- output.writeln(output.bold('V2 to v1 Migration'));
200
- if (dryRun) {
201
- output.printWarning('Dry run mode — no changes will be made.');
202
- }
203
- output.writeln();
204
- // Ensure .monomind directory exists
205
- if (!dryRun) {
206
- fs.mkdirSync(v1Dir, { recursive: true });
207
- }
208
- // --- Backup ---
209
- if (!skipBackup && !dryRun) {
210
- output.writeln(output.dim('Creating backup...'));
211
- fs.mkdirSync(backupDir, { recursive: true });
212
- }
213
- // --- Config migration ---
214
- if (!target || target === 'config') {
215
- const v2ConfigPath = path.join(cwd, 'monomind.config.json');
216
- try {
217
- if (fs.existsSync(v2ConfigPath) && fs.statSync(v2ConfigPath).size <= MAX_MIGRATE_FILE_BYTES) {
218
- const raw = fs.readFileSync(v2ConfigPath, 'utf-8');
219
- const parsed = JSON.parse(raw);
220
- if (parsed.version === '2' || parsed.version === 2 || !parsed.version) {
221
- if (dryRun) {
222
- output.printInfo(`Would migrate config: ${v2ConfigPath}`);
223
- }
224
- else {
225
- // Backup
226
- if (!skipBackup) {
227
- fs.copyFileSync(v2ConfigPath, path.join(backupDir, 'monomind.config.json'));
228
- }
229
- // Transform to v1 format
230
- const v1Config = { ...sanitizeJsonObject(parsed), version: '3' };
231
- // Rename swarm.mode -> swarm.topology if present
232
- if (v1Config.swarm && typeof v1Config.swarm === 'object') {
233
- const swarm = v1Config.swarm;
234
- if ('mode' in swarm && !('topology' in swarm)) {
235
- swarm.topology = swarm.mode;
236
- delete swarm.mode;
237
- }
238
- }
239
- // Rename memory.type -> memory.backend if present
240
- if (v1Config.memory && typeof v1Config.memory === 'object') {
241
- const mem = v1Config.memory;
242
- if ('type' in mem && !('backend' in mem)) {
243
- mem.backend = mem.type;
244
- delete mem.type;
245
- }
246
- }
247
- const v1ConfigPath = path.join(v1Dir, 'config.json');
248
- const tmpV1 = `${v1ConfigPath}.${process.pid}.${Date.now()}.tmp`;
249
- fs.writeFileSync(tmpV1, JSON.stringify(v1Config, null, 2), { flag: 'wx' });
250
- fs.renameSync(tmpV1, v1ConfigPath);
251
- output.printSuccess(`Config migrated to ${v1ConfigPath}`);
252
- }
253
- migrated.push('config');
254
- }
255
- else {
256
- output.printInfo('Config already at v1 — skipping.');
257
- skipped.push('config');
258
- }
259
- }
260
- else {
261
- output.writeln(output.dim('No v2 config found — skipping config migration.'));
262
- skipped.push('config');
263
- }
264
- }
265
- catch (err) {
266
- output.printError('Config migration failed', String(err));
267
- skipped.push('config');
268
- }
269
- }
270
- // --- Memory migration ---
271
- if (!target || target === 'memory') {
272
- const v2MemoryDir = path.join(cwd, 'data', 'memory');
273
- try {
274
- if (fs.existsSync(v2MemoryDir)) {
275
- const files = fs.readdirSync(v2MemoryDir);
276
- const jsonFiles = files.filter(f => f.endsWith('.json'));
277
- const hasDb = files.includes('memory.db');
278
- if (jsonFiles.length > 0 || hasDb) {
279
- if (dryRun) {
280
- output.printInfo(`Would migrate memory: ${jsonFiles.length} JSON files, ${hasDb ? '1 DB' : 'no DB'}`);
281
- }
282
- else {
283
- // Backup memory files
284
- if (!skipBackup) {
285
- const memBackup = path.join(backupDir, 'data', 'memory');
286
- fs.mkdirSync(memBackup, { recursive: true });
287
- for (const f of files) {
288
- const src = path.join(v2MemoryDir, f);
289
- if (fs.statSync(src).isFile()) {
290
- fs.copyFileSync(src, path.join(memBackup, f));
291
- }
292
- }
293
- }
294
- output.printSuccess(`Memory files backed up (${jsonFiles.length} JSON, ${hasDb ? '1 DB' : '0 DB'}).`);
295
- output.printInfo('Run "monomind memory init --force" to import v2 memory into v1 LanceDB.');
296
- }
297
- migrated.push('memory');
298
- }
299
- else {
300
- output.writeln(output.dim('No v2 memory files found — skipping.'));
301
- skipped.push('memory');
302
- }
303
- }
304
- else {
305
- output.writeln(output.dim('No v2 memory directory found — skipping.'));
306
- skipped.push('memory');
307
- }
308
- }
309
- catch (err) {
310
- output.printError('Memory migration failed', String(err));
311
- skipped.push('memory');
312
- }
313
- }
314
- // --- Session migration ---
315
- if (!target || target === 'sessions') {
316
- const v2SessionsDir = path.join(cwd, 'data', 'sessions');
317
- try {
318
- if (fs.existsSync(v2SessionsDir)) {
319
- const files = fs.readdirSync(v2SessionsDir);
320
- if (files.length > 0) {
321
- if (dryRun) {
322
- output.printInfo(`Would migrate sessions: ${files.length} files from ${v2SessionsDir}`);
323
- }
324
- else {
325
- const v1SessionsDir = path.join(v1Dir, 'sessions');
326
- fs.mkdirSync(v1SessionsDir, { recursive: true });
327
- // Backup
328
- if (!skipBackup) {
329
- const sessBackup = path.join(backupDir, 'data', 'sessions');
330
- fs.mkdirSync(sessBackup, { recursive: true });
331
- for (const f of files) {
332
- const src = path.join(v2SessionsDir, f);
333
- if (fs.statSync(src).isFile()) {
334
- fs.copyFileSync(src, path.join(sessBackup, f));
335
- }
336
- }
337
- }
338
- // Copy to v1 location
339
- for (const f of files) {
340
- const src = path.join(v2SessionsDir, f);
341
- if (fs.statSync(src).isFile()) {
342
- fs.copyFileSync(src, path.join(v1SessionsDir, f));
343
- }
344
- }
345
- output.printSuccess(`Sessions migrated: ${files.length} files to ${v1SessionsDir}`);
346
- }
347
- migrated.push('sessions');
348
- }
349
- else {
350
- output.writeln(output.dim('No v2 session files found — skipping.'));
351
- skipped.push('sessions');
352
- }
353
- }
354
- else {
355
- output.writeln(output.dim('No v2 sessions directory found — skipping.'));
356
- skipped.push('sessions');
357
- }
358
- }
359
- catch (err) {
360
- output.printError('Session migration failed', String(err));
361
- skipped.push('sessions');
362
- }
363
- }
364
- // --- Save migration state ---
365
- if (!dryRun && migrated.length > 0) {
366
- const state = {
367
- status: 'completed',
368
- timestamp,
369
- backupPath: skipBackup ? null : backupDir,
370
- migrated,
371
- skipped
372
- };
373
- const tmpMig = `${migrationStatePath}.${process.pid}.${Date.now()}.tmp`;
374
- fs.writeFileSync(tmpMig, JSON.stringify(state, null, 2), { flag: 'wx' });
375
- fs.renameSync(tmpMig, migrationStatePath);
376
- output.writeln();
377
- output.printSuccess(`Migration state saved to ${migrationStatePath}`);
378
- }
379
- // Summary
380
- output.writeln();
381
- if (dryRun) {
382
- output.printInfo(`Dry run complete. ${migrated.length} component(s) would be migrated.`);
383
- }
384
- else if (migrated.length > 0) {
385
- output.printSuccess(`Migration complete. ${migrated.length} component(s) migrated: ${migrated.join(', ')}`);
386
- output.printInfo('Run "monomind migrate verify" to validate the migration.');
387
- }
388
- else {
389
- output.printInfo('Nothing to migrate.');
390
- }
391
- return { success: true, data: { migrated, skipped, dryRun } };
392
- }
393
- };
394
- // Verify migration
395
- const verifyCommand = {
396
- name: 'verify',
397
- description: 'Verify migration integrity',
398
- options: [
399
- {
400
- name: 'fix',
401
- description: 'Automatically fix issues',
402
- type: 'boolean',
403
- default: false
404
- }
405
- ],
406
- action: async (ctx) => {
407
- const cwd = ctx.cwd || process.cwd();
408
- const v1Dir = path.join(cwd, '.monomind');
409
- const migrationStatePath = path.join(v1Dir, 'migration-state.json');
410
- const checks = [];
411
- let allPassed = true;
412
- output.writeln();
413
- output.writeln(output.bold('Migration Verification'));
414
- output.writeln();
415
- // Check 1: Migration state file exists
416
- let migrationState = null;
417
- try {
418
- if (fs.existsSync(migrationStatePath) && fs.statSync(migrationStatePath).size <= MAX_MIGRATE_FILE_BYTES) {
419
- const raw = fs.readFileSync(migrationStatePath, 'utf-8');
420
- migrationState = JSON.parse(raw);
421
- checks.push({ check: 'Migration state file', result: 'passed' });
422
- }
423
- else {
424
- checks.push({ check: 'Migration state file', result: 'failed' });
425
- allPassed = false;
426
- }
427
- }
428
- catch {
429
- checks.push({ check: 'Migration state file', result: 'failed' });
430
- allPassed = false;
431
- }
432
- // Check 2: v1 config exists and is valid JSON
433
- const v1ConfigPath = path.join(v1Dir, 'config.json');
434
- try {
435
- if (fs.existsSync(v1ConfigPath) && fs.statSync(v1ConfigPath).size <= MAX_MIGRATE_FILE_BYTES) {
436
- const raw = fs.readFileSync(v1ConfigPath, 'utf-8');
437
- JSON.parse(raw); // validate JSON
438
- checks.push({ check: 'v1 config (valid JSON)', result: 'passed' });
439
- }
440
- else {
441
- // Config might not have been migrated if there was no v2 config
442
- const wasMigrated = migrationState &&
443
- Array.isArray(migrationState.migrated) &&
444
- migrationState.migrated.includes('config');
445
- if (wasMigrated) {
446
- checks.push({ check: 'v1 config (valid JSON)', result: 'failed' });
447
- allPassed = false;
448
- }
449
- else {
450
- checks.push({ check: 'v1 config (valid JSON)', result: 'skipped' });
451
- }
452
- }
453
- }
454
- catch {
455
- checks.push({ check: 'v1 config (valid JSON)', result: 'failed' });
456
- allPassed = false;
457
- }
458
- // Check 3: Backup exists
459
- if (migrationState && migrationState.backupPath) {
460
- const backupPath = migrationState.backupPath;
461
- try {
462
- if (fs.existsSync(backupPath) && fs.statSync(backupPath).isDirectory()) {
463
- checks.push({ check: 'Backup directory', result: 'passed' });
464
- }
465
- else {
466
- checks.push({ check: 'Backup directory', result: 'failed' });
467
- allPassed = false;
468
- }
469
- }
470
- catch {
471
- checks.push({ check: 'Backup directory', result: 'failed' });
472
- allPassed = false;
473
- }
474
- }
475
- else if (migrationState && migrationState.backupPath === null) {
476
- checks.push({ check: 'Backup directory', result: 'skipped (backup was disabled)' });
477
- }
478
- else {
479
- checks.push({ check: 'Backup directory', result: 'failed' });
480
- allPassed = false;
481
- }
482
- // Check 4: v1 sessions directory if sessions were migrated
483
- if (migrationState &&
484
- Array.isArray(migrationState.migrated) &&
485
- migrationState.migrated.includes('sessions')) {
486
- const v1Sessions = path.join(v1Dir, 'sessions');
487
- try {
488
- if (fs.existsSync(v1Sessions) && fs.readdirSync(v1Sessions).length > 0) {
489
- checks.push({ check: 'v1 sessions directory', result: 'passed' });
490
- }
491
- else {
492
- checks.push({ check: 'v1 sessions directory', result: 'failed' });
493
- allPassed = false;
494
- }
495
- }
496
- catch {
497
- checks.push({ check: 'v1 sessions directory', result: 'failed' });
498
- allPassed = false;
499
- }
500
- }
501
- // Display
502
- if (ctx.flags.format === 'json') {
503
- output.printJson({ checks, allPassed });
504
- return { success: allPassed, data: { checks, allPassed } };
505
- }
506
- output.printTable({
507
- columns: [
508
- { key: 'check', header: 'Check', width: 30 },
509
- { key: 'result', header: 'Result', width: 35 }
510
- ],
511
- data: checks.map(c => ({
512
- check: c.check,
513
- result: formatMigrationStatus(c.result)
514
- })),
515
- border: false
516
- });
517
- output.writeln();
518
- if (allPassed) {
519
- output.printSuccess('All verification checks passed.');
520
- }
521
- else {
522
- output.printError('Some verification checks failed.');
523
- output.printInfo('Run "monomind migrate run" to re-run the migration, or "migrate rollback" to restore from backup.');
524
- }
525
- return { success: allPassed, data: { checks, allPassed }, exitCode: allPassed ? 0 : 1 };
526
- }
527
- };
528
- // Rollback migration
529
- const rollbackCommand = {
530
- name: 'rollback',
531
- description: 'Rollback to previous version',
532
- options: [
533
- {
534
- name: 'backup-id',
535
- description: 'Backup ID to restore',
536
- type: 'string'
537
- },
538
- {
539
- name: 'force',
540
- short: 'f',
541
- description: 'Skip confirmation',
542
- type: 'boolean',
543
- default: false
544
- }
545
- ],
546
- action: async (ctx) => {
547
- const cwd = ctx.cwd || process.cwd();
548
- const v1Dir = path.join(cwd, '.monomind');
549
- const migrationStatePath = path.join(v1Dir, 'migration-state.json');
550
- output.writeln();
551
- output.writeln(output.bold('Migration Rollback'));
552
- output.writeln();
553
- // Read migration state
554
- let migrationState;
555
- try {
556
- if (!fs.existsSync(migrationStatePath)) {
557
- output.printError('No migration state found.', 'Run "migrate run" first before attempting rollback.');
558
- return { success: false, exitCode: 1 };
559
- }
560
- if (fs.statSync(migrationStatePath).size > MAX_MIGRATE_FILE_BYTES) {
561
- output.printError('Migration state file too large.');
562
- return { success: false, exitCode: 1 };
563
- }
564
- const raw = fs.readFileSync(migrationStatePath, 'utf-8');
565
- migrationState = sanitizeJsonObject(JSON.parse(raw));
566
- }
567
- catch (err) {
568
- output.printError('Failed to read migration state', String(err));
569
- return { success: false, exitCode: 1 };
570
- }
571
- const backupId = ctx.flags['backup-id'];
572
- if (backupId && (backupId.includes('/') || backupId.includes('\\') || backupId.includes('..'))) {
573
- output.printError('Invalid backup ID: must not contain path separators.');
574
- return { success: false, exitCode: 1 };
575
- }
576
- const rawBackupPath = backupId
577
- ? path.join(cwd, '.monomind', 'backups', backupId)
578
- : migrationState.backupPath;
579
- if (!rawBackupPath) {
580
- output.printError('No backup path found. Provide --backup-id or run migration first.');
581
- return { success: false, exitCode: 1 };
582
- }
583
- const resolvedBackup = path.resolve(rawBackupPath);
584
- const allowedRoot = path.resolve(path.join(cwd, '.monomind'));
585
- if (!resolvedBackup.startsWith(allowedRoot + path.sep) && resolvedBackup !== allowedRoot) {
586
- output.printError('Backup path must be within the .monomind directory.');
587
- return { success: false, exitCode: 1 };
588
- }
589
- const backupPath = resolvedBackup;
590
- if (!fs.existsSync(backupPath)) {
591
- output.printError('Backup directory not found.', `Expected: ${backupPath}`);
592
- return { success: false, exitCode: 1 };
593
- }
594
- const restored = [];
595
- try {
596
- // Restore config
597
- const backupConfig = path.join(backupPath, 'monomind.config.json');
598
- if (fs.existsSync(backupConfig)) {
599
- const destConfig = path.join(cwd, 'monomind.config.json');
600
- safeFileCopy(backupConfig, destConfig);
601
- // Remove v1 config
602
- const v1Config = path.join(v1Dir, 'config.json');
603
- if (fs.existsSync(v1Config)) {
604
- fs.unlinkSync(v1Config);
605
- }
606
- output.printSuccess('Restored: config');
607
- restored.push('config');
608
- }
609
- // Restore memory
610
- const backupMemory = path.join(backupPath, 'data', 'memory');
611
- if (fs.existsSync(backupMemory)) {
612
- const destMemory = path.join(cwd, 'data', 'memory');
613
- fs.mkdirSync(destMemory, { recursive: true });
614
- const files = fs.readdirSync(backupMemory);
615
- for (const f of files) {
616
- safeFileCopy(path.join(backupMemory, f), path.join(destMemory, f));
617
- }
618
- output.printSuccess(`Restored: memory (${files.length} files)`);
619
- restored.push('memory');
620
- }
621
- // Restore sessions
622
- const backupSessions = path.join(backupPath, 'data', 'sessions');
623
- if (fs.existsSync(backupSessions)) {
624
- const destSessions = path.join(cwd, 'data', 'sessions');
625
- fs.mkdirSync(destSessions, { recursive: true });
626
- const files = fs.readdirSync(backupSessions);
627
- for (const f of files) {
628
- safeFileCopy(path.join(backupSessions, f), path.join(destSessions, f));
629
- }
630
- // Remove v1 sessions
631
- const v1Sessions = path.join(v1Dir, 'sessions');
632
- if (fs.existsSync(v1Sessions)) {
633
- const v1Files = fs.readdirSync(v1Sessions);
634
- for (const f of v1Files) {
635
- fs.unlinkSync(path.join(v1Sessions, f));
636
- }
637
- fs.rmdirSync(v1Sessions);
638
- }
639
- output.printSuccess(`Restored: sessions (${files.length} files)`);
640
- restored.push('sessions');
641
- }
642
- // Delete migration state
643
- fs.unlinkSync(migrationStatePath);
644
- output.writeln();
645
- output.printSuccess(`Rollback complete. Restored: ${restored.join(', ') || 'nothing to restore'}`);
646
- return { success: true, data: { restored } };
647
- }
648
- catch (err) {
649
- output.printError('Rollback failed', String(err));
650
- return { success: false, exitCode: 1 };
651
- }
652
- }
653
- };
654
- // Breaking changes info
655
- const breakingCommand = {
656
- name: 'breaking',
657
- description: 'Show v1 breaking changes',
658
- action: async (ctx) => {
659
- const changes = [
660
- {
661
- category: 'Configuration',
662
- changes: [
663
- { change: 'Config file renamed', from: 'monomind.json', to: 'monomind.config.json' },
664
- { change: 'Swarm config restructured', from: 'swarm.mode', to: 'swarm.topology' },
665
- { change: 'Provider config format', from: 'provider: "anthropic"', to: 'providers: [...]' }
666
- ]
667
- },
668
- {
669
- category: 'Memory',
670
- changes: [
671
- { change: 'Backend option changed', from: 'memory: { type }', to: 'memory: { backend }' },
672
- { change: 'HNSW enabled by default', from: 'Manual opt-in', to: 'Auto-enabled' },
673
- { change: 'Storage path changed', from: '.monomind/memory', to: 'data/memory' }
674
- ]
675
- },
676
- {
677
- category: 'CLI',
678
- changes: [
679
- { change: 'Agent command renamed', from: 'spawn <type>', to: 'agent spawn -t <type>' },
680
- { change: 'Memory command added', from: 'N/A', to: 'memory <subcommand>' },
681
- { change: 'Hook command enhanced', from: 'hook <type>', to: 'hooks <subcommand>' }
682
- ]
683
- },
684
- {
685
- category: 'API',
686
- changes: [
687
- { change: 'Removed Deno support', from: 'Deno + Node.js', to: 'Node.js 20+ only' },
688
- { change: 'Event system changed', from: 'EventEmitter', to: 'Event sourcing' },
689
- { change: 'Coordination unified', from: 'Multiple coordinators', to: 'SwarmCoordinator' }
690
- ]
691
- },
692
- {
693
- category: 'Embeddings',
694
- changes: [
695
- { change: 'Provider changed', from: 'OpenAI API / TF.js', to: 'ONNX Runtime (local)' },
696
- { change: 'Geometry support', from: 'Euclidean only', to: 'Hyperbolic (Poincaré ball)' },
697
- { change: 'Cache system', from: 'Memory-only', to: 'sql.js persistent cache' }
698
- ]
699
- }
700
- ];
701
- if (ctx.flags.format === 'json') {
702
- output.printJson(changes);
703
- return { success: true, data: changes };
704
- }
705
- output.writeln();
706
- output.writeln(output.bold('v1 Breaking Changes'));
707
- output.writeln();
708
- for (const category of changes) {
709
- output.writeln(output.highlight(category.category));
710
- output.printTable({
711
- columns: [
712
- { key: 'change', header: 'Change', width: 25 },
713
- { key: 'from', header: 'V2', width: 25 },
714
- { key: 'to', header: 'v1', width: 25 }
715
- ],
716
- data: category.changes,
717
- border: false
718
- });
719
- output.writeln();
720
- }
721
- output.printInfo('Run "monomind migrate run" to automatically handle these changes');
722
- return { success: true, data: changes };
723
- }
724
- };
725
- // Main migrate command
726
- export const migrateCommand = {
727
- name: 'migrate',
728
- description: 'V2 to v1 migration tools',
729
- subcommands: [statusCommand, runCommand, verifyCommand, rollbackCommand, breakingCommand],
730
- options: [],
731
- examples: [
732
- { command: 'monomind migrate status', description: 'Check migration status' },
733
- { command: 'monomind migrate run --dry-run', description: 'Preview migration' },
734
- { command: 'monomind migrate run -t all', description: 'Run full migration' }
735
- ],
736
- action: async (ctx) => {
737
- output.writeln();
738
- output.writeln(output.bold('V2 to v1 Migration Tools'));
739
- output.writeln();
740
- output.writeln('Usage: monomind migrate <subcommand> [options]');
741
- output.writeln();
742
- output.writeln('Subcommands:');
743
- output.printList([
744
- `${output.highlight('status')} - Check migration status`,
745
- `${output.highlight('run')} - Run migration`,
746
- `${output.highlight('verify')} - Verify migration integrity`,
747
- `${output.highlight('rollback')} - Rollback to previous version`,
748
- `${output.highlight('breaking')} - Show breaking changes`
749
- ]);
750
- return { success: true };
751
- }
752
- };
753
- // Helper functions
754
- function formatMigrationStatus(status) {
755
- if (status === 'migrated' || status === 'passed' || status === 'completed') {
756
- return output.success(status);
757
- }
758
- if (status === 'pending' || status === 'partial') {
759
- return output.warning(status);
760
- }
761
- if (status === 'failed') {
762
- return output.error(status);
763
- }
764
- if (status === 'not-required' || status.startsWith('skipped') || status === 'v1' || status === 'missing') {
765
- return output.dim(status);
766
- }
767
- if (status === 'v2') {
768
- return output.warning(status);
769
- }
770
- if (status === 'v2 + v1') {
771
- return output.success(status);
772
- }
773
- return status;
774
- }
775
- function getMigrationSteps(target) {
776
- const allSteps = [
777
- { name: 'Configuration Files', description: 'Migrate config schema to v1 format', source: './monomind.json', dest: './monomind.config.json' },
778
- { name: 'Memory Backend', description: 'Upgrade to hybrid backend with LanceDB', source: './.monomind/memory', dest: './data/memory' },
779
- { name: 'Agent Definitions', description: 'Convert agent configs to v1 format', source: './.monomind/agents', dest: './packages/agents' },
780
- { name: 'Hook Registry', description: 'Migrate hooks to v1 hook system', source: './src/hooks', dest: './packages/hooks' },
781
- { name: 'Workflow Definitions', description: 'Convert workflows to event-sourced format', source: './.monomind/workflows', dest: './data/workflows' },
782
- { name: 'Embeddings System', description: 'Migrate to ONNX with hyperbolic (Poincaré ball)', source: 'OpenAI/TF.js embeddings', dest: '.monomind/embeddings.json' }
783
- ];
784
- if (target === 'all')
785
- return allSteps;
786
- return allSteps.filter(s => s.name.toLowerCase().includes(target.toLowerCase()));
787
- }
788
- export default migrateCommand;
789
- //# sourceMappingURL=migrate.js.map