@claude-flow/cli 3.0.0-alpha.6 → 3.0.0-alpha.7
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/.agentic-flow/intelligence.json +4 -4
- package/.claude-flow/agents/store.json +16 -0
- package/.claude-flow/daemon-state.json +123 -0
- package/.claude-flow/hive-mind/state.json +51 -0
- package/.claude-flow/metrics/codebase-map.json +11 -0
- package/.claude-flow/metrics/consolidation.json +6 -0
- package/.claude-flow/metrics/performance.json +3 -3
- package/.claude-flow/metrics/security-audit.json +10 -0
- package/.claude-flow/metrics/task-metrics.json +3 -3
- package/.claude-flow/metrics/test-gaps.json +6 -0
- package/agents/architect.yaml +1 -1
- package/agents/coder.yaml +1 -1
- package/agents/reviewer.yaml +1 -1
- package/agents/security-architect.yaml +1 -1
- package/agents/tester.yaml +1 -1
- package/dist/src/commands/agent.d.ts.map +1 -1
- package/dist/src/commands/agent.js +42 -26
- package/dist/src/commands/agent.js.map +1 -1
- package/dist/src/commands/daemon.d.ts +8 -0
- package/dist/src/commands/daemon.d.ts.map +1 -0
- package/dist/src/commands/daemon.js +351 -0
- package/dist/src/commands/daemon.js.map +1 -0
- package/dist/src/commands/hive-mind.d.ts.map +1 -1
- package/dist/src/commands/hive-mind.js +252 -35
- package/dist/src/commands/hive-mind.js.map +1 -1
- package/dist/src/commands/index.d.ts +1 -0
- package/dist/src/commands/index.d.ts.map +1 -1
- package/dist/src/commands/index.js +4 -1
- package/dist/src/commands/index.js.map +1 -1
- package/dist/src/commands/start.js +2 -2
- package/dist/src/commands/start.js.map +1 -1
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +5 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/init/settings-generator.d.ts.map +1 -1
- package/dist/src/init/settings-generator.js +22 -12
- package/dist/src/init/settings-generator.js.map +1 -1
- package/dist/src/mcp-client.d.ts.map +1 -1
- package/dist/src/mcp-client.js +13 -1
- package/dist/src/mcp-client.js.map +1 -1
- package/dist/src/mcp-tools/agent-tools.d.ts +1 -1
- package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/agent-tools.js +350 -14
- package/dist/src/mcp-tools/agent-tools.js.map +1 -1
- package/dist/src/mcp-tools/config-tools.d.ts +1 -1
- package/dist/src/mcp-tools/config-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/config-tools.js +262 -15
- package/dist/src/mcp-tools/config-tools.js.map +1 -1
- package/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
- package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/hive-mind-tools.js +447 -0
- package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -0
- package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/hooks-tools.js +46 -7
- package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
- package/dist/src/mcp-tools/index.d.ts +2 -0
- package/dist/src/mcp-tools/index.d.ts.map +1 -1
- package/dist/src/mcp-tools/index.js +2 -0
- package/dist/src/mcp-tools/index.js.map +1 -1
- package/dist/src/mcp-tools/session-tools.d.ts +1 -1
- package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/session-tools.js +237 -22
- package/dist/src/mcp-tools/session-tools.js.map +1 -1
- package/dist/src/mcp-tools/task-tools.d.ts +1 -1
- package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
- package/dist/src/mcp-tools/task-tools.js +219 -17
- package/dist/src/mcp-tools/task-tools.js.map +1 -1
- package/dist/src/mcp-tools/workflow-tools.d.ts +8 -0
- package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -0
- package/dist/src/mcp-tools/workflow-tools.js +481 -0
- package/dist/src/mcp-tools/workflow-tools.js.map +1 -0
- package/dist/src/services/index.d.ts +7 -0
- package/dist/src/services/index.d.ts.map +1 -0
- package/dist/src/services/index.js +6 -0
- package/dist/src/services/index.js.map +1 -0
- package/dist/src/services/worker-daemon.d.ts +126 -0
- package/dist/src/services/worker-daemon.d.ts.map +1 -0
- package/dist/src/services/worker-daemon.js +464 -0
- package/dist/src/services/worker-daemon.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/commands/agent.ts +42 -28
- package/src/commands/daemon.ts +395 -0
- package/src/commands/hive-mind.ts +229 -63
- package/src/commands/index.ts +4 -1
- package/src/commands/start.ts +2 -2
- package/src/index.ts +5 -2
- package/src/init/settings-generator.ts +22 -12
- package/src/mcp-client.ts +13 -1
- package/src/mcp-tools/agent-tools.ts +388 -14
- package/src/mcp-tools/config-tools.ts +297 -15
- package/src/mcp-tools/hive-mind-tools.ts +521 -0
- package/src/mcp-tools/hooks-tools.ts +46 -7
- package/src/mcp-tools/index.ts +2 -0
- package/src/mcp-tools/session-tools.ts +280 -23
- package/src/mcp-tools/task-tools.ts +265 -20
- package/src/mcp-tools/workflow-tools.ts +573 -0
- package/src/services/index.ts +15 -0
- package/src/services/worker-daemon.ts +594 -0
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow MCP Tools for CLI
|
|
3
|
+
*
|
|
4
|
+
* Tool definitions for workflow automation and orchestration.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import type { MCPTool } from './types.js';
|
|
10
|
+
|
|
11
|
+
// Storage paths
|
|
12
|
+
const STORAGE_DIR = '.claude-flow';
|
|
13
|
+
const WORKFLOW_DIR = 'workflows';
|
|
14
|
+
const WORKFLOW_FILE = 'store.json';
|
|
15
|
+
|
|
16
|
+
interface WorkflowStep {
|
|
17
|
+
stepId: string;
|
|
18
|
+
name: string;
|
|
19
|
+
type: 'task' | 'condition' | 'parallel' | 'loop' | 'wait';
|
|
20
|
+
config: Record<string, unknown>;
|
|
21
|
+
status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped';
|
|
22
|
+
result?: unknown;
|
|
23
|
+
startedAt?: string;
|
|
24
|
+
completedAt?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
interface WorkflowRecord {
|
|
28
|
+
workflowId: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
steps: WorkflowStep[];
|
|
32
|
+
status: 'draft' | 'ready' | 'running' | 'paused' | 'completed' | 'failed';
|
|
33
|
+
currentStep: number;
|
|
34
|
+
variables: Record<string, unknown>;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
startedAt?: string;
|
|
37
|
+
completedAt?: string;
|
|
38
|
+
error?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
interface WorkflowStore {
|
|
42
|
+
workflows: Record<string, WorkflowRecord>;
|
|
43
|
+
templates: Record<string, WorkflowRecord>;
|
|
44
|
+
version: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function getWorkflowDir(): string {
|
|
48
|
+
return join(process.cwd(), STORAGE_DIR, WORKFLOW_DIR);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getWorkflowPath(): string {
|
|
52
|
+
return join(getWorkflowDir(), WORKFLOW_FILE);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function ensureWorkflowDir(): void {
|
|
56
|
+
const dir = getWorkflowDir();
|
|
57
|
+
if (!existsSync(dir)) {
|
|
58
|
+
mkdirSync(dir, { recursive: true });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function loadWorkflowStore(): WorkflowStore {
|
|
63
|
+
try {
|
|
64
|
+
const path = getWorkflowPath();
|
|
65
|
+
if (existsSync(path)) {
|
|
66
|
+
const data = readFileSync(path, 'utf-8');
|
|
67
|
+
return JSON.parse(data);
|
|
68
|
+
}
|
|
69
|
+
} catch {
|
|
70
|
+
// Return default store on error
|
|
71
|
+
}
|
|
72
|
+
return { workflows: {}, templates: {}, version: '3.0.0' };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function saveWorkflowStore(store: WorkflowStore): void {
|
|
76
|
+
ensureWorkflowDir();
|
|
77
|
+
writeFileSync(getWorkflowPath(), JSON.stringify(store, null, 2), 'utf-8');
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export const workflowTools: MCPTool[] = [
|
|
81
|
+
{
|
|
82
|
+
name: 'workflow/create',
|
|
83
|
+
description: 'Create a new workflow',
|
|
84
|
+
category: 'workflow',
|
|
85
|
+
inputSchema: {
|
|
86
|
+
type: 'object',
|
|
87
|
+
properties: {
|
|
88
|
+
name: { type: 'string', description: 'Workflow name' },
|
|
89
|
+
description: { type: 'string', description: 'Workflow description' },
|
|
90
|
+
steps: {
|
|
91
|
+
type: 'array',
|
|
92
|
+
description: 'Workflow steps',
|
|
93
|
+
items: {
|
|
94
|
+
type: 'object',
|
|
95
|
+
properties: {
|
|
96
|
+
name: { type: 'string' },
|
|
97
|
+
type: { type: 'string', enum: ['task', 'condition', 'parallel', 'loop', 'wait'] },
|
|
98
|
+
config: { type: 'object' },
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
variables: { type: 'object', description: 'Initial variables' },
|
|
103
|
+
},
|
|
104
|
+
required: ['name'],
|
|
105
|
+
},
|
|
106
|
+
handler: async (input) => {
|
|
107
|
+
const store = loadWorkflowStore();
|
|
108
|
+
const workflowId = `workflow-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
109
|
+
|
|
110
|
+
const steps: WorkflowStep[] = ((input.steps as Array<{name?: string; type?: string; config?: Record<string, unknown>}>) || []).map((s, i) => ({
|
|
111
|
+
stepId: `step-${i + 1}`,
|
|
112
|
+
name: s.name || `Step ${i + 1}`,
|
|
113
|
+
type: (s.type as WorkflowStep['type']) || 'task',
|
|
114
|
+
config: s.config || {} as Record<string, unknown>,
|
|
115
|
+
status: 'pending' as const,
|
|
116
|
+
}));
|
|
117
|
+
|
|
118
|
+
const workflow: WorkflowRecord = {
|
|
119
|
+
workflowId,
|
|
120
|
+
name: input.name as string,
|
|
121
|
+
description: input.description as string,
|
|
122
|
+
steps,
|
|
123
|
+
status: steps.length > 0 ? 'ready' : 'draft',
|
|
124
|
+
currentStep: 0,
|
|
125
|
+
variables: (input.variables as Record<string, unknown>) || {},
|
|
126
|
+
createdAt: new Date().toISOString(),
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
store.workflows[workflowId] = workflow;
|
|
130
|
+
saveWorkflowStore(store);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
workflowId,
|
|
134
|
+
name: workflow.name,
|
|
135
|
+
status: workflow.status,
|
|
136
|
+
stepCount: steps.length,
|
|
137
|
+
createdAt: workflow.createdAt,
|
|
138
|
+
};
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'workflow/execute',
|
|
143
|
+
description: 'Execute a workflow',
|
|
144
|
+
category: 'workflow',
|
|
145
|
+
inputSchema: {
|
|
146
|
+
type: 'object',
|
|
147
|
+
properties: {
|
|
148
|
+
workflowId: { type: 'string', description: 'Workflow ID to execute' },
|
|
149
|
+
variables: { type: 'object', description: 'Runtime variables to inject' },
|
|
150
|
+
startFromStep: { type: 'number', description: 'Step to start from (0-indexed)' },
|
|
151
|
+
},
|
|
152
|
+
required: ['workflowId'],
|
|
153
|
+
},
|
|
154
|
+
handler: async (input) => {
|
|
155
|
+
const store = loadWorkflowStore();
|
|
156
|
+
const workflowId = input.workflowId as string;
|
|
157
|
+
const workflow = store.workflows[workflowId];
|
|
158
|
+
|
|
159
|
+
if (!workflow) {
|
|
160
|
+
return { workflowId, error: 'Workflow not found' };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (workflow.status === 'running') {
|
|
164
|
+
return { workflowId, error: 'Workflow already running' };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Inject runtime variables
|
|
168
|
+
if (input.variables) {
|
|
169
|
+
workflow.variables = { ...workflow.variables, ...(input.variables as Record<string, unknown>) };
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
workflow.status = 'running';
|
|
173
|
+
workflow.startedAt = new Date().toISOString();
|
|
174
|
+
workflow.currentStep = (input.startFromStep as number) || 0;
|
|
175
|
+
|
|
176
|
+
// Execute steps (in real implementation, this would be async/event-driven)
|
|
177
|
+
const results: Array<{ stepId: string; status: string }> = [];
|
|
178
|
+
for (let i = workflow.currentStep; i < workflow.steps.length; i++) {
|
|
179
|
+
const step = workflow.steps[i];
|
|
180
|
+
step.status = 'running';
|
|
181
|
+
step.startedAt = new Date().toISOString();
|
|
182
|
+
|
|
183
|
+
// For now, mark as completed (real implementation would execute actual tasks)
|
|
184
|
+
step.status = 'completed';
|
|
185
|
+
step.completedAt = new Date().toISOString();
|
|
186
|
+
step.result = { executed: true, stepType: step.type };
|
|
187
|
+
|
|
188
|
+
results.push({ stepId: step.stepId, status: step.status });
|
|
189
|
+
workflow.currentStep = i + 1;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
workflow.status = 'completed';
|
|
193
|
+
workflow.completedAt = new Date().toISOString();
|
|
194
|
+
|
|
195
|
+
saveWorkflowStore(store);
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
workflowId,
|
|
199
|
+
status: workflow.status,
|
|
200
|
+
stepsExecuted: results.length,
|
|
201
|
+
results,
|
|
202
|
+
startedAt: workflow.startedAt,
|
|
203
|
+
completedAt: workflow.completedAt,
|
|
204
|
+
duration: new Date(workflow.completedAt).getTime() - new Date(workflow.startedAt!).getTime(),
|
|
205
|
+
};
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
name: 'workflow/status',
|
|
210
|
+
description: 'Get workflow status',
|
|
211
|
+
category: 'workflow',
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: 'object',
|
|
214
|
+
properties: {
|
|
215
|
+
workflowId: { type: 'string', description: 'Workflow ID' },
|
|
216
|
+
verbose: { type: 'boolean', description: 'Include step details' },
|
|
217
|
+
},
|
|
218
|
+
required: ['workflowId'],
|
|
219
|
+
},
|
|
220
|
+
handler: async (input) => {
|
|
221
|
+
const store = loadWorkflowStore();
|
|
222
|
+
const workflowId = input.workflowId as string;
|
|
223
|
+
const workflow = store.workflows[workflowId];
|
|
224
|
+
|
|
225
|
+
if (!workflow) {
|
|
226
|
+
return { workflowId, error: 'Workflow not found' };
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const completedSteps = workflow.steps.filter(s => s.status === 'completed').length;
|
|
230
|
+
const progress = workflow.steps.length > 0 ? (completedSteps / workflow.steps.length) * 100 : 0;
|
|
231
|
+
|
|
232
|
+
const status = {
|
|
233
|
+
workflowId: workflow.workflowId,
|
|
234
|
+
name: workflow.name,
|
|
235
|
+
status: workflow.status,
|
|
236
|
+
progress,
|
|
237
|
+
currentStep: workflow.currentStep,
|
|
238
|
+
totalSteps: workflow.steps.length,
|
|
239
|
+
completedSteps,
|
|
240
|
+
createdAt: workflow.createdAt,
|
|
241
|
+
startedAt: workflow.startedAt,
|
|
242
|
+
completedAt: workflow.completedAt,
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
if (input.verbose) {
|
|
246
|
+
return {
|
|
247
|
+
...status,
|
|
248
|
+
description: workflow.description,
|
|
249
|
+
variables: workflow.variables,
|
|
250
|
+
steps: workflow.steps.map(s => ({
|
|
251
|
+
stepId: s.stepId,
|
|
252
|
+
name: s.name,
|
|
253
|
+
type: s.type,
|
|
254
|
+
status: s.status,
|
|
255
|
+
startedAt: s.startedAt,
|
|
256
|
+
completedAt: s.completedAt,
|
|
257
|
+
})),
|
|
258
|
+
error: workflow.error,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
return status;
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
name: 'workflow/list',
|
|
267
|
+
description: 'List all workflows',
|
|
268
|
+
category: 'workflow',
|
|
269
|
+
inputSchema: {
|
|
270
|
+
type: 'object',
|
|
271
|
+
properties: {
|
|
272
|
+
status: { type: 'string', description: 'Filter by status' },
|
|
273
|
+
limit: { type: 'number', description: 'Max workflows to return' },
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
handler: async (input) => {
|
|
277
|
+
const store = loadWorkflowStore();
|
|
278
|
+
let workflows = Object.values(store.workflows);
|
|
279
|
+
|
|
280
|
+
// Apply filters
|
|
281
|
+
if (input.status) {
|
|
282
|
+
workflows = workflows.filter(w => w.status === input.status);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Sort by creation date (newest first)
|
|
286
|
+
workflows.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
|
|
287
|
+
|
|
288
|
+
// Apply limit
|
|
289
|
+
const limit = (input.limit as number) || 20;
|
|
290
|
+
workflows = workflows.slice(0, limit);
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
workflows: workflows.map(w => ({
|
|
294
|
+
workflowId: w.workflowId,
|
|
295
|
+
name: w.name,
|
|
296
|
+
status: w.status,
|
|
297
|
+
stepCount: w.steps.length,
|
|
298
|
+
createdAt: w.createdAt,
|
|
299
|
+
completedAt: w.completedAt,
|
|
300
|
+
})),
|
|
301
|
+
total: workflows.length,
|
|
302
|
+
filters: { status: input.status },
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
name: 'workflow/pause',
|
|
308
|
+
description: 'Pause a running workflow',
|
|
309
|
+
category: 'workflow',
|
|
310
|
+
inputSchema: {
|
|
311
|
+
type: 'object',
|
|
312
|
+
properties: {
|
|
313
|
+
workflowId: { type: 'string', description: 'Workflow ID' },
|
|
314
|
+
},
|
|
315
|
+
required: ['workflowId'],
|
|
316
|
+
},
|
|
317
|
+
handler: async (input) => {
|
|
318
|
+
const store = loadWorkflowStore();
|
|
319
|
+
const workflowId = input.workflowId as string;
|
|
320
|
+
const workflow = store.workflows[workflowId];
|
|
321
|
+
|
|
322
|
+
if (!workflow) {
|
|
323
|
+
return { workflowId, error: 'Workflow not found' };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (workflow.status !== 'running') {
|
|
327
|
+
return { workflowId, error: 'Workflow not running' };
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
workflow.status = 'paused';
|
|
331
|
+
saveWorkflowStore(store);
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
workflowId,
|
|
335
|
+
status: workflow.status,
|
|
336
|
+
pausedAt: new Date().toISOString(),
|
|
337
|
+
currentStep: workflow.currentStep,
|
|
338
|
+
};
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'workflow/resume',
|
|
343
|
+
description: 'Resume a paused workflow',
|
|
344
|
+
category: 'workflow',
|
|
345
|
+
inputSchema: {
|
|
346
|
+
type: 'object',
|
|
347
|
+
properties: {
|
|
348
|
+
workflowId: { type: 'string', description: 'Workflow ID' },
|
|
349
|
+
},
|
|
350
|
+
required: ['workflowId'],
|
|
351
|
+
},
|
|
352
|
+
handler: async (input) => {
|
|
353
|
+
const store = loadWorkflowStore();
|
|
354
|
+
const workflowId = input.workflowId as string;
|
|
355
|
+
const workflow = store.workflows[workflowId];
|
|
356
|
+
|
|
357
|
+
if (!workflow) {
|
|
358
|
+
return { workflowId, error: 'Workflow not found' };
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (workflow.status !== 'paused') {
|
|
362
|
+
return { workflowId, error: 'Workflow not paused' };
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
workflow.status = 'running';
|
|
366
|
+
saveWorkflowStore(store);
|
|
367
|
+
|
|
368
|
+
// Continue execution from current step
|
|
369
|
+
const results: Array<{ stepId: string; status: string }> = [];
|
|
370
|
+
for (let i = workflow.currentStep; i < workflow.steps.length; i++) {
|
|
371
|
+
const step = workflow.steps[i];
|
|
372
|
+
step.status = 'running';
|
|
373
|
+
step.startedAt = new Date().toISOString();
|
|
374
|
+
step.status = 'completed';
|
|
375
|
+
step.completedAt = new Date().toISOString();
|
|
376
|
+
step.result = { executed: true };
|
|
377
|
+
results.push({ stepId: step.stepId, status: step.status });
|
|
378
|
+
workflow.currentStep = i + 1;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
workflow.status = 'completed';
|
|
382
|
+
workflow.completedAt = new Date().toISOString();
|
|
383
|
+
saveWorkflowStore(store);
|
|
384
|
+
|
|
385
|
+
return {
|
|
386
|
+
workflowId,
|
|
387
|
+
status: workflow.status,
|
|
388
|
+
resumed: true,
|
|
389
|
+
stepsExecuted: results.length,
|
|
390
|
+
completedAt: workflow.completedAt,
|
|
391
|
+
};
|
|
392
|
+
},
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
name: 'workflow/cancel',
|
|
396
|
+
description: 'Cancel a workflow',
|
|
397
|
+
category: 'workflow',
|
|
398
|
+
inputSchema: {
|
|
399
|
+
type: 'object',
|
|
400
|
+
properties: {
|
|
401
|
+
workflowId: { type: 'string', description: 'Workflow ID' },
|
|
402
|
+
reason: { type: 'string', description: 'Cancellation reason' },
|
|
403
|
+
},
|
|
404
|
+
required: ['workflowId'],
|
|
405
|
+
},
|
|
406
|
+
handler: async (input) => {
|
|
407
|
+
const store = loadWorkflowStore();
|
|
408
|
+
const workflowId = input.workflowId as string;
|
|
409
|
+
const workflow = store.workflows[workflowId];
|
|
410
|
+
|
|
411
|
+
if (!workflow) {
|
|
412
|
+
return { workflowId, error: 'Workflow not found' };
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (workflow.status === 'completed' || workflow.status === 'failed') {
|
|
416
|
+
return { workflowId, error: 'Workflow already finished' };
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
workflow.status = 'failed';
|
|
420
|
+
workflow.error = (input.reason as string) || 'Cancelled by user';
|
|
421
|
+
workflow.completedAt = new Date().toISOString();
|
|
422
|
+
|
|
423
|
+
// Mark remaining steps as skipped
|
|
424
|
+
for (let i = workflow.currentStep; i < workflow.steps.length; i++) {
|
|
425
|
+
workflow.steps[i].status = 'skipped';
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
saveWorkflowStore(store);
|
|
429
|
+
|
|
430
|
+
return {
|
|
431
|
+
workflowId,
|
|
432
|
+
status: workflow.status,
|
|
433
|
+
cancelledAt: workflow.completedAt,
|
|
434
|
+
reason: workflow.error,
|
|
435
|
+
skippedSteps: workflow.steps.length - workflow.currentStep,
|
|
436
|
+
};
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'workflow/delete',
|
|
441
|
+
description: 'Delete a workflow',
|
|
442
|
+
category: 'workflow',
|
|
443
|
+
inputSchema: {
|
|
444
|
+
type: 'object',
|
|
445
|
+
properties: {
|
|
446
|
+
workflowId: { type: 'string', description: 'Workflow ID' },
|
|
447
|
+
},
|
|
448
|
+
required: ['workflowId'],
|
|
449
|
+
},
|
|
450
|
+
handler: async (input) => {
|
|
451
|
+
const store = loadWorkflowStore();
|
|
452
|
+
const workflowId = input.workflowId as string;
|
|
453
|
+
|
|
454
|
+
if (!store.workflows[workflowId]) {
|
|
455
|
+
return { workflowId, error: 'Workflow not found' };
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
const workflow = store.workflows[workflowId];
|
|
459
|
+
if (workflow.status === 'running') {
|
|
460
|
+
return { workflowId, error: 'Cannot delete running workflow' };
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
delete store.workflows[workflowId];
|
|
464
|
+
saveWorkflowStore(store);
|
|
465
|
+
|
|
466
|
+
return {
|
|
467
|
+
workflowId,
|
|
468
|
+
deleted: true,
|
|
469
|
+
deletedAt: new Date().toISOString(),
|
|
470
|
+
};
|
|
471
|
+
},
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
name: 'workflow/template',
|
|
475
|
+
description: 'Save workflow as template or create from template',
|
|
476
|
+
category: 'workflow',
|
|
477
|
+
inputSchema: {
|
|
478
|
+
type: 'object',
|
|
479
|
+
properties: {
|
|
480
|
+
action: { type: 'string', enum: ['save', 'create', 'list'], description: 'Template action' },
|
|
481
|
+
workflowId: { type: 'string', description: 'Workflow ID (for save)' },
|
|
482
|
+
templateId: { type: 'string', description: 'Template ID (for create)' },
|
|
483
|
+
templateName: { type: 'string', description: 'Template name (for save)' },
|
|
484
|
+
newName: { type: 'string', description: 'New workflow name (for create)' },
|
|
485
|
+
},
|
|
486
|
+
required: ['action'],
|
|
487
|
+
},
|
|
488
|
+
handler: async (input) => {
|
|
489
|
+
const store = loadWorkflowStore();
|
|
490
|
+
const action = input.action as string;
|
|
491
|
+
|
|
492
|
+
if (action === 'save') {
|
|
493
|
+
const workflow = store.workflows[input.workflowId as string];
|
|
494
|
+
if (!workflow) {
|
|
495
|
+
return { action, error: 'Workflow not found' };
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const templateId = `template-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
499
|
+
const template: WorkflowRecord = {
|
|
500
|
+
...workflow,
|
|
501
|
+
workflowId: templateId,
|
|
502
|
+
name: (input.templateName as string) || `${workflow.name} Template`,
|
|
503
|
+
status: 'draft',
|
|
504
|
+
currentStep: 0,
|
|
505
|
+
createdAt: new Date().toISOString(),
|
|
506
|
+
startedAt: undefined,
|
|
507
|
+
completedAt: undefined,
|
|
508
|
+
};
|
|
509
|
+
|
|
510
|
+
// Reset step statuses
|
|
511
|
+
template.steps = template.steps.map(s => ({
|
|
512
|
+
...s,
|
|
513
|
+
status: 'pending',
|
|
514
|
+
result: undefined,
|
|
515
|
+
startedAt: undefined,
|
|
516
|
+
completedAt: undefined,
|
|
517
|
+
}));
|
|
518
|
+
|
|
519
|
+
store.templates[templateId] = template;
|
|
520
|
+
saveWorkflowStore(store);
|
|
521
|
+
|
|
522
|
+
return {
|
|
523
|
+
action,
|
|
524
|
+
templateId,
|
|
525
|
+
name: template.name,
|
|
526
|
+
savedAt: new Date().toISOString(),
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
if (action === 'create') {
|
|
531
|
+
const template = store.templates[input.templateId as string];
|
|
532
|
+
if (!template) {
|
|
533
|
+
return { action, error: 'Template not found' };
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const workflowId = `workflow-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
537
|
+
const workflow: WorkflowRecord = {
|
|
538
|
+
...template,
|
|
539
|
+
workflowId,
|
|
540
|
+
name: (input.newName as string) || template.name.replace(' Template', ''),
|
|
541
|
+
status: 'ready',
|
|
542
|
+
createdAt: new Date().toISOString(),
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
store.workflows[workflowId] = workflow;
|
|
546
|
+
saveWorkflowStore(store);
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
action,
|
|
550
|
+
workflowId,
|
|
551
|
+
name: workflow.name,
|
|
552
|
+
fromTemplate: input.templateId,
|
|
553
|
+
createdAt: workflow.createdAt,
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (action === 'list') {
|
|
558
|
+
return {
|
|
559
|
+
action,
|
|
560
|
+
templates: Object.values(store.templates).map(t => ({
|
|
561
|
+
templateId: t.workflowId,
|
|
562
|
+
name: t.name,
|
|
563
|
+
stepCount: t.steps.length,
|
|
564
|
+
createdAt: t.createdAt,
|
|
565
|
+
})),
|
|
566
|
+
total: Object.keys(store.templates).length,
|
|
567
|
+
};
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return { action, error: 'Unknown action' };
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
];
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* V3 CLI Services Index
|
|
3
|
+
* Central registry for all background services
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export {
|
|
7
|
+
WorkerDaemon,
|
|
8
|
+
getDaemon,
|
|
9
|
+
startDaemon,
|
|
10
|
+
stopDaemon,
|
|
11
|
+
type WorkerType,
|
|
12
|
+
} from './worker-daemon.js';
|
|
13
|
+
|
|
14
|
+
// Re-export types
|
|
15
|
+
export type { default as WorkerDaemonType } from './worker-daemon.js';
|