opencode-swarm-plugin 0.48.0 → 0.49.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/bin/swarm.js CHANGED
@@ -26930,7 +26930,7 @@ __export(exports_skills2, {
26930
26930
  });
26931
26931
  import { readdir as readdir2, readFile as readFile2, stat as stat2, mkdir as mkdir2, writeFile as writeFile2, rm as rm2 } from "fs/promises";
26932
26932
  import {
26933
- join as join18,
26933
+ join as join17,
26934
26934
  basename as basename2,
26935
26935
  dirname as dirname8,
26936
26936
  resolve as resolve2,
@@ -27006,19 +27006,19 @@ function validateSkillMetadata2(raw, filePath) {
27006
27006
  }
27007
27007
  function getGlobalSkillsDir2() {
27008
27008
  const home = process.env.HOME || process.env.USERPROFILE || "~";
27009
- return join18(home, ".config", "opencode", "skill");
27009
+ return join17(home, ".config", "opencode", "skill");
27010
27010
  }
27011
27011
  function getClaudeGlobalSkillsDir2() {
27012
27012
  const home = process.env.HOME || process.env.USERPROFILE || "~";
27013
- return join18(home, ".claude", "skills");
27013
+ return join17(home, ".claude", "skills");
27014
27014
  }
27015
27015
  function getPackageSkillsDir2() {
27016
27016
  try {
27017
27017
  const currentFilePath = fileURLToPath2(import.meta.url);
27018
- return join18(dirname8(currentFilePath), "..", "global-skills");
27018
+ return join17(dirname8(currentFilePath), "..", "global-skills");
27019
27019
  } catch {
27020
27020
  const currentDir = decodeURIComponent(new URL(".", import.meta.url).pathname);
27021
- return join18(currentDir, "..", "global-skills");
27021
+ return join17(currentDir, "..", "global-skills");
27022
27022
  }
27023
27023
  }
27024
27024
  async function findSkillFiles2(baseDir) {
@@ -27027,7 +27027,7 @@ async function findSkillFiles2(baseDir) {
27027
27027
  const entries = await readdir2(baseDir, { withFileTypes: true });
27028
27028
  for (const entry of entries) {
27029
27029
  if (entry.isDirectory()) {
27030
- const skillPath = join18(baseDir, entry.name, "SKILL.md");
27030
+ const skillPath = join17(baseDir, entry.name, "SKILL.md");
27031
27031
  try {
27032
27032
  const s = await stat2(skillPath);
27033
27033
  if (s.isFile()) {
@@ -27041,7 +27041,7 @@ async function findSkillFiles2(baseDir) {
27041
27041
  }
27042
27042
  async function findSkillScripts2(skillDir) {
27043
27043
  const scripts = [];
27044
- const scriptsDir = join18(skillDir, "scripts");
27044
+ const scriptsDir = join17(skillDir, "scripts");
27045
27045
  try {
27046
27046
  const entries = await readdir2(scriptsDir, { withFileTypes: true });
27047
27047
  for (const entry of entries) {
@@ -27089,7 +27089,7 @@ async function discoverSkills2(projectDir) {
27089
27089
  }
27090
27090
  }
27091
27091
  for (const relPath of PROJECT_SKILL_DIRECTORIES2) {
27092
- await loadSkillsFromDir(join18(dir, relPath));
27092
+ await loadSkillsFromDir(join17(dir, relPath));
27093
27093
  }
27094
27094
  await loadSkillsFromDir(getGlobalSkillsDir2());
27095
27095
  await loadSkillsFromDir(getClaudeGlobalSkillsDir2());
@@ -27460,7 +27460,7 @@ Scripts run in the skill's directory with the project directory as an argument.`
27460
27460
  if (!skill.scripts.includes(args2.script)) {
27461
27461
  return `Script '${args2.script}' not found in skill '${args2.skill}'. Available: ${skill.scripts.join(", ") || "none"}`;
27462
27462
  }
27463
- const scriptPath = join18(skill.directory, "scripts", args2.script);
27463
+ const scriptPath = join17(skill.directory, "scripts", args2.script);
27464
27464
  const scriptArgs = args2.args || [];
27465
27465
  try {
27466
27466
  const TIMEOUT_MS = 60000;
@@ -27569,14 +27569,14 @@ Good skills have:
27569
27569
  const csoWarnings = validateCSOCompliance2(args2.name, args2.description);
27570
27570
  let skillDir;
27571
27571
  if (args2.directory === "global") {
27572
- skillDir = join18(getGlobalSkillsDir2(), args2.name);
27572
+ skillDir = join17(getGlobalSkillsDir2(), args2.name);
27573
27573
  } else if (args2.directory === "global-claude") {
27574
- skillDir = join18(getClaudeGlobalSkillsDir2(), args2.name);
27574
+ skillDir = join17(getClaudeGlobalSkillsDir2(), args2.name);
27575
27575
  } else {
27576
27576
  const baseDir = args2.directory || DEFAULT_SKILLS_DIR2;
27577
- skillDir = join18(skillsProjectDirectory2, baseDir, args2.name);
27577
+ skillDir = join17(skillsProjectDirectory2, baseDir, args2.name);
27578
27578
  }
27579
- const skillPath = join18(skillDir, "SKILL.md");
27579
+ const skillPath = join17(skillDir, "SKILL.md");
27580
27580
  try {
27581
27581
  await mkdir2(skillDir, { recursive: true });
27582
27582
  const content = generateSkillContent2(args2.name, args2.description, args2.body, { tags: args2.tags, tools: args2.tools });
@@ -27733,8 +27733,8 @@ executed with skills_execute. Use for:
27733
27733
  if (isAbsolute2(args2.script_name) || args2.script_name.includes("..") || args2.script_name.includes("/") || args2.script_name.includes("\\") || basename2(args2.script_name) !== args2.script_name) {
27734
27734
  return "Invalid script name. Use simple filenames without paths.";
27735
27735
  }
27736
- const scriptsDir = join18(skill.directory, "scripts");
27737
- const scriptPath = join18(scriptsDir, args2.script_name);
27736
+ const scriptsDir = join17(skill.directory, "scripts");
27737
+ const scriptPath = join17(scriptsDir, args2.script_name);
27738
27738
  try {
27739
27739
  await mkdir2(scriptsDir, { recursive: true });
27740
27740
  await writeFile2(scriptPath, args2.content, {
@@ -27783,20 +27783,20 @@ Perfect for learning to create effective skills.`,
27783
27783
  }
27784
27784
  let skillDir;
27785
27785
  if (args2.directory === "global") {
27786
- skillDir = join18(getGlobalSkillsDir2(), args2.name);
27786
+ skillDir = join17(getGlobalSkillsDir2(), args2.name);
27787
27787
  } else {
27788
27788
  const baseDir = args2.directory || DEFAULT_SKILLS_DIR2;
27789
- skillDir = join18(skillsProjectDirectory2, baseDir, args2.name);
27789
+ skillDir = join17(skillsProjectDirectory2, baseDir, args2.name);
27790
27790
  }
27791
27791
  const createdFiles = [];
27792
27792
  try {
27793
27793
  await mkdir2(skillDir, { recursive: true });
27794
- const skillPath = join18(skillDir, "SKILL.md");
27794
+ const skillPath = join17(skillDir, "SKILL.md");
27795
27795
  const skillContent = generateSkillTemplate2(args2.name, args2.description);
27796
27796
  await writeFile2(skillPath, skillContent, "utf-8");
27797
27797
  createdFiles.push("SKILL.md");
27798
27798
  if (args2.include_example_script !== false) {
27799
- const scriptsDir = join18(skillDir, "scripts");
27799
+ const scriptsDir = join17(skillDir, "scripts");
27800
27800
  await mkdir2(scriptsDir, { recursive: true });
27801
27801
  const exampleScript = `#!/usr/bin/env bash
27802
27802
  # Example helper script for ${args2.name}
@@ -27810,15 +27810,15 @@ echo "Project directory: $1"
27810
27810
 
27811
27811
  # TODO: Add actual script logic
27812
27812
  `;
27813
- const scriptPath = join18(scriptsDir, "example.sh");
27813
+ const scriptPath = join17(scriptsDir, "example.sh");
27814
27814
  await writeFile2(scriptPath, exampleScript, { mode: 493 });
27815
27815
  createdFiles.push("scripts/example.sh");
27816
27816
  }
27817
27817
  if (args2.include_reference !== false) {
27818
- const refsDir = join18(skillDir, "references");
27818
+ const refsDir = join17(skillDir, "references");
27819
27819
  await mkdir2(refsDir, { recursive: true });
27820
27820
  const refContent = generateReferenceTemplate2(args2.name);
27821
- const refPath = join18(refsDir, "guide.md");
27821
+ const refPath = join17(refsDir, "guide.md");
27822
27822
  await writeFile2(refPath, refContent, "utf-8");
27823
27823
  createdFiles.push("references/guide.md");
27824
27824
  }
@@ -27874,11 +27874,16 @@ echo "Project directory: $1"
27874
27874
  // src/swarm-insights.ts
27875
27875
  var exports_swarm_insights2 = {};
27876
27876
  __export(exports_swarm_insights2, {
27877
+ trackCoordinatorViolation: () => trackCoordinatorViolation2,
27878
+ getViolationAnalytics: () => getViolationAnalytics2,
27877
27879
  getStrategyInsights: () => getStrategyInsights2,
27880
+ getRejectionAnalytics: () => getRejectionAnalytics3,
27878
27881
  getPatternInsights: () => getPatternInsights2,
27879
27882
  getFileInsights: () => getFileInsights2,
27883
+ getFileFailureHistory: () => getFileFailureHistory2,
27880
27884
  getCachedInsights: () => getCachedInsights2,
27881
27885
  formatInsightsForPrompt: () => formatInsightsForPrompt2,
27886
+ formatFileHistoryWarnings: () => formatFileHistoryWarnings2,
27882
27887
  clearInsightsCache: () => clearInsightsCache2
27883
27888
  });
27884
27889
  async function getStrategyInsights2(swarmMail, _task) {
@@ -27950,6 +27955,136 @@ async function getFileInsights2(swarmMail, files) {
27950
27955
  async function getFileGotchas2(_swarmMail, _file4) {
27951
27956
  return [];
27952
27957
  }
27958
+ async function getFileFailureHistory2(swarmMail, files) {
27959
+ if (files.length === 0)
27960
+ return [];
27961
+ const db = await swarmMail.getDatabase();
27962
+ const histories = [];
27963
+ for (const file4 of files) {
27964
+ const query = `
27965
+ SELECT data
27966
+ FROM events
27967
+ WHERE type = 'review_feedback'
27968
+ AND json_extract(data, '$.status') = 'needs_changes'
27969
+ AND json_extract(data, '$.issues') LIKE ?
27970
+ `;
27971
+ const result = await db.query(query, [`%${file4}%`]);
27972
+ if (!result.rows || result.rows.length === 0) {
27973
+ continue;
27974
+ }
27975
+ const issueTexts = [];
27976
+ for (const row of result.rows) {
27977
+ try {
27978
+ const data = JSON.parse(row.data);
27979
+ const issuesStr = data.issues;
27980
+ if (!issuesStr)
27981
+ continue;
27982
+ const issues = JSON.parse(issuesStr);
27983
+ for (const issue4 of issues) {
27984
+ if (issue4.file === file4) {
27985
+ issueTexts.push(issue4.issue);
27986
+ }
27987
+ }
27988
+ } catch (e) {
27989
+ continue;
27990
+ }
27991
+ }
27992
+ if (issueTexts.length === 0) {
27993
+ continue;
27994
+ }
27995
+ const issueCounts = new Map;
27996
+ for (const text of issueTexts) {
27997
+ issueCounts.set(text, (issueCounts.get(text) || 0) + 1);
27998
+ }
27999
+ const topIssues = Array.from(issueCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([text]) => text);
28000
+ histories.push({
28001
+ file: file4,
28002
+ rejectionCount: issueTexts.length,
28003
+ topIssues
28004
+ });
28005
+ }
28006
+ return histories;
28007
+ }
28008
+ async function getRejectionAnalytics3(swarmMail) {
28009
+ const db = await swarmMail.getDatabase();
28010
+ const query = `
28011
+ SELECT data
28012
+ FROM events
28013
+ WHERE type = 'review_feedback'
28014
+ ORDER BY timestamp DESC
28015
+ `;
28016
+ const result = await db.query(query, []);
28017
+ if (!result.rows || result.rows.length === 0) {
28018
+ return {
28019
+ totalReviews: 0,
28020
+ approved: 0,
28021
+ rejected: 0,
28022
+ approvalRate: 0,
28023
+ topReasons: []
28024
+ };
28025
+ }
28026
+ let approved = 0;
28027
+ let rejected = 0;
28028
+ const reasonCounts = new Map;
28029
+ for (const row of result.rows) {
28030
+ try {
28031
+ const data = JSON.parse(row.data);
28032
+ if (data.status === "approved") {
28033
+ approved++;
28034
+ } else if (data.status === "needs_changes") {
28035
+ rejected++;
28036
+ if (data.issues) {
28037
+ const issues = JSON.parse(data.issues);
28038
+ for (const issue4 of issues) {
28039
+ const category = categorizeRejectionReason3(issue4.issue);
28040
+ reasonCounts.set(category, (reasonCounts.get(category) || 0) + 1);
28041
+ }
28042
+ }
28043
+ }
28044
+ } catch (e) {
28045
+ continue;
28046
+ }
28047
+ }
28048
+ const totalReviews = approved + rejected;
28049
+ const approvalRate = totalReviews > 0 ? approved / totalReviews * 100 : 0;
28050
+ const topReasons = Array.from(reasonCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([category, count]) => ({
28051
+ category,
28052
+ count,
28053
+ percentage: rejected > 0 ? count / rejected * 100 : 0
28054
+ }));
28055
+ return {
28056
+ totalReviews,
28057
+ approved,
28058
+ rejected,
28059
+ approvalRate,
28060
+ topReasons
28061
+ };
28062
+ }
28063
+ function categorizeRejectionReason3(reason) {
28064
+ const lowerReason = reason.toLowerCase();
28065
+ if (lowerReason.includes("test") || lowerReason.includes("spec") || lowerReason.includes("coverage")) {
28066
+ return "Missing tests";
28067
+ }
28068
+ if (lowerReason.includes("type") || lowerReason.includes("undefined") || lowerReason.includes("null") || lowerReason.includes("assignable")) {
28069
+ return "Type errors";
28070
+ }
28071
+ if (lowerReason.includes("incomplete") || lowerReason.includes("missing") || lowerReason.includes("forgot") || lowerReason.includes("didn't implement")) {
28072
+ return "Incomplete implementation";
28073
+ }
28074
+ if (lowerReason.includes("wrong file") || lowerReason.includes("modified incorrect") || lowerReason.includes("shouldn't have changed")) {
28075
+ return "Wrong file modified";
28076
+ }
28077
+ if (lowerReason.includes("performance") || lowerReason.includes("slow") || lowerReason.includes("inefficient")) {
28078
+ return "Performance issue";
28079
+ }
28080
+ if (lowerReason.includes("security") || lowerReason.includes("vulnerability") || lowerReason.includes("unsafe")) {
28081
+ return "Security vulnerability";
28082
+ }
28083
+ if (lowerReason.includes("error handling") || lowerReason.includes("try/catch") || lowerReason.includes("exception")) {
28084
+ return "Missing error handling";
28085
+ }
28086
+ return "Other";
28087
+ }
27953
28088
  async function getPatternInsights2(swarmMail) {
27954
28089
  const db = await swarmMail.getDatabase();
27955
28090
  const patterns = [];
@@ -28023,24 +28158,125 @@ ${patternLines.join(`
28023
28158
  return result;
28024
28159
  }
28025
28160
  async function getCachedInsights2(_swarmMail, cacheKey, computeFn) {
28026
- const cached6 = insightsCache2.get(cacheKey);
28161
+ const cached6 = insightsCache3.get(cacheKey);
28027
28162
  if (cached6 && cached6.expires > Date.now()) {
28028
28163
  return cached6.data;
28029
28164
  }
28030
28165
  const data = await computeFn();
28031
- insightsCache2.set(cacheKey, {
28166
+ insightsCache3.set(cacheKey, {
28032
28167
  data,
28033
- expires: Date.now() + CACHE_TTL_MS2
28168
+ expires: Date.now() + CACHE_TTL_MS3
28034
28169
  });
28035
28170
  return data;
28036
28171
  }
28037
28172
  function clearInsightsCache2() {
28038
- insightsCache2.clear();
28173
+ insightsCache3.clear();
28174
+ }
28175
+ async function trackCoordinatorViolation2(swarmMail, violation) {
28176
+ const db = await swarmMail.getDatabase();
28177
+ const query = `
28178
+ INSERT INTO events (type, project_key, timestamp, data)
28179
+ VALUES (?, ?, ?, ?)
28180
+ RETURNING id
28181
+ `;
28182
+ const data = JSON.stringify({
28183
+ session_id: violation.session_id,
28184
+ epic_id: violation.epic_id,
28185
+ event_type: "VIOLATION",
28186
+ violation_type: violation.violation_type,
28187
+ payload: violation.payload
28188
+ });
28189
+ const result = await db.query(query, [
28190
+ "coordinator_violation",
28191
+ violation.project_key,
28192
+ Date.now(),
28193
+ data
28194
+ ]);
28195
+ return result.rows[0].id;
28196
+ }
28197
+ async function getViolationAnalytics2(swarmMail, projectKey) {
28198
+ const db = await swarmMail.getDatabase();
28199
+ const violationsQuery = projectKey ? `
28200
+ SELECT data
28201
+ FROM events
28202
+ WHERE type = 'coordinator_violation'
28203
+ AND project_key = ?
28204
+ ORDER BY timestamp DESC
28205
+ ` : `
28206
+ SELECT data
28207
+ FROM events
28208
+ WHERE type = 'coordinator_violation'
28209
+ ORDER BY timestamp DESC
28210
+ `;
28211
+ const params = projectKey ? [projectKey] : [];
28212
+ const result = await db.query(violationsQuery, params);
28213
+ if (!result.rows || result.rows.length === 0) {
28214
+ return {
28215
+ totalViolations: 0,
28216
+ byType: [],
28217
+ violationRate: 0
28218
+ };
28219
+ }
28220
+ const violationCounts = new Map;
28221
+ let totalViolations = 0;
28222
+ for (const row of result.rows) {
28223
+ try {
28224
+ const data = JSON.parse(row.data);
28225
+ const violationType = data.violation_type;
28226
+ if (violationType) {
28227
+ violationCounts.set(violationType, (violationCounts.get(violationType) || 0) + 1);
28228
+ totalViolations++;
28229
+ }
28230
+ } catch (e) {
28231
+ continue;
28232
+ }
28233
+ }
28234
+ const byType = Array.from(violationCounts.entries()).sort((a, b) => b[1] - a[1]).map(([violationType, count]) => ({
28235
+ violationType,
28236
+ count,
28237
+ percentage: count / totalViolations * 100
28238
+ }));
28239
+ const coordinationQuery = projectKey ? `
28240
+ SELECT COUNT(*) as count
28241
+ FROM events
28242
+ WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
28243
+ AND project_key = ?
28244
+ ` : `
28245
+ SELECT COUNT(*) as count
28246
+ FROM events
28247
+ WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
28248
+ `;
28249
+ const coordResult = await db.query(coordinationQuery, params);
28250
+ const coordinationCount = coordResult.rows[0]?.count || 0;
28251
+ const violationRate = coordinationCount > 0 ? totalViolations / coordinationCount * 100 : 0;
28252
+ return {
28253
+ totalViolations,
28254
+ byType,
28255
+ violationRate
28256
+ };
28039
28257
  }
28040
- var insightsCache2, CACHE_TTL_MS2;
28258
+ function formatFileHistoryWarnings2(histories) {
28259
+ if (histories.length === 0) {
28260
+ return "";
28261
+ }
28262
+ const lines = ["⚠️ FILE HISTORY WARNINGS:"];
28263
+ for (const history of histories) {
28264
+ const workerText = history.rejectionCount === 1 ? "1 previous worker rejected" : `${history.rejectionCount} previous workers rejected`;
28265
+ const issuesText = history.topIssues.join(", ");
28266
+ lines.push(`- ${history.file}: ${workerText} for ${issuesText}`);
28267
+ }
28268
+ let result = lines.join(`
28269
+ `);
28270
+ const maxChars = 300 * 4;
28271
+ if (result.length > maxChars) {
28272
+ result = result.slice(0, maxChars - 3) + "...";
28273
+ }
28274
+ return result;
28275
+ }
28276
+ var insightsCache3, CACHE_TTL_MS3;
28041
28277
  var init_swarm_insights2 = __esm(() => {
28042
- insightsCache2 = new Map;
28043
- CACHE_TTL_MS2 = 5 * 60 * 1000;
28278
+ insightsCache3 = new Map;
28279
+ CACHE_TTL_MS3 = 5 * 60 * 1000;
28044
28280
  });
28045
28281
 
28046
28282
  // src/model-selection.ts
@@ -39175,8 +39411,8 @@ import {
39175
39411
  statSync as statSync2,
39176
39412
  writeFileSync as writeFileSync7
39177
39413
  } from "fs";
39178
- import { homedir as homedir11 } from "os";
39179
- import { basename as basename3, dirname as dirname9, join as join29 } from "path";
39414
+ import { homedir as homedir10 } from "os";
39415
+ import { basename as basename3, dirname as dirname9, join as join28 } from "path";
39180
39416
  import { fileURLToPath as fileURLToPath3 } from "url";
39181
39417
 
39182
39418
  // dist/hive.js
@@ -52662,7 +52898,8 @@ var CellTreeSchema = exports_external.object({
52662
52898
  title: exports_external.string().min(1),
52663
52899
  description: exports_external.string().optional().default("")
52664
52900
  }),
52665
- subtasks: exports_external.array(SubtaskSpecSchema).min(1)
52901
+ subtasks: exports_external.array(SubtaskSpecSchema).min(1),
52902
+ strategy: exports_external.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy from swarm_select_strategy. If not provided, defaults to feature-based.")
52666
52903
  });
52667
52904
  var EpicCreateArgsSchema = exports_external.object({
52668
52905
  epic_title: exports_external.string().min(1),
@@ -53369,13 +53606,13 @@ async function autoMigrateFromJSONL(adapter, projectKey) {
53369
53606
  skipExisting: true
53370
53607
  });
53371
53608
  if (result.created > 0 || result.updated > 0) {
53372
- console.log(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
53609
+ console.error(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
53373
53610
  }
53374
53611
  if (result.errors.length > 0) {
53375
- console.warn(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
53612
+ console.error(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
53376
53613
  }
53377
53614
  } catch (error45) {
53378
- console.warn(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error45 instanceof Error ? error45.message : String(error45));
53615
+ console.error(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error45 instanceof Error ? error45.message : String(error45));
53379
53616
  }
53380
53617
  }
53381
53618
  function formatCellForOutput(adapterCell) {
@@ -72486,11 +72723,16 @@ echo "Project directory: $1"
72486
72723
  });
72487
72724
  var exports_swarm_insights = {};
72488
72725
  __export3(exports_swarm_insights, {
72726
+ trackCoordinatorViolation: () => trackCoordinatorViolation,
72727
+ getViolationAnalytics: () => getViolationAnalytics,
72489
72728
  getStrategyInsights: () => getStrategyInsights,
72729
+ getRejectionAnalytics: () => getRejectionAnalytics,
72490
72730
  getPatternInsights: () => getPatternInsights,
72491
72731
  getFileInsights: () => getFileInsights,
72732
+ getFileFailureHistory: () => getFileFailureHistory,
72492
72733
  getCachedInsights: () => getCachedInsights,
72493
72734
  formatInsightsForPrompt: () => formatInsightsForPrompt,
72735
+ formatFileHistoryWarnings: () => formatFileHistoryWarnings,
72494
72736
  clearInsightsCache: () => clearInsightsCache
72495
72737
  });
72496
72738
  async function getStrategyInsights(swarmMail, _task) {
@@ -72562,6 +72804,136 @@ async function getFileInsights(swarmMail, files) {
72562
72804
  async function getFileGotchas(_swarmMail, _file22) {
72563
72805
  return [];
72564
72806
  }
72807
+ async function getFileFailureHistory(swarmMail, files) {
72808
+ if (files.length === 0)
72809
+ return [];
72810
+ const db = await swarmMail.getDatabase();
72811
+ const histories = [];
72812
+ for (const file22 of files) {
72813
+ const query = `
72814
+ SELECT data
72815
+ FROM events
72816
+ WHERE type = 'review_feedback'
72817
+ AND json_extract(data, '$.status') = 'needs_changes'
72818
+ AND json_extract(data, '$.issues') LIKE ?
72819
+ `;
72820
+ const result = await db.query(query, [`%${file22}%`]);
72821
+ if (!result.rows || result.rows.length === 0) {
72822
+ continue;
72823
+ }
72824
+ const issueTexts = [];
72825
+ for (const row of result.rows) {
72826
+ try {
72827
+ const data = JSON.parse(row.data);
72828
+ const issuesStr = data.issues;
72829
+ if (!issuesStr)
72830
+ continue;
72831
+ const issues = JSON.parse(issuesStr);
72832
+ for (const issue22 of issues) {
72833
+ if (issue22.file === file22) {
72834
+ issueTexts.push(issue22.issue);
72835
+ }
72836
+ }
72837
+ } catch (e) {
72838
+ continue;
72839
+ }
72840
+ }
72841
+ if (issueTexts.length === 0) {
72842
+ continue;
72843
+ }
72844
+ const issueCounts = new Map;
72845
+ for (const text of issueTexts) {
72846
+ issueCounts.set(text, (issueCounts.get(text) || 0) + 1);
72847
+ }
72848
+ const topIssues = Array.from(issueCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 3).map(([text]) => text);
72849
+ histories.push({
72850
+ file: file22,
72851
+ rejectionCount: issueTexts.length,
72852
+ topIssues
72853
+ });
72854
+ }
72855
+ return histories;
72856
+ }
72857
+ async function getRejectionAnalytics(swarmMail) {
72858
+ const db = await swarmMail.getDatabase();
72859
+ const query = `
72860
+ SELECT data
72861
+ FROM events
72862
+ WHERE type = 'review_feedback'
72863
+ ORDER BY timestamp DESC
72864
+ `;
72865
+ const result = await db.query(query, []);
72866
+ if (!result.rows || result.rows.length === 0) {
72867
+ return {
72868
+ totalReviews: 0,
72869
+ approved: 0,
72870
+ rejected: 0,
72871
+ approvalRate: 0,
72872
+ topReasons: []
72873
+ };
72874
+ }
72875
+ let approved = 0;
72876
+ let rejected = 0;
72877
+ const reasonCounts = new Map;
72878
+ for (const row of result.rows) {
72879
+ try {
72880
+ const data = JSON.parse(row.data);
72881
+ if (data.status === "approved") {
72882
+ approved++;
72883
+ } else if (data.status === "needs_changes") {
72884
+ rejected++;
72885
+ if (data.issues) {
72886
+ const issues = JSON.parse(data.issues);
72887
+ for (const issue22 of issues) {
72888
+ const category = categorizeRejectionReason(issue22.issue);
72889
+ reasonCounts.set(category, (reasonCounts.get(category) || 0) + 1);
72890
+ }
72891
+ }
72892
+ }
72893
+ } catch (e) {
72894
+ continue;
72895
+ }
72896
+ }
72897
+ const totalReviews = approved + rejected;
72898
+ const approvalRate = totalReviews > 0 ? approved / totalReviews * 100 : 0;
72899
+ const topReasons = Array.from(reasonCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([category, count]) => ({
72900
+ category,
72901
+ count,
72902
+ percentage: rejected > 0 ? count / rejected * 100 : 0
72903
+ }));
72904
+ return {
72905
+ totalReviews,
72906
+ approved,
72907
+ rejected,
72908
+ approvalRate,
72909
+ topReasons
72910
+ };
72911
+ }
72912
+ function categorizeRejectionReason(reason) {
72913
+ const lowerReason = reason.toLowerCase();
72914
+ if (lowerReason.includes("test") || lowerReason.includes("spec") || lowerReason.includes("coverage")) {
72915
+ return "Missing tests";
72916
+ }
72917
+ if (lowerReason.includes("type") || lowerReason.includes("undefined") || lowerReason.includes("null") || lowerReason.includes("assignable")) {
72918
+ return "Type errors";
72919
+ }
72920
+ if (lowerReason.includes("incomplete") || lowerReason.includes("missing") || lowerReason.includes("forgot") || lowerReason.includes("didn't implement")) {
72921
+ return "Incomplete implementation";
72922
+ }
72923
+ if (lowerReason.includes("wrong file") || lowerReason.includes("modified incorrect") || lowerReason.includes("shouldn't have changed")) {
72924
+ return "Wrong file modified";
72925
+ }
72926
+ if (lowerReason.includes("performance") || lowerReason.includes("slow") || lowerReason.includes("inefficient")) {
72927
+ return "Performance issue";
72928
+ }
72929
+ if (lowerReason.includes("security") || lowerReason.includes("vulnerability") || lowerReason.includes("unsafe")) {
72930
+ return "Security vulnerability";
72931
+ }
72932
+ if (lowerReason.includes("error handling") || lowerReason.includes("try/catch") || lowerReason.includes("exception")) {
72933
+ return "Missing error handling";
72934
+ }
72935
+ return "Other";
72936
+ }
72565
72937
  async function getPatternInsights(swarmMail) {
72566
72938
  const db = await swarmMail.getDatabase();
72567
72939
  const patterns = [];
@@ -72649,6 +73021,107 @@ async function getCachedInsights(_swarmMail, cacheKey, computeFn) {
72649
73021
  function clearInsightsCache() {
72650
73022
  insightsCache.clear();
72651
73023
  }
73024
+ async function trackCoordinatorViolation(swarmMail, violation) {
73025
+ const db = await swarmMail.getDatabase();
73026
+ const query = `
73027
+ INSERT INTO events (type, project_key, timestamp, data)
73028
+ VALUES (?, ?, ?, ?)
73029
+ RETURNING id
73030
+ `;
73031
+ const data = JSON.stringify({
73032
+ session_id: violation.session_id,
73033
+ epic_id: violation.epic_id,
73034
+ event_type: "VIOLATION",
73035
+ violation_type: violation.violation_type,
73036
+ payload: violation.payload
73037
+ });
73038
+ const result = await db.query(query, [
73039
+ "coordinator_violation",
73040
+ violation.project_key,
73041
+ Date.now(),
73042
+ data
73043
+ ]);
73044
+ return result.rows[0].id;
73045
+ }
73046
+ async function getViolationAnalytics(swarmMail, projectKey) {
73047
+ const db = await swarmMail.getDatabase();
73048
+ const violationsQuery = projectKey ? `
73049
+ SELECT data
73050
+ FROM events
73051
+ WHERE type = 'coordinator_violation'
73052
+ AND project_key = ?
73053
+ ORDER BY timestamp DESC
73054
+ ` : `
73055
+ SELECT data
73056
+ FROM events
73057
+ WHERE type = 'coordinator_violation'
73058
+ ORDER BY timestamp DESC
73059
+ `;
73060
+ const params = projectKey ? [projectKey] : [];
73061
+ const result = await db.query(violationsQuery, params);
73062
+ if (!result.rows || result.rows.length === 0) {
73063
+ return {
73064
+ totalViolations: 0,
73065
+ byType: [],
73066
+ violationRate: 0
73067
+ };
73068
+ }
73069
+ const violationCounts = new Map;
73070
+ let totalViolations = 0;
73071
+ for (const row of result.rows) {
73072
+ try {
73073
+ const data = JSON.parse(row.data);
73074
+ const violationType = data.violation_type;
73075
+ if (violationType) {
73076
+ violationCounts.set(violationType, (violationCounts.get(violationType) || 0) + 1);
73077
+ totalViolations++;
73078
+ }
73079
+ } catch (e) {
73080
+ continue;
73081
+ }
73082
+ }
73083
+ const byType = Array.from(violationCounts.entries()).sort((a, b) => b[1] - a[1]).map(([violationType, count]) => ({
73084
+ violationType,
73085
+ count,
73086
+ percentage: count / totalViolations * 100
73087
+ }));
73088
+ const coordinationQuery = projectKey ? `
73089
+ SELECT COUNT(*) as count
73090
+ FROM events
73091
+ WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
73092
+ AND project_key = ?
73093
+ ` : `
73094
+ SELECT COUNT(*) as count
73095
+ FROM events
73096
+ WHERE type IN ('worker_spawned', 'review_feedback', 'message_sent')
73097
+ `;
73098
+ const coordResult = await db.query(coordinationQuery, params);
73099
+ const coordinationCount = coordResult.rows[0]?.count || 0;
73100
+ const violationRate = coordinationCount > 0 ? totalViolations / coordinationCount * 100 : 0;
73101
+ return {
73102
+ totalViolations,
73103
+ byType,
73104
+ violationRate
73105
+ };
73106
+ }
73107
+ function formatFileHistoryWarnings(histories) {
73108
+ if (histories.length === 0) {
73109
+ return "";
73110
+ }
73111
+ const lines = ["⚠️ FILE HISTORY WARNINGS:"];
73112
+ for (const history of histories) {
73113
+ const workerText = history.rejectionCount === 1 ? "1 previous worker rejected" : `${history.rejectionCount} previous workers rejected`;
73114
+ const issuesText = history.topIssues.join(", ");
73115
+ lines.push(`- ${history.file}: ${workerText} for ${issuesText}`);
73116
+ }
73117
+ let result = lines.join(`
73118
+ `);
73119
+ const maxChars = 300 * 4;
73120
+ if (result.length > maxChars) {
73121
+ result = result.slice(0, maxChars - 3) + "...";
73122
+ }
73123
+ return result;
73124
+ }
72652
73125
  var insightsCache;
72653
73126
  var CACHE_TTL_MS;
72654
73127
  var init_swarm_insights = __esm3(() => {
@@ -74537,7 +75010,8 @@ var CellTreeSchema2 = exports_external2.object({
74537
75010
  title: exports_external2.string().min(1),
74538
75011
  description: exports_external2.string().optional().default("")
74539
75012
  }),
74540
- subtasks: exports_external2.array(SubtaskSpecSchema2).min(1)
75013
+ subtasks: exports_external2.array(SubtaskSpecSchema2).min(1),
75014
+ strategy: exports_external2.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy from swarm_select_strategy. If not provided, defaults to feature-based.")
74541
75015
  });
74542
75016
  var EpicCreateArgsSchema2 = exports_external2.object({
74543
75017
  epic_title: exports_external2.string().min(1),
@@ -75411,13 +75885,13 @@ async function autoMigrateFromJSONL2(adapter, projectKey) {
75411
75885
  skipExisting: true
75412
75886
  });
75413
75887
  if (result.created > 0 || result.updated > 0) {
75414
- console.log(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
75888
+ console.error(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
75415
75889
  }
75416
75890
  if (result.errors.length > 0) {
75417
- console.warn(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
75891
+ console.error(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
75418
75892
  }
75419
75893
  } catch (error452) {
75420
- console.warn(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error452 instanceof Error ? error452.message : String(error452));
75894
+ console.error(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error452 instanceof Error ? error452.message : String(error452));
75421
75895
  }
75422
75896
  }
75423
75897
  function formatCellForOutput2(adapterCell) {
@@ -77566,7 +78040,7 @@ var swarm_complete = tool2({
77566
78040
  files_touched: tool2.schema.array(tool2.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
77567
78041
  skip_verification: tool2.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
77568
78042
  planned_files: tool2.schema.array(tool2.schema.string()).optional().describe("Files that were originally planned to be modified"),
77569
- start_time: tool2.schema.number().optional().describe("Task start timestamp (Unix ms) for duration calculation"),
78043
+ start_time: tool2.schema.number().describe("Task start timestamp (Unix ms) for duration calculation - REQUIRED for accurate analytics"),
77570
78044
  error_count: tool2.schema.number().optional().describe("Number of errors encountered during task"),
77571
78045
  retry_count: tool2.schema.number().optional().describe("Number of retry attempts during task"),
77572
78046
  skip_review: tool2.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
@@ -77770,7 +78244,7 @@ This will be recorded as a negative learning signal.`;
77770
78244
  syncError = error452 instanceof Error ? error452.message : String(error452);
77771
78245
  console.warn(`[swarm_complete] Auto-sync failed (non-fatal): ${syncError}`);
77772
78246
  }
77773
- const completionDurationMs = args.start_time ? Date.now() - args.start_time : 0;
78247
+ const completionDurationMs = Date.now() - args.start_time;
77774
78248
  const eventEpicId = cell.parent_id || (args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id);
77775
78249
  try {
77776
78250
  const event = createEvent3("subtask_outcome", {
@@ -77826,12 +78300,18 @@ This will be recorded as a negative learning signal.`;
77826
78300
  memoryError = `Failed to store memory: ${error452 instanceof Error ? error452.message : String(error452)}`;
77827
78301
  console.warn(`[swarm_complete] ${memoryError}`);
77828
78302
  }
78303
+ let reservationsReleased = false;
78304
+ let reservationsReleasedCount = 0;
78305
+ let reservationsReleaseError;
77829
78306
  try {
77830
- await releaseSwarmFiles({
78307
+ const releaseResult = await releaseSwarmFiles({
77831
78308
  projectPath: args.project_key,
77832
78309
  agentName: args.agent_name
77833
78310
  });
78311
+ reservationsReleased = true;
78312
+ reservationsReleasedCount = releaseResult.released;
77834
78313
  } catch (error452) {
78314
+ reservationsReleaseError = error452 instanceof Error ? error452.message : String(error452);
77835
78315
  console.warn(`[swarm] Failed to release file reservations for ${args.agent_name}:`, error452);
77836
78316
  }
77837
78317
  const epicId2 = args.bead_id.includes(".") ? args.bead_id.split(".")[0] : args.bead_id;
@@ -77867,7 +78347,9 @@ This will be recorded as a negative learning signal.`;
77867
78347
  success: true,
77868
78348
  bead_id: args.bead_id,
77869
78349
  closed: true,
77870
- reservations_released: true,
78350
+ reservations_released: reservationsReleased,
78351
+ reservations_released_count: reservationsReleasedCount,
78352
+ reservations_release_error: reservationsReleaseError,
77871
78353
  synced: syncSuccess,
77872
78354
  sync_error: syncError,
77873
78355
  message_sent: messageSent,
@@ -91816,14 +92298,14 @@ async function maybeAutoMigrate(db) {
91816
92298
  if (memoryCount > 0) {
91817
92299
  return;
91818
92300
  }
91819
- console.log("[memory] Legacy database detected, starting auto-migration...");
92301
+ console.error("[memory] Legacy database detected, starting auto-migration...");
91820
92302
  const result = await migrateLegacyMemories({
91821
92303
  targetDb: db,
91822
92304
  dryRun: false,
91823
- onProgress: console.log
92305
+ onProgress: (msg) => console.error(msg)
91824
92306
  });
91825
92307
  if (result.migrated > 0) {
91826
- console.log(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
92308
+ console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
91827
92309
  }
91828
92310
  if (result.failed > 0) {
91829
92311
  console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
@@ -91892,6 +92374,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91892
92374
  async find(args2) {
91893
92375
  const limit = args2.limit ?? 10;
91894
92376
  let results;
92377
+ let usedFallback = false;
91895
92378
  if (args2.fts) {
91896
92379
  results = await store.ftsSearch(args2.query, {
91897
92380
  limit,
@@ -91902,14 +92385,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91902
92385
  const ollama = yield* Ollama;
91903
92386
  return yield* ollama.embed(args2.query);
91904
92387
  });
91905
- const queryEmbedding = await exports_Effect.runPromise(program.pipe(exports_Effect.provide(ollamaLayer)));
91906
- results = await store.search(queryEmbedding, {
91907
- limit,
91908
- threshold: 0.3,
91909
- collection: args2.collection
91910
- });
92388
+ try {
92389
+ const queryEmbedding = await exports_Effect.runPromise(program.pipe(exports_Effect.provide(ollamaLayer)));
92390
+ results = await store.search(queryEmbedding, {
92391
+ limit,
92392
+ threshold: 0.3,
92393
+ collection: args2.collection
92394
+ });
92395
+ } catch (e) {
92396
+ console.warn("[hivemind] Ollama unavailable, falling back to full-text search");
92397
+ usedFallback = true;
92398
+ results = await store.ftsSearch(args2.query, {
92399
+ limit,
92400
+ collection: args2.collection
92401
+ });
92402
+ }
91911
92403
  }
91912
- return {
92404
+ const response = {
91913
92405
  results: results.map((r) => ({
91914
92406
  id: r.memory.id,
91915
92407
  content: args2.expand ? r.memory.content : truncateContent(r.memory.content),
@@ -91920,6 +92412,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91920
92412
  })),
91921
92413
  count: results.length
91922
92414
  };
92415
+ if (usedFallback) {
92416
+ response.fallback_used = true;
92417
+ }
92418
+ return response;
91923
92419
  },
91924
92420
  async get(args2) {
91925
92421
  return store.get(args2.id);
@@ -92738,6 +93234,74 @@ Your role is **ONLY** to:
92738
93234
 
92739
93235
  **ALWAYS spawn workers, even for sequential tasks.** Sequential just means spawn them in order and wait for each to complete before spawning the next.
92740
93236
 
93237
+ ### Explicit NEVER Rules (With Examples)
93238
+
93239
+ \`\`\`
93240
+ ╔═══════════════════════════════════════════════════════════════════════════╗
93241
+ ║ ║
93242
+ ║ ❌ COORDINATORS NEVER DO THIS: ║
93243
+ ║ ║
93244
+ ║ - Read implementation files (read(), glob src/**, grep for patterns) ║
93245
+ ║ - Edit code (edit(), write() any .ts/.js/.tsx files) ║
93246
+ ║ - Run tests (bash "bun test", "npm test", pytest) ║
93247
+ ║ - Implement features (adding functions, components, logic) ║
93248
+ ║ - Fix bugs (changing code to fix errors) ║
93249
+ ║ - Install packages (bash "bun add", "npm install") ║
93250
+ ║ - Commit changes (bash "git add", "git commit") ║
93251
+ ║ - Reserve files (swarmmail_reserve - workers do this) ║
93252
+ ║ ║
93253
+ ║ ✅ COORDINATORS ONLY DO THIS: ║
93254
+ ║ ║
93255
+ ║ - Clarify task scope (ask questions, understand requirements) ║
93256
+ ║ - Read package.json/tsconfig.json for structure (metadata only) ║
93257
+ ║ - Decompose into subtasks (swarm_plan_prompt, validate_decomposition) ║
93258
+ ║ - Spawn workers (swarm_spawn_subtask, Task(subagent_type="worker")) ║
93259
+ ║ - Monitor progress (swarmmail_inbox, swarm_status) ║
93260
+ ║ - Review completed work (swarm_review, swarm_review_feedback) ║
93261
+ ║ - Verify final state (check all workers completed, hive_sync) ║
93262
+ ║ ║
93263
+ ╚═══════════════════════════════════════════════════════════════════════════╝
93264
+ \`\`\`
93265
+
93266
+ **Examples of Violations:**
93267
+
93268
+ ❌ **WRONG** - Coordinator reading implementation:
93269
+ \`\`\`
93270
+ read("src/auth/login.ts") // NO - spawn worker to analyze
93271
+ glob("src/components/**/*.tsx") // NO - spawn worker to inventory
93272
+ grep(pattern="export", include="*.ts") // NO - spawn worker to search
93273
+ \`\`\`
93274
+
93275
+ ❌ **WRONG** - Coordinator editing code:
93276
+ \`\`\`
93277
+ edit("src/types.ts", ...) // NO - spawn worker to fix
93278
+ write("src/new.ts", ...) // NO - spawn worker to create
93279
+ \`\`\`
93280
+
93281
+ ❌ **WRONG** - Coordinator running tests:
93282
+ \`\`\`
93283
+ bash("bun test src/auth.test.ts") // NO - worker runs tests
93284
+ \`\`\`
93285
+
93286
+ ❌ **WRONG** - Coordinator reserving files:
93287
+ \`\`\`
93288
+ swarmmail_reserve(paths=["src/auth.ts"]) // NO - worker reserves their own files
93289
+ swarm_spawn_subtask(bead_id="...", files=["src/auth.ts"])
93290
+ \`\`\`
93291
+
93292
+ ✅ **CORRECT** - Coordinator spawning worker:
93293
+ \`\`\`
93294
+ // Coordinator delegates ALL work
93295
+ swarm_spawn_subtask(
93296
+ bead_id="fix-auth-bug",
93297
+ epic_id="epic-123",
93298
+ subtask_title="Fix null check in login handler",
93299
+ files=["src/auth/login.ts", "src/auth/login.test.ts"],
93300
+ shared_context="Bug: login fails when username is null"
93301
+ )
93302
+ Task(subagent_type="swarm-worker", prompt="<from above>")
93303
+ \`\`\`
93304
+
92741
93305
  ### Why This Matters
92742
93306
 
92743
93307
  | Coordinator Work | Worker Work | Consequence of Mixing |
@@ -93822,15 +94386,15 @@ import {
93822
94386
  createHiveAdapter as createHiveAdapter4,
93823
94387
  resolvePartialId as resolvePartialId4,
93824
94388
  createDurableStreamAdapter,
93825
- createDurableStreamServer
94389
+ createDurableStreamServer,
94390
+ consolidateDatabases,
94391
+ getGlobalDbPath as getGlobalDbPath2
93826
94392
  } from "swarm-mail";
93827
94393
  import { execSync, spawn } from "child_process";
93828
94394
  import { tmpdir as tmpdir3 } from "os";
93829
94395
 
93830
94396
  // src/query-tools.ts
93831
- import { createLibSQLAdapter as createLibSQLAdapter2 } from "swarm-mail";
93832
- import { join as join7 } from "node:path";
93833
- import { homedir as homedir3 } from "node:os";
94397
+ import { createLibSQLAdapter as createLibSQLAdapter2, getGlobalDbPath } from "swarm-mail";
93834
94398
  var presetQueries = {
93835
94399
  failed_decompositions: `
93836
94400
  SELECT
@@ -93981,8 +94545,7 @@ var presetQueries = {
93981
94545
  `
93982
94546
  };
93983
94547
  function getDbPath() {
93984
- const home = homedir3();
93985
- return join7(home, ".swarm-tools", "swarm-mail.db");
94548
+ return getGlobalDbPath();
93986
94549
  }
93987
94550
  async function createDbAdapter() {
93988
94551
  const dbPath = getDbPath();
@@ -95192,8 +95755,8 @@ function detectRegressions(projectPath, threshold = 0.1) {
95192
95755
  }
95193
95756
 
95194
95757
  // src/observability-health.ts
95195
- import { homedir as homedir4 } from "node:os";
95196
- import { join as join10 } from "node:path";
95758
+ import { homedir as homedir3 } from "node:os";
95759
+ import { join as join9 } from "node:path";
95197
95760
  import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync8, statSync } from "node:fs";
95198
95761
  var EXPECTED_HOOKS = [
95199
95762
  "tool.execute.after",
@@ -95290,16 +95853,16 @@ function calculateSessionQuality(input) {
95290
95853
  };
95291
95854
  }
95292
95855
  function querySessionQuality(options2) {
95293
- const sessionsPath = join10(homedir4(), ".config", "swarm-tools", "sessions");
95856
+ const sessionsPath = join9(homedir3(), ".config", "swarm-tools", "sessions");
95294
95857
  if (!existsSync8(sessionsPath)) {
95295
95858
  return { totalSessions: 0, qualitySessions: 0 };
95296
95859
  }
95297
95860
  const since = Date.now() - options2.days * 24 * 60 * 60 * 1000;
95298
- const sessionFiles = readdirSync(sessionsPath).filter((f) => f.endsWith(".jsonl") && statSync(join10(sessionsPath, f)).mtimeMs >= since);
95861
+ const sessionFiles = readdirSync(sessionsPath).filter((f) => f.endsWith(".jsonl") && statSync(join9(sessionsPath, f)).mtimeMs >= since);
95299
95862
  let qualityCount = 0;
95300
95863
  for (const file4 of sessionFiles) {
95301
95864
  try {
95302
- const content = readFileSync8(join10(sessionsPath, file4), "utf-8");
95865
+ const content = readFileSync8(join9(sessionsPath, file4), "utf-8");
95303
95866
  const lines = content.trim().split(`
95304
95867
  `);
95305
95868
  let hasDecisions = false;
@@ -95409,6 +95972,90 @@ async function getObservabilityHealth(projectPath, options2 = {}) {
95409
95972
  };
95410
95973
  }
95411
95974
 
95975
+ // src/swarm-insights.ts
95976
+ async function getRejectionAnalytics2(swarmMail) {
95977
+ const db = await swarmMail.getDatabase();
95978
+ const query = `
95979
+ SELECT data
95980
+ FROM events
95981
+ WHERE type = 'review_feedback'
95982
+ ORDER BY timestamp DESC
95983
+ `;
95984
+ const result = await db.query(query, []);
95985
+ if (!result.rows || result.rows.length === 0) {
95986
+ return {
95987
+ totalReviews: 0,
95988
+ approved: 0,
95989
+ rejected: 0,
95990
+ approvalRate: 0,
95991
+ topReasons: []
95992
+ };
95993
+ }
95994
+ let approved = 0;
95995
+ let rejected = 0;
95996
+ const reasonCounts = new Map;
95997
+ for (const row of result.rows) {
95998
+ try {
95999
+ const data = JSON.parse(row.data);
96000
+ if (data.status === "approved") {
96001
+ approved++;
96002
+ } else if (data.status === "needs_changes") {
96003
+ rejected++;
96004
+ if (data.issues) {
96005
+ const issues = JSON.parse(data.issues);
96006
+ for (const issue4 of issues) {
96007
+ const category = categorizeRejectionReason2(issue4.issue);
96008
+ reasonCounts.set(category, (reasonCounts.get(category) || 0) + 1);
96009
+ }
96010
+ }
96011
+ }
96012
+ } catch (e) {
96013
+ continue;
96014
+ }
96015
+ }
96016
+ const totalReviews = approved + rejected;
96017
+ const approvalRate = totalReviews > 0 ? approved / totalReviews * 100 : 0;
96018
+ const topReasons = Array.from(reasonCounts.entries()).sort((a, b) => b[1] - a[1]).slice(0, 5).map(([category, count]) => ({
96019
+ category,
96020
+ count,
96021
+ percentage: rejected > 0 ? count / rejected * 100 : 0
96022
+ }));
96023
+ return {
96024
+ totalReviews,
96025
+ approved,
96026
+ rejected,
96027
+ approvalRate,
96028
+ topReasons
96029
+ };
96030
+ }
96031
+ function categorizeRejectionReason2(reason) {
96032
+ const lowerReason = reason.toLowerCase();
96033
+ if (lowerReason.includes("test") || lowerReason.includes("spec") || lowerReason.includes("coverage")) {
96034
+ return "Missing tests";
96035
+ }
96036
+ if (lowerReason.includes("type") || lowerReason.includes("undefined") || lowerReason.includes("null") || lowerReason.includes("assignable")) {
96037
+ return "Type errors";
96038
+ }
96039
+ if (lowerReason.includes("incomplete") || lowerReason.includes("missing") || lowerReason.includes("forgot") || lowerReason.includes("didn't implement")) {
96040
+ return "Incomplete implementation";
96041
+ }
96042
+ if (lowerReason.includes("wrong file") || lowerReason.includes("modified incorrect") || lowerReason.includes("shouldn't have changed")) {
96043
+ return "Wrong file modified";
96044
+ }
96045
+ if (lowerReason.includes("performance") || lowerReason.includes("slow") || lowerReason.includes("inefficient")) {
96046
+ return "Performance issue";
96047
+ }
96048
+ if (lowerReason.includes("security") || lowerReason.includes("vulnerability") || lowerReason.includes("unsafe")) {
96049
+ return "Security vulnerability";
96050
+ }
96051
+ if (lowerReason.includes("error handling") || lowerReason.includes("try/catch") || lowerReason.includes("exception")) {
96052
+ return "Missing error handling";
96053
+ }
96054
+ return "Other";
96055
+ }
96056
+ var insightsCache2 = new Map;
96057
+ var CACHE_TTL_MS2 = 5 * 60 * 1000;
96058
+
95412
96059
  // src/eval-history.ts
95413
96060
  import * as fs5 from "node:fs";
95414
96061
  import * as path6 from "node:path";
@@ -95806,7 +96453,7 @@ import {
95806
96453
  findCellsByPartialId as findCellsByPartialId3
95807
96454
  } from "swarm-mail";
95808
96455
  import { existsSync as existsSync13, readFileSync as readFileSync13 } from "node:fs";
95809
- import { join as join14 } from "node:path";
96456
+ import { join as join13 } from "node:path";
95810
96457
 
95811
96458
  // src/schemas/cell.ts
95812
96459
  init_zod3();
@@ -95884,7 +96531,8 @@ var CellTreeSchema3 = exports_external3.object({
95884
96531
  title: exports_external3.string().min(1),
95885
96532
  description: exports_external3.string().optional().default("")
95886
96533
  }),
95887
- subtasks: exports_external3.array(SubtaskSpecSchema3).min(1)
96534
+ subtasks: exports_external3.array(SubtaskSpecSchema3).min(1),
96535
+ strategy: exports_external3.enum(["file-based", "feature-based", "risk-based", "research-based"]).optional().describe("Decomposition strategy from swarm_select_strategy. If not provided, defaults to feature-based.")
95888
96536
  });
95889
96537
  var EpicCreateArgsSchema3 = exports_external3.object({
95890
96538
  epic_title: exports_external3.string().min(1),
@@ -96404,7 +97052,7 @@ class HiveError3 extends Error {
96404
97052
  }
96405
97053
  }
96406
97054
  function ensureHiveDirectory3(projectPath) {
96407
- const hiveDir = join14(projectPath, ".hive");
97055
+ const hiveDir = join13(projectPath, ".hive");
96408
97056
  if (!existsSync13(hiveDir)) {
96409
97057
  const { mkdirSync: mkdirSync7 } = __require("node:fs");
96410
97058
  mkdirSync7(hiveDir, { recursive: true });
@@ -96461,7 +97109,7 @@ async function getHiveAdapter3(projectKey) {
96461
97109
  return adapter;
96462
97110
  }
96463
97111
  async function autoMigrateFromJSONL3(adapter, projectKey) {
96464
- const jsonlPath = join14(projectKey, ".hive", "issues.jsonl");
97112
+ const jsonlPath = join13(projectKey, ".hive", "issues.jsonl");
96465
97113
  if (!existsSync13(jsonlPath)) {
96466
97114
  return;
96467
97115
  }
@@ -96475,13 +97123,13 @@ async function autoMigrateFromJSONL3(adapter, projectKey) {
96475
97123
  skipExisting: true
96476
97124
  });
96477
97125
  if (result.created > 0 || result.updated > 0) {
96478
- console.log(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
97126
+ console.error(`[hive] Auto-migrated ${result.created} cells from ${jsonlPath} (${result.skipped} skipped, ${result.errors.length} errors)`);
96479
97127
  }
96480
97128
  if (result.errors.length > 0) {
96481
- console.warn(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
97129
+ console.error(`[hive] Migration errors:`, result.errors.slice(0, 5).map((e) => `${e.cellId}: ${e.error}`));
96482
97130
  }
96483
97131
  } catch (error54) {
96484
- console.warn(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error54 instanceof Error ? error54.message : String(error54));
97132
+ console.error(`[hive] Failed to auto-migrate from ${jsonlPath}:`, error54 instanceof Error ? error54.message : String(error54));
96485
97133
  }
96486
97134
  }
96487
97135
  function formatCellForOutput3(adapterCell) {
@@ -97082,7 +97730,7 @@ var hive_sync3 = tool3({
97082
97730
  const flushResult = await withTimeout(flushManager.flush(), TIMEOUT_MS, "flush hive");
97083
97731
  const swarmMail = await getSwarmMailLibSQL12(projectKey);
97084
97732
  const db = await swarmMail.getDatabase();
97085
- const hivePath = join14(projectKey, ".hive");
97733
+ const hivePath = join13(projectKey, ".hive");
97086
97734
  let memoriesSynced = 0;
97087
97735
  try {
97088
97736
  const memoryResult = await syncMemories3(db, hivePath);
@@ -97586,8 +98234,8 @@ function formatToolAvailability2(availability) {
97586
98234
  // src/rate-limiter.ts
97587
98235
  var import_ioredis = __toESM(require_built3(), 1);
97588
98236
  import { mkdirSync as mkdirSync7, existsSync as existsSync14 } from "node:fs";
97589
- import { dirname as dirname7, join as join15 } from "node:path";
97590
- import { homedir as homedir7 } from "node:os";
98237
+ import { dirname as dirname7, join as join14 } from "node:path";
98238
+ import { homedir as homedir6 } from "node:os";
97591
98239
  var sqliteAvailable = false;
97592
98240
  var createDatabase = null;
97593
98241
  try {
@@ -97847,7 +98495,7 @@ async function createRateLimiter(options2) {
97847
98495
  const {
97848
98496
  backend,
97849
98497
  redisUrl = process.env.OPENCODE_RATE_LIMIT_REDIS_URL || "redis://localhost:6379",
97850
- sqlitePath = process.env.OPENCODE_RATE_LIMIT_SQLITE_PATH || join15(homedir7(), ".config", "opencode", "rate-limits.db")
98498
+ sqlitePath = process.env.OPENCODE_RATE_LIMIT_SQLITE_PATH || join14(homedir6(), ".config", "opencode", "rate-limits.db")
97851
98499
  } = options2 || {};
97852
98500
  if (backend === "memory") {
97853
98501
  return new InMemoryRateLimiter;
@@ -97913,7 +98561,7 @@ import {
97913
98561
  writeFileSync as writeFileSync5,
97914
98562
  unlinkSync
97915
98563
  } from "fs";
97916
- import { join as join16 } from "path";
98564
+ import { join as join15 } from "path";
97917
98565
  import { tmpdir } from "os";
97918
98566
  var AGENT_MAIL_URL = "http://127.0.0.1:8765";
97919
98567
  var DEFAULT_TTL_SECONDS = 3600;
@@ -97934,10 +98582,10 @@ var RECOVERY_CONFIG = {
97934
98582
  restartCooldownMs: 1e4,
97935
98583
  enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
97936
98584
  };
97937
- var SESSION_STATE_DIR = process.env.SWARM_STATE_DIR || join16(tmpdir(), "swarm-sessions");
98585
+ var SESSION_STATE_DIR = process.env.SWARM_STATE_DIR || join15(tmpdir(), "swarm-sessions");
97938
98586
  function getSessionStatePath(sessionID) {
97939
98587
  const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
97940
- return join16(SESSION_STATE_DIR, `${safeID}.json`);
98588
+ return join15(SESSION_STATE_DIR, `${safeID}.json`);
97941
98589
  }
97942
98590
  function loadSessionState(sessionID) {
97943
98591
  const path4 = getSessionStatePath(sessionID);
@@ -98647,17 +99295,17 @@ import {
98647
99295
  writeFileSync as writeFileSync6,
98648
99296
  unlinkSync as unlinkSync2
98649
99297
  } from "node:fs";
98650
- import { join as join17 } from "node:path";
99298
+ import { join as join16 } from "node:path";
98651
99299
  import { tmpdir as tmpdir2 } from "node:os";
98652
99300
  var MAX_INBOX_LIMIT2 = 5;
98653
99301
  var swarmMailProjectDirectory = null;
98654
99302
  function getSwarmMailProjectDirectory() {
98655
99303
  return swarmMailProjectDirectory ?? undefined;
98656
99304
  }
98657
- var SESSION_STATE_DIR2 = process.env.SWARM_STATE_DIR || join17(tmpdir2(), "swarm-sessions");
99305
+ var SESSION_STATE_DIR2 = process.env.SWARM_STATE_DIR || join16(tmpdir2(), "swarm-sessions");
98658
99306
  function getSessionStatePath2(sessionID) {
98659
99307
  const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
98660
- return join17(SESSION_STATE_DIR2, `${safeID}.json`);
99308
+ return join16(SESSION_STATE_DIR2, `${safeID}.json`);
98661
99309
  }
98662
99310
  function loadSessionState2(sessionID) {
98663
99311
  const path4 = getSessionStatePath2(sessionID);
@@ -101804,12 +102452,12 @@ init_skills2();
101804
102452
  // src/swarm-worktree.ts
101805
102453
  init_dist2();
101806
102454
  init_zod3();
101807
- import { join as join19 } from "node:path";
102455
+ import { join as join18 } from "node:path";
101808
102456
  import { existsSync as existsSync17 } from "node:fs";
101809
102457
  var WORKTREE_DIR2 = ".swarm/worktrees";
101810
102458
  function getWorktreePath2(projectPath, taskId) {
101811
102459
  const safeTaskId = taskId.replace(/[^a-zA-Z0-9.-]/g, "_");
101812
- return join19(projectPath, WORKTREE_DIR2, safeTaskId);
102460
+ return join18(projectPath, WORKTREE_DIR2, safeTaskId);
101813
102461
  }
101814
102462
  function parseTaskIdFromPath2(worktreePath) {
101815
102463
  const parts2 = worktreePath.split("/");
@@ -101843,7 +102491,7 @@ async function getWorktreeCommits2(worktreePath, startCommit) {
101843
102491
  `).filter((c) => c.length > 0);
101844
102492
  }
101845
102493
  async function ensureWorktreeDir2(projectPath) {
101846
- const worktreeDir = join19(projectPath, WORKTREE_DIR2);
102494
+ const worktreeDir = join18(projectPath, WORKTREE_DIR2);
101847
102495
  await Bun.$`mkdir -p ${worktreeDir}`.quiet().nothrow();
101848
102496
  }
101849
102497
  var swarm_worktree_create2 = tool3({
@@ -101980,7 +102628,7 @@ var swarm_worktree_cleanup2 = tool3({
101980
102628
  return JSON.stringify(result3, null, 2);
101981
102629
  }
101982
102630
  const output = listResult.stdout.toString();
101983
- const worktreeDir = join19(args2.project_path, WORKTREE_DIR2);
102631
+ const worktreeDir = join18(args2.project_path, WORKTREE_DIR2);
101984
102632
  const worktrees = output.split(`
101985
102633
 
101986
102634
  `).filter((block) => block.includes(worktreeDir)).map((block) => {
@@ -102045,7 +102693,7 @@ var swarm_worktree_list2 = tool3({
102045
102693
  }, null, 2);
102046
102694
  }
102047
102695
  const output = listResult.stdout.toString();
102048
- const worktreeDir = join19(args2.project_path, WORKTREE_DIR2);
102696
+ const worktreeDir = join18(args2.project_path, WORKTREE_DIR2);
102049
102697
  const worktrees = [];
102050
102698
  const blocks = output.split(`
102051
102699
 
@@ -103106,7 +103754,7 @@ var swarm_complete2 = tool3({
103106
103754
  files_touched: tool3.schema.array(tool3.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
103107
103755
  skip_verification: tool3.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
103108
103756
  planned_files: tool3.schema.array(tool3.schema.string()).optional().describe("Files that were originally planned to be modified"),
103109
- start_time: tool3.schema.number().optional().describe("Task start timestamp (Unix ms) for duration calculation"),
103757
+ start_time: tool3.schema.number().describe("Task start timestamp (Unix ms) for duration calculation - REQUIRED for accurate analytics"),
103110
103758
  error_count: tool3.schema.number().optional().describe("Number of errors encountered during task"),
103111
103759
  retry_count: tool3.schema.number().optional().describe("Number of retry attempts during task"),
103112
103760
  skip_review: tool3.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
@@ -103310,7 +103958,7 @@ This will be recorded as a negative learning signal.`;
103310
103958
  syncError = error54 instanceof Error ? error54.message : String(error54);
103311
103959
  console.warn(`[swarm_complete] Auto-sync failed (non-fatal): ${syncError}`);
103312
103960
  }
103313
- const completionDurationMs = args2.start_time ? Date.now() - args2.start_time : 0;
103961
+ const completionDurationMs = Date.now() - args2.start_time;
103314
103962
  const eventEpicId = cell.parent_id || (args2.bead_id.includes(".") ? args2.bead_id.split(".")[0] : args2.bead_id);
103315
103963
  try {
103316
103964
  const event = createEvent8("subtask_outcome", {
@@ -103366,12 +104014,18 @@ This will be recorded as a negative learning signal.`;
103366
104014
  memoryError = `Failed to store memory: ${error54 instanceof Error ? error54.message : String(error54)}`;
103367
104015
  console.warn(`[swarm_complete] ${memoryError}`);
103368
104016
  }
104017
+ let reservationsReleased = false;
104018
+ let reservationsReleasedCount = 0;
104019
+ let reservationsReleaseError;
103369
104020
  try {
103370
- await releaseSwarmFiles3({
104021
+ const releaseResult = await releaseSwarmFiles3({
103371
104022
  projectPath: args2.project_key,
103372
104023
  agentName: args2.agent_name
103373
104024
  });
104025
+ reservationsReleased = true;
104026
+ reservationsReleasedCount = releaseResult.released;
103374
104027
  } catch (error54) {
104028
+ reservationsReleaseError = error54 instanceof Error ? error54.message : String(error54);
103375
104029
  console.warn(`[swarm] Failed to release file reservations for ${args2.agent_name}:`, error54);
103376
104030
  }
103377
104031
  const epicId2 = args2.bead_id.includes(".") ? args2.bead_id.split(".")[0] : args2.bead_id;
@@ -103407,7 +104061,9 @@ This will be recorded as a negative learning signal.`;
103407
104061
  success: true,
103408
104062
  bead_id: args2.bead_id,
103409
104063
  closed: true,
103410
- reservations_released: true,
104064
+ reservations_released: reservationsReleased,
104065
+ reservations_released_count: reservationsReleasedCount,
104066
+ reservations_release_error: reservationsReleaseError,
103411
104067
  synced: syncSuccess,
103412
104068
  sync_error: syncError,
103413
104069
  message_sent: messageSent,
@@ -105238,7 +105894,7 @@ var dedupeWith2 = /* @__PURE__ */ dual2(2, (self, isEquivalent) => {
105238
105894
  return [];
105239
105895
  });
105240
105896
  var dedupe2 = (self) => dedupeWith2(self, equivalence2());
105241
- var join20 = /* @__PURE__ */ dual2(2, (self, sep4) => fromIterable8(self).join(sep4));
105897
+ var join19 = /* @__PURE__ */ dual2(2, (self, sep4) => fromIterable8(self).join(sep4));
105242
105898
 
105243
105899
  // ../../node_modules/.bun/effect@3.19.12/node_modules/effect/dist/esm/Number.js
105244
105900
  var Order3 = number10;
@@ -109789,7 +110445,7 @@ var InvalidData2 = (path4, message, options2 = {
109789
110445
  Object.defineProperty(error54, "toString", {
109790
110446
  enumerable: false,
109791
110447
  value() {
109792
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110448
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109793
110449
  return `(Invalid data at ${path10}: "${this.message}")`;
109794
110450
  }
109795
110451
  });
@@ -109805,7 +110461,7 @@ var MissingData2 = (path4, message, options2 = {
109805
110461
  Object.defineProperty(error54, "toString", {
109806
110462
  enumerable: false,
109807
110463
  value() {
109808
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110464
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109809
110465
  return `(Missing data at ${path10}: "${this.message}")`;
109810
110466
  }
109811
110467
  });
@@ -109822,7 +110478,7 @@ var SourceUnavailable2 = (path4, message, cause3, options2 = {
109822
110478
  Object.defineProperty(error54, "toString", {
109823
110479
  enumerable: false,
109824
110480
  value() {
109825
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110481
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109826
110482
  return `(Source unavailable at ${path10}: "${this.message}")`;
109827
110483
  }
109828
110484
  });
@@ -109838,7 +110494,7 @@ var Unsupported2 = (path4, message, options2 = {
109838
110494
  Object.defineProperty(error54, "toString", {
109839
110495
  enumerable: false,
109840
110496
  value() {
109841
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110497
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109842
110498
  return `(Unsupported operation at ${path10}: "${this.message}")`;
109843
110499
  }
109844
110500
  });
@@ -109958,7 +110614,7 @@ var fromEnv2 = (options2) => {
109958
110614
  pathDelim: "_",
109959
110615
  seqDelim: ","
109960
110616
  }, options2);
109961
- const makePathString = (path4) => pipe4(path4, join20(pathDelim));
110617
+ const makePathString = (path4) => pipe4(path4, join19(pathDelim));
109962
110618
  const unmakePathString = (pathString) => pathString.split(pathDelim);
109963
110619
  const getEnv = () => typeof process !== "undefined" && ("env" in process) && typeof process.env === "object" ? process.env : {};
109964
110620
  const load = (path4, primitive, split = true) => {
@@ -110082,7 +110738,7 @@ var fromFlatLoop2 = (flat, prefix, config4, split) => {
110082
110738
  return fail5(right4.left);
110083
110739
  }
110084
110740
  if (isRight5(left4) && isRight5(right4)) {
110085
- const path4 = pipe4(prefix, join20("."));
110741
+ const path4 = pipe4(prefix, join19("."));
110086
110742
  const fail6 = fromFlatLoopFail2(prefix, path4);
110087
110743
  const [lefts, rights] = extend4(fail6, fail6, pipe4(left4.right, map8(right5)), pipe4(right4.right, map8(right5)));
110088
110744
  return pipe4(lefts, zip3(rights), forEachSequential2(([left6, right6]) => pipe4(zip5(left6, right6), map18(([left7, right7]) => op.zip(left7, right7)))));
@@ -112256,11 +112912,11 @@ var interruptAllAs2 = /* @__PURE__ */ dual2(2, /* @__PURE__ */ fnUntraced3(funct
112256
112912
  }
112257
112913
  }));
112258
112914
  var interruptAsFork2 = /* @__PURE__ */ dual2(2, (self, fiberId4) => self.interruptAsFork(fiberId4));
112259
- var join21 = (self) => zipLeft3(flatten11(self.await), self.inheritAll);
112915
+ var join20 = (self) => zipLeft3(flatten11(self.await), self.inheritAll);
112260
112916
  var _never4 = {
112261
112917
  ...CommitPrototype3,
112262
112918
  commit() {
112263
- return join21(this);
112919
+ return join20(this);
112264
112920
  },
112265
112921
  ...fiberProto2,
112266
112922
  id: () => none14,
@@ -113507,7 +114163,7 @@ class FiberRuntime2 extends Class4 {
113507
114163
  this.refreshRefCache();
113508
114164
  }
113509
114165
  commit() {
113510
- return join21(this);
114166
+ return join20(this);
113511
114167
  }
113512
114168
  id() {
113513
114169
  return this._fiberId;
@@ -114522,7 +115178,7 @@ var forEachConcurrentDiscard2 = (self, f, batching, processAll, n) => uninterrup
114522
115178
  next();
114523
115179
  }
114524
115180
  }));
114525
- return asVoid4(onExit4(flatten11(restore(join21(processingFiber))), exitMatch2({
115181
+ return asVoid4(onExit4(flatten11(restore(join20(processingFiber))), exitMatch2({
114526
115182
  onFailure: (cause4) => {
114527
115183
  onInterruptSignal();
114528
115184
  const target2 = residual.length + 1;
@@ -114844,7 +115500,7 @@ var fiberAll2 = (fibers) => {
114844
115500
  const _fiberAll = {
114845
115501
  ...CommitPrototype5,
114846
115502
  commit() {
114847
- return join21(this);
115503
+ return join20(this);
114848
115504
  },
114849
115505
  [FiberTypeId2]: fiberVariance5,
114850
115506
  id: () => fromIterable8(fibers).reduce((id, fiber) => combine12(id, fiber.id()), none14),
@@ -114897,14 +115553,14 @@ var raceWith3 = /* @__PURE__ */ dual2(3, (self, other, options2) => raceFibersWi
114897
115553
  }
114898
115554
  })
114899
115555
  }));
114900
- var disconnect3 = (self) => uninterruptibleMask4((restore) => fiberIdWith3((fiberId4) => flatMap15(forkDaemon3(restore(self)), (fiber) => pipe4(restore(join21(fiber)), onInterrupt3(() => pipe4(fiber, interruptAsFork2(fiberId4)))))));
115556
+ var disconnect3 = (self) => uninterruptibleMask4((restore) => fiberIdWith3((fiberId4) => flatMap15(forkDaemon3(restore(self)), (fiber) => pipe4(restore(join20(fiber)), onInterrupt3(() => pipe4(fiber, interruptAsFork2(fiberId4)))))));
114901
115557
  var race3 = /* @__PURE__ */ dual2(2, (self, that) => fiberIdWith3((parentFiberId) => raceWith3(self, that, {
114902
115558
  onSelfDone: (exit4, right4) => exitMatchEffect2(exit4, {
114903
- onFailure: (cause4) => pipe4(join21(right4), mapErrorCause3((cause22) => parallel4(cause4, cause22))),
115559
+ onFailure: (cause4) => pipe4(join20(right4), mapErrorCause3((cause22) => parallel4(cause4, cause22))),
114904
115560
  onSuccess: (value) => pipe4(right4, interruptAsFiber2(parentFiberId), as3(value))
114905
115561
  }),
114906
115562
  onOtherDone: (exit4, left4) => exitMatchEffect2(exit4, {
114907
- onFailure: (cause4) => pipe4(join21(left4), mapErrorCause3((cause22) => parallel4(cause22, cause4))),
115563
+ onFailure: (cause4) => pipe4(join20(left4), mapErrorCause3((cause22) => parallel4(cause22, cause4))),
114908
115564
  onSuccess: (value) => pipe4(left4, interruptAsFiber2(parentFiberId), as3(value))
114909
115565
  })
114910
115566
  })));
@@ -116058,8 +116714,8 @@ var forkIn3 = /* @__PURE__ */ dual2(2, (self, scope4) => withFiberRuntime3((pare
116058
116714
  return succeed5(fiber);
116059
116715
  }));
116060
116716
  var forkScoped3 = (self) => scopeWith3((scope4) => forkIn3(self, scope4));
116061
- var fromFiber3 = (fiber) => join21(fiber);
116062
- var fromFiberEffect3 = (fiber) => suspend4(() => flatMap15(fiber, join21));
116717
+ var fromFiber3 = (fiber) => join20(fiber);
116718
+ var fromFiberEffect3 = (fiber) => suspend4(() => flatMap15(fiber, join20));
116063
116719
  var memoKeySymbol2 = /* @__PURE__ */ Symbol.for("effect/Effect/memoizeFunction.key");
116064
116720
 
116065
116721
  class Key2 {
@@ -117643,14 +118299,14 @@ async function maybeAutoMigrate2(db) {
117643
118299
  if (memoryCount > 0) {
117644
118300
  return;
117645
118301
  }
117646
- console.log("[memory] Legacy database detected, starting auto-migration...");
118302
+ console.error("[memory] Legacy database detected, starting auto-migration...");
117647
118303
  const result = await migrateLegacyMemories2({
117648
118304
  targetDb: db,
117649
118305
  dryRun: false,
117650
- onProgress: console.log
118306
+ onProgress: (msg) => console.error(msg)
117651
118307
  });
117652
118308
  if (result.migrated > 0) {
117653
- console.log(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
118309
+ console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
117654
118310
  }
117655
118311
  if (result.failed > 0) {
117656
118312
  console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
@@ -117719,6 +118375,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117719
118375
  async find(args3) {
117720
118376
  const limit = args3.limit ?? 10;
117721
118377
  let results;
118378
+ let usedFallback = false;
117722
118379
  if (args3.fts) {
117723
118380
  results = await store.ftsSearch(args3.query, {
117724
118381
  limit,
@@ -117729,14 +118386,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117729
118386
  const ollama = yield* Ollama2;
117730
118387
  return yield* ollama.embed(args3.query);
117731
118388
  });
117732
- const queryEmbedding = await exports_Effect2.runPromise(program.pipe(exports_Effect2.provide(ollamaLayer)));
117733
- results = await store.search(queryEmbedding, {
117734
- limit,
117735
- threshold: 0.3,
117736
- collection: args3.collection
117737
- });
118389
+ try {
118390
+ const queryEmbedding = await exports_Effect2.runPromise(program.pipe(exports_Effect2.provide(ollamaLayer)));
118391
+ results = await store.search(queryEmbedding, {
118392
+ limit,
118393
+ threshold: 0.3,
118394
+ collection: args3.collection
118395
+ });
118396
+ } catch (e) {
118397
+ console.warn("[hivemind] Ollama unavailable, falling back to full-text search");
118398
+ usedFallback = true;
118399
+ results = await store.ftsSearch(args3.query, {
118400
+ limit,
118401
+ collection: args3.collection
118402
+ });
118403
+ }
117738
118404
  }
117739
- return {
118405
+ const response = {
117740
118406
  results: results.map((r) => ({
117741
118407
  id: r.memory.id,
117742
118408
  content: args3.expand ? r.memory.content : truncateContent(r.memory.content),
@@ -117747,6 +118413,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117747
118413
  })),
117748
118414
  count: results.length
117749
118415
  };
118416
+ if (usedFallback) {
118417
+ response.fallback_used = true;
118418
+ }
118419
+ return response;
117750
118420
  },
117751
118421
  async get(args3) {
117752
118422
  return store.get(args3.id);
@@ -119285,7 +119955,7 @@ var promptTools2 = {
119285
119955
  init_dist2();
119286
119956
  import { existsSync as existsSync18 } from "node:fs";
119287
119957
  import { readFile as readFile3 } from "node:fs/promises";
119288
- import { join as join23 } from "node:path";
119958
+ import { join as join21 } from "node:path";
119289
119959
 
119290
119960
  // ../../node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/index.js
119291
119961
  var composer = require_composer();
@@ -119463,20 +120133,20 @@ async function parsePackageJson(packageJsonPath, packages) {
119463
120133
  }
119464
120134
  }
119465
120135
  async function getInstalledVersions(projectPath, packages, checkUpgrades = false) {
119466
- const npmLock = join23(projectPath, "package-lock.json");
120136
+ const npmLock = join21(projectPath, "package-lock.json");
119467
120137
  let versions2 = [];
119468
120138
  if (existsSync18(npmLock)) {
119469
120139
  versions2 = await parseNpmLockfile(npmLock, packages);
119470
120140
  } else {
119471
- const pnpmLock = join23(projectPath, "pnpm-lock.yaml");
120141
+ const pnpmLock = join21(projectPath, "pnpm-lock.yaml");
119472
120142
  if (existsSync18(pnpmLock)) {
119473
120143
  versions2 = await parsePnpmLockfile(pnpmLock, packages);
119474
120144
  } else {
119475
- const yarnLock = join23(projectPath, "yarn.lock");
120145
+ const yarnLock = join21(projectPath, "yarn.lock");
119476
120146
  if (existsSync18(yarnLock)) {
119477
120147
  versions2 = await parseYarnLockfile(yarnLock, packages);
119478
120148
  } else {
119479
- const packageJson = join23(projectPath, "package.json");
120149
+ const packageJson = join21(projectPath, "package.json");
119480
120150
  if (existsSync18(packageJson)) {
119481
120151
  versions2 = await parsePackageJson(packageJson, packages);
119482
120152
  }
@@ -120628,7 +121298,7 @@ import {
120628
121298
  } from "swarm-mail";
120629
121299
  import * as os5 from "node:os";
120630
121300
  import * as path10 from "node:path";
120631
- import { join as join26 } from "node:path";
121301
+ import { join as join25 } from "node:path";
120632
121302
  var cachedAdapter3 = null;
120633
121303
  var cachedIndexer = null;
120634
121304
  var cachedProjectPath3 = null;
@@ -120835,7 +121505,7 @@ var hivemind_sync = tool3({
120835
121505
  const projectPath = cachedProjectPath3 || process.cwd();
120836
121506
  const swarmMail = await getSwarmMailLibSQL16(projectPath);
120837
121507
  const dbAdapter = await swarmMail.getDatabase();
120838
- const hiveDir = join26(projectPath, ".hive");
121508
+ const hiveDir = join25(projectPath, ".hive");
120839
121509
  const result = await syncMemories4(dbAdapter, hiveDir);
120840
121510
  await emitEvent("memories_synced", {
120841
121511
  imported: result.imported.created,
@@ -121370,9 +122040,9 @@ import { checkSwarmHealth as checkSwarmHealth4 } from "swarm-mail";
121370
122040
  // src/logger.ts
121371
122041
  var import_pino = __toESM(require_pino(), 1);
121372
122042
  import { mkdirSync as mkdirSync10, existsSync as existsSync19 } from "node:fs";
121373
- import { join as join27 } from "node:path";
121374
- import { homedir as homedir9 } from "node:os";
121375
- var DEFAULT_LOG_DIR = join27(homedir9(), ".config", "swarm-tools", "logs");
122043
+ import { join as join26 } from "node:path";
122044
+ import { homedir as homedir8 } from "node:os";
122045
+ var DEFAULT_LOG_DIR = join26(homedir8(), ".config", "swarm-tools", "logs");
121376
122046
  function ensureLogDir(logDir) {
121377
122047
  if (!existsSync19(logDir)) {
121378
122048
  mkdirSync10(logDir, { recursive: true });
@@ -121391,7 +122061,7 @@ function getLogger(logDir = DEFAULT_LOG_DIR) {
121391
122061
  let logger;
121392
122062
  if (process.env.SWARM_LOG_FILE === "1") {
121393
122063
  ensureLogDir(logDir);
121394
- const logPath = join27(logDir, "swarm.log");
122064
+ const logPath = join26(logDir, "swarm.log");
121395
122065
  logger = import_pino.default(baseConfig, import_pino.default.destination({ dest: logPath, sync: false }));
121396
122066
  } else {
121397
122067
  logger = import_pino.default(baseConfig);
@@ -121795,7 +122465,7 @@ var allTools = {
121795
122465
 
121796
122466
  // bin/swarm.ts
121797
122467
  var __dirname2 = dirname9(fileURLToPath3(import.meta.url));
121798
- var pkgPath = join29(__dirname2, "..", "..", "package.json");
122468
+ var pkgPath = existsSync20(join28(__dirname2, "..", "package.json")) ? join28(__dirname2, "..", "package.json") : join28(__dirname2, "..", "..", "package.json");
121799
122469
  var pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
121800
122470
  var VERSION = pkg.version;
121801
122471
  var BEE = `
@@ -122134,8 +122804,8 @@ function copyDirRecursiveSync(srcDir, destDir) {
122134
122804
  mkdirSync11(destDir, { recursive: true });
122135
122805
  const entries2 = readdirSync2(srcDir, { withFileTypes: true });
122136
122806
  for (const entry of entries2) {
122137
- const srcPath = join29(srcDir, entry.name);
122138
- const destPath = join29(destDir, entry.name);
122807
+ const srcPath = join28(srcDir, entry.name);
122808
+ const destPath = join28(destDir, entry.name);
122139
122809
  if (entry.isDirectory()) {
122140
122810
  copyDirRecursiveSync(srcPath, destPath);
122141
122811
  continue;
@@ -122149,7 +122819,7 @@ function copyDirRecursiveSync(srcDir, destDir) {
122149
122819
  }
122150
122820
  }
122151
122821
  function writeBundledSkillMarker(skillDir, info) {
122152
- const markerPath = join29(skillDir, BUNDLED_SKILL_MARKER_FILENAME);
122822
+ const markerPath = join28(skillDir, BUNDLED_SKILL_MARKER_FILENAME);
122153
122823
  writeFileSync7(markerPath, JSON.stringify({
122154
122824
  managed_by: "opencode-swarm-plugin",
122155
122825
  version: info.version,
@@ -122166,9 +122836,9 @@ function syncBundledSkillsToGlobal({
122166
122836
  const updated = [];
122167
122837
  const skipped = [];
122168
122838
  for (const name of bundledSkills) {
122169
- const srcSkillDir = join29(bundledSkillsPath, name);
122170
- const destSkillDir = join29(globalSkillsPath, name);
122171
- const markerPath = join29(destSkillDir, BUNDLED_SKILL_MARKER_FILENAME);
122839
+ const srcSkillDir = join28(bundledSkillsPath, name);
122840
+ const destSkillDir = join28(globalSkillsPath, name);
122841
+ const markerPath = join28(destSkillDir, BUNDLED_SKILL_MARKER_FILENAME);
122172
122842
  if (!existsSync20(destSkillDir)) {
122173
122843
  copyDirRecursiveSync(srcSkillDir, destSkillDir);
122174
122844
  writeBundledSkillMarker(destSkillDir, { version: version4 });
@@ -122197,7 +122867,7 @@ function backupFileWithTimestamp(filePath) {
122197
122867
  const dir = dirname9(filePath);
122198
122868
  const base = basename3(filePath);
122199
122869
  const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
122200
- const backupPath = join29(dir, `${base}.swarm-backup-${timestamp}`);
122870
+ const backupPath = join28(dir, `${base}.swarm-backup-${timestamp}`);
122201
122871
  copyFileSync(filePath, backupPath);
122202
122872
  return backupPath;
122203
122873
  } catch {
@@ -122584,7 +123254,7 @@ function updateAgentsMdFile({
122584
123254
  return { changed: true, backupPath, changes };
122585
123255
  }
122586
123256
  function getPluginWrapper() {
122587
- const templatePath = join29(__dirname2, "..", "examples", "plugin-wrapper-template.ts");
123257
+ const templatePath = join28(__dirname2, "..", "examples", "plugin-wrapper-template.ts");
122588
123258
  try {
122589
123259
  return readFileSync16(templatePath, "utf-8");
122590
123260
  } catch (error54) {
@@ -123019,9 +123689,9 @@ async function doctor(debug = false) {
123019
123689
  const requiredMissing = required4.filter((r) => !r.available);
123020
123690
  const optionalMissing = optional4.filter((r) => !r.available);
123021
123691
  p.log.step("Skills:");
123022
- const configDir = join29(homedir11(), ".config", "opencode");
123023
- const globalSkillsPath = join29(configDir, "skills");
123024
- const bundledSkillsPath = join29(__dirname2, "..", "global-skills");
123692
+ const configDir = join28(homedir10(), ".config", "opencode");
123693
+ const globalSkillsPath = join28(configDir, "skills");
123694
+ const bundledSkillsPath = join28(__dirname2, "..", "global-skills");
123025
123695
  if (existsSync20(globalSkillsPath)) {
123026
123696
  try {
123027
123697
  const { readdirSync: readdirSync3 } = __require("fs");
@@ -123095,9 +123765,9 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123095
123765
  p.log.success(`Bun v${bunCheck.version} detected`);
123096
123766
  const cwd = process.cwd();
123097
123767
  const tempDirName = getLibSQLProjectTempDirName(cwd);
123098
- const tempDir = join29(tmpdir3(), tempDirName);
123099
- const pglitePath = join29(tempDir, "streams");
123100
- const libsqlPath = join29(tempDir, "streams.db");
123768
+ const tempDir = join28(tmpdir3(), tempDirName);
123769
+ const pglitePath = join28(tempDir, "streams");
123770
+ const libsqlPath = join28(tempDir, "streams.db");
123101
123771
  if (pgliteExists(pglitePath)) {
123102
123772
  const migrateSpinner = p.spinner();
123103
123773
  migrateSpinner.start("Migrating...");
@@ -123124,19 +123794,19 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123124
123794
  }
123125
123795
  let isReinstall = false;
123126
123796
  p.log.step("Checking existing configuration...");
123127
- const configDir = join29(homedir11(), ".config", "opencode");
123128
- const pluginDir = join29(configDir, "plugin");
123129
- const commandDir = join29(configDir, "command");
123130
- const agentDir = join29(configDir, "agent");
123131
- const pluginPath = join29(pluginDir, "swarm.ts");
123132
- const commandPath = join29(commandDir, "swarm.md");
123133
- const plannerAgentPath = join29(agentDir, "swarm-planner.md");
123134
- const workerAgentPath = join29(agentDir, "swarm-worker.md");
123135
- const researcherAgentPath = join29(agentDir, "swarm-researcher.md");
123136
- const swarmAgentDir = join29(agentDir, "swarm");
123137
- const legacyPlannerPath = join29(swarmAgentDir, "planner.md");
123138
- const legacyWorkerPath = join29(swarmAgentDir, "worker.md");
123139
- const legacyResearcherPath = join29(swarmAgentDir, "researcher.md");
123797
+ const configDir = join28(homedir10(), ".config", "opencode");
123798
+ const pluginDir = join28(configDir, "plugin");
123799
+ const commandDir = join28(configDir, "command");
123800
+ const agentDir = join28(configDir, "agent");
123801
+ const pluginPath = join28(pluginDir, "swarm.ts");
123802
+ const commandPath = join28(commandDir, "swarm.md");
123803
+ const plannerAgentPath = join28(agentDir, "swarm-planner.md");
123804
+ const workerAgentPath = join28(agentDir, "swarm-worker.md");
123805
+ const researcherAgentPath = join28(agentDir, "swarm-researcher.md");
123806
+ const swarmAgentDir = join28(agentDir, "swarm");
123807
+ const legacyPlannerPath = join28(swarmAgentDir, "planner.md");
123808
+ const legacyWorkerPath = join28(swarmAgentDir, "worker.md");
123809
+ const legacyResearcherPath = join28(swarmAgentDir, "researcher.md");
123140
123810
  const existingFiles = [
123141
123811
  pluginPath,
123142
123812
  commandPath,
@@ -123370,7 +124040,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123370
124040
  p.log.message(dim(" No legacy .beads directory found"));
123371
124041
  }
123372
124042
  p.log.step("Checking for legacy MCP servers...");
123373
- const opencodeConfigPath = join29(configDir, "config.json");
124043
+ const opencodeConfigPath = join28(configDir, "config.json");
123374
124044
  if (existsSync20(opencodeConfigPath)) {
123375
124045
  try {
123376
124046
  const opencodeConfig = JSON.parse(readFileSync16(opencodeConfigPath, "utf-8"));
@@ -123402,6 +124072,66 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123402
124072
  } else {
123403
124073
  p.log.message(dim(" No OpenCode config found (skipping MCP check)"));
123404
124074
  }
124075
+ p.log.step("Checking for stray databases...");
124076
+ const globalDbPath = getGlobalDbPath2();
124077
+ try {
124078
+ const report = await consolidateDatabases(cwd, globalDbPath, {
124079
+ yes: nonInteractive,
124080
+ interactive: !nonInteractive
124081
+ });
124082
+ if (report.straysFound > 0) {
124083
+ if (report.totalRowsMigrated > 0) {
124084
+ p.log.success(`Migrated ${report.totalRowsMigrated} records from ${report.straysMigrated} stray database(s)`);
124085
+ for (const migration of report.migrations) {
124086
+ const { migrated, skipped } = migration.result;
124087
+ if (migrated > 0 || skipped > 0) {
124088
+ p.log.message(dim(` ${migration.path}: ${migrated} migrated, ${skipped} skipped`));
124089
+ }
124090
+ }
124091
+ } else {
124092
+ p.log.message(dim(" All data already in global database (no migration needed)"));
124093
+ }
124094
+ } else {
124095
+ p.log.message(dim(" No stray databases found"));
124096
+ }
124097
+ if (report.errors.length > 0) {
124098
+ p.log.warn(`${report.errors.length} error(s) during consolidation`);
124099
+ for (const error54 of report.errors) {
124100
+ p.log.message(dim(` ${error54}`));
124101
+ }
124102
+ }
124103
+ } catch (error54) {
124104
+ p.log.warn("Database consolidation check failed");
124105
+ if (error54 instanceof Error) {
124106
+ p.log.message(dim(` ${error54.message}`));
124107
+ }
124108
+ }
124109
+ p.log.step("Running database integrity check...");
124110
+ try {
124111
+ const repairResult = await runDbRepair({ dryRun: false });
124112
+ if (repairResult.totalCleaned === 0) {
124113
+ p.log.success("Database integrity verified - no issues found");
124114
+ } else {
124115
+ p.log.success(`Cleaned ${repairResult.totalCleaned} orphaned/invalid records`);
124116
+ if (repairResult.nullBeads > 0) {
124117
+ p.log.message(dim(` - ${repairResult.nullBeads} beads with NULL IDs`));
124118
+ }
124119
+ if (repairResult.orphanedRecipients > 0) {
124120
+ p.log.message(dim(` - ${repairResult.orphanedRecipients} orphaned message recipients`));
124121
+ }
124122
+ if (repairResult.messagesWithoutRecipients > 0) {
124123
+ p.log.message(dim(` - ${repairResult.messagesWithoutRecipients} messages without recipients`));
124124
+ }
124125
+ if (repairResult.expiredReservations > 0) {
124126
+ p.log.message(dim(` - ${repairResult.expiredReservations} expired reservations`));
124127
+ }
124128
+ }
124129
+ } catch (error54) {
124130
+ p.log.warn("Database repair check failed (non-critical)");
124131
+ if (error54 instanceof Error) {
124132
+ p.log.message(dim(` ${error54.message}`));
124133
+ }
124134
+ }
123405
124135
  const DEFAULT_COORDINATOR = "anthropic/claude-opus-4-5";
123406
124136
  const DEFAULT_WORKER = "anthropic/claude-sonnet-4-5";
123407
124137
  const DEFAULT_LITE = "anthropic/claude-haiku-4-5";
@@ -123549,8 +124279,8 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123549
124279
  p.log.message(dim(` Lite: ${liteModel}`));
123550
124280
  p.log.step("Setting up OpenCode integration...");
123551
124281
  const stats = { created: 0, updated: 0, unchanged: 0 };
123552
- const legacySkillsDir = join29(configDir, "skills");
123553
- const skillsDir = join29(configDir, "skill");
124282
+ const legacySkillsDir = join28(configDir, "skills");
124283
+ const skillsDir = join28(configDir, "skill");
123554
124284
  if (existsSync20(legacySkillsDir) && !existsSync20(skillsDir)) {
123555
124285
  p.log.step("Migrating skills directory...");
123556
124286
  try {
@@ -123584,7 +124314,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123584
124314
  } catch {}
123585
124315
  }
123586
124316
  p.log.message(dim(` Skills directory: ${skillsDir}`));
123587
- const bundledSkillsPath = join29(__dirname2, "..", "global-skills");
124317
+ const bundledSkillsPath = join28(__dirname2, "..", "global-skills");
123588
124318
  const bundledSkills = listDirectoryNames(bundledSkillsPath);
123589
124319
  if (existsSync20(bundledSkillsPath)) {
123590
124320
  if (bundledSkills.length > 0) {
@@ -123593,7 +124323,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123593
124323
  }
123594
124324
  if (bundledSkills.length > 0) {
123595
124325
  const globalSkills = listDirectoryNames(skillsDir);
123596
- const managedBundled = globalSkills.filter((name) => existsSync20(join29(skillsDir, name, BUNDLED_SKILL_MARKER_FILENAME)));
124326
+ const managedBundled = globalSkills.filter((name) => existsSync20(join28(skillsDir, name, BUNDLED_SKILL_MARKER_FILENAME)));
123597
124327
  const missingBundled = bundledSkills.filter((name) => !globalSkills.includes(name));
123598
124328
  if (missingBundled.length > 0 || managedBundled.length > 0) {
123599
124329
  {
@@ -123623,7 +124353,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123623
124353
  }
123624
124354
  }
123625
124355
  }
123626
- const agentsPath = join29(configDir, "AGENTS.md");
124356
+ const agentsPath = join28(configDir, "AGENTS.md");
123627
124357
  if (existsSync20(agentsPath)) {
123628
124358
  {
123629
124359
  const s2 = p.spinner();
@@ -123790,14 +124520,14 @@ async function version4() {
123790
124520
  showUpdateNotification(updateInfo3);
123791
124521
  }
123792
124522
  function config4() {
123793
- const configDir = join29(homedir11(), ".config", "opencode");
123794
- const pluginPath = join29(configDir, "plugin", "swarm.ts");
123795
- const commandPath = join29(configDir, "command", "swarm.md");
123796
- const agentDir = join29(configDir, "agent");
123797
- const plannerAgentPath = join29(agentDir, "swarm-planner.md");
123798
- const workerAgentPath = join29(agentDir, "swarm-worker.md");
123799
- const researcherAgentPath = join29(agentDir, "swarm-researcher.md");
123800
- const globalSkillsPath = join29(configDir, "skills");
124523
+ const configDir = join28(homedir10(), ".config", "opencode");
124524
+ const pluginPath = join28(configDir, "plugin", "swarm.ts");
124525
+ const commandPath = join28(configDir, "command", "swarm.md");
124526
+ const agentDir = join28(configDir, "agent");
124527
+ const plannerAgentPath = join28(agentDir, "swarm-planner.md");
124528
+ const workerAgentPath = join28(agentDir, "swarm-worker.md");
124529
+ const researcherAgentPath = join28(agentDir, "swarm-researcher.md");
124530
+ const globalSkillsPath = join28(configDir, "skills");
123801
124531
  console.log(yellow(BANNER));
123802
124532
  console.log(dim(" " + TAGLINE + " v" + VERSION));
123803
124533
  console.log();
@@ -123840,7 +124570,7 @@ function config4() {
123840
124570
  console.log(` ${dim(".claude/skills/")}`);
123841
124571
  console.log(` ${dim("skill/")}`);
123842
124572
  console.log();
123843
- const bundledSkillsPath = join29(__dirname2, "..", "global-skills");
124573
+ const bundledSkillsPath = join28(__dirname2, "..", "global-skills");
123844
124574
  if (existsSync20(bundledSkillsPath)) {
123845
124575
  try {
123846
124576
  const { readdirSync: readdirSync3 } = __require("fs");
@@ -124269,6 +124999,7 @@ ${cyan("Stats & History:")}
124269
124999
  swarm stats Show swarm health metrics powered by swarm-insights (last 7 days)
124270
125000
  swarm stats --since 24h Show stats for custom time period
124271
125001
  swarm stats --regressions Show eval regressions (>10% score drops)
125002
+ swarm stats --rejections Show rejection reason analytics
124272
125003
  swarm stats --json Output as JSON for scripting
124273
125004
  swarm o11y Show observability health dashboard (hook coverage, events, sessions)
124274
125005
  swarm o11y --since 7d Custom time period for event stats (default: 7 days)
@@ -124410,7 +125141,7 @@ async function listTools() {
124410
125141
  }
124411
125142
  async function agents(nonInteractive = false) {
124412
125143
  const home = process.env.HOME || process.env.USERPROFILE || "~";
124413
- const agentsPath = join29(home, ".config", "opencode", "AGENTS.md");
125144
+ const agentsPath = join28(home, ".config", "opencode", "AGENTS.md");
124414
125145
  p.intro(yellow(BANNER));
124415
125146
  if (!existsSync20(agentsPath)) {
124416
125147
  p.log.warn("No AGENTS.md found at " + agentsPath);
@@ -124484,9 +125215,9 @@ async function migrate() {
124484
125215
  p.intro("swarm migrate v" + VERSION);
124485
125216
  const projectPath = process.cwd();
124486
125217
  const tempDirName = getLibSQLProjectTempDirName(projectPath);
124487
- const tempDir = join29(tmpdir3(), tempDirName);
124488
- const pglitePath = join29(tempDir, "streams");
124489
- const libsqlPath = join29(tempDir, "streams.db");
125218
+ const tempDir = join28(tmpdir3(), tempDirName);
125219
+ const pglitePath = join28(tempDir, "streams");
125220
+ const libsqlPath = join28(tempDir, "streams.db");
124490
125221
  if (!pgliteExists(pglitePath)) {
124491
125222
  p.log.success("No PGlite database found - nothing to migrate!");
124492
125223
  p.outro("Done");
@@ -124595,7 +125326,7 @@ function listSessionFiles(dir) {
124595
125326
  const files = readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
124596
125327
  const sessions = [];
124597
125328
  for (const file4 of files) {
124598
- const filePath = join29(dir, file4);
125329
+ const filePath = join28(dir, file4);
124599
125330
  try {
124600
125331
  const events = parseSessionFile(filePath);
124601
125332
  if (events.length === 0)
@@ -124645,7 +125376,7 @@ function formatEvent(event, useColor = true) {
124645
125376
  }
124646
125377
  async function logSessions() {
124647
125378
  const args3 = process.argv.slice(4);
124648
- const sessionsDir = join29(homedir11(), ".config", "swarm-tools", "sessions");
125379
+ const sessionsDir = join28(homedir10(), ".config", "swarm-tools", "sessions");
124649
125380
  let sessionId = null;
124650
125381
  let latest = false;
124651
125382
  let jsonOutput = false;
@@ -124849,7 +125580,7 @@ function readLogFiles(dir) {
124849
125580
  if (!existsSync20(dir))
124850
125581
  return [];
124851
125582
  const allFiles = readdirSync2(dir);
124852
- const logFiles = allFiles.filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f)).sort().map((f) => join29(dir, f));
125583
+ const logFiles = allFiles.filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f)).sort().map((f) => join28(dir, f));
124853
125584
  const entries2 = [];
124854
125585
  for (const file4 of logFiles) {
124855
125586
  try {
@@ -125034,7 +125765,7 @@ async function logs() {
125034
125765
  moduleFilter = arg;
125035
125766
  }
125036
125767
  }
125037
- const logsDir = join29(homedir11(), ".config", "swarm-tools", "logs");
125768
+ const logsDir = join28(homedir10(), ".config", "swarm-tools", "logs");
125038
125769
  if (!existsSync20(logsDir)) {
125039
125770
  if (!jsonOutput) {
125040
125771
  p.log.warn("No logs directory found");
@@ -125072,7 +125803,7 @@ async function logs() {
125072
125803
  return;
125073
125804
  const files = readdirSync2(logsDir).filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f));
125074
125805
  for (const file4 of files) {
125075
- const filePath = join29(logsDir, file4);
125806
+ const filePath = join28(logsDir, file4);
125076
125807
  try {
125077
125808
  const stats = statSync2(filePath);
125078
125809
  filePositions.set(filePath, stats.size);
@@ -125110,7 +125841,7 @@ async function logs() {
125110
125841
  return;
125111
125842
  const files = readdirSync2(logsDir).filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f));
125112
125843
  for (const file4 of files) {
125113
- const filePath = join29(logsDir, file4);
125844
+ const filePath = join28(logsDir, file4);
125114
125845
  const newLines = readNewLines(filePath);
125115
125846
  for (const line of newLines) {
125116
125847
  const parsed = parseLogLine(line, filePath);
@@ -125161,7 +125892,112 @@ async function logs() {
125161
125892
  console.log();
125162
125893
  }
125163
125894
  }
125895
+ async function runDbRepair(options2) {
125896
+ const { dryRun } = options2;
125897
+ const globalDbPath = getGlobalDbPath2();
125898
+ const swarmMail = await getSwarmMailLibSQL19(globalDbPath);
125899
+ const db = await swarmMail.getDatabase();
125900
+ const nullBeadsResult = await db.query("SELECT COUNT(*) as count FROM beads WHERE id IS NULL");
125901
+ const nullBeads = Number(nullBeadsResult[0]?.count ?? 0);
125902
+ const orphanedRecipientsResult = await db.query("SELECT COUNT(*) as count FROM message_recipients WHERE NOT EXISTS (SELECT 1 FROM agents WHERE agents.name = message_recipients.agent_name)");
125903
+ const orphanedRecipients = Number(orphanedRecipientsResult[0]?.count ?? 0);
125904
+ const messagesWithoutRecipientsResult = await db.query("SELECT COUNT(*) as count FROM messages WHERE NOT EXISTS (SELECT 1 FROM message_recipients WHERE message_recipients.message_id = messages.id)");
125905
+ const messagesWithoutRecipients = Number(messagesWithoutRecipientsResult[0]?.count ?? 0);
125906
+ const expiredReservationsResult = await db.query("SELECT COUNT(*) as count FROM reservations WHERE released_at IS NULL AND expires_at < strftime('%s', 'now') * 1000");
125907
+ const expiredReservations = Number(expiredReservationsResult[0]?.count ?? 0);
125908
+ const totalCleaned = nullBeads + orphanedRecipients + messagesWithoutRecipients + expiredReservations;
125909
+ if (dryRun || totalCleaned === 0) {
125910
+ return {
125911
+ nullBeads,
125912
+ orphanedRecipients,
125913
+ messagesWithoutRecipients,
125914
+ expiredReservations,
125915
+ totalCleaned
125916
+ };
125917
+ }
125918
+ if (nullBeads > 0) {
125919
+ await db.query("DELETE FROM beads WHERE id IS NULL");
125920
+ }
125921
+ if (orphanedRecipients > 0) {
125922
+ await db.query("DELETE FROM message_recipients WHERE NOT EXISTS (SELECT 1 FROM agents WHERE agents.name = message_recipients.agent_name)");
125923
+ }
125924
+ if (messagesWithoutRecipients > 0) {
125925
+ await db.query("DELETE FROM messages WHERE NOT EXISTS (SELECT 1 FROM message_recipients WHERE message_recipients.message_id = messages.id)");
125926
+ }
125927
+ if (expiredReservations > 0) {
125928
+ await db.query("UPDATE reservations SET released_at = strftime('%s', 'now') * 1000 WHERE released_at IS NULL AND expires_at < strftime('%s', 'now') * 1000");
125929
+ }
125930
+ return {
125931
+ nullBeads,
125932
+ orphanedRecipients,
125933
+ messagesWithoutRecipients,
125934
+ expiredReservations,
125935
+ totalCleaned
125936
+ };
125937
+ }
125938
+ async function dbRepair() {
125939
+ const args3 = process.argv.slice(4);
125940
+ let dryRun = false;
125941
+ for (const arg of args3) {
125942
+ if (arg === "--dry-run") {
125943
+ dryRun = true;
125944
+ }
125945
+ }
125946
+ p.intro(dryRun ? "swarm db repair (DRY RUN)" : "swarm db repair");
125947
+ const s = p.spinner();
125948
+ s.start("Analyzing database...");
125949
+ try {
125950
+ const result = await runDbRepair({ dryRun: true });
125951
+ s.stop("Analysis complete");
125952
+ p.log.step(dryRun ? "Would delete:" : "Deleting:");
125953
+ if (result.nullBeads > 0) {
125954
+ p.log.message(` - ${result.nullBeads} beads with NULL IDs`);
125955
+ }
125956
+ if (result.orphanedRecipients > 0) {
125957
+ p.log.message(` - ${result.orphanedRecipients} orphaned message_recipients`);
125958
+ }
125959
+ if (result.messagesWithoutRecipients > 0) {
125960
+ p.log.message(` - ${result.messagesWithoutRecipients} messages without recipients`);
125961
+ }
125962
+ if (result.expiredReservations > 0) {
125963
+ p.log.message(` - ${result.expiredReservations} expired unreleased reservations`);
125964
+ }
125965
+ if (result.totalCleaned === 0) {
125966
+ p.outro(green("\u2713 Database is clean! No records to delete."));
125967
+ return;
125968
+ }
125969
+ console.log();
125970
+ p.log.message(dim(`Total: ${result.totalCleaned} records ${dryRun ? "would be" : "will be"} cleaned`));
125971
+ console.log();
125972
+ if (dryRun) {
125973
+ p.outro(dim("Run without --dry-run to execute cleanup"));
125974
+ return;
125975
+ }
125976
+ const confirmed = await p.confirm({
125977
+ message: `Delete ${result.totalCleaned} records?`,
125978
+ initialValue: false
125979
+ });
125980
+ if (p.isCancel(confirmed) || !confirmed) {
125981
+ p.cancel("Cleanup cancelled");
125982
+ return;
125983
+ }
125984
+ const cleanupSpinner = p.spinner();
125985
+ cleanupSpinner.start("Cleaning database...");
125986
+ await runDbRepair({ dryRun: false });
125987
+ cleanupSpinner.stop("Cleanup complete");
125988
+ p.outro(green(`\u2713 Successfully cleaned ${result.totalCleaned} records`));
125989
+ } catch (error54) {
125990
+ s.stop("Error");
125991
+ p.log.error(error54 instanceof Error ? error54.message : String(error54));
125992
+ process.exit(1);
125993
+ }
125994
+ }
125164
125995
  async function db() {
125996
+ const args3 = process.argv.slice(3);
125997
+ if (args3[0] === "repair") {
125998
+ await dbRepair();
125999
+ return;
126000
+ }
125165
126001
  const projectPath = process.cwd();
125166
126002
  const projectName = basename3(projectPath);
125167
126003
  const hash5 = hashLibSQLProjectPath(projectPath);
@@ -125222,7 +126058,7 @@ async function db() {
125222
126058
  console.log(dim(" Will be created on first use"));
125223
126059
  }
125224
126060
  console.log();
125225
- const pglitePath = join29(dbDir, "streams");
126061
+ const pglitePath = join28(dbDir, "streams");
125226
126062
  if (existsSync20(pglitePath)) {
125227
126063
  console.log(` \x1B[33m!\x1B[0m Legacy PGLite directory exists`);
125228
126064
  console.log(dim(` ${pglitePath}`));
@@ -125333,6 +126169,7 @@ async function stats() {
125333
126169
  let period = "7d";
125334
126170
  let format5 = "text";
125335
126171
  let showRegressions = false;
126172
+ let showRejections = false;
125336
126173
  for (let i = 0;i < args3.length; i++) {
125337
126174
  if (args3[i] === "--since" || args3[i] === "-s") {
125338
126175
  period = args3[i + 1] || "7d";
@@ -125341,6 +126178,8 @@ async function stats() {
125341
126178
  format5 = "json";
125342
126179
  } else if (args3[i] === "--regressions") {
125343
126180
  showRegressions = true;
126181
+ } else if (args3[i] === "--rejections") {
126182
+ showRejections = true;
125344
126183
  }
125345
126184
  }
125346
126185
  try {
@@ -125369,21 +126208,21 @@ async function stats() {
125369
126208
  strategy: row.strategy,
125370
126209
  success: row.success === "true"
125371
126210
  })));
125372
- const sessionsPath = join29(homedir11(), ".config", "swarm-tools", "sessions");
126211
+ const sessionsPath = join28(homedir10(), ".config", "swarm-tools", "sessions");
125373
126212
  let coordinatorStats = {
125374
126213
  violationRate: 0,
125375
126214
  spawnEfficiency: 0,
125376
126215
  reviewThoroughness: 0
125377
126216
  };
125378
126217
  if (existsSync20(sessionsPath)) {
125379
- const sessionFiles = readdirSync2(sessionsPath).filter((f) => f.endsWith(".jsonl") && statSync2(join29(sessionsPath, f)).mtimeMs >= since);
126218
+ const sessionFiles = readdirSync2(sessionsPath).filter((f) => f.endsWith(".jsonl") && statSync2(join28(sessionsPath, f)).mtimeMs >= since);
125380
126219
  let totalViolations = 0;
125381
126220
  let totalSpawns = 0;
125382
126221
  let totalReviews = 0;
125383
126222
  let totalSwarms = 0;
125384
126223
  for (const file4 of sessionFiles) {
125385
126224
  try {
125386
- const content = readFileSync16(join29(sessionsPath, file4), "utf-8");
126225
+ const content = readFileSync16(join28(sessionsPath, file4), "utf-8");
125387
126226
  const lines = content.trim().split(`
125388
126227
  `);
125389
126228
  let violations = 0;
@@ -125448,6 +126287,34 @@ async function stats() {
125448
126287
  `);
125449
126288
  }
125450
126289
  }
126290
+ } else if (showRejections) {
126291
+ const rejectionAnalytics = await getRejectionAnalytics2(swarmMail);
126292
+ if (format5 === "json") {
126293
+ console.log(JSON.stringify(rejectionAnalytics, null, 2));
126294
+ } else {
126295
+ console.log();
126296
+ const boxWidth = 61;
126297
+ const pad = (text2) => text2 + " ".repeat(Math.max(0, boxWidth - text2.length));
126298
+ console.log("\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510");
126299
+ console.log("\u2502" + pad(" REJECTION ANALYSIS (last " + period + ")") + "\u2502");
126300
+ console.log("\u251C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2524");
126301
+ console.log("\u2502" + pad(" Total Reviews: " + rejectionAnalytics.totalReviews) + "\u2502");
126302
+ const rejectionRate = rejectionAnalytics.totalReviews > 0 ? (100 - rejectionAnalytics.approvalRate).toFixed(0) : "0";
126303
+ console.log("\u2502" + pad(" Approved: " + rejectionAnalytics.approved + " (" + rejectionAnalytics.approvalRate.toFixed(0) + "%)") + "\u2502");
126304
+ console.log("\u2502" + pad(" Rejected: " + rejectionAnalytics.rejected + " (" + rejectionRate + "%)") + "\u2502");
126305
+ console.log("\u2502" + pad("") + "\u2502");
126306
+ if (rejectionAnalytics.topReasons.length > 0) {
126307
+ console.log("\u2502" + pad(" Top Rejection Reasons:") + "\u2502");
126308
+ for (const reason of rejectionAnalytics.topReasons) {
126309
+ const line = " \u251C\u2500\u2500 " + reason.category + ": " + reason.count + " (" + reason.percentage.toFixed(0) + "%)";
126310
+ console.log("\u2502" + pad(line) + "\u2502");
126311
+ }
126312
+ } else {
126313
+ console.log("\u2502" + pad(" No rejections in this period") + "\u2502");
126314
+ }
126315
+ console.log("\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518");
126316
+ console.log();
126317
+ }
125451
126318
  } else {
125452
126319
  if (format5 === "json") {
125453
126320
  console.log(JSON.stringify(stats2, null, 2));
@@ -125673,7 +126540,7 @@ async function evalRun() {
125673
126540
  }
125674
126541
  }
125675
126542
  if (ciMode) {
125676
- const resultsPath = join29(projectPath, ".hive", "eval-results.json");
126543
+ const resultsPath = join28(projectPath, ".hive", "eval-results.json");
125677
126544
  ensureHiveDirectory(projectPath);
125678
126545
  writeFileSync7(resultsPath, JSON.stringify(results, null, 2));
125679
126546
  console.log(`
@@ -125861,11 +126728,11 @@ async function memory() {
125861
126728
  try {
125862
126729
  const { getDb: getDb2 } = await import("swarm-mail");
125863
126730
  const tempDirName = getLibSQLProjectTempDirName(projectPath);
125864
- const tempDir = join29(tmpdir3(), tempDirName);
126731
+ const tempDir = join28(tmpdir3(), tempDirName);
125865
126732
  if (!existsSync20(tempDir)) {
125866
126733
  mkdirSync11(tempDir, { recursive: true });
125867
126734
  }
125868
- const dbPath = join29(tempDir, "streams.db");
126735
+ const dbPath = join28(tempDir, "streams.db");
125869
126736
  const dbUrl = `file://${dbPath}`;
125870
126737
  const db2 = await getDb2(dbUrl);
125871
126738
  const { createMemoryAdapter: createMemoryAdapter3 } = await import("swarm-mail");