opencode-swarm-plugin 0.25.0 → 0.25.1

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.
@@ -1,9 +1,9 @@
1
1
  $ bun build ./src/index.ts --outdir ./dist --target node --external @electric-sql/pglite --external swarm-mail && bun build ./src/plugin.ts --outfile ./dist/plugin.js --target node --external @electric-sql/pglite --external swarm-mail && tsc
2
- Bundled 196 modules in 32ms
2
+ Bundled 197 modules in 32ms
3
3
 
4
4
  index.js 1.16 MB (entry point)
5
5
 
6
- Bundled 197 modules in 31ms
6
+ Bundled 198 modules in 31ms
7
7
 
8
- plugin.js 1.13 MB (entry point)
8
+ plugin.js 1.14 MB (entry point)
9
9
 
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # opencode-swarm-plugin
2
2
 
3
+ ## 0.25.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`757f4a6`](https://github.com/joelhooks/swarm-tools/commit/757f4a690721b3f04a414e4c1694660862504e54) Thanks [@joelhooks](https://github.com/joelhooks)! - Fix skills_update tool - add `content` parameter as primary (with `body` as backwards-compat alias)
8
+
9
+ The tool was only accepting `body` but users expected `content`. Now both work:
10
+
11
+ - `skills_update(name="foo", content="new stuff")` - preferred
12
+ - `skills_update(name="foo", body="new stuff")` - still works for backwards compat
13
+
14
+ - [`3d619ff`](https://github.com/joelhooks/swarm-tools/commit/3d619ffda78b2e6066491f053e8fad8dac7b5b71) Thanks [@joelhooks](https://github.com/joelhooks)! - Fix swarm_complete failing when bead project doesn't match CWD
15
+
16
+ - Use `project_key` as working directory for `bd close` command
17
+ - Improved error messages with context-specific recovery steps
18
+ - Added planning guardrails to warn when todowrite is used for parallel work (should use swarm)
19
+
3
20
  ## 0.25.0
4
21
 
5
22
  ### Minor Changes
package/dist/index.d.ts CHANGED
@@ -301,6 +301,7 @@ export declare const allTools: {
301
301
  args: {
302
302
  name: import("zod").ZodString;
303
303
  description: import("zod").ZodOptional<import("zod").ZodString>;
304
+ content: import("zod").ZodOptional<import("zod").ZodString>;
304
305
  body: import("zod").ZodOptional<import("zod").ZodString>;
305
306
  append_body: import("zod").ZodOptional<import("zod").ZodString>;
306
307
  tags: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
@@ -310,6 +311,7 @@ export declare const allTools: {
310
311
  execute(args: {
311
312
  name: string;
312
313
  description?: string | undefined;
314
+ content?: string | undefined;
313
315
  body?: string | undefined;
314
316
  append_body?: string | undefined;
315
317
  tags?: string[] | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -26806,7 +26806,8 @@ Use this to refine skills based on experience:
26806
26806
  args: {
26807
26807
  name: tool.schema.string().describe("Name of the skill to update"),
26808
26808
  description: tool.schema.string().max(1024).optional().describe("New description (replaces existing)"),
26809
- body: tool.schema.string().optional().describe("New body content (replaces existing)"),
26809
+ content: tool.schema.string().optional().describe("New content/body (replaces existing SKILL.md body)"),
26810
+ body: tool.schema.string().optional().describe("Alias for content - new body (replaces existing)"),
26810
26811
  append_body: tool.schema.string().optional().describe("Content to append to existing body"),
26811
26812
  tags: tool.schema.array(tool.schema.string()).optional().describe("New tags (replaces existing)"),
26812
26813
  add_tags: tool.schema.array(tool.schema.string()).optional().describe("Tags to add to existing"),
@@ -26821,8 +26822,9 @@ Use this to refine skills based on experience:
26821
26822
  }
26822
26823
  const newDescription = args.description ?? skill.metadata.description;
26823
26824
  let newBody = skill.body;
26824
- if (args.body) {
26825
- newBody = args.body;
26825
+ const bodyContent = args.content ?? args.body;
26826
+ if (bodyContent) {
26827
+ newBody = bodyContent;
26826
26828
  } else if (args.append_body) {
26827
26829
  newBody = `${skill.body}
26828
26830
 
@@ -26849,7 +26851,7 @@ ${args.append_body}`;
26849
26851
  path: skill.path,
26850
26852
  updated: {
26851
26853
  description: args.description ? true : false,
26852
- body: args.body || args.append_body ? true : false,
26854
+ content: args.content || args.body || args.append_body ? true : false,
26853
26855
  tags: args.tags || args.add_tags ? true : false,
26854
26856
  tools: args.tools ? true : false
26855
26857
  },
@@ -32373,23 +32375,32 @@ Continuing with completion, but this should be fixed for future subtasks.`;
32373
32375
  }, null, 2);
32374
32376
  }
32375
32377
  }
32376
- const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.quiet().nothrow();
32378
+ const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.cwd(args.project_key).quiet().nothrow();
32377
32379
  if (closeResult.exitCode !== 0) {
32378
32380
  const stderrOutput = closeResult.stderr.toString().trim();
32381
+ const stdoutOutput = closeResult.stdout.toString().trim();
32382
+ const isNoDatabaseError = stderrOutput.includes("no beads database found");
32383
+ const isNotFoundError = stderrOutput.includes("not found") || stderrOutput.includes("does not exist");
32379
32384
  return JSON.stringify({
32380
32385
  success: false,
32381
32386
  error: "Failed to close bead",
32382
32387
  failed_step: "bd close",
32383
- details: stderrOutput || "Unknown error from bd close command",
32388
+ details: stderrOutput || stdoutOutput || "Unknown error from bd close command",
32384
32389
  bead_id: args.bead_id,
32390
+ project_key: args.project_key,
32385
32391
  recovery: {
32386
- steps: [
32392
+ steps: isNoDatabaseError ? [
32393
+ `1. Verify project_key is correct: "${args.project_key}"`,
32394
+ `2. Check .beads/ exists in that directory`,
32395
+ `3. Bead ID prefix "${args.bead_id.split("-")[0]}" should match project`,
32396
+ `4. Try: beads_close(id="${args.bead_id}", reason="...")`
32397
+ ] : [
32387
32398
  `1. Check bead exists: bd show ${args.bead_id}`,
32388
32399
  `2. Check bead status (might already be closed): beads_query()`,
32389
32400
  `3. If bead is blocked, unblock first: beads_update(id="${args.bead_id}", status="in_progress")`,
32390
32401
  `4. Try closing directly: beads_close(id="${args.bead_id}", reason="...")`
32391
32402
  ],
32392
- hint: "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing."
32403
+ hint: isNoDatabaseError ? `The project_key "${args.project_key}" doesn't have a .beads/ directory. Make sure you're using the correct project path.` : isNotFoundError ? `Bead "${args.bead_id}" not found. It may have been closed already or the ID is incorrect.` : "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing."
32393
32404
  }
32394
32405
  }, null, 2);
32395
32406
  }
@@ -34362,6 +34373,85 @@ function createMetrics(result, toolName) {
34362
34373
  timestamp: Date.now()
34363
34374
  };
34364
34375
  }
34376
+
34377
+ // src/planning-guardrails.ts
34378
+ var FILE_MODIFICATION_PATTERNS = [
34379
+ /\bimplement\b/i,
34380
+ /\bcreate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34381
+ /\badd\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34382
+ /\bupdate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34383
+ /\bmodify\b/i,
34384
+ /\brefactor\b/i,
34385
+ /\bextract\b/i,
34386
+ /\bmigrate\b/i,
34387
+ /\bconvert\b/i,
34388
+ /\brewrite\b/i,
34389
+ /\bfix\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34390
+ /\bwrite\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34391
+ /src\//i,
34392
+ /lib\//i,
34393
+ /packages?\//i,
34394
+ /components?\//i
34395
+ ];
34396
+ var TRACKING_PATTERNS = [
34397
+ /\breview\b/i,
34398
+ /\bcheck\b/i,
34399
+ /\bverify\b/i,
34400
+ /\btest\b.*pass/i,
34401
+ /\brun\b.*test/i,
34402
+ /\bdeploy\b/i,
34403
+ /\bmerge\b/i,
34404
+ /\bpr\b/i,
34405
+ /\bpush\b/i,
34406
+ /\bcommit\b/i
34407
+ ];
34408
+ function analyzeTodoWrite(args) {
34409
+ const todos = args.todos;
34410
+ if (!todos || !Array.isArray(todos) || todos.length < 6) {
34411
+ return {
34412
+ looksLikeParallelWork: false,
34413
+ fileModificationCount: 0,
34414
+ totalCount: todos?.length ?? 0
34415
+ };
34416
+ }
34417
+ let fileModificationCount = 0;
34418
+ for (const todo of todos) {
34419
+ if (typeof todo !== "object" || todo === null)
34420
+ continue;
34421
+ const content = todo.content ?? "";
34422
+ const isFileModification = FILE_MODIFICATION_PATTERNS.some((pattern) => pattern.test(content));
34423
+ const isTracking = TRACKING_PATTERNS.some((pattern) => pattern.test(content));
34424
+ if (isFileModification && !isTracking) {
34425
+ fileModificationCount++;
34426
+ }
34427
+ }
34428
+ const ratio = fileModificationCount / todos.length;
34429
+ const looksLikeParallelWork = ratio >= 0.5 && fileModificationCount >= 4;
34430
+ if (looksLikeParallelWork) {
34431
+ return {
34432
+ looksLikeParallelWork: true,
34433
+ fileModificationCount,
34434
+ totalCount: todos.length,
34435
+ warning: `⚠️ This looks like a multi-file implementation plan (${fileModificationCount}/${todos.length} items are file modifications).
34436
+
34437
+ Consider using swarm instead:
34438
+ swarm_decompose → beads_create_epic → parallel task spawns
34439
+
34440
+ TodoWrite is for tracking progress, not parallelizable implementation work.
34441
+ Swarm workers can complete these ${fileModificationCount} tasks in parallel.
34442
+
34443
+ (Continuing with todowrite - this is just a suggestion)`
34444
+ };
34445
+ }
34446
+ return {
34447
+ looksLikeParallelWork: false,
34448
+ fileModificationCount,
34449
+ totalCount: todos.length
34450
+ };
34451
+ }
34452
+ function shouldAnalyzeTool(toolName) {
34453
+ return toolName === "todowrite" || toolName === "TodoWrite";
34454
+ }
34365
34455
  // src/storage.ts
34366
34456
  init_learning();
34367
34457
 
@@ -34881,6 +34971,15 @@ var SwarmPlugin = async (input) => {
34881
34971
  await releaseReservations();
34882
34972
  }
34883
34973
  },
34974
+ "tool.execute.before": async (input2, output) => {
34975
+ const toolName = input2.tool;
34976
+ if (shouldAnalyzeTool(toolName)) {
34977
+ const analysis = analyzeTodoWrite(output.args);
34978
+ if (analysis.warning) {
34979
+ console.warn(`[swarm-plugin] ${analysis.warning}`);
34980
+ }
34981
+ }
34982
+ },
34884
34983
  "tool.execute.after": async (input2, output) => {
34885
34984
  const toolName = input2.tool;
34886
34985
  if (output.output && typeof output.output === "string") {
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Planning Guardrails
3
+ *
4
+ * Detects when agents are about to make planning mistakes and warns them.
5
+ * Non-blocking - just emits warnings to help agents self-correct.
6
+ *
7
+ * @module planning-guardrails
8
+ */
9
+ /**
10
+ * Result of analyzing todowrite args
11
+ */
12
+ export interface TodoWriteAnalysis {
13
+ /** Whether this looks like parallel work that should use swarm */
14
+ looksLikeParallelWork: boolean;
15
+ /** Number of todos that look like file modifications */
16
+ fileModificationCount: number;
17
+ /** Total number of todos */
18
+ totalCount: number;
19
+ /** Warning message if applicable */
20
+ warning?: string;
21
+ }
22
+ /**
23
+ * Analyze todowrite args to detect potential planning mistakes
24
+ *
25
+ * Triggers warning when:
26
+ * - 6+ todos created in one call
27
+ * - Most todos match file modification patterns
28
+ * - Few todos match tracking patterns
29
+ *
30
+ * @param args - The todowrite tool arguments
31
+ * @returns Analysis result with optional warning
32
+ */
33
+ export declare function analyzeTodoWrite(args: {
34
+ todos?: unknown[];
35
+ }): TodoWriteAnalysis;
36
+ /**
37
+ * Check if a tool call should trigger planning guardrails
38
+ *
39
+ * @param toolName - Name of the tool being called
40
+ * @returns Whether this tool should be analyzed
41
+ */
42
+ export declare function shouldAnalyzeTool(toolName: string): boolean;
43
+ //# sourceMappingURL=planning-guardrails.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"planning-guardrails.d.ts","sourceRoot":"","sources":["../src/planning-guardrails.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyCH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kEAAkE;IAClE,qBAAqB,EAAE,OAAO,CAAC;IAE/B,wDAAwD;IACxD,qBAAqB,EAAE,MAAM,CAAC;IAE9B,4BAA4B;IAC5B,UAAU,EAAE,MAAM,CAAC;IAEnB,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,EAAE,CAAA;CAAE,GAAG,iBAAiB,CA8D/E;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAE3D"}
package/dist/plugin.js CHANGED
@@ -26806,7 +26806,8 @@ Use this to refine skills based on experience:
26806
26806
  args: {
26807
26807
  name: tool.schema.string().describe("Name of the skill to update"),
26808
26808
  description: tool.schema.string().max(1024).optional().describe("New description (replaces existing)"),
26809
- body: tool.schema.string().optional().describe("New body content (replaces existing)"),
26809
+ content: tool.schema.string().optional().describe("New content/body (replaces existing SKILL.md body)"),
26810
+ body: tool.schema.string().optional().describe("Alias for content - new body (replaces existing)"),
26810
26811
  append_body: tool.schema.string().optional().describe("Content to append to existing body"),
26811
26812
  tags: tool.schema.array(tool.schema.string()).optional().describe("New tags (replaces existing)"),
26812
26813
  add_tags: tool.schema.array(tool.schema.string()).optional().describe("Tags to add to existing"),
@@ -26821,8 +26822,9 @@ Use this to refine skills based on experience:
26821
26822
  }
26822
26823
  const newDescription = args.description ?? skill.metadata.description;
26823
26824
  let newBody = skill.body;
26824
- if (args.body) {
26825
- newBody = args.body;
26825
+ const bodyContent = args.content ?? args.body;
26826
+ if (bodyContent) {
26827
+ newBody = bodyContent;
26826
26828
  } else if (args.append_body) {
26827
26829
  newBody = `${skill.body}
26828
26830
 
@@ -26849,7 +26851,7 @@ ${args.append_body}`;
26849
26851
  path: skill.path,
26850
26852
  updated: {
26851
26853
  description: args.description ? true : false,
26852
- body: args.body || args.append_body ? true : false,
26854
+ content: args.content || args.body || args.append_body ? true : false,
26853
26855
  tags: args.tags || args.add_tags ? true : false,
26854
26856
  tools: args.tools ? true : false
26855
26857
  },
@@ -32164,23 +32166,32 @@ Continuing with completion, but this should be fixed for future subtasks.`;
32164
32166
  }, null, 2);
32165
32167
  }
32166
32168
  }
32167
- const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.quiet().nothrow();
32169
+ const closeResult = await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`.cwd(args.project_key).quiet().nothrow();
32168
32170
  if (closeResult.exitCode !== 0) {
32169
32171
  const stderrOutput = closeResult.stderr.toString().trim();
32172
+ const stdoutOutput = closeResult.stdout.toString().trim();
32173
+ const isNoDatabaseError = stderrOutput.includes("no beads database found");
32174
+ const isNotFoundError = stderrOutput.includes("not found") || stderrOutput.includes("does not exist");
32170
32175
  return JSON.stringify({
32171
32176
  success: false,
32172
32177
  error: "Failed to close bead",
32173
32178
  failed_step: "bd close",
32174
- details: stderrOutput || "Unknown error from bd close command",
32179
+ details: stderrOutput || stdoutOutput || "Unknown error from bd close command",
32175
32180
  bead_id: args.bead_id,
32181
+ project_key: args.project_key,
32176
32182
  recovery: {
32177
- steps: [
32183
+ steps: isNoDatabaseError ? [
32184
+ `1. Verify project_key is correct: "${args.project_key}"`,
32185
+ `2. Check .beads/ exists in that directory`,
32186
+ `3. Bead ID prefix "${args.bead_id.split("-")[0]}" should match project`,
32187
+ `4. Try: beads_close(id="${args.bead_id}", reason="...")`
32188
+ ] : [
32178
32189
  `1. Check bead exists: bd show ${args.bead_id}`,
32179
32190
  `2. Check bead status (might already be closed): beads_query()`,
32180
32191
  `3. If bead is blocked, unblock first: beads_update(id="${args.bead_id}", status="in_progress")`,
32181
32192
  `4. Try closing directly: beads_close(id="${args.bead_id}", reason="...")`
32182
32193
  ],
32183
- hint: "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing."
32194
+ hint: isNoDatabaseError ? `The project_key "${args.project_key}" doesn't have a .beads/ directory. Make sure you're using the correct project path.` : isNotFoundError ? `Bead "${args.bead_id}" not found. It may have been closed already or the ID is incorrect.` : "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing."
32184
32195
  }
32185
32196
  }, null, 2);
32186
32197
  }
@@ -34099,6 +34110,85 @@ function guardrailOutput(toolName, output, config2 = DEFAULT_GUARDRAIL_CONFIG) {
34099
34110
  truncatedLength
34100
34111
  };
34101
34112
  }
34113
+
34114
+ // src/planning-guardrails.ts
34115
+ var FILE_MODIFICATION_PATTERNS = [
34116
+ /\bimplement\b/i,
34117
+ /\bcreate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34118
+ /\badd\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34119
+ /\bupdate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34120
+ /\bmodify\b/i,
34121
+ /\brefactor\b/i,
34122
+ /\bextract\b/i,
34123
+ /\bmigrate\b/i,
34124
+ /\bconvert\b/i,
34125
+ /\brewrite\b/i,
34126
+ /\bfix\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34127
+ /\bwrite\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
34128
+ /src\//i,
34129
+ /lib\//i,
34130
+ /packages?\//i,
34131
+ /components?\//i
34132
+ ];
34133
+ var TRACKING_PATTERNS = [
34134
+ /\breview\b/i,
34135
+ /\bcheck\b/i,
34136
+ /\bverify\b/i,
34137
+ /\btest\b.*pass/i,
34138
+ /\brun\b.*test/i,
34139
+ /\bdeploy\b/i,
34140
+ /\bmerge\b/i,
34141
+ /\bpr\b/i,
34142
+ /\bpush\b/i,
34143
+ /\bcommit\b/i
34144
+ ];
34145
+ function analyzeTodoWrite(args) {
34146
+ const todos = args.todos;
34147
+ if (!todos || !Array.isArray(todos) || todos.length < 6) {
34148
+ return {
34149
+ looksLikeParallelWork: false,
34150
+ fileModificationCount: 0,
34151
+ totalCount: todos?.length ?? 0
34152
+ };
34153
+ }
34154
+ let fileModificationCount = 0;
34155
+ for (const todo of todos) {
34156
+ if (typeof todo !== "object" || todo === null)
34157
+ continue;
34158
+ const content = todo.content ?? "";
34159
+ const isFileModification = FILE_MODIFICATION_PATTERNS.some((pattern) => pattern.test(content));
34160
+ const isTracking = TRACKING_PATTERNS.some((pattern) => pattern.test(content));
34161
+ if (isFileModification && !isTracking) {
34162
+ fileModificationCount++;
34163
+ }
34164
+ }
34165
+ const ratio = fileModificationCount / todos.length;
34166
+ const looksLikeParallelWork = ratio >= 0.5 && fileModificationCount >= 4;
34167
+ if (looksLikeParallelWork) {
34168
+ return {
34169
+ looksLikeParallelWork: true,
34170
+ fileModificationCount,
34171
+ totalCount: todos.length,
34172
+ warning: `⚠️ This looks like a multi-file implementation plan (${fileModificationCount}/${todos.length} items are file modifications).
34173
+
34174
+ Consider using swarm instead:
34175
+ swarm_decompose → beads_create_epic → parallel task spawns
34176
+
34177
+ TodoWrite is for tracking progress, not parallelizable implementation work.
34178
+ Swarm workers can complete these ${fileModificationCount} tasks in parallel.
34179
+
34180
+ (Continuing with todowrite - this is just a suggestion)`
34181
+ };
34182
+ }
34183
+ return {
34184
+ looksLikeParallelWork: false,
34185
+ fileModificationCount,
34186
+ totalCount: todos.length
34187
+ };
34188
+ }
34189
+ function shouldAnalyzeTool(toolName) {
34190
+ return toolName === "todowrite" || toolName === "TodoWrite";
34191
+ }
34102
34192
  // src/storage.ts
34103
34193
  init_learning();
34104
34194
 
@@ -34285,6 +34375,15 @@ var SwarmPlugin = async (input) => {
34285
34375
  await releaseReservations();
34286
34376
  }
34287
34377
  },
34378
+ "tool.execute.before": async (input2, output) => {
34379
+ const toolName = input2.tool;
34380
+ if (shouldAnalyzeTool(toolName)) {
34381
+ const analysis = analyzeTodoWrite(output.args);
34382
+ if (analysis.warning) {
34383
+ console.warn(`[swarm-plugin] ${analysis.warning}`);
34384
+ }
34385
+ }
34386
+ },
34288
34387
  "tool.execute.after": async (input2, output) => {
34289
34388
  const toolName = input2.tool;
34290
34389
  if (output.output && typeof output.output === "string") {
package/dist/skills.d.ts CHANGED
@@ -233,6 +233,7 @@ export declare const skills_update: {
233
233
  args: {
234
234
  name: import("zod").ZodString;
235
235
  description: import("zod").ZodOptional<import("zod").ZodString>;
236
+ content: import("zod").ZodOptional<import("zod").ZodString>;
236
237
  body: import("zod").ZodOptional<import("zod").ZodString>;
237
238
  append_body: import("zod").ZodOptional<import("zod").ZodString>;
238
239
  tags: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
@@ -242,6 +243,7 @@ export declare const skills_update: {
242
243
  execute(args: {
243
244
  name: string;
244
245
  description?: string | undefined;
246
+ content?: string | undefined;
245
247
  body?: string | undefined;
246
248
  append_body?: string | undefined;
247
249
  tags?: string[] | undefined;
@@ -389,6 +391,7 @@ export declare const skillsTools: {
389
391
  args: {
390
392
  name: import("zod").ZodString;
391
393
  description: import("zod").ZodOptional<import("zod").ZodString>;
394
+ content: import("zod").ZodOptional<import("zod").ZodString>;
392
395
  body: import("zod").ZodOptional<import("zod").ZodString>;
393
396
  append_body: import("zod").ZodOptional<import("zod").ZodString>;
394
397
  tags: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
@@ -398,6 +401,7 @@ export declare const skillsTools: {
398
401
  execute(args: {
399
402
  name: string;
400
403
  description?: string | undefined;
404
+ content?: string | undefined;
401
405
  body?: string | undefined;
402
406
  append_body?: string | undefined;
403
407
  tags?: string[] | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"skills.d.ts","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAoBH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IACpB,mDAAmD;IACnD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,KAAK;IACpB,kCAAkC;IAClC,QAAQ,EAAE,aAAa,CAAC;IACxB,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,UAAU,EAAE,OAAO,CAAC;IACpB,kDAAkD;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,OAAO,CAAC;CACrB;AAYD;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAG3D;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG;IACjD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;CACd,CAQA;AAmKD;;;;;;;;;GASG;AACH,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAuD7B;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,GAAG,IAAI,CAAC,CAGlE;AAED;;GAEG;AACH,wBAAsB,UAAU,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC,CAQtD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAMD;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;CAyCtB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;CAoCrB,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;CAwEzB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;CAsDtB,CAAC;AAeH;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,kCAAkC;IAClC,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,GAClB,qBAAqB,CA2FvB;AAwGD;;;;;GAKG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;CA6GxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;CAyGxB,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;CA4CxB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;CAqE5B,CAAC;AAiGH;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;CA6ItB,CAAC;AAMH;;GAEG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAUvB,CAAC;AAMF;;;;;GAKG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,CAAC,CAoBhE;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,MAAM,EAAE,CAAC,CA2BnB"}
@@ -1 +1 @@
1
- {"version":3,"file":"swarm-orchestrate.d.ts","sourceRoot":"","sources":["../src/swarm-orchestrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgiBxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU;;;;;;;;CAuGrB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;CAoFvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;CA8GzB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CA6E1B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiiBzB,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkJ/B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;CA6CjC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;CAmClC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;CAmB9B,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;CAoJ9B,CAAC;AA4BH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqG3B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;CAsGxB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgMtB,CAAC;AAMH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAc5B,CAAC"}
1
+ {"version":3,"file":"swarm-orchestrate.d.ts","sourceRoot":"","sources":["../src/swarm-orchestrate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAgiBxB;;;;;;;;;;GAUG;AACH,eAAO,MAAM,UAAU;;;;;;;;CAuGrB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,YAAY;;;;;;;;;;CAoFvB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;CA8GzB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;CA6E1B,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqjBzB,CAAC;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkJ/B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;CA6CjC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB;;;;;;;;;;CAmClC,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;CAmB9B,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;CAoJ9B,CAAC;AA4BH;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqG3B,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;CAsGxB,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgMtB,CAAC;AAMH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAc5B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-swarm-plugin",
3
- "version": "0.25.0",
3
+ "version": "0.25.1",
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/index.ts CHANGED
@@ -44,6 +44,10 @@ import {
44
44
  DEFAULT_GUARDRAIL_CONFIG,
45
45
  type GuardrailResult,
46
46
  } from "./output-guardrails";
47
+ import {
48
+ analyzeTodoWrite,
49
+ shouldAnalyzeTool,
50
+ } from "./planning-guardrails";
47
51
 
48
52
  /**
49
53
  * OpenCode Swarm Plugin
@@ -164,6 +168,24 @@ export const SwarmPlugin: Plugin = async (
164
168
  }
165
169
  },
166
170
 
171
+ /**
172
+ * Hook before tool execution for planning guardrails
173
+ *
174
+ * Warns when agents are about to make planning mistakes:
175
+ * - Using todowrite for multi-file implementation (should use swarm)
176
+ */
177
+ "tool.execute.before": async (input, output) => {
178
+ const toolName = input.tool;
179
+
180
+ // Check for planning anti-patterns
181
+ if (shouldAnalyzeTool(toolName)) {
182
+ const analysis = analyzeTodoWrite(output.args);
183
+ if (analysis.warning) {
184
+ console.warn(`[swarm-plugin] ${analysis.warning}`);
185
+ }
186
+ }
187
+ },
188
+
167
189
  /**
168
190
  * Hook after tool execution for automatic cleanup and guardrails
169
191
  *
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect } from "bun:test";
2
+ import { analyzeTodoWrite, shouldAnalyzeTool } from "./planning-guardrails";
3
+
4
+ describe("planning-guardrails", () => {
5
+ describe("shouldAnalyzeTool", () => {
6
+ it("returns true for todowrite", () => {
7
+ expect(shouldAnalyzeTool("todowrite")).toBe(true);
8
+ expect(shouldAnalyzeTool("TodoWrite")).toBe(true);
9
+ });
10
+
11
+ it("returns false for other tools", () => {
12
+ expect(shouldAnalyzeTool("beads_create")).toBe(false);
13
+ expect(shouldAnalyzeTool("swarm_decompose")).toBe(false);
14
+ expect(shouldAnalyzeTool("read")).toBe(false);
15
+ });
16
+ });
17
+
18
+ describe("analyzeTodoWrite", () => {
19
+ it("returns no warning for small todo lists", () => {
20
+ const result = analyzeTodoWrite({
21
+ todos: [
22
+ { content: "Implement feature A", status: "pending" },
23
+ { content: "Add tests", status: "pending" },
24
+ ],
25
+ });
26
+
27
+ expect(result.looksLikeParallelWork).toBe(false);
28
+ expect(result.warning).toBeUndefined();
29
+ expect(result.totalCount).toBe(2);
30
+ });
31
+
32
+ it("warns for 6+ file modification todos", () => {
33
+ const result = analyzeTodoWrite({
34
+ todos: [
35
+ { content: "Implement src/auth/login.ts", status: "pending" },
36
+ { content: "Create src/auth/logout.ts", status: "pending" },
37
+ { content: "Add src/auth/types.ts", status: "pending" },
38
+ { content: "Update src/auth/index.ts", status: "pending" },
39
+ { content: "Refactor src/lib/session.ts", status: "pending" },
40
+ { content: "Modify src/middleware/auth.ts", status: "pending" },
41
+ ],
42
+ });
43
+
44
+ expect(result.looksLikeParallelWork).toBe(true);
45
+ expect(result.warning).toBeDefined();
46
+ expect(result.warning).toContain("multi-file implementation plan");
47
+ expect(result.warning).toContain("swarm");
48
+ expect(result.fileModificationCount).toBeGreaterThanOrEqual(4);
49
+ });
50
+
51
+ it("does not warn for tracking/coordination todos", () => {
52
+ const result = analyzeTodoWrite({
53
+ todos: [
54
+ { content: "Review PR #123", status: "pending" },
55
+ { content: "Check tests pass", status: "pending" },
56
+ { content: "Verify deployment", status: "pending" },
57
+ { content: "Run integration tests", status: "pending" },
58
+ { content: "Merge to main", status: "pending" },
59
+ { content: "Push to production", status: "pending" },
60
+ ],
61
+ });
62
+
63
+ expect(result.looksLikeParallelWork).toBe(false);
64
+ expect(result.warning).toBeUndefined();
65
+ });
66
+
67
+ it("does not warn for mixed todos with few file modifications", () => {
68
+ const result = analyzeTodoWrite({
69
+ todos: [
70
+ { content: "Implement src/feature.ts", status: "pending" },
71
+ { content: "Review changes", status: "pending" },
72
+ { content: "Run tests", status: "pending" },
73
+ { content: "Check linting", status: "pending" },
74
+ { content: "Deploy to staging", status: "pending" },
75
+ { content: "Verify in browser", status: "pending" },
76
+ ],
77
+ });
78
+
79
+ // Only 1 file modification out of 6 - should not trigger
80
+ expect(result.looksLikeParallelWork).toBe(false);
81
+ expect(result.warning).toBeUndefined();
82
+ });
83
+
84
+ it("handles empty or missing todos", () => {
85
+ expect(analyzeTodoWrite({}).looksLikeParallelWork).toBe(false);
86
+ expect(analyzeTodoWrite({ todos: [] }).looksLikeParallelWork).toBe(false);
87
+ expect(analyzeTodoWrite({ todos: undefined as any }).looksLikeParallelWork).toBe(false);
88
+ });
89
+
90
+ it("handles malformed todo items", () => {
91
+ const result = analyzeTodoWrite({
92
+ todos: [
93
+ null,
94
+ undefined,
95
+ "string instead of object",
96
+ { noContent: true },
97
+ { content: "Implement src/valid.ts", status: "pending" },
98
+ { content: "Create src/another.ts", status: "pending" },
99
+ ] as any,
100
+ });
101
+
102
+ // Should handle gracefully without crashing
103
+ expect(result.totalCount).toBe(6);
104
+ });
105
+ });
106
+ });
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Planning Guardrails
3
+ *
4
+ * Detects when agents are about to make planning mistakes and warns them.
5
+ * Non-blocking - just emits warnings to help agents self-correct.
6
+ *
7
+ * @module planning-guardrails
8
+ */
9
+
10
+ /**
11
+ * Patterns that suggest file modification work
12
+ * These indicate the todo is about implementation, not tracking
13
+ */
14
+ const FILE_MODIFICATION_PATTERNS = [
15
+ /\bimplement\b/i,
16
+ /\bcreate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
17
+ /\badd\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
18
+ /\bupdate\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
19
+ /\bmodify\b/i,
20
+ /\brefactor\b/i,
21
+ /\bextract\b/i,
22
+ /\bmigrate\b/i,
23
+ /\bconvert\b/i,
24
+ /\brewrite\b/i,
25
+ /\bfix\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
26
+ /\bwrite\b.*\.(ts|js|tsx|jsx|py|rs|go|java|rb|swift|kt)/i,
27
+ /src\//i,
28
+ /lib\//i,
29
+ /packages?\//i,
30
+ /components?\//i,
31
+ ];
32
+
33
+ /**
34
+ * Patterns that suggest this is tracking/coordination work (OK for todowrite)
35
+ */
36
+ const TRACKING_PATTERNS = [
37
+ /\breview\b/i,
38
+ /\bcheck\b/i,
39
+ /\bverify\b/i,
40
+ /\btest\b.*pass/i,
41
+ /\brun\b.*test/i,
42
+ /\bdeploy\b/i,
43
+ /\bmerge\b/i,
44
+ /\bpr\b/i,
45
+ /\bpush\b/i,
46
+ /\bcommit\b/i,
47
+ ];
48
+
49
+ /**
50
+ * Result of analyzing todowrite args
51
+ */
52
+ export interface TodoWriteAnalysis {
53
+ /** Whether this looks like parallel work that should use swarm */
54
+ looksLikeParallelWork: boolean;
55
+
56
+ /** Number of todos that look like file modifications */
57
+ fileModificationCount: number;
58
+
59
+ /** Total number of todos */
60
+ totalCount: number;
61
+
62
+ /** Warning message if applicable */
63
+ warning?: string;
64
+ }
65
+
66
+ /**
67
+ * Analyze todowrite args to detect potential planning mistakes
68
+ *
69
+ * Triggers warning when:
70
+ * - 6+ todos created in one call
71
+ * - Most todos match file modification patterns
72
+ * - Few todos match tracking patterns
73
+ *
74
+ * @param args - The todowrite tool arguments
75
+ * @returns Analysis result with optional warning
76
+ */
77
+ export function analyzeTodoWrite(args: { todos?: unknown[] }): TodoWriteAnalysis {
78
+ const todos = args.todos;
79
+
80
+ // Not enough todos to analyze
81
+ if (!todos || !Array.isArray(todos) || todos.length < 6) {
82
+ return {
83
+ looksLikeParallelWork: false,
84
+ fileModificationCount: 0,
85
+ totalCount: todos?.length ?? 0,
86
+ };
87
+ }
88
+
89
+ // Count todos that look like file modifications
90
+ let fileModificationCount = 0;
91
+
92
+ for (const todo of todos) {
93
+ if (typeof todo !== "object" || todo === null) continue;
94
+
95
+ const content = (todo as { content?: string }).content ?? "";
96
+
97
+ // Check if it matches file modification patterns
98
+ const isFileModification = FILE_MODIFICATION_PATTERNS.some((pattern) =>
99
+ pattern.test(content)
100
+ );
101
+
102
+ // Check if it matches tracking patterns
103
+ const isTracking = TRACKING_PATTERNS.some((pattern) =>
104
+ pattern.test(content)
105
+ );
106
+
107
+ if (isFileModification && !isTracking) {
108
+ fileModificationCount++;
109
+ }
110
+ // trackingCount not currently used but kept for future ratio analysis
111
+ }
112
+
113
+ // Trigger warning if most todos look like file modifications
114
+ const ratio = fileModificationCount / todos.length;
115
+ const looksLikeParallelWork = ratio >= 0.5 && fileModificationCount >= 4;
116
+
117
+ if (looksLikeParallelWork) {
118
+ return {
119
+ looksLikeParallelWork: true,
120
+ fileModificationCount,
121
+ totalCount: todos.length,
122
+ warning: `⚠️ This looks like a multi-file implementation plan (${fileModificationCount}/${todos.length} items are file modifications).
123
+
124
+ Consider using swarm instead:
125
+ swarm_decompose → beads_create_epic → parallel task spawns
126
+
127
+ TodoWrite is for tracking progress, not parallelizable implementation work.
128
+ Swarm workers can complete these ${fileModificationCount} tasks in parallel.
129
+
130
+ (Continuing with todowrite - this is just a suggestion)`,
131
+ };
132
+ }
133
+
134
+ return {
135
+ looksLikeParallelWork: false,
136
+ fileModificationCount,
137
+ totalCount: todos.length,
138
+ };
139
+ }
140
+
141
+ /**
142
+ * Check if a tool call should trigger planning guardrails
143
+ *
144
+ * @param toolName - Name of the tool being called
145
+ * @returns Whether this tool should be analyzed
146
+ */
147
+ export function shouldAnalyzeTool(toolName: string): boolean {
148
+ return toolName === "todowrite" || toolName === "TodoWrite";
149
+ }
package/src/skills.ts CHANGED
@@ -995,10 +995,14 @@ Use this to refine skills based on experience:
995
995
  .max(1024)
996
996
  .optional()
997
997
  .describe("New description (replaces existing)"),
998
+ content: tool.schema
999
+ .string()
1000
+ .optional()
1001
+ .describe("New content/body (replaces existing SKILL.md body)"),
998
1002
  body: tool.schema
999
1003
  .string()
1000
1004
  .optional()
1001
- .describe("New body content (replaces existing)"),
1005
+ .describe("Alias for content - new body (replaces existing)"),
1002
1006
  append_body: tool.schema
1003
1007
  .string()
1004
1008
  .optional()
@@ -1027,10 +1031,11 @@ Use this to refine skills based on experience:
1027
1031
  // Build updated metadata
1028
1032
  const newDescription = args.description ?? skill.metadata.description;
1029
1033
 
1030
- // Handle body updates
1034
+ // Handle body updates (content is preferred, body is alias for backwards compat)
1031
1035
  let newBody = skill.body;
1032
- if (args.body) {
1033
- newBody = args.body;
1036
+ const bodyContent = args.content ?? args.body;
1037
+ if (bodyContent) {
1038
+ newBody = bodyContent;
1034
1039
  } else if (args.append_body) {
1035
1040
  newBody = `${skill.body}\n\n${args.append_body}`;
1036
1041
  }
@@ -1067,7 +1072,7 @@ Use this to refine skills based on experience:
1067
1072
  path: skill.path,
1068
1073
  updated: {
1069
1074
  description: args.description ? true : false,
1070
- body: args.body || args.append_body ? true : false,
1075
+ content: args.content || args.body || args.append_body ? true : false,
1071
1076
  tags: args.tags || args.add_tags ? true : false,
1072
1077
  tools: args.tools ? true : false,
1073
1078
  },
@@ -1189,29 +1189,49 @@ Continuing with completion, but this should be fixed for future subtasks.`;
1189
1189
  }
1190
1190
  }
1191
1191
 
1192
- // Close the bead
1192
+ // Close the bead - use project_key as working directory to find correct .beads/
1193
+ // This fixes the issue where bead ID prefix (e.g., "pdf-library-g84.2") doesn't match CWD
1193
1194
  const closeResult =
1194
1195
  await Bun.$`bd close ${args.bead_id} --reason ${args.summary} --json`
1196
+ .cwd(args.project_key)
1195
1197
  .quiet()
1196
1198
  .nothrow();
1197
1199
 
1198
1200
  if (closeResult.exitCode !== 0) {
1199
1201
  const stderrOutput = closeResult.stderr.toString().trim();
1202
+ const stdoutOutput = closeResult.stdout.toString().trim();
1203
+
1204
+ // Check for common error patterns and provide better guidance
1205
+ const isNoDatabaseError = stderrOutput.includes("no beads database found");
1206
+ const isNotFoundError = stderrOutput.includes("not found") || stderrOutput.includes("does not exist");
1207
+
1200
1208
  return JSON.stringify(
1201
1209
  {
1202
1210
  success: false,
1203
1211
  error: "Failed to close bead",
1204
1212
  failed_step: "bd close",
1205
- details: stderrOutput || "Unknown error from bd close command",
1213
+ details: stderrOutput || stdoutOutput || "Unknown error from bd close command",
1206
1214
  bead_id: args.bead_id,
1215
+ project_key: args.project_key,
1207
1216
  recovery: {
1208
- steps: [
1209
- `1. Check bead exists: bd show ${args.bead_id}`,
1210
- `2. Check bead status (might already be closed): beads_query()`,
1211
- `3. If bead is blocked, unblock first: beads_update(id="${args.bead_id}", status="in_progress")`,
1212
- `4. Try closing directly: beads_close(id="${args.bead_id}", reason="...")`,
1213
- ],
1214
- hint: "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing.",
1217
+ steps: isNoDatabaseError
1218
+ ? [
1219
+ `1. Verify project_key is correct: "${args.project_key}"`,
1220
+ `2. Check .beads/ exists in that directory`,
1221
+ `3. Bead ID prefix "${args.bead_id.split("-")[0]}" should match project`,
1222
+ `4. Try: beads_close(id="${args.bead_id}", reason="...")`,
1223
+ ]
1224
+ : [
1225
+ `1. Check bead exists: bd show ${args.bead_id}`,
1226
+ `2. Check bead status (might already be closed): beads_query()`,
1227
+ `3. If bead is blocked, unblock first: beads_update(id="${args.bead_id}", status="in_progress")`,
1228
+ `4. Try closing directly: beads_close(id="${args.bead_id}", reason="...")`,
1229
+ ],
1230
+ hint: isNoDatabaseError
1231
+ ? `The project_key "${args.project_key}" doesn't have a .beads/ directory. Make sure you're using the correct project path.`
1232
+ : isNotFoundError
1233
+ ? `Bead "${args.bead_id}" not found. It may have been closed already or the ID is incorrect.`
1234
+ : "If bead is in 'blocked' status, you must change it to 'in_progress' or 'open' before closing.",
1215
1235
  },
1216
1236
  },
1217
1237
  null,