@claude-flow/cli 3.5.42 → 3.5.44

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 (64) hide show
  1. package/README.md +199 -196
  2. package/dist/src/commands/claims.d.ts.map +1 -1
  3. package/dist/src/commands/claims.js +347 -96
  4. package/dist/src/commands/claims.js.map +1 -1
  5. package/dist/src/commands/config.d.ts.map +1 -1
  6. package/dist/src/commands/config.js +58 -108
  7. package/dist/src/commands/config.js.map +1 -1
  8. package/dist/src/commands/deployment.d.ts.map +1 -1
  9. package/dist/src/commands/deployment.js +532 -149
  10. package/dist/src/commands/deployment.js.map +1 -1
  11. package/dist/src/commands/hive-mind.d.ts.map +1 -1
  12. package/dist/src/commands/hive-mind.js +7 -0
  13. package/dist/src/commands/hive-mind.js.map +1 -1
  14. package/dist/src/commands/memory.d.ts.map +1 -1
  15. package/dist/src/commands/memory.js +7 -0
  16. package/dist/src/commands/memory.js.map +1 -1
  17. package/dist/src/commands/migrate.d.ts.map +1 -1
  18. package/dist/src/commands/migrate.js +491 -159
  19. package/dist/src/commands/migrate.js.map +1 -1
  20. package/dist/src/commands/providers.d.ts.map +1 -1
  21. package/dist/src/commands/providers.js +162 -36
  22. package/dist/src/commands/providers.js.map +1 -1
  23. package/dist/src/commands/swarm.d.ts.map +1 -1
  24. package/dist/src/commands/swarm.js +79 -25
  25. package/dist/src/commands/swarm.js.map +1 -1
  26. package/dist/src/config-adapter.js +1 -1
  27. package/dist/src/config-adapter.js.map +1 -1
  28. package/dist/src/init/executor.d.ts.map +1 -1
  29. package/dist/src/init/executor.js +2 -3
  30. package/dist/src/init/executor.js.map +1 -1
  31. package/dist/src/mcp-client.d.ts.map +1 -1
  32. package/dist/src/mcp-client.js +3 -0
  33. package/dist/src/mcp-client.js.map +1 -1
  34. package/dist/src/mcp-server.d.ts +3 -1
  35. package/dist/src/mcp-server.d.ts.map +1 -1
  36. package/dist/src/mcp-server.js +31 -4
  37. package/dist/src/mcp-server.js.map +1 -1
  38. package/dist/src/mcp-tools/guidance-tools.d.ts +15 -0
  39. package/dist/src/mcp-tools/guidance-tools.d.ts.map +1 -0
  40. package/dist/src/mcp-tools/guidance-tools.js +617 -0
  41. package/dist/src/mcp-tools/guidance-tools.js.map +1 -0
  42. package/dist/src/mcp-tools/index.d.ts +1 -0
  43. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  44. package/dist/src/mcp-tools/index.js +1 -0
  45. package/dist/src/mcp-tools/index.js.map +1 -1
  46. package/dist/src/mcp-tools/ruvllm-tools.d.ts.map +1 -1
  47. package/dist/src/mcp-tools/ruvllm-tools.js +4 -3
  48. package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -1
  49. package/dist/src/parser.d.ts.map +1 -1
  50. package/dist/src/parser.js +3 -1
  51. package/dist/src/parser.js.map +1 -1
  52. package/dist/src/services/config-file-manager.d.ts +37 -0
  53. package/dist/src/services/config-file-manager.d.ts.map +1 -0
  54. package/dist/src/services/config-file-manager.js +224 -0
  55. package/dist/src/services/config-file-manager.js.map +1 -0
  56. package/dist/src/services/headless-worker-executor.d.ts.map +1 -1
  57. package/dist/src/services/headless-worker-executor.js +30 -5
  58. package/dist/src/services/headless-worker-executor.js.map +1 -1
  59. package/dist/src/services/worker-daemon.d.ts +17 -0
  60. package/dist/src/services/worker-daemon.d.ts.map +1 -1
  61. package/dist/src/services/worker-daemon.js +66 -2
  62. package/dist/src/services/worker-daemon.js.map +1 -1
  63. package/dist/tsconfig.tsbuildinfo +1 -1
  64. package/package.json +3 -1
@@ -3,7 +3,8 @@
3
3
  * Migration tools for V2 to V3 transition
4
4
  */
5
5
  import { output } from '../output.js';
6
- import { select, confirm } from '../prompt.js';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
7
8
  // Migration targets
8
9
  const MIGRATION_TARGETS = [
9
10
  { value: 'config', label: 'Configuration', hint: 'Migrate configuration files' },
@@ -19,49 +20,117 @@ const statusCommand = {
19
20
  name: 'status',
20
21
  description: 'Check migration status',
21
22
  action: async (ctx) => {
22
- const status = {
23
- v2Version: '2.6.0',
24
- v3Version: '3.0.0-alpha.1',
25
- migrationState: 'partial',
26
- components: [
27
- { name: 'Configuration', status: 'migrated', v2Path: './claude-flow.json', v3Path: './claude-flow.config.json' },
28
- { name: 'Memory Data', status: 'pending', v2Path: './.claude-flow/memory', v3Path: './data/memory' },
29
- { name: 'Agent Configs', status: 'pending', v2Path: './.claude-flow/agents', v3Path: './v3/agents' },
30
- { name: 'Hooks', status: 'pending', v2Path: './src/hooks', v3Path: './v3/hooks' },
31
- { name: 'Workflows', status: 'not-required', v2Path: 'N/A', v3Path: 'N/A' },
32
- { name: 'Embeddings', status: 'pending', v2Path: 'OpenAI/TF.js', v3Path: 'ONNX + Hyperbolic' }
33
- ],
34
- recommendations: [
35
- 'Backup v2 data before migration',
36
- 'Test migration in staging environment first',
37
- 'Review breaking changes in CHANGELOG.md'
38
- ]
39
- };
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
40
105
  if (ctx.flags.format === 'json') {
41
- output.printJson(status);
42
- return { success: true, data: status };
106
+ output.printJson({ components, migrationState });
107
+ return { success: true, data: { components, migrationState } };
43
108
  }
44
109
  output.writeln();
45
110
  output.writeln(output.bold('Migration Status'));
46
111
  output.writeln();
47
- output.writeln(`V2 Version: ${status.v2Version}`);
48
- output.writeln(`V3 Version: ${status.v3Version}`);
49
- output.writeln(`State: ${formatMigrationStatus(status.migrationState)}`);
50
- output.writeln();
51
- output.writeln(output.bold('Components'));
52
112
  output.printTable({
53
113
  columns: [
54
- { key: 'name', header: 'Component', width: 18 },
55
- { key: 'status', header: 'Status', width: 15, format: (v) => formatMigrationStatus(String(v)) },
56
- { key: 'v2Path', header: 'V2 Path', width: 25 },
57
- { key: 'v3Path', header: 'V3 Path', width: 25 }
114
+ { key: 'component', header: 'Component', width: 20 },
115
+ { key: 'status', header: 'Status', width: 15 },
116
+ { key: 'migrationNeeded', header: 'Migration Needed', width: 20 }
58
117
  ],
59
- data: status.components
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
60
124
  });
125
+ const needsMigration = components.some(c => c.migrationNeeded === 'yes');
61
126
  output.writeln();
62
- output.writeln(output.bold('Recommendations'));
63
- output.printList(status.recommendations);
64
- return { success: true, data: status };
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 } };
65
134
  }
66
135
  };
67
136
  // Run migration
@@ -97,59 +166,199 @@ const runCommand = {
97
166
  }
98
167
  ],
99
168
  action: async (ctx) => {
100
- let target = ctx.flags.target;
101
- const dryRun = ctx.flags.dryRun;
102
- const backup = ctx.flags.backup;
103
- const force = ctx.flags.force;
104
- if (!target && ctx.interactive) {
105
- target = await select({
106
- message: 'Select migration target:',
107
- options: MIGRATION_TARGETS,
108
- default: 'all'
109
- });
110
- }
111
- target = target || 'all';
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 = [];
112
178
  output.writeln();
179
+ output.writeln(output.bold('V2 to V3 Migration'));
113
180
  if (dryRun) {
114
- output.printInfo('DRY RUN - No changes will be made');
115
- output.writeln();
181
+ output.printWarning('Dry run mode no changes will be made.');
116
182
  }
117
- output.printInfo(`Migrating: ${target}`);
118
183
  output.writeln();
119
- // Backup step
120
- if (backup && !dryRun) {
184
+ // Ensure .claude-flow directory exists
185
+ if (!dryRun) {
186
+ fs.mkdirSync(v3Dir, { recursive: true });
187
+ }
188
+ // --- Backup ---
189
+ if (!skipBackup && !dryRun) {
121
190
  output.writeln(output.dim('Creating backup...'));
122
- output.writeln(output.dim(` Backup created: ./.claude-flow-backup-${Date.now()}`));
123
- output.writeln();
191
+ fs.mkdirSync(backupDir, { recursive: true });
124
192
  }
125
- // Migration steps based on target
126
- const steps = getMigrationSteps(target);
127
- for (const step of steps) {
128
- output.writeln(`${output.info('>')} ${step.name}`);
129
- output.writeln(output.dim(` ${step.description}`));
130
- if (!dryRun) {
131
- // Execute migration step
132
- output.writeln(output.dim(` ${output.success('[OK]')} Completed`));
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
+ }
133
236
  }
134
237
  else {
135
- output.writeln(output.dim(` Would migrate: ${step.source} -> ${step.dest}`));
238
+ output.writeln(output.dim('No v2 config found — skipping config migration.'));
239
+ skipped.push('config');
136
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));
137
346
  output.writeln();
347
+ output.printSuccess(`Migration state saved to ${migrationStatePath}`);
138
348
  }
139
- const result = {
140
- target,
141
- dryRun,
142
- backup,
143
- stepsCompleted: steps.length,
144
- success: true
145
- };
349
+ // Summary
350
+ output.writeln();
146
351
  if (dryRun) {
147
- output.printInfo('Dry run complete. Run without --dry-run to apply changes.');
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.');
148
357
  }
149
358
  else {
150
- output.printSuccess('Migration completed successfully');
359
+ output.printInfo('Nothing to migrate.');
151
360
  }
152
- return { success: true, data: result };
361
+ return { success: true, data: { migrated, skipped, dryRun } };
153
362
  }
154
363
  };
155
364
  // Verify migration
@@ -165,47 +374,125 @@ const verifyCommand = {
165
374
  }
166
375
  ],
167
376
  action: async (ctx) => {
168
- const fix = ctx.flags.fix;
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;
169
382
  output.writeln();
170
- output.printInfo('Verifying migration...');
383
+ output.writeln(output.bold('Migration Verification'));
171
384
  output.writeln();
172
- const checks = [
173
- { name: 'Configuration Schema', status: 'passed', details: 'V3 schema valid' },
174
- { name: 'Memory Data Integrity', status: 'passed', details: 'All entries valid' },
175
- { name: 'Agent Configurations', status: 'warning', details: '2 deprecated fields detected' },
176
- { name: 'Hook Definitions', status: 'passed', details: 'All hooks registered' },
177
- { name: 'File Permissions', status: 'passed', details: 'Correct permissions' },
178
- { name: 'Dependencies', status: 'passed', details: 'All dependencies available' }
179
- ];
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
+ }
180
476
  output.printTable({
181
477
  columns: [
182
- { key: 'name', header: 'Check', width: 25 },
183
- { key: 'status', header: 'Status', width: 12, format: (v) => {
184
- if (v === 'passed')
185
- return output.success('PASSED');
186
- if (v === 'warning')
187
- return output.warning('WARNING');
188
- return output.error('FAILED');
189
- } },
190
- { key: 'details', header: 'Details', width: 30 }
478
+ { key: 'check', header: 'Check', width: 30 },
479
+ { key: 'result', header: 'Result', width: 35 }
191
480
  ],
192
- data: checks
481
+ data: checks.map(c => ({
482
+ check: c.check,
483
+ result: formatMigrationStatus(c.result)
484
+ })),
485
+ border: false
193
486
  });
194
- const hasIssues = checks.some(c => c.status !== 'passed');
195
487
  output.writeln();
196
- if (hasIssues) {
197
- if (fix) {
198
- output.printInfo('Attempting to fix issues...');
199
- output.printSuccess('Issues fixed');
200
- }
201
- else {
202
- output.printWarning('Some issues detected. Run with --fix to attempt automatic fixes.');
203
- }
488
+ if (allPassed) {
489
+ output.printSuccess('All verification checks passed.');
204
490
  }
205
491
  else {
206
- output.printSuccess('All verification checks passed');
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.');
207
494
  }
208
- return { success: true, data: { checks, hasIssues } };
495
+ return { success: allPassed, data: { checks, allPassed }, exitCode: allPassed ? 0 : 1 };
209
496
  }
210
497
  };
211
498
  // Rollback migration
@@ -227,53 +514,93 @@ const rollbackCommand = {
227
514
  }
228
515
  ],
229
516
  action: async (ctx) => {
230
- const backupId = ctx.flags.backupId;
231
- const force = ctx.flags.force;
232
- // List available backups
233
- const backups = [
234
- { id: 'backup-1704369600', date: '2024-01-04 10:00:00', size: '45.2 MB' },
235
- { id: 'backup-1704283200', date: '2024-01-03 10:00:00', size: '44.8 MB' },
236
- { id: 'backup-1704196800', date: '2024-01-02 10:00:00', size: '43.5 MB' }
237
- ];
238
- if (!backupId && ctx.interactive) {
239
- output.writeln();
240
- output.writeln(output.bold('Available Backups'));
241
- output.writeln();
242
- output.printTable({
243
- columns: [
244
- { key: 'id', header: 'Backup ID', width: 20 },
245
- { key: 'date', header: 'Date', width: 22 },
246
- { key: 'size', header: 'Size', width: 12, align: 'right' }
247
- ],
248
- data: backups
249
- });
250
- output.writeln();
251
- const confirmed = await confirm({
252
- message: `Rollback to most recent backup (${backups[0].id})?`,
253
- default: false
254
- });
255
- if (!confirmed) {
256
- output.printInfo('Operation cancelled');
257
- return { success: true };
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'));
522
+ output.writeln();
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 };
258
529
  }
530
+ const raw = fs.readFileSync(migrationStatePath, 'utf-8');
531
+ migrationState = JSON.parse(raw);
259
532
  }
260
- const targetBackup = backupId || backups[0].id;
261
- if (!force && !ctx.interactive) {
262
- output.printError('Use --force to rollback without confirmation');
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));
263
602
  return { success: false, exitCode: 1 };
264
603
  }
265
- output.writeln();
266
- output.printInfo(`Rolling back to ${targetBackup}...`);
267
- output.writeln();
268
- output.writeln(output.dim(' Stopping services...'));
269
- output.writeln(output.dim(' Restoring configuration...'));
270
- output.writeln(output.dim(' Restoring memory data...'));
271
- output.writeln(output.dim(' Restoring agent configs...'));
272
- output.writeln(output.dim(' Verifying integrity...'));
273
- output.writeln();
274
- output.printSuccess(`Rolled back to ${targetBackup}`);
275
- output.writeln(output.dim(' Note: Restart services to apply changes'));
276
- return { success: true, data: { backupId: targetBackup, rolledBack: true } };
277
604
  }
278
605
  };
279
606
  // Breaking changes info
@@ -378,20 +705,25 @@ export const migrateCommand = {
378
705
  };
379
706
  // Helper functions
380
707
  function formatMigrationStatus(status) {
381
- switch (status) {
382
- case 'migrated':
383
- case 'passed':
384
- return output.success(status);
385
- case 'pending':
386
- case 'partial':
387
- return output.warning(status);
388
- case 'failed':
389
- return output.error(status);
390
- case 'not-required':
391
- return output.dim(status);
392
- default:
393
- 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);
394
725
  }
726
+ return status;
395
727
  }
396
728
  function getMigrationSteps(target) {
397
729
  const allSteps = [