opencode-swarm-plugin 0.6.1 → 0.6.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +123 -21
  2. package/package.json +4 -1
  3. package/scripts/setup.ts +371 -0
package/README.md CHANGED
@@ -20,49 +20,151 @@ This plugin provides intelligent, self-improving tools for multi-agent workflows
20
20
  - **Graceful degradation** - Works with whatever tools are available, degrades features when tools missing
21
21
  - **Swarm discipline** - Enforces beads tracking, aggressive planning, and agent communication
22
22
 
23
- ## Installation
23
+ ## Quick Start
24
+
25
+ ### 1. Install Dependencies
26
+
27
+ ```bash
28
+ # OpenCode (plugin host)
29
+ brew install sst/tap/opencode
30
+
31
+ # Beads CLI (issue tracking)
32
+ npm install -g @joelhooks/beads
33
+ ```
34
+
35
+ ### 2. Install Plugin
24
36
 
25
37
  ```bash
26
38
  npm install opencode-swarm-plugin
27
- # or
28
- bun add opencode-swarm-plugin
29
- # or
30
- pnpm add opencode-swarm-plugin
31
39
  ```
32
40
 
33
- Copy the plugin to your OpenCode plugins directory:
41
+ ### 3. Create Plugin Wrapper
42
+
43
+ Create a file at `~/.config/opencode/plugins/swarm.ts`:
44
+
45
+ ```ts
46
+ import { SwarmPlugin } from "opencode-swarm-plugin";
47
+ export default SwarmPlugin;
48
+ ```
49
+
50
+ That's it! OpenCode will load the plugin automatically.
51
+
52
+ ### 4. Copy Examples (Recommended)
53
+
54
+ ```bash
55
+ # Create directories
56
+ mkdir -p ~/.config/opencode/commands ~/.config/opencode/agents
57
+
58
+ # Copy /swarm command
59
+ cp node_modules/opencode-swarm-plugin/examples/commands/swarm.md ~/.config/opencode/commands/
60
+
61
+ # Copy @swarm-planner agent
62
+ cp node_modules/opencode-swarm-plugin/examples/agents/swarm-planner.md ~/.config/opencode/agents/
63
+ ```
64
+
65
+ ### 5. Initialize Beads in Your Project
34
66
 
35
67
  ```bash
36
- cp node_modules/opencode-swarm-plugin/dist/plugin.js ~/.config/opencode/plugin/swarm.js
68
+ cd your-project
69
+ bd init
37
70
  ```
38
71
 
39
- Plugins are automatically loaded from `~/.config/opencode/plugin/` - no config file changes needed.
72
+ ### Alternative: Direct Copy (No Wrapper)
40
73
 
41
- > **Note:** The package has two entry points:
42
- >
43
- > - `dist/index.js` - Full library exports (schemas, errors, utilities, learning modules)
44
- > - `dist/plugin.js` - Plugin entry point that only exports the `plugin` function for OpenCode
74
+ If you prefer not to use the wrapper pattern:
75
+
76
+ ```bash
77
+ mkdir -p ~/.config/opencode/plugins
78
+ cp node_modules/opencode-swarm-plugin/dist/plugin.js ~/.config/opencode/plugins/swarm.js
79
+ ```
80
+
81
+ ## Dependencies
82
+
83
+ ### Required
84
+
85
+ | Dependency | Purpose | Install |
86
+ | ----------------------------------------------- | ------------------------- | --------------------------------- |
87
+ | [OpenCode](https://opencode.ai) | Plugin host | `brew install sst/tap/opencode` |
88
+ | [Beads CLI](https://github.com/joelhooks/beads) | Git-backed issue tracking | `npm install -g @joelhooks/beads` |
89
+
90
+ ### Optional (Graceful Degradation)
45
91
 
46
- ## Prerequisites
92
+ The plugin works without these, but with reduced functionality:
47
93
 
48
- | Requirement | Purpose |
49
- | ---------------- | ------------------------------------------- |
50
- | OpenCode 1.0+ | Plugin host |
51
- | Agent Mail MCP | Multi-agent coordination (`localhost:8765`) |
52
- | Beads CLI (`bd`) | Git-backed issue tracking |
94
+ | Dependency | Purpose | Without It |
95
+ | ----------------------------------------------------- | ------------------------ | ---------------------------------------- |
96
+ | [Agent Mail](https://github.com/joelhooks/agent-mail) | Multi-agent coordination | No file reservations, no agent messaging |
97
+ | [Redis](https://redis.io) | Rate limiting | Falls back to SQLite |
98
+ | [CASS](https://github.com/Dicklesworthstone/cass) | Historical context | No "similar past tasks" in decomposition |
99
+ | [UBS](https://github.com/joelhooks/ubs) | Bug scanning | No pre-completion validation |
53
100
 
54
- ### Verify Agent Mail is running
101
+ ### Verify Installation
55
102
 
56
103
  ```bash
104
+ # Check OpenCode
105
+ opencode --version
106
+
107
+ # Check Beads
108
+ bd --version
109
+
110
+ # Check Agent Mail (if using multi-agent)
57
111
  curl http://127.0.0.1:8765/health/liveness
112
+
113
+ # Check Redis (optional)
114
+ redis-cli ping
58
115
  ```
59
116
 
60
- ### Verify beads is installed
117
+ ## Installation Details
118
+
119
+ ### From npm
61
120
 
62
121
  ```bash
63
- bd --version
122
+ npm install opencode-swarm-plugin
123
+ # or
124
+ bun add opencode-swarm-plugin
125
+ # or
126
+ pnpm add opencode-swarm-plugin
127
+ ```
128
+
129
+ ### Plugin Setup (Wrapper Pattern)
130
+
131
+ Create `~/.config/opencode/plugins/swarm.ts`:
132
+
133
+ ```ts
134
+ import { SwarmPlugin } from "opencode-swarm-plugin";
135
+ export default SwarmPlugin;
136
+ ```
137
+
138
+ OpenCode runs on Bun and loads TypeScript directly - no build step needed.
139
+
140
+ ### Plugin Setup (Direct Copy)
141
+
142
+ Alternatively, copy the pre-built bundle:
143
+
144
+ ```bash
145
+ mkdir -p ~/.config/opencode/plugins
146
+ cp node_modules/opencode-swarm-plugin/dist/plugin.js ~/.config/opencode/plugins/swarm.js
147
+ ```
148
+
149
+ ### Example Files
150
+
151
+ Copy the `/swarm` command and `@swarm-planner` agent:
152
+
153
+ ```bash
154
+ # Command
155
+ mkdir -p ~/.config/opencode/commands
156
+ cp node_modules/opencode-swarm-plugin/examples/commands/swarm.md ~/.config/opencode/commands/
157
+
158
+ # Agent
159
+ mkdir -p ~/.config/opencode/agents
160
+ cp node_modules/opencode-swarm-plugin/examples/agents/swarm-planner.md ~/.config/opencode/agents/
64
161
  ```
65
162
 
163
+ > **Note:** The package has two entry points:
164
+ >
165
+ > - `dist/index.js` - Full library exports (schemas, errors, utilities, learning modules)
166
+ > - `dist/plugin.js` - Plugin entry point that only exports the `plugin` function for OpenCode
167
+
66
168
  ## Tools Reference
67
169
 
68
170
  ### Beads Tools
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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
+ "opencode-swarm-setup": "./scripts/setup.ts"
10
+ },
8
11
  "exports": {
9
12
  ".": {
10
13
  "import": "./dist/index.js",
@@ -0,0 +1,371 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * OpenCode Swarm Plugin - Setup Script
4
+ *
5
+ * Checks for required dependencies and installs them if missing.
6
+ * Mac-specific (uses Homebrew).
7
+ *
8
+ * Usage:
9
+ * bunx opencode-swarm-plugin/scripts/setup.ts
10
+ * # or after cloning:
11
+ * bun scripts/setup.ts
12
+ */
13
+
14
+ import { $ } from "bun";
15
+ import { existsSync, mkdirSync, copyFileSync } from "fs";
16
+ import { homedir } from "os";
17
+ import { join } from "path";
18
+
19
+ // ANSI colors
20
+ const green = (s: string) => `\x1b[32m${s}\x1b[0m`;
21
+ const red = (s: string) => `\x1b[31m${s}\x1b[0m`;
22
+ const yellow = (s: string) => `\x1b[33m${s}\x1b[0m`;
23
+ const blue = (s: string) => `\x1b[34m${s}\x1b[0m`;
24
+ const dim = (s: string) => `\x1b[2m${s}\x1b[0m`;
25
+
26
+ const CHECK = green("✓");
27
+ const CROSS = red("✗");
28
+ const WARN = yellow("⚠");
29
+ const INFO = blue("→");
30
+
31
+ interface Dependency {
32
+ name: string;
33
+ check: () => Promise<boolean>;
34
+ install: () => Promise<void>;
35
+ optional?: boolean;
36
+ purpose: string;
37
+ }
38
+
39
+ /**
40
+ * Check if a command exists
41
+ */
42
+ async function commandExists(cmd: string): Promise<boolean> {
43
+ try {
44
+ const result = await $`which ${cmd}`.quiet().nothrow();
45
+ return result.exitCode === 0;
46
+ } catch {
47
+ return false;
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Check if a URL is reachable
53
+ */
54
+ async function urlReachable(url: string): Promise<boolean> {
55
+ try {
56
+ const response = await fetch(url, { method: "HEAD" });
57
+ return response.ok;
58
+ } catch {
59
+ return false;
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Run a shell command with output
65
+ */
66
+ async function run(cmd: string, args: string[] = []): Promise<boolean> {
67
+ try {
68
+ const proc = Bun.spawn([cmd, ...args], {
69
+ stdout: "inherit",
70
+ stderr: "inherit",
71
+ });
72
+ const exitCode = await proc.exited;
73
+ return exitCode === 0;
74
+ } catch {
75
+ return false;
76
+ }
77
+ }
78
+
79
+ const dependencies: Dependency[] = [
80
+ {
81
+ name: "Homebrew",
82
+ purpose: "Package manager for installing other dependencies",
83
+ check: async () => commandExists("brew"),
84
+ install: async () => {
85
+ console.log(`${INFO} Installing Homebrew...`);
86
+ const script = await fetch(
87
+ "https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh",
88
+ ).then((r) => r.text());
89
+ await $`/bin/bash -c ${script}`;
90
+ },
91
+ },
92
+ {
93
+ name: "OpenCode",
94
+ purpose: "AI coding assistant (plugin host)",
95
+ check: async () => commandExists("opencode"),
96
+ install: async () => {
97
+ console.log(`${INFO} Installing OpenCode...`);
98
+ await run("brew", ["install", "sst/tap/opencode"]);
99
+ },
100
+ },
101
+ {
102
+ name: "Beads CLI (bd)",
103
+ purpose: "Git-backed issue tracking",
104
+ check: async () => commandExists("bd"),
105
+ install: async () => {
106
+ console.log(`${INFO} Installing Beads...`);
107
+ // Try npm global install first
108
+ const npmResult = await $`npm install -g @joelhooks/beads`
109
+ .quiet()
110
+ .nothrow();
111
+ if (npmResult.exitCode !== 0) {
112
+ console.log(`${WARN} npm install failed, trying go install...`);
113
+ await run("go", [
114
+ "install",
115
+ "github.com/joelhooks/beads/cmd/bd@latest",
116
+ ]);
117
+ }
118
+ },
119
+ },
120
+ {
121
+ name: "Agent Mail MCP",
122
+ purpose: "Multi-agent coordination server",
123
+ check: async () => {
124
+ // Check if server is running
125
+ const running = await urlReachable(
126
+ "http://127.0.0.1:8765/health/liveness",
127
+ );
128
+ if (running) return true;
129
+ // Check if binary exists
130
+ return commandExists("agent-mail");
131
+ },
132
+ install: async () => {
133
+ console.log(`${INFO} Installing Agent Mail...`);
134
+ console.log(dim(" Agent Mail requires manual setup:"));
135
+ console.log(
136
+ dim(" 1. Clone: git clone https://github.com/joelhooks/agent-mail"),
137
+ );
138
+ console.log(
139
+ dim(" 2. Build: cd agent-mail && go build -o agent-mail ./cmd/server"),
140
+ );
141
+ console.log(dim(" 3. Run: ./agent-mail serve"));
142
+ console.log();
143
+ console.log(
144
+ `${WARN} Skipping automatic install - see instructions above`,
145
+ );
146
+ },
147
+ optional: true,
148
+ },
149
+ {
150
+ name: "Redis",
151
+ purpose: "Rate limiting (optional, falls back to SQLite)",
152
+ check: async () => {
153
+ // Check if redis-server is running or installed
154
+ const running = await $`redis-cli ping`.quiet().nothrow();
155
+ if (running.exitCode === 0) return true;
156
+ return commandExists("redis-server");
157
+ },
158
+ install: async () => {
159
+ console.log(`${INFO} Installing Redis...`);
160
+ await run("brew", ["install", "redis"]);
161
+ console.log(dim(" Start with: brew services start redis"));
162
+ },
163
+ optional: true,
164
+ },
165
+ {
166
+ name: "CASS (cass-memory)",
167
+ purpose: "Cross-agent session search for historical context",
168
+ check: async () => commandExists("cass"),
169
+ install: async () => {
170
+ console.log(`${INFO} CASS installation...`);
171
+ console.log(
172
+ dim(" Install from: https://github.com/Dicklesworthstone/cass"),
173
+ );
174
+ console.log(`${WARN} Skipping automatic install - see link above`);
175
+ },
176
+ optional: true,
177
+ },
178
+ {
179
+ name: "UBS (bug scanner)",
180
+ purpose: "Pre-completion bug scanning",
181
+ check: async () => commandExists("ubs"),
182
+ install: async () => {
183
+ console.log(`${INFO} UBS installation...`);
184
+ console.log(dim(" UBS is bundled with OpenCode plugins"));
185
+ console.log(
186
+ `${WARN} Skipping - should be available if OpenCode is installed`,
187
+ );
188
+ },
189
+ optional: true,
190
+ },
191
+ ];
192
+
193
+ /**
194
+ * Setup OpenCode directories and copy plugin
195
+ */
196
+ async function setupOpenCodeDirs(): Promise<void> {
197
+ const configDir = join(homedir(), ".config", "opencode");
198
+ const pluginsDir = join(configDir, "plugins");
199
+ const commandsDir = join(configDir, "commands");
200
+ const agentsDir = join(configDir, "agents");
201
+
202
+ // Create directories
203
+ for (const dir of [pluginsDir, commandsDir, agentsDir]) {
204
+ if (!existsSync(dir)) {
205
+ mkdirSync(dir, { recursive: true });
206
+ console.log(`${CHECK} Created ${dir}`);
207
+ }
208
+ }
209
+
210
+ // Find plugin files (either in node_modules or local)
211
+ const possiblePaths = [
212
+ join(process.cwd(), "dist", "plugin.js"),
213
+ join(
214
+ process.cwd(),
215
+ "node_modules",
216
+ "opencode-swarm-plugin",
217
+ "dist",
218
+ "plugin.js",
219
+ ),
220
+ ];
221
+
222
+ let pluginSrc: string | null = null;
223
+ for (const p of possiblePaths) {
224
+ if (existsSync(p)) {
225
+ pluginSrc = p;
226
+ break;
227
+ }
228
+ }
229
+
230
+ if (pluginSrc) {
231
+ const pluginDest = join(pluginsDir, "swarm.js");
232
+ copyFileSync(pluginSrc, pluginDest);
233
+ console.log(`${CHECK} Copied plugin to ${pluginDest}`);
234
+ } else {
235
+ console.log(
236
+ `${WARN} Plugin not found - run 'pnpm build' first or install from npm`,
237
+ );
238
+ }
239
+
240
+ // Copy example files if they exist
241
+ const examplesDir = join(process.cwd(), "examples");
242
+ const nodeModulesExamples = join(
243
+ process.cwd(),
244
+ "node_modules",
245
+ "opencode-swarm-plugin",
246
+ "examples",
247
+ );
248
+
249
+ const examplesSrc = existsSync(examplesDir)
250
+ ? examplesDir
251
+ : nodeModulesExamples;
252
+
253
+ if (existsSync(examplesSrc)) {
254
+ const swarmCmd = join(examplesSrc, "commands", "swarm.md");
255
+ const plannerAgent = join(examplesSrc, "agents", "swarm-planner.md");
256
+
257
+ if (existsSync(swarmCmd)) {
258
+ copyFileSync(swarmCmd, join(commandsDir, "swarm.md"));
259
+ console.log(`${CHECK} Copied /swarm command`);
260
+ }
261
+
262
+ if (existsSync(plannerAgent)) {
263
+ copyFileSync(plannerAgent, join(agentsDir, "swarm-planner.md"));
264
+ console.log(`${CHECK} Copied @swarm-planner agent`);
265
+ }
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Main setup function
271
+ */
272
+ async function main() {
273
+ console.log();
274
+ console.log(
275
+ blue("═══════════════════════════════════════════════════════════"),
276
+ );
277
+ console.log(blue(" OpenCode Swarm Plugin - Setup"));
278
+ console.log(
279
+ blue("═══════════════════════════════════════════════════════════"),
280
+ );
281
+ console.log();
282
+
283
+ // Check platform
284
+ if (process.platform !== "darwin") {
285
+ console.log(
286
+ `${WARN} This script is optimized for macOS. Some installs may not work.`,
287
+ );
288
+ console.log();
289
+ }
290
+
291
+ // Check dependencies
292
+ console.log(blue("Checking dependencies...\n"));
293
+
294
+ const missing: Dependency[] = [];
295
+ const optionalMissing: Dependency[] = [];
296
+
297
+ for (const dep of dependencies) {
298
+ const installed = await dep.check();
299
+ const status = installed ? CHECK : dep.optional ? WARN : CROSS;
300
+ const suffix = dep.optional ? dim(" (optional)") : "";
301
+
302
+ console.log(`${status} ${dep.name}${suffix}`);
303
+ console.log(dim(` ${dep.purpose}`));
304
+
305
+ if (!installed) {
306
+ if (dep.optional) {
307
+ optionalMissing.push(dep);
308
+ } else {
309
+ missing.push(dep);
310
+ }
311
+ }
312
+ }
313
+
314
+ console.log();
315
+
316
+ // Install missing required dependencies
317
+ if (missing.length > 0) {
318
+ console.log(blue("Installing missing required dependencies...\n"));
319
+
320
+ for (const dep of missing) {
321
+ try {
322
+ await dep.install();
323
+ const nowInstalled = await dep.check();
324
+ if (nowInstalled) {
325
+ console.log(`${CHECK} ${dep.name} installed successfully\n`);
326
+ } else {
327
+ console.log(`${CROSS} ${dep.name} installation may have failed\n`);
328
+ }
329
+ } catch (error) {
330
+ console.log(`${CROSS} Failed to install ${dep.name}: ${error}\n`);
331
+ }
332
+ }
333
+ }
334
+
335
+ // Offer to install optional dependencies
336
+ if (optionalMissing.length > 0) {
337
+ console.log(yellow("Optional dependencies not installed:"));
338
+ for (const dep of optionalMissing) {
339
+ console.log(` - ${dep.name}: ${dep.purpose}`);
340
+ }
341
+ console.log();
342
+ console.log(
343
+ dim("The plugin will work without these, with degraded features."),
344
+ );
345
+ console.log();
346
+ }
347
+
348
+ // Setup OpenCode directories
349
+ console.log(blue("Setting up OpenCode directories...\n"));
350
+ await setupOpenCodeDirs();
351
+
352
+ console.log();
353
+ console.log(
354
+ blue("═══════════════════════════════════════════════════════════"),
355
+ );
356
+ console.log(blue(" Setup Complete!"));
357
+ console.log(
358
+ blue("═══════════════════════════════════════════════════════════"),
359
+ );
360
+ console.log();
361
+ console.log("Next steps:");
362
+ console.log(
363
+ ` 1. ${dim("Start Agent Mail (if using multi-agent):")} agent-mail serve`,
364
+ );
365
+ console.log(` 2. ${dim("Initialize beads in your project:")} bd init`);
366
+ console.log(` 3. ${dim("Start OpenCode:")} opencode`);
367
+ console.log(` 4. ${dim("Try the swarm command:")} /swarm "your task here"`);
368
+ console.log();
369
+ }
370
+
371
+ main().catch(console.error);