@claude-flow/cli 3.5.43 → 3.5.45

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 (56) hide show
  1. package/README.md +199 -196
  2. package/dist/src/autopilot-state.d.ts +77 -0
  3. package/dist/src/autopilot-state.d.ts.map +1 -0
  4. package/dist/src/autopilot-state.js +279 -0
  5. package/dist/src/autopilot-state.js.map +1 -0
  6. package/dist/src/commands/autopilot.d.ts +15 -0
  7. package/dist/src/commands/autopilot.d.ts.map +1 -0
  8. package/dist/src/commands/autopilot.js +362 -0
  9. package/dist/src/commands/autopilot.js.map +1 -0
  10. package/dist/src/commands/claims.d.ts.map +1 -1
  11. package/dist/src/commands/claims.js +352 -34
  12. package/dist/src/commands/claims.js.map +1 -1
  13. package/dist/src/commands/config.d.ts.map +1 -1
  14. package/dist/src/commands/config.js +66 -34
  15. package/dist/src/commands/config.js.map +1 -1
  16. package/dist/src/commands/deployment.d.ts.map +1 -1
  17. package/dist/src/commands/deployment.js +538 -46
  18. package/dist/src/commands/deployment.js.map +1 -1
  19. package/dist/src/commands/index.d.ts +2 -0
  20. package/dist/src/commands/index.d.ts.map +1 -1
  21. package/dist/src/commands/index.js +7 -0
  22. package/dist/src/commands/index.js.map +1 -1
  23. package/dist/src/commands/migrate.d.ts.map +1 -1
  24. package/dist/src/commands/migrate.js +531 -39
  25. package/dist/src/commands/migrate.js.map +1 -1
  26. package/dist/src/commands/providers.d.ts.map +1 -1
  27. package/dist/src/commands/providers.js +163 -29
  28. package/dist/src/commands/providers.js.map +1 -1
  29. package/dist/src/init/executor.d.ts.map +1 -1
  30. package/dist/src/init/executor.js +2 -3
  31. package/dist/src/init/executor.js.map +1 -1
  32. package/dist/src/mcp-client.d.ts.map +1 -1
  33. package/dist/src/mcp-client.js +6 -0
  34. package/dist/src/mcp-client.js.map +1 -1
  35. package/dist/src/mcp-server.d.ts +3 -1
  36. package/dist/src/mcp-server.d.ts.map +1 -1
  37. package/dist/src/mcp-server.js +31 -4
  38. package/dist/src/mcp-server.js.map +1 -1
  39. package/dist/src/mcp-tools/autopilot-tools.d.ts +12 -0
  40. package/dist/src/mcp-tools/autopilot-tools.d.ts.map +1 -0
  41. package/dist/src/mcp-tools/autopilot-tools.js +227 -0
  42. package/dist/src/mcp-tools/autopilot-tools.js.map +1 -0
  43. package/dist/src/mcp-tools/guidance-tools.d.ts +15 -0
  44. package/dist/src/mcp-tools/guidance-tools.d.ts.map +1 -0
  45. package/dist/src/mcp-tools/guidance-tools.js +617 -0
  46. package/dist/src/mcp-tools/guidance-tools.js.map +1 -0
  47. package/dist/src/mcp-tools/index.d.ts +2 -0
  48. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  49. package/dist/src/mcp-tools/index.js +2 -0
  50. package/dist/src/mcp-tools/index.js.map +1 -1
  51. package/dist/src/services/config-file-manager.d.ts +37 -0
  52. package/dist/src/services/config-file-manager.d.ts.map +1 -0
  53. package/dist/src/services/config-file-manager.js +224 -0
  54. package/dist/src/services/config-file-manager.js.map +1 -0
  55. package/dist/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +3 -1
@@ -3,6 +3,8 @@
3
3
  * Migration tools for V2 to V3 transition
4
4
  */
5
5
  import { output } from '../output.js';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
6
8
  // Migration targets
7
9
  const MIGRATION_TARGETS = [
8
10
  { value: 'config', label: 'Configuration', hint: 'Migrate configuration files' },
@@ -17,13 +19,118 @@ const MIGRATION_TARGETS = [
17
19
  const statusCommand = {
18
20
  name: 'status',
19
21
  description: 'Check migration status',
20
- action: async (_ctx) => {
21
- // #1425: This command is not yet implemented — was returning hardcoded fake migration status
22
+ action: async (ctx) => {
23
+ const cwd = ctx.cwd || process.cwd();
24
+ const components = [];
25
+ // Check v2 config: claude-flow.config.json with version "2" or missing version
26
+ const v2ConfigPath = path.join(cwd, 'claude-flow.config.json');
27
+ const v3ConfigDir = path.join(cwd, '.claude-flow');
28
+ let hasV2Config = false;
29
+ let hasV3Config = false;
30
+ try {
31
+ if (fs.existsSync(v2ConfigPath)) {
32
+ const raw = fs.readFileSync(v2ConfigPath, 'utf-8');
33
+ const parsed = JSON.parse(raw);
34
+ if (parsed.version === '2' || parsed.version === 2 || !parsed.version) {
35
+ hasV2Config = true;
36
+ }
37
+ }
38
+ }
39
+ catch { /* ignore parse errors */ }
40
+ try {
41
+ hasV3Config = fs.existsSync(v3ConfigDir) && fs.statSync(v3ConfigDir).isDirectory();
42
+ }
43
+ catch { /* ignore */ }
44
+ if (hasV2Config && hasV3Config) {
45
+ components.push({ component: 'Config', status: 'v2 + v3', migrationNeeded: 'no' });
46
+ }
47
+ else if (hasV2Config) {
48
+ components.push({ component: 'Config', status: 'v2', migrationNeeded: 'yes' });
49
+ }
50
+ else if (hasV3Config) {
51
+ components.push({ component: 'Config', status: 'v3', migrationNeeded: 'no' });
52
+ }
53
+ else {
54
+ components.push({ component: 'Config', status: 'missing', migrationNeeded: 'no' });
55
+ }
56
+ // Check v2 memory: ./data/memory/*.json or memory.db
57
+ const v2MemoryDir = path.join(cwd, 'data', 'memory');
58
+ let hasV2MemoryJson = false;
59
+ let hasV2MemoryDb = false;
60
+ try {
61
+ if (fs.existsSync(v2MemoryDir)) {
62
+ const files = fs.readdirSync(v2MemoryDir);
63
+ hasV2MemoryJson = files.some(f => f.endsWith('.json'));
64
+ hasV2MemoryDb = files.includes('memory.db');
65
+ }
66
+ }
67
+ catch { /* ignore */ }
68
+ if (hasV2MemoryJson || hasV2MemoryDb) {
69
+ components.push({ component: 'Memory', status: 'v2', migrationNeeded: 'yes' });
70
+ }
71
+ else {
72
+ components.push({ component: 'Memory', status: 'missing', migrationNeeded: 'no' });
73
+ }
74
+ // Check v2 sessions: ./data/sessions/
75
+ const v2SessionsDir = path.join(cwd, 'data', 'sessions');
76
+ let hasV2Sessions = false;
77
+ try {
78
+ if (fs.existsSync(v2SessionsDir)) {
79
+ const files = fs.readdirSync(v2SessionsDir);
80
+ hasV2Sessions = files.length > 0;
81
+ }
82
+ }
83
+ catch { /* ignore */ }
84
+ if (hasV2Sessions) {
85
+ components.push({ component: 'Sessions', status: 'v2', migrationNeeded: 'yes' });
86
+ }
87
+ else {
88
+ components.push({ component: 'Sessions', status: 'missing', migrationNeeded: 'no' });
89
+ }
90
+ // Check migration state
91
+ const migrationStatePath = path.join(cwd, '.claude-flow', 'migration-state.json');
92
+ let migrationState = null;
93
+ try {
94
+ if (fs.existsSync(migrationStatePath)) {
95
+ const raw = fs.readFileSync(migrationStatePath, 'utf-8');
96
+ const parsed = JSON.parse(raw);
97
+ migrationState = parsed.status || 'unknown';
98
+ }
99
+ }
100
+ catch { /* ignore */ }
101
+ if (migrationState) {
102
+ components.push({ component: 'Migration State', status: migrationState, migrationNeeded: 'no' });
103
+ }
104
+ // Display results
105
+ if (ctx.flags.format === 'json') {
106
+ output.printJson({ components, migrationState });
107
+ return { success: true, data: { components, migrationState } };
108
+ }
109
+ output.writeln();
110
+ output.writeln(output.bold('Migration Status'));
111
+ output.writeln();
112
+ output.printTable({
113
+ columns: [
114
+ { key: 'component', header: 'Component', width: 20 },
115
+ { key: 'status', header: 'Status', width: 15 },
116
+ { key: 'migrationNeeded', header: 'Migration Needed', width: 20 }
117
+ ],
118
+ data: components.map(c => ({
119
+ component: c.component,
120
+ status: formatMigrationStatus(c.status),
121
+ migrationNeeded: c.migrationNeeded === 'yes' ? output.warning('yes') : output.dim('no')
122
+ })),
123
+ border: false
124
+ });
125
+ const needsMigration = components.some(c => c.migrationNeeded === 'yes');
22
126
  output.writeln();
23
- output.printError('migrate status is not yet implemented');
24
- output.writeln(output.dim('Migration status detection requires filesystem analysis not yet built.'));
25
- output.writeln(output.dim('Track progress: https://github.com/ruvnet/claude-flow/issues/1425'));
26
- return { success: false, exitCode: 1 };
127
+ if (needsMigration) {
128
+ output.printInfo('V2 artifacts detected. Run "claude-flow migrate run" to migrate.');
129
+ }
130
+ else {
131
+ output.printSuccess('No migration needed.');
132
+ }
133
+ return { success: true, data: { components, needsMigration } };
27
134
  }
28
135
  };
29
136
  // Run migration
@@ -58,14 +165,200 @@ const runCommand = {
58
165
  default: false
59
166
  }
60
167
  ],
61
- action: async (_ctx) => {
62
- // #1425: This command is not yet implemented — was faking migration execution
63
- output.writeln();
64
- output.printError('migrate run is not yet implemented');
65
- output.writeln(output.dim('V2→V3 migration requires file transformation logic not yet built.'));
66
- output.writeln(output.dim('See "migrate breaking" for a list of V3 breaking changes.'));
67
- output.writeln(output.dim('Track progress: https://github.com/ruvnet/claude-flow/issues/1425'));
68
- return { success: false, exitCode: 1 };
168
+ action: async (ctx) => {
169
+ const cwd = ctx.cwd || process.cwd();
170
+ const dryRun = ctx.flags['dry-run'] === true;
171
+ const skipBackup = ctx.flags.backup === false;
172
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
173
+ const v3Dir = path.join(cwd, '.claude-flow');
174
+ const backupDir = path.join(v3Dir, 'backup', `v2-${timestamp}`);
175
+ const migrationStatePath = path.join(v3Dir, 'migration-state.json');
176
+ const migrated = [];
177
+ const skipped = [];
178
+ output.writeln();
179
+ output.writeln(output.bold('V2 to V3 Migration'));
180
+ if (dryRun) {
181
+ output.printWarning('Dry run mode — no changes will be made.');
182
+ }
183
+ output.writeln();
184
+ // Ensure .claude-flow directory exists
185
+ if (!dryRun) {
186
+ fs.mkdirSync(v3Dir, { recursive: true });
187
+ }
188
+ // --- Backup ---
189
+ if (!skipBackup && !dryRun) {
190
+ output.writeln(output.dim('Creating backup...'));
191
+ fs.mkdirSync(backupDir, { recursive: true });
192
+ }
193
+ // --- Config migration ---
194
+ const v2ConfigPath = path.join(cwd, 'claude-flow.config.json');
195
+ try {
196
+ if (fs.existsSync(v2ConfigPath)) {
197
+ const raw = fs.readFileSync(v2ConfigPath, 'utf-8');
198
+ const parsed = JSON.parse(raw);
199
+ if (parsed.version === '2' || parsed.version === 2 || !parsed.version) {
200
+ if (dryRun) {
201
+ output.printInfo(`Would migrate config: ${v2ConfigPath}`);
202
+ }
203
+ else {
204
+ // Backup
205
+ if (!skipBackup) {
206
+ fs.copyFileSync(v2ConfigPath, path.join(backupDir, 'claude-flow.config.json'));
207
+ }
208
+ // Transform to v3 format
209
+ const v3Config = { ...parsed, version: '3' };
210
+ // Rename swarm.mode -> swarm.topology if present
211
+ if (v3Config.swarm && typeof v3Config.swarm === 'object') {
212
+ const swarm = v3Config.swarm;
213
+ if ('mode' in swarm && !('topology' in swarm)) {
214
+ swarm.topology = swarm.mode;
215
+ delete swarm.mode;
216
+ }
217
+ }
218
+ // Rename memory.type -> memory.backend if present
219
+ if (v3Config.memory && typeof v3Config.memory === 'object') {
220
+ const mem = v3Config.memory;
221
+ if ('type' in mem && !('backend' in mem)) {
222
+ mem.backend = mem.type;
223
+ delete mem.type;
224
+ }
225
+ }
226
+ const v3ConfigPath = path.join(v3Dir, 'config.json');
227
+ fs.writeFileSync(v3ConfigPath, JSON.stringify(v3Config, null, 2));
228
+ output.printSuccess(`Config migrated to ${v3ConfigPath}`);
229
+ }
230
+ migrated.push('config');
231
+ }
232
+ else {
233
+ output.printInfo('Config already at v3 — skipping.');
234
+ skipped.push('config');
235
+ }
236
+ }
237
+ else {
238
+ output.writeln(output.dim('No v2 config found — skipping config migration.'));
239
+ skipped.push('config');
240
+ }
241
+ }
242
+ catch (err) {
243
+ output.printError('Config migration failed', String(err));
244
+ skipped.push('config');
245
+ }
246
+ // --- Memory migration ---
247
+ const v2MemoryDir = path.join(cwd, 'data', 'memory');
248
+ try {
249
+ if (fs.existsSync(v2MemoryDir)) {
250
+ const files = fs.readdirSync(v2MemoryDir);
251
+ const jsonFiles = files.filter(f => f.endsWith('.json'));
252
+ const hasDb = files.includes('memory.db');
253
+ if (jsonFiles.length > 0 || hasDb) {
254
+ if (dryRun) {
255
+ output.printInfo(`Would migrate memory: ${jsonFiles.length} JSON files, ${hasDb ? '1 DB' : 'no DB'}`);
256
+ }
257
+ else {
258
+ // Backup memory files
259
+ if (!skipBackup) {
260
+ const memBackup = path.join(backupDir, 'data', 'memory');
261
+ fs.mkdirSync(memBackup, { recursive: true });
262
+ for (const f of files) {
263
+ const src = path.join(v2MemoryDir, f);
264
+ if (fs.statSync(src).isFile()) {
265
+ fs.copyFileSync(src, path.join(memBackup, f));
266
+ }
267
+ }
268
+ }
269
+ output.printSuccess(`Memory files backed up (${jsonFiles.length} JSON, ${hasDb ? '1 DB' : '0 DB'}).`);
270
+ output.printInfo('Run "claude-flow memory init --force" to import v2 memory into v3 AgentDB.');
271
+ }
272
+ migrated.push('memory');
273
+ }
274
+ else {
275
+ output.writeln(output.dim('No v2 memory files found — skipping.'));
276
+ skipped.push('memory');
277
+ }
278
+ }
279
+ else {
280
+ output.writeln(output.dim('No v2 memory directory found — skipping.'));
281
+ skipped.push('memory');
282
+ }
283
+ }
284
+ catch (err) {
285
+ output.printError('Memory migration failed', String(err));
286
+ skipped.push('memory');
287
+ }
288
+ // --- Session migration ---
289
+ const v2SessionsDir = path.join(cwd, 'data', 'sessions');
290
+ try {
291
+ if (fs.existsSync(v2SessionsDir)) {
292
+ const files = fs.readdirSync(v2SessionsDir);
293
+ if (files.length > 0) {
294
+ if (dryRun) {
295
+ output.printInfo(`Would migrate sessions: ${files.length} files from ${v2SessionsDir}`);
296
+ }
297
+ else {
298
+ const v3SessionsDir = path.join(v3Dir, 'sessions');
299
+ fs.mkdirSync(v3SessionsDir, { recursive: true });
300
+ // Backup
301
+ if (!skipBackup) {
302
+ const sessBackup = path.join(backupDir, 'data', 'sessions');
303
+ fs.mkdirSync(sessBackup, { recursive: true });
304
+ for (const f of files) {
305
+ const src = path.join(v2SessionsDir, f);
306
+ if (fs.statSync(src).isFile()) {
307
+ fs.copyFileSync(src, path.join(sessBackup, f));
308
+ }
309
+ }
310
+ }
311
+ // Copy to v3 location
312
+ for (const f of files) {
313
+ const src = path.join(v2SessionsDir, f);
314
+ if (fs.statSync(src).isFile()) {
315
+ fs.copyFileSync(src, path.join(v3SessionsDir, f));
316
+ }
317
+ }
318
+ output.printSuccess(`Sessions migrated: ${files.length} files to ${v3SessionsDir}`);
319
+ }
320
+ migrated.push('sessions');
321
+ }
322
+ else {
323
+ output.writeln(output.dim('No v2 session files found — skipping.'));
324
+ skipped.push('sessions');
325
+ }
326
+ }
327
+ else {
328
+ output.writeln(output.dim('No v2 sessions directory found — skipping.'));
329
+ skipped.push('sessions');
330
+ }
331
+ }
332
+ catch (err) {
333
+ output.printError('Session migration failed', String(err));
334
+ skipped.push('sessions');
335
+ }
336
+ // --- Save migration state ---
337
+ if (!dryRun && migrated.length > 0) {
338
+ const state = {
339
+ status: 'completed',
340
+ timestamp,
341
+ backupPath: skipBackup ? null : backupDir,
342
+ migrated,
343
+ skipped
344
+ };
345
+ fs.writeFileSync(migrationStatePath, JSON.stringify(state, null, 2));
346
+ output.writeln();
347
+ output.printSuccess(`Migration state saved to ${migrationStatePath}`);
348
+ }
349
+ // Summary
350
+ output.writeln();
351
+ if (dryRun) {
352
+ output.printInfo(`Dry run complete. ${migrated.length} component(s) would be migrated.`);
353
+ }
354
+ else if (migrated.length > 0) {
355
+ output.printSuccess(`Migration complete. ${migrated.length} component(s) migrated: ${migrated.join(', ')}`);
356
+ output.printInfo('Run "claude-flow migrate verify" to validate the migration.');
357
+ }
358
+ else {
359
+ output.printInfo('Nothing to migrate.');
360
+ }
361
+ return { success: true, data: { migrated, skipped, dryRun } };
69
362
  }
70
363
  };
71
364
  // Verify migration
@@ -80,13 +373,126 @@ const verifyCommand = {
80
373
  default: false
81
374
  }
82
375
  ],
83
- action: async (_ctx) => {
84
- // #1425: This command is not yet implemented — was returning hardcoded fake verification results
376
+ action: async (ctx) => {
377
+ const cwd = ctx.cwd || process.cwd();
378
+ const v3Dir = path.join(cwd, '.claude-flow');
379
+ const migrationStatePath = path.join(v3Dir, 'migration-state.json');
380
+ const checks = [];
381
+ let allPassed = true;
382
+ output.writeln();
383
+ output.writeln(output.bold('Migration Verification'));
85
384
  output.writeln();
86
- output.printError('migrate verify is not yet implemented');
87
- output.writeln(output.dim('Migration verification requires integrity checks not yet built.'));
88
- output.writeln(output.dim('Track progress: https://github.com/ruvnet/claude-flow/issues/1425'));
89
- return { success: false, exitCode: 1 };
385
+ // Check 1: Migration state file exists
386
+ let migrationState = null;
387
+ try {
388
+ if (fs.existsSync(migrationStatePath)) {
389
+ const raw = fs.readFileSync(migrationStatePath, 'utf-8');
390
+ migrationState = JSON.parse(raw);
391
+ checks.push({ check: 'Migration state file', result: 'passed' });
392
+ }
393
+ else {
394
+ checks.push({ check: 'Migration state file', result: 'failed' });
395
+ allPassed = false;
396
+ }
397
+ }
398
+ catch {
399
+ checks.push({ check: 'Migration state file', result: 'failed' });
400
+ allPassed = false;
401
+ }
402
+ // Check 2: V3 config exists and is valid JSON
403
+ const v3ConfigPath = path.join(v3Dir, 'config.json');
404
+ try {
405
+ if (fs.existsSync(v3ConfigPath)) {
406
+ const raw = fs.readFileSync(v3ConfigPath, 'utf-8');
407
+ JSON.parse(raw); // validate JSON
408
+ checks.push({ check: 'V3 config (valid JSON)', result: 'passed' });
409
+ }
410
+ else {
411
+ // Config might not have been migrated if there was no v2 config
412
+ const wasMigrated = migrationState &&
413
+ Array.isArray(migrationState.migrated) &&
414
+ migrationState.migrated.includes('config');
415
+ if (wasMigrated) {
416
+ checks.push({ check: 'V3 config (valid JSON)', result: 'failed' });
417
+ allPassed = false;
418
+ }
419
+ else {
420
+ checks.push({ check: 'V3 config (valid JSON)', result: 'skipped' });
421
+ }
422
+ }
423
+ }
424
+ catch {
425
+ checks.push({ check: 'V3 config (valid JSON)', result: 'failed' });
426
+ allPassed = false;
427
+ }
428
+ // Check 3: Backup exists
429
+ if (migrationState && migrationState.backupPath) {
430
+ const backupPath = migrationState.backupPath;
431
+ try {
432
+ if (fs.existsSync(backupPath) && fs.statSync(backupPath).isDirectory()) {
433
+ checks.push({ check: 'Backup directory', result: 'passed' });
434
+ }
435
+ else {
436
+ checks.push({ check: 'Backup directory', result: 'failed' });
437
+ allPassed = false;
438
+ }
439
+ }
440
+ catch {
441
+ checks.push({ check: 'Backup directory', result: 'failed' });
442
+ allPassed = false;
443
+ }
444
+ }
445
+ else if (migrationState && migrationState.backupPath === null) {
446
+ checks.push({ check: 'Backup directory', result: 'skipped (backup was disabled)' });
447
+ }
448
+ else {
449
+ checks.push({ check: 'Backup directory', result: 'failed' });
450
+ allPassed = false;
451
+ }
452
+ // Check 4: V3 sessions directory if sessions were migrated
453
+ if (migrationState &&
454
+ Array.isArray(migrationState.migrated) &&
455
+ migrationState.migrated.includes('sessions')) {
456
+ const v3Sessions = path.join(v3Dir, 'sessions');
457
+ try {
458
+ if (fs.existsSync(v3Sessions) && fs.readdirSync(v3Sessions).length > 0) {
459
+ checks.push({ check: 'V3 sessions directory', result: 'passed' });
460
+ }
461
+ else {
462
+ checks.push({ check: 'V3 sessions directory', result: 'failed' });
463
+ allPassed = false;
464
+ }
465
+ }
466
+ catch {
467
+ checks.push({ check: 'V3 sessions directory', result: 'failed' });
468
+ allPassed = false;
469
+ }
470
+ }
471
+ // Display
472
+ if (ctx.flags.format === 'json') {
473
+ output.printJson({ checks, allPassed });
474
+ return { success: allPassed, data: { checks, allPassed } };
475
+ }
476
+ output.printTable({
477
+ columns: [
478
+ { key: 'check', header: 'Check', width: 30 },
479
+ { key: 'result', header: 'Result', width: 35 }
480
+ ],
481
+ data: checks.map(c => ({
482
+ check: c.check,
483
+ result: formatMigrationStatus(c.result)
484
+ })),
485
+ border: false
486
+ });
487
+ output.writeln();
488
+ if (allPassed) {
489
+ output.printSuccess('All verification checks passed.');
490
+ }
491
+ else {
492
+ output.printError('Some verification checks failed.');
493
+ output.printInfo('Run "claude-flow migrate run" to re-run the migration, or "migrate rollback" to restore from backup.');
494
+ }
495
+ return { success: allPassed, data: { checks, allPassed }, exitCode: allPassed ? 0 : 1 };
90
496
  }
91
497
  };
92
498
  // Rollback migration
@@ -107,13 +513,94 @@ const rollbackCommand = {
107
513
  default: false
108
514
  }
109
515
  ],
110
- action: async (_ctx) => {
111
- // #1425: This command is not yet implemented — was faking rollback execution
516
+ action: async (ctx) => {
517
+ const cwd = ctx.cwd || process.cwd();
518
+ const v3Dir = path.join(cwd, '.claude-flow');
519
+ const migrationStatePath = path.join(v3Dir, 'migration-state.json');
520
+ output.writeln();
521
+ output.writeln(output.bold('Migration Rollback'));
112
522
  output.writeln();
113
- output.printError('migrate rollback is not yet implemented');
114
- output.writeln(output.dim('Migration rollback requires backup/restore logic not yet built.'));
115
- output.writeln(output.dim('Track progress: https://github.com/ruvnet/claude-flow/issues/1425'));
116
- return { success: false, exitCode: 1 };
523
+ // Read migration state
524
+ let migrationState;
525
+ try {
526
+ if (!fs.existsSync(migrationStatePath)) {
527
+ output.printError('No migration state found.', 'Run "migrate run" first before attempting rollback.');
528
+ return { success: false, exitCode: 1 };
529
+ }
530
+ const raw = fs.readFileSync(migrationStatePath, 'utf-8');
531
+ migrationState = JSON.parse(raw);
532
+ }
533
+ catch (err) {
534
+ output.printError('Failed to read migration state', String(err));
535
+ return { success: false, exitCode: 1 };
536
+ }
537
+ const backupPath = migrationState.backupPath;
538
+ if (!backupPath) {
539
+ output.printError('No backup path in migration state.', 'Migration was run with --no-backup. Cannot rollback.');
540
+ return { success: false, exitCode: 1 };
541
+ }
542
+ if (!fs.existsSync(backupPath)) {
543
+ output.printError('Backup directory not found.', `Expected: ${backupPath}`);
544
+ return { success: false, exitCode: 1 };
545
+ }
546
+ const restored = [];
547
+ try {
548
+ // Restore config
549
+ const backupConfig = path.join(backupPath, 'claude-flow.config.json');
550
+ if (fs.existsSync(backupConfig)) {
551
+ const destConfig = path.join(cwd, 'claude-flow.config.json');
552
+ fs.copyFileSync(backupConfig, destConfig);
553
+ // Remove v3 config
554
+ const v3Config = path.join(v3Dir, 'config.json');
555
+ if (fs.existsSync(v3Config)) {
556
+ fs.unlinkSync(v3Config);
557
+ }
558
+ output.printSuccess('Restored: config');
559
+ restored.push('config');
560
+ }
561
+ // Restore memory
562
+ const backupMemory = path.join(backupPath, 'data', 'memory');
563
+ if (fs.existsSync(backupMemory)) {
564
+ const destMemory = path.join(cwd, 'data', 'memory');
565
+ fs.mkdirSync(destMemory, { recursive: true });
566
+ const files = fs.readdirSync(backupMemory);
567
+ for (const f of files) {
568
+ fs.copyFileSync(path.join(backupMemory, f), path.join(destMemory, f));
569
+ }
570
+ output.printSuccess(`Restored: memory (${files.length} files)`);
571
+ restored.push('memory');
572
+ }
573
+ // Restore sessions
574
+ const backupSessions = path.join(backupPath, 'data', 'sessions');
575
+ if (fs.existsSync(backupSessions)) {
576
+ const destSessions = path.join(cwd, 'data', 'sessions');
577
+ fs.mkdirSync(destSessions, { recursive: true });
578
+ const files = fs.readdirSync(backupSessions);
579
+ for (const f of files) {
580
+ fs.copyFileSync(path.join(backupSessions, f), path.join(destSessions, f));
581
+ }
582
+ // Remove v3 sessions
583
+ const v3Sessions = path.join(v3Dir, 'sessions');
584
+ if (fs.existsSync(v3Sessions)) {
585
+ const v3Files = fs.readdirSync(v3Sessions);
586
+ for (const f of v3Files) {
587
+ fs.unlinkSync(path.join(v3Sessions, f));
588
+ }
589
+ fs.rmdirSync(v3Sessions);
590
+ }
591
+ output.printSuccess(`Restored: sessions (${files.length} files)`);
592
+ restored.push('sessions');
593
+ }
594
+ // Delete migration state
595
+ fs.unlinkSync(migrationStatePath);
596
+ output.writeln();
597
+ output.printSuccess(`Rollback complete. Restored: ${restored.join(', ') || 'nothing to restore'}`);
598
+ return { success: true, data: { restored } };
599
+ }
600
+ catch (err) {
601
+ output.printError('Rollback failed', String(err));
602
+ return { success: false, exitCode: 1 };
603
+ }
117
604
  }
118
605
  };
119
606
  // Breaking changes info
@@ -218,20 +705,25 @@ export const migrateCommand = {
218
705
  };
219
706
  // Helper functions
220
707
  function formatMigrationStatus(status) {
221
- switch (status) {
222
- case 'migrated':
223
- case 'passed':
224
- return output.success(status);
225
- case 'pending':
226
- case 'partial':
227
- return output.warning(status);
228
- case 'failed':
229
- return output.error(status);
230
- case 'not-required':
231
- return output.dim(status);
232
- default:
233
- return status;
708
+ if (status === 'migrated' || status === 'passed' || status === 'completed') {
709
+ return output.success(status);
710
+ }
711
+ if (status === 'pending' || status === 'partial') {
712
+ return output.warning(status);
713
+ }
714
+ if (status === 'failed') {
715
+ return output.error(status);
716
+ }
717
+ if (status === 'not-required' || status.startsWith('skipped') || status === 'v3' || status === 'missing') {
718
+ return output.dim(status);
719
+ }
720
+ if (status === 'v2') {
721
+ return output.warning(status);
722
+ }
723
+ if (status === 'v2 + v3') {
724
+ return output.success(status);
234
725
  }
726
+ return status;
235
727
  }
236
728
  function getMigrationSteps(target) {
237
729
  const allSteps = [