@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.
- package/README.md +199 -196
- package/dist/src/commands/claims.d.ts.map +1 -1
- package/dist/src/commands/claims.js +347 -96
- package/dist/src/commands/claims.js.map +1 -1
- package/dist/src/commands/config.d.ts.map +1 -1
- package/dist/src/commands/config.js +58 -108
- package/dist/src/commands/config.js.map +1 -1
- package/dist/src/commands/deployment.d.ts.map +1 -1
- package/dist/src/commands/deployment.js +532 -149
- package/dist/src/commands/deployment.js.map +1 -1
- package/dist/src/commands/hive-mind.d.ts.map +1 -1
- package/dist/src/commands/hive-mind.js +7 -0
- package/dist/src/commands/hive-mind.js.map +1 -1
- package/dist/src/commands/memory.d.ts.map +1 -1
- package/dist/src/commands/memory.js +7 -0
- package/dist/src/commands/memory.js.map +1 -1
- package/dist/src/commands/migrate.d.ts.map +1 -1
- package/dist/src/commands/migrate.js +491 -159
- package/dist/src/commands/migrate.js.map +1 -1
- package/dist/src/commands/providers.d.ts.map +1 -1
- package/dist/src/commands/providers.js +162 -36
- package/dist/src/commands/providers.js.map +1 -1
- package/dist/src/commands/swarm.d.ts.map +1 -1
- package/dist/src/commands/swarm.js +79 -25
- package/dist/src/commands/swarm.js.map +1 -1
- package/dist/src/config-adapter.js +1 -1
- package/dist/src/config-adapter.js.map +1 -1
- package/dist/src/init/executor.d.ts.map +1 -1
- package/dist/src/init/executor.js +2 -3
- package/dist/src/init/executor.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +3 -0
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-server.d.ts +3 -1
- package/dist/src/mcp-server.d.ts.map +1 -1
- package/dist/src/mcp-server.js +31 -4
- package/dist/src/mcp-server.js.map +1 -1
- package/dist/src/mcp-tools/guidance-tools.d.ts +15 -0
- package/dist/src/mcp-tools/guidance-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/guidance-tools.js +617 -0
- package/dist/src/mcp-tools/guidance-tools.js.map +1 -0
- package/dist/src/mcp-tools/index.d.ts +1 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +1 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/ruvllm-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/ruvllm-tools.js +4 -3
- package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -1
- package/dist/src/parser.d.ts.map +1 -1
- package/dist/src/parser.js +3 -1
- package/dist/src/parser.js.map +1 -1
- package/dist/src/services/config-file-manager.d.ts +37 -0
- package/dist/src/services/config-file-manager.d.ts.map +1 -0
- package/dist/src/services/config-file-manager.js +224 -0
- package/dist/src/services/config-file-manager.js.map +1 -0
- package/dist/src/services/headless-worker-executor.d.ts.map +1 -1
- package/dist/src/services/headless-worker-executor.js +30 -5
- package/dist/src/services/headless-worker-executor.js.map +1 -1
- package/dist/src/services/worker-daemon.d.ts +17 -0
- package/dist/src/services/worker-daemon.d.ts.map +1 -1
- package/dist/src/services/worker-daemon.js +66 -2
- package/dist/src/services/worker-daemon.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- 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
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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(
|
|
42
|
-
return { success: true, data:
|
|
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: '
|
|
55
|
-
{ key: 'status', header: 'Status', width: 15
|
|
56
|
-
{ key: '
|
|
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:
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
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
|
-
|
|
101
|
-
const dryRun = ctx.flags
|
|
102
|
-
const
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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.
|
|
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
|
-
//
|
|
120
|
-
if (
|
|
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
|
-
|
|
123
|
-
output.writeln();
|
|
191
|
+
fs.mkdirSync(backupDir, { recursive: true });
|
|
124
192
|
}
|
|
125
|
-
//
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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(
|
|
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
|
-
|
|
140
|
-
|
|
141
|
-
dryRun,
|
|
142
|
-
backup,
|
|
143
|
-
stepsCompleted: steps.length,
|
|
144
|
-
success: true
|
|
145
|
-
};
|
|
349
|
+
// Summary
|
|
350
|
+
output.writeln();
|
|
146
351
|
if (dryRun) {
|
|
147
|
-
output.printInfo(
|
|
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.
|
|
359
|
+
output.printInfo('Nothing to migrate.');
|
|
151
360
|
}
|
|
152
|
-
return { success: true, data:
|
|
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
|
|
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.
|
|
383
|
+
output.writeln(output.bold('Migration Verification'));
|
|
171
384
|
output.writeln();
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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: '
|
|
183
|
-
{ key: '
|
|
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 (
|
|
197
|
-
|
|
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.
|
|
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:
|
|
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
|
|
231
|
-
const
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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
|
-
|
|
261
|
-
|
|
262
|
-
|
|
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
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
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 = [
|