opencode-swarm 6.60.0 → 6.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -297,7 +297,8 @@ var init_constants = __esm(() => {
297
297
  "imports",
298
298
  "retrieve_summary",
299
299
  "symbols",
300
- "knowledge_recall"
300
+ "knowledge_recall",
301
+ "req_coverage"
301
302
  ],
302
303
  critic_drift_verifier: [
303
304
  "complexity_hotspots",
@@ -14990,7 +14991,8 @@ var init_schema = __esm(() => {
14990
14991
  ]),
14991
14992
  require_reviewer_test_engineer: exports_external.boolean().default(true)
14992
14993
  }).optional(),
14993
- profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional()
14994
+ profiles: exports_external.record(exports_external.string(), GuardrailsProfileSchema).optional(),
14995
+ block_destructive_commands: exports_external.boolean().default(true)
14994
14996
  });
14995
14997
  WatchdogConfigSchema = exports_external.object({
14996
14998
  scope_guard: exports_external.boolean().default(true),
@@ -30654,7 +30656,7 @@ var init_dist = __esm(() => {
30654
30656
 
30655
30657
  // src/tools/create-tool.ts
30656
30658
  function classifyToolError(error93) {
30657
- const msg = (error93 instanceof Error ? error93.message : String(error93)).toLowerCase();
30659
+ const msg = (error93 instanceof Error ? error93.message ?? "" : String(error93)).toLowerCase();
30658
30660
  if (msg.includes("not registered") || msg.includes("unknown tool"))
30659
30661
  return "not_registered";
30660
30662
  if (msg.includes("not whitelisted") || msg.includes("not allowed"))
@@ -34920,6 +34922,10 @@ async function discoverBuildCommands(workingDir, options) {
34920
34922
  const skipped = [...profileSkipped];
34921
34923
  for (const ecosystem of ECOSYSTEMS) {
34922
34924
  if (coveredEcosystems.has(ecosystem.ecosystem)) {
34925
+ skipped.push({
34926
+ ecosystem: ecosystem.ecosystem,
34927
+ reason: `Covered by profile detection`
34928
+ });
34923
34929
  continue;
34924
34930
  }
34925
34931
  if (!checkToolchain(ecosystem.toolchainCommands)) {
@@ -41322,7 +41328,7 @@ async function scanDocIndex(directory) {
41322
41328
  try {
41323
41329
  const fullPath = path46.join(directory, file3.path);
41324
41330
  const stat2 = fs34.statSync(fullPath);
41325
- if (stat2.mtimeMs > new Date(existingManifest.scanned_at).getTime()) {
41331
+ if (stat2.mtimeMs > file3.mtime) {
41326
41332
  cacheValid = false;
41327
41333
  break;
41328
41334
  }
@@ -41619,6 +41625,98 @@ var init_doc_scan = __esm(() => {
41619
41625
  });
41620
41626
  });
41621
41627
 
41628
+ // src/tools/knowledge-recall.ts
41629
+ var exports_knowledge_recall = {};
41630
+ __export(exports_knowledge_recall, {
41631
+ knowledge_recall: () => knowledge_recall
41632
+ });
41633
+ var knowledge_recall;
41634
+ var init_knowledge_recall = __esm(() => {
41635
+ init_dist();
41636
+ init_knowledge_store();
41637
+ init_create_tool();
41638
+ knowledge_recall = createSwarmTool({
41639
+ description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
41640
+ args: {
41641
+ query: tool.schema.string().min(3).describe("Natural language search query"),
41642
+ top_n: tool.schema.number().int().min(1).max(20).optional().describe("Maximum results to return (default: 5)"),
41643
+ tier: tool.schema.enum(["all", "swarm", "hive"]).optional().describe("Knowledge tier to search (default: 'all')")
41644
+ },
41645
+ execute: async (args2, directory) => {
41646
+ let queryInput;
41647
+ let topNInput;
41648
+ let tierInput;
41649
+ try {
41650
+ if (args2 && typeof args2 === "object") {
41651
+ const obj = args2;
41652
+ queryInput = obj.query;
41653
+ topNInput = obj.top_n;
41654
+ tierInput = obj.tier;
41655
+ }
41656
+ } catch {}
41657
+ if (typeof queryInput !== "string" || queryInput.length < 3) {
41658
+ return JSON.stringify({
41659
+ results: [],
41660
+ total: 0,
41661
+ error: "query must be a string with at least 3 characters"
41662
+ });
41663
+ }
41664
+ let topN = 5;
41665
+ if (topNInput !== undefined) {
41666
+ if (typeof topNInput === "number" && Number.isInteger(topNInput)) {
41667
+ topN = Math.max(1, Math.min(20, topNInput));
41668
+ }
41669
+ }
41670
+ let tier = "all";
41671
+ if (tierInput !== undefined && typeof tierInput === "string") {
41672
+ if (tierInput === "swarm" || tierInput === "hive") {
41673
+ tier = tierInput;
41674
+ }
41675
+ }
41676
+ const swarmPath = resolveSwarmKnowledgePath(directory);
41677
+ const hivePath = resolveHiveKnowledgePath();
41678
+ const [swarmEntries, hiveEntries] = await Promise.all([
41679
+ readKnowledge(swarmPath),
41680
+ readKnowledge(hivePath)
41681
+ ]);
41682
+ let entries = [];
41683
+ if (tier === "all" || tier === "swarm") {
41684
+ entries = entries.concat(swarmEntries);
41685
+ }
41686
+ if (tier === "all" || tier === "hive") {
41687
+ entries = entries.concat(hiveEntries);
41688
+ }
41689
+ if (entries.length === 0) {
41690
+ const result2 = { results: [], total: 0 };
41691
+ return JSON.stringify(result2);
41692
+ }
41693
+ const normalizedQuery = normalize2(queryInput);
41694
+ const queryBigrams = wordBigrams(normalizedQuery);
41695
+ const scoredEntries = entries.map((entry) => {
41696
+ const entryText = `${entry.lesson} ${entry.tags.join(" ")} ${entry.category}`;
41697
+ const entryBigrams = wordBigrams(entryText);
41698
+ const textScore = jaccardBigram(queryBigrams, entryBigrams);
41699
+ const boost = entry.status === "established" ? 0.1 : entry.status === "promoted" ? 0.05 : 0;
41700
+ const finalScore = textScore + boost;
41701
+ return {
41702
+ id: entry.id,
41703
+ confidence: entry.confidence,
41704
+ category: entry.category,
41705
+ lesson: entry.lesson,
41706
+ score: finalScore
41707
+ };
41708
+ });
41709
+ scoredEntries.sort((a, b) => b.score - a.score);
41710
+ const topResults = scoredEntries.slice(0, topN);
41711
+ const result = {
41712
+ results: topResults,
41713
+ total: topResults.length
41714
+ };
41715
+ return JSON.stringify(result);
41716
+ }
41717
+ });
41718
+ });
41719
+
41622
41720
  // src/environment/prompt-renderer.ts
41623
41721
  var exports_prompt_renderer = {};
41624
41722
  __export(exports_prompt_renderer, {
@@ -45263,7 +45361,7 @@ ${warnings.map((w) => ` - ${w}`).join(`
45263
45361
  `)}` : "";
45264
45362
  const cautionMessage = `
45265
45363
 
45266
- \u26A0\uFE0F Caution: Spec drift was acknowledged \u2014 verify that the implementation still matches the spec before proceeding.`;
45364
+ \u26A0\uFE0F Warning: Spec drift was acknowledged \u2014 verify that the implementation still matches the spec before proceeding.`;
45267
45365
  return baseMessage + warningMessage + cautionMessage;
45268
45366
  }
45269
45367
 
@@ -48051,42 +48149,73 @@ init_utils2();
48051
48149
  var DEFAULT_CURATOR_LLM_TIMEOUT_MS = 300000;
48052
48150
  function parseKnowledgeRecommendations(llmOutput) {
48053
48151
  const recommendations = [];
48054
- const section = llmOutput.match(/OBSERVATIONS:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
48055
- if (!section)
48056
- return recommendations;
48057
- const lines = section[1].split(`
48152
+ const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
48153
+ const obsSection = llmOutput.match(/OBSERVATIONS:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
48154
+ if (obsSection) {
48155
+ const lines = obsSection[1].split(`
48058
48156
  `);
48059
- for (const line of lines) {
48060
- const trimmed = line.trim();
48061
- if (!trimmed.startsWith("-"))
48062
- continue;
48063
- const match = trimmed.match(/^-\s+entry\s+(\S+)\s+\(([^)]+)\):\s+(.+)$/i);
48064
- if (!match)
48065
- continue;
48066
- const uuid8 = match[1];
48067
- const parenthetical = match[2];
48068
- const text = match[3].trim().replace(/\s+\([^)]+\)$/, "");
48069
- const UUID_V4 = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
48070
- const entryId = uuid8 === "new" || !UUID_V4.test(uuid8) ? undefined : uuid8;
48071
- let action = "rewrite";
48072
- const lowerParenthetical = parenthetical.toLowerCase();
48073
- if (lowerParenthetical.includes("suggests boost confidence") || lowerParenthetical.includes("mark hive_eligible") || lowerParenthetical.includes("appears high-confidence")) {
48074
- action = "promote";
48075
- } else if (lowerParenthetical.includes("suggests archive") || lowerParenthetical.includes("appears stale")) {
48076
- action = "archive";
48077
- } else if (lowerParenthetical.includes("contradicts project state")) {
48078
- action = "flag_contradiction";
48079
- } else if (lowerParenthetical.includes("suggests rewrite") || lowerParenthetical.includes("could be tighter")) {
48080
- action = "rewrite";
48081
- } else if (lowerParenthetical.includes("new candidate")) {
48082
- action = "promote";
48083
- }
48084
- recommendations.push({
48085
- action,
48086
- entry_id: entryId,
48087
- lesson: text,
48088
- reason: text
48089
- });
48157
+ for (const line of lines) {
48158
+ const trimmed = line.trim();
48159
+ if (!trimmed.startsWith("-"))
48160
+ continue;
48161
+ const match = trimmed.match(/^-\s+entry\s+(\S+)\s+\(([^)]+)\):\s+(.+)$/i);
48162
+ if (!match)
48163
+ continue;
48164
+ const uuid8 = match[1];
48165
+ const parenthetical = match[2];
48166
+ const text = match[3].trim().replace(/\s+\([^)]+\)$/, "");
48167
+ const entryId = uuid8 === "new" || !UUID_V4.test(uuid8) ? undefined : uuid8;
48168
+ let action = "rewrite";
48169
+ const lowerParenthetical = parenthetical.toLowerCase();
48170
+ if (lowerParenthetical.includes("suggests boost confidence") || lowerParenthetical.includes("mark hive_eligible") || lowerParenthetical.includes("appears high-confidence")) {
48171
+ action = "promote";
48172
+ } else if (lowerParenthetical.includes("suggests archive") || lowerParenthetical.includes("appears stale")) {
48173
+ action = "archive";
48174
+ } else if (lowerParenthetical.includes("contradicts project state")) {
48175
+ action = "flag_contradiction";
48176
+ } else if (lowerParenthetical.includes("suggests rewrite") || lowerParenthetical.includes("could be tighter")) {
48177
+ action = "rewrite";
48178
+ } else if (lowerParenthetical.includes("new candidate")) {
48179
+ action = "promote";
48180
+ }
48181
+ recommendations.push({
48182
+ action,
48183
+ entry_id: entryId,
48184
+ lesson: text,
48185
+ reason: text
48186
+ });
48187
+ }
48188
+ }
48189
+ const updatesSection = llmOutput.match(/KNOWLEDGE_UPDATES:\s*\n([\s\S]*?)(?:\n\n|\n[A-Z_]+:|$)/);
48190
+ if (updatesSection) {
48191
+ const validActions = new Set([
48192
+ "promote",
48193
+ "archive",
48194
+ "rewrite",
48195
+ "flag_contradiction"
48196
+ ]);
48197
+ const lines = updatesSection[1].split(`
48198
+ `);
48199
+ for (const line of lines) {
48200
+ const trimmed = line.trim();
48201
+ if (!trimmed.startsWith("-"))
48202
+ continue;
48203
+ const match = trimmed.match(/^-\s+(\S+)\s+(\S+):\s+(.+)$/);
48204
+ if (!match)
48205
+ continue;
48206
+ const action = match[1].toLowerCase();
48207
+ if (!validActions.has(action))
48208
+ continue;
48209
+ const id = match[2];
48210
+ const text = match[3].trim();
48211
+ const entryId = UUID_V4.test(id) ? id : undefined;
48212
+ recommendations.push({
48213
+ action,
48214
+ entry_id: entryId,
48215
+ lesson: text,
48216
+ reason: text
48217
+ });
48218
+ }
48090
48219
  }
48091
48220
  return recommendations;
48092
48221
  }
@@ -52920,7 +53049,8 @@ Run \`/swarm evidence ${result.task_id ?? "unknown"}\` to view it, or \`/swarm s
52920
53049
  var COMMAND_REGISTRY = {
52921
53050
  "acknowledge-spec-drift": {
52922
53051
  handler: (ctx) => handleAcknowledgeSpecDriftCommand(ctx.directory, ctx.args),
52923
- description: "Acknowledge that the spec has drifted from the plan and suppress further warnings"
53052
+ description: "Acknowledge that the spec has drifted from the plan and suppress further warnings",
53053
+ args: ""
52924
53054
  },
52925
53055
  status: {
52926
53056
  handler: (ctx) => handleStatusCommand(ctx.directory, ctx.agents),
@@ -52966,112 +53096,156 @@ var COMMAND_REGISTRY = {
52966
53096
  },
52967
53097
  "sync-plan": {
52968
53098
  handler: (ctx) => handleSyncPlanCommand(ctx.directory, ctx.args),
52969
- description: "Ensure plan.json and plan.md are synced"
53099
+ description: "Ensure plan.json and plan.md are synced",
53100
+ args: ""
52970
53101
  },
52971
53102
  benchmark: {
52972
53103
  handler: (ctx) => handleBenchmarkCommand(ctx.directory, ctx.args),
52973
- description: "Show performance metrics [--cumulative] [--ci-gate]"
53104
+ description: "Show performance metrics [--cumulative] [--ci-gate]",
53105
+ args: "--cumulative, --ci-gate"
52974
53106
  },
52975
53107
  export: {
52976
53108
  handler: (ctx) => handleExportCommand(ctx.directory, ctx.args),
52977
- description: "Export plan and context as JSON"
53109
+ description: "Export plan and context as JSON",
53110
+ args: "",
53111
+ details: "Exports the current plan and context as JSON to stdout. Useful for piping to external tools or debugging swarm state."
52978
53112
  },
52979
53113
  evidence: {
52980
53114
  handler: (ctx) => handleEvidenceCommand(ctx.directory, ctx.args),
52981
- description: "Show evidence bundles [taskId]"
53115
+ description: "Show evidence bundles [taskId]",
53116
+ args: "<taskId>",
53117
+ details: 'Displays review results, test verdicts, and other evidence bundles for the given task ID (e.g., "2.1").'
52982
53118
  },
52983
53119
  "evidence summary": {
52984
53120
  handler: (ctx) => handleEvidenceSummaryCommand(ctx.directory),
52985
53121
  description: "Generate evidence summary with completion ratio and blockers",
52986
- subcommandOf: "evidence"
53122
+ subcommandOf: "evidence",
53123
+ args: "",
53124
+ details: "Generates a summary showing completion ratio across all tasks, lists blockers, and identifies missing evidence."
52987
53125
  },
52988
53126
  "evidence-summary": {
52989
53127
  handler: (ctx) => handleEvidenceSummaryCommand(ctx.directory),
52990
53128
  description: "Generate evidence summary with completion ratio and blockers",
52991
- subcommandOf: "evidence"
53129
+ subcommandOf: "evidence",
53130
+ args: "",
53131
+ details: "Generates a summary showing completion ratio across all tasks, lists blockers, and identifies missing evidence."
52992
53132
  },
52993
53133
  archive: {
52994
53134
  handler: (ctx) => handleArchiveCommand(ctx.directory, ctx.args),
52995
- description: "Archive old evidence bundles [--dry-run]"
53135
+ description: "Archive old evidence bundles [--dry-run]",
53136
+ details: "Archives evidence bundles older than max_age_days (config, default 90) or beyond max_bundles cap (config, default 1000). --dry-run previews which bundles would be archived without deleting them. Applies two-tier retention: age-based first, then count-based on oldest remaining.",
53137
+ args: "--dry-run"
52996
53138
  },
52997
53139
  curate: {
52998
53140
  handler: (ctx) => handleCurateCommand(ctx.directory, ctx.args),
52999
- description: "Run knowledge curation and hive promotion review"
53141
+ description: "Run knowledge curation and hive promotion review",
53142
+ args: ""
53000
53143
  },
53001
53144
  "dark-matter": {
53002
53145
  handler: (ctx) => handleDarkMatterCommand(ctx.directory, ctx.args),
53003
- description: "Detect hidden file couplings via co-change NPMI analysis"
53146
+ description: "Detect hidden file couplings via co-change NPMI analysis",
53147
+ args: "--threshold <number>, --min-commits <number>"
53004
53148
  },
53005
53149
  close: {
53006
53150
  handler: (ctx) => handleCloseCommand(ctx.directory, ctx.args),
53007
- description: "Use /swarm close to close the swarm project and archive evidence"
53151
+ description: "Use /swarm close to close the swarm project and archive evidence",
53152
+ details: "Idempotent 4-stage terminal finalization: (1) finalize writes retrospectives for in-progress phases, (2) archive creates timestamped bundle of swarm artifacts and evidence, (3) clean removes active-state files for a clean slate, (4) align performs safe git ff-only to main. Resets agent sessions and delegation chains. Reads .swarm/close-lessons.md for explicit lessons and runs curation.",
53153
+ args: "--prune-branches"
53008
53154
  },
53009
53155
  simulate: {
53010
53156
  handler: (ctx) => handleSimulateCommand(ctx.directory, ctx.args),
53011
- description: "Dry-run impact analysis of proposed changes [--target <glob>]"
53157
+ description: "Dry-run hidden coupling analysis with configurable thresholds",
53158
+ args: "--threshold <number>, --min-commits <number>"
53012
53159
  },
53013
53160
  analyze: {
53014
53161
  handler: (ctx) => handleAnalyzeCommand(ctx.directory, ctx.args),
53015
- description: "Analyze spec.md vs plan.md for requirement coverage gaps"
53162
+ description: "Analyze spec.md vs plan.md for requirement coverage gaps",
53163
+ args: ""
53016
53164
  },
53017
53165
  clarify: {
53018
53166
  handler: (ctx) => handleClarifyCommand(ctx.directory, ctx.args),
53019
- description: "Clarify and refine an existing feature specification"
53167
+ description: "Clarify and refine an existing feature specification",
53168
+ args: "[description-text]"
53020
53169
  },
53021
53170
  specify: {
53022
53171
  handler: (ctx) => handleSpecifyCommand(ctx.directory, ctx.args),
53023
- description: "Generate or import a feature specification [description]"
53172
+ description: "Generate or import a feature specification [description]",
53173
+ args: "[description-text]"
53024
53174
  },
53025
53175
  promote: {
53026
53176
  handler: (ctx) => handlePromoteCommand(ctx.directory, ctx.args),
53027
- description: "Manually promote lesson to hive knowledge"
53177
+ description: "Manually promote lesson to hive knowledge",
53178
+ details: "Promotes a lesson directly to hive knowledge (--category flag sets category) or references an existing swarm lesson by ID (--from-swarm). Validates lesson text before promotion. Either direct text or --from-swarm ID is required.",
53179
+ args: "--category <category>, --from-swarm <lesson-id>, <lesson-text>"
53028
53180
  },
53029
53181
  reset: {
53030
53182
  handler: (ctx) => handleResetCommand(ctx.directory, ctx.args),
53031
- description: "Clear swarm state files [--confirm]"
53183
+ description: "Clear swarm state files [--confirm]",
53184
+ details: "DELETES plan.md, context.md, and summaries/ directory from .swarm/. Stops background automation and clears in-memory queues. SAFETY: requires --confirm flag \u2014 without it, displays a warning and tips to export first.",
53185
+ args: "--confirm (required)"
53032
53186
  },
53033
53187
  "reset-session": {
53034
53188
  handler: (ctx) => handleResetSessionCommand(ctx.directory, ctx.args),
53035
- description: "Clear session state while preserving plan, evidence, and knowledge"
53189
+ description: "Clear session state while preserving plan, evidence, and knowledge",
53190
+ details: "Deletes only .swarm/session/state.json and any other session files. Clears in-memory agent sessions and delegation chains. Preserves plan, evidence, and knowledge for cross-session continuity.",
53191
+ args: ""
53036
53192
  },
53037
53193
  rollback: {
53038
53194
  handler: (ctx) => handleRollbackCommand(ctx.directory, ctx.args),
53039
- description: "Restore swarm state to a checkpoint <phase>"
53195
+ description: "Restore swarm state to a checkpoint <phase>",
53196
+ details: "Restores .swarm/ state by directly overwriting files from a checkpoint directory (checkpoints/phase-<N>). Writes rollback event to events.jsonl. Without phase argument, lists available checkpoints. Partial failures are reported but processing continues.",
53197
+ args: "<phase-number>"
53040
53198
  },
53041
53199
  retrieve: {
53042
53200
  handler: (ctx) => handleRetrieveCommand(ctx.directory, ctx.args),
53043
- description: "Retrieve full output from a summary <id>"
53201
+ description: "Retrieve full output from a summary <id>",
53202
+ args: "<summary-id>",
53203
+ details: "Loads the full tool output that was previously summarized (referenced by IDs like S1, S2). Use when you need the complete output instead of the truncated summary."
53044
53204
  },
53045
53205
  handoff: {
53046
53206
  handler: (ctx) => handleHandoffCommand(ctx.directory, ctx.args),
53047
- description: "Prepare state for clean model switch (new session)"
53207
+ description: "Prepare state for clean model switch (new session)",
53208
+ args: "",
53209
+ details: "Generates handoff.md with full session state snapshot, including plan progress, recent decisions, and agent delegation history. Prepended to the next session prompt for seamless model switches."
53048
53210
  },
53049
53211
  turbo: {
53050
53212
  handler: (ctx) => handleTurboCommand(ctx.directory, ctx.args, ctx.sessionID),
53051
- description: "Toggle Turbo Mode for the active session [on|off]"
53213
+ description: "Toggle Turbo Mode for the active session [on|off]",
53214
+ args: "on, off",
53215
+ details: 'Toggles Turbo Mode which skips non-critical QA gates for faster iteration. When enabled, the architect can proceed without waiting for all automated checks. Session-scoped \u2014 resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.'
53052
53216
  },
53053
53217
  "full-auto": {
53054
53218
  handler: (ctx) => handleFullAutoCommand(ctx.directory, ctx.args, ctx.sessionID),
53055
- description: "Toggle Full-Auto Mode for the active session [on|off]"
53219
+ description: "Toggle Full-Auto Mode for the active session [on|off]",
53220
+ args: "on, off",
53221
+ details: 'Toggles Full-Auto Mode which enables autonomous execution without confirmation prompts. When enabled, the architect proceeds through implementation steps automatically. Session-scoped \u2014 resets on new session. Use "on" or "off" to set explicitly, or toggle with no argument.'
53056
53222
  },
53057
53223
  "write-retro": {
53058
53224
  handler: (ctx) => handleWriteRetroCommand(ctx.directory, ctx.args),
53059
- description: "Write a retrospective evidence bundle for a completed phase <json>"
53225
+ description: "Write a retrospective evidence bundle for a completed phase <json>",
53226
+ details: "Writes retrospective evidence bundle to .swarm/evidence/retro-{phase}/evidence.json. Required JSON: phase, summary, task_count, task_complexity, total_tool_calls, coder_revisions, reviewer_rejections, test_failures, security_findings, integration_issues. Optional: lessons_learned (max 5), top_rejection_reasons, task_id, metadata.",
53227
+ args: "<json: {phase, summary, task_count, task_complexity, ...}>"
53060
53228
  },
53061
53229
  "knowledge migrate": {
53062
53230
  handler: (ctx) => handleKnowledgeMigrateCommand(ctx.directory, ctx.args),
53063
53231
  description: "Migrate knowledge entries to the current format",
53064
- subcommandOf: "knowledge"
53232
+ subcommandOf: "knowledge",
53233
+ details: "One-time migration from .swarm/context.md SME cache to .swarm/knowledge.jsonl. Skips if sentinel file .swarm/.knowledge-migrated exists, if context.md is absent, or if context.md is empty. Reports entries migrated, dropped (validation/dedup), and total processed.",
53234
+ args: "<directory>"
53065
53235
  },
53066
53236
  "knowledge quarantine": {
53067
53237
  handler: (ctx) => handleKnowledgeQuarantineCommand(ctx.directory, ctx.args),
53068
53238
  description: "Move a knowledge entry to quarantine <id> [reason]",
53069
- subcommandOf: "knowledge"
53239
+ subcommandOf: "knowledge",
53240
+ details: 'Moves a knowledge entry to quarantine with optional reason string (defaults to "Quarantined via /swarm knowledge quarantine command"). Validates entry ID format (1-64 alphanumeric/hyphen/underscore). Quarantined entries are excluded from knowledge queries.',
53241
+ args: "<entry-id> [reason]"
53070
53242
  },
53071
53243
  "knowledge restore": {
53072
53244
  handler: (ctx) => handleKnowledgeRestoreCommand(ctx.directory, ctx.args),
53073
53245
  description: "Restore a quarantined knowledge entry <id>",
53074
- subcommandOf: "knowledge"
53246
+ subcommandOf: "knowledge",
53247
+ details: "Restores a quarantined knowledge entry back to the active knowledge store by ID. Validates entry ID format (1-64 alphanumeric/hyphen/underscore). Entry must currently be in quarantine state.",
53248
+ args: "<entry-id>"
53075
53249
  },
53076
53250
  knowledge: {
53077
53251
  handler: (ctx) => handleKnowledgeListCommand(ctx.directory, ctx.args),
@@ -53079,7 +53253,9 @@ var COMMAND_REGISTRY = {
53079
53253
  },
53080
53254
  checkpoint: {
53081
53255
  handler: (ctx) => handleCheckpointCommand(ctx.directory, ctx.args),
53082
- description: "Manage project checkpoints [save|restore|delete|list] <label>"
53256
+ description: "Manage project checkpoints [save|restore|delete|list] <label>",
53257
+ details: "save: creates named snapshot of current .swarm/ state. restore: soft-resets to checkpoint by overwriting current .swarm/ files. delete: removes named checkpoint. list: shows all checkpoints with timestamps. All subcommands require a label except list.",
53258
+ args: "<save|restore|delete|list> <label>"
53083
53259
  }
53084
53260
  };
53085
53261
  var VALID_COMMANDS = Object.keys(COMMAND_REGISTRY);
@@ -53429,8 +53605,8 @@ SECURITY_KEYWORDS: password, secret, token, credential, auth, login, encryption,
53429
53605
  {{AGENT_PREFIX}}designer - UI/UX design specs (scaffold generation for UI components \u2014 runs BEFORE coder on UI tasks)
53430
53606
 
53431
53607
  ## SLASH COMMANDS
53432
- Available commands via /swarm: {{SLASH_COMMANDS}}
53433
- Type /swarm (no arguments) for full help.
53608
+ {{SLASH_COMMANDS}}
53609
+ Commands above are documented with args and behavioral details. Run commands via /swarm <command> [args].
53434
53610
  Outside OpenCode, invoke any plugin command via: \`bunx opencode-swarm run <command> [args]\` (e.g. \`bunx opencode-swarm run knowledge migrate\`). Do not use \`bun -e\` or look for \`src/commands/\` \u2014 those paths are internal to the plugin source and do not exist in user project directories.
53435
53611
 
53436
53612
  SMEs advise only. Reviewer and critic review only. None of them write code.
@@ -54158,7 +54334,131 @@ function buildAvailableToolsList() {
54158
54334
  }).join(", ");
54159
54335
  }
54160
54336
  function buildSlashCommandsList() {
54161
- return Object.keys(COMMAND_REGISTRY).sort().join(", ") + ".";
54337
+ const SKIP_ALIASES = new Set(["config-doctor", "evidence-summary"]);
54338
+ const READ_ONLY_OBSERVATION = new Set([
54339
+ "status",
54340
+ "history",
54341
+ "agents",
54342
+ "config",
54343
+ "plan",
54344
+ "benchmark",
54345
+ "export",
54346
+ "retrieve"
54347
+ ]);
54348
+ const CATEGORY_ORDER = [
54349
+ "Session Lifecycle",
54350
+ "Planning",
54351
+ "Execution Modes",
54352
+ "Observation",
54353
+ "Knowledge",
54354
+ "State Management",
54355
+ "Diagnostics"
54356
+ ];
54357
+ const COMMANDS_BY_CATEGORY = {
54358
+ "Session Lifecycle": [
54359
+ "close",
54360
+ "reset",
54361
+ "reset-session",
54362
+ "handoff",
54363
+ "archive"
54364
+ ],
54365
+ Planning: [
54366
+ "specify",
54367
+ "clarify",
54368
+ "analyze",
54369
+ "plan",
54370
+ "sync-plan",
54371
+ "acknowledge-spec-drift"
54372
+ ],
54373
+ "Execution Modes": ["turbo", "full-auto"],
54374
+ Observation: [
54375
+ "status",
54376
+ "history",
54377
+ "agents",
54378
+ "config",
54379
+ "benchmark",
54380
+ "export",
54381
+ "evidence",
54382
+ "evidence summary",
54383
+ "retrieve"
54384
+ ],
54385
+ Knowledge: [
54386
+ "knowledge",
54387
+ "knowledge migrate",
54388
+ "knowledge quarantine",
54389
+ "knowledge restore",
54390
+ "promote",
54391
+ "curate"
54392
+ ],
54393
+ "State Management": ["checkpoint", "rollback", "write-retro"],
54394
+ Diagnostics: [
54395
+ "diagnose",
54396
+ "preflight",
54397
+ "doctor tools",
54398
+ "config doctor",
54399
+ "simulate",
54400
+ "dark-matter"
54401
+ ]
54402
+ };
54403
+ const lines = [];
54404
+ const subcommandMap = {};
54405
+ for (const [cmdName, cmdEntry] of Object.entries(COMMAND_REGISTRY)) {
54406
+ const entry = cmdEntry;
54407
+ if (entry.subcommandOf) {
54408
+ if (!subcommandMap[entry.subcommandOf]) {
54409
+ subcommandMap[entry.subcommandOf] = [];
54410
+ }
54411
+ subcommandMap[entry.subcommandOf].push(cmdName);
54412
+ }
54413
+ }
54414
+ const compoundsInValidCommands = new Set;
54415
+ for (const category of CATEGORY_ORDER) {
54416
+ lines.push(`**${category}**`);
54417
+ const commandNames = COMMANDS_BY_CATEGORY[category];
54418
+ for (const name2 of commandNames) {
54419
+ const entry = COMMAND_REGISTRY[name2];
54420
+ if (!entry)
54421
+ continue;
54422
+ if (SKIP_ALIASES.has(name2))
54423
+ continue;
54424
+ if (entry.subcommandOf && !VALID_COMMANDS.includes(name2))
54425
+ continue;
54426
+ lines.push(`- \`/swarm ${name2}\` \u2014 ${entry.description}`);
54427
+ if (entry.subcommandOf && VALID_COMMANDS.includes(name2)) {
54428
+ compoundsInValidCommands.add(name2);
54429
+ }
54430
+ if (READ_ONLY_OBSERVATION.has(name2))
54431
+ continue;
54432
+ if (entry.details) {
54433
+ lines.push(` ${entry.details}`);
54434
+ }
54435
+ if (entry.args) {
54436
+ lines.push(` Args: ${entry.args}`);
54437
+ }
54438
+ }
54439
+ for (const parent of commandNames) {
54440
+ const subs = subcommandMap[parent];
54441
+ if (!subs)
54442
+ continue;
54443
+ for (const subName of subs) {
54444
+ const subEntry = COMMAND_REGISTRY[subName];
54445
+ if (!subEntry)
54446
+ continue;
54447
+ if (compoundsInValidCommands.has(subName) || subEntry.subcommandOf && VALID_COMMANDS.includes(subName) || SKIP_ALIASES.has(subName)) {
54448
+ continue;
54449
+ }
54450
+ lines.push(` - \`/swarm ${subName}\` \u2014 ${subEntry.description}`);
54451
+ if (subEntry.details) {
54452
+ lines.push(` ${subEntry.details}`);
54453
+ }
54454
+ if (subEntry.args) {
54455
+ lines.push(` Args: ${subEntry.args}`);
54456
+ }
54457
+ }
54458
+ }
54459
+ }
54460
+ return lines.join(`
54461
+ `);
54162
54462
  }
54163
54463
  function createArchitectAgent(model, customPrompt, customAppendPrompt, adversarialTesting) {
54164
54464
  let prompt = ARCHITECT_PROMPT;
@@ -56198,7 +56498,7 @@ class PlanSyncWorker {
56198
56498
  const planJsonPath = path36.join(swarmDir, "plan.json");
56199
56499
  const markerPath = path36.join(swarmDir, ".plan-write-marker");
56200
56500
  const planStats = fs24.statSync(planJsonPath);
56201
- const planMtimeMs = planStats.mtimeMs;
56501
+ const planMtimeMs = Math.floor(planStats.mtimeMs);
56202
56502
  const markerContent = fs24.readFileSync(markerPath, "utf8");
56203
56503
  const marker = JSON.parse(markerContent);
56204
56504
  const markerTimestampMs = new Date(marker.timestamp).getTime();
@@ -56229,12 +56529,54 @@ init_status_artifact();
56229
56529
  init_trigger();
56230
56530
 
56231
56531
  // src/commands/index.ts
56232
- var HELP_TEXT = [
56233
- "## Swarm Commands",
56234
- "",
56235
- ...VALID_COMMANDS.filter((cmd) => !cmd.includes(" ")).map((cmd) => `- \`/swarm ${cmd}\` \u2014 ${COMMAND_REGISTRY[cmd].description}`)
56236
- ].join(`
56532
+ function buildHelpText() {
56533
+ const lines = ["## Swarm Commands", ""];
56534
+ const shownAsSubcommand = new Set;
56535
+ for (const cmd of VALID_COMMANDS) {
56536
+ if (cmd.includes(" ")) {
56537
+ const parent = cmd.split(" ")[0];
56538
+ if (VALID_COMMANDS.includes(parent)) {
56539
+ shownAsSubcommand.add(cmd);
56540
+ }
56541
+ continue;
56542
+ }
56543
+ const entry = COMMAND_REGISTRY[cmd];
56544
+ lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`);
56545
+ if (entry.args) {
56546
+ lines.push(` Args: \`${entry.args}\``);
56547
+ }
56548
+ if (entry.details) {
56549
+ lines.push(` ${entry.details}`);
56550
+ }
56551
+ const subcommands = VALID_COMMANDS.filter((sub) => sub.startsWith(`${cmd} `) && sub !== cmd);
56552
+ for (const sub of subcommands) {
56553
+ const subEntry = COMMAND_REGISTRY[sub];
56554
+ const subName = sub.slice(cmd.length + 1);
56555
+ lines.push(` - \`${subName}\` \u2014 ${subEntry.description}`);
56556
+ if (subEntry.args) {
56557
+ lines.push(` Args: \`${subEntry.args}\``);
56558
+ }
56559
+ if (subEntry.details) {
56560
+ lines.push(` ${subEntry.details}`);
56561
+ }
56562
+ }
56563
+ }
56564
+ for (const cmd of VALID_COMMANDS) {
56565
+ if (!cmd.includes(" ") || shownAsSubcommand.has(cmd))
56566
+ continue;
56567
+ const entry = COMMAND_REGISTRY[cmd];
56568
+ lines.push(`- \`/swarm ${cmd}\` \u2014 ${entry.description}`);
56569
+ if (entry.args) {
56570
+ lines.push(` Args: \`${entry.args}\``);
56571
+ }
56572
+ if (entry.details) {
56573
+ lines.push(` ${entry.details}`);
56574
+ }
56575
+ }
56576
+ return lines.join(`
56237
56577
  `);
56578
+ }
56579
+ var HELP_TEXT = buildHelpText();
56238
56580
  function createSwarmCommandHandler(directory, agents) {
56239
56581
  return async (input, output) => {
56240
56582
  if (input.command !== "swarm" && !input.command.startsWith("swarm-")) {
@@ -57676,6 +58018,54 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
57676
58018
  "pre_check_batch"
57677
58019
  ];
57678
58020
  const requireReviewerAndTestEngineer = cfg.qa_gates?.require_reviewer_test_engineer ?? true;
58021
+ function checkDestructiveCommand(tool3, args2) {
58022
+ if (tool3 !== "bash" && tool3 !== "shell")
58023
+ return;
58024
+ if (cfg.block_destructive_commands === false)
58025
+ return;
58026
+ const toolArgs = args2;
58027
+ const command = typeof toolArgs?.command === "string" ? toolArgs.command.trim() : "";
58028
+ if (!command)
58029
+ return;
58030
+ if (/:\s*\(\s*\)\s*\{[^}]*\|[^}]*:/.test(command)) {
58031
+ throw new Error(`BLOCKED: Potentially destructive shell command detected: fork bomb pattern`);
58032
+ }
58033
+ const rmFlagPattern = /^rm\s+(-r\s+-f|-f\s+-r|-rf|-fr)\s+(.+)$/;
58034
+ const rmMatch = rmFlagPattern.exec(command);
58035
+ if (rmMatch) {
58036
+ const targetPart = rmMatch[2].trim();
58037
+ const targets = targetPart.split(/\s+/);
58038
+ const safeTargets = /^(node_modules|\.git)$/;
58039
+ const allSafe = targets.every((t) => safeTargets.test(t));
58040
+ if (!allSafe) {
58041
+ throw new Error(`BLOCKED: Potentially destructive shell command: rm -rf on unsafe path(s): ${targetPart}`);
58042
+ }
58043
+ }
58044
+ if (/^git\s+push\b.*?(--force|-f)\b/.test(command)) {
58045
+ throw new Error(`BLOCKED: Force push detected \u2014 git push --force is not allowed`);
58046
+ }
58047
+ if (/^git\s+reset\s+--hard/.test(command)) {
58048
+ throw new Error(`BLOCKED: "git reset --hard" detected \u2014 use --soft or --mixed with caution`);
58049
+ }
58050
+ if (/^git\s+reset\s+--mixed\s+\S+/.test(command)) {
58051
+ throw new Error(`BLOCKED: "git reset --mixed" with a target branch/commit is not allowed`);
58052
+ }
58053
+ if (/^kubectl\s+delete\b/.test(command)) {
58054
+ throw new Error(`BLOCKED: "kubectl delete" detected \u2014 destructive cluster operation`);
58055
+ }
58056
+ if (/^docker\s+system\s+prune\b/.test(command)) {
58057
+ throw new Error(`BLOCKED: "docker system prune" detected \u2014 destructive container operation`);
58058
+ }
58059
+ if (/^\s*DROP\s+(TABLE|DATABASE|SCHEMA)\b/i.test(command)) {
58060
+ throw new Error(`BLOCKED: SQL DROP command detected \u2014 destructive database operation`);
58061
+ }
58062
+ if (/^\s*TRUNCATE\s+TABLE\b/i.test(command)) {
58063
+ throw new Error(`BLOCKED: SQL TRUNCATE command detected \u2014 destructive database operation`);
58064
+ }
58065
+ if (/^mkfs[./]/.test(command)) {
58066
+ throw new Error(`BLOCKED: Disk format command (mkfs) detected \u2014 disk formatting operation`);
58067
+ }
58068
+ }
57679
58069
  async function checkGateLimits(params) {
57680
58070
  const { sessionID, window: window2, agentConfig, elapsedMinutes, repetitionCount } = params;
57681
58071
  if (agentConfig.max_tool_calls > 0 && window2.toolCalls >= agentConfig.max_tool_calls) {
@@ -58025,6 +58415,7 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
58025
58415
  handleDelegatedWriteTracking(input.sessionID, input.tool, output.args);
58026
58416
  handleLoopDetection(input.sessionID, input.tool, output.args);
58027
58417
  handleTestSuiteBlocking(input.tool, output.args);
58418
+ checkDestructiveCommand(input.tool, output.args);
58028
58419
  if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
58029
58420
  handlePlanAndScopeProtection(input.sessionID, input.tool, output.args);
58030
58421
  const toolArgs = output.args;
@@ -58205,18 +58596,18 @@ function createGuardrailsHooks(directory, directoryOrConfig, config3, authorityC
58205
58596
  const fallbackModels = swarmAgents?.[baseAgentName]?.fallback_models;
58206
58597
  session.modelFallbackExhausted = !fallbackModels || session.model_fallback_index > fallbackModels.length;
58207
58598
  const fallbackModel = resolveFallbackModel(baseAgentName, session.model_fallback_index, swarmAgents);
58599
+ const primaryModel = swarmAgents?.[baseAgentName]?.model ?? "default";
58208
58600
  if (fallbackModel) {
58209
- const primaryModel = swarmAgents?.[baseAgentName]?.model ?? "default";
58210
58601
  if (swarmAgents?.[baseAgentName]) {
58211
58602
  swarmAgents[baseAgentName].model = fallbackModel;
58212
58603
  }
58213
- telemetry.modelFallback(input.sessionID, session.agentName, primaryModel, fallbackModel, "transient_model_error");
58214
58604
  session.pendingAdvisoryMessages ??= [];
58215
58605
  session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Applied fallback model "${fallbackModel}" (attempt ${session.model_fallback_index}). ` + `Using /swarm handoff to reset to primary model.`);
58216
58606
  } else {
58217
58607
  session.pendingAdvisoryMessages ??= [];
58218
58608
  session.pendingAdvisoryMessages.push(`MODEL FALLBACK: Transient model error detected (attempt ${session.model_fallback_index}). ` + `No fallback models configured for this agent. Add "fallback_models": ["model-a", "model-b"] ` + `to the agent's config in opencode-swarm.json.`);
58219
58609
  }
58610
+ telemetry.modelFallback(input.sessionID, session.agentName, primaryModel, fallbackModel ?? "none", "transient_model_error");
58220
58611
  swarmState.pendingEvents++;
58221
58612
  }
58222
58613
  }
@@ -58589,17 +58980,22 @@ function normalizePathWithCache(filePath, cwd) {
58589
58980
  return fallback;
58590
58981
  }
58591
58982
  }
58592
- function getGlobMatcher(pattern) {
58983
+ function getGlobMatcher(pattern, caseInsensitive = process.platform === "win32" || process.platform === "darwin") {
58593
58984
  const cached3 = globMatcherCache.get(pattern);
58594
58985
  if (cached3 !== undefined) {
58595
58986
  return cached3;
58596
58987
  }
58597
- const matcher = import_picomatch.default(pattern, {
58598
- dot: true,
58599
- nocase: process.platform === "win32"
58600
- });
58601
- globMatcherCache.set(pattern, matcher);
58602
- return matcher;
58988
+ try {
58989
+ const matcher = import_picomatch.default(pattern, {
58990
+ dot: true,
58991
+ nocase: caseInsensitive
58992
+ });
58993
+ globMatcherCache.set(pattern, matcher);
58994
+ return matcher;
58995
+ } catch (err2) {
58996
+ warn(`picomatch error for pattern "${pattern}": ${err2}`);
58997
+ return () => false;
58998
+ }
58603
58999
  }
58604
59000
  var DEFAULT_AGENT_AUTHORITY_RULES = {
58605
59001
  architect: {
@@ -58677,7 +59073,7 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
58677
59073
  const dir = cwd || process.cwd();
58678
59074
  let normalizedPath;
58679
59075
  try {
58680
- const normalizedWithSymlinks = normalizePathWithCache(filePath, cwd);
59076
+ const normalizedWithSymlinks = normalizePathWithCache(filePath, dir);
58681
59077
  const resolved = path38.resolve(dir, normalizedWithSymlinks);
58682
59078
  normalizedPath = path38.relative(dir, resolved).replace(/\\/g, "/");
58683
59079
  } catch {
@@ -58730,6 +59126,16 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
58730
59126
  return { allowed: true };
58731
59127
  }
58732
59128
  }
59129
+ if (rules.blockedPrefix && rules.blockedPrefix.length > 0) {
59130
+ for (const prefix of rules.blockedPrefix) {
59131
+ if (normalizedPath.startsWith(prefix)) {
59132
+ return {
59133
+ allowed: false,
59134
+ reason: `Path blocked: ${normalizedPath} is under ${prefix}`
59135
+ };
59136
+ }
59137
+ }
59138
+ }
58733
59139
  if (rules.allowedPrefix != null && rules.allowedPrefix.length > 0) {
58734
59140
  const isAllowed = rules.allowedPrefix.some((prefix) => normalizedPath.startsWith(prefix));
58735
59141
  if (!isAllowed) {
@@ -58744,16 +59150,6 @@ function checkFileAuthorityWithRules(agentName, filePath, cwd, effectiveRules) {
58744
59150
  reason: `Path ${normalizedPath} not in allowed list for ${normalizedAgent}`
58745
59151
  };
58746
59152
  }
58747
- if (rules.blockedPrefix && rules.blockedPrefix.length > 0) {
58748
- for (const prefix of rules.blockedPrefix) {
58749
- if (normalizedPath.startsWith(prefix)) {
58750
- return {
58751
- allowed: false,
58752
- reason: `Path blocked: ${normalizedPath} is under ${prefix}`
58753
- };
58754
- }
58755
- }
58756
- }
58757
59153
  if (rules.blockedZones && rules.blockedZones.length > 0) {
58758
59154
  const { zone } = classifyFile(normalizedPath);
58759
59155
  if (rules.blockedZones.includes(zone)) {
@@ -59526,7 +59922,9 @@ var END_OF_SENTENCE_QUESTION_PATTERN = /\?\s*$/;
59526
59922
  var PHASE_COMPLETION_PATTERNS = [
59527
59923
  /Ready for Phase (?:\d+|\[?N\+1\]?)\??/i,
59528
59924
  /phase.{0,20}(?:complete|finish|done|wrap)/i,
59529
- /move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i
59925
+ /move(?:d?)?\s+(?:on\s+)?to\s+(?:the\s+)?(?:next\s+)?phase/i,
59926
+ /(?:proceed|move)\s+to\s+the\s+next\s+phase/i,
59927
+ /what would you like.{0,20}(?:next|do next)/i
59530
59928
  ];
59531
59929
  var QUESTION_ESCALATION_PATTERNS = [
59532
59930
  /escalat/i,
@@ -61431,6 +61829,47 @@ ${handoffBlock}`);
61431
61829
  }
61432
61830
  } catch {}
61433
61831
  }
61832
+ if (baseRole === "coder") {
61833
+ const sessionId_ccp = _input.sessionID ?? "";
61834
+ const ccpSession = swarmState.agentSessions.get(sessionId_ccp);
61835
+ try {
61836
+ const coderScope = ccpSession?.declaredCoderScope;
61837
+ const primaryFile = coderScope?.[0] ?? "";
61838
+ if (primaryFile.length > 0) {
61839
+ const { knowledge_recall: knowledge_recall2 } = await Promise.resolve().then(() => (init_knowledge_recall(), exports_knowledge_recall));
61840
+ const rawResult = await knowledge_recall2.execute({ query: primaryFile }, { directory });
61841
+ if (rawResult && typeof rawResult === "string") {
61842
+ const parsed = JSON.parse(rawResult);
61843
+ if (parsed.results.length > 0) {
61844
+ const lines = parsed.results.map((r) => {
61845
+ const lesson = r.lesson.length > 200 ? `${r.lesson.slice(0, 200)}...` : r.lesson;
61846
+ return `- [${r.category}] ${lesson}`;
61847
+ });
61848
+ tryInject(`## CONTEXT FROM KNOWLEDGE BASE
61849
+ ${lines.join(`
61850
+ `)}`);
61851
+ }
61852
+ }
61853
+ }
61854
+ } catch {}
61855
+ try {
61856
+ const taskId_ccp = ccpSession?.currentTaskId;
61857
+ if (taskId_ccp && !taskId_ccp.includes("..") && !taskId_ccp.includes("/") && !taskId_ccp.includes("\\") && !taskId_ccp.includes("\x00")) {
61858
+ const evidencePath = path47.join(directory, ".swarm", "evidence", `${taskId_ccp}.json`);
61859
+ if (fs35.existsSync(evidencePath)) {
61860
+ const evidenceContent = fs35.readFileSync(evidencePath, "utf-8");
61861
+ const evidenceData = JSON.parse(evidenceContent);
61862
+ const rejections = (evidenceData.bundle?.entries ?? []).filter((e) => e.type === "gate" && e.gate_type === "reviewer" && e.verdict === "reject");
61863
+ if (rejections.length > 0) {
61864
+ const lines = rejections.map((r) => `- ${r.reason ?? "No reason provided"}`);
61865
+ tryInject(`## PRIOR REJECTIONS
61866
+ ${lines.join(`
61867
+ `)}`);
61868
+ }
61869
+ }
61870
+ }
61871
+ } catch {}
61872
+ }
61434
61873
  if (baseRole === "coder") {
61435
61874
  const taskText_lang_a = plan2 && plan2.migration_status !== "migration_failed" ? extractCurrentTaskFromPlan(plan2) : null;
61436
61875
  const langConstraints_a = buildLanguageCoderConstraints(taskText_lang_a);
@@ -67760,90 +68199,10 @@ var knowledge_query = createSwarmTool({
67760
68199
  `);
67761
68200
  }
67762
68201
  });
67763
- // src/tools/knowledge-recall.ts
67764
- init_dist();
67765
- init_knowledge_store();
67766
- init_create_tool();
67767
- var knowledge_recall = createSwarmTool({
67768
- description: "Search the knowledge base for relevant past decisions, patterns, and lessons learned. Returns ranked results by semantic similarity.",
67769
- args: {
67770
- query: tool.schema.string().min(3).describe("Natural language search query"),
67771
- top_n: tool.schema.number().int().min(1).max(20).optional().describe("Maximum results to return (default: 5)"),
67772
- tier: tool.schema.enum(["all", "swarm", "hive"]).optional().describe("Knowledge tier to search (default: 'all')")
67773
- },
67774
- execute: async (args2, directory) => {
67775
- let queryInput;
67776
- let topNInput;
67777
- let tierInput;
67778
- try {
67779
- if (args2 && typeof args2 === "object") {
67780
- const obj = args2;
67781
- queryInput = obj.query;
67782
- topNInput = obj.top_n;
67783
- tierInput = obj.tier;
67784
- }
67785
- } catch {}
67786
- if (typeof queryInput !== "string" || queryInput.length < 3) {
67787
- return JSON.stringify({
67788
- results: [],
67789
- total: 0,
67790
- error: "query must be a string with at least 3 characters"
67791
- });
67792
- }
67793
- let topN = 5;
67794
- if (topNInput !== undefined) {
67795
- if (typeof topNInput === "number" && Number.isInteger(topNInput)) {
67796
- topN = Math.max(1, Math.min(20, topNInput));
67797
- }
67798
- }
67799
- let tier = "all";
67800
- if (tierInput !== undefined && typeof tierInput === "string") {
67801
- if (tierInput === "swarm" || tierInput === "hive") {
67802
- tier = tierInput;
67803
- }
67804
- }
67805
- const swarmPath = resolveSwarmKnowledgePath(directory);
67806
- const hivePath = resolveHiveKnowledgePath();
67807
- const [swarmEntries, hiveEntries] = await Promise.all([
67808
- readKnowledge(swarmPath),
67809
- readKnowledge(hivePath)
67810
- ]);
67811
- let entries = [];
67812
- if (tier === "all" || tier === "swarm") {
67813
- entries = entries.concat(swarmEntries);
67814
- }
67815
- if (tier === "all" || tier === "hive") {
67816
- entries = entries.concat(hiveEntries);
67817
- }
67818
- if (entries.length === 0) {
67819
- const result2 = { results: [], total: 0 };
67820
- return JSON.stringify(result2);
67821
- }
67822
- const normalizedQuery = normalize2(queryInput);
67823
- const queryBigrams = wordBigrams(normalizedQuery);
67824
- const scoredEntries = entries.map((entry) => {
67825
- const entryText = `${entry.lesson} ${entry.tags.join(" ")} ${entry.category}`;
67826
- const entryBigrams = wordBigrams(entryText);
67827
- const textScore = jaccardBigram(queryBigrams, entryBigrams);
67828
- const boost = entry.status === "established" ? 0.1 : entry.status === "promoted" ? 0.05 : 0;
67829
- const finalScore = textScore + boost;
67830
- return {
67831
- id: entry.id,
67832
- confidence: entry.confidence,
67833
- category: entry.category,
67834
- lesson: entry.lesson,
67835
- score: finalScore
67836
- };
67837
- });
67838
- scoredEntries.sort((a, b) => b.score - a.score);
67839
- const topResults = scoredEntries.slice(0, topN);
67840
- const result = {
67841
- results: topResults,
67842
- total: topResults.length
67843
- };
67844
- return JSON.stringify(result);
67845
- }
67846
- });
68202
+
68203
+ // src/tools/index.ts
68204
+ init_knowledge_recall();
68205
+
67847
68206
  // src/tools/knowledge-remove.ts
67848
68207
  init_dist();
67849
68208
  init_knowledge_store();
@@ -76173,7 +76532,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
76173
76532
  message: "Invalid working_directory: null bytes are not allowed"
76174
76533
  };
76175
76534
  }
76176
- if (process.platform === "win32") {
76535
+ {
76177
76536
  const devicePathPattern = /^\\\\|^(NUL|CON|AUX|COM[1-9]|LPT[1-9])(\..*)?$/i;
76178
76537
  if (devicePathPattern.test(args2.working_directory)) {
76179
76538
  return {
@@ -76329,7 +76688,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
76329
76688
  errors: [error93 instanceof Error ? error93.message : String(error93)]
76330
76689
  };
76331
76690
  } finally {
76332
- if (lockResult && lockResult.acquired && lockResult.lock._release) {
76691
+ if (lockResult?.acquired && lockResult.lock._release) {
76333
76692
  try {
76334
76693
  await lockResult.lock._release();
76335
76694
  } catch (releaseError) {