opencode-swarm-plugin 0.57.6 → 0.58.4

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
@@ -63,11 +63,12 @@ import { tmpdir } from "os";
63
63
 
64
64
  // Query & observability tools
65
65
  import {
66
- executeQuery,
66
+ executeQueryCLI,
67
67
  executePreset,
68
68
  formatAsTable,
69
69
  formatAsCSV,
70
70
  formatAsJSON,
71
+ type QueryResult,
71
72
  } from "../src/query-tools.js";
72
73
  import {
73
74
  getWorkerStatus,
@@ -2831,12 +2832,27 @@ async function claudeCommand() {
2831
2832
  case "user-prompt":
2832
2833
  await claudeUserPrompt();
2833
2834
  break;
2835
+ case "pre-edit":
2836
+ await claudePreEdit();
2837
+ break;
2838
+ case "pre-complete":
2839
+ await claudePreComplete();
2840
+ break;
2841
+ case "post-complete":
2842
+ await claudePostComplete();
2843
+ break;
2834
2844
  case "pre-compact":
2835
2845
  await claudePreCompact();
2836
2846
  break;
2837
2847
  case "session-end":
2838
2848
  await claudeSessionEnd();
2839
2849
  break;
2850
+ case "track-tool":
2851
+ await claudeTrackTool(Bun.argv[4]); // tool name is 4th arg
2852
+ break;
2853
+ case "compliance":
2854
+ await claudeCompliance();
2855
+ break;
2840
2856
  default:
2841
2857
  console.error(`Unknown subcommand: ${subcommand}`);
2842
2858
  showClaudeHelp();
@@ -2855,6 +2871,9 @@ Commands:
2855
2871
  init Create project-local .claude/ config
2856
2872
  session-start Hook: session start context (JSON output)
2857
2873
  user-prompt Hook: prompt submit context (JSON output)
2874
+ pre-edit Hook: pre-Edit/Write reminder (hivemind check)
2875
+ pre-complete Hook: pre-swarm_complete checklist
2876
+ post-complete Hook: post-swarm_complete learnings reminder
2858
2877
  pre-compact Hook: pre-compaction handler
2859
2878
  session-end Hook: session cleanup
2860
2879
  `);
@@ -3286,6 +3305,319 @@ async function claudeSessionEnd() {
3286
3305
  }
3287
3306
  }
3288
3307
 
3308
+ /**
3309
+ * Claude hook: pre-edit reminder for workers
3310
+ *
3311
+ * Runs BEFORE Edit/Write tool calls. Reminds worker to query hivemind first.
3312
+ * This is a gentle nudge, not a blocker.
3313
+ */
3314
+ async function claudePreEdit() {
3315
+ try {
3316
+ const input = await readHookInput<ClaudeHookInput & { tool_name?: string; tool_input?: Record<string, unknown> }>();
3317
+
3318
+ // Only inject reminder if this looks like a worker context
3319
+ // (workers have initialized via swarmmail_init)
3320
+ const projectPath = resolveClaudeProjectPath(input);
3321
+
3322
+ // Check if we've seen hivemind_find in this session
3323
+ // For now, we always remind - tracking will come later
3324
+ const contextLines: string[] = [];
3325
+
3326
+ contextLines.push("⚠️ **Before this edit**: Did you run `hivemind_find` to check for existing solutions?");
3327
+ contextLines.push("If you haven't queried hivemind yet, consider doing so to avoid re-solving problems.");
3328
+
3329
+ writeClaudeHookOutput("PreToolUse:Edit", contextLines.join("\n"), { suppressOutput: true });
3330
+ } catch (error) {
3331
+ // Non-fatal - don't block the edit
3332
+ console.error(error instanceof Error ? error.message : String(error));
3333
+ }
3334
+ }
3335
+
3336
+ /**
3337
+ * Claude hook: pre-complete check for workers
3338
+ *
3339
+ * Runs BEFORE swarm_complete. Checks compliance with mandatory steps
3340
+ * and warns if any were skipped.
3341
+ */
3342
+ async function claudePreComplete() {
3343
+ try {
3344
+ const input = await readHookInput<ClaudeHookInput & { tool_input?: Record<string, unknown> }>();
3345
+ const projectPath = resolveClaudeProjectPath(input);
3346
+ const contextLines: string[] = [];
3347
+
3348
+ // Check session tracking markers
3349
+ const trackingDir = join(projectPath, ".claude", ".worker-tracking");
3350
+ const sessionId = (input as { session_id?: string }).session_id || "";
3351
+ const sessionDir = join(trackingDir, sessionId.slice(0, 8));
3352
+
3353
+ const mandatoryTools = [
3354
+ { name: "swarmmail_init", label: "Initialize coordination" },
3355
+ { name: "hivemind_find", label: "Query past learnings" },
3356
+ ];
3357
+ const recommendedTools = [
3358
+ { name: "skills_use", label: "Load relevant skills" },
3359
+ { name: "hivemind_store", label: "Store new learnings" },
3360
+ ];
3361
+
3362
+ const missing: string[] = [];
3363
+ const skippedRecommended: string[] = [];
3364
+
3365
+ for (const tool of mandatoryTools) {
3366
+ const markerPath = join(sessionDir, `${tool.name}.marker`);
3367
+ if (!existsSync(markerPath)) {
3368
+ missing.push(tool.label);
3369
+ }
3370
+ }
3371
+
3372
+ for (const tool of recommendedTools) {
3373
+ const markerPath = join(sessionDir, `${tool.name}.marker`);
3374
+ if (!existsSync(markerPath)) {
3375
+ skippedRecommended.push(tool.label);
3376
+ }
3377
+ }
3378
+
3379
+ if (missing.length > 0) {
3380
+ contextLines.push("⚠️ **MANDATORY STEPS SKIPPED:**");
3381
+ for (const step of missing) {
3382
+ contextLines.push(` ❌ ${step}`);
3383
+ }
3384
+ contextLines.push("");
3385
+ contextLines.push("These steps are critical for swarm coordination.");
3386
+ contextLines.push("Consider running `hivemind_find` before future completions.");
3387
+ }
3388
+
3389
+ if (skippedRecommended.length > 0 && missing.length === 0) {
3390
+ contextLines.push("📝 **Recommended steps not observed:**");
3391
+ for (const step of skippedRecommended) {
3392
+ contextLines.push(` - ${step}`);
3393
+ }
3394
+ }
3395
+
3396
+ if (missing.length === 0 && skippedRecommended.length === 0) {
3397
+ contextLines.push("✅ **All mandatory and recommended steps completed!**");
3398
+ }
3399
+
3400
+ // Emit compliance event for analytics
3401
+ try {
3402
+ const swarmMail = await getSwarmMailLibSQL(projectPath);
3403
+ const db = await swarmMail.getDatabase(projectPath);
3404
+
3405
+ await db.execute({
3406
+ sql: `INSERT INTO swarm_events (event_type, project_path, agent_name, epic_id, bead_id, payload, created_at)
3407
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`,
3408
+ args: [
3409
+ "worker_compliance",
3410
+ projectPath,
3411
+ "worker",
3412
+ "",
3413
+ "",
3414
+ JSON.stringify({
3415
+ session_id: sessionId,
3416
+ mandatory_skipped: missing.length,
3417
+ recommended_skipped: skippedRecommended.length,
3418
+ score: Math.round(((mandatoryTools.length - missing.length) / mandatoryTools.length) * 100),
3419
+ }),
3420
+ ],
3421
+ });
3422
+ } catch {
3423
+ // Non-fatal
3424
+ }
3425
+
3426
+ if (contextLines.length > 0) {
3427
+ writeClaudeHookOutput("PreToolUse:swarm_complete", contextLines.join("\n"), { suppressOutput: missing.length === 0 });
3428
+ }
3429
+ } catch (error) {
3430
+ console.error(error instanceof Error ? error.message : String(error));
3431
+ }
3432
+ }
3433
+
3434
+ /**
3435
+ * Claude hook: post-complete reminder for workers
3436
+ *
3437
+ * Runs AFTER swarm_complete. Reminds to store learnings if any were discovered.
3438
+ */
3439
+ async function claudePostComplete() {
3440
+ try {
3441
+ const input = await readHookInput<ClaudeHookInput & { tool_output?: string }>();
3442
+ const contextLines: string[] = [];
3443
+
3444
+ contextLines.push("✅ Task completed. If you discovered anything valuable during this work:");
3445
+ contextLines.push("```");
3446
+ contextLines.push("hivemind_store(");
3447
+ contextLines.push(' information="<what you learned, WHY it matters>",');
3448
+ contextLines.push(' tags="<domain, pattern-type>"');
3449
+ contextLines.push(")");
3450
+ contextLines.push("```");
3451
+ contextLines.push("**The swarm's collective intelligence grows when agents share learnings.**");
3452
+
3453
+ writeClaudeHookOutput("PostToolUse:swarm_complete", contextLines.join("\n"), { suppressOutput: true });
3454
+ } catch (error) {
3455
+ console.error(error instanceof Error ? error.message : String(error));
3456
+ }
3457
+ }
3458
+
3459
+ /**
3460
+ * Track when a mandatory tool is called.
3461
+ *
3462
+ * Creates a session-specific marker file that records tool usage.
3463
+ * These markers are checked at swarm_complete to calculate compliance.
3464
+ */
3465
+ async function claudeTrackTool(toolName: string) {
3466
+ if (!toolName) return;
3467
+
3468
+ try {
3469
+ const input = await readHookInput<ClaudeHookInput>();
3470
+ const projectPath = resolveClaudeProjectPath(input);
3471
+
3472
+ // Get or create session tracking directory
3473
+ const trackingDir = join(projectPath, ".claude", ".worker-tracking");
3474
+ const sessionId = (input as { session_id?: string }).session_id || `unknown-${Date.now()}`;
3475
+ const sessionDir = join(trackingDir, sessionId.slice(0, 8)); // Use first 8 chars of session ID
3476
+
3477
+ if (!existsSync(sessionDir)) {
3478
+ mkdirSync(sessionDir, { recursive: true });
3479
+ }
3480
+
3481
+ // Write marker file for this tool
3482
+ const markerPath = join(sessionDir, `${toolName}.marker`);
3483
+ writeFileSync(markerPath, new Date().toISOString());
3484
+
3485
+ // Also emit an event for long-term analytics (non-blocking)
3486
+ try {
3487
+ const swarmMail = await getSwarmMailLibSQL(projectPath);
3488
+ const db = await swarmMail.getDatabase(projectPath);
3489
+
3490
+ await db.execute({
3491
+ sql: `INSERT INTO swarm_events (event_type, project_path, agent_name, epic_id, bead_id, payload, created_at)
3492
+ VALUES (?, ?, ?, ?, ?, ?, datetime('now'))`,
3493
+ args: [
3494
+ "worker_tool_call",
3495
+ projectPath,
3496
+ "worker", // We don't have agent name in hook context
3497
+ "",
3498
+ "",
3499
+ JSON.stringify({ tool: toolName, session_id: sessionId }),
3500
+ ],
3501
+ });
3502
+ } catch {
3503
+ // Non-fatal - tracking is best-effort
3504
+ }
3505
+ } catch (error) {
3506
+ // Silent failure - don't interrupt the tool call
3507
+ console.error(`[track-tool] ${error instanceof Error ? error.message : String(error)}`);
3508
+ }
3509
+ }
3510
+
3511
+ /**
3512
+ * Check worker compliance - which mandatory tools were called.
3513
+ *
3514
+ * Returns compliance data based on session tracking markers.
3515
+ */
3516
+ async function claudeCompliance() {
3517
+ try {
3518
+ const input = await readHookInput<ClaudeHookInput>();
3519
+ const projectPath = resolveClaudeProjectPath(input);
3520
+
3521
+ const trackingDir = join(projectPath, ".claude", ".worker-tracking");
3522
+ const sessionId = (input as { session_id?: string }).session_id || "";
3523
+ const sessionDir = join(trackingDir, sessionId.slice(0, 8));
3524
+
3525
+ const mandatoryTools = ["swarmmail_init", "hivemind_find", "skills_use"];
3526
+ const recommendedTools = ["hivemind_store"];
3527
+
3528
+ const compliance: Record<string, boolean> = {};
3529
+
3530
+ for (const tool of [...mandatoryTools, ...recommendedTools]) {
3531
+ const markerPath = join(sessionDir, `${tool}.marker`);
3532
+ compliance[tool] = existsSync(markerPath);
3533
+ }
3534
+
3535
+ const mandatoryCount = mandatoryTools.filter(t => compliance[t]).length;
3536
+ const score = Math.round((mandatoryCount / mandatoryTools.length) * 100);
3537
+
3538
+ console.log(JSON.stringify({
3539
+ session_id: sessionId,
3540
+ compliance,
3541
+ mandatory_score: score,
3542
+ mandatory_met: mandatoryCount,
3543
+ mandatory_total: mandatoryTools.length,
3544
+ stored_learnings: compliance.hivemind_store,
3545
+ }));
3546
+ } catch (error) {
3547
+ console.error(error instanceof Error ? error.message : String(error));
3548
+ }
3549
+ }
3550
+
3551
+ /**
3552
+ * Query worker compliance stats across all sessions.
3553
+ */
3554
+ async function showWorkerCompliance() {
3555
+ const projectPath = process.cwd();
3556
+
3557
+ try {
3558
+ // Use shared executeQuery (handles DB connection internally)
3559
+ const toolUsageSql = `SELECT
3560
+ json_extract(payload, '$.tool') as tool,
3561
+ COUNT(*) as count,
3562
+ COUNT(DISTINCT json_extract(payload, '$.session_id')) as sessions
3563
+ FROM swarm_events
3564
+ WHERE event_type = 'worker_tool_call'
3565
+ AND created_at > datetime('now', '-7 days')
3566
+ GROUP BY tool
3567
+ ORDER BY count DESC`;
3568
+
3569
+ const toolResult = await executeQueryCLI(projectPath, toolUsageSql);
3570
+ const rows = toolResult.rows;
3571
+
3572
+ console.log(yellow(BANNER));
3573
+ console.log(cyan("\n📊 Worker Tool Usage (Last 7 Days)\n"));
3574
+
3575
+ if (rows.length === 0) {
3576
+ console.log(dim("No worker tool usage data found."));
3577
+ console.log(dim("Data is collected when workers run with the Claude Code plugin."));
3578
+ return;
3579
+ }
3580
+
3581
+ console.log(dim("Tool Calls Sessions"));
3582
+ console.log(dim("─".repeat(45)));
3583
+
3584
+ for (const row of rows) {
3585
+ const tool = String(row.tool).padEnd(22);
3586
+ const count = String(row.count).padStart(6);
3587
+ const sessions = String(row.sessions).padStart(8);
3588
+ console.log(`${tool} ${count} ${sessions}`);
3589
+ }
3590
+
3591
+ // Calculate compliance rate
3592
+ const hivemindFinds = rows.find(r => r.tool === "hivemind_find");
3593
+ const completesSql = `SELECT COUNT(*) as count FROM swarm_events
3594
+ WHERE event_type = 'worker_completed'
3595
+ AND created_at > datetime('now', '-7 days')`;
3596
+ const completesResult = await executeQueryCLI(projectPath, completesSql);
3597
+
3598
+ const completes = Number(completesResult.rows[0]?.count || 0);
3599
+ const finds = Number(hivemindFinds?.count || 0);
3600
+
3601
+ if (completes > 0) {
3602
+ const rate = Math.round((Math.min(finds, completes) / completes) * 100);
3603
+ console.log(dim("\n─".repeat(45)));
3604
+ console.log(`\n${green("Hivemind compliance rate:")} ${rate}% (${finds} queries / ${completes} completions)`);
3605
+ }
3606
+
3607
+ } catch (error) {
3608
+ const msg = error instanceof Error ? error.message : String(error);
3609
+ if (msg.includes("no such table")) {
3610
+ console.log(yellow(BANNER));
3611
+ console.log(cyan("\n📊 Worker Tool Usage\n"));
3612
+ console.log(dim("No tracking data yet."));
3613
+ console.log(dim("Compliance tracking starts when workers run with the Claude Code plugin hooks."));
3614
+ console.log(dim("\nThe swarm_events table will be created on first tracked tool call."));
3615
+ } else {
3616
+ console.error("Error fetching compliance data:", msg);
3617
+ }
3618
+ }
3619
+ }
3620
+
3289
3621
  async function init() {
3290
3622
  p.intro("swarm init v" + VERSION);
3291
3623
 
@@ -3633,16 +3965,16 @@ async function query() {
3633
3965
  const projectPath = process.cwd();
3634
3966
 
3635
3967
  try {
3636
- let rows: any[];
3968
+ let result: QueryResult;
3637
3969
 
3638
3970
  if (parsed.preset) {
3639
3971
  // Execute preset query
3640
3972
  p.log.step(`Executing preset: ${parsed.preset}`);
3641
- rows = await executePreset(projectPath, parsed.preset);
3973
+ result = await executePreset(projectPath, parsed.preset);
3642
3974
  } else if (parsed.query) {
3643
3975
  // Execute custom SQL
3644
3976
  p.log.step("Executing custom SQL");
3645
- rows = await executeQuery(projectPath, parsed.query);
3977
+ result = await executeQueryCLI(projectPath, parsed.query);
3646
3978
  } else {
3647
3979
  p.log.error("No query specified. Use --sql or --preset");
3648
3980
  p.outro("Aborted");
@@ -3653,14 +3985,14 @@ async function query() {
3653
3985
  let output: string;
3654
3986
  switch (parsed.format) {
3655
3987
  case "csv":
3656
- output = formatAsCSV(rows);
3988
+ output = formatAsCSV(result);
3657
3989
  break;
3658
3990
  case "json":
3659
- output = formatAsJSON(rows);
3991
+ output = formatAsJSON(result);
3660
3992
  break;
3661
3993
  case "table":
3662
3994
  default:
3663
- output = formatAsTable(rows);
3995
+ output = formatAsTable(result);
3664
3996
  break;
3665
3997
  }
3666
3998
 
@@ -3668,7 +4000,7 @@ async function query() {
3668
4000
  console.log(output);
3669
4001
  console.log();
3670
4002
 
3671
- p.outro(`Found ${rows.length} result(s)`);
4003
+ p.outro(`Found ${result.rowCount} result(s)`);
3672
4004
  } catch (error) {
3673
4005
  p.log.error("Query failed");
3674
4006
  p.log.message(error instanceof Error ? error.message : String(error));
@@ -4032,6 +4364,7 @@ ${cyan("Commands:")}
4032
4364
  swarm eval Eval-driven development commands
4033
4365
  swarm query SQL analytics with presets (--sql, --preset, --format)
4034
4366
  swarm dashboard Live terminal UI with worker status (--epic, --refresh)
4367
+ swarm compliance Worker tool usage compliance stats (hivemind, skills, etc)
4035
4368
  swarm replay Event replay with timing (--speed, --type, --agent, --since, --until)
4036
4369
  swarm export Export events (--format otlp/csv/json, --epic, --output)
4037
4370
  swarm tree Visualize cell hierarchy as ASCII tree (--status, --epic, --json)
@@ -4118,6 +4451,7 @@ ${cyan("Observability Commands:")}
4118
4451
  swarm export --format csv Export as CSV
4119
4452
  swarm export --epic <id> Export specific epic only
4120
4453
  swarm export --output <file> Write to file instead of stdout
4454
+ swarm compliance Show worker tool usage compliance stats
4121
4455
  swarm tree Show all cells as tree
4122
4456
  swarm tree --status open Show only open cells
4123
4457
  swarm tree --epic <id> Show specific epic subtree
@@ -4142,6 +4476,11 @@ ${cyan("Claude Code:")}
4142
4476
  swarm claude init Create project-local .claude config
4143
4477
  swarm claude session-start Hook: session start context
4144
4478
  swarm claude user-prompt Hook: prompt submit context
4479
+ swarm claude pre-edit Hook: pre-Edit/Write (hivemind reminder)
4480
+ swarm claude pre-complete Hook: pre-swarm_complete (compliance check)
4481
+ swarm claude post-complete Hook: post-swarm_complete (store learnings)
4482
+ swarm claude track-tool <name> Hook: track mandatory tool usage
4483
+ swarm claude compliance Hook: show session compliance data
4145
4484
  swarm claude pre-compact Hook: pre-compaction handler
4146
4485
  swarm claude session-end Hook: session cleanup
4147
4486
 
@@ -7449,6 +7788,9 @@ switch (command) {
7449
7788
  case "dashboard":
7450
7789
  await dashboard();
7451
7790
  break;
7791
+ case "compliance":
7792
+ await showWorkerCompliance();
7793
+ break;
7452
7794
  case "replay":
7453
7795
  await replay();
7454
7796
  break;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "swarm",
3
3
  "description": "Multi-agent task decomposition and coordination for Claude Code",
4
- "version": "0.57.5",
4
+ "version": "0.58.3",
5
5
  "author": {
6
6
  "name": "Joel Hooks"
7
7
  },
@@ -0,0 +1,187 @@
1
+ # Swarm Plugin for Claude Code
2
+
3
+ Multi-agent task decomposition and coordination for Claude Code.
4
+
5
+ ## Prerequisites
6
+
7
+ The swarm CLI must be installed globally:
8
+
9
+ ```bash
10
+ npm install -g opencode-swarm-plugin
11
+ # or
12
+ bun add -g opencode-swarm-plugin
13
+ ```
14
+
15
+ Verify installation:
16
+
17
+ ```bash
18
+ swarm version
19
+ ```
20
+
21
+ ## Installation
22
+
23
+ ### Via Marketplace (Recommended)
24
+
25
+ Add the marketplace to your Claude Code settings, then install:
26
+
27
+ ```bash
28
+ claude /plugin install swarm
29
+ ```
30
+
31
+ ### Via Plugin Directory (Development)
32
+
33
+ ```bash
34
+ claude --plugin-dir /path/to/opencode-swarm-plugin/packages/opencode-swarm-plugin/claude-plugin
35
+ ```
36
+
37
+ ## Usage
38
+
39
+ ### Start a Swarm
40
+
41
+ Use the `/swarm:swarm` command to decompose a task into parallel subtasks:
42
+
43
+ ```
44
+ /swarm:swarm Add user authentication with OAuth support
45
+ ```
46
+
47
+ The coordinator will:
48
+ 1. Query hivemind for past learnings
49
+ 2. Clarify scope if needed
50
+ 3. Decompose into parallel subtasks
51
+ 4. Spawn workers for each subtask
52
+ 5. Review completed work
53
+ 6. Store learnings for future swarms
54
+
55
+ ### Other Commands
56
+
57
+ | Command | Description |
58
+ |---------|-------------|
59
+ | `/swarm:swarm <task>` | Decompose and execute a multi-agent task |
60
+ | `/swarm:status` | Check worker progress, inbox, reservations |
61
+ | `/swarm:inbox` | Review inter-agent messages |
62
+ | `/swarm:hive` | Query and manage tasks (cells) |
63
+ | `/swarm:handoff` | End session, release locks, sync state |
64
+
65
+ ### Skills (Auto-Invoked)
66
+
67
+ The plugin includes skills that Claude uses automatically:
68
+
69
+ - **swarm-coordination** - Multi-agent workflow guidance
70
+ - **always-on-guidance** - Model-specific defaults and best practices
71
+
72
+ ## Available Tools
73
+
74
+ ### Coordinator Tools
75
+
76
+ | Tool | Purpose |
77
+ |------|---------|
78
+ | `hivemind_find` | Query past learnings before decomposing |
79
+ | `hivemind_store` | Store discovered patterns |
80
+ | `swarm_decompose` | Break task into subtasks |
81
+ | `swarm_spawn_subtask` | Launch worker for subtask |
82
+ | `swarm_spawn_researcher` | Launch read-only researcher |
83
+ | `swarm_review` | Review worker output |
84
+ | `swarm_status` | Check epic progress |
85
+ | `hive_create_epic` | Create epic with subtasks |
86
+ | `hive_query` | Query task database |
87
+ | `swarmmail_*` | Inter-agent messaging |
88
+
89
+ ### Worker Tools
90
+
91
+ | Tool | Purpose |
92
+ |------|---------|
93
+ | `hivemind_find` | Query learnings before starting |
94
+ | `hivemind_store` | Store what you learn |
95
+ | `swarm_progress` | Report progress (25/50/75%) |
96
+ | `swarm_checkpoint` | Save context before risky ops |
97
+ | `swarm_complete` | Signal task completion |
98
+ | `hive_update` | Update task status |
99
+ | `hive_close` | Close completed task |
100
+ | `swarmmail_reserve` | Reserve files exclusively |
101
+ | `swarmmail_send` | Message other agents |
102
+
103
+ ## Architecture
104
+
105
+ ```
106
+ Coordinator (Opus)
107
+ ├── Queries hivemind for past learnings
108
+ ├── Decomposes task into subtasks
109
+ ├── Spawns workers (Sonnet)
110
+ ├── Reviews each worker before spawning next
111
+ └── Stores coordination learnings
112
+
113
+ Workers (Sonnet)
114
+ ├── Query hivemind before starting
115
+ ├── Reserve files to prevent conflicts
116
+ ├── Execute scoped subtask
117
+ ├── Report progress at 25/50/75%
118
+ └── Store domain learnings
119
+
120
+ Researchers (Opus, read-only)
121
+ ├── Fetch documentation
122
+ ├── Analyze dependencies
123
+ └── Store findings in hivemind
124
+ ```
125
+
126
+ ## Troubleshooting
127
+
128
+ ### MCP Server Not Connecting
129
+
130
+ Ensure swarm is installed globally and in your PATH:
131
+
132
+ ```bash
133
+ which swarm
134
+ swarm mcp-serve # Should start without errors
135
+ ```
136
+
137
+ ### Tools Not Available
138
+
139
+ Check that the MCP server is running:
140
+
141
+ ```bash
142
+ claude mcp list
143
+ ```
144
+
145
+ The `swarm-tools` server should show as connected.
146
+
147
+ ### File Conflicts
148
+
149
+ If multiple agents are editing the same file, use file reservations:
150
+
151
+ ```
152
+ swarmmail_reserve(paths: ["src/**/*.ts"], exclusive: true)
153
+ ```
154
+
155
+ ### Context Exhaustion
156
+
157
+ Use checkpoints before large operations:
158
+
159
+ ```
160
+ swarm_checkpoint(reason: "Before refactor")
161
+ ```
162
+
163
+ ## Development
164
+
165
+ ### Testing Locally
166
+
167
+ ```bash
168
+ # From the plugin directory
169
+ claude --plugin-dir .
170
+ ```
171
+
172
+ ### Building
173
+
174
+ ```bash
175
+ cd packages/opencode-swarm-plugin
176
+ bun run build
177
+ ```
178
+
179
+ ### Running Tests
180
+
181
+ ```bash
182
+ bun test
183
+ ```
184
+
185
+ ## License
186
+
187
+ MIT
@@ -5,6 +5,7 @@ model: sonnet
5
5
  skills:
6
6
  - always-on-guidance
7
7
  - swarm-coordination
8
+ - swarm-cli
8
9
  - testing-patterns
9
10
  - system-design
10
11
  tools: