opencode-swarm-plugin 0.9.0 → 0.11.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 +44 -0
- package/README.md +184 -105
- package/bin/swarm.ts +549 -26
- package/dist/index.js +232 -26
- package/dist/plugin.js +232 -26
- package/package.json +1 -1
package/bin/swarm.ts
CHANGED
|
@@ -13,11 +13,284 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import * as p from "@clack/prompts";
|
|
16
|
-
import { existsSync, mkdirSync, writeFileSync } from "fs";
|
|
16
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
|
17
17
|
import { homedir } from "os";
|
|
18
|
-
import { join } from "path";
|
|
18
|
+
import { dirname, join } from "path";
|
|
19
|
+
import { fileURLToPath } from "url";
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const pkg = JSON.parse(
|
|
23
|
+
readFileSync(join(__dirname, "..", "package.json"), "utf-8"),
|
|
24
|
+
);
|
|
25
|
+
const VERSION: string = pkg.version;
|
|
26
|
+
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// ASCII Art & Branding
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
const BEE = `
|
|
32
|
+
\\ \` - ' /
|
|
33
|
+
- .(o o). -
|
|
34
|
+
( >.< )
|
|
35
|
+
/| |\\
|
|
36
|
+
(_| |_) bzzzz...
|
|
37
|
+
`;
|
|
38
|
+
|
|
39
|
+
const BANNER = `
|
|
40
|
+
███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗
|
|
41
|
+
██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║
|
|
42
|
+
███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║
|
|
43
|
+
╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║
|
|
44
|
+
███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║
|
|
45
|
+
╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
const TAGLINE = "Multi-agent coordination for OpenCode";
|
|
49
|
+
|
|
50
|
+
const dim = (s: string) => `\x1b[2m${s}\x1b[0m`;
|
|
51
|
+
const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`;
|
|
52
|
+
const cyan = (s: string) => `\x1b[36m${s}\x1b[0m`;
|
|
53
|
+
const green = (s: string) => `\x1b[32m${s}\x1b[0m`;
|
|
54
|
+
const magenta = (s: string) => `\x1b[35m${s}\x1b[0m`;
|
|
55
|
+
|
|
56
|
+
const PACKAGE_NAME = "opencode-swarm-plugin";
|
|
57
|
+
|
|
58
|
+
// ============================================================================
|
|
59
|
+
// Seasonal Messages (inspired by Astro's Houston)
|
|
60
|
+
// ============================================================================
|
|
61
|
+
|
|
62
|
+
type Season = "spooky" | "holiday" | "new-year" | "summer" | "default";
|
|
63
|
+
|
|
64
|
+
function getSeason(): Season {
|
|
65
|
+
const date = new Date();
|
|
66
|
+
const month = date.getMonth() + 1;
|
|
67
|
+
const day = date.getDate();
|
|
68
|
+
|
|
69
|
+
if (month === 1 && day <= 7) return "new-year";
|
|
70
|
+
if (month === 10 && day > 7) return "spooky";
|
|
71
|
+
if (month === 12 && day > 7 && day < 26) return "holiday";
|
|
72
|
+
if (month >= 6 && month <= 8) return "summer";
|
|
73
|
+
return "default";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
interface SeasonalBee {
|
|
77
|
+
messages: string[];
|
|
78
|
+
decorations?: string[];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function getSeasonalBee(): SeasonalBee {
|
|
82
|
+
const season = getSeason();
|
|
83
|
+
const year = new Date().getFullYear();
|
|
84
|
+
|
|
85
|
+
switch (season) {
|
|
86
|
+
case "new-year":
|
|
87
|
+
return {
|
|
88
|
+
messages: [
|
|
89
|
+
`New year, new swarm! Let's build something amazing in ${year}!`,
|
|
90
|
+
`${year} is the year of parallel agents! bzzzz...`,
|
|
91
|
+
`Kicking off ${year} with coordinated chaos!`,
|
|
92
|
+
`Happy ${year}! Time to orchestrate some magic.`,
|
|
93
|
+
],
|
|
94
|
+
decorations: ["🎉", "🎊", "✨"],
|
|
95
|
+
};
|
|
96
|
+
case "spooky":
|
|
97
|
+
return {
|
|
98
|
+
messages: [
|
|
99
|
+
`Boo! Just kidding. Let's spawn some agents!`,
|
|
100
|
+
`The hive is buzzing with spooky energy...`,
|
|
101
|
+
`No tricks here, only parallel treats!`,
|
|
102
|
+
`Let's conjure up a swarm of worker bees!`,
|
|
103
|
+
`Something wicked this way computes...`,
|
|
104
|
+
],
|
|
105
|
+
decorations: ["🎃", "👻", "🕷️", "🦇"],
|
|
106
|
+
};
|
|
107
|
+
case "holiday":
|
|
108
|
+
return {
|
|
109
|
+
messages: [
|
|
110
|
+
`'Tis the season to parallelize!`,
|
|
111
|
+
`The hive is warm and cozy. Let's build!`,
|
|
112
|
+
`Ho ho ho! Time to unwrap some agents!`,
|
|
113
|
+
`Jingle bells, agents swell, tasks get done today!`,
|
|
114
|
+
`The best gift? A well-coordinated swarm.`,
|
|
115
|
+
],
|
|
116
|
+
decorations: ["🎄", "🎁", "❄️", "⭐"],
|
|
117
|
+
};
|
|
118
|
+
case "summer":
|
|
119
|
+
return {
|
|
120
|
+
messages: [
|
|
121
|
+
`Summer vibes and parallel pipelines!`,
|
|
122
|
+
`The hive is buzzing in the sunshine!`,
|
|
123
|
+
`Hot code, cool agents. Let's go!`,
|
|
124
|
+
`Beach day? Nah, build day!`,
|
|
125
|
+
],
|
|
126
|
+
decorations: ["☀️", "🌻", "🌴"],
|
|
127
|
+
};
|
|
128
|
+
default:
|
|
129
|
+
return {
|
|
130
|
+
messages: [
|
|
131
|
+
`The hive awaits your command.`,
|
|
132
|
+
`Ready to coordinate the swarm!`,
|
|
133
|
+
`Let's build something awesome together.`,
|
|
134
|
+
`Parallel agents, standing by.`,
|
|
135
|
+
`Time to orchestrate some magic!`,
|
|
136
|
+
`The bees are ready to work.`,
|
|
137
|
+
`Bzzzz... initializing swarm intelligence.`,
|
|
138
|
+
`Many agents, one mission.`,
|
|
139
|
+
],
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function getRandomMessage(): string {
|
|
145
|
+
const { messages } = getSeasonalBee();
|
|
146
|
+
return messages[Math.floor(Math.random() * messages.length)];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getDecoratedBee(): string {
|
|
150
|
+
const { decorations } = getSeasonalBee();
|
|
151
|
+
if (!decorations || Math.random() > 0.5) return cyan(BEE);
|
|
152
|
+
|
|
153
|
+
const decoration =
|
|
154
|
+
decorations[Math.floor(Math.random() * decorations.length)];
|
|
155
|
+
// Add decoration to the bee
|
|
156
|
+
return cyan(BEE.replace("bzzzz...", `bzzzz... ${decoration}`));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// Model Configuration
|
|
161
|
+
// ============================================================================
|
|
162
|
+
|
|
163
|
+
interface ModelOption {
|
|
164
|
+
value: string;
|
|
165
|
+
label: string;
|
|
166
|
+
hint: string;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const COORDINATOR_MODELS: ModelOption[] = [
|
|
170
|
+
{
|
|
171
|
+
value: "anthropic/claude-sonnet-4-5",
|
|
172
|
+
label: "Claude Sonnet 4.5",
|
|
173
|
+
hint: "Best balance of speed and capability (recommended)",
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
value: "anthropic/claude-opus-4-5",
|
|
177
|
+
label: "Claude Opus 4.5",
|
|
178
|
+
hint: "Most capable, slower and more expensive",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
value: "openai/gpt-4o",
|
|
182
|
+
label: "GPT-4o",
|
|
183
|
+
hint: "Fast, good for most tasks",
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
value: "google/gemini-2.0-flash",
|
|
187
|
+
label: "Gemini 2.0 Flash",
|
|
188
|
+
hint: "Fast and capable",
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
value: "google/gemini-1.5-pro",
|
|
192
|
+
label: "Gemini 1.5 Pro",
|
|
193
|
+
hint: "More capable, larger context",
|
|
194
|
+
},
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const WORKER_MODELS: ModelOption[] = [
|
|
198
|
+
{
|
|
199
|
+
value: "anthropic/claude-haiku-4-5",
|
|
200
|
+
label: "Claude Haiku 4.5",
|
|
201
|
+
hint: "Fast and cost-effective (recommended)",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
value: "anthropic/claude-sonnet-4-5",
|
|
205
|
+
label: "Claude Sonnet 4.5",
|
|
206
|
+
hint: "More capable, slower",
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
value: "openai/gpt-4o-mini",
|
|
210
|
+
label: "GPT-4o Mini",
|
|
211
|
+
hint: "Fast and cheap",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
value: "google/gemini-2.0-flash",
|
|
215
|
+
label: "Gemini 2.0 Flash",
|
|
216
|
+
hint: "Fast and capable",
|
|
217
|
+
},
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
// ============================================================================
|
|
221
|
+
// Update Checking
|
|
222
|
+
// ============================================================================
|
|
223
|
+
|
|
224
|
+
interface UpdateInfo {
|
|
225
|
+
current: string;
|
|
226
|
+
latest: string;
|
|
227
|
+
updateAvailable: boolean;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
async function checkForUpdates(): Promise<UpdateInfo | null> {
|
|
231
|
+
try {
|
|
232
|
+
const response = await fetch(
|
|
233
|
+
`https://registry.npmjs.org/${PACKAGE_NAME}/latest`,
|
|
234
|
+
{
|
|
235
|
+
signal: AbortSignal.timeout(3000), // 3 second timeout
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
if (!response.ok) return null;
|
|
239
|
+
const data = await response.json();
|
|
240
|
+
const latest = data.version;
|
|
241
|
+
const updateAvailable =
|
|
242
|
+
latest !== VERSION && compareVersions(latest, VERSION) > 0;
|
|
243
|
+
return { current: VERSION, latest, updateAvailable };
|
|
244
|
+
} catch {
|
|
245
|
+
return null; // Silently fail - don't block CLI
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function compareVersions(a: string, b: string): number {
|
|
250
|
+
const partsA = a.split(".").map(Number);
|
|
251
|
+
const partsB = b.split(".").map(Number);
|
|
252
|
+
for (let i = 0; i < 3; i++) {
|
|
253
|
+
if (partsA[i] > partsB[i]) return 1;
|
|
254
|
+
if (partsA[i] < partsB[i]) return -1;
|
|
255
|
+
}
|
|
256
|
+
return 0;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function showUpdateNotification(info: UpdateInfo) {
|
|
260
|
+
if (info.updateAvailable) {
|
|
261
|
+
console.log();
|
|
262
|
+
console.log(
|
|
263
|
+
yellow(" ╭─────────────────────────────────────────────────────╮"),
|
|
264
|
+
);
|
|
265
|
+
console.log(
|
|
266
|
+
yellow(" │") +
|
|
267
|
+
" Update available! " +
|
|
268
|
+
dim(info.current) +
|
|
269
|
+
" → " +
|
|
270
|
+
green(info.latest) +
|
|
271
|
+
" " +
|
|
272
|
+
yellow("│"),
|
|
273
|
+
);
|
|
274
|
+
console.log(
|
|
275
|
+
yellow(" │") +
|
|
276
|
+
" Run: " +
|
|
277
|
+
cyan("npm install -g " + PACKAGE_NAME + "@latest") +
|
|
278
|
+
" " +
|
|
279
|
+
yellow("│"),
|
|
280
|
+
);
|
|
281
|
+
console.log(
|
|
282
|
+
yellow(" │") +
|
|
283
|
+
" Or: " +
|
|
284
|
+
cyan("swarm update") +
|
|
285
|
+
" " +
|
|
286
|
+
yellow("│"),
|
|
287
|
+
);
|
|
288
|
+
console.log(
|
|
289
|
+
yellow(" ╰─────────────────────────────────────────────────────╯"),
|
|
290
|
+
);
|
|
291
|
+
console.log();
|
|
292
|
+
}
|
|
293
|
+
}
|
|
21
294
|
|
|
22
295
|
// ============================================================================
|
|
23
296
|
// Types
|
|
@@ -193,7 +466,7 @@ You are a swarm coordinator. Take a complex task, break it into beads, and unlea
|
|
|
193
466
|
2. **Decompose**: Use \`swarm_select_strategy\` then \`swarm_plan_prompt\` to break down the task
|
|
194
467
|
3. **Create beads**: \`beads_create_epic\` with subtasks and file assignments
|
|
195
468
|
4. **Reserve files**: \`agentmail_reserve\` for each subtask's files
|
|
196
|
-
5. **Spawn agents**: Use Task tool with \`swarm_spawn_subtask\` prompts -
|
|
469
|
+
5. **Spawn agents**: Use Task tool with \`swarm_spawn_subtask\` prompts (or use @swarm-worker for sequential/single-file tasks)
|
|
197
470
|
6. **Monitor**: Check \`agentmail_inbox\` for progress, use \`agentmail_summarize_thread\` for overview
|
|
198
471
|
7. **Complete**: \`swarm_complete\` when done, then \`beads_sync\` to push
|
|
199
472
|
|
|
@@ -210,10 +483,10 @@ The plugin auto-selects decomposition strategy based on task keywords:
|
|
|
210
483
|
Begin decomposition now.
|
|
211
484
|
`;
|
|
212
485
|
|
|
213
|
-
const
|
|
486
|
+
const getPlannerAgent = (model: string) => `---
|
|
214
487
|
name: swarm-planner
|
|
215
488
|
description: Strategic task decomposition for swarm coordination
|
|
216
|
-
model:
|
|
489
|
+
model: ${model}
|
|
217
490
|
---
|
|
218
491
|
|
|
219
492
|
You are a swarm planner. Decompose tasks into optimal parallel subtasks.
|
|
@@ -250,6 +523,27 @@ You are a swarm planner. Decompose tasks into optimal parallel subtasks.
|
|
|
250
523
|
- Order by dependency (if B needs A, A comes first)
|
|
251
524
|
`;
|
|
252
525
|
|
|
526
|
+
const getWorkerAgent = (model: string) => `---
|
|
527
|
+
name: swarm-worker
|
|
528
|
+
description: Executes subtasks in a swarm - fast, focused, cost-effective
|
|
529
|
+
model: ${model}
|
|
530
|
+
---
|
|
531
|
+
|
|
532
|
+
You are a swarm worker agent. Execute your assigned subtask efficiently.
|
|
533
|
+
|
|
534
|
+
## Rules
|
|
535
|
+
- Focus ONLY on your assigned files
|
|
536
|
+
- Report progress via Agent Mail
|
|
537
|
+
- Use beads_update to track status
|
|
538
|
+
- Call swarm_complete when done
|
|
539
|
+
|
|
540
|
+
## Workflow
|
|
541
|
+
1. Read assigned files
|
|
542
|
+
2. Implement changes
|
|
543
|
+
3. Verify (typecheck if applicable)
|
|
544
|
+
4. Report completion
|
|
545
|
+
`;
|
|
546
|
+
|
|
253
547
|
// ============================================================================
|
|
254
548
|
// Commands
|
|
255
549
|
// ============================================================================
|
|
@@ -312,10 +606,20 @@ async function doctor() {
|
|
|
312
606
|
} else {
|
|
313
607
|
p.outro("All dependencies installed!");
|
|
314
608
|
}
|
|
609
|
+
|
|
610
|
+
// Check for updates (non-blocking)
|
|
611
|
+
const updateInfo = await checkForUpdates();
|
|
612
|
+
if (updateInfo) showUpdateNotification(updateInfo);
|
|
315
613
|
}
|
|
316
614
|
|
|
317
615
|
async function setup() {
|
|
318
616
|
console.clear();
|
|
617
|
+
console.log(yellow(BANNER));
|
|
618
|
+
console.log(getDecoratedBee());
|
|
619
|
+
console.log();
|
|
620
|
+
console.log(magenta(" " + getRandomMessage()));
|
|
621
|
+
console.log();
|
|
622
|
+
|
|
319
623
|
p.intro("opencode-swarm-plugin v" + VERSION);
|
|
320
624
|
|
|
321
625
|
const s = p.spinner();
|
|
@@ -441,6 +745,103 @@ async function setup() {
|
|
|
441
745
|
}
|
|
442
746
|
}
|
|
443
747
|
|
|
748
|
+
// Model selection
|
|
749
|
+
p.log.step("Configure swarm agents...");
|
|
750
|
+
|
|
751
|
+
const coordinatorModel = await p.select({
|
|
752
|
+
message: "Select coordinator model (for orchestration/planning):",
|
|
753
|
+
options: [
|
|
754
|
+
{
|
|
755
|
+
value: "anthropic/claude-sonnet-4-5",
|
|
756
|
+
label: "Claude Sonnet 4.5",
|
|
757
|
+
hint: "Best balance of speed and capability (recommended)",
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
value: "anthropic/claude-haiku-4-5",
|
|
761
|
+
label: "Claude Haiku 4.5",
|
|
762
|
+
hint: "Fast and cost-effective",
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
value: "anthropic/claude-opus-4-5",
|
|
766
|
+
label: "Claude Opus 4.5",
|
|
767
|
+
hint: "Most capable, slower",
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
value: "openai/gpt-4o",
|
|
771
|
+
label: "GPT-4o",
|
|
772
|
+
hint: "Fast, good for most tasks",
|
|
773
|
+
},
|
|
774
|
+
{
|
|
775
|
+
value: "openai/gpt-4-turbo",
|
|
776
|
+
label: "GPT-4 Turbo",
|
|
777
|
+
hint: "Powerful, more expensive",
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
value: "google/gemini-2.0-flash",
|
|
781
|
+
label: "Gemini 2.0 Flash",
|
|
782
|
+
hint: "Fast and capable",
|
|
783
|
+
},
|
|
784
|
+
{
|
|
785
|
+
value: "google/gemini-1.5-pro",
|
|
786
|
+
label: "Gemini 1.5 Pro",
|
|
787
|
+
hint: "More capable",
|
|
788
|
+
},
|
|
789
|
+
],
|
|
790
|
+
initialValue: "anthropic/claude-sonnet-4-5",
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
if (p.isCancel(coordinatorModel)) {
|
|
794
|
+
p.cancel("Setup cancelled");
|
|
795
|
+
process.exit(0);
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
const workerModel = await p.select({
|
|
799
|
+
message: "Select worker model (for task execution):",
|
|
800
|
+
options: [
|
|
801
|
+
{
|
|
802
|
+
value: "anthropic/claude-haiku-4-5",
|
|
803
|
+
label: "Claude Haiku 4.5",
|
|
804
|
+
hint: "Fast and cost-effective (recommended)",
|
|
805
|
+
},
|
|
806
|
+
{
|
|
807
|
+
value: "anthropic/claude-sonnet-4-5",
|
|
808
|
+
label: "Claude Sonnet 4.5",
|
|
809
|
+
hint: "Best balance of speed and capability",
|
|
810
|
+
},
|
|
811
|
+
{
|
|
812
|
+
value: "anthropic/claude-opus-4-5",
|
|
813
|
+
label: "Claude Opus 4.5",
|
|
814
|
+
hint: "Most capable, slower",
|
|
815
|
+
},
|
|
816
|
+
{
|
|
817
|
+
value: "openai/gpt-4o",
|
|
818
|
+
label: "GPT-4o",
|
|
819
|
+
hint: "Fast, good for most tasks",
|
|
820
|
+
},
|
|
821
|
+
{
|
|
822
|
+
value: "openai/gpt-4-turbo",
|
|
823
|
+
label: "GPT-4 Turbo",
|
|
824
|
+
hint: "Powerful, more expensive",
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
value: "google/gemini-2.0-flash",
|
|
828
|
+
label: "Gemini 2.0 Flash",
|
|
829
|
+
hint: "Fast and capable",
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
value: "google/gemini-1.5-pro",
|
|
833
|
+
label: "Gemini 1.5 Pro",
|
|
834
|
+
hint: "More capable",
|
|
835
|
+
},
|
|
836
|
+
],
|
|
837
|
+
initialValue: "anthropic/claude-haiku-4-5",
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
if (p.isCancel(workerModel)) {
|
|
841
|
+
p.cancel("Setup cancelled");
|
|
842
|
+
process.exit(0);
|
|
843
|
+
}
|
|
844
|
+
|
|
444
845
|
p.log.step("Setting up OpenCode integration...");
|
|
445
846
|
|
|
446
847
|
const configDir = join(homedir(), ".config", "opencode");
|
|
@@ -456,7 +857,8 @@ async function setup() {
|
|
|
456
857
|
|
|
457
858
|
const pluginPath = join(pluginsDir, "swarm.ts");
|
|
458
859
|
const commandPath = join(commandsDir, "swarm.md");
|
|
459
|
-
const
|
|
860
|
+
const plannerAgentPath = join(agentsDir, "swarm-planner.md");
|
|
861
|
+
const workerAgentPath = join(agentsDir, "swarm-worker.md");
|
|
460
862
|
|
|
461
863
|
writeFileSync(pluginPath, PLUGIN_WRAPPER);
|
|
462
864
|
p.log.success("Plugin: " + pluginPath);
|
|
@@ -464,8 +866,11 @@ async function setup() {
|
|
|
464
866
|
writeFileSync(commandPath, SWARM_COMMAND);
|
|
465
867
|
p.log.success("Command: " + commandPath);
|
|
466
868
|
|
|
467
|
-
writeFileSync(
|
|
468
|
-
p.log.success("
|
|
869
|
+
writeFileSync(plannerAgentPath, getPlannerAgent(coordinatorModel as string));
|
|
870
|
+
p.log.success("Planner agent: " + plannerAgentPath);
|
|
871
|
+
|
|
872
|
+
writeFileSync(workerAgentPath, getWorkerAgent(workerModel as string));
|
|
873
|
+
p.log.success("Worker agent: " + workerAgentPath);
|
|
469
874
|
|
|
470
875
|
p.note(
|
|
471
876
|
'cd your-project\nbd init\nopencode\n/swarm "your task"',
|
|
@@ -559,29 +964,141 @@ async function init() {
|
|
|
559
964
|
}
|
|
560
965
|
}
|
|
561
966
|
|
|
562
|
-
function version() {
|
|
563
|
-
console.log(
|
|
967
|
+
async function version() {
|
|
968
|
+
console.log(yellow(BANNER));
|
|
969
|
+
console.log(dim(" " + TAGLINE));
|
|
970
|
+
console.log();
|
|
971
|
+
console.log(" Version: " + VERSION);
|
|
972
|
+
console.log(" Docs: https://github.com/joelhooks/opencode-swarm-plugin");
|
|
973
|
+
console.log();
|
|
974
|
+
|
|
975
|
+
// Check for updates (non-blocking)
|
|
976
|
+
const updateInfo = await checkForUpdates();
|
|
977
|
+
if (updateInfo) showUpdateNotification(updateInfo);
|
|
564
978
|
}
|
|
565
979
|
|
|
566
|
-
function
|
|
567
|
-
|
|
568
|
-
|
|
980
|
+
function config() {
|
|
981
|
+
const configDir = join(homedir(), ".config", "opencode");
|
|
982
|
+
const pluginPath = join(configDir, "plugins", "swarm.ts");
|
|
983
|
+
const commandPath = join(configDir, "commands", "swarm.md");
|
|
984
|
+
const plannerAgentPath = join(configDir, "agents", "swarm-planner.md");
|
|
985
|
+
const workerAgentPath = join(configDir, "agents", "swarm-worker.md");
|
|
986
|
+
|
|
987
|
+
console.log(yellow(BANNER));
|
|
988
|
+
console.log(dim(" " + TAGLINE + " v" + VERSION));
|
|
989
|
+
console.log();
|
|
990
|
+
console.log(cyan("Config Files:"));
|
|
991
|
+
console.log();
|
|
992
|
+
|
|
993
|
+
const files = [
|
|
994
|
+
{ path: pluginPath, desc: "Plugin loader", emoji: "🔌" },
|
|
995
|
+
{ path: commandPath, desc: "/swarm command prompt", emoji: "📜" },
|
|
996
|
+
{ path: plannerAgentPath, desc: "@swarm-planner agent", emoji: "🤖" },
|
|
997
|
+
{ path: workerAgentPath, desc: "@swarm-worker agent", emoji: "🐝" },
|
|
998
|
+
];
|
|
999
|
+
|
|
1000
|
+
for (const { path, desc, emoji } of files) {
|
|
1001
|
+
const exists = existsSync(path);
|
|
1002
|
+
const status = exists ? "✓" : "✗";
|
|
1003
|
+
const color = exists ? "\x1b[32m" : "\x1b[31m";
|
|
1004
|
+
console.log(` ${emoji} ${desc}`);
|
|
1005
|
+
console.log(` ${color}${status}\x1b[0m ${dim(path)}`);
|
|
1006
|
+
console.log();
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
console.log(dim("Edit these files to customize swarm behavior."));
|
|
1010
|
+
console.log(dim("Run 'swarm setup' to regenerate defaults."));
|
|
1011
|
+
console.log();
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
async function update() {
|
|
1015
|
+
p.intro("swarm update v" + VERSION);
|
|
1016
|
+
|
|
1017
|
+
const s = p.spinner();
|
|
1018
|
+
s.start("Checking for updates...");
|
|
1019
|
+
|
|
1020
|
+
const updateInfo = await checkForUpdates();
|
|
1021
|
+
|
|
1022
|
+
if (!updateInfo) {
|
|
1023
|
+
s.stop("Failed to check for updates");
|
|
1024
|
+
p.log.error("Could not reach npm registry");
|
|
1025
|
+
p.outro("Try again later or update manually:");
|
|
1026
|
+
console.log(" " + cyan("npm install -g " + PACKAGE_NAME + "@latest"));
|
|
1027
|
+
process.exit(1);
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
if (!updateInfo.updateAvailable) {
|
|
1031
|
+
s.stop("Already on latest version");
|
|
1032
|
+
p.log.success("You're running " + VERSION);
|
|
1033
|
+
p.outro("No update needed!");
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
|
|
1037
|
+
s.stop("Update available: " + VERSION + " → " + updateInfo.latest);
|
|
1038
|
+
|
|
1039
|
+
const confirmUpdate = await p.confirm({
|
|
1040
|
+
message: "Update to v" + updateInfo.latest + "?",
|
|
1041
|
+
initialValue: true,
|
|
1042
|
+
});
|
|
569
1043
|
|
|
570
|
-
|
|
1044
|
+
if (p.isCancel(confirmUpdate) || !confirmUpdate) {
|
|
1045
|
+
p.outro("Update cancelled");
|
|
1046
|
+
return;
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
const updateSpinner = p.spinner();
|
|
1050
|
+
updateSpinner.start("Updating to v" + updateInfo.latest + "...");
|
|
571
1051
|
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
swarm init Initialize beads in current project
|
|
576
|
-
swarm version Show version
|
|
577
|
-
swarm help Show this help
|
|
1052
|
+
const success = await runInstall(
|
|
1053
|
+
"npm install -g " + PACKAGE_NAME + "@latest",
|
|
1054
|
+
);
|
|
578
1055
|
|
|
579
|
-
|
|
1056
|
+
if (success) {
|
|
1057
|
+
updateSpinner.stop("Updated to v" + updateInfo.latest);
|
|
1058
|
+
p.outro("Success! Restart your terminal to use the new version.");
|
|
1059
|
+
} else {
|
|
1060
|
+
updateSpinner.stop("Update failed");
|
|
1061
|
+
p.log.error("Failed to update via npm");
|
|
1062
|
+
p.log.message("Try manually:");
|
|
1063
|
+
console.log(" " + cyan("npm install -g " + PACKAGE_NAME + "@latest"));
|
|
1064
|
+
p.outro("Update failed");
|
|
1065
|
+
process.exit(1);
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
async function help() {
|
|
1070
|
+
console.log(yellow(BANNER));
|
|
1071
|
+
console.log(dim(" " + TAGLINE + " v" + VERSION));
|
|
1072
|
+
console.log(getDecoratedBee());
|
|
1073
|
+
console.log(magenta(" " + getRandomMessage()));
|
|
1074
|
+
console.log(`
|
|
1075
|
+
${cyan("Commands:")}
|
|
1076
|
+
swarm setup Interactive installer - checks and installs dependencies
|
|
1077
|
+
swarm doctor Health check - shows status of all dependencies
|
|
1078
|
+
swarm init Initialize beads in current project
|
|
1079
|
+
swarm config Show paths to generated config files
|
|
1080
|
+
swarm update Update to latest version
|
|
1081
|
+
swarm version Show version and banner
|
|
1082
|
+
swarm help Show this help
|
|
1083
|
+
|
|
1084
|
+
${cyan("Usage in OpenCode:")}
|
|
580
1085
|
/swarm "Add user authentication with OAuth"
|
|
581
|
-
@swarm-planner "
|
|
1086
|
+
@swarm-planner "Decompose this into parallel tasks"
|
|
1087
|
+
@swarm-worker "Execute this specific subtask"
|
|
582
1088
|
|
|
583
|
-
|
|
1089
|
+
${cyan("Customization:")}
|
|
1090
|
+
Edit the generated files to customize behavior:
|
|
1091
|
+
${dim("~/.config/opencode/commands/swarm.md")} - /swarm command prompt
|
|
1092
|
+
${dim("~/.config/opencode/agents/swarm-planner.md")} - @swarm-planner (coordinator)
|
|
1093
|
+
${dim("~/.config/opencode/agents/swarm-worker.md")} - @swarm-worker (fast executor)
|
|
1094
|
+
${dim("~/.config/opencode/plugins/swarm.ts")} - Plugin loader
|
|
1095
|
+
|
|
1096
|
+
${dim("Docs: https://github.com/joelhooks/opencode-swarm-plugin")}
|
|
584
1097
|
`);
|
|
1098
|
+
|
|
1099
|
+
// Check for updates (non-blocking)
|
|
1100
|
+
const updateInfo = await checkForUpdates();
|
|
1101
|
+
if (updateInfo) showUpdateNotification(updateInfo);
|
|
585
1102
|
}
|
|
586
1103
|
|
|
587
1104
|
// ============================================================================
|
|
@@ -600,15 +1117,21 @@ switch (command) {
|
|
|
600
1117
|
case "init":
|
|
601
1118
|
await init();
|
|
602
1119
|
break;
|
|
1120
|
+
case "config":
|
|
1121
|
+
config();
|
|
1122
|
+
break;
|
|
1123
|
+
case "update":
|
|
1124
|
+
await update();
|
|
1125
|
+
break;
|
|
603
1126
|
case "version":
|
|
604
1127
|
case "--version":
|
|
605
1128
|
case "-v":
|
|
606
|
-
version();
|
|
1129
|
+
await version();
|
|
607
1130
|
break;
|
|
608
1131
|
case "help":
|
|
609
1132
|
case "--help":
|
|
610
1133
|
case "-h":
|
|
611
|
-
help();
|
|
1134
|
+
await help();
|
|
612
1135
|
break;
|
|
613
1136
|
case undefined:
|
|
614
1137
|
await setup();
|