opencode-swarm-plugin 0.11.2 → 0.12.0
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/.beads/issues.jsonl +6 -0
- package/README.md +8 -8
- package/bin/swarm.ts +200 -14
- package/dist/index.js +52 -1
- package/dist/plugin.js +51 -1
- package/examples/plugin-wrapper-template.ts +723 -0
- package/package.json +1 -1
- package/src/agent-mail.ts +106 -3
- package/src/index.ts +22 -0
|
@@ -0,0 +1,723 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Swarm Plugin Wrapper
|
|
3
|
+
*
|
|
4
|
+
* This is a thin wrapper that shells out to the `swarm` CLI for all tool execution.
|
|
5
|
+
* Generated by: swarm setup
|
|
6
|
+
*
|
|
7
|
+
* The plugin only depends on @opencode-ai/plugin (provided by OpenCode).
|
|
8
|
+
* All tool logic lives in the npm package - this just bridges to it.
|
|
9
|
+
*
|
|
10
|
+
* Environment variables:
|
|
11
|
+
* - OPENCODE_SESSION_ID: Passed to CLI for session state persistence
|
|
12
|
+
* - OPENCODE_MESSAGE_ID: Passed to CLI for context
|
|
13
|
+
* - OPENCODE_AGENT: Passed to CLI for context
|
|
14
|
+
*/
|
|
15
|
+
import type { Plugin, PluginInput, Hooks } from "@opencode-ai/plugin";
|
|
16
|
+
import { tool } from "@opencode-ai/plugin";
|
|
17
|
+
import { spawn } from "child_process";
|
|
18
|
+
|
|
19
|
+
const SWARM_CLI = "swarm";
|
|
20
|
+
|
|
21
|
+
// =============================================================================
|
|
22
|
+
// CLI Execution Helper
|
|
23
|
+
// =============================================================================
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Execute a swarm tool via CLI
|
|
27
|
+
*
|
|
28
|
+
* Spawns `swarm tool <name> --json '<args>'` and returns the result.
|
|
29
|
+
* Passes session context via environment variables.
|
|
30
|
+
*/
|
|
31
|
+
async function execTool(
|
|
32
|
+
name: string,
|
|
33
|
+
args: Record<string, unknown>,
|
|
34
|
+
ctx: { sessionID: string; messageID: string; agent: string },
|
|
35
|
+
): Promise<string> {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
const hasArgs = Object.keys(args).length > 0;
|
|
38
|
+
const cliArgs = hasArgs
|
|
39
|
+
? ["tool", name, "--json", JSON.stringify(args)]
|
|
40
|
+
: ["tool", name];
|
|
41
|
+
|
|
42
|
+
const proc = spawn(SWARM_CLI, cliArgs, {
|
|
43
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
44
|
+
env: {
|
|
45
|
+
...process.env,
|
|
46
|
+
OPENCODE_SESSION_ID: ctx.sessionID,
|
|
47
|
+
OPENCODE_MESSAGE_ID: ctx.messageID,
|
|
48
|
+
OPENCODE_AGENT: ctx.agent,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
let stdout = "";
|
|
53
|
+
let stderr = "";
|
|
54
|
+
|
|
55
|
+
proc.stdout.on("data", (data) => {
|
|
56
|
+
stdout += data;
|
|
57
|
+
});
|
|
58
|
+
proc.stderr.on("data", (data) => {
|
|
59
|
+
stderr += data;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
proc.on("close", (code) => {
|
|
63
|
+
if (code === 0) {
|
|
64
|
+
// Success - return the JSON output
|
|
65
|
+
try {
|
|
66
|
+
const result = JSON.parse(stdout);
|
|
67
|
+
if (result.success && result.data !== undefined) {
|
|
68
|
+
// Unwrap the data for cleaner tool output
|
|
69
|
+
resolve(
|
|
70
|
+
typeof result.data === "string"
|
|
71
|
+
? result.data
|
|
72
|
+
: JSON.stringify(result.data, null, 2),
|
|
73
|
+
);
|
|
74
|
+
} else if (!result.success && result.error) {
|
|
75
|
+
// Tool returned an error in JSON format
|
|
76
|
+
reject(new Error(result.error.message || "Tool execution failed"));
|
|
77
|
+
} else {
|
|
78
|
+
resolve(stdout);
|
|
79
|
+
}
|
|
80
|
+
} catch {
|
|
81
|
+
resolve(stdout);
|
|
82
|
+
}
|
|
83
|
+
} else if (code === 2) {
|
|
84
|
+
reject(new Error(`Unknown tool: ${name}`));
|
|
85
|
+
} else if (code === 3) {
|
|
86
|
+
reject(new Error(`Invalid JSON args: ${stderr}`));
|
|
87
|
+
} else {
|
|
88
|
+
// Tool returned error
|
|
89
|
+
try {
|
|
90
|
+
const result = JSON.parse(stdout);
|
|
91
|
+
if (!result.success && result.error) {
|
|
92
|
+
reject(
|
|
93
|
+
new Error(
|
|
94
|
+
result.error.message || `Tool failed with code ${code}`,
|
|
95
|
+
),
|
|
96
|
+
);
|
|
97
|
+
} else {
|
|
98
|
+
reject(
|
|
99
|
+
new Error(stderr || stdout || `Tool failed with code ${code}`),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
reject(
|
|
104
|
+
new Error(stderr || stdout || `Tool failed with code ${code}`),
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
proc.on("error", (err) => {
|
|
111
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
112
|
+
reject(
|
|
113
|
+
new Error(
|
|
114
|
+
`swarm CLI not found. Install with: npm install -g opencode-swarm-plugin`,
|
|
115
|
+
),
|
|
116
|
+
);
|
|
117
|
+
} else {
|
|
118
|
+
reject(err);
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// =============================================================================
|
|
125
|
+
// Beads Tools
|
|
126
|
+
// =============================================================================
|
|
127
|
+
|
|
128
|
+
const beads_create = tool({
|
|
129
|
+
description: "Create a new bead with type-safe validation",
|
|
130
|
+
args: {
|
|
131
|
+
title: tool.schema.string().describe("Bead title"),
|
|
132
|
+
type: tool.schema
|
|
133
|
+
.enum(["bug", "feature", "task", "epic", "chore"])
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("Issue type (default: task)"),
|
|
136
|
+
priority: tool.schema
|
|
137
|
+
.number()
|
|
138
|
+
.min(0)
|
|
139
|
+
.max(3)
|
|
140
|
+
.optional()
|
|
141
|
+
.describe("Priority 0-3 (default: 2)"),
|
|
142
|
+
description: tool.schema.string().optional().describe("Bead description"),
|
|
143
|
+
parent_id: tool.schema
|
|
144
|
+
.string()
|
|
145
|
+
.optional()
|
|
146
|
+
.describe("Parent bead ID for epic children"),
|
|
147
|
+
},
|
|
148
|
+
execute: (args, ctx) => execTool("beads_create", args, ctx),
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const beads_create_epic = tool({
|
|
152
|
+
description: "Create epic with subtasks in one atomic operation",
|
|
153
|
+
args: {
|
|
154
|
+
epic_title: tool.schema.string().describe("Epic title"),
|
|
155
|
+
epic_description: tool.schema
|
|
156
|
+
.string()
|
|
157
|
+
.optional()
|
|
158
|
+
.describe("Epic description"),
|
|
159
|
+
subtasks: tool.schema
|
|
160
|
+
.array(
|
|
161
|
+
tool.schema.object({
|
|
162
|
+
title: tool.schema.string(),
|
|
163
|
+
priority: tool.schema.number().min(0).max(3).optional(),
|
|
164
|
+
files: tool.schema.array(tool.schema.string()).optional(),
|
|
165
|
+
}),
|
|
166
|
+
)
|
|
167
|
+
.describe("Subtasks to create under the epic"),
|
|
168
|
+
},
|
|
169
|
+
execute: (args, ctx) => execTool("beads_create_epic", args, ctx),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const beads_query = tool({
|
|
173
|
+
description: "Query beads with filters (replaces bd list, bd ready, bd wip)",
|
|
174
|
+
args: {
|
|
175
|
+
status: tool.schema
|
|
176
|
+
.enum(["open", "in_progress", "blocked", "closed"])
|
|
177
|
+
.optional()
|
|
178
|
+
.describe("Filter by status"),
|
|
179
|
+
type: tool.schema
|
|
180
|
+
.enum(["bug", "feature", "task", "epic", "chore"])
|
|
181
|
+
.optional()
|
|
182
|
+
.describe("Filter by type"),
|
|
183
|
+
ready: tool.schema
|
|
184
|
+
.boolean()
|
|
185
|
+
.optional()
|
|
186
|
+
.describe("Only show unblocked beads"),
|
|
187
|
+
limit: tool.schema
|
|
188
|
+
.number()
|
|
189
|
+
.optional()
|
|
190
|
+
.describe("Max results (default: 20)"),
|
|
191
|
+
},
|
|
192
|
+
execute: (args, ctx) => execTool("beads_query", args, ctx),
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
const beads_update = tool({
|
|
196
|
+
description: "Update bead status/description",
|
|
197
|
+
args: {
|
|
198
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
199
|
+
status: tool.schema
|
|
200
|
+
.enum(["open", "in_progress", "blocked", "closed"])
|
|
201
|
+
.optional()
|
|
202
|
+
.describe("New status"),
|
|
203
|
+
description: tool.schema.string().optional().describe("New description"),
|
|
204
|
+
priority: tool.schema
|
|
205
|
+
.number()
|
|
206
|
+
.min(0)
|
|
207
|
+
.max(3)
|
|
208
|
+
.optional()
|
|
209
|
+
.describe("New priority"),
|
|
210
|
+
},
|
|
211
|
+
execute: (args, ctx) => execTool("beads_update", args, ctx),
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const beads_close = tool({
|
|
215
|
+
description: "Close a bead with reason",
|
|
216
|
+
args: {
|
|
217
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
218
|
+
reason: tool.schema.string().describe("Completion reason"),
|
|
219
|
+
},
|
|
220
|
+
execute: (args, ctx) => execTool("beads_close", args, ctx),
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
const beads_start = tool({
|
|
224
|
+
description: "Mark a bead as in-progress",
|
|
225
|
+
args: {
|
|
226
|
+
id: tool.schema.string().describe("Bead ID"),
|
|
227
|
+
},
|
|
228
|
+
execute: (args, ctx) => execTool("beads_start", args, ctx),
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const beads_ready = tool({
|
|
232
|
+
description: "Get the next ready bead (unblocked, highest priority)",
|
|
233
|
+
args: {},
|
|
234
|
+
execute: (args, ctx) => execTool("beads_ready", args, ctx),
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const beads_sync = tool({
|
|
238
|
+
description: "Sync beads to git and push (MANDATORY at session end)",
|
|
239
|
+
args: {
|
|
240
|
+
auto_pull: tool.schema.boolean().optional().describe("Pull before sync"),
|
|
241
|
+
},
|
|
242
|
+
execute: (args, ctx) => execTool("beads_sync", args, ctx),
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const beads_link_thread = tool({
|
|
246
|
+
description: "Add metadata linking bead to Agent Mail thread",
|
|
247
|
+
args: {
|
|
248
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
249
|
+
thread_id: tool.schema.string().describe("Agent Mail thread ID"),
|
|
250
|
+
},
|
|
251
|
+
execute: (args, ctx) => execTool("beads_link_thread", args, ctx),
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// =============================================================================
|
|
255
|
+
// Agent Mail Tools
|
|
256
|
+
// =============================================================================
|
|
257
|
+
|
|
258
|
+
const agentmail_init = tool({
|
|
259
|
+
description: "Initialize Agent Mail session",
|
|
260
|
+
args: {
|
|
261
|
+
project_path: tool.schema.string().describe("Absolute path to the project"),
|
|
262
|
+
agent_name: tool.schema.string().optional().describe("Custom agent name"),
|
|
263
|
+
task_description: tool.schema
|
|
264
|
+
.string()
|
|
265
|
+
.optional()
|
|
266
|
+
.describe("Task description"),
|
|
267
|
+
},
|
|
268
|
+
execute: (args, ctx) => execTool("agentmail_init", args, ctx),
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
const agentmail_send = tool({
|
|
272
|
+
description: "Send message to other agents",
|
|
273
|
+
args: {
|
|
274
|
+
to: tool.schema
|
|
275
|
+
.array(tool.schema.string())
|
|
276
|
+
.describe("Recipient agent names"),
|
|
277
|
+
subject: tool.schema.string().describe("Message subject"),
|
|
278
|
+
body: tool.schema.string().describe("Message body"),
|
|
279
|
+
thread_id: tool.schema
|
|
280
|
+
.string()
|
|
281
|
+
.optional()
|
|
282
|
+
.describe("Thread ID for grouping"),
|
|
283
|
+
importance: tool.schema
|
|
284
|
+
.enum(["low", "normal", "high", "urgent"])
|
|
285
|
+
.optional()
|
|
286
|
+
.describe("Message importance"),
|
|
287
|
+
ack_required: tool.schema
|
|
288
|
+
.boolean()
|
|
289
|
+
.optional()
|
|
290
|
+
.describe("Require acknowledgment"),
|
|
291
|
+
},
|
|
292
|
+
execute: (args, ctx) => execTool("agentmail_send", args, ctx),
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
const agentmail_inbox = tool({
|
|
296
|
+
description: "Fetch inbox (CONTEXT-SAFE: bodies excluded, limit 5)",
|
|
297
|
+
args: {
|
|
298
|
+
limit: tool.schema
|
|
299
|
+
.number()
|
|
300
|
+
.max(5)
|
|
301
|
+
.optional()
|
|
302
|
+
.describe("Max messages (max 5)"),
|
|
303
|
+
urgent_only: tool.schema
|
|
304
|
+
.boolean()
|
|
305
|
+
.optional()
|
|
306
|
+
.describe("Only urgent messages"),
|
|
307
|
+
since_ts: tool.schema
|
|
308
|
+
.string()
|
|
309
|
+
.optional()
|
|
310
|
+
.describe("Messages since timestamp"),
|
|
311
|
+
},
|
|
312
|
+
execute: (args, ctx) => execTool("agentmail_inbox", args, ctx),
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
const agentmail_read_message = tool({
|
|
316
|
+
description: "Fetch ONE message body by ID",
|
|
317
|
+
args: {
|
|
318
|
+
message_id: tool.schema.number().describe("Message ID"),
|
|
319
|
+
},
|
|
320
|
+
execute: (args, ctx) => execTool("agentmail_read_message", args, ctx),
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
const agentmail_summarize_thread = tool({
|
|
324
|
+
description: "Summarize thread (PREFERRED over fetching all messages)",
|
|
325
|
+
args: {
|
|
326
|
+
thread_id: tool.schema.string().describe("Thread ID"),
|
|
327
|
+
include_examples: tool.schema
|
|
328
|
+
.boolean()
|
|
329
|
+
.optional()
|
|
330
|
+
.describe("Include example messages"),
|
|
331
|
+
},
|
|
332
|
+
execute: (args, ctx) => execTool("agentmail_summarize_thread", args, ctx),
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
const agentmail_reserve = tool({
|
|
336
|
+
description: "Reserve file paths for exclusive editing",
|
|
337
|
+
args: {
|
|
338
|
+
paths: tool.schema
|
|
339
|
+
.array(tool.schema.string())
|
|
340
|
+
.describe("File paths/patterns"),
|
|
341
|
+
ttl_seconds: tool.schema.number().optional().describe("Reservation TTL"),
|
|
342
|
+
exclusive: tool.schema.boolean().optional().describe("Exclusive lock"),
|
|
343
|
+
reason: tool.schema.string().optional().describe("Reservation reason"),
|
|
344
|
+
},
|
|
345
|
+
execute: (args, ctx) => execTool("agentmail_reserve", args, ctx),
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
const agentmail_release = tool({
|
|
349
|
+
description: "Release file reservations",
|
|
350
|
+
args: {
|
|
351
|
+
paths: tool.schema
|
|
352
|
+
.array(tool.schema.string())
|
|
353
|
+
.optional()
|
|
354
|
+
.describe("Paths to release"),
|
|
355
|
+
reservation_ids: tool.schema
|
|
356
|
+
.array(tool.schema.number())
|
|
357
|
+
.optional()
|
|
358
|
+
.describe("Reservation IDs"),
|
|
359
|
+
},
|
|
360
|
+
execute: (args, ctx) => execTool("agentmail_release", args, ctx),
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
const agentmail_ack = tool({
|
|
364
|
+
description: "Acknowledge a message",
|
|
365
|
+
args: {
|
|
366
|
+
message_id: tool.schema.number().describe("Message ID"),
|
|
367
|
+
},
|
|
368
|
+
execute: (args, ctx) => execTool("agentmail_ack", args, ctx),
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
const agentmail_search = tool({
|
|
372
|
+
description: "Search messages by keyword",
|
|
373
|
+
args: {
|
|
374
|
+
query: tool.schema.string().describe("Search query"),
|
|
375
|
+
limit: tool.schema.number().optional().describe("Max results"),
|
|
376
|
+
},
|
|
377
|
+
execute: (args, ctx) => execTool("agentmail_search", args, ctx),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const agentmail_health = tool({
|
|
381
|
+
description: "Check if Agent Mail server is running",
|
|
382
|
+
args: {},
|
|
383
|
+
execute: (args, ctx) => execTool("agentmail_health", args, ctx),
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
// =============================================================================
|
|
387
|
+
// Structured Tools
|
|
388
|
+
// =============================================================================
|
|
389
|
+
|
|
390
|
+
const structured_extract_json = tool({
|
|
391
|
+
description: "Extract JSON from markdown/text response",
|
|
392
|
+
args: {
|
|
393
|
+
text: tool.schema.string().describe("Text containing JSON"),
|
|
394
|
+
},
|
|
395
|
+
execute: (args, ctx) => execTool("structured_extract_json", args, ctx),
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
const structured_validate = tool({
|
|
399
|
+
description: "Validate agent response against a schema",
|
|
400
|
+
args: {
|
|
401
|
+
response: tool.schema.string().describe("Agent response to validate"),
|
|
402
|
+
schema_name: tool.schema
|
|
403
|
+
.enum(["evaluation", "task_decomposition", "bead_tree"])
|
|
404
|
+
.describe("Schema to validate against"),
|
|
405
|
+
max_retries: tool.schema
|
|
406
|
+
.number()
|
|
407
|
+
.min(1)
|
|
408
|
+
.max(5)
|
|
409
|
+
.optional()
|
|
410
|
+
.describe("Max retries"),
|
|
411
|
+
},
|
|
412
|
+
execute: (args, ctx) => execTool("structured_validate", args, ctx),
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const structured_parse_evaluation = tool({
|
|
416
|
+
description: "Parse and validate evaluation response",
|
|
417
|
+
args: {
|
|
418
|
+
response: tool.schema.string().describe("Agent response"),
|
|
419
|
+
},
|
|
420
|
+
execute: (args, ctx) => execTool("structured_parse_evaluation", args, ctx),
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
const structured_parse_decomposition = tool({
|
|
424
|
+
description: "Parse and validate task decomposition response",
|
|
425
|
+
args: {
|
|
426
|
+
response: tool.schema.string().describe("Agent response"),
|
|
427
|
+
},
|
|
428
|
+
execute: (args, ctx) => execTool("structured_parse_decomposition", args, ctx),
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
const structured_parse_bead_tree = tool({
|
|
432
|
+
description: "Parse and validate bead tree response",
|
|
433
|
+
args: {
|
|
434
|
+
response: tool.schema.string().describe("Agent response"),
|
|
435
|
+
},
|
|
436
|
+
execute: (args, ctx) => execTool("structured_parse_bead_tree", args, ctx),
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// =============================================================================
|
|
440
|
+
// Swarm Tools
|
|
441
|
+
// =============================================================================
|
|
442
|
+
|
|
443
|
+
const swarm_init = tool({
|
|
444
|
+
description: "Initialize swarm session and check tool availability",
|
|
445
|
+
args: {
|
|
446
|
+
project_path: tool.schema.string().optional().describe("Project path"),
|
|
447
|
+
},
|
|
448
|
+
execute: (args, ctx) => execTool("swarm_init", args, ctx),
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
const swarm_select_strategy = tool({
|
|
452
|
+
description: "Analyze task and recommend decomposition strategy",
|
|
453
|
+
args: {
|
|
454
|
+
task: tool.schema.string().min(1).describe("Task to analyze"),
|
|
455
|
+
codebase_context: tool.schema
|
|
456
|
+
.string()
|
|
457
|
+
.optional()
|
|
458
|
+
.describe("Codebase context"),
|
|
459
|
+
},
|
|
460
|
+
execute: (args, ctx) => execTool("swarm_select_strategy", args, ctx),
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
const swarm_plan_prompt = tool({
|
|
464
|
+
description: "Generate strategy-specific decomposition prompt",
|
|
465
|
+
args: {
|
|
466
|
+
task: tool.schema.string().min(1).describe("Task to decompose"),
|
|
467
|
+
strategy: tool.schema
|
|
468
|
+
.enum(["file-based", "feature-based", "risk-based", "auto"])
|
|
469
|
+
.optional()
|
|
470
|
+
.describe("Decomposition strategy"),
|
|
471
|
+
max_subtasks: tool.schema
|
|
472
|
+
.number()
|
|
473
|
+
.int()
|
|
474
|
+
.min(2)
|
|
475
|
+
.max(10)
|
|
476
|
+
.optional()
|
|
477
|
+
.describe("Max subtasks"),
|
|
478
|
+
context: tool.schema.string().optional().describe("Additional context"),
|
|
479
|
+
query_cass: tool.schema
|
|
480
|
+
.boolean()
|
|
481
|
+
.optional()
|
|
482
|
+
.describe("Query CASS for similar tasks"),
|
|
483
|
+
cass_limit: tool.schema
|
|
484
|
+
.number()
|
|
485
|
+
.int()
|
|
486
|
+
.min(1)
|
|
487
|
+
.max(10)
|
|
488
|
+
.optional()
|
|
489
|
+
.describe("CASS limit"),
|
|
490
|
+
},
|
|
491
|
+
execute: (args, ctx) => execTool("swarm_plan_prompt", args, ctx),
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
const swarm_decompose = tool({
|
|
495
|
+
description: "Generate decomposition prompt for breaking task into subtasks",
|
|
496
|
+
args: {
|
|
497
|
+
task: tool.schema.string().min(1).describe("Task to decompose"),
|
|
498
|
+
max_subtasks: tool.schema
|
|
499
|
+
.number()
|
|
500
|
+
.int()
|
|
501
|
+
.min(2)
|
|
502
|
+
.max(10)
|
|
503
|
+
.optional()
|
|
504
|
+
.describe("Max subtasks"),
|
|
505
|
+
context: tool.schema.string().optional().describe("Additional context"),
|
|
506
|
+
query_cass: tool.schema.boolean().optional().describe("Query CASS"),
|
|
507
|
+
cass_limit: tool.schema
|
|
508
|
+
.number()
|
|
509
|
+
.int()
|
|
510
|
+
.min(1)
|
|
511
|
+
.max(10)
|
|
512
|
+
.optional()
|
|
513
|
+
.describe("CASS limit"),
|
|
514
|
+
},
|
|
515
|
+
execute: (args, ctx) => execTool("swarm_decompose", args, ctx),
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
const swarm_validate_decomposition = tool({
|
|
519
|
+
description: "Validate a decomposition response against BeadTreeSchema",
|
|
520
|
+
args: {
|
|
521
|
+
response: tool.schema.string().describe("Decomposition response"),
|
|
522
|
+
},
|
|
523
|
+
execute: (args, ctx) => execTool("swarm_validate_decomposition", args, ctx),
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
const swarm_status = tool({
|
|
527
|
+
description: "Get status of a swarm by epic ID",
|
|
528
|
+
args: {
|
|
529
|
+
epic_id: tool.schema.string().describe("Epic bead ID"),
|
|
530
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
531
|
+
},
|
|
532
|
+
execute: (args, ctx) => execTool("swarm_status", args, ctx),
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
const swarm_progress = tool({
|
|
536
|
+
description: "Report progress on a subtask to coordinator",
|
|
537
|
+
args: {
|
|
538
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
539
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
540
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
541
|
+
status: tool.schema
|
|
542
|
+
.enum(["in_progress", "blocked", "completed", "failed"])
|
|
543
|
+
.describe("Status"),
|
|
544
|
+
message: tool.schema.string().optional().describe("Progress message"),
|
|
545
|
+
progress_percent: tool.schema
|
|
546
|
+
.number()
|
|
547
|
+
.min(0)
|
|
548
|
+
.max(100)
|
|
549
|
+
.optional()
|
|
550
|
+
.describe("Progress %"),
|
|
551
|
+
files_touched: tool.schema
|
|
552
|
+
.array(tool.schema.string())
|
|
553
|
+
.optional()
|
|
554
|
+
.describe("Files modified"),
|
|
555
|
+
},
|
|
556
|
+
execute: (args, ctx) => execTool("swarm_progress", args, ctx),
|
|
557
|
+
});
|
|
558
|
+
|
|
559
|
+
const swarm_complete = tool({
|
|
560
|
+
description:
|
|
561
|
+
"Mark subtask complete, release reservations, notify coordinator",
|
|
562
|
+
args: {
|
|
563
|
+
project_key: tool.schema.string().describe("Project key"),
|
|
564
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
565
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
566
|
+
summary: tool.schema.string().describe("Completion summary"),
|
|
567
|
+
evaluation: tool.schema.string().optional().describe("Self-evaluation"),
|
|
568
|
+
files_touched: tool.schema
|
|
569
|
+
.array(tool.schema.string())
|
|
570
|
+
.optional()
|
|
571
|
+
.describe("Files modified"),
|
|
572
|
+
skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS scan"),
|
|
573
|
+
},
|
|
574
|
+
execute: (args, ctx) => execTool("swarm_complete", args, ctx),
|
|
575
|
+
});
|
|
576
|
+
|
|
577
|
+
const swarm_record_outcome = tool({
|
|
578
|
+
description: "Record subtask outcome for implicit feedback scoring",
|
|
579
|
+
args: {
|
|
580
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
581
|
+
duration_ms: tool.schema.number().int().min(0).describe("Duration in ms"),
|
|
582
|
+
error_count: tool.schema
|
|
583
|
+
.number()
|
|
584
|
+
.int()
|
|
585
|
+
.min(0)
|
|
586
|
+
.optional()
|
|
587
|
+
.describe("Error count"),
|
|
588
|
+
retry_count: tool.schema
|
|
589
|
+
.number()
|
|
590
|
+
.int()
|
|
591
|
+
.min(0)
|
|
592
|
+
.optional()
|
|
593
|
+
.describe("Retry count"),
|
|
594
|
+
success: tool.schema.boolean().describe("Whether task succeeded"),
|
|
595
|
+
files_touched: tool.schema
|
|
596
|
+
.array(tool.schema.string())
|
|
597
|
+
.optional()
|
|
598
|
+
.describe("Files modified"),
|
|
599
|
+
criteria: tool.schema
|
|
600
|
+
.array(tool.schema.string())
|
|
601
|
+
.optional()
|
|
602
|
+
.describe("Evaluation criteria"),
|
|
603
|
+
strategy: tool.schema
|
|
604
|
+
.enum(["file-based", "feature-based", "risk-based"])
|
|
605
|
+
.optional()
|
|
606
|
+
.describe("Strategy used"),
|
|
607
|
+
},
|
|
608
|
+
execute: (args, ctx) => execTool("swarm_record_outcome", args, ctx),
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
const swarm_subtask_prompt = tool({
|
|
612
|
+
description: "Generate the prompt for a spawned subtask agent",
|
|
613
|
+
args: {
|
|
614
|
+
agent_name: tool.schema.string().describe("Agent name"),
|
|
615
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
616
|
+
epic_id: tool.schema.string().describe("Epic ID"),
|
|
617
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
618
|
+
subtask_description: tool.schema
|
|
619
|
+
.string()
|
|
620
|
+
.optional()
|
|
621
|
+
.describe("Description"),
|
|
622
|
+
files: tool.schema.array(tool.schema.string()).describe("Files to work on"),
|
|
623
|
+
shared_context: tool.schema.string().optional().describe("Shared context"),
|
|
624
|
+
},
|
|
625
|
+
execute: (args, ctx) => execTool("swarm_subtask_prompt", args, ctx),
|
|
626
|
+
});
|
|
627
|
+
|
|
628
|
+
const swarm_spawn_subtask = tool({
|
|
629
|
+
description: "Prepare a subtask for spawning with Task tool",
|
|
630
|
+
args: {
|
|
631
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
632
|
+
epic_id: tool.schema.string().describe("Epic ID"),
|
|
633
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
634
|
+
subtask_description: tool.schema
|
|
635
|
+
.string()
|
|
636
|
+
.optional()
|
|
637
|
+
.describe("Description"),
|
|
638
|
+
files: tool.schema.array(tool.schema.string()).describe("Files to work on"),
|
|
639
|
+
shared_context: tool.schema.string().optional().describe("Shared context"),
|
|
640
|
+
},
|
|
641
|
+
execute: (args, ctx) => execTool("swarm_spawn_subtask", args, ctx),
|
|
642
|
+
});
|
|
643
|
+
|
|
644
|
+
const swarm_complete_subtask = tool({
|
|
645
|
+
description: "Handle subtask completion after Task agent returns",
|
|
646
|
+
args: {
|
|
647
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
648
|
+
task_result: tool.schema.string().describe("Task result JSON"),
|
|
649
|
+
files_touched: tool.schema
|
|
650
|
+
.array(tool.schema.string())
|
|
651
|
+
.optional()
|
|
652
|
+
.describe("Files modified"),
|
|
653
|
+
},
|
|
654
|
+
execute: (args, ctx) => execTool("swarm_complete_subtask", args, ctx),
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
const swarm_evaluation_prompt = tool({
|
|
658
|
+
description: "Generate self-evaluation prompt for a completed subtask",
|
|
659
|
+
args: {
|
|
660
|
+
bead_id: tool.schema.string().describe("Bead ID"),
|
|
661
|
+
subtask_title: tool.schema.string().describe("Subtask title"),
|
|
662
|
+
files_touched: tool.schema
|
|
663
|
+
.array(tool.schema.string())
|
|
664
|
+
.describe("Files modified"),
|
|
665
|
+
},
|
|
666
|
+
execute: (args, ctx) => execTool("swarm_evaluation_prompt", args, ctx),
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
// =============================================================================
|
|
670
|
+
// Plugin Export
|
|
671
|
+
// =============================================================================
|
|
672
|
+
|
|
673
|
+
export const SwarmPlugin: Plugin = async (
|
|
674
|
+
_input: PluginInput,
|
|
675
|
+
): Promise<Hooks> => {
|
|
676
|
+
return {
|
|
677
|
+
tool: {
|
|
678
|
+
// Beads
|
|
679
|
+
beads_create,
|
|
680
|
+
beads_create_epic,
|
|
681
|
+
beads_query,
|
|
682
|
+
beads_update,
|
|
683
|
+
beads_close,
|
|
684
|
+
beads_start,
|
|
685
|
+
beads_ready,
|
|
686
|
+
beads_sync,
|
|
687
|
+
beads_link_thread,
|
|
688
|
+
// Agent Mail
|
|
689
|
+
agentmail_init,
|
|
690
|
+
agentmail_send,
|
|
691
|
+
agentmail_inbox,
|
|
692
|
+
agentmail_read_message,
|
|
693
|
+
agentmail_summarize_thread,
|
|
694
|
+
agentmail_reserve,
|
|
695
|
+
agentmail_release,
|
|
696
|
+
agentmail_ack,
|
|
697
|
+
agentmail_search,
|
|
698
|
+
agentmail_health,
|
|
699
|
+
// Structured
|
|
700
|
+
structured_extract_json,
|
|
701
|
+
structured_validate,
|
|
702
|
+
structured_parse_evaluation,
|
|
703
|
+
structured_parse_decomposition,
|
|
704
|
+
structured_parse_bead_tree,
|
|
705
|
+
// Swarm
|
|
706
|
+
swarm_init,
|
|
707
|
+
swarm_select_strategy,
|
|
708
|
+
swarm_plan_prompt,
|
|
709
|
+
swarm_decompose,
|
|
710
|
+
swarm_validate_decomposition,
|
|
711
|
+
swarm_status,
|
|
712
|
+
swarm_progress,
|
|
713
|
+
swarm_complete,
|
|
714
|
+
swarm_record_outcome,
|
|
715
|
+
swarm_subtask_prompt,
|
|
716
|
+
swarm_spawn_subtask,
|
|
717
|
+
swarm_complete_subtask,
|
|
718
|
+
swarm_evaluation_prompt,
|
|
719
|
+
},
|
|
720
|
+
};
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
export default SwarmPlugin;
|