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.
@@ -40378,14 +40378,17 @@ class FlushManager {
40378
40378
  outputPath;
40379
40379
  debounceMs;
40380
40380
  onFlush;
40381
+ onError;
40381
40382
  timer = null;
40382
40383
  flushing = false;
40384
+ lastError = null;
40383
40385
  constructor(options2) {
40384
40386
  this.adapter = options2.adapter;
40385
40387
  this.projectKey = options2.projectKey;
40386
40388
  this.outputPath = options2.outputPath;
40387
40389
  this.debounceMs = options2.debounceMs ?? 30000;
40388
40390
  this.onFlush = options2.onFlush;
40391
+ this.onError = options2.onError;
40389
40392
  }
40390
40393
  scheduleFlush() {
40391
40394
  if (this.timer) {
@@ -40393,7 +40396,11 @@ class FlushManager {
40393
40396
  }
40394
40397
  this.timer = setTimeout(() => {
40395
40398
  this.flush().catch((err) => {
40399
+ this.lastError = err instanceof Error ? err : new Error(String(err));
40396
40400
  console.error("[FlushManager] Flush error:", err);
40401
+ if (this.onError) {
40402
+ this.onError(this.lastError);
40403
+ }
40397
40404
  });
40398
40405
  }, this.debounceMs);
40399
40406
  }
@@ -40455,6 +40462,15 @@ class FlushManager {
40455
40462
  this.flushing = false;
40456
40463
  }
40457
40464
  }
40465
+ hasError() {
40466
+ return this.lastError !== null;
40467
+ }
40468
+ getLastError() {
40469
+ return this.lastError;
40470
+ }
40471
+ clearError() {
40472
+ this.lastError = null;
40473
+ }
40458
40474
  stop() {
40459
40475
  if (this.timer) {
40460
40476
  clearTimeout(this.timer);
@@ -144060,13 +144076,13 @@ Scripts run in the skill's directory with the project directory as an argument.`
144060
144076
  }
144061
144077
  const scriptPath = join23(skill.directory, "scripts", args2.script);
144062
144078
  const scriptArgs = args2.args || [];
144079
+ const TIMEOUT_MS = 60000;
144080
+ const proc = Bun.spawn([scriptPath, skillsProjectDirectory, ...scriptArgs], {
144081
+ cwd: skill.directory,
144082
+ stdout: "pipe",
144083
+ stderr: "pipe"
144084
+ });
144063
144085
  try {
144064
- const TIMEOUT_MS = 60000;
144065
- const proc = Bun.spawn([scriptPath, skillsProjectDirectory, ...scriptArgs], {
144066
- cwd: skill.directory,
144067
- stdout: "pipe",
144068
- stderr: "pipe"
144069
- });
144070
144086
  const timeoutPromise = new Promise((resolve5) => {
144071
144087
  setTimeout(() => resolve5({ timedOut: true }), TIMEOUT_MS);
144072
144088
  });
@@ -144080,7 +144096,6 @@ Scripts run in the skill's directory with the project directory as an argument.`
144080
144096
  })();
144081
144097
  const result = await Promise.race([resultPromise, timeoutPromise]);
144082
144098
  if (result.timedOut) {
144083
- proc.kill();
144084
144099
  return `Script timed out after ${TIMEOUT_MS / 1000} seconds.`;
144085
144100
  }
144086
144101
  const output = result.stdout + result.stderr;
@@ -144092,6 +144107,8 @@ ${output}`;
144092
144107
  }
144093
144108
  } catch (error47) {
144094
144109
  return `Failed to execute script: ${error47 instanceof Error ? error47.message : String(error47)}`;
144110
+ } finally {
144111
+ proc.kill();
144095
144112
  }
144096
144113
  }
144097
144114
  });
@@ -144994,9 +145011,11 @@ async function execSemanticMemory(args2) {
144994
145011
  stderr: "pipe"
144995
145012
  });
144996
145013
  try {
145014
+ const TIMEOUT_MS = 30000;
145015
+ const timeoutPromise = new Promise((_, reject3) => setTimeout(() => reject3(new Error("Process timed out after 30s")), TIMEOUT_MS));
144997
145016
  const stdout = Buffer.from(await new Response(proc.stdout).arrayBuffer());
144998
145017
  const stderr = Buffer.from(await new Response(proc.stderr).arrayBuffer());
144999
- const exitCode = await proc.exited;
145018
+ const exitCode = await Promise.race([proc.exited, timeoutPromise]);
145000
145019
  return { exitCode, stdout, stderr };
145001
145020
  } finally {
145002
145021
  proc.kill();
@@ -175327,12 +175346,16 @@ async function runGitCommand(args2) {
175327
175346
  stdout: "pipe",
175328
175347
  stderr: "pipe"
175329
175348
  });
175330
- const [stdout, stderr] = await Promise.all([
175331
- new Response(proc.stdout).text(),
175332
- new Response(proc.stderr).text()
175333
- ]);
175334
- const exitCode = await proc.exited;
175335
- return { exitCode, stdout, stderr };
175349
+ try {
175350
+ const [stdout, stderr] = await Promise.all([
175351
+ new Response(proc.stdout).text(),
175352
+ new Response(proc.stderr).text()
175353
+ ]);
175354
+ const exitCode = await proc.exited;
175355
+ return { exitCode, stdout, stderr };
175356
+ } finally {
175357
+ proc.kill();
175358
+ }
175336
175359
  }
175337
175360
 
175338
175361
  class HiveError extends Error {
@@ -175638,9 +175661,9 @@ var hive_create = tool({
175638
175661
  }
175639
175662
  });
175640
175663
  var hive_create_epic = tool({
175641
- description: "Create epic with subtasks in one atomic operation",
175664
+ description: "Create epic with subtasks atomically. REQUIRED: epic_title, subtasks (array with {title, files?}). Use after swarm_validate_decomposition confirms your decomposition is valid. Each subtask should list files it will modify to enable parallel work without conflicts.",
175642
175665
  args: {
175643
- epic_title: tool.schema.string().describe("Epic title"),
175666
+ epic_title: tool.schema.string().describe("Epic title (e.g., 'Implement user auth')"),
175644
175667
  epic_description: tool.schema.string().optional().describe("Epic description"),
175645
175668
  epic_id: tool.schema.string().optional().describe("Custom ID for the epic (e.g., 'phase-0')"),
175646
175669
  subtasks: tool.schema.array(tool.schema.object({
@@ -175659,6 +175682,28 @@ var hive_create_epic = tool({
175659
175682
  }).optional().describe("Recovery context from checkpoint compaction")
175660
175683
  },
175661
175684
  async execute(args2, ctx) {
175685
+ const missing = [];
175686
+ if (!args2.epic_title)
175687
+ missing.push("epic_title");
175688
+ if (!args2.subtasks || args2.subtasks.length === 0)
175689
+ missing.push("subtasks (array of subtask objects)");
175690
+ if (missing.length > 0) {
175691
+ return JSON.stringify({
175692
+ success: false,
175693
+ error: `Missing required parameters: ${missing.join(", ")}`,
175694
+ hint: "hive_create_epic creates an epic with subtasks atomically.",
175695
+ example: {
175696
+ epic_title: "Implement user authentication",
175697
+ epic_description: "Add login, logout, and session management",
175698
+ subtasks: [
175699
+ { title: "Create auth service", files: ["src/auth/service.ts"] },
175700
+ { title: "Add login endpoint", files: ["src/api/login.ts"] },
175701
+ { title: "Add session middleware", files: ["src/middleware/session.ts"] }
175702
+ ]
175703
+ },
175704
+ tip: "Each subtask should have a title and optionally files it will modify. This helps with file reservation and parallel execution."
175705
+ }, null, 2);
175706
+ }
175662
175707
  const validated = EpicCreateArgsSchema.parse(args2);
175663
175708
  const projectKey = getHiveWorkingDirectory();
175664
175709
  const adapter5 = await getHiveAdapter(projectKey);
@@ -176497,13 +176542,17 @@ var toolCheckers = {
176497
176542
  stderr: "pipe"
176498
176543
  });
176499
176544
  const timeout6 = setTimeout(() => proc.kill(), BUNX_TIMEOUT_MS);
176500
- const exitCode = await proc.exited;
176501
- clearTimeout(timeout6);
176502
- return {
176503
- available: exitCode === 0,
176504
- checkedAt: new Date().toISOString(),
176505
- version: "bunx"
176506
- };
176545
+ try {
176546
+ const exitCode = await proc.exited;
176547
+ return {
176548
+ available: exitCode === 0,
176549
+ checkedAt: new Date().toISOString(),
176550
+ version: "bunx"
176551
+ };
176552
+ } finally {
176553
+ clearTimeout(timeout6);
176554
+ proc.kill();
176555
+ }
176507
176556
  } catch (e) {
176508
176557
  return {
176509
176558
  available: false,
@@ -176576,13 +176625,17 @@ var toolCheckers = {
176576
176625
  stderr: "pipe"
176577
176626
  });
176578
176627
  const timeout6 = setTimeout(() => proc.kill(), BUNX_TIMEOUT_MS);
176579
- const exitCode = await proc.exited;
176580
- clearTimeout(timeout6);
176581
- return {
176582
- available: exitCode === 0,
176583
- checkedAt: new Date().toISOString(),
176584
- version: "bunx-semantic-memory"
176585
- };
176628
+ try {
176629
+ const exitCode = await proc.exited;
176630
+ return {
176631
+ available: exitCode === 0,
176632
+ checkedAt: new Date().toISOString(),
176633
+ version: "bunx-semantic-memory"
176634
+ };
176635
+ } finally {
176636
+ clearTimeout(timeout6);
176637
+ proc.kill();
176638
+ }
176586
176639
  } catch (e) {
176587
176640
  return {
176588
176641
  available: false,
@@ -176929,17 +176982,20 @@ class SqliteRateLimiter {
176929
176982
  const MAX_BATCHES = 10;
176930
176983
  const cutoff = Date.now() - 7200000;
176931
176984
  let totalDeleted = 0;
176985
+ let batches = 0;
176932
176986
  for (let i = 0;i < MAX_BATCHES; i++) {
176933
- const result = this.db.run(`DELETE FROM rate_limits
176987
+ const result = this.db.run(`DELETE FROM rate_limits
176934
176988
  WHERE rowid IN (
176935
- SELECT rowid FROM rate_limits
176936
- WHERE timestamp < ?
176989
+ SELECT rowid FROM rate_limits
176990
+ WHERE timestamp < ?
176937
176991
  LIMIT ?
176938
176992
  )`, [cutoff, BATCH_SIZE]);
176939
176993
  totalDeleted += result.changes;
176994
+ batches++;
176940
176995
  if (result.changes < BATCH_SIZE)
176941
176996
  break;
176942
176997
  }
176998
+ return { totalDeleted, batches };
176943
176999
  }
176944
177000
  async recordRequest(agentName, endpoint) {
176945
177001
  const now3 = Date.now();
@@ -179413,14 +179469,27 @@ function formatCassHistoryForPrompt(history) {
179413
179469
  `);
179414
179470
  }
179415
179471
  var swarm_decompose = tool({
179416
- description: "Generate decomposition prompt for breaking task into parallelizable subtasks. Optionally queries CASS for similar past tasks.",
179472
+ description: "Generate decomposition prompt for breaking task into parallelizable subtasks. REQUIRED: task (the work to decompose). Returns a prompt - use it to plan subtasks, then validate with swarm_validate_decomposition, then create with hive_create_epic.",
179417
179473
  args: {
179418
- task: tool.schema.string().min(1).describe("Task description to decompose"),
179474
+ task: tool.schema.string().min(1).describe("Task description (e.g., 'Add user authentication with login/logout')"),
179419
179475
  context: tool.schema.string().optional().describe("Additional context (codebase info, constraints, etc.)"),
179420
179476
  query_cass: tool.schema.boolean().optional().describe("Query CASS for similar past tasks (default: true)"),
179421
179477
  cass_limit: tool.schema.number().int().min(1).optional().describe("Max CASS results to include (default: 3)")
179422
179478
  },
179423
179479
  async execute(args2) {
179480
+ if (!args2.task) {
179481
+ return JSON.stringify({
179482
+ success: false,
179483
+ error: "Missing required parameter: task",
179484
+ hint: "swarm_decompose generates a decomposition prompt for breaking a task into parallelizable subtasks.",
179485
+ example: {
179486
+ task: "Implement user authentication with login, logout, and session management",
179487
+ context: "This is a Next.js app using Prisma for database access",
179488
+ query_cass: true
179489
+ },
179490
+ tip: "Provide a clear task description. The tool will generate a prompt for decomposition and optionally query past similar tasks for patterns."
179491
+ }, null, 2);
179492
+ }
179424
179493
  const { formatMemoryQueryForDecomposition: formatMemoryQueryForDecomposition2 } = await Promise.resolve().then(() => (init_learning(), exports_learning));
179425
179494
  let cassContext = "";
179426
179495
  let cassResultInfo;
@@ -179473,9 +179542,9 @@ ${fullContext}` : `## Additional Context
179473
179542
  }
179474
179543
  });
179475
179544
  var swarm_validate_decomposition = tool({
179476
- description: "Validate a decomposition response against CellTreeSchema and capture for eval",
179545
+ description: "Validate decomposition JSON before creating epic. REQUIRED: response (JSON string with {epic: {title, description}, subtasks: [{title, files, dependencies}]}). Checks for file conflicts and valid dependencies. Call after planning, before hive_create_epic.",
179477
179546
  args: {
179478
- response: tool.schema.string().describe("JSON response from agent (CellTree format)"),
179547
+ response: tool.schema.string().describe("JSON string with epic and subtasks (see example in error hint)"),
179479
179548
  project_path: tool.schema.string().optional().describe("Project path for eval capture"),
179480
179549
  task: tool.schema.string().optional().describe("Original task description for eval capture"),
179481
179550
  context: tool.schema.string().optional().describe("Context provided for decomposition"),
@@ -179483,8 +179552,35 @@ var swarm_validate_decomposition = tool({
179483
179552
  epic_id: tool.schema.string().optional().describe("Epic ID for eval capture")
179484
179553
  },
179485
179554
  async execute(args2) {
179555
+ if (!args2.response) {
179556
+ return JSON.stringify({
179557
+ success: false,
179558
+ valid: false,
179559
+ error: "Missing required parameter: response",
179560
+ hint: "swarm_validate_decomposition validates a decomposition JSON. Pass the decomposition output as the 'response' parameter.",
179561
+ example: {
179562
+ response: JSON.stringify({
179563
+ epic: { title: "Epic title", description: "Epic description" },
179564
+ subtasks: [
179565
+ { title: "Subtask 1", description: "...", files: ["src/file.ts"], dependencies: [] }
179566
+ ]
179567
+ }),
179568
+ project_path: "/path/to/project",
179569
+ task: "Original task description"
179570
+ },
179571
+ tip: "This tool validates the JSON output from your decomposition planning. The 'response' should be a JSON string matching CellTreeSchema."
179572
+ }, null, 2);
179573
+ }
179486
179574
  try {
179487
- const parsed = JSON.parse(args2.response);
179575
+ let parsed;
179576
+ if (typeof args2.response === "string") {
179577
+ parsed = JSON.parse(args2.response);
179578
+ if (typeof parsed === "string") {
179579
+ parsed = JSON.parse(parsed);
179580
+ }
179581
+ } else {
179582
+ parsed = args2.response;
179583
+ }
179488
179584
  const validated = CellTreeSchema.parse(parsed);
179489
179585
  const conflicts = detectFileConflicts(validated.subtasks);
179490
179586
  if (conflicts.length > 0) {
@@ -182574,17 +182670,41 @@ var swarm_status = tool({
182574
182670
  }
182575
182671
  });
182576
182672
  var swarm_progress = tool({
182577
- description: "Report progress on a subtask to coordinator",
182673
+ description: "Report progress on a subtask to coordinator. REQUIRED: project_key, agent_name, bead_id, status. Call periodically (every 25% or when blocked) to keep coordinator informed. Use status='blocked' with message explaining the blocker if you're stuck.",
182578
182674
  args: {
182579
- project_key: tool.schema.string().describe("Project path"),
182580
- agent_name: tool.schema.string().describe("Your Agent Mail name"),
182581
- bead_id: tool.schema.string().describe("Subtask bead ID"),
182675
+ project_key: tool.schema.string().describe("Project path (e.g., '/Users/name/project')"),
182676
+ agent_name: tool.schema.string().describe("Your agent name from swarmmail_init"),
182677
+ bead_id: tool.schema.string().describe("Task ID from your spawn prompt or hive cell"),
182582
182678
  status: tool.schema.enum(["in_progress", "blocked", "completed", "failed"]).describe("Current status"),
182583
182679
  message: tool.schema.string().optional().describe("Progress message or blockers"),
182584
182680
  progress_percent: tool.schema.number().min(0).max(100).optional().describe("Completion percentage"),
182585
182681
  files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified so far")
182586
182682
  },
182587
182683
  async execute(args2) {
182684
+ const missing = [];
182685
+ if (!args2.bead_id)
182686
+ missing.push("bead_id");
182687
+ if (!args2.project_key)
182688
+ missing.push("project_key");
182689
+ if (!args2.agent_name)
182690
+ missing.push("agent_name");
182691
+ if (!args2.status)
182692
+ missing.push("status");
182693
+ if (missing.length > 0) {
182694
+ return JSON.stringify({
182695
+ success: false,
182696
+ error: `Missing required parameters: ${missing.join(", ")}`,
182697
+ hint: "swarm_progress reports task progress to the coordinator.",
182698
+ example: {
182699
+ project_key: "/path/to/project",
182700
+ agent_name: "your-agent-name",
182701
+ bead_id: "your-task-id from spawn",
182702
+ status: "in_progress",
182703
+ progress_percent: 50,
182704
+ message: "Completed X, working on Y"
182705
+ }
182706
+ }, null, 2);
182707
+ }
182588
182708
  const progress = {
182589
182709
  bead_id: args2.bead_id,
182590
182710
  agent_name: args2.agent_name,
@@ -182710,12 +182830,12 @@ ${args2.files_affected.map((f) => `- \`${f}\``).join(`
182710
182830
  }
182711
182831
  });
182712
182832
  var swarm_complete = tool({
182713
- description: "Mark subtask complete with Verification Gate. Runs typecheck and tests before allowing completion.",
182833
+ description: "Mark subtask complete with Verification Gate. REQUIRED: project_key, agent_name, bead_id (from your task assignment), summary, start_time (Date.now() from when you started). Before calling: 1) hivemind_store your learnings, 2) list files_touched for verification. Runs typecheck/tests before finalizing.",
182714
182834
  args: {
182715
- project_key: tool.schema.string().describe("Project path"),
182716
- agent_name: tool.schema.string().describe("Your Agent Mail name"),
182717
- bead_id: tool.schema.string().describe("Subtask bead ID"),
182718
- summary: tool.schema.string().describe("Brief summary of work done"),
182835
+ project_key: tool.schema.string().describe("Project path (e.g., '/Users/name/project')"),
182836
+ agent_name: tool.schema.string().describe("Your agent name from swarmmail_init"),
182837
+ bead_id: tool.schema.string().describe("Task ID from your spawn prompt or hive cell"),
182838
+ summary: tool.schema.string().describe("What you accomplished (1-3 sentences)"),
182719
182839
  evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
182720
182840
  files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
182721
182841
  skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
@@ -182726,6 +182846,33 @@ var swarm_complete = tool({
182726
182846
  skip_review: tool.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
182727
182847
  },
182728
182848
  async execute(args2, _ctx) {
182849
+ const missing = [];
182850
+ if (!args2.bead_id)
182851
+ missing.push("bead_id");
182852
+ if (!args2.project_key)
182853
+ missing.push("project_key");
182854
+ if (!args2.agent_name)
182855
+ missing.push("agent_name");
182856
+ if (!args2.summary)
182857
+ missing.push("summary");
182858
+ if (args2.start_time === undefined)
182859
+ missing.push("start_time");
182860
+ if (missing.length > 0) {
182861
+ return JSON.stringify({
182862
+ success: false,
182863
+ error: `Missing required parameters: ${missing.join(", ")}`,
182864
+ hint: "swarm_complete marks a subtask as done. All parameters are required.",
182865
+ example: {
182866
+ project_key: "/path/to/project",
182867
+ agent_name: "your-agent-name",
182868
+ bead_id: "epic-id.subtask-num OR cell-id from hive",
182869
+ summary: "Brief description of what you completed",
182870
+ start_time: Date.now(),
182871
+ files_touched: ["src/file1.ts", "src/file2.ts"]
182872
+ },
182873
+ tip: "The bead_id comes from swarm_spawn_subtask or hive_create. Check your task assignment for the correct ID."
182874
+ }, null, 2);
182875
+ }
182729
182876
  const epicId = args2.bead_id.includes(".") ? args2.bead_id.split(".")[0] : args2.bead_id;
182730
182877
  if (!args2.skip_review) {
182731
182878
  const reviewStatusResult = getReviewStatus(args2.bead_id);
@@ -184062,6 +184209,26 @@ Before writing code:
184062
184209
  Begin work on your subtask now.`;
184063
184210
  var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
184064
184211
 
184212
+ ╔═══════════════════════════════════════════════════════════════════════════════╗
184213
+ ║ ║
184214
+ ║ \uD83D\uDED1 STOP - READ THIS FIRST - BEFORE ANY EDIT OR WRITE \uD83D\uDED1 ║
184215
+ ║ ║
184216
+ ║ You MUST do these 3 things BEFORE your first Edit/Write call: ║
184217
+ ║ ║
184218
+ ║ 1️⃣ hivemind_find(query="<your task keywords>", limit=5, expand=true) ║
184219
+ ║ → Check if past agents already solved this ║
184220
+ ║ → Find gotchas, patterns, warnings ║
184221
+ ║ ║
184222
+ ║ 2️⃣ skills_list() then skills_use(name="<relevant>") ║
184223
+ ║ → testing-patterns, swarm-coordination, system-design ║
184224
+ ║ ║
184225
+ ║ 3️⃣ swarmmail_send(to=["coordinator"], ...) when blocked ║
184226
+ ║ → Don't spin >5min - ASK FOR HELP ║
184227
+ ║ ║
184228
+ ║ SKIPPING THESE = wasted time repeating solved problems ║
184229
+ ║ ║
184230
+ ╚═══════════════════════════════════════════════════════════════════════════════╝
184231
+
184065
184232
  ## [IDENTITY]
184066
184233
  Agent: (assigned at spawn)
184067
184234
  Cell: {bead_id}
@@ -22,6 +22,73 @@
22
22
  ]
23
23
  }
24
24
  ],
25
+ "PreToolUse": [
26
+ {
27
+ "matcher": "Edit|Write",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "swarm claude pre-edit"
32
+ }
33
+ ]
34
+ },
35
+ {
36
+ "matcher": "swarm_complete",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "swarm claude pre-complete"
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "PostToolUse": [
46
+ {
47
+ "matcher": "hivemind_find|mcp__plugin_swarm_swarm-tools__hivemind_find",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "swarm claude track-tool hivemind_find"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "matcher": "skills_use|Skill",
57
+ "hooks": [
58
+ {
59
+ "type": "command",
60
+ "command": "swarm claude track-tool skills_use"
61
+ }
62
+ ]
63
+ },
64
+ {
65
+ "matcher": "swarmmail_init|mcp__plugin_swarm_swarm-tools__swarmmail_init",
66
+ "hooks": [
67
+ {
68
+ "type": "command",
69
+ "command": "swarm claude track-tool swarmmail_init"
70
+ }
71
+ ]
72
+ },
73
+ {
74
+ "matcher": "hivemind_store|mcp__plugin_swarm_swarm-tools__hivemind_store",
75
+ "hooks": [
76
+ {
77
+ "type": "command",
78
+ "command": "swarm claude track-tool hivemind_store"
79
+ }
80
+ ]
81
+ },
82
+ {
83
+ "matcher": "swarm_complete",
84
+ "hooks": [
85
+ {
86
+ "type": "command",
87
+ "command": "swarm claude post-complete"
88
+ }
89
+ ]
90
+ }
91
+ ],
25
92
  "PreCompact": [
26
93
  {
27
94
  "matcher": "",
@@ -0,0 +1,101 @@
1
+ # Release Skill
2
+
3
+ Handles version bumps and releases for the opencode-swarm-plugin monorepo.
4
+
5
+ ## Version Touchpoints
6
+
7
+ When bumping versions, ALL of these files must be updated:
8
+
9
+ ### opencode-swarm-plugin
10
+ - `packages/opencode-swarm-plugin/package.json` - npm package version
11
+ - `packages/opencode-swarm-plugin/claude-plugin/.claude-plugin/plugin.json` - Claude plugin version
12
+
13
+ ### claude-code-swarm-plugin (thin wrapper)
14
+ - `packages/claude-code-swarm-plugin/package.json` - npm package version
15
+ - `packages/claude-code-swarm-plugin/.claude-plugin/plugin.json` - Claude plugin version
16
+
17
+ ### Other packages (if applicable)
18
+ - `packages/swarm-evals/package.json`
19
+ - `packages/swarm-mail/package.json`
20
+ - `packages/swarm-dashboard/package.json`
21
+
22
+ ## Release Process
23
+
24
+ 1. **Create changeset** (for proper changelog generation):
25
+ ```bash
26
+ # Interactive (won't work in CI/agent context)
27
+ bun changeset
28
+
29
+ # Manual - create file in .changeset/
30
+ echo '---
31
+ "opencode-swarm-plugin": minor
32
+ "claude-code-swarm-plugin": minor
33
+ ---
34
+
35
+ feat: description of changes' > .changeset/my-change.md
36
+ ```
37
+
38
+ 2. **Version bump** (applies changesets):
39
+ ```bash
40
+ bun run ci:version
41
+ ```
42
+ This runs `changeset version` and updates package.jsons
43
+
44
+ 3. **CRITICAL: Sync plugin.json versions manually**:
45
+ ```bash
46
+ # Get version from package.json
47
+ VERSION=$(cat packages/opencode-swarm-plugin/package.json | jq -r '.version')
48
+
49
+ # Update both plugin.json files
50
+ jq ".version = \"$VERSION\"" packages/opencode-swarm-plugin/claude-plugin/.claude-plugin/plugin.json > /tmp/p1.json && mv /tmp/p1.json packages/opencode-swarm-plugin/claude-plugin/.claude-plugin/plugin.json
51
+
52
+ VERSION=$(cat packages/claude-code-swarm-plugin/package.json | jq -r '.version')
53
+ jq ".version = \"$VERSION\"" packages/claude-code-swarm-plugin/.claude-plugin/plugin.json > /tmp/p2.json && mv /tmp/p2.json packages/claude-code-swarm-plugin/.claude-plugin/plugin.json
54
+ ```
55
+
56
+ 4. **Build**:
57
+ ```bash
58
+ cd packages/opencode-swarm-plugin && bun run build
59
+ ```
60
+
61
+ 5. **Publish**:
62
+ ```bash
63
+ bun run ci:publish
64
+ ```
65
+
66
+ 6. **Commit and push**:
67
+ ```bash
68
+ git add -A && git commit -m "release: bump versions" && git push
69
+ ```
70
+
71
+ ## Quick Version Bump Script
72
+
73
+ Run this to bump all version touchpoints at once:
74
+
75
+ ```bash
76
+ #!/bin/bash
77
+ NEW_VERSION=$1
78
+
79
+ if [ -z "$NEW_VERSION" ]; then
80
+ echo "Usage: ./bump-version.sh 0.59.0"
81
+ exit 1
82
+ fi
83
+
84
+ # Main package
85
+ jq ".version = \"$NEW_VERSION\"" packages/opencode-swarm-plugin/package.json > /tmp/pkg.json && mv /tmp/pkg.json packages/opencode-swarm-plugin/package.json
86
+ jq ".version = \"$NEW_VERSION\"" packages/opencode-swarm-plugin/claude-plugin/.claude-plugin/plugin.json > /tmp/plug.json && mv /tmp/plug.json packages/opencode-swarm-plugin/claude-plugin/.claude-plugin/plugin.json
87
+
88
+ # Thin wrapper (claude-code-swarm-plugin)
89
+ jq ".version = \"$NEW_VERSION\"" packages/claude-code-swarm-plugin/package.json > /tmp/pkg.json && mv /tmp/pkg.json packages/claude-code-swarm-plugin/package.json
90
+ jq ".version = \"$NEW_VERSION\"" packages/claude-code-swarm-plugin/.claude-plugin/plugin.json > /tmp/plug.json && mv /tmp/plug.json packages/claude-code-swarm-plugin/.claude-plugin/plugin.json
91
+
92
+ echo "Bumped all packages to $NEW_VERSION"
93
+ git diff --stat
94
+ ```
95
+
96
+ ## Gotchas
97
+
98
+ - **plugin.json is NOT auto-bumped by changesets** - must be done manually
99
+ - **Two separate plugins**: `opencode-swarm-plugin` (full) and `claude-code-swarm-plugin` (thin wrapper)
100
+ - **Claude Code marketplace installs from cache** - users may need to uninstall/reinstall for updates
101
+ - **npm publish needs `--access public`** for scoped packages