opencode-swarm-plugin 0.54.2 → 0.55.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/README.md CHANGED
@@ -218,18 +218,6 @@ pip install -e .
218
218
  cass index # Run periodically to index new sessions
219
219
  ```
220
220
 
221
- ### Bug Scanning (UBS)
222
-
223
- Auto-runs on subtask completion:
224
-
225
- ```bash
226
- git clone https://github.com/Dicklesworthstone/ultimate_bug_scanner
227
- cd ultimate_bug_scanner
228
- pip install -e .
229
- ```
230
-
231
- Check status: `swarm doctor`
232
-
233
221
  ---
234
222
 
235
223
  ## Core Concepts
package/bin/swarm.ts CHANGED
@@ -504,15 +504,6 @@ const DEPENDENCIES: Dependency[] = [
504
504
  installType: "manual",
505
505
  description: "Indexes and searches AI coding agent history for context",
506
506
  },
507
- {
508
- name: "UBS (Ultimate Bug Scanner)",
509
- command: "ubs",
510
- checkArgs: ["--help"],
511
- required: false,
512
- install: "https://github.com/Dicklesworthstone/ultimate_bug_scanner",
513
- installType: "manual",
514
- description: "AI-powered static analysis for pre-completion bug scanning",
515
- },
516
507
  {
517
508
  name: "Ollama",
518
509
  command: "ollama",
@@ -1409,7 +1400,7 @@ skills_list()
1409
1400
  # Check what MCP servers are available (look for context7, pdf-brain, fetch, etc.)
1410
1401
  # Note: No direct MCP listing tool - infer from task context or ask coordinator
1411
1402
 
1412
- # Check for CLI tools if relevant (bd, cass, ubs, ollama)
1403
+ # Check for CLI tools if relevant (bd, cass, ollama)
1413
1404
  # Use Bash tool to check: which <tool-name>
1414
1405
  \`\`\`
1415
1406
 
@@ -1538,7 +1529,6 @@ bash("which <tool>", description="Check if <tool> is available")
1538
1529
 
1539
1530
  # Examples:
1540
1531
  bash("which cass", description="Check CASS availability")
1541
- bash("which ubs", description="Check UBS availability")
1542
1532
  bash("ollama --version", description="Check Ollama availability")
1543
1533
  \`\`\`
1544
1534
 
@@ -1610,8 +1600,6 @@ function getFixCommand(dep: Dependency): string | null {
1610
1600
  return "brew install redis && brew services start redis";
1611
1601
  case "CASS (Coding Agent Session Search)":
1612
1602
  return "See: https://github.com/Dicklesworthstone/coding_agent_session_search";
1613
- case "UBS (Ultimate Bug Scanner)":
1614
- return "See: https://github.com/Dicklesworthstone/ultimate_bug_scanner";
1615
1603
  default:
1616
1604
  // Fallback to generic install command if available
1617
1605
  return dep.installType !== "manual" ? dep.install : null;
@@ -1683,7 +1671,7 @@ async function doctor(debug = false) {
1683
1671
  // Check skills
1684
1672
  p.log.step("Skills:");
1685
1673
  const configDir = join(homedir(), ".config", "opencode");
1686
- const globalSkillsPath = join(configDir, "skills");
1674
+ const globalSkillsPath = join(configDir, "skill");
1687
1675
  const bundledSkillsPath = join(__dirname, "..", "global-skills");
1688
1676
 
1689
1677
  // Global skills directory
@@ -2743,7 +2731,7 @@ function config() {
2743
2731
  const plannerAgentPath = join(agentDir, "swarm-planner.md");
2744
2732
  const workerAgentPath = join(agentDir, "swarm-worker.md");
2745
2733
  const researcherAgentPath = join(agentDir, "swarm-researcher.md");
2746
- const globalSkillsPath = join(configDir, "skills");
2734
+ const globalSkillsPath = join(configDir, "skill");
2747
2735
 
2748
2736
  console.log(yellow(BANNER));
2749
2737
  console.log(dim(" " + TAGLINE + " v" + VERSION));
package/dist/bin/swarm.js CHANGED
@@ -61,7 +61,9 @@ __export(exports_dist, {
61
61
  resolvePartialId: () => resolvePartialId,
62
62
  reserveSwarmFiles: () => reserveSwarmFiles,
63
63
  replayCellEvents: () => replayCellEvents,
64
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent,
64
65
  releaseSwarmFiles: () => releaseSwarmFiles,
66
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles,
65
67
  recoverySuccess: () => recoverySuccess,
66
68
  rebuildBeadBlockedCache: () => rebuildBeadBlockedCache,
67
69
  rebuildAllBlockedCaches: () => rebuildAllBlockedCaches,
@@ -15781,12 +15783,15 @@ async function handleFileReservedDrizzle(db2, event) {
15781
15783
  async function handleFileReleasedDrizzle(db2, event) {
15782
15784
  if (event.type !== "file_released")
15783
15785
  return;
15786
+ const targetAgent = event.target_agent ?? event.agent_name;
15784
15787
  if (event.reservation_ids && event.reservation_ids.length > 0) {
15785
15788
  await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(inArray(reservationsTable.id, event.reservation_ids));
15786
15789
  } else if (event.paths && event.paths.length > 0) {
15787
- await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(and3(eq(reservationsTable.project_key, event.project_key), eq(reservationsTable.agent_name, event.agent_name), inArray(reservationsTable.path_pattern, event.paths), sql`${reservationsTable.released_at} IS NULL`));
15790
+ await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(and3(eq(reservationsTable.project_key, event.project_key), eq(reservationsTable.agent_name, targetAgent), inArray(reservationsTable.path_pattern, event.paths), sql`${reservationsTable.released_at} IS NULL`));
15791
+ } else if (event.release_all) {
15792
+ await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(and3(eq(reservationsTable.project_key, event.project_key), sql`${reservationsTable.released_at} IS NULL`));
15788
15793
  } else {
15789
- await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(and3(eq(reservationsTable.project_key, event.project_key), eq(reservationsTable.agent_name, event.agent_name), sql`${reservationsTable.released_at} IS NULL`));
15794
+ await db2.update(reservationsTable).set({ released_at: event.timestamp }).where(and3(eq(reservationsTable.project_key, event.project_key), eq(reservationsTable.agent_name, targetAgent), sql`${reservationsTable.released_at} IS NULL`));
15790
15795
  }
15791
15796
  }
15792
15797
  async function handleDecompositionGeneratedDrizzle(db2, event) {
@@ -16190,6 +16195,36 @@ async function releaseSwarmFiles(options2) {
16190
16195
  releasedAt: Date.now()
16191
16196
  };
16192
16197
  }
16198
+ async function releaseAllSwarmFiles(options2) {
16199
+ const { projectPath, actorName, dbOverride } = options2;
16200
+ const currentReservations = await getActiveReservations2(projectPath, projectPath, undefined, dbOverride);
16201
+ const releaseCount = currentReservations.length;
16202
+ await appendEvent(createEvent("file_released", {
16203
+ project_key: projectPath,
16204
+ agent_name: actorName,
16205
+ release_all: true,
16206
+ file_count: releaseCount
16207
+ }), projectPath, dbOverride);
16208
+ return {
16209
+ released: releaseCount,
16210
+ releasedAt: Date.now()
16211
+ };
16212
+ }
16213
+ async function releaseSwarmFilesForAgent(options2) {
16214
+ const { projectPath, actorName, targetAgent, dbOverride } = options2;
16215
+ const currentReservations = await getActiveReservations2(projectPath, projectPath, targetAgent, dbOverride);
16216
+ const releaseCount = currentReservations.length;
16217
+ await appendEvent(createEvent("file_released", {
16218
+ project_key: projectPath,
16219
+ agent_name: actorName,
16220
+ target_agent: targetAgent,
16221
+ file_count: releaseCount
16222
+ }), projectPath, dbOverride);
16223
+ return {
16224
+ released: releaseCount,
16225
+ releasedAt: Date.now()
16226
+ };
16227
+ }
16193
16228
  async function acknowledgeSwarmMessage(options2) {
16194
16229
  const { projectPath, messageId, agentName, dbOverride } = options2;
16195
16230
  const timestamp = Date.now();
@@ -17194,14 +17229,18 @@ async function handleFileReserved(db2, event) {
17194
17229
  async function handleFileReleased(db2, event) {
17195
17230
  if (event.type !== "file_released")
17196
17231
  return;
17232
+ const targetAgent = event.target_agent ?? event.agent_name;
17197
17233
  if (event.reservation_ids && event.reservation_ids.length > 0) {
17198
17234
  await db2.query(`UPDATE reservations SET released_at = $1 WHERE id = ANY($2)`, [event.timestamp, event.reservation_ids]);
17199
17235
  } else if (event.paths && event.paths.length > 0) {
17200
17236
  await db2.query(`UPDATE reservations SET released_at = $1
17201
- WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name, event.paths]);
17237
+ WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, targetAgent, event.paths]);
17238
+ } else if (event.release_all) {
17239
+ await db2.query(`UPDATE reservations SET released_at = $1
17240
+ WHERE project_key = $2 AND released_at IS NULL`, [event.timestamp, event.project_key]);
17202
17241
  } else {
17203
17242
  await db2.query(`UPDATE reservations SET released_at = $1
17204
- WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name]);
17243
+ WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, targetAgent]);
17205
17244
  }
17206
17245
  }
17207
17246
  async function handleDecompositionGenerated(db2, event) {
@@ -94024,6 +94063,8 @@ ${stack.split(`
94024
94063
  FileReleasedEventSchema = BaseEventSchema.extend({
94025
94064
  type: exports_external.literal("file_released"),
94026
94065
  agent_name: exports_external.string(),
94066
+ target_agent: exports_external.string().optional(),
94067
+ release_all: exports_external.boolean().optional(),
94027
94068
  paths: exports_external.array(exports_external.string()).optional(),
94028
94069
  reservation_ids: exports_external.array(exports_external.number()).optional(),
94029
94070
  lock_holder_ids: exports_external.array(exports_external.string()).optional(),
@@ -107031,7 +107072,9 @@ ${stack.split(`
107031
107072
  __export2(exports_swarm_mail, {
107032
107073
  sendSwarmMessage: () => sendSwarmMessage,
107033
107074
  reserveSwarmFiles: () => reserveSwarmFiles,
107075
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent,
107034
107076
  releaseSwarmFiles: () => releaseSwarmFiles,
107077
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles,
107035
107078
  readSwarmMessage: () => readSwarmMessage,
107036
107079
  initSwarmAgent: () => initSwarmAgent,
107037
107080
  getSwarmInbox: () => getSwarmInbox,
@@ -108037,7 +108080,9 @@ ${stack.split(`
108037
108080
  rollbackTo: () => rollbackTo,
108038
108081
  reserveSwarmFiles: () => reserveSwarmFiles,
108039
108082
  reserveAgentFiles: () => reserveAgentFiles,
108083
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent,
108040
108084
  releaseSwarmFiles: () => releaseSwarmFiles,
108085
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles,
108041
108086
  releaseAgentFiles: () => releaseAgentFiles,
108042
108087
  readSwarmMessage: () => readSwarmMessage,
108043
108088
  readEvents: () => readEvents,
@@ -116537,7 +116582,9 @@ __export(exports_dist2, {
116537
116582
  resolvePartialId: () => resolvePartialId2,
116538
116583
  reserveSwarmFiles: () => reserveSwarmFiles2,
116539
116584
  replayCellEvents: () => replayCellEvents2,
116585
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent2,
116540
116586
  releaseSwarmFiles: () => releaseSwarmFiles2,
116587
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles2,
116541
116588
  recoverySuccess: () => recoverySuccess2,
116542
116589
  rebuildBeadBlockedCache: () => rebuildBeadBlockedCache2,
116543
116590
  rebuildAllBlockedCaches: () => rebuildAllBlockedCaches2,
@@ -132257,12 +132304,15 @@ async function handleFileReservedDrizzle2(db2, event) {
132257
132304
  async function handleFileReleasedDrizzle2(db2, event) {
132258
132305
  if (event.type !== "file_released")
132259
132306
  return;
132307
+ const targetAgent = event.target_agent ?? event.agent_name;
132260
132308
  if (event.reservation_ids && event.reservation_ids.length > 0) {
132261
132309
  await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(inArray2(reservationsTable2.id, event.reservation_ids));
132262
132310
  } else if (event.paths && event.paths.length > 0) {
132263
- await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(and32(eq2(reservationsTable2.project_key, event.project_key), eq2(reservationsTable2.agent_name, event.agent_name), inArray2(reservationsTable2.path_pattern, event.paths), sql2`${reservationsTable2.released_at} IS NULL`));
132311
+ await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(and32(eq2(reservationsTable2.project_key, event.project_key), eq2(reservationsTable2.agent_name, targetAgent), inArray2(reservationsTable2.path_pattern, event.paths), sql2`${reservationsTable2.released_at} IS NULL`));
132312
+ } else if (event.release_all) {
132313
+ await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(and32(eq2(reservationsTable2.project_key, event.project_key), sql2`${reservationsTable2.released_at} IS NULL`));
132264
132314
  } else {
132265
- await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(and32(eq2(reservationsTable2.project_key, event.project_key), eq2(reservationsTable2.agent_name, event.agent_name), sql2`${reservationsTable2.released_at} IS NULL`));
132315
+ await db2.update(reservationsTable2).set({ released_at: event.timestamp }).where(and32(eq2(reservationsTable2.project_key, event.project_key), eq2(reservationsTable2.agent_name, targetAgent), sql2`${reservationsTable2.released_at} IS NULL`));
132266
132316
  }
132267
132317
  }
132268
132318
  async function handleDecompositionGeneratedDrizzle2(db2, event) {
@@ -132666,6 +132716,36 @@ async function releaseSwarmFiles2(options2) {
132666
132716
  releasedAt: Date.now()
132667
132717
  };
132668
132718
  }
132719
+ async function releaseAllSwarmFiles2(options2) {
132720
+ const { projectPath, actorName, dbOverride } = options2;
132721
+ const currentReservations = await getActiveReservations22(projectPath, projectPath, undefined, dbOverride);
132722
+ const releaseCount = currentReservations.length;
132723
+ await appendEvent3(createEvent2("file_released", {
132724
+ project_key: projectPath,
132725
+ agent_name: actorName,
132726
+ release_all: true,
132727
+ file_count: releaseCount
132728
+ }), projectPath, dbOverride);
132729
+ return {
132730
+ released: releaseCount,
132731
+ releasedAt: Date.now()
132732
+ };
132733
+ }
132734
+ async function releaseSwarmFilesForAgent2(options2) {
132735
+ const { projectPath, actorName, targetAgent, dbOverride } = options2;
132736
+ const currentReservations = await getActiveReservations22(projectPath, projectPath, targetAgent, dbOverride);
132737
+ const releaseCount = currentReservations.length;
132738
+ await appendEvent3(createEvent2("file_released", {
132739
+ project_key: projectPath,
132740
+ agent_name: actorName,
132741
+ target_agent: targetAgent,
132742
+ file_count: releaseCount
132743
+ }), projectPath, dbOverride);
132744
+ return {
132745
+ released: releaseCount,
132746
+ releasedAt: Date.now()
132747
+ };
132748
+ }
132669
132749
  async function acknowledgeSwarmMessage2(options2) {
132670
132750
  const { projectPath, messageId, agentName, dbOverride } = options2;
132671
132751
  const timestamp = Date.now();
@@ -133670,14 +133750,18 @@ async function handleFileReserved2(db2, event) {
133670
133750
  async function handleFileReleased2(db2, event) {
133671
133751
  if (event.type !== "file_released")
133672
133752
  return;
133753
+ const targetAgent = event.target_agent ?? event.agent_name;
133673
133754
  if (event.reservation_ids && event.reservation_ids.length > 0) {
133674
133755
  await db2.query(`UPDATE reservations SET released_at = $1 WHERE id = ANY($2)`, [event.timestamp, event.reservation_ids]);
133675
133756
  } else if (event.paths && event.paths.length > 0) {
133676
133757
  await db2.query(`UPDATE reservations SET released_at = $1
133677
- WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name, event.paths]);
133758
+ WHERE project_key = $2 AND agent_name = $3 AND path_pattern = ANY($4) AND released_at IS NULL`, [event.timestamp, event.project_key, targetAgent, event.paths]);
133759
+ } else if (event.release_all) {
133760
+ await db2.query(`UPDATE reservations SET released_at = $1
133761
+ WHERE project_key = $2 AND released_at IS NULL`, [event.timestamp, event.project_key]);
133678
133762
  } else {
133679
133763
  await db2.query(`UPDATE reservations SET released_at = $1
133680
- WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, event.agent_name]);
133764
+ WHERE project_key = $2 AND agent_name = $3 AND released_at IS NULL`, [event.timestamp, event.project_key, targetAgent]);
133681
133765
  }
133682
133766
  }
133683
133767
  async function handleDecompositionGenerated2(db2, event) {
@@ -210500,6 +210584,8 @@ ${stack.split(`
210500
210584
  FileReleasedEventSchema2 = BaseEventSchema2.extend({
210501
210585
  type: exports_external6.literal("file_released"),
210502
210586
  agent_name: exports_external6.string(),
210587
+ target_agent: exports_external6.string().optional(),
210588
+ release_all: exports_external6.boolean().optional(),
210503
210589
  paths: exports_external6.array(exports_external6.string()).optional(),
210504
210590
  reservation_ids: exports_external6.array(exports_external6.number()).optional(),
210505
210591
  lock_holder_ids: exports_external6.array(exports_external6.string()).optional(),
@@ -223507,7 +223593,9 @@ ${stack.split(`
223507
223593
  __export5(exports_swarm_mail2, {
223508
223594
  sendSwarmMessage: () => sendSwarmMessage2,
223509
223595
  reserveSwarmFiles: () => reserveSwarmFiles2,
223596
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent2,
223510
223597
  releaseSwarmFiles: () => releaseSwarmFiles2,
223598
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles2,
223511
223599
  readSwarmMessage: () => readSwarmMessage2,
223512
223600
  initSwarmAgent: () => initSwarmAgent2,
223513
223601
  getSwarmInbox: () => getSwarmInbox2,
@@ -224513,7 +224601,9 @@ ${stack.split(`
224513
224601
  rollbackTo: () => rollbackTo2,
224514
224602
  reserveSwarmFiles: () => reserveSwarmFiles2,
224515
224603
  reserveAgentFiles: () => reserveAgentFiles2,
224604
+ releaseSwarmFilesForAgent: () => releaseSwarmFilesForAgent2,
224516
224605
  releaseSwarmFiles: () => releaseSwarmFiles2,
224606
+ releaseAllSwarmFiles: () => releaseAllSwarmFiles2,
224517
224607
  releaseAgentFiles: () => releaseAgentFiles2,
224518
224608
  readSwarmMessage: () => readSwarmMessage2,
224519
224609
  readEvents: () => readEvents3,
@@ -347573,29 +347663,6 @@ var toolCheckers = {
347573
347663
  };
347574
347664
  }
347575
347665
  },
347576
- ubs: async () => {
347577
- const exists9 = await commandExists("ubs");
347578
- if (!exists9) {
347579
- return {
347580
- available: false,
347581
- checkedAt: new Date().toISOString(),
347582
- error: "ubs command not found"
347583
- };
347584
- }
347585
- try {
347586
- const result = await Bun.$`ubs doctor`.quiet().nothrow();
347587
- return {
347588
- available: result.exitCode === 0,
347589
- checkedAt: new Date().toISOString()
347590
- };
347591
- } catch (e) {
347592
- return {
347593
- available: false,
347594
- checkedAt: new Date().toISOString(),
347595
- error: String(e)
347596
- };
347597
- }
347598
- },
347599
347666
  hive: async () => {
347600
347667
  const exists9 = await commandExists("hive");
347601
347668
  if (!exists9) {
@@ -347656,7 +347723,6 @@ var fallbackBehaviors = {
347656
347723
  "semantic-memory": "Learning data stored in-memory only (lost on session end)",
347657
347724
  cass: "Decomposition proceeds without historical context from past sessions",
347658
347725
  hivemind: "Unified memory unavailable - learnings stored in-memory only, no session history search",
347659
- ubs: "Subtask completion skips bug scanning - manual review recommended",
347660
347726
  hive: "Swarm cannot track issues - task coordination will be less reliable",
347661
347727
  beads: "DEPRECATED: Use hive instead. Swarm cannot track issues - task coordination will be less reliable",
347662
347728
  "swarm-mail": "Multi-agent coordination disabled - file conflicts possible if multiple agents active",
@@ -347689,7 +347755,6 @@ async function checkAllTools() {
347689
347755
  "semantic-memory",
347690
347756
  "cass",
347691
347757
  "hivemind",
347692
- "ubs",
347693
347758
  "hive",
347694
347759
  "beads",
347695
347760
  "swarm-mail",
@@ -349759,9 +349824,6 @@ var swarm_init = tool2({
349759
349824
  if (!availability.get("cass")?.status.available) {
349760
349825
  degradedFeatures.push("historical context from past sessions");
349761
349826
  }
349762
- if (!availability.get("ubs")?.status.available) {
349763
- degradedFeatures.push("pre-completion bug scanning");
349764
- }
349765
349827
  if (!availability.get("hivemind")?.status.available) {
349766
349828
  degradedFeatures.push("persistent learning (using in-memory fallback)");
349767
349829
  }
@@ -350575,7 +350637,7 @@ ${errorStack.slice(0, 1000)}
350575
350637
  "",
350576
350638
  `### Recovery Actions`,
350577
350639
  "1. Check error message for specific issue",
350578
- "2. Review failed step (UBS scan, typecheck, cell close, etc.)",
350640
+ "2. Review failed step (typecheck, tests, cell close, etc.)",
350579
350641
  "3. Fix underlying issue or use skip flags if appropriate",
350580
350642
  "4. Retry swarm_complete after fixing"
350581
350643
  ].filter(Boolean).join(`
@@ -351635,7 +351697,6 @@ swarm_complete(
351635
351697
  \`\`\`
351636
351698
 
351637
351699
  **This automatically:**
351638
- - Runs UBS bug scan
351639
351700
  - Releases file reservations
351640
351701
  - Records learning signals
351641
351702
  - Notifies coordinator
@@ -357394,29 +357455,6 @@ var toolCheckers2 = {
357394
357455
  };
357395
357456
  }
357396
357457
  },
357397
- ubs: async () => {
357398
- const exists9 = await commandExists2("ubs");
357399
- if (!exists9) {
357400
- return {
357401
- available: false,
357402
- checkedAt: new Date().toISOString(),
357403
- error: "ubs command not found"
357404
- };
357405
- }
357406
- try {
357407
- const result = await Bun.$`ubs doctor`.quiet().nothrow();
357408
- return {
357409
- available: result.exitCode === 0,
357410
- checkedAt: new Date().toISOString()
357411
- };
357412
- } catch (e) {
357413
- return {
357414
- available: false,
357415
- checkedAt: new Date().toISOString(),
357416
- error: String(e)
357417
- };
357418
- }
357419
- },
357420
357458
  hive: async () => {
357421
357459
  const exists9 = await commandExists2("hive");
357422
357460
  if (!exists9) {
@@ -357477,7 +357515,6 @@ var fallbackBehaviors2 = {
357477
357515
  "semantic-memory": "Learning data stored in-memory only (lost on session end)",
357478
357516
  cass: "Decomposition proceeds without historical context from past sessions",
357479
357517
  hivemind: "Unified memory unavailable - learnings stored in-memory only, no session history search",
357480
- ubs: "Subtask completion skips bug scanning - manual review recommended",
357481
357518
  hive: "Swarm cannot track issues - task coordination will be less reliable",
357482
357519
  beads: "DEPRECATED: Use hive instead. Swarm cannot track issues - task coordination will be less reliable",
357483
357520
  "swarm-mail": "Multi-agent coordination disabled - file conflicts possible if multiple agents active",
@@ -357510,7 +357547,6 @@ async function checkAllTools2() {
357510
357547
  "semantic-memory",
357511
357548
  "cass",
357512
357549
  "hivemind",
357513
- "ubs",
357514
357550
  "hive",
357515
357551
  "beads",
357516
357552
  "swarm-mail",
@@ -358882,6 +358918,67 @@ var swarmmail_release = tool3({
358882
358918
  }
358883
358919
  }
358884
358920
  });
358921
+ var swarmmail_release_all = tool3({
358922
+ description: "Release all file reservations in the project (coordinator override)",
358923
+ args: {},
358924
+ async execute(_args, ctx) {
358925
+ const sessionID = ctx.sessionID || "default";
358926
+ const state = loadSessionState2(sessionID);
358927
+ if (!state) {
358928
+ return JSON.stringify({ error: "Session not initialized. Call swarmmail_init first." }, null, 2);
358929
+ }
358930
+ try {
358931
+ const result = await releaseAllSwarmFiles({
358932
+ projectPath: state.projectKey,
358933
+ actorName: state.agentName
358934
+ });
358935
+ state.reservations = [];
358936
+ saveSessionState2(sessionID, state);
358937
+ return JSON.stringify({
358938
+ released: result.released,
358939
+ released_at: result.releasedAt,
358940
+ release_all: true
358941
+ }, null, 2);
358942
+ } catch (error56) {
358943
+ return JSON.stringify({
358944
+ error: `Failed to release all files: ${error56 instanceof Error ? error56.message : String(error56)}`
358945
+ }, null, 2);
358946
+ }
358947
+ }
358948
+ });
358949
+ var swarmmail_release_agent = tool3({
358950
+ description: "Release all file reservations for a specific agent (coordinator override)",
358951
+ args: {
358952
+ agent_name: tool3.schema.string().describe("Target agent name")
358953
+ },
358954
+ async execute(args5, ctx) {
358955
+ const sessionID = ctx.sessionID || "default";
358956
+ const state = loadSessionState2(sessionID);
358957
+ if (!state) {
358958
+ return JSON.stringify({ error: "Session not initialized. Call swarmmail_init first." }, null, 2);
358959
+ }
358960
+ try {
358961
+ const result = await releaseSwarmFilesForAgent({
358962
+ projectPath: state.projectKey,
358963
+ actorName: state.agentName,
358964
+ targetAgent: args5.agent_name
358965
+ });
358966
+ if (args5.agent_name === state.agentName) {
358967
+ state.reservations = [];
358968
+ saveSessionState2(sessionID, state);
358969
+ }
358970
+ return JSON.stringify({
358971
+ released: result.released,
358972
+ released_at: result.releasedAt,
358973
+ target_agent: args5.agent_name
358974
+ }, null, 2);
358975
+ } catch (error56) {
358976
+ return JSON.stringify({
358977
+ error: `Failed to release files for agent: ${error56 instanceof Error ? error56.message : String(error56)}`
358978
+ }, null, 2);
358979
+ }
358980
+ }
358981
+ });
358885
358982
  var swarmmail_ack = tool3({
358886
358983
  description: "Acknowledge a message (for messages that require acknowledgement)",
358887
358984
  args: {
@@ -358944,6 +359041,8 @@ var swarmMailTools = {
358944
359041
  swarmmail_read_message,
358945
359042
  swarmmail_reserve,
358946
359043
  swarmmail_release,
359044
+ swarmmail_release_all,
359045
+ swarmmail_release_agent,
358947
359046
  swarmmail_ack,
358948
359047
  swarmmail_health
358949
359048
  };
@@ -362767,9 +362866,6 @@ var swarm_init2 = tool3({
362767
362866
  if (!availability.get("cass")?.status.available) {
362768
362867
  degradedFeatures.push("historical context from past sessions");
362769
362868
  }
362770
- if (!availability.get("ubs")?.status.available) {
362771
- degradedFeatures.push("pre-completion bug scanning");
362772
- }
362773
362869
  if (!availability.get("hivemind")?.status.available) {
362774
362870
  degradedFeatures.push("persistent learning (using in-memory fallback)");
362775
362871
  }
@@ -363583,7 +363679,7 @@ ${errorStack.slice(0, 1000)}
363583
363679
  "",
363584
363680
  `### Recovery Actions`,
363585
363681
  "1. Check error message for specific issue",
363586
- "2. Review failed step (UBS scan, typecheck, cell close, etc.)",
363682
+ "2. Review failed step (typecheck, tests, cell close, etc.)",
363587
363683
  "3. Fix underlying issue or use skip flags if appropriate",
363588
363684
  "4. Retry swarm_complete after fixing"
363589
363685
  ].filter(Boolean).join(`
@@ -364594,7 +364690,6 @@ swarm_complete(
364594
364690
  \`\`\`
364595
364691
 
364596
364692
  **This automatically:**
364597
- - Runs UBS bug scan
364598
364693
  - Releases file reservations
364599
364694
  - Records learning signals
364600
364695
  - Notifies coordinator
@@ -368266,15 +368361,6 @@ var DEPENDENCIES = [
368266
368361
  installType: "manual",
368267
368362
  description: "Indexes and searches AI coding agent history for context"
368268
368363
  },
368269
- {
368270
- name: "UBS (Ultimate Bug Scanner)",
368271
- command: "ubs",
368272
- checkArgs: ["--help"],
368273
- required: false,
368274
- install: "https://github.com/Dicklesworthstone/ultimate_bug_scanner",
368275
- installType: "manual",
368276
- description: "AI-powered static analysis for pre-completion bug scanning"
368277
- },
368278
368364
  {
368279
368365
  name: "Ollama",
368280
368366
  command: "ollama",
@@ -368986,7 +369072,7 @@ skills_list()
368986
369072
  # Check what MCP servers are available (look for context7, pdf-brain, fetch, etc.)
368987
369073
  # Note: No direct MCP listing tool - infer from task context or ask coordinator
368988
369074
 
368989
- # Check for CLI tools if relevant (bd, cass, ubs, ollama)
369075
+ # Check for CLI tools if relevant (bd, cass, ollama)
368990
369076
  # Use Bash tool to check: which <tool-name>
368991
369077
  \`\`\`
368992
369078
 
@@ -369115,7 +369201,6 @@ bash("which <tool>", description="Check if <tool> is available")
369115
369201
 
369116
369202
  # Examples:
369117
369203
  bash("which cass", description="Check CASS availability")
369118
- bash("which ubs", description="Check UBS availability")
369119
369204
  bash("ollama --version", description="Check Ollama availability")
369120
369205
  \`\`\`
369121
369206
 
@@ -369178,8 +369263,6 @@ function getFixCommand(dep) {
369178
369263
  return "brew install redis && brew services start redis";
369179
369264
  case "CASS (Coding Agent Session Search)":
369180
369265
  return "See: https://github.com/Dicklesworthstone/coding_agent_session_search";
369181
- case "UBS (Ultimate Bug Scanner)":
369182
- return "See: https://github.com/Dicklesworthstone/ultimate_bug_scanner";
369183
369266
  default:
369184
369267
  return dep.installType !== "manual" ? dep.install : null;
369185
369268
  }
@@ -369236,7 +369319,7 @@ async function doctor(debug = false) {
369236
369319
  const optionalMissing = optional4.filter((r) => !r.available);
369237
369320
  p4.log.step("Skills:");
369238
369321
  const configDir = join57(homedir16(), ".config", "opencode");
369239
- const globalSkillsPath = join57(configDir, "skills");
369322
+ const globalSkillsPath = join57(configDir, "skill");
369240
369323
  const bundledSkillsPath = join57(__dirname2, "..", "global-skills");
369241
369324
  if (existsSync37(globalSkillsPath)) {
369242
369325
  try {
@@ -370073,7 +370156,7 @@ function config8() {
370073
370156
  const plannerAgentPath = join57(agentDir, "swarm-planner.md");
370074
370157
  const workerAgentPath = join57(agentDir, "swarm-worker.md");
370075
370158
  const researcherAgentPath = join57(agentDir, "swarm-researcher.md");
370076
- const globalSkillsPath = join57(configDir, "skills");
370159
+ const globalSkillsPath = join57(configDir, "skill");
370077
370160
  console.log(yellow3(BANNER));
370078
370161
  console.log(dim4(" " + TAGLINE + " v" + VERSION6));
370079
370162
  console.log();
@@ -981,6 +981,20 @@ const swarmmail_release = tool({
981
981
  execute: (args, ctx) => execTool("swarmmail_release", args, ctx),
982
982
  });
983
983
 
984
+ const swarmmail_release_all = tool({
985
+ description: "Release all file reservations in the project (coordinator override)",
986
+ args: {},
987
+ execute: (args, ctx) => execTool("swarmmail_release_all", args, ctx),
988
+ });
989
+
990
+ const swarmmail_release_agent = tool({
991
+ description: "Release all file reservations for a specific agent (coordinator override)",
992
+ args: {
993
+ agent_name: tool.schema.string().describe("Target agent name"),
994
+ },
995
+ execute: (args, ctx) => execTool("swarmmail_release_agent", args, ctx),
996
+ });
997
+
984
998
  const swarmmail_ack = tool({
985
999
  description: "Acknowledge a message",
986
1000
  args: {
@@ -1176,7 +1190,7 @@ const swarm_progress = tool({
1176
1190
 
1177
1191
  const swarm_complete = tool({
1178
1192
  description:
1179
- "Mark subtask complete with Verification Gate. Runs UBS scan, typecheck, and tests before allowing completion.",
1193
+ "Mark subtask complete with Verification Gate. Runs typecheck and tests before allowing completion.",
1180
1194
  args: {
1181
1195
  project_key: tool.schema.string().describe("Project key"),
1182
1196
  agent_name: tool.schema.string().describe("Agent name"),
@@ -1187,11 +1201,10 @@ const swarm_complete = tool({
1187
1201
  .array(tool.schema.string())
1188
1202
  .optional()
1189
1203
  .describe("Files modified - will be verified"),
1190
- skip_ubs_scan: tool.schema.boolean().optional().describe("Skip UBS scan"),
1191
1204
  skip_verification: tool.schema
1192
1205
  .boolean()
1193
1206
  .optional()
1194
- .describe("Skip ALL verification (UBS, typecheck, tests)"),
1207
+ .describe("Skip ALL verification (typecheck, tests)"),
1195
1208
  skip_review: tool.schema
1196
1209
  .boolean()
1197
1210
  .optional()
@@ -2832,6 +2845,8 @@ const SwarmPlugin: Plugin = async (
2832
2845
  swarmmail_read_message,
2833
2846
  swarmmail_reserve,
2834
2847
  swarmmail_release,
2848
+ swarmmail_release_all,
2849
+ swarmmail_release_agent,
2835
2850
  swarmmail_ack,
2836
2851
  swarmmail_health,
2837
2852
  // Structured
@@ -0,0 +1,21 @@
1
+ export interface AtomicWriteOptions {
2
+ append?: boolean;
3
+ }
4
+ /**
5
+ * Writes content to a file atomically using a temp file + rename pattern.
6
+ *
7
+ * @param path - Target file path
8
+ * @param content - Content to write
9
+ * @param options - Write options (append mode)
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Basic write
14
+ * await writeFileAtomic("output.txt", "Hello, World!");
15
+ *
16
+ * // Append mode
17
+ * await writeFileAtomic("log.txt", "New entry\n", { append: true });
18
+ * ```
19
+ */
20
+ export declare function writeFileAtomic(path: string, content: string, options?: AtomicWriteOptions): Promise<void>;
21
+ //# sourceMappingURL=atomic-write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.d.ts","sourceRoot":"","sources":["../../src/hooks/atomic-write.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,kBAAkB,GAC3B,OAAO,CAAC,IAAI,CAAC,CAqBf"}