opencode-swarm-plugin 0.15.0 → 0.17.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.
@@ -27,6 +27,49 @@ Swarm Mail is embedded (no external server needed) and provides:
27
27
 
28
28
  ## Workflow
29
29
 
30
+ ### 0. Task Clarity Check (BEFORE ANYTHING ELSE)
31
+
32
+ **Before decomposing, ask yourself: Is this task clear enough to parallelize?**
33
+
34
+ **Vague Task Signals:**
35
+
36
+ - No specific files or components mentioned
37
+ - Vague verbs: "improve", "fix", "update", "make better"
38
+ - Large scope without constraints: "refactor the codebase"
39
+ - Missing success criteria: "add auth" (what kind? OAuth? JWT? Session?)
40
+ - Ambiguous boundaries: "handle errors" (which errors? where?)
41
+
42
+ **If task is vague, ASK QUESTIONS FIRST:**
43
+
44
+ ```
45
+ The task "<task>" needs clarification before I can decompose it effectively.
46
+
47
+ 1. [Specific question about scope/files/approach]
48
+
49
+ Options:
50
+ a) [Option A with trade-off]
51
+ b) [Option B with trade-off]
52
+ c) [Option C with trade-off]
53
+
54
+ Which approach, or should I explore something else?
55
+ ```
56
+
57
+ **Rules for clarifying questions:**
58
+
59
+ - ONE question at a time (don't overwhelm)
60
+ - Offer 2-3 concrete options when possible
61
+ - Lead with your recommendation and why
62
+ - Wait for answer before next question
63
+
64
+ **Clear Task Signals (proceed to decompose):**
65
+
66
+ - Specific files or directories mentioned
67
+ - Concrete action verbs: "add X to Y", "migrate A to B", "extract C from D"
68
+ - Defined scope: "the auth module", "API routes in /api/v2"
69
+ - Measurable outcome: "tests pass", "type errors fixed", "endpoint returns X"
70
+
71
+ **When in doubt, ask.** A 30-second clarification beats a 30-minute wrong decomposition.
72
+
30
73
  ### 1. Initialize Swarm Mail (FIRST)
31
74
 
32
75
  ```bash
@@ -63,6 +63,53 @@ Swarm Mail is embedded (no external server needed) and provides:
63
63
 
64
64
  **Heuristic:** If you can describe the task in one sentence without "and", don't swarm.
65
65
 
66
+ ## Task Clarity Check (BEFORE Decomposing)
67
+
68
+ **Before decomposing, ask: Is this task clear enough to parallelize?**
69
+
70
+ ### Vague Task Signals (ASK QUESTIONS FIRST)
71
+
72
+ | Signal | Example | Problem |
73
+ | ------------------------ | ------------------------------ | -------------------------------- |
74
+ | No files mentioned | "improve performance" | Where? Which files? |
75
+ | Vague verbs | "fix", "update", "make better" | What specifically? |
76
+ | Large undefined scope | "refactor the codebase" | Which parts? What pattern? |
77
+ | Missing success criteria | "add auth" | OAuth? JWT? Session? What flows? |
78
+ | Ambiguous boundaries | "handle errors" | Which errors? Where? How? |
79
+
80
+ ### How to Clarify
81
+
82
+ ```markdown
83
+ The task "<task>" needs clarification before I can decompose it.
84
+
85
+ **Question:** [Specific question about scope/files/approach]
86
+
87
+ Options:
88
+ a) [Option A] - [trade-off]
89
+ b) [Option B] - [trade-off]
90
+ c) [Option C] - [trade-off]
91
+
92
+ I'd recommend (a) because [reason]. Which approach?
93
+ ```
94
+
95
+ **Rules:**
96
+
97
+ - ONE question at a time (don't overwhelm)
98
+ - Offer 2-3 concrete options when possible
99
+ - Lead with your recommendation and why
100
+ - Wait for answer before asking next question
101
+
102
+ ### Clear Task Signals (PROCEED to decompose)
103
+
104
+ | Signal | Example | Why it's clear |
105
+ | ------------------ | ------------------------------ | ---------------- |
106
+ | Specific files | "update src/auth/\*.ts" | Scope defined |
107
+ | Concrete verbs | "migrate from X to Y" | Action defined |
108
+ | Defined scope | "the payment module" | Boundaries clear |
109
+ | Measurable outcome | "tests pass", "no type errors" | Success criteria |
110
+
111
+ **When in doubt, ask.** A 30-second clarification beats a 30-minute wrong decomposition.
112
+
66
113
  ## Coordinator Workflow
67
114
 
68
115
  ### Phase 1: Initialize Swarm Mail (FIRST)
@@ -309,16 +356,17 @@ One blocker affects multiple subtasks.
309
356
 
310
357
  ## Anti-Patterns
311
358
 
312
- | Anti-Pattern | Symptom | Fix |
313
- | ------------------------ | ------------------------------------------ | ------------------------------------ |
314
- | **Mega-Coordinator** | Coordinator editing files | Coordinator only orchestrates |
315
- | **Silent Swarm** | No communication, late conflicts | Require updates, check inbox |
316
- | **Over-Decomposed** | 10 subtasks for 20 lines | 2-5 subtasks max |
317
- | **Under-Specified** | "Implement backend" | Clear goal, files, criteria |
318
- | **Inline Planning** ⚠️ | Context pollution, exhaustion on long runs | Delegate planning to subagent |
319
- | **Heavy File Reading** | Coordinator reading 10+ files | Subagent reads, returns summary only |
320
- | **Deep CASS Drilling** | Multiple cass_search calls inline | Subagent searches, summarizes |
321
- | **Manual Decomposition** | Hand-crafting subtasks without validation | Use swarm_plan_prompt + validation |
359
+ | Anti-Pattern | Symptom | Fix |
360
+ | --------------------------- | ------------------------------------------ | ------------------------------------ |
361
+ | **Decomposing Vague Tasks** | Wrong subtasks, wasted agent cycles | Ask clarifying questions FIRST |
362
+ | **Mega-Coordinator** | Coordinator editing files | Coordinator only orchestrates |
363
+ | **Silent Swarm** | No communication, late conflicts | Require updates, check inbox |
364
+ | **Over-Decomposed** | 10 subtasks for 20 lines | 2-5 subtasks max |
365
+ | **Under-Specified** | "Implement backend" | Clear goal, files, criteria |
366
+ | **Inline Planning** ⚠️ | Context pollution, exhaustion on long runs | Delegate planning to subagent |
367
+ | **Heavy File Reading** | Coordinator reading 10+ files | Subagent reads, returns summary only |
368
+ | **Deep CASS Drilling** | Multiple cass_search calls inline | Subagent searches, summarizes |
369
+ | **Manual Decomposition** | Hand-crafting subtasks without validation | Use swarm_plan_prompt + validation |
322
370
 
323
371
  ## Shared Context Template
324
372
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.15.0",
3
+ "version": "0.17.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",
package/src/beads.ts CHANGED
@@ -178,7 +178,10 @@ function parseBead(output: string): Bead {
178
178
  // CLI commands like `bd close`, `bd update` return arrays even for single items
179
179
  const data = Array.isArray(parsed) ? parsed[0] : parsed;
180
180
  if (!data) {
181
- throw new BeadError("No bead data in response", "parse");
181
+ throw new BeadError(
182
+ "No bead data in response. The bd CLI may not be installed or returned unexpected output. Try: Run 'bd --version' to verify installation, or check if .beads/ directory exists in project.",
183
+ "parse",
184
+ );
182
185
  }
183
186
  return BeadSchema.parse(data);
184
187
  } catch (error) {
@@ -191,7 +194,10 @@ function parseBead(output: string): Bead {
191
194
  if (error instanceof BeadError) {
192
195
  throw error;
193
196
  }
194
- throw new BeadError(`Failed to parse bead JSON: ${output}`, "parse");
197
+ throw new BeadError(
198
+ `Failed to parse bead JSON because output is malformed. Try: Check if bd CLI is up to date with 'bd --version' (need v1.0.0+), or inspect output: ${output.slice(0, 100)}`,
199
+ "parse",
200
+ );
195
201
  }
196
202
  }
197
203
 
@@ -209,7 +215,10 @@ function parseBeads(output: string): Bead[] {
209
215
  error,
210
216
  );
211
217
  }
212
- throw new BeadError(`Failed to parse beads JSON: ${output}`, "parse");
218
+ throw new BeadError(
219
+ `Failed to parse beads JSON because output is malformed. Try: Check if bd CLI is up to date with 'bd --version' (need v1.0.0+), or inspect output: ${output.slice(0, 100)}`,
220
+ "parse",
221
+ );
213
222
  }
214
223
  }
215
224
 
@@ -249,7 +258,7 @@ export const beads_create = tool({
249
258
 
250
259
  if (result.exitCode !== 0) {
251
260
  throw new BeadError(
252
- `Failed to create bead: ${result.stderr}`,
261
+ `Failed to create bead because bd command exited with code ${result.exitCode}. Error: ${result.stderr}. Try: Check if beads initialized with 'bd init' in project root, or verify .beads/ directory exists.`,
253
262
  cmdParts.join(" "),
254
263
  result.exitCode,
255
264
  result.stderr,
@@ -260,7 +269,7 @@ export const beads_create = tool({
260
269
  const stdout = result.stdout.trim();
261
270
  if (!stdout) {
262
271
  throw new BeadError(
263
- "bd create returned empty output",
272
+ "bd create returned empty output because command produced no response. Try: Check if bd is properly installed with 'bd --version', or run 'bd list' to test basic functionality.",
264
273
  cmdParts.join(" "),
265
274
  0,
266
275
  "Empty stdout",
@@ -270,7 +279,7 @@ export const beads_create = tool({
270
279
  // Check for error messages in stdout (bd sometimes outputs errors to stdout)
271
280
  if (stdout.startsWith("error:") || stdout.startsWith("Error:")) {
272
281
  throw new BeadError(
273
- `bd create failed: ${stdout}`,
282
+ `bd create failed because command returned error in stdout: ${stdout}. Try: Check error message above, verify beads initialized with 'bd init', or check .beads/issues.jsonl for corruption.`,
274
283
  cmdParts.join(" "),
275
284
  0,
276
285
  stdout,
@@ -331,7 +340,7 @@ export const beads_create_epic = tool({
331
340
 
332
341
  if (epicResult.exitCode !== 0) {
333
342
  throw new BeadError(
334
- `Failed to create epic: ${epicResult.stderr}`,
343
+ `Failed to create epic because bd command failed: ${epicResult.stderr}. Try: Verify beads initialized with 'bd init', check if .beads/ directory is writable, or run 'bd list' to test basic functionality.`,
335
344
  epicCmd.join(" "),
336
345
  epicResult.exitCode,
337
346
  );
@@ -361,7 +370,7 @@ export const beads_create_epic = tool({
361
370
 
362
371
  if (subtaskResult.exitCode !== 0) {
363
372
  throw new BeadError(
364
- `Failed to create subtask: ${subtaskResult.stderr}`,
373
+ `Failed to create subtask because bd command failed: ${subtaskResult.stderr}. Try: Check if parent epic exists with 'bd show ${epic.id}', verify .beads/issues.jsonl is not corrupted, or check for invalid characters in title.`,
365
374
  subtaskCmd.join(" "),
366
375
  subtaskResult.exitCode,
367
376
  );
@@ -430,7 +439,7 @@ export const beads_create_epic = tool({
430
439
  }
431
440
 
432
441
  throw new BeadError(
433
- `Epic creation failed: ${errorMsg}${rollbackInfo}`,
442
+ `Epic creation failed: ${errorMsg}${rollbackInfo}. Try: If rollback failed, manually close beads with 'bd close <id> --reason "Rollback"', check .beads/issues.jsonl for partial state, or re-run beads_create_epic with corrected parameters.`,
434
443
  "beads_create_epic",
435
444
  1,
436
445
  );
@@ -482,7 +491,7 @@ export const beads_query = tool({
482
491
 
483
492
  if (result.exitCode !== 0) {
484
493
  throw new BeadError(
485
- `Failed to query beads: ${result.stderr}`,
494
+ `Failed to query beads because bd command failed: ${result.stderr}. Try: Check if beads initialized with 'bd init', verify .beads/ directory exists, or run 'bd --version' to check CLI version.`,
486
495
  cmd.join(" "),
487
496
  result.exitCode,
488
497
  );
@@ -534,7 +543,7 @@ export const beads_update = tool({
534
543
 
535
544
  if (result.exitCode !== 0) {
536
545
  throw new BeadError(
537
- `Failed to update bead: ${result.stderr}`,
546
+ `Failed to update bead because bd command failed: ${result.stderr}. Try: Verify bead exists with 'bd show ${validated.id}', check for invalid status values, or inspect .beads/issues.jsonl for corruption.`,
538
547
  cmd.join(" "),
539
548
  result.exitCode,
540
549
  );
@@ -570,7 +579,7 @@ export const beads_close = tool({
570
579
 
571
580
  if (result.exitCode !== 0) {
572
581
  throw new BeadError(
573
- `Failed to close bead: ${result.stderr}`,
582
+ `Failed to close bead because bd command failed: ${result.stderr}. Try: Verify bead exists and is not already closed with 'beads_query(status="closed")' or 'bd show ${validated.id}', check if bead ID is correct.`,
574
583
  cmd.join(" "),
575
584
  result.exitCode,
576
585
  );
@@ -601,7 +610,7 @@ export const beads_start = tool({
601
610
 
602
611
  if (result.exitCode !== 0) {
603
612
  throw new BeadError(
604
- `Failed to start bead: ${result.stderr}`,
613
+ `Failed to start bead because bd update command failed: ${result.stderr}. Try: Verify bead exists with 'bd show ${args.id}', check if already in_progress with 'beads_query(status="in_progress")', or use beads_update directly.`,
605
614
  `bd update ${args.id} --status in_progress --json`,
606
615
  result.exitCode,
607
616
  );
@@ -623,7 +632,7 @@ export const beads_ready = tool({
623
632
 
624
633
  if (result.exitCode !== 0) {
625
634
  throw new BeadError(
626
- `Failed to get ready beads: ${result.stderr}`,
635
+ `Failed to get ready beads because bd ready command failed: ${result.stderr}. Try: Check if beads initialized with 'bd init', verify .beads/ directory is readable, or run 'bd list --json' to test basic query.`,
627
636
  "bd ready --json",
628
637
  result.exitCode,
629
638
  );
@@ -696,7 +705,7 @@ export const beads_sync = tool({
696
705
  );
697
706
  if (flushResult.exitCode !== 0) {
698
707
  throw new BeadError(
699
- `Failed to flush beads: ${flushResult.stderr}`,
708
+ `Failed to flush beads because bd sync failed: ${flushResult.stderr}. Try: Check if .beads/ directory is writable, verify no corrupted JSONL files, or run 'bd list' to test basic beads functionality.`,
700
709
  "bd sync --flush-only",
701
710
  flushResult.exitCode,
702
711
  );
@@ -715,7 +724,7 @@ export const beads_sync = tool({
715
724
  const addResult = await runGitCommand(["add", ".beads/"]);
716
725
  if (addResult.exitCode !== 0) {
717
726
  throw new BeadError(
718
- `Failed to stage beads: ${addResult.stderr}`,
727
+ `Failed to stage beads because git add failed: ${addResult.stderr}. Try: Check if .beads/ directory exists, verify git is initialized with 'git status', or check for .gitignore patterns blocking .beads/.`,
719
728
  "git add .beads/",
720
729
  addResult.exitCode,
721
730
  );
@@ -732,7 +741,7 @@ export const beads_sync = tool({
732
741
  !commitResult.stdout.includes("nothing to commit")
733
742
  ) {
734
743
  throw new BeadError(
735
- `Failed to commit beads: ${commitResult.stderr}`,
744
+ `Failed to commit beads because git commit failed: ${commitResult.stderr}. Try: Check git config (user.name, user.email) with 'git config --list', verify working tree is clean, or check for pre-commit hooks blocking commit.`,
736
745
  "git commit",
737
746
  commitResult.exitCode,
738
747
  );
@@ -798,7 +807,7 @@ export const beads_sync = tool({
798
807
 
799
808
  if (pullResult.exitCode !== 0) {
800
809
  throw new BeadError(
801
- `Failed to pull: ${pullResult.stderr}`,
810
+ `Failed to pull because git pull --rebase failed: ${pullResult.stderr}. Try: Resolve merge conflicts manually with 'git status', check if remote is accessible with 'git remote -v', or use skip_verification to bypass automatic pull.`,
802
811
  "git pull --rebase",
803
812
  pullResult.exitCode,
804
813
  );
@@ -824,7 +833,7 @@ export const beads_sync = tool({
824
833
  );
825
834
  if (pushResult.exitCode !== 0) {
826
835
  throw new BeadError(
827
- `Failed to push: ${pushResult.stderr}`,
836
+ `Failed to push because git push failed: ${pushResult.stderr}. Try: Check if remote branch is up to date with 'git pull --rebase', verify push permissions, check remote URL with 'git remote -v', or force push with 'git push --force-with-lease' if safe.`,
828
837
  "git push",
829
838
  pushResult.exitCode,
830
839
  );
@@ -858,7 +867,7 @@ export const beads_link_thread = tool({
858
867
 
859
868
  if (queryResult.exitCode !== 0) {
860
869
  throw new BeadError(
861
- `Failed to get bead: ${queryResult.stderr}`,
870
+ `Failed to get bead because bd show command failed: ${queryResult.stderr}. Try: Verify bead ID is correct with 'beads_query()', check if bead exists with 'bd list --json', or check .beads/issues.jsonl for valid entries.`,
862
871
  `bd show ${args.bead_id} --json`,
863
872
  queryResult.exitCode,
864
873
  );
@@ -887,7 +896,7 @@ export const beads_link_thread = tool({
887
896
 
888
897
  if (updateResult.exitCode !== 0) {
889
898
  throw new BeadError(
890
- `Failed to update bead: ${updateResult.stderr}`,
899
+ `Failed to update bead because bd update command failed: ${updateResult.stderr}. Try: Verify bead exists with 'bd show ${args.bead_id}', check for invalid characters in description, or inspect .beads/issues.jsonl for corruption.`,
891
900
  `bd update ${args.bead_id} -d ...`,
892
901
  updateResult.exitCode,
893
902
  );
package/src/index.ts CHANGED
@@ -38,6 +38,7 @@ import { structuredTools } from "./structured";
38
38
  import { swarmTools } from "./swarm";
39
39
  import { repoCrawlTools } from "./repo-crawl";
40
40
  import { skillsTools, setSkillsProjectDirectory } from "./skills";
41
+ import { mandateTools } from "./mandates";
41
42
 
42
43
  /**
43
44
  * OpenCode Swarm Plugin
@@ -50,6 +51,7 @@ import { skillsTools, setSkillsProjectDirectory } from "./skills";
50
51
  * - swarm:* - Swarm orchestration and task decomposition
51
52
  * - repo-crawl:* - GitHub API tools for repository research
52
53
  * - skills:* - Agent skills discovery, activation, and execution
54
+ * - mandate:* - Agent voting system for collaborative knowledge curation
53
55
  *
54
56
  * @param input - Plugin context from OpenCode
55
57
  * @returns Plugin hooks including tools, events, and tool execution hooks
@@ -132,6 +134,7 @@ export const SwarmPlugin: Plugin = async (
132
134
  * - agent-mail:init, agent-mail:send, agent-mail:reserve, etc. (legacy MCP)
133
135
  * - swarm-mail:init, swarm-mail:send, swarm-mail:reserve, etc. (embedded)
134
136
  * - repo-crawl:readme, repo-crawl:structure, etc.
137
+ * - mandate:file, mandate:vote, mandate:query, etc.
135
138
  */
136
139
  tool: {
137
140
  ...beadsTools,
@@ -140,6 +143,7 @@ export const SwarmPlugin: Plugin = async (
140
143
  ...swarmTools,
141
144
  ...repoCrawlTools,
142
145
  ...skillsTools,
146
+ ...mandateTools,
143
147
  },
144
148
 
145
149
  /**
@@ -361,6 +365,7 @@ export const allTools = {
361
365
  ...swarmTools,
362
366
  ...repoCrawlTools,
363
367
  ...skillsTools,
368
+ ...mandateTools,
364
369
  } as const;
365
370
 
366
371
  /**
@@ -473,3 +478,76 @@ export {
473
478
  type SkillMetadata,
474
479
  type SkillRef,
475
480
  } from "./skills";
481
+
482
+ /**
483
+ * Re-export mandates module
484
+ *
485
+ * Agent voting system for collaborative knowledge curation.
486
+ *
487
+ * Includes:
488
+ * - mandateTools - All mandate tools (file, vote, query, list, stats)
489
+ * - MandateError - Error class
490
+ *
491
+ * Features:
492
+ * - Submit ideas, tips, lore, snippets, and feature requests
493
+ * - Vote on entries (upvote/downvote) with 90-day decay
494
+ * - Semantic search for relevant mandates
495
+ * - Status transitions based on consensus (candidate → established → mandate)
496
+ * - Persistent storage with semantic-memory
497
+ *
498
+ * Types:
499
+ * - MandateEntry, Vote, MandateScore - Core data types
500
+ * - MandateStatus, MandateContentType - Enum types
501
+ */
502
+ export { mandateTools, MandateError } from "./mandates";
503
+
504
+ /**
505
+ * Re-export mandate-storage module
506
+ *
507
+ * Includes:
508
+ * - createMandateStorage - Factory function
509
+ * - getMandateStorage, setMandateStorage, resetMandateStorage - Global instance management
510
+ * - updateMandateStatus, updateAllMandateStatuses - Status update helpers
511
+ * - InMemoryMandateStorage, SemanticMemoryMandateStorage - Storage implementations
512
+ *
513
+ * Types:
514
+ * - MandateStorage - Unified storage interface
515
+ * - MandateStorageConfig, MandateStorageBackend, MandateStorageCollections - Configuration types
516
+ */
517
+ export {
518
+ createMandateStorage,
519
+ getMandateStorage,
520
+ setMandateStorage,
521
+ resetMandateStorage,
522
+ updateMandateStatus,
523
+ updateAllMandateStatuses,
524
+ InMemoryMandateStorage,
525
+ SemanticMemoryMandateStorage,
526
+ DEFAULT_MANDATE_STORAGE_CONFIG,
527
+ type MandateStorage,
528
+ type MandateStorageConfig,
529
+ type MandateStorageBackend,
530
+ type MandateStorageCollections,
531
+ } from "./mandate-storage";
532
+
533
+ /**
534
+ * Re-export mandate-promotion module
535
+ *
536
+ * Includes:
537
+ * - evaluatePromotion - Evaluate status transitions
538
+ * - shouldPromote - Determine new status based on score
539
+ * - formatPromotionResult - Format promotion result for display
540
+ * - evaluateBatchPromotions, getStatusChanges, groupByTransition - Batch helpers
541
+ *
542
+ * Types:
543
+ * - PromotionResult - Promotion evaluation result
544
+ */
545
+ export {
546
+ evaluatePromotion,
547
+ shouldPromote,
548
+ formatPromotionResult,
549
+ evaluateBatchPromotions,
550
+ getStatusChanges,
551
+ groupByTransition,
552
+ type PromotionResult,
553
+ } from "./mandate-promotion";