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/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 VERSION = "0.9.0";
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 - spawn ALL in parallel
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 PLANNER_AGENT = `---
486
+ const getPlannerAgent = (model: string) => `---
214
487
  name: swarm-planner
215
488
  description: Strategic task decomposition for swarm coordination
216
- model: claude-sonnet-4-5
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 agentPath = join(agentsDir, "swarm-planner.md");
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(agentPath, PLANNER_AGENT);
468
- p.log.success("Agent: " + agentPath);
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("opencode-swarm-plugin v" + VERSION);
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 help() {
567
- console.log(`
568
- opencode-swarm-plugin v${VERSION}
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
- Multi-agent swarm coordination for OpenCode.
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
- 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
1052
+ const success = await runInstall(
1053
+ "npm install -g " + PACKAGE_NAME + "@latest",
1054
+ );
578
1055
 
579
- After setup, use in OpenCode:
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 "Refactor components to use hooks"
1086
+ @swarm-planner "Decompose this into parallel tasks"
1087
+ @swarm-worker "Execute this specific subtask"
582
1088
 
583
- Documentation: https://github.com/joelhooks/opencode-swarm-plugin
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();