opencode-swarm-plugin 0.6.1 → 0.9.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 +7 -80
- package/README.md +174 -508
- package/bin/swarm.ts +620 -0
- package/bun.lock +7 -0
- package/package.json +5 -1
- package/src/agent-mail.ts +401 -44
package/bin/swarm.ts
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
/**
|
|
3
|
+
* OpenCode Swarm Plugin CLI
|
|
4
|
+
*
|
|
5
|
+
* A beautiful interactive CLI for setting up and managing swarm coordination.
|
|
6
|
+
*
|
|
7
|
+
* Commands:
|
|
8
|
+
* swarm setup - Interactive installer for all dependencies
|
|
9
|
+
* swarm doctor - Check dependency health with detailed status
|
|
10
|
+
* swarm init - Initialize swarm in current project
|
|
11
|
+
* swarm version - Show version info
|
|
12
|
+
* swarm - Interactive mode (same as setup)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as p from "@clack/prompts";
|
|
16
|
+
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
17
|
+
import { homedir } from "os";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
|
|
20
|
+
const VERSION = "0.9.0";
|
|
21
|
+
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Types
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
26
|
+
interface Dependency {
|
|
27
|
+
name: string;
|
|
28
|
+
command: string;
|
|
29
|
+
checkArgs: string[];
|
|
30
|
+
required: boolean;
|
|
31
|
+
install: string;
|
|
32
|
+
installType: "brew" | "curl" | "go" | "npm" | "manual";
|
|
33
|
+
description: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface CheckResult {
|
|
37
|
+
dep: Dependency;
|
|
38
|
+
available: boolean;
|
|
39
|
+
version?: string;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ============================================================================
|
|
43
|
+
// Dependencies
|
|
44
|
+
// ============================================================================
|
|
45
|
+
|
|
46
|
+
const DEPENDENCIES: Dependency[] = [
|
|
47
|
+
{
|
|
48
|
+
name: "OpenCode",
|
|
49
|
+
command: "opencode",
|
|
50
|
+
checkArgs: ["--version"],
|
|
51
|
+
required: true,
|
|
52
|
+
install: "brew install sst/tap/opencode",
|
|
53
|
+
installType: "brew",
|
|
54
|
+
description: "AI coding assistant (plugin host)",
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: "Beads",
|
|
58
|
+
command: "bd",
|
|
59
|
+
checkArgs: ["--version"],
|
|
60
|
+
required: true,
|
|
61
|
+
install:
|
|
62
|
+
"curl -fsSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash",
|
|
63
|
+
installType: "curl",
|
|
64
|
+
description: "Git-backed issue tracking",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: "Go",
|
|
68
|
+
command: "go",
|
|
69
|
+
checkArgs: ["version"],
|
|
70
|
+
required: false,
|
|
71
|
+
install: "brew install go",
|
|
72
|
+
installType: "brew",
|
|
73
|
+
description: "Required for Agent Mail",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
name: "Agent Mail",
|
|
77
|
+
command: "agent-mail",
|
|
78
|
+
checkArgs: ["--help"],
|
|
79
|
+
required: false,
|
|
80
|
+
install: "go install github.com/joelhooks/agent-mail/cmd/agent-mail@latest",
|
|
81
|
+
installType: "go",
|
|
82
|
+
description: "Multi-agent coordination & file reservations",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
name: "CASS",
|
|
86
|
+
command: "cass",
|
|
87
|
+
checkArgs: ["--help"],
|
|
88
|
+
required: false,
|
|
89
|
+
install: "https://github.com/Dicklesworthstone/cass",
|
|
90
|
+
installType: "manual",
|
|
91
|
+
description: "Cross-agent session search",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: "UBS",
|
|
95
|
+
command: "ubs",
|
|
96
|
+
checkArgs: ["--help"],
|
|
97
|
+
required: false,
|
|
98
|
+
install: "https://github.com/joelhooks/ubs",
|
|
99
|
+
installType: "manual",
|
|
100
|
+
description: "Pre-commit bug scanning",
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
name: "semantic-memory",
|
|
104
|
+
command: "semantic-memory",
|
|
105
|
+
checkArgs: ["--help"],
|
|
106
|
+
required: false,
|
|
107
|
+
install: "npm install -g semantic-memory",
|
|
108
|
+
installType: "npm",
|
|
109
|
+
description: "Learning persistence with vector search",
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "Redis",
|
|
113
|
+
command: "redis-cli",
|
|
114
|
+
checkArgs: ["ping"],
|
|
115
|
+
required: false,
|
|
116
|
+
install: "brew install redis && brew services start redis",
|
|
117
|
+
installType: "brew",
|
|
118
|
+
description: "Rate limiting (SQLite fallback available)",
|
|
119
|
+
},
|
|
120
|
+
];
|
|
121
|
+
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Utilities
|
|
124
|
+
// ============================================================================
|
|
125
|
+
|
|
126
|
+
async function checkCommand(
|
|
127
|
+
cmd: string,
|
|
128
|
+
args: string[],
|
|
129
|
+
): Promise<{ available: boolean; version?: string }> {
|
|
130
|
+
try {
|
|
131
|
+
const proc = Bun.spawn([cmd, ...args], {
|
|
132
|
+
stdout: "pipe",
|
|
133
|
+
stderr: "pipe",
|
|
134
|
+
});
|
|
135
|
+
const exitCode = await proc.exited;
|
|
136
|
+
if (exitCode === 0) {
|
|
137
|
+
const output = await new Response(proc.stdout).text();
|
|
138
|
+
const versionMatch = output.match(/v?(\d+\.\d+\.\d+)/);
|
|
139
|
+
return { available: true, version: versionMatch?.[1] };
|
|
140
|
+
}
|
|
141
|
+
return { available: false };
|
|
142
|
+
} catch {
|
|
143
|
+
return { available: false };
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function runInstall(command: string): Promise<boolean> {
|
|
148
|
+
try {
|
|
149
|
+
const proc = Bun.spawn(["bash", "-c", command], {
|
|
150
|
+
stdout: "inherit",
|
|
151
|
+
stderr: "inherit",
|
|
152
|
+
});
|
|
153
|
+
const exitCode = await proc.exited;
|
|
154
|
+
return exitCode === 0;
|
|
155
|
+
} catch {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function checkAllDependencies(): Promise<CheckResult[]> {
|
|
161
|
+
const results: CheckResult[] = [];
|
|
162
|
+
for (const dep of DEPENDENCIES) {
|
|
163
|
+
const { available, version } = await checkCommand(
|
|
164
|
+
dep.command,
|
|
165
|
+
dep.checkArgs,
|
|
166
|
+
);
|
|
167
|
+
results.push({ dep, available, version });
|
|
168
|
+
}
|
|
169
|
+
return results;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// File Templates
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
const PLUGIN_WRAPPER = `import { SwarmPlugin } from "opencode-swarm-plugin"
|
|
177
|
+
export default SwarmPlugin
|
|
178
|
+
`;
|
|
179
|
+
|
|
180
|
+
const SWARM_COMMAND = `---
|
|
181
|
+
description: Decompose task into parallel subtasks and coordinate agents
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
You are a swarm coordinator. Take a complex task, break it into beads, and unleash parallel agents.
|
|
185
|
+
|
|
186
|
+
## Usage
|
|
187
|
+
|
|
188
|
+
/swarm <task description or bead-id>
|
|
189
|
+
|
|
190
|
+
## Workflow
|
|
191
|
+
|
|
192
|
+
1. **Initialize**: \`agentmail_init\` with project_path and task_description
|
|
193
|
+
2. **Decompose**: Use \`swarm_select_strategy\` then \`swarm_plan_prompt\` to break down the task
|
|
194
|
+
3. **Create beads**: \`beads_create_epic\` with subtasks and file assignments
|
|
195
|
+
4. **Reserve files**: \`agentmail_reserve\` for each subtask's files
|
|
196
|
+
5. **Spawn agents**: Use Task tool with \`swarm_spawn_subtask\` prompts - spawn ALL in parallel
|
|
197
|
+
6. **Monitor**: Check \`agentmail_inbox\` for progress, use \`agentmail_summarize_thread\` for overview
|
|
198
|
+
7. **Complete**: \`swarm_complete\` when done, then \`beads_sync\` to push
|
|
199
|
+
|
|
200
|
+
## Strategy Selection
|
|
201
|
+
|
|
202
|
+
The plugin auto-selects decomposition strategy based on task keywords:
|
|
203
|
+
|
|
204
|
+
| Strategy | Best For | Keywords |
|
|
205
|
+
| ------------- | ----------------------- | -------------------------------------- |
|
|
206
|
+
| file-based | Refactoring, migrations | refactor, migrate, rename, update all |
|
|
207
|
+
| feature-based | New features | add, implement, build, create, feature |
|
|
208
|
+
| risk-based | Bug fixes, security | fix, bug, security, critical, urgent |
|
|
209
|
+
|
|
210
|
+
Begin decomposition now.
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
const PLANNER_AGENT = `---
|
|
214
|
+
name: swarm-planner
|
|
215
|
+
description: Strategic task decomposition for swarm coordination
|
|
216
|
+
model: claude-sonnet-4-5
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
You are a swarm planner. Decompose tasks into optimal parallel subtasks.
|
|
220
|
+
|
|
221
|
+
## Workflow
|
|
222
|
+
|
|
223
|
+
1. Call \`swarm_select_strategy\` to analyze the task
|
|
224
|
+
2. Call \`swarm_plan_prompt\` to get strategy-specific guidance
|
|
225
|
+
3. Create a BeadTree following the guidelines
|
|
226
|
+
4. Return ONLY valid JSON - no markdown, no explanation
|
|
227
|
+
|
|
228
|
+
## Output Format
|
|
229
|
+
|
|
230
|
+
\`\`\`json
|
|
231
|
+
{
|
|
232
|
+
"epic": { "title": "...", "description": "..." },
|
|
233
|
+
"subtasks": [
|
|
234
|
+
{
|
|
235
|
+
"title": "...",
|
|
236
|
+
"description": "...",
|
|
237
|
+
"files": ["src/..."],
|
|
238
|
+
"dependencies": [],
|
|
239
|
+
"estimated_complexity": 2
|
|
240
|
+
}
|
|
241
|
+
]
|
|
242
|
+
}
|
|
243
|
+
\`\`\`
|
|
244
|
+
|
|
245
|
+
## Rules
|
|
246
|
+
|
|
247
|
+
- 2-7 subtasks (too few = not parallel, too many = overhead)
|
|
248
|
+
- No file overlap between subtasks
|
|
249
|
+
- Include tests with the code they test
|
|
250
|
+
- Order by dependency (if B needs A, A comes first)
|
|
251
|
+
`;
|
|
252
|
+
|
|
253
|
+
// ============================================================================
|
|
254
|
+
// Commands
|
|
255
|
+
// ============================================================================
|
|
256
|
+
|
|
257
|
+
async function doctor() {
|
|
258
|
+
p.intro("swarm doctor v" + VERSION);
|
|
259
|
+
|
|
260
|
+
const s = p.spinner();
|
|
261
|
+
s.start("Checking dependencies...");
|
|
262
|
+
|
|
263
|
+
const results = await checkAllDependencies();
|
|
264
|
+
|
|
265
|
+
s.stop("Dependencies checked");
|
|
266
|
+
|
|
267
|
+
const required = results.filter((r) => r.dep.required);
|
|
268
|
+
const optional = results.filter((r) => !r.dep.required);
|
|
269
|
+
|
|
270
|
+
p.log.step("Required dependencies:");
|
|
271
|
+
for (const { dep, available, version } of required) {
|
|
272
|
+
if (available) {
|
|
273
|
+
p.log.success(dep.name + (version ? " v" + version : ""));
|
|
274
|
+
} else {
|
|
275
|
+
p.log.error(dep.name + " - not found");
|
|
276
|
+
p.log.message(" Install: " + dep.install);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
p.log.step("Optional dependencies:");
|
|
281
|
+
for (const { dep, available, version } of optional) {
|
|
282
|
+
if (available) {
|
|
283
|
+
p.log.success(
|
|
284
|
+
dep.name + (version ? " v" + version : "") + " - " + dep.description,
|
|
285
|
+
);
|
|
286
|
+
} else {
|
|
287
|
+
p.log.warn(dep.name + " - not found (" + dep.description + ")");
|
|
288
|
+
if (dep.installType !== "manual") {
|
|
289
|
+
p.log.message(" Install: " + dep.install);
|
|
290
|
+
} else {
|
|
291
|
+
p.log.message(" See: " + dep.install);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
const requiredMissing = required.filter((r) => !r.available);
|
|
297
|
+
const optionalMissing = optional.filter((r) => !r.available);
|
|
298
|
+
|
|
299
|
+
if (requiredMissing.length > 0) {
|
|
300
|
+
p.outro(
|
|
301
|
+
"Missing " +
|
|
302
|
+
requiredMissing.length +
|
|
303
|
+
" required dependencies. Run 'swarm setup' to install.",
|
|
304
|
+
);
|
|
305
|
+
process.exit(1);
|
|
306
|
+
} else if (optionalMissing.length > 0) {
|
|
307
|
+
p.outro(
|
|
308
|
+
"All required dependencies installed. " +
|
|
309
|
+
optionalMissing.length +
|
|
310
|
+
" optional missing.",
|
|
311
|
+
);
|
|
312
|
+
} else {
|
|
313
|
+
p.outro("All dependencies installed!");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
async function setup() {
|
|
318
|
+
console.clear();
|
|
319
|
+
p.intro("opencode-swarm-plugin v" + VERSION);
|
|
320
|
+
|
|
321
|
+
const s = p.spinner();
|
|
322
|
+
s.start("Checking dependencies...");
|
|
323
|
+
|
|
324
|
+
const results = await checkAllDependencies();
|
|
325
|
+
|
|
326
|
+
s.stop("Dependencies checked");
|
|
327
|
+
|
|
328
|
+
const required = results.filter((r) => r.dep.required);
|
|
329
|
+
const optional = results.filter((r) => !r.dep.required);
|
|
330
|
+
const requiredMissing = required.filter((r) => !r.available);
|
|
331
|
+
const optionalMissing = optional.filter((r) => !r.available);
|
|
332
|
+
|
|
333
|
+
for (const { dep, available } of results) {
|
|
334
|
+
if (available) {
|
|
335
|
+
p.log.success(dep.name);
|
|
336
|
+
} else if (dep.required) {
|
|
337
|
+
p.log.error(dep.name + " (required)");
|
|
338
|
+
} else {
|
|
339
|
+
p.log.warn(dep.name + " (optional)");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (requiredMissing.length > 0) {
|
|
344
|
+
p.log.step("Missing " + requiredMissing.length + " required dependencies");
|
|
345
|
+
|
|
346
|
+
for (const { dep } of requiredMissing) {
|
|
347
|
+
const shouldInstall = await p.confirm({
|
|
348
|
+
message: "Install " + dep.name + "? (" + dep.description + ")",
|
|
349
|
+
initialValue: true,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
if (p.isCancel(shouldInstall)) {
|
|
353
|
+
p.cancel("Setup cancelled");
|
|
354
|
+
process.exit(0);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (shouldInstall) {
|
|
358
|
+
const installSpinner = p.spinner();
|
|
359
|
+
installSpinner.start("Installing " + dep.name + "...");
|
|
360
|
+
|
|
361
|
+
const success = await runInstall(dep.install);
|
|
362
|
+
|
|
363
|
+
if (success) {
|
|
364
|
+
installSpinner.stop(dep.name + " installed");
|
|
365
|
+
} else {
|
|
366
|
+
installSpinner.stop("Failed to install " + dep.name);
|
|
367
|
+
p.log.error("Manual install: " + dep.install);
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
p.log.warn("Skipping " + dep.name + " - swarm may not work correctly");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (optionalMissing.length > 0) {
|
|
376
|
+
const installable = optionalMissing.filter(
|
|
377
|
+
(r) => r.dep.installType !== "manual",
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
if (installable.length > 0) {
|
|
381
|
+
const toInstall = await p.multiselect({
|
|
382
|
+
message: "Install optional dependencies?",
|
|
383
|
+
options: installable.map(({ dep }) => ({
|
|
384
|
+
value: dep.name,
|
|
385
|
+
label: dep.name,
|
|
386
|
+
hint: dep.description,
|
|
387
|
+
})),
|
|
388
|
+
required: false,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
if (p.isCancel(toInstall)) {
|
|
392
|
+
p.cancel("Setup cancelled");
|
|
393
|
+
process.exit(0);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (Array.isArray(toInstall) && toInstall.length > 0) {
|
|
397
|
+
for (const name of toInstall) {
|
|
398
|
+
const { dep } = installable.find((r) => r.dep.name === name)!;
|
|
399
|
+
|
|
400
|
+
if (dep.name === "Agent Mail") {
|
|
401
|
+
const goResult = results.find((r) => r.dep.name === "Go");
|
|
402
|
+
if (!goResult?.available) {
|
|
403
|
+
p.log.warn("Agent Mail requires Go. Installing Go first...");
|
|
404
|
+
const goDep = DEPENDENCIES.find((d) => d.name === "Go")!;
|
|
405
|
+
const goSpinner = p.spinner();
|
|
406
|
+
goSpinner.start("Installing Go...");
|
|
407
|
+
const goSuccess = await runInstall(goDep.install);
|
|
408
|
+
if (goSuccess) {
|
|
409
|
+
goSpinner.stop("Go installed");
|
|
410
|
+
} else {
|
|
411
|
+
goSpinner.stop("Failed to install Go");
|
|
412
|
+
p.log.error("Cannot install Agent Mail without Go");
|
|
413
|
+
continue;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const installSpinner = p.spinner();
|
|
419
|
+
installSpinner.start("Installing " + dep.name + "...");
|
|
420
|
+
|
|
421
|
+
const success = await runInstall(dep.install);
|
|
422
|
+
|
|
423
|
+
if (success) {
|
|
424
|
+
installSpinner.stop(dep.name + " installed");
|
|
425
|
+
} else {
|
|
426
|
+
installSpinner.stop("Failed to install " + dep.name);
|
|
427
|
+
p.log.message(" Manual: " + dep.install);
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const manual = optionalMissing.filter(
|
|
434
|
+
(r) => r.dep.installType === "manual",
|
|
435
|
+
);
|
|
436
|
+
if (manual.length > 0) {
|
|
437
|
+
p.log.step("Manual installation required:");
|
|
438
|
+
for (const { dep } of manual) {
|
|
439
|
+
p.log.message(" " + dep.name + ": " + dep.install);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
p.log.step("Setting up OpenCode integration...");
|
|
445
|
+
|
|
446
|
+
const configDir = join(homedir(), ".config", "opencode");
|
|
447
|
+
const pluginsDir = join(configDir, "plugins");
|
|
448
|
+
const commandsDir = join(configDir, "commands");
|
|
449
|
+
const agentsDir = join(configDir, "agents");
|
|
450
|
+
|
|
451
|
+
for (const dir of [pluginsDir, commandsDir, agentsDir]) {
|
|
452
|
+
if (!existsSync(dir)) {
|
|
453
|
+
mkdirSync(dir, { recursive: true });
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const pluginPath = join(pluginsDir, "swarm.ts");
|
|
458
|
+
const commandPath = join(commandsDir, "swarm.md");
|
|
459
|
+
const agentPath = join(agentsDir, "swarm-planner.md");
|
|
460
|
+
|
|
461
|
+
writeFileSync(pluginPath, PLUGIN_WRAPPER);
|
|
462
|
+
p.log.success("Plugin: " + pluginPath);
|
|
463
|
+
|
|
464
|
+
writeFileSync(commandPath, SWARM_COMMAND);
|
|
465
|
+
p.log.success("Command: " + commandPath);
|
|
466
|
+
|
|
467
|
+
writeFileSync(agentPath, PLANNER_AGENT);
|
|
468
|
+
p.log.success("Agent: " + agentPath);
|
|
469
|
+
|
|
470
|
+
p.note(
|
|
471
|
+
'cd your-project\nbd init\nopencode\n/swarm "your task"',
|
|
472
|
+
"Next steps",
|
|
473
|
+
);
|
|
474
|
+
|
|
475
|
+
p.outro("Setup complete! Run 'swarm doctor' to verify.");
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function init() {
|
|
479
|
+
p.intro("swarm init v" + VERSION);
|
|
480
|
+
|
|
481
|
+
const gitDir = existsSync(".git");
|
|
482
|
+
if (!gitDir) {
|
|
483
|
+
p.log.error("Not in a git repository");
|
|
484
|
+
p.log.message("Run 'git init' first, or cd to a git repo");
|
|
485
|
+
p.outro("Aborted");
|
|
486
|
+
process.exit(1);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const beadsDir = existsSync(".beads");
|
|
490
|
+
if (beadsDir) {
|
|
491
|
+
p.log.warn("Beads already initialized in this project");
|
|
492
|
+
|
|
493
|
+
const reinit = await p.confirm({
|
|
494
|
+
message: "Re-initialize beads?",
|
|
495
|
+
initialValue: false,
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
if (p.isCancel(reinit) || !reinit) {
|
|
499
|
+
p.outro("Aborted");
|
|
500
|
+
process.exit(0);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const s = p.spinner();
|
|
505
|
+
s.start("Initializing beads...");
|
|
506
|
+
|
|
507
|
+
const success = await runInstall("bd init");
|
|
508
|
+
|
|
509
|
+
if (success) {
|
|
510
|
+
s.stop("Beads initialized");
|
|
511
|
+
p.log.success("Created .beads/ directory");
|
|
512
|
+
|
|
513
|
+
const createBead = await p.confirm({
|
|
514
|
+
message: "Create your first bead?",
|
|
515
|
+
initialValue: true,
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
if (!p.isCancel(createBead) && createBead) {
|
|
519
|
+
const title = await p.text({
|
|
520
|
+
message: "Bead title:",
|
|
521
|
+
placeholder: "Implement user authentication",
|
|
522
|
+
validate: (v) => (v.length === 0 ? "Title required" : undefined),
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
if (!p.isCancel(title)) {
|
|
526
|
+
const typeResult = await p.select({
|
|
527
|
+
message: "Type:",
|
|
528
|
+
options: [
|
|
529
|
+
{ value: "feature", label: "Feature", hint: "New functionality" },
|
|
530
|
+
{ value: "bug", label: "Bug", hint: "Something broken" },
|
|
531
|
+
{ value: "task", label: "Task", hint: "General work item" },
|
|
532
|
+
{ value: "chore", label: "Chore", hint: "Maintenance" },
|
|
533
|
+
],
|
|
534
|
+
});
|
|
535
|
+
|
|
536
|
+
if (!p.isCancel(typeResult)) {
|
|
537
|
+
const beadSpinner = p.spinner();
|
|
538
|
+
beadSpinner.start("Creating bead...");
|
|
539
|
+
|
|
540
|
+
const createSuccess = await runInstall(
|
|
541
|
+
'bd create --title "' + title + '" --type ' + typeResult,
|
|
542
|
+
);
|
|
543
|
+
|
|
544
|
+
if (createSuccess) {
|
|
545
|
+
beadSpinner.stop("Bead created");
|
|
546
|
+
} else {
|
|
547
|
+
beadSpinner.stop("Failed to create bead");
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
p.outro("Project initialized! Use '/swarm' in OpenCode to get started.");
|
|
554
|
+
} else {
|
|
555
|
+
s.stop("Failed to initialize beads");
|
|
556
|
+
p.log.error("Make sure 'bd' is installed: swarm doctor");
|
|
557
|
+
p.outro("Aborted");
|
|
558
|
+
process.exit(1);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
function version() {
|
|
563
|
+
console.log("opencode-swarm-plugin v" + VERSION);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
function help() {
|
|
567
|
+
console.log(`
|
|
568
|
+
opencode-swarm-plugin v${VERSION}
|
|
569
|
+
|
|
570
|
+
Multi-agent swarm coordination for OpenCode.
|
|
571
|
+
|
|
572
|
+
Commands:
|
|
573
|
+
swarm setup Interactive installer - checks and installs dependencies
|
|
574
|
+
swarm doctor Health check - shows status of all dependencies
|
|
575
|
+
swarm init Initialize beads in current project
|
|
576
|
+
swarm version Show version
|
|
577
|
+
swarm help Show this help
|
|
578
|
+
|
|
579
|
+
After setup, use in OpenCode:
|
|
580
|
+
/swarm "Add user authentication with OAuth"
|
|
581
|
+
@swarm-planner "Refactor components to use hooks"
|
|
582
|
+
|
|
583
|
+
Documentation: https://github.com/joelhooks/opencode-swarm-plugin
|
|
584
|
+
`);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// ============================================================================
|
|
588
|
+
// Main
|
|
589
|
+
// ============================================================================
|
|
590
|
+
|
|
591
|
+
const command = process.argv[2];
|
|
592
|
+
|
|
593
|
+
switch (command) {
|
|
594
|
+
case "setup":
|
|
595
|
+
await setup();
|
|
596
|
+
break;
|
|
597
|
+
case "doctor":
|
|
598
|
+
await doctor();
|
|
599
|
+
break;
|
|
600
|
+
case "init":
|
|
601
|
+
await init();
|
|
602
|
+
break;
|
|
603
|
+
case "version":
|
|
604
|
+
case "--version":
|
|
605
|
+
case "-v":
|
|
606
|
+
version();
|
|
607
|
+
break;
|
|
608
|
+
case "help":
|
|
609
|
+
case "--help":
|
|
610
|
+
case "-h":
|
|
611
|
+
help();
|
|
612
|
+
break;
|
|
613
|
+
case undefined:
|
|
614
|
+
await setup();
|
|
615
|
+
break;
|
|
616
|
+
default:
|
|
617
|
+
console.error("Unknown command: " + command);
|
|
618
|
+
help();
|
|
619
|
+
process.exit(1);
|
|
620
|
+
}
|
package/bun.lock
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "opencode-swarm-plugin",
|
|
7
7
|
"dependencies": {
|
|
8
|
+
"@clack/prompts": "^0.11.0",
|
|
8
9
|
"@opencode-ai/plugin": "^1.0.134",
|
|
9
10
|
"ioredis": "^5.4.1",
|
|
10
11
|
"zod": "4.1.8",
|
|
@@ -20,6 +21,10 @@
|
|
|
20
21
|
},
|
|
21
22
|
},
|
|
22
23
|
"packages": {
|
|
24
|
+
"@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="],
|
|
25
|
+
|
|
26
|
+
"@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="],
|
|
27
|
+
|
|
23
28
|
"@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="],
|
|
24
29
|
|
|
25
30
|
"@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="],
|
|
@@ -204,6 +209,8 @@
|
|
|
204
209
|
|
|
205
210
|
"siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="],
|
|
206
211
|
|
|
212
|
+
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
|
213
|
+
|
|
207
214
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
|
208
215
|
|
|
209
216
|
"stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="],
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Multi-agent swarm coordination for OpenCode with learning capabilities, beads integration, and Agent Mail",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"swarm": "./bin/swarm.ts"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"import": "./dist/index.js",
|
|
@@ -22,6 +25,7 @@
|
|
|
22
25
|
"clean": "rm -rf dist"
|
|
23
26
|
},
|
|
24
27
|
"dependencies": {
|
|
28
|
+
"@clack/prompts": "^0.11.0",
|
|
25
29
|
"@opencode-ai/plugin": "^1.0.134",
|
|
26
30
|
"ioredis": "^5.4.1",
|
|
27
31
|
"zod": "4.1.8"
|