@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
|
@@ -5,95 +5,249 @@
|
|
|
5
5
|
* Created with ❤️ by ruv.io
|
|
6
6
|
*/
|
|
7
7
|
import { output } from '../output.js';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import * as path from 'path';
|
|
10
|
+
// ============================================
|
|
11
|
+
// State Helpers
|
|
12
|
+
// ============================================
|
|
13
|
+
function getStateDir(cwd) {
|
|
14
|
+
return path.join(cwd, '.claude-flow');
|
|
15
|
+
}
|
|
16
|
+
function getStatePath(cwd) {
|
|
17
|
+
return path.join(getStateDir(cwd), 'deployments.json');
|
|
18
|
+
}
|
|
19
|
+
function emptyState() {
|
|
20
|
+
return { environments: {}, history: [], activeDeployment: undefined };
|
|
21
|
+
}
|
|
22
|
+
function loadDeploymentState(cwd) {
|
|
23
|
+
const filePath = getStatePath(cwd);
|
|
24
|
+
if (!fs.existsSync(filePath)) {
|
|
25
|
+
return emptyState();
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
29
|
+
return JSON.parse(raw);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return emptyState();
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function saveDeploymentState(cwd, state) {
|
|
36
|
+
const dir = getStateDir(cwd);
|
|
37
|
+
if (!fs.existsSync(dir)) {
|
|
38
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
39
|
+
}
|
|
40
|
+
const filePath = getStatePath(cwd);
|
|
41
|
+
const tmpPath = filePath + '.tmp';
|
|
42
|
+
fs.writeFileSync(tmpPath, JSON.stringify(state, null, 2), 'utf-8');
|
|
43
|
+
fs.renameSync(tmpPath, filePath);
|
|
44
|
+
}
|
|
45
|
+
function generateId() {
|
|
46
|
+
const ts = Date.now().toString(36);
|
|
47
|
+
const rand = Math.random().toString(36).slice(2, 8);
|
|
48
|
+
return `dep-${ts}-${rand}`;
|
|
49
|
+
}
|
|
50
|
+
function readProjectVersion(cwd) {
|
|
51
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
52
|
+
if (!fs.existsSync(pkgPath)) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
|
|
57
|
+
return pkg.version ?? null;
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// ============================================
|
|
8
64
|
// Deploy subcommand
|
|
65
|
+
// ============================================
|
|
9
66
|
const deployCommand = {
|
|
10
67
|
name: 'deploy',
|
|
11
68
|
description: 'Deploy to target environment',
|
|
12
69
|
options: [
|
|
13
70
|
{ name: 'env', short: 'e', type: 'string', description: 'Environment: dev, staging, prod', default: 'staging' },
|
|
14
|
-
{ name: 'version', short: 'v', type: 'string', description: 'Version to deploy'
|
|
71
|
+
{ name: 'version', short: 'v', type: 'string', description: 'Version to deploy' },
|
|
15
72
|
{ name: 'dry-run', short: 'd', type: 'boolean', description: 'Simulate deployment without changes' },
|
|
16
|
-
{ name: '
|
|
17
|
-
{ name: 'rollback-on-fail', type: 'boolean', description: 'Auto rollback on failure', default: 'true' },
|
|
73
|
+
{ name: 'description', type: 'string', description: 'Deployment description' },
|
|
18
74
|
],
|
|
19
75
|
examples: [
|
|
20
76
|
{ command: 'claude-flow deployment deploy -e prod', description: 'Deploy to production' },
|
|
21
77
|
{ command: 'claude-flow deployment deploy --dry-run', description: 'Simulate deployment' },
|
|
22
78
|
],
|
|
23
79
|
action: async (ctx) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
80
|
+
try {
|
|
81
|
+
const envName = String(ctx.flags['env'] || 'staging');
|
|
82
|
+
const dryRun = Boolean(ctx.flags['dry-run']);
|
|
83
|
+
const description = ctx.flags['description'] ? String(ctx.flags['description']) : undefined;
|
|
84
|
+
let version = ctx.flags['version'] ? String(ctx.flags['version']) : null;
|
|
85
|
+
if (!version) {
|
|
86
|
+
version = readProjectVersion(ctx.cwd) || '0.0.0';
|
|
87
|
+
}
|
|
88
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
89
|
+
// Ensure environment exists; auto-create if it doesn't
|
|
90
|
+
if (!state.environments[envName]) {
|
|
91
|
+
state.environments[envName] = {
|
|
92
|
+
name: envName,
|
|
93
|
+
type: envName === 'prod' || envName === 'production' ? 'production' : envName === 'staging' ? 'staging' : 'local',
|
|
94
|
+
createdAt: new Date().toISOString(),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
const record = {
|
|
98
|
+
id: generateId(),
|
|
99
|
+
environment: envName,
|
|
100
|
+
version,
|
|
101
|
+
status: 'deployed',
|
|
102
|
+
timestamp: new Date().toISOString(),
|
|
103
|
+
description,
|
|
104
|
+
};
|
|
105
|
+
if (dryRun) {
|
|
106
|
+
output.writeln();
|
|
107
|
+
output.printInfo('Dry run - no changes will be made');
|
|
108
|
+
output.writeln();
|
|
109
|
+
output.writeln(output.bold('Deployment Preview'));
|
|
110
|
+
output.printTable({
|
|
111
|
+
columns: [
|
|
112
|
+
{ key: 'field', header: 'Field' },
|
|
113
|
+
{ key: 'value', header: 'Value' },
|
|
114
|
+
],
|
|
115
|
+
data: [
|
|
116
|
+
{ field: 'ID', value: record.id },
|
|
117
|
+
{ field: 'Environment', value: envName },
|
|
118
|
+
{ field: 'Version', value: version },
|
|
119
|
+
{ field: 'Status', value: 'deployed (dry-run)' },
|
|
120
|
+
{ field: 'Description', value: description || '-' },
|
|
121
|
+
],
|
|
122
|
+
});
|
|
123
|
+
return { success: true };
|
|
124
|
+
}
|
|
125
|
+
state.history.push(record);
|
|
126
|
+
state.activeDeployment = record.id;
|
|
127
|
+
saveDeploymentState(ctx.cwd, state);
|
|
128
|
+
output.writeln();
|
|
129
|
+
output.printSuccess(`Deployed version ${version} to ${envName}`);
|
|
32
130
|
output.writeln();
|
|
131
|
+
output.printTable({
|
|
132
|
+
columns: [
|
|
133
|
+
{ key: 'field', header: 'Field' },
|
|
134
|
+
{ key: 'value', header: 'Value' },
|
|
135
|
+
],
|
|
136
|
+
data: [
|
|
137
|
+
{ field: 'ID', value: record.id },
|
|
138
|
+
{ field: 'Environment', value: envName },
|
|
139
|
+
{ field: 'Version', value: version },
|
|
140
|
+
{ field: 'Status', value: record.status },
|
|
141
|
+
{ field: 'Timestamp', value: record.timestamp },
|
|
142
|
+
{ field: 'Description', value: description || '-' },
|
|
143
|
+
],
|
|
144
|
+
});
|
|
145
|
+
return { success: true, data: record };
|
|
33
146
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
{
|
|
38
|
-
{ name: 'Security scan', status: 'pending' },
|
|
39
|
-
{ name: 'Deploy to target', status: 'pending' },
|
|
40
|
-
{ name: 'Health checks', status: 'pending' },
|
|
41
|
-
{ name: 'DNS update', status: 'pending' },
|
|
42
|
-
];
|
|
43
|
-
for (const step of steps) {
|
|
44
|
-
const spinner = output.createSpinner({ text: step.name + '...', spinner: 'dots' });
|
|
45
|
-
spinner.start();
|
|
46
|
-
await new Promise(r => setTimeout(r, 400));
|
|
47
|
-
spinner.succeed(step.name);
|
|
147
|
+
catch (err) {
|
|
148
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
149
|
+
output.printError('Deploy failed', msg);
|
|
150
|
+
return { success: false, exitCode: 1 };
|
|
48
151
|
}
|
|
49
|
-
output.writeln();
|
|
50
|
-
output.printBox([
|
|
51
|
-
`Environment: ${env}`,
|
|
52
|
-
`Version: ${version}`,
|
|
53
|
-
`Status: ${output.success('Deployed')}`,
|
|
54
|
-
``,
|
|
55
|
-
`URL: https://${env === 'prod' ? 'api' : env}.claude-flow.io`,
|
|
56
|
-
`Deployed at: ${new Date().toISOString()}`,
|
|
57
|
-
`Duration: 12.4s`,
|
|
58
|
-
].join('\n'), 'Deployment Complete');
|
|
59
|
-
return { success: true };
|
|
60
152
|
},
|
|
61
153
|
};
|
|
154
|
+
// ============================================
|
|
62
155
|
// Status subcommand
|
|
156
|
+
// ============================================
|
|
63
157
|
const statusCommand = {
|
|
64
158
|
name: 'status',
|
|
65
159
|
description: 'Check deployment status across environments',
|
|
66
160
|
options: [
|
|
67
161
|
{ name: 'env', short: 'e', type: 'string', description: 'Specific environment to check' },
|
|
68
|
-
{ name: 'watch', short: 'w', type: 'boolean', description: 'Watch for changes' },
|
|
69
162
|
],
|
|
70
163
|
examples: [
|
|
71
164
|
{ command: 'claude-flow deployment status', description: 'Show all environments' },
|
|
72
165
|
{ command: 'claude-flow deployment status -e prod', description: 'Check production' },
|
|
73
166
|
],
|
|
74
167
|
action: async (ctx) => {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
168
|
+
try {
|
|
169
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
170
|
+
const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
|
|
171
|
+
output.writeln();
|
|
172
|
+
output.writeln(output.bold('Deployment Status'));
|
|
173
|
+
output.writeln();
|
|
174
|
+
// Active deployment
|
|
175
|
+
if (state.activeDeployment) {
|
|
176
|
+
const active = state.history.find(r => r.id === state.activeDeployment);
|
|
177
|
+
if (active) {
|
|
178
|
+
output.printInfo(`Active deployment: ${active.id} (v${active.version} on ${active.environment})`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
output.writeln(output.dim('No active deployment'));
|
|
183
|
+
}
|
|
184
|
+
// Environments table
|
|
185
|
+
const envEntries = Object.values(state.environments);
|
|
186
|
+
if (filterEnv) {
|
|
187
|
+
const env = state.environments[filterEnv];
|
|
188
|
+
if (!env) {
|
|
189
|
+
output.printWarning(`Environment '${filterEnv}' not found`);
|
|
190
|
+
return { success: true };
|
|
191
|
+
}
|
|
192
|
+
output.writeln();
|
|
193
|
+
output.writeln(output.bold('Environment'));
|
|
194
|
+
output.printTable({
|
|
195
|
+
columns: [
|
|
196
|
+
{ key: 'name', header: 'Name' },
|
|
197
|
+
{ key: 'type', header: 'Type' },
|
|
198
|
+
{ key: 'url', header: 'URL' },
|
|
199
|
+
{ key: 'createdAt', header: 'Created' },
|
|
200
|
+
],
|
|
201
|
+
data: [{ name: env.name, type: env.type, url: env.url || '-', createdAt: env.createdAt }],
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
else if (envEntries.length > 0) {
|
|
205
|
+
output.writeln();
|
|
206
|
+
output.writeln(output.bold('Environments'));
|
|
207
|
+
output.printTable({
|
|
208
|
+
columns: [
|
|
209
|
+
{ key: 'name', header: 'Name' },
|
|
210
|
+
{ key: 'type', header: 'Type' },
|
|
211
|
+
{ key: 'url', header: 'URL' },
|
|
212
|
+
{ key: 'createdAt', header: 'Created' },
|
|
213
|
+
],
|
|
214
|
+
data: envEntries.map(e => ({ name: e.name, type: e.type, url: e.url || '-', createdAt: e.createdAt })),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
output.writeln(output.dim('No environments configured'));
|
|
219
|
+
}
|
|
220
|
+
// Recent history (last 5)
|
|
221
|
+
let recent = [...state.history].reverse().slice(0, 5);
|
|
222
|
+
if (filterEnv) {
|
|
223
|
+
recent = recent.filter(r => r.environment === filterEnv);
|
|
224
|
+
}
|
|
225
|
+
if (recent.length > 0) {
|
|
226
|
+
output.writeln();
|
|
227
|
+
output.writeln(output.bold('Recent Deployments'));
|
|
228
|
+
output.printTable({
|
|
229
|
+
columns: [
|
|
230
|
+
{ key: 'id', header: 'ID' },
|
|
231
|
+
{ key: 'environment', header: 'Env' },
|
|
232
|
+
{ key: 'version', header: 'Version' },
|
|
233
|
+
{ key: 'status', header: 'Status' },
|
|
234
|
+
{ key: 'timestamp', header: 'Time' },
|
|
235
|
+
],
|
|
236
|
+
data: recent.map(r => ({ ...r })),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return { success: true };
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
243
|
+
output.printError('Status check failed', msg);
|
|
244
|
+
return { success: false, exitCode: 1 };
|
|
245
|
+
}
|
|
94
246
|
},
|
|
95
247
|
};
|
|
248
|
+
// ============================================
|
|
96
249
|
// Rollback subcommand
|
|
250
|
+
// ============================================
|
|
97
251
|
const rollbackCommand = {
|
|
98
252
|
name: 'rollback',
|
|
99
253
|
description: 'Rollback to previous deployment',
|
|
@@ -107,35 +261,82 @@ const rollbackCommand = {
|
|
|
107
261
|
{ command: 'claude-flow deployment rollback -e prod -v v3.0.0', description: 'Rollback to specific version' },
|
|
108
262
|
],
|
|
109
263
|
action: async (ctx) => {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
264
|
+
try {
|
|
265
|
+
const envName = String(ctx.flags['env'] || '');
|
|
266
|
+
if (!envName) {
|
|
267
|
+
output.printError('Environment is required', 'Use --env or -e to specify');
|
|
268
|
+
return { success: false, exitCode: 1 };
|
|
269
|
+
}
|
|
270
|
+
const targetVersion = ctx.flags['version'] ? String(ctx.flags['version']) : null;
|
|
271
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
272
|
+
// Find deployments for this environment in reverse chronological order
|
|
273
|
+
const envHistory = state.history
|
|
274
|
+
.filter(r => r.environment === envName && r.status === 'deployed')
|
|
275
|
+
.reverse();
|
|
276
|
+
if (envHistory.length < 2 && !targetVersion) {
|
|
277
|
+
output.printWarning('No previous deployment to rollback to');
|
|
278
|
+
return { success: false, exitCode: 1 };
|
|
279
|
+
}
|
|
280
|
+
let rollbackTo;
|
|
281
|
+
if (targetVersion) {
|
|
282
|
+
rollbackTo = envHistory.find(r => r.version === targetVersion);
|
|
283
|
+
if (!rollbackTo) {
|
|
284
|
+
output.printError(`Version '${targetVersion}' not found in deployment history for '${envName}'`);
|
|
285
|
+
return { success: false, exitCode: 1 };
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
else {
|
|
289
|
+
// Rollback to the deployment before the most recent one
|
|
290
|
+
rollbackTo = envHistory[1];
|
|
291
|
+
}
|
|
292
|
+
// Mark current active deployment for this env as rolled-back
|
|
293
|
+
const current = envHistory[0];
|
|
294
|
+
if (current) {
|
|
295
|
+
const idx = state.history.findIndex(r => r.id === current.id);
|
|
296
|
+
if (idx >= 0) {
|
|
297
|
+
state.history[idx].status = 'rolled-back';
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Create a new record for the rollback
|
|
301
|
+
const record = {
|
|
302
|
+
id: generateId(),
|
|
303
|
+
environment: envName,
|
|
304
|
+
version: rollbackTo.version,
|
|
305
|
+
status: 'deployed',
|
|
306
|
+
timestamp: new Date().toISOString(),
|
|
307
|
+
description: `Rollback from ${current?.version || 'unknown'} to ${rollbackTo.version}`,
|
|
308
|
+
};
|
|
309
|
+
state.history.push(record);
|
|
310
|
+
state.activeDeployment = record.id;
|
|
311
|
+
saveDeploymentState(ctx.cwd, state);
|
|
312
|
+
output.writeln();
|
|
313
|
+
output.printSuccess(`Rolled back ${envName} to version ${rollbackTo.version}`);
|
|
314
|
+
output.writeln();
|
|
315
|
+
output.printTable({
|
|
316
|
+
columns: [
|
|
317
|
+
{ key: 'field', header: 'Field' },
|
|
318
|
+
{ key: 'value', header: 'Value' },
|
|
319
|
+
],
|
|
320
|
+
data: [
|
|
321
|
+
{ field: 'Rollback ID', value: record.id },
|
|
322
|
+
{ field: 'Environment', value: envName },
|
|
323
|
+
{ field: 'From Version', value: current?.version || 'unknown' },
|
|
324
|
+
{ field: 'To Version', value: rollbackTo.version },
|
|
325
|
+
{ field: 'Timestamp', value: record.timestamp },
|
|
326
|
+
],
|
|
327
|
+
});
|
|
328
|
+
return { success: true, data: record };
|
|
115
329
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
output.writeln();
|
|
121
|
-
const spinner = output.createSpinner({ text: 'Initiating rollback...', spinner: 'dots' });
|
|
122
|
-
spinner.start();
|
|
123
|
-
const steps = ['Stopping current deployment', 'Restoring previous version', 'Running health checks', 'Updating DNS'];
|
|
124
|
-
for (const step of steps) {
|
|
125
|
-
spinner.setText(step + '...');
|
|
126
|
-
await new Promise(r => setTimeout(r, 400));
|
|
330
|
+
catch (err) {
|
|
331
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
332
|
+
output.printError('Rollback failed', msg);
|
|
333
|
+
return { success: false, exitCode: 1 };
|
|
127
334
|
}
|
|
128
|
-
spinner.succeed('Rollback complete');
|
|
129
|
-
output.writeln();
|
|
130
|
-
output.printBox([
|
|
131
|
-
`Environment: ${env}`,
|
|
132
|
-
`Rolled back to: ${version || 'v3.0.0-alpha.9'}`,
|
|
133
|
-
`Status: ${output.success('Active')}`,
|
|
134
|
-
].join('\n'), 'Rollback Complete');
|
|
135
|
-
return { success: true };
|
|
136
335
|
},
|
|
137
336
|
};
|
|
138
|
-
//
|
|
337
|
+
// ============================================
|
|
338
|
+
// History subcommand (logs)
|
|
339
|
+
// ============================================
|
|
139
340
|
const historyCommand = {
|
|
140
341
|
name: 'history',
|
|
141
342
|
description: 'View deployment history',
|
|
@@ -148,72 +349,153 @@ const historyCommand = {
|
|
|
148
349
|
{ command: 'claude-flow deployment history -e prod', description: 'Production history' },
|
|
149
350
|
],
|
|
150
351
|
action: async (ctx) => {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
{
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
352
|
+
try {
|
|
353
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
354
|
+
const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
|
|
355
|
+
const limit = Number(ctx.flags['limit']) || 10;
|
|
356
|
+
let records = [...state.history].reverse();
|
|
357
|
+
if (filterEnv) {
|
|
358
|
+
records = records.filter(r => r.environment === filterEnv);
|
|
359
|
+
}
|
|
360
|
+
records = records.slice(0, limit);
|
|
361
|
+
output.writeln();
|
|
362
|
+
output.writeln(output.bold('Deployment History'));
|
|
363
|
+
if (filterEnv) {
|
|
364
|
+
output.writeln(output.dim(`Filtered by environment: ${filterEnv}`));
|
|
365
|
+
}
|
|
366
|
+
output.writeln();
|
|
367
|
+
if (records.length === 0) {
|
|
368
|
+
output.writeln(output.dim('No deployment history found'));
|
|
369
|
+
return { success: true };
|
|
370
|
+
}
|
|
371
|
+
output.printTable({
|
|
372
|
+
columns: [
|
|
373
|
+
{ key: 'id', header: 'ID' },
|
|
374
|
+
{ key: 'environment', header: 'Env' },
|
|
375
|
+
{ key: 'version', header: 'Version' },
|
|
376
|
+
{ key: 'status', header: 'Status' },
|
|
377
|
+
{ key: 'timestamp', header: 'Time' },
|
|
378
|
+
{ key: 'description', header: 'Description' },
|
|
379
|
+
],
|
|
380
|
+
data: records.map(r => ({
|
|
381
|
+
...r,
|
|
382
|
+
description: r.description || '-',
|
|
383
|
+
})),
|
|
384
|
+
});
|
|
385
|
+
output.writeln();
|
|
386
|
+
output.writeln(output.dim(`Showing ${records.length} of ${state.history.length} total records`));
|
|
387
|
+
return { success: true };
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
391
|
+
output.printError('Failed to load history', msg);
|
|
392
|
+
return { success: false, exitCode: 1 };
|
|
393
|
+
}
|
|
173
394
|
},
|
|
174
395
|
};
|
|
396
|
+
// ============================================
|
|
175
397
|
// Environments subcommand
|
|
398
|
+
// ============================================
|
|
176
399
|
const environmentsCommand = {
|
|
177
400
|
name: 'environments',
|
|
178
401
|
description: 'Manage deployment environments',
|
|
179
402
|
aliases: ['envs'],
|
|
180
403
|
options: [
|
|
181
|
-
{ name: 'action', short: 'a', type: 'string', description: 'Action: list,
|
|
404
|
+
{ name: 'action', short: 'a', type: 'string', description: 'Action: list, add, remove', default: 'list' },
|
|
182
405
|
{ name: 'name', short: 'n', type: 'string', description: 'Environment name' },
|
|
406
|
+
{ name: 'type', short: 't', type: 'string', description: 'Environment type: local, staging, production', default: 'local' },
|
|
407
|
+
{ name: 'url', short: 'u', type: 'string', description: 'Environment URL' },
|
|
183
408
|
],
|
|
184
409
|
examples: [
|
|
185
410
|
{ command: 'claude-flow deployment environments', description: 'List environments' },
|
|
186
|
-
{ command: 'claude-flow deployment envs -a
|
|
411
|
+
{ command: 'claude-flow deployment envs -a add -n preview -t staging', description: 'Add environment' },
|
|
412
|
+
{ command: 'claude-flow deployment envs -a remove -n preview', description: 'Remove environment' },
|
|
187
413
|
],
|
|
188
414
|
action: async (ctx) => {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
415
|
+
try {
|
|
416
|
+
const action = String(ctx.flags['action'] || 'list');
|
|
417
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
418
|
+
if (action === 'list') {
|
|
419
|
+
const envs = Object.values(state.environments);
|
|
420
|
+
output.writeln();
|
|
421
|
+
output.writeln(output.bold('Deployment Environments'));
|
|
422
|
+
output.writeln();
|
|
423
|
+
if (envs.length === 0) {
|
|
424
|
+
output.writeln(output.dim('No environments configured. Use --action add to create one.'));
|
|
425
|
+
return { success: true };
|
|
426
|
+
}
|
|
427
|
+
output.printTable({
|
|
428
|
+
columns: [
|
|
429
|
+
{ key: 'name', header: 'Name' },
|
|
430
|
+
{ key: 'type', header: 'Type' },
|
|
431
|
+
{ key: 'url', header: 'URL' },
|
|
432
|
+
{ key: 'createdAt', header: 'Created' },
|
|
433
|
+
],
|
|
434
|
+
data: envs.map(e => ({ name: e.name, type: e.type, url: e.url || '-', createdAt: e.createdAt })),
|
|
435
|
+
});
|
|
436
|
+
return { success: true };
|
|
437
|
+
}
|
|
438
|
+
if (action === 'add') {
|
|
439
|
+
const name = ctx.flags['name'] ? String(ctx.flags['name']) : null;
|
|
440
|
+
if (!name) {
|
|
441
|
+
output.printError('Environment name is required', 'Use --name or -n to specify');
|
|
442
|
+
return { success: false, exitCode: 1 };
|
|
443
|
+
}
|
|
444
|
+
if (state.environments[name]) {
|
|
445
|
+
output.printWarning(`Environment '${name}' already exists`);
|
|
446
|
+
return { success: false, exitCode: 1 };
|
|
447
|
+
}
|
|
448
|
+
const envType = String(ctx.flags['type'] || 'local');
|
|
449
|
+
const url = ctx.flags['url'] ? String(ctx.flags['url']) : undefined;
|
|
450
|
+
state.environments[name] = {
|
|
451
|
+
name,
|
|
452
|
+
type: envType,
|
|
453
|
+
url,
|
|
454
|
+
createdAt: new Date().toISOString(),
|
|
455
|
+
};
|
|
456
|
+
saveDeploymentState(ctx.cwd, state);
|
|
457
|
+
output.writeln();
|
|
458
|
+
output.printSuccess(`Added environment '${name}' (${envType})`);
|
|
459
|
+
if (url) {
|
|
460
|
+
output.writeln(output.dim(` URL: ${url}`));
|
|
461
|
+
}
|
|
462
|
+
return { success: true };
|
|
463
|
+
}
|
|
464
|
+
if (action === 'remove') {
|
|
465
|
+
const name = ctx.flags['name'] ? String(ctx.flags['name']) : null;
|
|
466
|
+
if (!name) {
|
|
467
|
+
output.printError('Environment name is required', 'Use --name or -n to specify');
|
|
468
|
+
return { success: false, exitCode: 1 };
|
|
469
|
+
}
|
|
470
|
+
if (!state.environments[name]) {
|
|
471
|
+
output.printWarning(`Environment '${name}' not found`);
|
|
472
|
+
return { success: false, exitCode: 1 };
|
|
473
|
+
}
|
|
474
|
+
delete state.environments[name];
|
|
475
|
+
saveDeploymentState(ctx.cwd, state);
|
|
476
|
+
output.writeln();
|
|
477
|
+
output.printSuccess(`Removed environment '${name}'`);
|
|
478
|
+
return { success: true };
|
|
479
|
+
}
|
|
480
|
+
output.printError(`Unknown action '${action}'`, 'Valid actions: list, add, remove');
|
|
481
|
+
return { success: false, exitCode: 1 };
|
|
482
|
+
}
|
|
483
|
+
catch (err) {
|
|
484
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
485
|
+
output.printError('Environments command failed', msg);
|
|
486
|
+
return { success: false, exitCode: 1 };
|
|
487
|
+
}
|
|
207
488
|
},
|
|
208
489
|
};
|
|
490
|
+
// ============================================
|
|
209
491
|
// Logs subcommand
|
|
492
|
+
// ============================================
|
|
210
493
|
const logsCommand = {
|
|
211
494
|
name: 'logs',
|
|
212
495
|
description: 'View deployment logs',
|
|
213
496
|
options: [
|
|
214
497
|
{ name: 'deployment', short: 'd', type: 'string', description: 'Deployment ID' },
|
|
215
498
|
{ name: 'env', short: 'e', type: 'string', description: 'Environment' },
|
|
216
|
-
{ name: 'follow', short: 'f', type: 'boolean', description: 'Follow log output' },
|
|
217
499
|
{ name: 'lines', short: 'n', type: 'number', description: 'Number of lines', default: '50' },
|
|
218
500
|
],
|
|
219
501
|
examples: [
|
|
@@ -221,42 +503,142 @@ const logsCommand = {
|
|
|
221
503
|
{ command: 'claude-flow deployment logs -d dep-123', description: 'View specific deployment' },
|
|
222
504
|
],
|
|
223
505
|
action: async (ctx) => {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
506
|
+
try {
|
|
507
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
508
|
+
const filterEnv = ctx.flags['env'] ? String(ctx.flags['env']) : null;
|
|
509
|
+
const deploymentId = ctx.flags['deployment'] ? String(ctx.flags['deployment']) : null;
|
|
510
|
+
const limit = Number(ctx.flags['lines']) || 50;
|
|
511
|
+
output.writeln();
|
|
512
|
+
output.writeln(output.bold('Deployment Logs'));
|
|
513
|
+
output.writeln();
|
|
514
|
+
let records = [...state.history].reverse();
|
|
515
|
+
if (deploymentId) {
|
|
516
|
+
records = records.filter(r => r.id === deploymentId);
|
|
517
|
+
if (records.length === 0) {
|
|
518
|
+
output.printWarning(`Deployment '${deploymentId}' not found`);
|
|
519
|
+
return { success: false, exitCode: 1 };
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
if (filterEnv) {
|
|
523
|
+
records = records.filter(r => r.environment === filterEnv);
|
|
524
|
+
}
|
|
525
|
+
records = records.slice(0, limit);
|
|
526
|
+
if (records.length === 0) {
|
|
527
|
+
output.writeln(output.dim('No deployment logs found'));
|
|
528
|
+
return { success: true };
|
|
529
|
+
}
|
|
530
|
+
output.printTable({
|
|
531
|
+
columns: [
|
|
532
|
+
{ key: 'id', header: 'ID' },
|
|
533
|
+
{ key: 'environment', header: 'Env' },
|
|
534
|
+
{ key: 'version', header: 'Version' },
|
|
535
|
+
{ key: 'status', header: 'Status' },
|
|
536
|
+
{ key: 'timestamp', header: 'Time' },
|
|
537
|
+
{ key: 'description', header: 'Description' },
|
|
538
|
+
],
|
|
539
|
+
data: records.map(r => ({
|
|
540
|
+
...r,
|
|
541
|
+
description: r.description || '-',
|
|
542
|
+
})),
|
|
543
|
+
});
|
|
544
|
+
output.writeln();
|
|
545
|
+
output.writeln(output.dim(`${records.length} entries shown`));
|
|
546
|
+
return { success: true };
|
|
547
|
+
}
|
|
548
|
+
catch (err) {
|
|
549
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
550
|
+
output.printError('Failed to load logs', msg);
|
|
551
|
+
return { success: false, exitCode: 1 };
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
};
|
|
555
|
+
// ============================================
|
|
556
|
+
// Release subcommand
|
|
557
|
+
// ============================================
|
|
558
|
+
const releaseCommand = {
|
|
559
|
+
name: 'release',
|
|
560
|
+
description: 'Create a new release deployment',
|
|
561
|
+
options: [
|
|
562
|
+
{ name: 'version', short: 'v', type: 'string', description: 'Release version' },
|
|
563
|
+
{ name: 'env', short: 'e', type: 'string', description: 'Target environment', default: 'production' },
|
|
564
|
+
{ name: 'description', short: 'd', type: 'string', description: 'Release description' },
|
|
565
|
+
],
|
|
566
|
+
examples: [
|
|
567
|
+
{ command: 'claude-flow deployment release -v 3.5.0', description: 'Release version 3.5.0' },
|
|
568
|
+
{ command: 'claude-flow deployment release -v 3.5.0 -d "Major update"', description: 'Release with description' },
|
|
569
|
+
],
|
|
570
|
+
action: async (ctx) => {
|
|
571
|
+
try {
|
|
572
|
+
const envName = String(ctx.flags['env'] || 'production');
|
|
573
|
+
const description = ctx.flags['description'] ? String(ctx.flags['description']) : undefined;
|
|
574
|
+
let version = ctx.flags['version'] ? String(ctx.flags['version']) : null;
|
|
575
|
+
if (!version) {
|
|
576
|
+
const pkgVersion = readProjectVersion(ctx.cwd);
|
|
577
|
+
if (!pkgVersion) {
|
|
578
|
+
output.printError('Version is required', 'Use --version or -v, or ensure package.json has a version field');
|
|
579
|
+
return { success: false, exitCode: 1 };
|
|
580
|
+
}
|
|
581
|
+
version = pkgVersion;
|
|
582
|
+
}
|
|
583
|
+
const state = loadDeploymentState(ctx.cwd);
|
|
584
|
+
// Ensure environment exists
|
|
585
|
+
if (!state.environments[envName]) {
|
|
586
|
+
state.environments[envName] = {
|
|
587
|
+
name: envName,
|
|
588
|
+
type: envName === 'prod' || envName === 'production' ? 'production' : 'staging',
|
|
589
|
+
createdAt: new Date().toISOString(),
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
const record = {
|
|
593
|
+
id: generateId(),
|
|
594
|
+
environment: envName,
|
|
595
|
+
version,
|
|
596
|
+
status: 'deployed',
|
|
597
|
+
timestamp: new Date().toISOString(),
|
|
598
|
+
description: description || `Release ${version}`,
|
|
599
|
+
};
|
|
600
|
+
state.history.push(record);
|
|
601
|
+
state.activeDeployment = record.id;
|
|
602
|
+
saveDeploymentState(ctx.cwd, state);
|
|
603
|
+
output.writeln();
|
|
604
|
+
output.printSuccess(`Released version ${version} to ${envName}`);
|
|
605
|
+
output.writeln();
|
|
606
|
+
output.printTable({
|
|
607
|
+
columns: [
|
|
608
|
+
{ key: 'field', header: 'Field' },
|
|
609
|
+
{ key: 'value', header: 'Value' },
|
|
610
|
+
],
|
|
611
|
+
data: [
|
|
612
|
+
{ field: 'Release ID', value: record.id },
|
|
613
|
+
{ field: 'Environment', value: envName },
|
|
614
|
+
{ field: 'Version', value: version },
|
|
615
|
+
{ field: 'Status', value: record.status },
|
|
616
|
+
{ field: 'Timestamp', value: record.timestamp },
|
|
617
|
+
{ field: 'Description', value: record.description || '-' },
|
|
618
|
+
],
|
|
619
|
+
});
|
|
620
|
+
return { success: true, data: record };
|
|
621
|
+
}
|
|
622
|
+
catch (err) {
|
|
623
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
624
|
+
output.printError('Release failed', msg);
|
|
625
|
+
return { success: false, exitCode: 1 };
|
|
246
626
|
}
|
|
247
|
-
return { success: true };
|
|
248
627
|
},
|
|
249
628
|
};
|
|
629
|
+
// ============================================
|
|
250
630
|
// Main deployment command
|
|
631
|
+
// ============================================
|
|
251
632
|
export const deploymentCommand = {
|
|
252
633
|
name: 'deployment',
|
|
253
634
|
description: 'Deployment management, environments, rollbacks',
|
|
254
635
|
aliases: ['deploy'],
|
|
255
|
-
subcommands: [deployCommand, statusCommand, rollbackCommand, historyCommand, environmentsCommand, logsCommand],
|
|
636
|
+
subcommands: [deployCommand, statusCommand, rollbackCommand, historyCommand, environmentsCommand, logsCommand, releaseCommand],
|
|
256
637
|
examples: [
|
|
257
638
|
{ command: 'claude-flow deployment deploy -e prod', description: 'Deploy to production' },
|
|
258
639
|
{ command: 'claude-flow deployment status', description: 'Check all environments' },
|
|
259
640
|
{ command: 'claude-flow deployment rollback -e prod', description: 'Rollback production' },
|
|
641
|
+
{ command: 'claude-flow deployment release -v 3.5.0', description: 'Create a release' },
|
|
260
642
|
],
|
|
261
643
|
action: async () => {
|
|
262
644
|
output.writeln();
|
|
@@ -271,6 +653,7 @@ export const deploymentCommand = {
|
|
|
271
653
|
'history - View deployment history',
|
|
272
654
|
'environments - Manage deployment environments',
|
|
273
655
|
'logs - View deployment logs',
|
|
656
|
+
'release - Create a new release',
|
|
274
657
|
]);
|
|
275
658
|
output.writeln();
|
|
276
659
|
output.writeln('Features:');
|
|
@@ -281,7 +664,7 @@ export const deploymentCommand = {
|
|
|
281
664
|
'Deployment previews for PRs',
|
|
282
665
|
]);
|
|
283
666
|
output.writeln();
|
|
284
|
-
output.writeln(output.dim('Created with
|
|
667
|
+
output.writeln(output.dim('Created with love by ruv.io'));
|
|
285
668
|
return { success: true };
|
|
286
669
|
},
|
|
287
670
|
};
|