@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.
Files changed (99) hide show
  1. package/.agentic-flow/intelligence.json +4 -4
  2. package/.claude-flow/agents/store.json +16 -0
  3. package/.claude-flow/daemon-state.json +123 -0
  4. package/.claude-flow/hive-mind/state.json +51 -0
  5. package/.claude-flow/metrics/codebase-map.json +11 -0
  6. package/.claude-flow/metrics/consolidation.json +6 -0
  7. package/.claude-flow/metrics/performance.json +3 -3
  8. package/.claude-flow/metrics/security-audit.json +10 -0
  9. package/.claude-flow/metrics/task-metrics.json +3 -3
  10. package/.claude-flow/metrics/test-gaps.json +6 -0
  11. package/agents/architect.yaml +1 -1
  12. package/agents/coder.yaml +1 -1
  13. package/agents/reviewer.yaml +1 -1
  14. package/agents/security-architect.yaml +1 -1
  15. package/agents/tester.yaml +1 -1
  16. package/dist/src/commands/agent.d.ts.map +1 -1
  17. package/dist/src/commands/agent.js +42 -26
  18. package/dist/src/commands/agent.js.map +1 -1
  19. package/dist/src/commands/daemon.d.ts +8 -0
  20. package/dist/src/commands/daemon.d.ts.map +1 -0
  21. package/dist/src/commands/daemon.js +351 -0
  22. package/dist/src/commands/daemon.js.map +1 -0
  23. package/dist/src/commands/hive-mind.d.ts.map +1 -1
  24. package/dist/src/commands/hive-mind.js +252 -35
  25. package/dist/src/commands/hive-mind.js.map +1 -1
  26. package/dist/src/commands/index.d.ts +1 -0
  27. package/dist/src/commands/index.d.ts.map +1 -1
  28. package/dist/src/commands/index.js +4 -1
  29. package/dist/src/commands/index.js.map +1 -1
  30. package/dist/src/commands/start.js +2 -2
  31. package/dist/src/commands/start.js.map +1 -1
  32. package/dist/src/index.d.ts.map +1 -1
  33. package/dist/src/index.js +5 -2
  34. package/dist/src/index.js.map +1 -1
  35. package/dist/src/init/settings-generator.d.ts.map +1 -1
  36. package/dist/src/init/settings-generator.js +22 -12
  37. package/dist/src/init/settings-generator.js.map +1 -1
  38. package/dist/src/mcp-client.d.ts.map +1 -1
  39. package/dist/src/mcp-client.js +13 -1
  40. package/dist/src/mcp-client.js.map +1 -1
  41. package/dist/src/mcp-tools/agent-tools.d.ts +1 -1
  42. package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
  43. package/dist/src/mcp-tools/agent-tools.js +350 -14
  44. package/dist/src/mcp-tools/agent-tools.js.map +1 -1
  45. package/dist/src/mcp-tools/config-tools.d.ts +1 -1
  46. package/dist/src/mcp-tools/config-tools.d.ts.map +1 -1
  47. package/dist/src/mcp-tools/config-tools.js +262 -15
  48. package/dist/src/mcp-tools/config-tools.js.map +1 -1
  49. package/dist/src/mcp-tools/hive-mind-tools.d.ts +8 -0
  50. package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -0
  51. package/dist/src/mcp-tools/hive-mind-tools.js +447 -0
  52. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -0
  53. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  54. package/dist/src/mcp-tools/hooks-tools.js +46 -7
  55. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  56. package/dist/src/mcp-tools/index.d.ts +2 -0
  57. package/dist/src/mcp-tools/index.d.ts.map +1 -1
  58. package/dist/src/mcp-tools/index.js +2 -0
  59. package/dist/src/mcp-tools/index.js.map +1 -1
  60. package/dist/src/mcp-tools/session-tools.d.ts +1 -1
  61. package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
  62. package/dist/src/mcp-tools/session-tools.js +237 -22
  63. package/dist/src/mcp-tools/session-tools.js.map +1 -1
  64. package/dist/src/mcp-tools/task-tools.d.ts +1 -1
  65. package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
  66. package/dist/src/mcp-tools/task-tools.js +219 -17
  67. package/dist/src/mcp-tools/task-tools.js.map +1 -1
  68. package/dist/src/mcp-tools/workflow-tools.d.ts +8 -0
  69. package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -0
  70. package/dist/src/mcp-tools/workflow-tools.js +481 -0
  71. package/dist/src/mcp-tools/workflow-tools.js.map +1 -0
  72. package/dist/src/services/index.d.ts +7 -0
  73. package/dist/src/services/index.d.ts.map +1 -0
  74. package/dist/src/services/index.js +6 -0
  75. package/dist/src/services/index.js.map +1 -0
  76. package/dist/src/services/worker-daemon.d.ts +126 -0
  77. package/dist/src/services/worker-daemon.d.ts.map +1 -0
  78. package/dist/src/services/worker-daemon.js +464 -0
  79. package/dist/src/services/worker-daemon.js.map +1 -0
  80. package/dist/tsconfig.tsbuildinfo +1 -1
  81. package/package.json +1 -1
  82. package/src/commands/agent.ts +42 -28
  83. package/src/commands/daemon.ts +395 -0
  84. package/src/commands/hive-mind.ts +229 -63
  85. package/src/commands/index.ts +4 -1
  86. package/src/commands/start.ts +2 -2
  87. package/src/index.ts +5 -2
  88. package/src/init/settings-generator.ts +22 -12
  89. package/src/mcp-client.ts +13 -1
  90. package/src/mcp-tools/agent-tools.ts +388 -14
  91. package/src/mcp-tools/config-tools.ts +297 -15
  92. package/src/mcp-tools/hive-mind-tools.ts +521 -0
  93. package/src/mcp-tools/hooks-tools.ts +46 -7
  94. package/src/mcp-tools/index.ts +2 -0
  95. package/src/mcp-tools/session-tools.ts +280 -23
  96. package/src/mcp-tools/task-tools.ts +265 -20
  97. package/src/mcp-tools/workflow-tools.ts +573 -0
  98. package/src/services/index.ts +15 -0
  99. package/src/services/worker-daemon.ts +594 -0
@@ -1,11 +1,122 @@
1
1
  /**
2
2
  * Session MCP Tools for CLI
3
3
  *
4
- * Tool definitions for session management.
4
+ * Tool definitions for session management with file persistence.
5
5
  */
6
6
 
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync, unlinkSync, statSync } from 'node:fs';
8
+ import { join } from 'node:path';
7
9
  import type { MCPTool } from './types.js';
8
10
 
11
+ // Storage paths
12
+ const STORAGE_DIR = '.claude-flow';
13
+ const SESSION_DIR = 'sessions';
14
+
15
+ interface SessionRecord {
16
+ sessionId: string;
17
+ name: string;
18
+ description?: string;
19
+ savedAt: string;
20
+ stats: {
21
+ tasks: number;
22
+ agents: number;
23
+ memoryEntries: number;
24
+ totalSize: number;
25
+ };
26
+ data?: {
27
+ memory?: Record<string, unknown>;
28
+ tasks?: Record<string, unknown>;
29
+ agents?: Record<string, unknown>;
30
+ };
31
+ }
32
+
33
+ function getSessionDir(): string {
34
+ return join(process.cwd(), STORAGE_DIR, SESSION_DIR);
35
+ }
36
+
37
+ function getSessionPath(sessionId: string): string {
38
+ // Sanitize sessionId to prevent path traversal
39
+ const safeId = sessionId.replace(/[^a-zA-Z0-9_-]/g, '_');
40
+ return join(getSessionDir(), `${safeId}.json`);
41
+ }
42
+
43
+ function ensureSessionDir(): void {
44
+ const dir = getSessionDir();
45
+ if (!existsSync(dir)) {
46
+ mkdirSync(dir, { recursive: true });
47
+ }
48
+ }
49
+
50
+ function loadSession(sessionId: string): SessionRecord | null {
51
+ try {
52
+ const path = getSessionPath(sessionId);
53
+ if (existsSync(path)) {
54
+ const data = readFileSync(path, 'utf-8');
55
+ return JSON.parse(data);
56
+ }
57
+ } catch {
58
+ // Return null on error
59
+ }
60
+ return null;
61
+ }
62
+
63
+ function saveSession(session: SessionRecord): void {
64
+ ensureSessionDir();
65
+ writeFileSync(getSessionPath(session.sessionId), JSON.stringify(session, null, 2), 'utf-8');
66
+ }
67
+
68
+ function listSessions(): SessionRecord[] {
69
+ ensureSessionDir();
70
+ const dir = getSessionDir();
71
+ const files = readdirSync(dir).filter(f => f.endsWith('.json'));
72
+
73
+ const sessions: SessionRecord[] = [];
74
+ for (const file of files) {
75
+ try {
76
+ const data = readFileSync(join(dir, file), 'utf-8');
77
+ sessions.push(JSON.parse(data));
78
+ } catch {
79
+ // Skip invalid files
80
+ }
81
+ }
82
+
83
+ return sessions;
84
+ }
85
+
86
+ // Load related stores for session data
87
+ function loadRelatedStores(options: { includeMemory?: boolean; includeTasks?: boolean; includeAgents?: boolean }) {
88
+ const data: SessionRecord['data'] = {};
89
+
90
+ if (options.includeMemory) {
91
+ try {
92
+ const memoryPath = join(process.cwd(), STORAGE_DIR, 'memory', 'store.json');
93
+ if (existsSync(memoryPath)) {
94
+ data.memory = JSON.parse(readFileSync(memoryPath, 'utf-8'));
95
+ }
96
+ } catch { /* ignore */ }
97
+ }
98
+
99
+ if (options.includeTasks) {
100
+ try {
101
+ const taskPath = join(process.cwd(), STORAGE_DIR, 'tasks', 'store.json');
102
+ if (existsSync(taskPath)) {
103
+ data.tasks = JSON.parse(readFileSync(taskPath, 'utf-8'));
104
+ }
105
+ } catch { /* ignore */ }
106
+ }
107
+
108
+ if (options.includeAgents) {
109
+ try {
110
+ const agentPath = join(process.cwd(), STORAGE_DIR, 'agents', 'store.json');
111
+ if (existsSync(agentPath)) {
112
+ data.agents = JSON.parse(readFileSync(agentPath, 'utf-8'));
113
+ }
114
+ } catch { /* ignore */ }
115
+ }
116
+
117
+ return data;
118
+ }
119
+
9
120
  export const sessionTools: MCPTool[] = [
10
121
  {
11
122
  name: 'session/save',
@@ -23,17 +134,44 @@ export const sessionTools: MCPTool[] = [
23
134
  required: ['name'],
24
135
  },
25
136
  handler: async (input) => {
26
- const sessionId = `session-${Date.now()}`;
27
- return {
137
+ const sessionId = `session-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
138
+
139
+ // Load related data based on options
140
+ const data = loadRelatedStores({
141
+ includeMemory: input.includeMemory as boolean,
142
+ includeTasks: input.includeTasks as boolean,
143
+ includeAgents: input.includeAgents as boolean,
144
+ });
145
+
146
+ // Calculate stats
147
+ const stats = {
148
+ tasks: data.tasks ? Object.keys((data.tasks as { tasks?: object }).tasks || {}).length : 0,
149
+ agents: data.agents ? Object.keys((data.agents as { agents?: object }).agents || {}).length : 0,
150
+ memoryEntries: data.memory ? Object.keys((data.memory as { entries?: object }).entries || {}).length : 0,
151
+ totalSize: 0,
152
+ };
153
+
154
+ const session: SessionRecord = {
28
155
  sessionId,
29
- name: input.name,
156
+ name: input.name as string,
157
+ description: input.description as string,
30
158
  savedAt: new Date().toISOString(),
31
- stats: {
32
- tasks: 0,
33
- agents: 0,
34
- memoryEntries: 0,
35
- totalSize: 0,
36
- },
159
+ stats,
160
+ data: Object.keys(data).length > 0 ? data : undefined,
161
+ };
162
+
163
+ // Calculate size
164
+ const sessionJson = JSON.stringify(session);
165
+ session.stats.totalSize = Buffer.byteLength(sessionJson, 'utf-8');
166
+
167
+ saveSession(session);
168
+
169
+ return {
170
+ sessionId,
171
+ name: session.name,
172
+ savedAt: session.savedAt,
173
+ stats: session.stats,
174
+ path: getSessionPath(sessionId),
37
175
  };
38
176
  },
39
177
  },
@@ -49,15 +187,59 @@ export const sessionTools: MCPTool[] = [
49
187
  },
50
188
  },
51
189
  handler: async (input) => {
190
+ let session: SessionRecord | null = null;
191
+
192
+ // Try to find by sessionId first
193
+ if (input.sessionId) {
194
+ session = loadSession(input.sessionId as string);
195
+ }
196
+
197
+ // Try to find by name if sessionId not found
198
+ if (!session && input.name) {
199
+ const sessions = listSessions();
200
+ session = sessions.find(s => s.name === input.name) || null;
201
+ }
202
+
203
+ // Try to find latest if no params
204
+ if (!session && !input.sessionId && !input.name) {
205
+ const sessions = listSessions();
206
+ if (sessions.length > 0) {
207
+ sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
208
+ session = sessions[0];
209
+ }
210
+ }
211
+
212
+ if (session) {
213
+ // Restore data to respective stores
214
+ if (session.data?.memory) {
215
+ const memoryDir = join(process.cwd(), STORAGE_DIR, 'memory');
216
+ if (!existsSync(memoryDir)) mkdirSync(memoryDir, { recursive: true });
217
+ writeFileSync(join(memoryDir, 'store.json'), JSON.stringify(session.data.memory, null, 2), 'utf-8');
218
+ }
219
+ if (session.data?.tasks) {
220
+ const taskDir = join(process.cwd(), STORAGE_DIR, 'tasks');
221
+ if (!existsSync(taskDir)) mkdirSync(taskDir, { recursive: true });
222
+ writeFileSync(join(taskDir, 'store.json'), JSON.stringify(session.data.tasks, null, 2), 'utf-8');
223
+ }
224
+ if (session.data?.agents) {
225
+ const agentDir = join(process.cwd(), STORAGE_DIR, 'agents');
226
+ if (!existsSync(agentDir)) mkdirSync(agentDir, { recursive: true });
227
+ writeFileSync(join(agentDir, 'store.json'), JSON.stringify(session.data.agents, null, 2), 'utf-8');
228
+ }
229
+
230
+ return {
231
+ sessionId: session.sessionId,
232
+ name: session.name,
233
+ restored: true,
234
+ restoredAt: new Date().toISOString(),
235
+ stats: session.stats,
236
+ };
237
+ }
238
+
52
239
  return {
53
240
  sessionId: input.sessionId || input.name || 'latest',
54
- restored: true,
55
- restoredAt: new Date().toISOString(),
56
- stats: {
57
- tasks: 0,
58
- agents: 0,
59
- memoryEntries: 0,
60
- },
241
+ restored: false,
242
+ error: 'Session not found',
61
243
  };
62
244
  },
63
245
  },
@@ -73,10 +255,32 @@ export const sessionTools: MCPTool[] = [
73
255
  },
74
256
  },
75
257
  handler: async (input) => {
258
+ let sessions = listSessions();
259
+
260
+ // Sort
261
+ const sortBy = (input.sortBy as string) || 'date';
262
+ if (sortBy === 'date') {
263
+ sessions.sort((a, b) => new Date(b.savedAt).getTime() - new Date(a.savedAt).getTime());
264
+ } else if (sortBy === 'name') {
265
+ sessions.sort((a, b) => a.name.localeCompare(b.name));
266
+ } else if (sortBy === 'size') {
267
+ sessions.sort((a, b) => b.stats.totalSize - a.stats.totalSize);
268
+ }
269
+
270
+ // Apply limit
271
+ const limit = (input.limit as number) || 10;
272
+ sessions = sessions.slice(0, limit);
273
+
76
274
  return {
77
- sessions: [],
78
- total: 0,
79
- limit: (input.limit as number) || 10,
275
+ sessions: sessions.map(s => ({
276
+ sessionId: s.sessionId,
277
+ name: s.name,
278
+ description: s.description,
279
+ savedAt: s.savedAt,
280
+ stats: s.stats,
281
+ })),
282
+ total: sessions.length,
283
+ limit,
80
284
  };
81
285
  },
82
286
  },
@@ -92,10 +296,63 @@ export const sessionTools: MCPTool[] = [
92
296
  required: ['sessionId'],
93
297
  },
94
298
  handler: async (input) => {
299
+ const sessionId = input.sessionId as string;
300
+ const path = getSessionPath(sessionId);
301
+
302
+ if (existsSync(path)) {
303
+ unlinkSync(path);
304
+ return {
305
+ sessionId,
306
+ deleted: true,
307
+ deletedAt: new Date().toISOString(),
308
+ };
309
+ }
310
+
95
311
  return {
96
- sessionId: input.sessionId,
97
- deleted: true,
98
- deletedAt: new Date().toISOString(),
312
+ sessionId,
313
+ deleted: false,
314
+ error: 'Session not found',
315
+ };
316
+ },
317
+ },
318
+ {
319
+ name: 'session/info',
320
+ description: 'Get detailed session information',
321
+ category: 'session',
322
+ inputSchema: {
323
+ type: 'object',
324
+ properties: {
325
+ sessionId: { type: 'string', description: 'Session ID' },
326
+ },
327
+ required: ['sessionId'],
328
+ },
329
+ handler: async (input) => {
330
+ const sessionId = input.sessionId as string;
331
+ const session = loadSession(sessionId);
332
+
333
+ if (session) {
334
+ const path = getSessionPath(sessionId);
335
+ const stat = statSync(path);
336
+
337
+ return {
338
+ sessionId: session.sessionId,
339
+ name: session.name,
340
+ description: session.description,
341
+ savedAt: session.savedAt,
342
+ stats: session.stats,
343
+ fileSize: stat.size,
344
+ path,
345
+ hasData: {
346
+ memory: !!session.data?.memory,
347
+ tasks: !!session.data?.tasks,
348
+ agents: !!session.data?.agents,
349
+ },
350
+ };
351
+ }
352
+
353
+ return {
354
+ sessionId,
355
+ error: 'Session not found',
99
356
  };
100
357
  },
101
358
  },
@@ -1,11 +1,71 @@
1
1
  /**
2
2
  * Task MCP Tools for CLI
3
3
  *
4
- * Tool definitions for task management.
4
+ * Tool definitions for task management with file persistence.
5
5
  */
6
6
 
7
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
8
+ import { join } from 'node:path';
7
9
  import type { MCPTool } from './types.js';
8
10
 
11
+ // Storage paths
12
+ const STORAGE_DIR = '.claude-flow';
13
+ const TASK_DIR = 'tasks';
14
+ const TASK_FILE = 'store.json';
15
+
16
+ interface TaskRecord {
17
+ taskId: string;
18
+ type: string;
19
+ description: string;
20
+ priority: 'low' | 'normal' | 'high' | 'critical';
21
+ status: 'pending' | 'in_progress' | 'completed' | 'failed' | 'cancelled';
22
+ progress: number;
23
+ assignedTo: string[];
24
+ tags: string[];
25
+ createdAt: string;
26
+ startedAt: string | null;
27
+ completedAt: string | null;
28
+ result?: Record<string, unknown>;
29
+ }
30
+
31
+ interface TaskStore {
32
+ tasks: Record<string, TaskRecord>;
33
+ version: string;
34
+ }
35
+
36
+ function getTaskDir(): string {
37
+ return join(process.cwd(), STORAGE_DIR, TASK_DIR);
38
+ }
39
+
40
+ function getTaskPath(): string {
41
+ return join(getTaskDir(), TASK_FILE);
42
+ }
43
+
44
+ function ensureTaskDir(): void {
45
+ const dir = getTaskDir();
46
+ if (!existsSync(dir)) {
47
+ mkdirSync(dir, { recursive: true });
48
+ }
49
+ }
50
+
51
+ function loadTaskStore(): TaskStore {
52
+ try {
53
+ const path = getTaskPath();
54
+ if (existsSync(path)) {
55
+ const data = readFileSync(path, 'utf-8');
56
+ return JSON.parse(data);
57
+ }
58
+ } catch {
59
+ // Return empty store on error
60
+ }
61
+ return { tasks: {}, version: '3.0.0' };
62
+ }
63
+
64
+ function saveTaskStore(store: TaskStore): void {
65
+ ensureTaskDir();
66
+ writeFileSync(getTaskPath(), JSON.stringify(store, null, 2), 'utf-8');
67
+ }
68
+
9
69
  export const taskTools: MCPTool[] = [
10
70
  {
11
71
  name: 'task/create',
@@ -23,16 +83,35 @@ export const taskTools: MCPTool[] = [
23
83
  required: ['type', 'description'],
24
84
  },
25
85
  handler: async (input) => {
26
- const taskId = `task-${Date.now()}`;
27
- return {
86
+ const store = loadTaskStore();
87
+ const taskId = `task-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
88
+
89
+ const task: TaskRecord = {
28
90
  taskId,
29
- type: input.type,
30
- description: input.description,
31
- priority: (input.priority as string) || 'normal',
91
+ type: input.type as string,
92
+ description: input.description as string,
93
+ priority: (input.priority as TaskRecord['priority']) || 'normal',
32
94
  status: 'pending',
33
- createdAt: new Date().toISOString(),
95
+ progress: 0,
34
96
  assignedTo: (input.assignTo as string[]) || [],
35
97
  tags: (input.tags as string[]) || [],
98
+ createdAt: new Date().toISOString(),
99
+ startedAt: null,
100
+ completedAt: null,
101
+ };
102
+
103
+ store.tasks[taskId] = task;
104
+ saveTaskStore(store);
105
+
106
+ return {
107
+ taskId,
108
+ type: task.type,
109
+ description: task.description,
110
+ priority: task.priority,
111
+ status: task.status,
112
+ createdAt: task.createdAt,
113
+ assignedTo: task.assignedTo,
114
+ tags: task.tags,
36
115
  };
37
116
  },
38
117
  },
@@ -48,13 +127,30 @@ export const taskTools: MCPTool[] = [
48
127
  required: ['taskId'],
49
128
  },
50
129
  handler: async (input) => {
130
+ const store = loadTaskStore();
131
+ const taskId = input.taskId as string;
132
+ const task = store.tasks[taskId];
133
+
134
+ if (task) {
135
+ return {
136
+ taskId: task.taskId,
137
+ type: task.type,
138
+ description: task.description,
139
+ status: task.status,
140
+ progress: task.progress,
141
+ priority: task.priority,
142
+ assignedTo: task.assignedTo,
143
+ tags: task.tags,
144
+ createdAt: task.createdAt,
145
+ startedAt: task.startedAt,
146
+ completedAt: task.completedAt,
147
+ };
148
+ }
149
+
51
150
  return {
52
- taskId: input.taskId,
53
- status: 'pending',
54
- progress: 0,
55
- assignedTo: [],
56
- startedAt: null,
57
- completedAt: null,
151
+ taskId,
152
+ status: 'not_found',
153
+ error: 'Task not found',
58
154
  };
59
155
  },
60
156
  },
@@ -68,13 +164,55 @@ export const taskTools: MCPTool[] = [
68
164
  status: { type: 'string', description: 'Filter by status' },
69
165
  type: { type: 'string', description: 'Filter by type' },
70
166
  assignedTo: { type: 'string', description: 'Filter by assigned agent' },
167
+ priority: { type: 'string', description: 'Filter by priority' },
168
+ limit: { type: 'number', description: 'Max tasks to return' },
71
169
  },
72
170
  },
73
171
  handler: async (input) => {
172
+ const store = loadTaskStore();
173
+ let tasks = Object.values(store.tasks);
174
+
175
+ // Apply filters
176
+ if (input.status) {
177
+ // Support comma-separated status values
178
+ const statuses = (input.status as string).split(',').map(s => s.trim());
179
+ tasks = tasks.filter(t => statuses.includes(t.status));
180
+ }
181
+ if (input.type) {
182
+ tasks = tasks.filter(t => t.type === input.type);
183
+ }
184
+ if (input.assignedTo) {
185
+ tasks = tasks.filter(t => t.assignedTo.includes(input.assignedTo as string));
186
+ }
187
+ if (input.priority) {
188
+ tasks = tasks.filter(t => t.priority === input.priority);
189
+ }
190
+
191
+ // Sort by creation date (newest first)
192
+ tasks.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
193
+
194
+ // Apply limit
195
+ const limit = (input.limit as number) || 50;
196
+ tasks = tasks.slice(0, limit);
197
+
74
198
  return {
75
- tasks: [],
76
- total: 0,
77
- filters: input,
199
+ tasks: tasks.map(t => ({
200
+ taskId: t.taskId,
201
+ type: t.type,
202
+ description: t.description,
203
+ status: t.status,
204
+ progress: t.progress,
205
+ priority: t.priority,
206
+ assignedTo: t.assignedTo,
207
+ createdAt: t.createdAt,
208
+ })),
209
+ total: tasks.length,
210
+ filters: {
211
+ status: input.status,
212
+ type: input.type,
213
+ assignedTo: input.assignedTo,
214
+ priority: input.priority,
215
+ },
78
216
  };
79
217
  },
80
218
  },
@@ -91,11 +229,118 @@ export const taskTools: MCPTool[] = [
91
229
  required: ['taskId'],
92
230
  },
93
231
  handler: async (input) => {
232
+ const store = loadTaskStore();
233
+ const taskId = input.taskId as string;
234
+ const task = store.tasks[taskId];
235
+
236
+ if (task) {
237
+ task.status = 'completed';
238
+ task.progress = 100;
239
+ task.completedAt = new Date().toISOString();
240
+ task.result = (input.result as Record<string, unknown>) || {};
241
+ saveTaskStore(store);
242
+
243
+ return {
244
+ taskId: task.taskId,
245
+ status: task.status,
246
+ completedAt: task.completedAt,
247
+ result: task.result,
248
+ };
249
+ }
250
+
251
+ return {
252
+ taskId,
253
+ status: 'not_found',
254
+ error: 'Task not found',
255
+ };
256
+ },
257
+ },
258
+ {
259
+ name: 'task/update',
260
+ description: 'Update task status or progress',
261
+ category: 'task',
262
+ inputSchema: {
263
+ type: 'object',
264
+ properties: {
265
+ taskId: { type: 'string', description: 'Task ID' },
266
+ status: { type: 'string', description: 'New status' },
267
+ progress: { type: 'number', description: 'Progress percentage (0-100)' },
268
+ assignTo: { type: 'array', items: { type: 'string' }, description: 'Agent IDs to assign' },
269
+ },
270
+ required: ['taskId'],
271
+ },
272
+ handler: async (input) => {
273
+ const store = loadTaskStore();
274
+ const taskId = input.taskId as string;
275
+ const task = store.tasks[taskId];
276
+
277
+ if (task) {
278
+ if (input.status) {
279
+ const newStatus = input.status as TaskRecord['status'];
280
+ task.status = newStatus;
281
+ if (newStatus === 'in_progress' && !task.startedAt) {
282
+ task.startedAt = new Date().toISOString();
283
+ }
284
+ }
285
+ if (typeof input.progress === 'number') {
286
+ task.progress = Math.min(100, Math.max(0, input.progress as number));
287
+ }
288
+ if (input.assignTo) {
289
+ task.assignedTo = input.assignTo as string[];
290
+ }
291
+ saveTaskStore(store);
292
+
293
+ return {
294
+ success: true,
295
+ taskId: task.taskId,
296
+ status: task.status,
297
+ progress: task.progress,
298
+ assignedTo: task.assignedTo,
299
+ };
300
+ }
301
+
302
+ return {
303
+ success: false,
304
+ taskId,
305
+ error: 'Task not found',
306
+ };
307
+ },
308
+ },
309
+ {
310
+ name: 'task/cancel',
311
+ description: 'Cancel a task',
312
+ category: 'task',
313
+ inputSchema: {
314
+ type: 'object',
315
+ properties: {
316
+ taskId: { type: 'string', description: 'Task ID' },
317
+ reason: { type: 'string', description: 'Cancellation reason' },
318
+ },
319
+ required: ['taskId'],
320
+ },
321
+ handler: async (input) => {
322
+ const store = loadTaskStore();
323
+ const taskId = input.taskId as string;
324
+ const task = store.tasks[taskId];
325
+
326
+ if (task) {
327
+ task.status = 'cancelled';
328
+ task.completedAt = new Date().toISOString();
329
+ task.result = { cancelReason: input.reason || 'Cancelled by user' };
330
+ saveTaskStore(store);
331
+
332
+ return {
333
+ success: true,
334
+ taskId: task.taskId,
335
+ status: task.status,
336
+ cancelledAt: task.completedAt,
337
+ };
338
+ }
339
+
94
340
  return {
95
- taskId: input.taskId,
96
- status: 'completed',
97
- completedAt: new Date().toISOString(),
98
- result: input.result || {},
341
+ success: false,
342
+ taskId,
343
+ error: 'Task not found',
99
344
  };
100
345
  },
101
346
  },