opencode-swarm-plugin 0.13.2 → 0.15.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.
package/dist/index.js CHANGED
@@ -28536,6 +28536,7 @@ var require_gray_matter = __commonJS((exports, module) => {
28536
28536
  // src/skills.ts
28537
28537
  var exports_skills = {};
28538
28538
  __export(exports_skills, {
28539
+ validateCSOCompliance: () => validateCSOCompliance,
28539
28540
  skills_use: () => skills_use,
28540
28541
  skills_update: () => skills_update,
28541
28542
  skills_read: () => skills_read,
@@ -28708,6 +28709,90 @@ async function listSkills() {
28708
28709
  function invalidateSkillsCache() {
28709
28710
  skillsCache = null;
28710
28711
  }
28712
+ function validateCSOCompliance(name, description) {
28713
+ const warnings = {
28714
+ critical: [],
28715
+ suggestions: []
28716
+ };
28717
+ const hasUseWhen = /\buse when\b/i.test(description);
28718
+ if (!hasUseWhen) {
28719
+ warnings.critical.push("Description should include 'Use when...' to focus on triggering conditions");
28720
+ }
28721
+ if (description.length > 1024) {
28722
+ warnings.critical.push(`Description is ${description.length} chars (max 1024) - will be rejected`);
28723
+ } else if (description.length > 500) {
28724
+ warnings.suggestions.push(`Description is ${description.length} chars (aim for <500 for optimal discoverability)`);
28725
+ }
28726
+ const firstPersonPattern = /\b(I|I'm|I'll|my|mine|myself)\b/i;
28727
+ const secondPersonPattern = /\b(you|you're|you'll|your|yours|yourself)\b/i;
28728
+ if (firstPersonPattern.test(description)) {
28729
+ warnings.critical.push("Description uses first-person ('I', 'my') - skills are injected into system prompt, use third-person only");
28730
+ }
28731
+ if (secondPersonPattern.test(description)) {
28732
+ warnings.critical.push("Description uses second-person ('you', 'your') - use third-person voice (e.g., 'Handles X' not 'You can handle X')");
28733
+ }
28734
+ const nameWords = name.split("-");
28735
+ const firstWord = nameWords[0];
28736
+ const isGerund = /ing$/.test(firstWord);
28737
+ const isVerbForm = /(ing|ize|ify|ate)$/.test(firstWord);
28738
+ if (!isGerund && !isVerbForm) {
28739
+ const actionVerbs = [
28740
+ "test",
28741
+ "debug",
28742
+ "fix",
28743
+ "scan",
28744
+ "check",
28745
+ "validate",
28746
+ "create",
28747
+ "build",
28748
+ "deploy",
28749
+ "run",
28750
+ "load",
28751
+ "fetch",
28752
+ "parse"
28753
+ ];
28754
+ const startsWithAction = actionVerbs.includes(firstWord);
28755
+ if (!startsWithAction) {
28756
+ warnings.suggestions.push(`Name '${name}' doesn't follow verb-first pattern. Consider gerunds (e.g., 'testing-skills' not 'test-skill') or action verbs for better clarity`);
28757
+ }
28758
+ }
28759
+ if (name.length > 64) {
28760
+ warnings.critical.push(`Name exceeds 64 character limit (${name.length} chars)`);
28761
+ }
28762
+ if (!/^[a-z0-9-]+$/.test(name)) {
28763
+ warnings.critical.push("Name must be lowercase letters, numbers, and hyphens only");
28764
+ }
28765
+ return warnings;
28766
+ }
28767
+ function formatCSOWarnings(warnings) {
28768
+ if (warnings.critical.length === 0 && warnings.suggestions.length === 0) {
28769
+ return null;
28770
+ }
28771
+ const parts = [];
28772
+ if (warnings.critical.length > 0) {
28773
+ parts.push("**CSO Critical Issues:**");
28774
+ for (const warning of warnings.critical) {
28775
+ parts.push(` ⚠️ ${warning}`);
28776
+ }
28777
+ }
28778
+ if (warnings.suggestions.length > 0) {
28779
+ parts.push(`
28780
+ **CSO Suggestions:**`);
28781
+ for (const suggestion of warnings.suggestions) {
28782
+ parts.push(` \uD83D\uDCA1 ${suggestion}`);
28783
+ }
28784
+ }
28785
+ parts.push(`
28786
+ **CSO Guide:**`);
28787
+ parts.push(" • Start description with 'Use when...' (focus on triggering conditions)");
28788
+ parts.push(" • Keep description <500 chars (max 1024)");
28789
+ parts.push(" • Use third-person voice only (injected into system prompt)");
28790
+ parts.push(" • Name: verb-first or gerunds (e.g., 'testing-async' not 'async-test')");
28791
+ parts.push(`
28792
+ Example: 'Use when tests have race conditions - replaces arbitrary timeouts with condition polling'`);
28793
+ return parts.join(`
28794
+ `);
28795
+ }
28711
28796
  function quoteYamlScalar(value) {
28712
28797
  const needsQuoting = /[:\n\r#"'`\[\]{}|>&*!?@]/.test(value) || value.startsWith(" ") || value.endsWith(" ") || value === "" || /^[0-9]/.test(value) || ["true", "false", "null", "yes", "no", "on", "off"].includes(value.toLowerCase());
28713
28798
  if (!needsQuoting) {
@@ -29059,6 +29144,7 @@ Good skills have:
29059
29144
  if (existing) {
29060
29145
  return `Skill '${args.name}' already exists at ${existing.path}. Use skills_update to modify it.`;
29061
29146
  }
29147
+ const csoWarnings = validateCSOCompliance(args.name, args.description);
29062
29148
  let skillDir;
29063
29149
  if (args.directory === "global") {
29064
29150
  skillDir = join5(getGlobalSkillsDir(), args.name);
@@ -29074,7 +29160,7 @@ Good skills have:
29074
29160
  const content = generateSkillContent(args.name, args.description, args.body, { tags: args.tags, tools: args.tools });
29075
29161
  await writeFile(skillPath, content, "utf-8");
29076
29162
  invalidateSkillsCache();
29077
- return JSON.stringify({
29163
+ const response = {
29078
29164
  success: true,
29079
29165
  skill: args.name,
29080
29166
  path: skillPath,
@@ -29084,7 +29170,12 @@ Good skills have:
29084
29170
  "Add examples.md or reference.md for supplementary content",
29085
29171
  "Add scripts/ directory for executable helpers"
29086
29172
  ]
29087
- }, null, 2);
29173
+ };
29174
+ const warningsMessage = formatCSOWarnings(csoWarnings);
29175
+ if (warningsMessage) {
29176
+ response.cso_warnings = warningsMessage;
29177
+ }
29178
+ return JSON.stringify(response, null, 2);
29088
29179
  } catch (error45) {
29089
29180
  return `Failed to create skill: ${error45 instanceof Error ? error45.message : String(error45)}`;
29090
29181
  }
@@ -32378,6 +32469,89 @@ class InMemoryFeedbackStorage {
32378
32469
  return [...this.events];
32379
32470
  }
32380
32471
  }
32472
+ var StrikeRecordSchema = exports_external.object({
32473
+ bead_id: exports_external.string(),
32474
+ strike_count: exports_external.number().int().min(0).max(3),
32475
+ failures: exports_external.array(exports_external.object({
32476
+ attempt: exports_external.string(),
32477
+ reason: exports_external.string(),
32478
+ timestamp: exports_external.string()
32479
+ })),
32480
+ first_strike_at: exports_external.string().optional(),
32481
+ last_strike_at: exports_external.string().optional()
32482
+ });
32483
+
32484
+ class InMemoryStrikeStorage {
32485
+ strikes = new Map;
32486
+ async store(record2) {
32487
+ this.strikes.set(record2.bead_id, record2);
32488
+ }
32489
+ async get(beadId) {
32490
+ return this.strikes.get(beadId) ?? null;
32491
+ }
32492
+ async getAll() {
32493
+ return Array.from(this.strikes.values());
32494
+ }
32495
+ async clear(beadId) {
32496
+ this.strikes.delete(beadId);
32497
+ }
32498
+ }
32499
+ async function addStrike(beadId, attempt, reason, storage = new InMemoryStrikeStorage) {
32500
+ const existing = await storage.get(beadId);
32501
+ const now = new Date().toISOString();
32502
+ const record2 = existing ?? {
32503
+ bead_id: beadId,
32504
+ strike_count: 0,
32505
+ failures: []
32506
+ };
32507
+ record2.strike_count = Math.min(3, record2.strike_count + 1);
32508
+ record2.failures.push({ attempt, reason, timestamp: now });
32509
+ record2.last_strike_at = now;
32510
+ if (!record2.first_strike_at) {
32511
+ record2.first_strike_at = now;
32512
+ }
32513
+ await storage.store(record2);
32514
+ return record2;
32515
+ }
32516
+ async function getStrikes(beadId, storage = new InMemoryStrikeStorage) {
32517
+ const record2 = await storage.get(beadId);
32518
+ return record2?.strike_count ?? 0;
32519
+ }
32520
+ async function isStrikedOut(beadId, storage = new InMemoryStrikeStorage) {
32521
+ const count = await getStrikes(beadId, storage);
32522
+ return count >= 3;
32523
+ }
32524
+ async function getArchitecturePrompt(beadId, storage = new InMemoryStrikeStorage) {
32525
+ const record2 = await storage.get(beadId);
32526
+ if (!record2 || record2.strike_count < 3) {
32527
+ return "";
32528
+ }
32529
+ const failuresList = record2.failures.map((f, i) => `${i + 1}. **${f.attempt}** - Failed: ${f.reason}`).join(`
32530
+ `);
32531
+ return `## Architecture Review Required
32532
+
32533
+ This bead (\`${beadId}\`) has failed 3 consecutive fix attempts:
32534
+
32535
+ ${failuresList}
32536
+
32537
+ This pattern suggests an **architectural problem**, not a bug.
32538
+
32539
+ **Questions to consider:**
32540
+ - Is the current approach fundamentally sound?
32541
+ - Should we refactor the architecture instead?
32542
+ - Are we fixing symptoms instead of root cause?
32543
+
32544
+ **Options:**
32545
+ 1. **Refactor architecture** (describe new approach)
32546
+ 2. **Continue with Fix #4** (explain why this time is different)
32547
+ 3. **Abandon this approach entirely**
32548
+
32549
+ **DO NOT attempt Fix #4 without answering these questions.**
32550
+ `;
32551
+ }
32552
+ async function clearStrikes(beadId, storage = new InMemoryStrikeStorage) {
32553
+ await storage.clear(beadId);
32554
+ }
32381
32555
 
32382
32556
  class InMemoryErrorStorage {
32383
32557
  errors = [];
@@ -32897,7 +33071,7 @@ You MUST keep your bead updated as you work:
32897
33071
 
32898
33072
  **Never work silently.** Your bead status is how the swarm tracks progress.
32899
33073
 
32900
- ## MANDATORY: Agent Mail Communication
33074
+ ## MANDATORY: Swarm Mail Communication
32901
33075
 
32902
33076
  You MUST communicate with other agents:
32903
33077
 
@@ -32906,9 +33080,9 @@ You MUST communicate with other agents:
32906
33080
  3. **Announce blockers** immediately - don't spin trying to fix alone
32907
33081
  4. **Coordinate on shared concerns** - if you see something affecting other agents, say so
32908
33082
 
32909
- Use Agent Mail for all communication:
33083
+ Use Swarm Mail for all communication:
32910
33084
  \`\`\`
32911
- agentmail_send(
33085
+ swarmmail_send(
32912
33086
  to: ["coordinator" or specific agent],
32913
33087
  subject: "Brief subject",
32914
33088
  body: "Message content",
@@ -32920,7 +33094,7 @@ agentmail_send(
32920
33094
 
32921
33095
  1. **Start**: Your bead is already marked in_progress
32922
33096
  2. **Progress**: Use swarm_progress to report status updates
32923
- 3. **Blocked**: Report immediately via Agent Mail - don't spin
33097
+ 3. **Blocked**: Report immediately via Swarm Mail - don't spin
32924
33098
  4. **Complete**: Use swarm_complete when done - it handles:
32925
33099
  - Closing your bead with a summary
32926
33100
  - Releasing file reservations
@@ -32942,7 +33116,7 @@ Before writing code:
32942
33116
  1. **Read the files** you're assigned to understand current state
32943
33117
  2. **Plan your approach** - what changes, in what order?
32944
33118
  3. **Identify risks** - what could go wrong? What dependencies?
32945
- 4. **Communicate your plan** via Agent Mail if non-trivial
33119
+ 4. **Communicate your plan** via Swarm Mail if non-trivial
32946
33120
 
32947
33121
  Begin work on your subtask now.`;
32948
33122
  var SUBTASK_PROMPT_V2 = `You are a swarm agent working on: **{subtask_title}**
@@ -33653,6 +33827,139 @@ var swarm_progress = tool({
33653
33827
  return `Progress reported: ${args.status}${args.progress_percent !== undefined ? ` (${args.progress_percent}%)` : ""}`;
33654
33828
  }
33655
33829
  });
33830
+ async function runTypecheckVerification() {
33831
+ const step = {
33832
+ name: "typecheck",
33833
+ command: "tsc --noEmit",
33834
+ passed: false,
33835
+ exitCode: -1
33836
+ };
33837
+ try {
33838
+ const tsconfigExists = await Bun.file("tsconfig.json").exists();
33839
+ if (!tsconfigExists) {
33840
+ step.skipped = true;
33841
+ step.skipReason = "No tsconfig.json found";
33842
+ step.passed = true;
33843
+ return step;
33844
+ }
33845
+ const result = await Bun.$`tsc --noEmit`.quiet().nothrow();
33846
+ step.exitCode = result.exitCode;
33847
+ step.passed = result.exitCode === 0;
33848
+ if (!step.passed) {
33849
+ step.error = result.stderr.toString().slice(0, 1000);
33850
+ step.output = result.stdout.toString().slice(0, 1000);
33851
+ }
33852
+ } catch (error45) {
33853
+ step.skipped = true;
33854
+ step.skipReason = `tsc not available: ${error45 instanceof Error ? error45.message : String(error45)}`;
33855
+ step.passed = true;
33856
+ }
33857
+ return step;
33858
+ }
33859
+ async function runTestVerification(filesTouched) {
33860
+ const step = {
33861
+ name: "tests",
33862
+ command: "bun test <related-files>",
33863
+ passed: false,
33864
+ exitCode: -1
33865
+ };
33866
+ if (filesTouched.length === 0) {
33867
+ step.skipped = true;
33868
+ step.skipReason = "No files touched";
33869
+ step.passed = true;
33870
+ return step;
33871
+ }
33872
+ const testPatterns = [];
33873
+ for (const file2 of filesTouched) {
33874
+ if (file2.includes(".test.") || file2.includes(".spec.")) {
33875
+ testPatterns.push(file2);
33876
+ continue;
33877
+ }
33878
+ const baseName = file2.replace(/\.(ts|tsx|js|jsx)$/, "");
33879
+ testPatterns.push(`${baseName}.test.ts`);
33880
+ testPatterns.push(`${baseName}.test.tsx`);
33881
+ testPatterns.push(`${baseName}.spec.ts`);
33882
+ }
33883
+ const existingTests = [];
33884
+ for (const pattern of testPatterns) {
33885
+ try {
33886
+ const exists = await Bun.file(pattern).exists();
33887
+ if (exists) {
33888
+ existingTests.push(pattern);
33889
+ }
33890
+ } catch {}
33891
+ }
33892
+ if (existingTests.length === 0) {
33893
+ step.skipped = true;
33894
+ step.skipReason = "No related test files found";
33895
+ step.passed = true;
33896
+ return step;
33897
+ }
33898
+ try {
33899
+ step.command = `bun test ${existingTests.join(" ")}`;
33900
+ const result = await Bun.$`bun test ${existingTests}`.quiet().nothrow();
33901
+ step.exitCode = result.exitCode;
33902
+ step.passed = result.exitCode === 0;
33903
+ if (!step.passed) {
33904
+ step.error = result.stderr.toString().slice(0, 1000);
33905
+ step.output = result.stdout.toString().slice(0, 1000);
33906
+ }
33907
+ } catch (error45) {
33908
+ step.skipped = true;
33909
+ step.skipReason = `Test runner failed: ${error45 instanceof Error ? error45.message : String(error45)}`;
33910
+ step.passed = true;
33911
+ }
33912
+ return step;
33913
+ }
33914
+ async function runVerificationGate(filesTouched, skipUbs = false) {
33915
+ const steps = [];
33916
+ const blockers = [];
33917
+ if (!skipUbs && filesTouched.length > 0) {
33918
+ const ubsResult = await runUbsScan(filesTouched);
33919
+ if (ubsResult) {
33920
+ const ubsStep = {
33921
+ name: "ubs_scan",
33922
+ command: `ubs scan ${filesTouched.join(" ")}`,
33923
+ passed: ubsResult.summary.critical === 0,
33924
+ exitCode: ubsResult.exitCode
33925
+ };
33926
+ if (!ubsStep.passed) {
33927
+ ubsStep.error = `Found ${ubsResult.summary.critical} critical bugs`;
33928
+ blockers.push(`UBS: ${ubsResult.summary.critical} critical bugs found`);
33929
+ }
33930
+ steps.push(ubsStep);
33931
+ } else {
33932
+ steps.push({
33933
+ name: "ubs_scan",
33934
+ command: "ubs scan",
33935
+ passed: true,
33936
+ exitCode: 0,
33937
+ skipped: true,
33938
+ skipReason: "UBS not available"
33939
+ });
33940
+ }
33941
+ }
33942
+ const typecheckStep = await runTypecheckVerification();
33943
+ steps.push(typecheckStep);
33944
+ if (!typecheckStep.passed && !typecheckStep.skipped) {
33945
+ blockers.push(`Typecheck: ${typecheckStep.error?.slice(0, 100) || "failed"}`);
33946
+ }
33947
+ const testStep = await runTestVerification(filesTouched);
33948
+ steps.push(testStep);
33949
+ if (!testStep.passed && !testStep.skipped) {
33950
+ blockers.push(`Tests: ${testStep.error?.slice(0, 100) || "failed"}`);
33951
+ }
33952
+ const passedCount = steps.filter((s) => s.passed).length;
33953
+ const skippedCount = steps.filter((s) => s.skipped).length;
33954
+ const failedCount = steps.filter((s) => !s.passed && !s.skipped).length;
33955
+ const summary = failedCount === 0 ? `Verification passed: ${passedCount} checks passed, ${skippedCount} skipped` : `Verification FAILED: ${failedCount} checks failed, ${passedCount} passed, ${skippedCount} skipped`;
33956
+ return {
33957
+ passed: failedCount === 0,
33958
+ steps,
33959
+ summary,
33960
+ blockers
33961
+ };
33962
+ }
33656
33963
  async function runUbsScan(files) {
33657
33964
  if (files.length === 0) {
33658
33965
  return null;
@@ -33755,19 +34062,44 @@ ${args.files_affected.map((f) => `- \`${f}\``).join(`
33755
34062
  }
33756
34063
  });
33757
34064
  var swarm_complete = tool({
33758
- description: "Mark subtask complete, release reservations, notify coordinator. Runs UBS bug scan if files_touched provided.",
34065
+ description: "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
33759
34066
  args: {
33760
34067
  project_key: tool.schema.string().describe("Project path"),
33761
34068
  agent_name: tool.schema.string().describe("Your Agent Mail name"),
33762
34069
  bead_id: tool.schema.string().describe("Subtask bead ID"),
33763
34070
  summary: tool.schema.string().describe("Brief summary of work done"),
33764
34071
  evaluation: tool.schema.string().optional().describe("Self-evaluation JSON (Evaluation schema)"),
33765
- files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be scanned by UBS for bugs"),
33766
- skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)")
34072
+ files_touched: tool.schema.array(tool.schema.string()).optional().describe("Files modified - will be verified (UBS, typecheck, tests)"),
34073
+ skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS bug scan (default: false)"),
34074
+ skip_verification: tool.schema.boolean().optional().describe("Skip ALL verification (UBS, typecheck, tests). Use sparingly! (default: false)")
33767
34075
  },
33768
34076
  async execute(args) {
34077
+ let verificationResult = null;
34078
+ if (!args.skip_verification && args.files_touched?.length) {
34079
+ verificationResult = await runVerificationGate(args.files_touched, args.skip_ubs_scan ?? false);
34080
+ if (!verificationResult.passed) {
34081
+ return JSON.stringify({
34082
+ success: false,
34083
+ error: "Verification Gate FAILED - fix issues before completing",
34084
+ verification: {
34085
+ passed: false,
34086
+ summary: verificationResult.summary,
34087
+ blockers: verificationResult.blockers,
34088
+ steps: verificationResult.steps.map((s) => ({
34089
+ name: s.name,
34090
+ passed: s.passed,
34091
+ skipped: s.skipped,
34092
+ skipReason: s.skipReason,
34093
+ error: s.error?.slice(0, 200)
34094
+ }))
34095
+ },
34096
+ hint: "Fix the failing checks and try again. Use skip_verification=true only as last resort.",
34097
+ gate_function: "IDENTIFY → RUN → READ → VERIFY → CLAIM (you are at VERIFY, claim blocked)"
34098
+ }, null, 2);
34099
+ }
34100
+ }
33769
34101
  let ubsResult = null;
33770
- if (args.files_touched && args.files_touched.length > 0 && !args.skip_ubs_scan) {
34102
+ if (!args.skip_verification && !verificationResult && args.files_touched?.length && !args.skip_ubs_scan) {
33771
34103
  ubsResult = await runUbsScan(args.files_touched);
33772
34104
  if (ubsResult && ubsResult.summary.critical > 0) {
33773
34105
  return JSON.stringify({
@@ -33838,12 +34170,22 @@ var swarm_complete = tool({
33838
34170
  closed: true,
33839
34171
  reservations_released: true,
33840
34172
  message_sent: true,
34173
+ verification_gate: verificationResult ? {
34174
+ passed: true,
34175
+ summary: verificationResult.summary,
34176
+ steps: verificationResult.steps.map((s) => ({
34177
+ name: s.name,
34178
+ passed: s.passed,
34179
+ skipped: s.skipped,
34180
+ skipReason: s.skipReason
34181
+ }))
34182
+ } : args.skip_verification ? { skipped: true, reason: "skip_verification=true" } : { skipped: true, reason: "no files_touched provided" },
33841
34183
  ubs_scan: ubsResult ? {
33842
34184
  ran: true,
33843
34185
  bugs_found: ubsResult.summary.total,
33844
34186
  summary: ubsResult.summary,
33845
34187
  warnings: ubsResult.bugs.filter((b) => b.severity !== "critical")
33846
- } : {
34188
+ } : verificationResult ? { ran: true, included_in_verification_gate: true } : {
33847
34189
  ran: false,
33848
34190
  reason: args.skip_ubs_scan ? "skipped" : "no files or ubs unavailable"
33849
34191
  },
@@ -34411,6 +34753,77 @@ var swarmTools = {
34411
34753
  swarm_get_error_context,
34412
34754
  swarm_resolve_error
34413
34755
  };
34756
+ var globalStrikeStorage = new InMemoryStrikeStorage;
34757
+ var swarm_check_strikes = tool({
34758
+ description: "Check 3-strike status for a bead. Records failures, detects architectural problems, generates architecture review prompts.",
34759
+ args: {
34760
+ bead_id: tool.schema.string().describe("Bead ID to check"),
34761
+ action: tool.schema.enum(["check", "add_strike", "clear", "get_prompt"]).describe("Action: check count, add strike, clear strikes, or get prompt"),
34762
+ attempt: tool.schema.string().optional().describe("Description of fix attempt (required for add_strike)"),
34763
+ reason: tool.schema.string().optional().describe("Why the fix failed (required for add_strike)")
34764
+ },
34765
+ async execute(args) {
34766
+ switch (args.action) {
34767
+ case "check": {
34768
+ const count = await getStrikes(args.bead_id, globalStrikeStorage);
34769
+ const strikedOut = await isStrikedOut(args.bead_id, globalStrikeStorage);
34770
+ return JSON.stringify({
34771
+ bead_id: args.bead_id,
34772
+ strike_count: count,
34773
+ is_striked_out: strikedOut,
34774
+ message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. Use get_prompt action for architecture review." : count === 0 ? "No strikes. Clear to proceed." : `${count} strike${count > 1 ? "s" : ""}. ${3 - count} remaining before architecture review required.`,
34775
+ next_action: strikedOut ? "Call with action=get_prompt to get architecture review questions" : "Continue with fix attempt"
34776
+ }, null, 2);
34777
+ }
34778
+ case "add_strike": {
34779
+ if (!args.attempt || !args.reason) {
34780
+ return JSON.stringify({
34781
+ error: "add_strike requires 'attempt' and 'reason' parameters"
34782
+ }, null, 2);
34783
+ }
34784
+ const record2 = await addStrike(args.bead_id, args.attempt, args.reason, globalStrikeStorage);
34785
+ const strikedOut = record2.strike_count >= 3;
34786
+ return JSON.stringify({
34787
+ bead_id: args.bead_id,
34788
+ strike_count: record2.strike_count,
34789
+ is_striked_out: strikedOut,
34790
+ failures: record2.failures,
34791
+ message: strikedOut ? "⚠️ STRUCK OUT: 3 strikes reached. STOP and question the architecture." : `Strike ${record2.strike_count} recorded. ${3 - record2.strike_count} remaining.`,
34792
+ warning: strikedOut ? "DO NOT attempt Fix #4. Call with action=get_prompt for architecture review." : undefined
34793
+ }, null, 2);
34794
+ }
34795
+ case "clear": {
34796
+ await clearStrikes(args.bead_id, globalStrikeStorage);
34797
+ return JSON.stringify({
34798
+ bead_id: args.bead_id,
34799
+ strike_count: 0,
34800
+ is_striked_out: false,
34801
+ message: "Strikes cleared. Fresh start."
34802
+ }, null, 2);
34803
+ }
34804
+ case "get_prompt": {
34805
+ const prompt = await getArchitecturePrompt(args.bead_id, globalStrikeStorage);
34806
+ if (!prompt) {
34807
+ return JSON.stringify({
34808
+ bead_id: args.bead_id,
34809
+ has_prompt: false,
34810
+ message: "No architecture prompt (not struck out yet)"
34811
+ }, null, 2);
34812
+ }
34813
+ return JSON.stringify({
34814
+ bead_id: args.bead_id,
34815
+ has_prompt: true,
34816
+ architecture_review_prompt: prompt,
34817
+ message: "Architecture review required. Present this prompt to the human partner."
34818
+ }, null, 2);
34819
+ }
34820
+ default:
34821
+ return JSON.stringify({
34822
+ error: `Unknown action: ${args.action}`
34823
+ }, null, 2);
34824
+ }
34825
+ }
34826
+ });
34414
34827
 
34415
34828
  // src/repo-crawl.ts
34416
34829
  init_dist();
@@ -35192,7 +35605,6 @@ var SwarmPlugin = async (input) => {
35192
35605
  return {
35193
35606
  tool: {
35194
35607
  ...beadsTools,
35195
- ...agentMailTools,
35196
35608
  ...swarmMailTools,
35197
35609
  ...structuredTools,
35198
35610
  ...swarmTools,
@@ -35240,7 +35652,6 @@ var SwarmPlugin = async (input) => {
35240
35652
  var src_default = SwarmPlugin;
35241
35653
  var allTools = {
35242
35654
  ...beadsTools,
35243
- ...agentMailTools,
35244
35655
  ...swarmMailTools,
35245
35656
  ...structuredTools,
35246
35657
  ...swarmTools,