opencode-swarm-plugin 0.48.1 → 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", {
@@ -91824,14 +92298,14 @@ async function maybeAutoMigrate(db) {
91824
92298
  if (memoryCount > 0) {
91825
92299
  return;
91826
92300
  }
91827
- console.log("[memory] Legacy database detected, starting auto-migration...");
92301
+ console.error("[memory] Legacy database detected, starting auto-migration...");
91828
92302
  const result = await migrateLegacyMemories({
91829
92303
  targetDb: db,
91830
92304
  dryRun: false,
91831
- onProgress: console.log
92305
+ onProgress: (msg) => console.error(msg)
91832
92306
  });
91833
92307
  if (result.migrated > 0) {
91834
- console.log(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
92308
+ console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
91835
92309
  }
91836
92310
  if (result.failed > 0) {
91837
92311
  console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
@@ -91900,6 +92374,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91900
92374
  async find(args2) {
91901
92375
  const limit = args2.limit ?? 10;
91902
92376
  let results;
92377
+ let usedFallback = false;
91903
92378
  if (args2.fts) {
91904
92379
  results = await store.ftsSearch(args2.query, {
91905
92380
  limit,
@@ -91910,14 +92385,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91910
92385
  const ollama = yield* Ollama;
91911
92386
  return yield* ollama.embed(args2.query);
91912
92387
  });
91913
- const queryEmbedding = await exports_Effect.runPromise(program.pipe(exports_Effect.provide(ollamaLayer)));
91914
- results = await store.search(queryEmbedding, {
91915
- limit,
91916
- threshold: 0.3,
91917
- collection: args2.collection
91918
- });
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
+ }
91919
92403
  }
91920
- return {
92404
+ const response = {
91921
92405
  results: results.map((r) => ({
91922
92406
  id: r.memory.id,
91923
92407
  content: args2.expand ? r.memory.content : truncateContent(r.memory.content),
@@ -91928,6 +92412,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
91928
92412
  })),
91929
92413
  count: results.length
91930
92414
  };
92415
+ if (usedFallback) {
92416
+ response.fallback_used = true;
92417
+ }
92418
+ return response;
91931
92419
  },
91932
92420
  async get(args2) {
91933
92421
  return store.get(args2.id);
@@ -92746,6 +93234,74 @@ Your role is **ONLY** to:
92746
93234
 
92747
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.
92748
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
+
92749
93305
  ### Why This Matters
92750
93306
 
92751
93307
  | Coordinator Work | Worker Work | Consequence of Mixing |
@@ -93830,15 +94386,15 @@ import {
93830
94386
  createHiveAdapter as createHiveAdapter4,
93831
94387
  resolvePartialId as resolvePartialId4,
93832
94388
  createDurableStreamAdapter,
93833
- createDurableStreamServer
94389
+ createDurableStreamServer,
94390
+ consolidateDatabases,
94391
+ getGlobalDbPath as getGlobalDbPath2
93834
94392
  } from "swarm-mail";
93835
94393
  import { execSync, spawn } from "child_process";
93836
94394
  import { tmpdir as tmpdir3 } from "os";
93837
94395
 
93838
94396
  // src/query-tools.ts
93839
- import { createLibSQLAdapter as createLibSQLAdapter2 } from "swarm-mail";
93840
- import { join as join7 } from "node:path";
93841
- import { homedir as homedir3 } from "node:os";
94397
+ import { createLibSQLAdapter as createLibSQLAdapter2, getGlobalDbPath } from "swarm-mail";
93842
94398
  var presetQueries = {
93843
94399
  failed_decompositions: `
93844
94400
  SELECT
@@ -93989,8 +94545,7 @@ var presetQueries = {
93989
94545
  `
93990
94546
  };
93991
94547
  function getDbPath() {
93992
- const home = homedir3();
93993
- return join7(home, ".swarm-tools", "swarm-mail.db");
94548
+ return getGlobalDbPath();
93994
94549
  }
93995
94550
  async function createDbAdapter() {
93996
94551
  const dbPath = getDbPath();
@@ -95200,8 +95755,8 @@ function detectRegressions(projectPath, threshold = 0.1) {
95200
95755
  }
95201
95756
 
95202
95757
  // src/observability-health.ts
95203
- import { homedir as homedir4 } from "node:os";
95204
- import { join as join10 } from "node:path";
95758
+ import { homedir as homedir3 } from "node:os";
95759
+ import { join as join9 } from "node:path";
95205
95760
  import { existsSync as existsSync8, readdirSync, readFileSync as readFileSync8, statSync } from "node:fs";
95206
95761
  var EXPECTED_HOOKS = [
95207
95762
  "tool.execute.after",
@@ -95298,16 +95853,16 @@ function calculateSessionQuality(input) {
95298
95853
  };
95299
95854
  }
95300
95855
  function querySessionQuality(options2) {
95301
- const sessionsPath = join10(homedir4(), ".config", "swarm-tools", "sessions");
95856
+ const sessionsPath = join9(homedir3(), ".config", "swarm-tools", "sessions");
95302
95857
  if (!existsSync8(sessionsPath)) {
95303
95858
  return { totalSessions: 0, qualitySessions: 0 };
95304
95859
  }
95305
95860
  const since = Date.now() - options2.days * 24 * 60 * 60 * 1000;
95306
- 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);
95307
95862
  let qualityCount = 0;
95308
95863
  for (const file4 of sessionFiles) {
95309
95864
  try {
95310
- const content = readFileSync8(join10(sessionsPath, file4), "utf-8");
95865
+ const content = readFileSync8(join9(sessionsPath, file4), "utf-8");
95311
95866
  const lines = content.trim().split(`
95312
95867
  `);
95313
95868
  let hasDecisions = false;
@@ -95417,6 +95972,90 @@ async function getObservabilityHealth(projectPath, options2 = {}) {
95417
95972
  };
95418
95973
  }
95419
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
+
95420
96059
  // src/eval-history.ts
95421
96060
  import * as fs5 from "node:fs";
95422
96061
  import * as path6 from "node:path";
@@ -95814,7 +96453,7 @@ import {
95814
96453
  findCellsByPartialId as findCellsByPartialId3
95815
96454
  } from "swarm-mail";
95816
96455
  import { existsSync as existsSync13, readFileSync as readFileSync13 } from "node:fs";
95817
- import { join as join14 } from "node:path";
96456
+ import { join as join13 } from "node:path";
95818
96457
 
95819
96458
  // src/schemas/cell.ts
95820
96459
  init_zod3();
@@ -95892,7 +96531,8 @@ var CellTreeSchema3 = exports_external3.object({
95892
96531
  title: exports_external3.string().min(1),
95893
96532
  description: exports_external3.string().optional().default("")
95894
96533
  }),
95895
- 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.")
95896
96536
  });
95897
96537
  var EpicCreateArgsSchema3 = exports_external3.object({
95898
96538
  epic_title: exports_external3.string().min(1),
@@ -96412,7 +97052,7 @@ class HiveError3 extends Error {
96412
97052
  }
96413
97053
  }
96414
97054
  function ensureHiveDirectory3(projectPath) {
96415
- const hiveDir = join14(projectPath, ".hive");
97055
+ const hiveDir = join13(projectPath, ".hive");
96416
97056
  if (!existsSync13(hiveDir)) {
96417
97057
  const { mkdirSync: mkdirSync7 } = __require("node:fs");
96418
97058
  mkdirSync7(hiveDir, { recursive: true });
@@ -96469,7 +97109,7 @@ async function getHiveAdapter3(projectKey) {
96469
97109
  return adapter;
96470
97110
  }
96471
97111
  async function autoMigrateFromJSONL3(adapter, projectKey) {
96472
- const jsonlPath = join14(projectKey, ".hive", "issues.jsonl");
97112
+ const jsonlPath = join13(projectKey, ".hive", "issues.jsonl");
96473
97113
  if (!existsSync13(jsonlPath)) {
96474
97114
  return;
96475
97115
  }
@@ -96483,13 +97123,13 @@ async function autoMigrateFromJSONL3(adapter, projectKey) {
96483
97123
  skipExisting: true
96484
97124
  });
96485
97125
  if (result.created > 0 || result.updated > 0) {
96486
- 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)`);
96487
97127
  }
96488
97128
  if (result.errors.length > 0) {
96489
- 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}`));
96490
97130
  }
96491
97131
  } catch (error54) {
96492
- 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));
96493
97133
  }
96494
97134
  }
96495
97135
  function formatCellForOutput3(adapterCell) {
@@ -97090,7 +97730,7 @@ var hive_sync3 = tool3({
97090
97730
  const flushResult = await withTimeout(flushManager.flush(), TIMEOUT_MS, "flush hive");
97091
97731
  const swarmMail = await getSwarmMailLibSQL12(projectKey);
97092
97732
  const db = await swarmMail.getDatabase();
97093
- const hivePath = join14(projectKey, ".hive");
97733
+ const hivePath = join13(projectKey, ".hive");
97094
97734
  let memoriesSynced = 0;
97095
97735
  try {
97096
97736
  const memoryResult = await syncMemories3(db, hivePath);
@@ -97594,8 +98234,8 @@ function formatToolAvailability2(availability) {
97594
98234
  // src/rate-limiter.ts
97595
98235
  var import_ioredis = __toESM(require_built3(), 1);
97596
98236
  import { mkdirSync as mkdirSync7, existsSync as existsSync14 } from "node:fs";
97597
- import { dirname as dirname7, join as join15 } from "node:path";
97598
- 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";
97599
98239
  var sqliteAvailable = false;
97600
98240
  var createDatabase = null;
97601
98241
  try {
@@ -97855,7 +98495,7 @@ async function createRateLimiter(options2) {
97855
98495
  const {
97856
98496
  backend,
97857
98497
  redisUrl = process.env.OPENCODE_RATE_LIMIT_REDIS_URL || "redis://localhost:6379",
97858
- 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")
97859
98499
  } = options2 || {};
97860
98500
  if (backend === "memory") {
97861
98501
  return new InMemoryRateLimiter;
@@ -97921,7 +98561,7 @@ import {
97921
98561
  writeFileSync as writeFileSync5,
97922
98562
  unlinkSync
97923
98563
  } from "fs";
97924
- import { join as join16 } from "path";
98564
+ import { join as join15 } from "path";
97925
98565
  import { tmpdir } from "os";
97926
98566
  var AGENT_MAIL_URL = "http://127.0.0.1:8765";
97927
98567
  var DEFAULT_TTL_SECONDS = 3600;
@@ -97942,10 +98582,10 @@ var RECOVERY_CONFIG = {
97942
98582
  restartCooldownMs: 1e4,
97943
98583
  enabled: process.env.OPENCODE_AGENT_MAIL_AUTO_RESTART !== "false"
97944
98584
  };
97945
- 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");
97946
98586
  function getSessionStatePath(sessionID) {
97947
98587
  const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
97948
- return join16(SESSION_STATE_DIR, `${safeID}.json`);
98588
+ return join15(SESSION_STATE_DIR, `${safeID}.json`);
97949
98589
  }
97950
98590
  function loadSessionState(sessionID) {
97951
98591
  const path4 = getSessionStatePath(sessionID);
@@ -98655,17 +99295,17 @@ import {
98655
99295
  writeFileSync as writeFileSync6,
98656
99296
  unlinkSync as unlinkSync2
98657
99297
  } from "node:fs";
98658
- import { join as join17 } from "node:path";
99298
+ import { join as join16 } from "node:path";
98659
99299
  import { tmpdir as tmpdir2 } from "node:os";
98660
99300
  var MAX_INBOX_LIMIT2 = 5;
98661
99301
  var swarmMailProjectDirectory = null;
98662
99302
  function getSwarmMailProjectDirectory() {
98663
99303
  return swarmMailProjectDirectory ?? undefined;
98664
99304
  }
98665
- 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");
98666
99306
  function getSessionStatePath2(sessionID) {
98667
99307
  const safeID = sessionID.replace(/[^a-zA-Z0-9_-]/g, "_");
98668
- return join17(SESSION_STATE_DIR2, `${safeID}.json`);
99308
+ return join16(SESSION_STATE_DIR2, `${safeID}.json`);
98669
99309
  }
98670
99310
  function loadSessionState2(sessionID) {
98671
99311
  const path4 = getSessionStatePath2(sessionID);
@@ -101812,12 +102452,12 @@ init_skills2();
101812
102452
  // src/swarm-worktree.ts
101813
102453
  init_dist2();
101814
102454
  init_zod3();
101815
- import { join as join19 } from "node:path";
102455
+ import { join as join18 } from "node:path";
101816
102456
  import { existsSync as existsSync17 } from "node:fs";
101817
102457
  var WORKTREE_DIR2 = ".swarm/worktrees";
101818
102458
  function getWorktreePath2(projectPath, taskId) {
101819
102459
  const safeTaskId = taskId.replace(/[^a-zA-Z0-9.-]/g, "_");
101820
- return join19(projectPath, WORKTREE_DIR2, safeTaskId);
102460
+ return join18(projectPath, WORKTREE_DIR2, safeTaskId);
101821
102461
  }
101822
102462
  function parseTaskIdFromPath2(worktreePath) {
101823
102463
  const parts2 = worktreePath.split("/");
@@ -101851,7 +102491,7 @@ async function getWorktreeCommits2(worktreePath, startCommit) {
101851
102491
  `).filter((c) => c.length > 0);
101852
102492
  }
101853
102493
  async function ensureWorktreeDir2(projectPath) {
101854
- const worktreeDir = join19(projectPath, WORKTREE_DIR2);
102494
+ const worktreeDir = join18(projectPath, WORKTREE_DIR2);
101855
102495
  await Bun.$`mkdir -p ${worktreeDir}`.quiet().nothrow();
101856
102496
  }
101857
102497
  var swarm_worktree_create2 = tool3({
@@ -101988,7 +102628,7 @@ var swarm_worktree_cleanup2 = tool3({
101988
102628
  return JSON.stringify(result3, null, 2);
101989
102629
  }
101990
102630
  const output = listResult.stdout.toString();
101991
- const worktreeDir = join19(args2.project_path, WORKTREE_DIR2);
102631
+ const worktreeDir = join18(args2.project_path, WORKTREE_DIR2);
101992
102632
  const worktrees = output.split(`
101993
102633
 
101994
102634
  `).filter((block) => block.includes(worktreeDir)).map((block) => {
@@ -102053,7 +102693,7 @@ var swarm_worktree_list2 = tool3({
102053
102693
  }, null, 2);
102054
102694
  }
102055
102695
  const output = listResult.stdout.toString();
102056
- const worktreeDir = join19(args2.project_path, WORKTREE_DIR2);
102696
+ const worktreeDir = join18(args2.project_path, WORKTREE_DIR2);
102057
102697
  const worktrees = [];
102058
102698
  const blocks = output.split(`
102059
102699
 
@@ -103114,7 +103754,7 @@ var swarm_complete2 = tool3({
103114
103754
  files_touched: tool3.schema.array(tool3.schema.string()).optional().describe("Files modified - will be verified (typecheck, tests)"),
103115
103755
  skip_verification: tool3.schema.boolean().optional().describe("Skip ALL verification (typecheck, tests). Use sparingly! (default: false)"),
103116
103756
  planned_files: tool3.schema.array(tool3.schema.string()).optional().describe("Files that were originally planned to be modified"),
103117
- 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"),
103118
103758
  error_count: tool3.schema.number().optional().describe("Number of errors encountered during task"),
103119
103759
  retry_count: tool3.schema.number().optional().describe("Number of retry attempts during task"),
103120
103760
  skip_review: tool3.schema.boolean().optional().describe("Skip review gate check (default: false). Use only for tasks that don't require coordinator review.")
@@ -103318,7 +103958,7 @@ This will be recorded as a negative learning signal.`;
103318
103958
  syncError = error54 instanceof Error ? error54.message : String(error54);
103319
103959
  console.warn(`[swarm_complete] Auto-sync failed (non-fatal): ${syncError}`);
103320
103960
  }
103321
- const completionDurationMs = args2.start_time ? Date.now() - args2.start_time : 0;
103961
+ const completionDurationMs = Date.now() - args2.start_time;
103322
103962
  const eventEpicId = cell.parent_id || (args2.bead_id.includes(".") ? args2.bead_id.split(".")[0] : args2.bead_id);
103323
103963
  try {
103324
103964
  const event = createEvent8("subtask_outcome", {
@@ -105254,7 +105894,7 @@ var dedupeWith2 = /* @__PURE__ */ dual2(2, (self, isEquivalent) => {
105254
105894
  return [];
105255
105895
  });
105256
105896
  var dedupe2 = (self) => dedupeWith2(self, equivalence2());
105257
- var join20 = /* @__PURE__ */ dual2(2, (self, sep4) => fromIterable8(self).join(sep4));
105897
+ var join19 = /* @__PURE__ */ dual2(2, (self, sep4) => fromIterable8(self).join(sep4));
105258
105898
 
105259
105899
  // ../../node_modules/.bun/effect@3.19.12/node_modules/effect/dist/esm/Number.js
105260
105900
  var Order3 = number10;
@@ -109805,7 +110445,7 @@ var InvalidData2 = (path4, message, options2 = {
109805
110445
  Object.defineProperty(error54, "toString", {
109806
110446
  enumerable: false,
109807
110447
  value() {
109808
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110448
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109809
110449
  return `(Invalid data at ${path10}: "${this.message}")`;
109810
110450
  }
109811
110451
  });
@@ -109821,7 +110461,7 @@ var MissingData2 = (path4, message, options2 = {
109821
110461
  Object.defineProperty(error54, "toString", {
109822
110462
  enumerable: false,
109823
110463
  value() {
109824
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110464
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109825
110465
  return `(Missing data at ${path10}: "${this.message}")`;
109826
110466
  }
109827
110467
  });
@@ -109838,7 +110478,7 @@ var SourceUnavailable2 = (path4, message, cause3, options2 = {
109838
110478
  Object.defineProperty(error54, "toString", {
109839
110479
  enumerable: false,
109840
110480
  value() {
109841
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110481
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109842
110482
  return `(Source unavailable at ${path10}: "${this.message}")`;
109843
110483
  }
109844
110484
  });
@@ -109854,7 +110494,7 @@ var Unsupported2 = (path4, message, options2 = {
109854
110494
  Object.defineProperty(error54, "toString", {
109855
110495
  enumerable: false,
109856
110496
  value() {
109857
- const path10 = pipe4(this.path, join20(options2.pathDelim));
110497
+ const path10 = pipe4(this.path, join19(options2.pathDelim));
109858
110498
  return `(Unsupported operation at ${path10}: "${this.message}")`;
109859
110499
  }
109860
110500
  });
@@ -109974,7 +110614,7 @@ var fromEnv2 = (options2) => {
109974
110614
  pathDelim: "_",
109975
110615
  seqDelim: ","
109976
110616
  }, options2);
109977
- const makePathString = (path4) => pipe4(path4, join20(pathDelim));
110617
+ const makePathString = (path4) => pipe4(path4, join19(pathDelim));
109978
110618
  const unmakePathString = (pathString) => pathString.split(pathDelim);
109979
110619
  const getEnv = () => typeof process !== "undefined" && ("env" in process) && typeof process.env === "object" ? process.env : {};
109980
110620
  const load = (path4, primitive, split = true) => {
@@ -110098,7 +110738,7 @@ var fromFlatLoop2 = (flat, prefix, config4, split) => {
110098
110738
  return fail5(right4.left);
110099
110739
  }
110100
110740
  if (isRight5(left4) && isRight5(right4)) {
110101
- const path4 = pipe4(prefix, join20("."));
110741
+ const path4 = pipe4(prefix, join19("."));
110102
110742
  const fail6 = fromFlatLoopFail2(prefix, path4);
110103
110743
  const [lefts, rights] = extend4(fail6, fail6, pipe4(left4.right, map8(right5)), pipe4(right4.right, map8(right5)));
110104
110744
  return pipe4(lefts, zip3(rights), forEachSequential2(([left6, right6]) => pipe4(zip5(left6, right6), map18(([left7, right7]) => op.zip(left7, right7)))));
@@ -112272,11 +112912,11 @@ var interruptAllAs2 = /* @__PURE__ */ dual2(2, /* @__PURE__ */ fnUntraced3(funct
112272
112912
  }
112273
112913
  }));
112274
112914
  var interruptAsFork2 = /* @__PURE__ */ dual2(2, (self, fiberId4) => self.interruptAsFork(fiberId4));
112275
- var join21 = (self) => zipLeft3(flatten11(self.await), self.inheritAll);
112915
+ var join20 = (self) => zipLeft3(flatten11(self.await), self.inheritAll);
112276
112916
  var _never4 = {
112277
112917
  ...CommitPrototype3,
112278
112918
  commit() {
112279
- return join21(this);
112919
+ return join20(this);
112280
112920
  },
112281
112921
  ...fiberProto2,
112282
112922
  id: () => none14,
@@ -113523,7 +114163,7 @@ class FiberRuntime2 extends Class4 {
113523
114163
  this.refreshRefCache();
113524
114164
  }
113525
114165
  commit() {
113526
- return join21(this);
114166
+ return join20(this);
113527
114167
  }
113528
114168
  id() {
113529
114169
  return this._fiberId;
@@ -114538,7 +115178,7 @@ var forEachConcurrentDiscard2 = (self, f, batching, processAll, n) => uninterrup
114538
115178
  next();
114539
115179
  }
114540
115180
  }));
114541
- return asVoid4(onExit4(flatten11(restore(join21(processingFiber))), exitMatch2({
115181
+ return asVoid4(onExit4(flatten11(restore(join20(processingFiber))), exitMatch2({
114542
115182
  onFailure: (cause4) => {
114543
115183
  onInterruptSignal();
114544
115184
  const target2 = residual.length + 1;
@@ -114860,7 +115500,7 @@ var fiberAll2 = (fibers) => {
114860
115500
  const _fiberAll = {
114861
115501
  ...CommitPrototype5,
114862
115502
  commit() {
114863
- return join21(this);
115503
+ return join20(this);
114864
115504
  },
114865
115505
  [FiberTypeId2]: fiberVariance5,
114866
115506
  id: () => fromIterable8(fibers).reduce((id, fiber) => combine12(id, fiber.id()), none14),
@@ -114913,14 +115553,14 @@ var raceWith3 = /* @__PURE__ */ dual2(3, (self, other, options2) => raceFibersWi
114913
115553
  }
114914
115554
  })
114915
115555
  }));
114916
- 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)))))));
114917
115557
  var race3 = /* @__PURE__ */ dual2(2, (self, that) => fiberIdWith3((parentFiberId) => raceWith3(self, that, {
114918
115558
  onSelfDone: (exit4, right4) => exitMatchEffect2(exit4, {
114919
- onFailure: (cause4) => pipe4(join21(right4), mapErrorCause3((cause22) => parallel4(cause4, cause22))),
115559
+ onFailure: (cause4) => pipe4(join20(right4), mapErrorCause3((cause22) => parallel4(cause4, cause22))),
114920
115560
  onSuccess: (value) => pipe4(right4, interruptAsFiber2(parentFiberId), as3(value))
114921
115561
  }),
114922
115562
  onOtherDone: (exit4, left4) => exitMatchEffect2(exit4, {
114923
- onFailure: (cause4) => pipe4(join21(left4), mapErrorCause3((cause22) => parallel4(cause22, cause4))),
115563
+ onFailure: (cause4) => pipe4(join20(left4), mapErrorCause3((cause22) => parallel4(cause22, cause4))),
114924
115564
  onSuccess: (value) => pipe4(left4, interruptAsFiber2(parentFiberId), as3(value))
114925
115565
  })
114926
115566
  })));
@@ -116074,8 +116714,8 @@ var forkIn3 = /* @__PURE__ */ dual2(2, (self, scope4) => withFiberRuntime3((pare
116074
116714
  return succeed5(fiber);
116075
116715
  }));
116076
116716
  var forkScoped3 = (self) => scopeWith3((scope4) => forkIn3(self, scope4));
116077
- var fromFiber3 = (fiber) => join21(fiber);
116078
- var fromFiberEffect3 = (fiber) => suspend4(() => flatMap15(fiber, join21));
116717
+ var fromFiber3 = (fiber) => join20(fiber);
116718
+ var fromFiberEffect3 = (fiber) => suspend4(() => flatMap15(fiber, join20));
116079
116719
  var memoKeySymbol2 = /* @__PURE__ */ Symbol.for("effect/Effect/memoizeFunction.key");
116080
116720
 
116081
116721
  class Key2 {
@@ -117659,14 +118299,14 @@ async function maybeAutoMigrate2(db) {
117659
118299
  if (memoryCount > 0) {
117660
118300
  return;
117661
118301
  }
117662
- console.log("[memory] Legacy database detected, starting auto-migration...");
118302
+ console.error("[memory] Legacy database detected, starting auto-migration...");
117663
118303
  const result = await migrateLegacyMemories2({
117664
118304
  targetDb: db,
117665
118305
  dryRun: false,
117666
- onProgress: console.log
118306
+ onProgress: (msg) => console.error(msg)
117667
118307
  });
117668
118308
  if (result.migrated > 0) {
117669
- console.log(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
118309
+ console.error(`[memory] Auto-migrated ${result.migrated} memories from legacy database`);
117670
118310
  }
117671
118311
  if (result.failed > 0) {
117672
118312
  console.warn(`[memory] ${result.failed} memories failed to migrate. See errors above.`);
@@ -117735,6 +118375,7 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117735
118375
  async find(args3) {
117736
118376
  const limit = args3.limit ?? 10;
117737
118377
  let results;
118378
+ let usedFallback = false;
117738
118379
  if (args3.fts) {
117739
118380
  results = await store.ftsSearch(args3.query, {
117740
118381
  limit,
@@ -117745,14 +118386,23 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117745
118386
  const ollama = yield* Ollama2;
117746
118387
  return yield* ollama.embed(args3.query);
117747
118388
  });
117748
- const queryEmbedding = await exports_Effect2.runPromise(program.pipe(exports_Effect2.provide(ollamaLayer)));
117749
- results = await store.search(queryEmbedding, {
117750
- limit,
117751
- threshold: 0.3,
117752
- collection: args3.collection
117753
- });
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
+ }
117754
118404
  }
117755
- return {
118405
+ const response = {
117756
118406
  results: results.map((r) => ({
117757
118407
  id: r.memory.id,
117758
118408
  content: args3.expand ? r.memory.content : truncateContent(r.memory.content),
@@ -117763,6 +118413,10 @@ Linked to ${result.links.length} related memor${result.links.length === 1 ? "y"
117763
118413
  })),
117764
118414
  count: results.length
117765
118415
  };
118416
+ if (usedFallback) {
118417
+ response.fallback_used = true;
118418
+ }
118419
+ return response;
117766
118420
  },
117767
118421
  async get(args3) {
117768
118422
  return store.get(args3.id);
@@ -119301,7 +119955,7 @@ var promptTools2 = {
119301
119955
  init_dist2();
119302
119956
  import { existsSync as existsSync18 } from "node:fs";
119303
119957
  import { readFile as readFile3 } from "node:fs/promises";
119304
- import { join as join23 } from "node:path";
119958
+ import { join as join21 } from "node:path";
119305
119959
 
119306
119960
  // ../../node_modules/.bun/yaml@2.8.2/node_modules/yaml/dist/index.js
119307
119961
  var composer = require_composer();
@@ -119479,20 +120133,20 @@ async function parsePackageJson(packageJsonPath, packages) {
119479
120133
  }
119480
120134
  }
119481
120135
  async function getInstalledVersions(projectPath, packages, checkUpgrades = false) {
119482
- const npmLock = join23(projectPath, "package-lock.json");
120136
+ const npmLock = join21(projectPath, "package-lock.json");
119483
120137
  let versions2 = [];
119484
120138
  if (existsSync18(npmLock)) {
119485
120139
  versions2 = await parseNpmLockfile(npmLock, packages);
119486
120140
  } else {
119487
- const pnpmLock = join23(projectPath, "pnpm-lock.yaml");
120141
+ const pnpmLock = join21(projectPath, "pnpm-lock.yaml");
119488
120142
  if (existsSync18(pnpmLock)) {
119489
120143
  versions2 = await parsePnpmLockfile(pnpmLock, packages);
119490
120144
  } else {
119491
- const yarnLock = join23(projectPath, "yarn.lock");
120145
+ const yarnLock = join21(projectPath, "yarn.lock");
119492
120146
  if (existsSync18(yarnLock)) {
119493
120147
  versions2 = await parseYarnLockfile(yarnLock, packages);
119494
120148
  } else {
119495
- const packageJson = join23(projectPath, "package.json");
120149
+ const packageJson = join21(projectPath, "package.json");
119496
120150
  if (existsSync18(packageJson)) {
119497
120151
  versions2 = await parsePackageJson(packageJson, packages);
119498
120152
  }
@@ -120644,7 +121298,7 @@ import {
120644
121298
  } from "swarm-mail";
120645
121299
  import * as os5 from "node:os";
120646
121300
  import * as path10 from "node:path";
120647
- import { join as join26 } from "node:path";
121301
+ import { join as join25 } from "node:path";
120648
121302
  var cachedAdapter3 = null;
120649
121303
  var cachedIndexer = null;
120650
121304
  var cachedProjectPath3 = null;
@@ -120851,7 +121505,7 @@ var hivemind_sync = tool3({
120851
121505
  const projectPath = cachedProjectPath3 || process.cwd();
120852
121506
  const swarmMail = await getSwarmMailLibSQL16(projectPath);
120853
121507
  const dbAdapter = await swarmMail.getDatabase();
120854
- const hiveDir = join26(projectPath, ".hive");
121508
+ const hiveDir = join25(projectPath, ".hive");
120855
121509
  const result = await syncMemories4(dbAdapter, hiveDir);
120856
121510
  await emitEvent("memories_synced", {
120857
121511
  imported: result.imported.created,
@@ -121386,9 +122040,9 @@ import { checkSwarmHealth as checkSwarmHealth4 } from "swarm-mail";
121386
122040
  // src/logger.ts
121387
122041
  var import_pino = __toESM(require_pino(), 1);
121388
122042
  import { mkdirSync as mkdirSync10, existsSync as existsSync19 } from "node:fs";
121389
- import { join as join27 } from "node:path";
121390
- import { homedir as homedir9 } from "node:os";
121391
- 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");
121392
122046
  function ensureLogDir(logDir) {
121393
122047
  if (!existsSync19(logDir)) {
121394
122048
  mkdirSync10(logDir, { recursive: true });
@@ -121407,7 +122061,7 @@ function getLogger(logDir = DEFAULT_LOG_DIR) {
121407
122061
  let logger;
121408
122062
  if (process.env.SWARM_LOG_FILE === "1") {
121409
122063
  ensureLogDir(logDir);
121410
- const logPath = join27(logDir, "swarm.log");
122064
+ const logPath = join26(logDir, "swarm.log");
121411
122065
  logger = import_pino.default(baseConfig, import_pino.default.destination({ dest: logPath, sync: false }));
121412
122066
  } else {
121413
122067
  logger = import_pino.default(baseConfig);
@@ -121811,7 +122465,7 @@ var allTools = {
121811
122465
 
121812
122466
  // bin/swarm.ts
121813
122467
  var __dirname2 = dirname9(fileURLToPath3(import.meta.url));
121814
- var pkgPath = join29(__dirname2, "..", "..", "package.json");
122468
+ var pkgPath = existsSync20(join28(__dirname2, "..", "package.json")) ? join28(__dirname2, "..", "package.json") : join28(__dirname2, "..", "..", "package.json");
121815
122469
  var pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
121816
122470
  var VERSION = pkg.version;
121817
122471
  var BEE = `
@@ -122150,8 +122804,8 @@ function copyDirRecursiveSync(srcDir, destDir) {
122150
122804
  mkdirSync11(destDir, { recursive: true });
122151
122805
  const entries2 = readdirSync2(srcDir, { withFileTypes: true });
122152
122806
  for (const entry of entries2) {
122153
- const srcPath = join29(srcDir, entry.name);
122154
- const destPath = join29(destDir, entry.name);
122807
+ const srcPath = join28(srcDir, entry.name);
122808
+ const destPath = join28(destDir, entry.name);
122155
122809
  if (entry.isDirectory()) {
122156
122810
  copyDirRecursiveSync(srcPath, destPath);
122157
122811
  continue;
@@ -122165,7 +122819,7 @@ function copyDirRecursiveSync(srcDir, destDir) {
122165
122819
  }
122166
122820
  }
122167
122821
  function writeBundledSkillMarker(skillDir, info) {
122168
- const markerPath = join29(skillDir, BUNDLED_SKILL_MARKER_FILENAME);
122822
+ const markerPath = join28(skillDir, BUNDLED_SKILL_MARKER_FILENAME);
122169
122823
  writeFileSync7(markerPath, JSON.stringify({
122170
122824
  managed_by: "opencode-swarm-plugin",
122171
122825
  version: info.version,
@@ -122182,9 +122836,9 @@ function syncBundledSkillsToGlobal({
122182
122836
  const updated = [];
122183
122837
  const skipped = [];
122184
122838
  for (const name of bundledSkills) {
122185
- const srcSkillDir = join29(bundledSkillsPath, name);
122186
- const destSkillDir = join29(globalSkillsPath, name);
122187
- 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);
122188
122842
  if (!existsSync20(destSkillDir)) {
122189
122843
  copyDirRecursiveSync(srcSkillDir, destSkillDir);
122190
122844
  writeBundledSkillMarker(destSkillDir, { version: version4 });
@@ -122213,7 +122867,7 @@ function backupFileWithTimestamp(filePath) {
122213
122867
  const dir = dirname9(filePath);
122214
122868
  const base = basename3(filePath);
122215
122869
  const timestamp = new Date().toISOString().replace(/[:.]/g, "").replace(/Z$/, "Z");
122216
- const backupPath = join29(dir, `${base}.swarm-backup-${timestamp}`);
122870
+ const backupPath = join28(dir, `${base}.swarm-backup-${timestamp}`);
122217
122871
  copyFileSync(filePath, backupPath);
122218
122872
  return backupPath;
122219
122873
  } catch {
@@ -122600,7 +123254,7 @@ function updateAgentsMdFile({
122600
123254
  return { changed: true, backupPath, changes };
122601
123255
  }
122602
123256
  function getPluginWrapper() {
122603
- const templatePath = join29(__dirname2, "..", "examples", "plugin-wrapper-template.ts");
123257
+ const templatePath = join28(__dirname2, "..", "examples", "plugin-wrapper-template.ts");
122604
123258
  try {
122605
123259
  return readFileSync16(templatePath, "utf-8");
122606
123260
  } catch (error54) {
@@ -123035,9 +123689,9 @@ async function doctor(debug = false) {
123035
123689
  const requiredMissing = required4.filter((r) => !r.available);
123036
123690
  const optionalMissing = optional4.filter((r) => !r.available);
123037
123691
  p.log.step("Skills:");
123038
- const configDir = join29(homedir11(), ".config", "opencode");
123039
- const globalSkillsPath = join29(configDir, "skills");
123040
- 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");
123041
123695
  if (existsSync20(globalSkillsPath)) {
123042
123696
  try {
123043
123697
  const { readdirSync: readdirSync3 } = __require("fs");
@@ -123111,9 +123765,9 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123111
123765
  p.log.success(`Bun v${bunCheck.version} detected`);
123112
123766
  const cwd = process.cwd();
123113
123767
  const tempDirName = getLibSQLProjectTempDirName(cwd);
123114
- const tempDir = join29(tmpdir3(), tempDirName);
123115
- const pglitePath = join29(tempDir, "streams");
123116
- 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");
123117
123771
  if (pgliteExists(pglitePath)) {
123118
123772
  const migrateSpinner = p.spinner();
123119
123773
  migrateSpinner.start("Migrating...");
@@ -123140,19 +123794,19 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123140
123794
  }
123141
123795
  let isReinstall = false;
123142
123796
  p.log.step("Checking existing configuration...");
123143
- const configDir = join29(homedir11(), ".config", "opencode");
123144
- const pluginDir = join29(configDir, "plugin");
123145
- const commandDir = join29(configDir, "command");
123146
- const agentDir = join29(configDir, "agent");
123147
- const pluginPath = join29(pluginDir, "swarm.ts");
123148
- const commandPath = join29(commandDir, "swarm.md");
123149
- const plannerAgentPath = join29(agentDir, "swarm-planner.md");
123150
- const workerAgentPath = join29(agentDir, "swarm-worker.md");
123151
- const researcherAgentPath = join29(agentDir, "swarm-researcher.md");
123152
- const swarmAgentDir = join29(agentDir, "swarm");
123153
- const legacyPlannerPath = join29(swarmAgentDir, "planner.md");
123154
- const legacyWorkerPath = join29(swarmAgentDir, "worker.md");
123155
- 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");
123156
123810
  const existingFiles = [
123157
123811
  pluginPath,
123158
123812
  commandPath,
@@ -123386,7 +124040,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123386
124040
  p.log.message(dim(" No legacy .beads directory found"));
123387
124041
  }
123388
124042
  p.log.step("Checking for legacy MCP servers...");
123389
- const opencodeConfigPath = join29(configDir, "config.json");
124043
+ const opencodeConfigPath = join28(configDir, "config.json");
123390
124044
  if (existsSync20(opencodeConfigPath)) {
123391
124045
  try {
123392
124046
  const opencodeConfig = JSON.parse(readFileSync16(opencodeConfigPath, "utf-8"));
@@ -123418,6 +124072,66 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123418
124072
  } else {
123419
124073
  p.log.message(dim(" No OpenCode config found (skipping MCP check)"));
123420
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
+ }
123421
124135
  const DEFAULT_COORDINATOR = "anthropic/claude-opus-4-5";
123422
124136
  const DEFAULT_WORKER = "anthropic/claude-sonnet-4-5";
123423
124137
  const DEFAULT_LITE = "anthropic/claude-haiku-4-5";
@@ -123565,8 +124279,8 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123565
124279
  p.log.message(dim(` Lite: ${liteModel}`));
123566
124280
  p.log.step("Setting up OpenCode integration...");
123567
124281
  const stats = { created: 0, updated: 0, unchanged: 0 };
123568
- const legacySkillsDir = join29(configDir, "skills");
123569
- const skillsDir = join29(configDir, "skill");
124282
+ const legacySkillsDir = join28(configDir, "skills");
124283
+ const skillsDir = join28(configDir, "skill");
123570
124284
  if (existsSync20(legacySkillsDir) && !existsSync20(skillsDir)) {
123571
124285
  p.log.step("Migrating skills directory...");
123572
124286
  try {
@@ -123600,7 +124314,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123600
124314
  } catch {}
123601
124315
  }
123602
124316
  p.log.message(dim(` Skills directory: ${skillsDir}`));
123603
- const bundledSkillsPath = join29(__dirname2, "..", "global-skills");
124317
+ const bundledSkillsPath = join28(__dirname2, "..", "global-skills");
123604
124318
  const bundledSkills = listDirectoryNames(bundledSkillsPath);
123605
124319
  if (existsSync20(bundledSkillsPath)) {
123606
124320
  if (bundledSkills.length > 0) {
@@ -123609,7 +124323,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123609
124323
  }
123610
124324
  if (bundledSkills.length > 0) {
123611
124325
  const globalSkills = listDirectoryNames(skillsDir);
123612
- 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)));
123613
124327
  const missingBundled = bundledSkills.filter((name) => !globalSkills.includes(name));
123614
124328
  if (missingBundled.length > 0 || managedBundled.length > 0) {
123615
124329
  {
@@ -123639,7 +124353,7 @@ async function setup(forceReinstall = false, nonInteractive = false) {
123639
124353
  }
123640
124354
  }
123641
124355
  }
123642
- const agentsPath = join29(configDir, "AGENTS.md");
124356
+ const agentsPath = join28(configDir, "AGENTS.md");
123643
124357
  if (existsSync20(agentsPath)) {
123644
124358
  {
123645
124359
  const s2 = p.spinner();
@@ -123806,14 +124520,14 @@ async function version4() {
123806
124520
  showUpdateNotification(updateInfo3);
123807
124521
  }
123808
124522
  function config4() {
123809
- const configDir = join29(homedir11(), ".config", "opencode");
123810
- const pluginPath = join29(configDir, "plugin", "swarm.ts");
123811
- const commandPath = join29(configDir, "command", "swarm.md");
123812
- const agentDir = join29(configDir, "agent");
123813
- const plannerAgentPath = join29(agentDir, "swarm-planner.md");
123814
- const workerAgentPath = join29(agentDir, "swarm-worker.md");
123815
- const researcherAgentPath = join29(agentDir, "swarm-researcher.md");
123816
- 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");
123817
124531
  console.log(yellow(BANNER));
123818
124532
  console.log(dim(" " + TAGLINE + " v" + VERSION));
123819
124533
  console.log();
@@ -123856,7 +124570,7 @@ function config4() {
123856
124570
  console.log(` ${dim(".claude/skills/")}`);
123857
124571
  console.log(` ${dim("skill/")}`);
123858
124572
  console.log();
123859
- const bundledSkillsPath = join29(__dirname2, "..", "global-skills");
124573
+ const bundledSkillsPath = join28(__dirname2, "..", "global-skills");
123860
124574
  if (existsSync20(bundledSkillsPath)) {
123861
124575
  try {
123862
124576
  const { readdirSync: readdirSync3 } = __require("fs");
@@ -124285,6 +124999,7 @@ ${cyan("Stats & History:")}
124285
124999
  swarm stats Show swarm health metrics powered by swarm-insights (last 7 days)
124286
125000
  swarm stats --since 24h Show stats for custom time period
124287
125001
  swarm stats --regressions Show eval regressions (>10% score drops)
125002
+ swarm stats --rejections Show rejection reason analytics
124288
125003
  swarm stats --json Output as JSON for scripting
124289
125004
  swarm o11y Show observability health dashboard (hook coverage, events, sessions)
124290
125005
  swarm o11y --since 7d Custom time period for event stats (default: 7 days)
@@ -124426,7 +125141,7 @@ async function listTools() {
124426
125141
  }
124427
125142
  async function agents(nonInteractive = false) {
124428
125143
  const home = process.env.HOME || process.env.USERPROFILE || "~";
124429
- const agentsPath = join29(home, ".config", "opencode", "AGENTS.md");
125144
+ const agentsPath = join28(home, ".config", "opencode", "AGENTS.md");
124430
125145
  p.intro(yellow(BANNER));
124431
125146
  if (!existsSync20(agentsPath)) {
124432
125147
  p.log.warn("No AGENTS.md found at " + agentsPath);
@@ -124500,9 +125215,9 @@ async function migrate() {
124500
125215
  p.intro("swarm migrate v" + VERSION);
124501
125216
  const projectPath = process.cwd();
124502
125217
  const tempDirName = getLibSQLProjectTempDirName(projectPath);
124503
- const tempDir = join29(tmpdir3(), tempDirName);
124504
- const pglitePath = join29(tempDir, "streams");
124505
- 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");
124506
125221
  if (!pgliteExists(pglitePath)) {
124507
125222
  p.log.success("No PGlite database found - nothing to migrate!");
124508
125223
  p.outro("Done");
@@ -124611,7 +125326,7 @@ function listSessionFiles(dir) {
124611
125326
  const files = readdirSync2(dir).filter((f) => f.endsWith(".jsonl"));
124612
125327
  const sessions = [];
124613
125328
  for (const file4 of files) {
124614
- const filePath = join29(dir, file4);
125329
+ const filePath = join28(dir, file4);
124615
125330
  try {
124616
125331
  const events = parseSessionFile(filePath);
124617
125332
  if (events.length === 0)
@@ -124661,7 +125376,7 @@ function formatEvent(event, useColor = true) {
124661
125376
  }
124662
125377
  async function logSessions() {
124663
125378
  const args3 = process.argv.slice(4);
124664
- const sessionsDir = join29(homedir11(), ".config", "swarm-tools", "sessions");
125379
+ const sessionsDir = join28(homedir10(), ".config", "swarm-tools", "sessions");
124665
125380
  let sessionId = null;
124666
125381
  let latest = false;
124667
125382
  let jsonOutput = false;
@@ -124865,7 +125580,7 @@ function readLogFiles(dir) {
124865
125580
  if (!existsSync20(dir))
124866
125581
  return [];
124867
125582
  const allFiles = readdirSync2(dir);
124868
- 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));
124869
125584
  const entries2 = [];
124870
125585
  for (const file4 of logFiles) {
124871
125586
  try {
@@ -125050,7 +125765,7 @@ async function logs() {
125050
125765
  moduleFilter = arg;
125051
125766
  }
125052
125767
  }
125053
- const logsDir = join29(homedir11(), ".config", "swarm-tools", "logs");
125768
+ const logsDir = join28(homedir10(), ".config", "swarm-tools", "logs");
125054
125769
  if (!existsSync20(logsDir)) {
125055
125770
  if (!jsonOutput) {
125056
125771
  p.log.warn("No logs directory found");
@@ -125088,7 +125803,7 @@ async function logs() {
125088
125803
  return;
125089
125804
  const files = readdirSync2(logsDir).filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f));
125090
125805
  for (const file4 of files) {
125091
- const filePath = join29(logsDir, file4);
125806
+ const filePath = join28(logsDir, file4);
125092
125807
  try {
125093
125808
  const stats = statSync2(filePath);
125094
125809
  filePositions.set(filePath, stats.size);
@@ -125126,7 +125841,7 @@ async function logs() {
125126
125841
  return;
125127
125842
  const files = readdirSync2(logsDir).filter((f) => /\.\d+log$/.test(f) || /\.log$/.test(f));
125128
125843
  for (const file4 of files) {
125129
- const filePath = join29(logsDir, file4);
125844
+ const filePath = join28(logsDir, file4);
125130
125845
  const newLines = readNewLines(filePath);
125131
125846
  for (const line of newLines) {
125132
125847
  const parsed = parseLogLine(line, filePath);
@@ -125177,7 +125892,112 @@ async function logs() {
125177
125892
  console.log();
125178
125893
  }
125179
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
+ }
125180
125995
  async function db() {
125996
+ const args3 = process.argv.slice(3);
125997
+ if (args3[0] === "repair") {
125998
+ await dbRepair();
125999
+ return;
126000
+ }
125181
126001
  const projectPath = process.cwd();
125182
126002
  const projectName = basename3(projectPath);
125183
126003
  const hash5 = hashLibSQLProjectPath(projectPath);
@@ -125238,7 +126058,7 @@ async function db() {
125238
126058
  console.log(dim(" Will be created on first use"));
125239
126059
  }
125240
126060
  console.log();
125241
- const pglitePath = join29(dbDir, "streams");
126061
+ const pglitePath = join28(dbDir, "streams");
125242
126062
  if (existsSync20(pglitePath)) {
125243
126063
  console.log(` \x1B[33m!\x1B[0m Legacy PGLite directory exists`);
125244
126064
  console.log(dim(` ${pglitePath}`));
@@ -125349,6 +126169,7 @@ async function stats() {
125349
126169
  let period = "7d";
125350
126170
  let format5 = "text";
125351
126171
  let showRegressions = false;
126172
+ let showRejections = false;
125352
126173
  for (let i = 0;i < args3.length; i++) {
125353
126174
  if (args3[i] === "--since" || args3[i] === "-s") {
125354
126175
  period = args3[i + 1] || "7d";
@@ -125357,6 +126178,8 @@ async function stats() {
125357
126178
  format5 = "json";
125358
126179
  } else if (args3[i] === "--regressions") {
125359
126180
  showRegressions = true;
126181
+ } else if (args3[i] === "--rejections") {
126182
+ showRejections = true;
125360
126183
  }
125361
126184
  }
125362
126185
  try {
@@ -125385,21 +126208,21 @@ async function stats() {
125385
126208
  strategy: row.strategy,
125386
126209
  success: row.success === "true"
125387
126210
  })));
125388
- const sessionsPath = join29(homedir11(), ".config", "swarm-tools", "sessions");
126211
+ const sessionsPath = join28(homedir10(), ".config", "swarm-tools", "sessions");
125389
126212
  let coordinatorStats = {
125390
126213
  violationRate: 0,
125391
126214
  spawnEfficiency: 0,
125392
126215
  reviewThoroughness: 0
125393
126216
  };
125394
126217
  if (existsSync20(sessionsPath)) {
125395
- 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);
125396
126219
  let totalViolations = 0;
125397
126220
  let totalSpawns = 0;
125398
126221
  let totalReviews = 0;
125399
126222
  let totalSwarms = 0;
125400
126223
  for (const file4 of sessionFiles) {
125401
126224
  try {
125402
- const content = readFileSync16(join29(sessionsPath, file4), "utf-8");
126225
+ const content = readFileSync16(join28(sessionsPath, file4), "utf-8");
125403
126226
  const lines = content.trim().split(`
125404
126227
  `);
125405
126228
  let violations = 0;
@@ -125464,6 +126287,34 @@ async function stats() {
125464
126287
  `);
125465
126288
  }
125466
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
+ }
125467
126318
  } else {
125468
126319
  if (format5 === "json") {
125469
126320
  console.log(JSON.stringify(stats2, null, 2));
@@ -125689,7 +126540,7 @@ async function evalRun() {
125689
126540
  }
125690
126541
  }
125691
126542
  if (ciMode) {
125692
- const resultsPath = join29(projectPath, ".hive", "eval-results.json");
126543
+ const resultsPath = join28(projectPath, ".hive", "eval-results.json");
125693
126544
  ensureHiveDirectory(projectPath);
125694
126545
  writeFileSync7(resultsPath, JSON.stringify(results, null, 2));
125695
126546
  console.log(`
@@ -125877,11 +126728,11 @@ async function memory() {
125877
126728
  try {
125878
126729
  const { getDb: getDb2 } = await import("swarm-mail");
125879
126730
  const tempDirName = getLibSQLProjectTempDirName(projectPath);
125880
- const tempDir = join29(tmpdir3(), tempDirName);
126731
+ const tempDir = join28(tmpdir3(), tempDirName);
125881
126732
  if (!existsSync20(tempDir)) {
125882
126733
  mkdirSync11(tempDir, { recursive: true });
125883
126734
  }
125884
- const dbPath = join29(tempDir, "streams.db");
126735
+ const dbPath = join28(tempDir, "streams.db");
125885
126736
  const dbUrl = `file://${dbPath}`;
125886
126737
  const db2 = await getDb2(dbUrl);
125887
126738
  const { createMemoryAdapter: createMemoryAdapter3 } = await import("swarm-mail");