opencode-swarm 6.23.0 → 6.23.2

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
@@ -15749,6 +15749,39 @@ async function savePlan(directory, plan) {
15749
15749
  throw new Error(`Invalid directory: directory must be a non-empty string`);
15750
15750
  }
15751
15751
  const validated = PlanSchema.parse(plan);
15752
+ try {
15753
+ const currentPlan = await loadPlanJsonOnly(directory);
15754
+ if (currentPlan) {
15755
+ const completedTaskIds = new Set;
15756
+ for (const phase of currentPlan.phases) {
15757
+ for (const task of phase.tasks) {
15758
+ if (task.status === "completed")
15759
+ completedTaskIds.add(task.id);
15760
+ }
15761
+ }
15762
+ if (completedTaskIds.size > 0) {
15763
+ for (const phase of validated.phases) {
15764
+ for (const task of phase.tasks) {
15765
+ if (completedTaskIds.has(task.id) && task.status !== "completed") {
15766
+ task.status = "completed";
15767
+ }
15768
+ }
15769
+ }
15770
+ }
15771
+ }
15772
+ } catch {}
15773
+ for (const phase of validated.phases) {
15774
+ const tasks = phase.tasks;
15775
+ if (tasks.length > 0 && tasks.every((t) => t.status === "completed")) {
15776
+ phase.status = "complete";
15777
+ } else if (tasks.some((t) => t.status === "in_progress")) {
15778
+ phase.status = "in_progress";
15779
+ } else if (tasks.some((t) => t.status === "blocked")) {
15780
+ phase.status = "blocked";
15781
+ } else {
15782
+ phase.status = "pending";
15783
+ }
15784
+ }
15752
15785
  const swarmDir = path4.resolve(directory, ".swarm");
15753
15786
  const planPath = path4.join(swarmDir, "plan.json");
15754
15787
  const tempPath = path4.join(swarmDir, `plan.json.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`);
@@ -35824,6 +35857,133 @@ var init_preflight_service = __esm(() => {
35824
35857
  };
35825
35858
  });
35826
35859
 
35860
+ // src/gate-evidence.ts
35861
+ var exports_gate_evidence = {};
35862
+ __export(exports_gate_evidence, {
35863
+ recordGateEvidence: () => recordGateEvidence,
35864
+ recordAgentDispatch: () => recordAgentDispatch,
35865
+ readTaskEvidence: () => readTaskEvidence,
35866
+ hasPassedAllGates: () => hasPassedAllGates,
35867
+ expandRequiredGates: () => expandRequiredGates,
35868
+ deriveRequiredGates: () => deriveRequiredGates,
35869
+ DEFAULT_REQUIRED_GATES: () => DEFAULT_REQUIRED_GATES
35870
+ });
35871
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, renameSync as renameSync7, unlinkSync as unlinkSync4 } from "fs";
35872
+ import * as path27 from "path";
35873
+ function assertValidTaskId(taskId) {
35874
+ if (!taskId || taskId.includes("..") || taskId.includes("/") || taskId.includes("\\") || taskId.includes("\x00") || !TASK_ID_PATTERN.test(taskId)) {
35875
+ throw new Error(`Invalid taskId: "${taskId}". Must match N.M or N.M.P (e.g. "1.1", "1.2.3").`);
35876
+ }
35877
+ }
35878
+ function deriveRequiredGates(agentType) {
35879
+ switch (agentType) {
35880
+ case "coder":
35881
+ return ["reviewer", "test_engineer"];
35882
+ case "docs":
35883
+ return ["docs"];
35884
+ case "designer":
35885
+ return ["designer", "reviewer", "test_engineer"];
35886
+ case "explorer":
35887
+ return ["explorer"];
35888
+ case "sme":
35889
+ return ["sme"];
35890
+ case "reviewer":
35891
+ return ["reviewer"];
35892
+ case "test_engineer":
35893
+ return ["test_engineer"];
35894
+ case "critic":
35895
+ return ["critic"];
35896
+ default:
35897
+ return ["reviewer", "test_engineer"];
35898
+ }
35899
+ }
35900
+ function expandRequiredGates(existingGates, newAgentType) {
35901
+ const newGates = deriveRequiredGates(newAgentType);
35902
+ const combined = [...new Set([...existingGates, ...newGates])];
35903
+ return combined.sort();
35904
+ }
35905
+ function getEvidenceDir(directory) {
35906
+ return path27.join(directory, ".swarm", "evidence");
35907
+ }
35908
+ function getEvidencePath(directory, taskId) {
35909
+ return path27.join(getEvidenceDir(directory), `${taskId}.json`);
35910
+ }
35911
+ function readExisting(evidencePath) {
35912
+ try {
35913
+ const raw = readFileSync13(evidencePath, "utf-8");
35914
+ return JSON.parse(raw);
35915
+ } catch {
35916
+ return null;
35917
+ }
35918
+ }
35919
+ async function atomicWrite(targetPath, content) {
35920
+ const tempPath = `${targetPath}.tmp.${Date.now()}.${Math.floor(Math.random() * 1e9)}`;
35921
+ try {
35922
+ await Bun.write(tempPath, content);
35923
+ renameSync7(tempPath, targetPath);
35924
+ } finally {
35925
+ try {
35926
+ unlinkSync4(tempPath);
35927
+ } catch {}
35928
+ }
35929
+ }
35930
+ async function recordGateEvidence(directory, taskId, gate, sessionId) {
35931
+ assertValidTaskId(taskId);
35932
+ const evidenceDir = getEvidenceDir(directory);
35933
+ const evidencePath = getEvidencePath(directory, taskId);
35934
+ mkdirSync8(evidenceDir, { recursive: true });
35935
+ const existing = readExisting(evidencePath);
35936
+ const requiredGates = existing ? expandRequiredGates(existing.required_gates, gate) : deriveRequiredGates(gate);
35937
+ const updated = {
35938
+ taskId,
35939
+ required_gates: requiredGates,
35940
+ gates: {
35941
+ ...existing?.gates ?? {},
35942
+ [gate]: {
35943
+ sessionId,
35944
+ timestamp: new Date().toISOString(),
35945
+ agent: gate
35946
+ }
35947
+ }
35948
+ };
35949
+ await atomicWrite(evidencePath, JSON.stringify(updated, null, 2));
35950
+ }
35951
+ async function recordAgentDispatch(directory, taskId, agentType) {
35952
+ assertValidTaskId(taskId);
35953
+ const evidenceDir = getEvidenceDir(directory);
35954
+ const evidencePath = getEvidencePath(directory, taskId);
35955
+ mkdirSync8(evidenceDir, { recursive: true });
35956
+ const existing = readExisting(evidencePath);
35957
+ const requiredGates = existing ? expandRequiredGates(existing.required_gates, agentType) : deriveRequiredGates(agentType);
35958
+ const updated = {
35959
+ taskId,
35960
+ required_gates: requiredGates,
35961
+ gates: existing?.gates ?? {}
35962
+ };
35963
+ await atomicWrite(evidencePath, JSON.stringify(updated, null, 2));
35964
+ }
35965
+ async function readTaskEvidence(directory, taskId) {
35966
+ try {
35967
+ assertValidTaskId(taskId);
35968
+ return readExisting(getEvidencePath(directory, taskId));
35969
+ } catch {
35970
+ return null;
35971
+ }
35972
+ }
35973
+ async function hasPassedAllGates(directory, taskId) {
35974
+ const evidence = await readTaskEvidence(directory, taskId);
35975
+ if (!evidence)
35976
+ return false;
35977
+ if (!Array.isArray(evidence.required_gates) || evidence.required_gates.length === 0)
35978
+ return false;
35979
+ return evidence.required_gates.every((gate) => evidence.gates[gate] != null);
35980
+ }
35981
+ var DEFAULT_REQUIRED_GATES, TASK_ID_PATTERN;
35982
+ var init_gate_evidence = __esm(() => {
35983
+ DEFAULT_REQUIRED_GATES = ["reviewer", "test_engineer"];
35984
+ TASK_ID_PATTERN = /^\d+\.\d+(\.\d+)*$/;
35985
+ });
35986
+
35827
35987
  // src/services/preflight-integration.ts
35828
35988
  var exports_preflight_integration = {};
35829
35989
  __export(exports_preflight_integration, {
@@ -37353,11 +37513,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37353
37513
  throw toThrow;
37354
37514
  }, "quit_");
37355
37515
  var scriptDirectory = "";
37356
- function locateFile(path39) {
37516
+ function locateFile(path40) {
37357
37517
  if (Module["locateFile"]) {
37358
- return Module["locateFile"](path39, scriptDirectory);
37518
+ return Module["locateFile"](path40, scriptDirectory);
37359
37519
  }
37360
- return scriptDirectory + path39;
37520
+ return scriptDirectory + path40;
37361
37521
  }
37362
37522
  __name(locateFile, "locateFile");
37363
37523
  var readAsync, readBinary;
@@ -39105,7 +39265,7 @@ var init_runtime = __esm(() => {
39105
39265
  });
39106
39266
 
39107
39267
  // src/index.ts
39108
- import * as path48 from "path";
39268
+ import * as path49 from "path";
39109
39269
 
39110
39270
  // src/agents/index.ts
39111
39271
  init_config();
@@ -48774,6 +48934,28 @@ function createDelegationGateHook(config3) {
48774
48934
  }
48775
48935
  }
48776
48936
  }
48937
+ if (typeof subagentType === "string") {
48938
+ const evidenceTaskId = session.currentTaskId ?? session.lastCoderDelegationTaskId;
48939
+ if (evidenceTaskId) {
48940
+ try {
48941
+ const gateAgents = [
48942
+ "reviewer",
48943
+ "test_engineer",
48944
+ "docs",
48945
+ "designer",
48946
+ "critic"
48947
+ ];
48948
+ const targetAgentForEvidence = stripKnownSwarmPrefix(subagentType);
48949
+ if (gateAgents.includes(targetAgentForEvidence)) {
48950
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
48951
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, targetAgentForEvidence, input.sessionID);
48952
+ } else {
48953
+ const { recordAgentDispatch: recordAgentDispatch2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
48954
+ await recordAgentDispatch2(process.cwd(), evidenceTaskId, targetAgentForEvidence);
48955
+ }
48956
+ } catch {}
48957
+ }
48958
+ }
48777
48959
  if (storedArgs !== undefined) {
48778
48960
  deleteStoredInputArgs(input.callID);
48779
48961
  }
@@ -48788,10 +48970,8 @@ function createDelegationGateHook(config3) {
48788
48970
  break;
48789
48971
  }
48790
48972
  }
48791
- if (lastCoderIndex === -1) {
48792
- return;
48793
- }
48794
- const afterCoder = delegationChain.slice(lastCoderIndex);
48973
+ const searchStart = lastCoderIndex === -1 ? 0 : lastCoderIndex;
48974
+ const afterCoder = delegationChain.slice(searchStart);
48795
48975
  for (const delegation of afterCoder) {
48796
48976
  const target = stripKnownSwarmPrefix(delegation.to);
48797
48977
  if (target === "reviewer")
@@ -48799,7 +48979,7 @@ function createDelegationGateHook(config3) {
48799
48979
  if (target === "test_engineer")
48800
48980
  hasTestEngineer = true;
48801
48981
  }
48802
- if (hasReviewer && hasTestEngineer) {
48982
+ if (lastCoderIndex !== -1 && hasReviewer && hasTestEngineer) {
48803
48983
  session.qaSkipCount = 0;
48804
48984
  session.qaSkipTaskIds = [];
48805
48985
  }
@@ -48868,6 +49048,21 @@ function createDelegationGateHook(config3) {
48868
49048
  }
48869
49049
  }
48870
49050
  }
49051
+ {
49052
+ const evidenceTaskId = session.currentTaskId ?? session.lastCoderDelegationTaskId;
49053
+ if (evidenceTaskId) {
49054
+ try {
49055
+ if (hasReviewer) {
49056
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49057
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, "reviewer", input.sessionID);
49058
+ }
49059
+ if (hasTestEngineer) {
49060
+ const { recordGateEvidence: recordGateEvidence2 } = await Promise.resolve().then(() => (init_gate_evidence(), exports_gate_evidence));
49061
+ await recordGateEvidence2(process.cwd(), evidenceTaskId, "test_engineer", input.sessionID);
49062
+ }
49063
+ } catch {}
49064
+ }
49065
+ }
48871
49066
  }
48872
49067
  }
48873
49068
  };
@@ -49059,11 +49254,11 @@ ${trimComment}${after}`;
49059
49254
  if (!hasReviewer || !hasTestEngineer || priorTaskStuckAtCoder) {
49060
49255
  if (session.qaSkipCount >= 1) {
49061
49256
  const skippedTasks = session.qaSkipTaskIds.join(", ");
49062
- throw new Error(`\uD83D\uDED1 QA GATE ENFORCEMENT: ${session.qaSkipCount + 1} consecutive coder delegations without reviewer/test_engineer. ` + `Skipped tasks: [${skippedTasks}]. ` + `DELEGATE to reviewer and test_engineer NOW before any further coder work.`);
49257
+ throw new Error(`\uD83D\uDED1 QA GATE ENFORCEMENT: ${session.qaSkipCount + 1} consecutive coder delegations without reviewer/test_engineer. Skipped tasks: [${skippedTasks}]. DELEGATE to reviewer and test_engineer NOW before any further coder work.`);
49063
49258
  }
49064
49259
  session.qaSkipCount++;
49065
49260
  session.qaSkipTaskIds.push(currentTaskId ?? "unknown");
49066
- warnings.push(`\u26A0\uFE0F PROTOCOL VIOLATION: Previous coder task completed, but QA gate was skipped. ` + `You MUST delegate to reviewer (code review) and test_engineer (test execution) ` + `before starting a new coder task. Review RULES 7-8 in your system prompt.`);
49261
+ warnings.push(`\u26A0\uFE0F PROTOCOL VIOLATION: Previous coder task completed, but QA gate was skipped. ` + `You MUST delegate to reviewer (code review) and test_engineer (test execution) before starting a new coder task. Review RULES 7-8 in your system prompt.`);
49067
49262
  }
49068
49263
  }
49069
49264
  }
@@ -49415,7 +49610,7 @@ import * as fs16 from "fs";
49415
49610
  init_utils2();
49416
49611
  init_manager2();
49417
49612
  import * as fs15 from "fs";
49418
- import * as path27 from "path";
49613
+ import * as path28 from "path";
49419
49614
  var DEFAULT_DRIFT_CONFIG = {
49420
49615
  staleThresholdPhases: 1,
49421
49616
  detectContradictions: true,
@@ -49569,7 +49764,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
49569
49764
  currentPhase = legacyPhase;
49570
49765
  }
49571
49766
  }
49572
- const contextPath = path27.join(directory, ".swarm", "context.md");
49767
+ const contextPath = path28.join(directory, ".swarm", "context.md");
49573
49768
  let contextContent = "";
49574
49769
  try {
49575
49770
  if (fs15.existsSync(contextPath)) {
@@ -51192,7 +51387,7 @@ function createDarkMatterDetectorHook(directory) {
51192
51387
  // src/hooks/knowledge-reader.ts
51193
51388
  import { existsSync as existsSync18 } from "fs";
51194
51389
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile4 } from "fs/promises";
51195
- import * as path28 from "path";
51390
+ import * as path29 from "path";
51196
51391
  var JACCARD_THRESHOLD = 0.6;
51197
51392
  var HIVE_TIER_BOOST = 0.05;
51198
51393
  var SAME_PROJECT_PENALTY = -0.05;
@@ -51240,7 +51435,7 @@ function inferCategoriesFromPhase(phaseDescription) {
51240
51435
  return ["process", "tooling"];
51241
51436
  }
51242
51437
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
51243
- const shownFile = path28.join(directory, ".swarm", ".knowledge-shown.json");
51438
+ const shownFile = path29.join(directory, ".swarm", ".knowledge-shown.json");
51244
51439
  try {
51245
51440
  let shownData = {};
51246
51441
  if (existsSync18(shownFile)) {
@@ -51248,7 +51443,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
51248
51443
  shownData = JSON.parse(content);
51249
51444
  }
51250
51445
  shownData[currentPhase] = lessonIds;
51251
- await mkdir4(path28.dirname(shownFile), { recursive: true });
51446
+ await mkdir4(path29.dirname(shownFile), { recursive: true });
51252
51447
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
51253
51448
  } catch {
51254
51449
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -51343,7 +51538,7 @@ async function readMergedKnowledge(directory, config3, context) {
51343
51538
  return topN;
51344
51539
  }
51345
51540
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
51346
- const shownFile = path28.join(directory, ".swarm", ".knowledge-shown.json");
51541
+ const shownFile = path29.join(directory, ".swarm", ".knowledge-shown.json");
51347
51542
  try {
51348
51543
  if (!existsSync18(shownFile)) {
51349
51544
  return;
@@ -51816,10 +52011,10 @@ Use this data to avoid repeating known failure patterns.`;
51816
52011
  init_event_bus();
51817
52012
  init_utils2();
51818
52013
  import * as fs17 from "fs";
51819
- import * as path29 from "path";
52014
+ import * as path30 from "path";
51820
52015
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
51821
52016
  async function readPriorDriftReports(directory) {
51822
- const swarmDir = path29.join(directory, ".swarm");
52017
+ const swarmDir = path30.join(directory, ".swarm");
51823
52018
  const entries = await fs17.promises.readdir(swarmDir).catch(() => null);
51824
52019
  if (entries === null)
51825
52020
  return [];
@@ -51846,7 +52041,7 @@ async function readPriorDriftReports(directory) {
51846
52041
  async function writeDriftReport(directory, report) {
51847
52042
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
51848
52043
  const filePath = validateSwarmPath(directory, filename);
51849
- const swarmDir = path29.dirname(filePath);
52044
+ const swarmDir = path30.dirname(filePath);
51850
52045
  await fs17.promises.mkdir(swarmDir, { recursive: true });
51851
52046
  try {
51852
52047
  await fs17.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -52155,7 +52350,7 @@ init_config_doctor();
52155
52350
 
52156
52351
  // src/session/snapshot-reader.ts
52157
52352
  init_utils2();
52158
- import path30 from "path";
52353
+ import path31 from "path";
52159
52354
  var VALID_TASK_WORKFLOW_STATES = [
52160
52355
  "idle",
52161
52356
  "coder_delegated",
@@ -52278,7 +52473,7 @@ function rehydrateState(snapshot) {
52278
52473
  async function reconcileTaskStatesFromPlan(directory) {
52279
52474
  let raw;
52280
52475
  try {
52281
- raw = await Bun.file(path30.join(directory, ".swarm/plan.json")).text();
52476
+ raw = await Bun.file(path31.join(directory, ".swarm/plan.json")).text();
52282
52477
  } catch {
52283
52478
  return;
52284
52479
  }
@@ -52502,7 +52697,7 @@ init_tool();
52502
52697
  init_create_tool();
52503
52698
  import { spawnSync } from "child_process";
52504
52699
  import * as fs19 from "fs";
52505
- import * as path31 from "path";
52700
+ import * as path32 from "path";
52506
52701
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
52507
52702
  var MAX_LABEL_LENGTH = 100;
52508
52703
  var GIT_TIMEOUT_MS = 30000;
@@ -52553,7 +52748,7 @@ function validateLabel(label) {
52553
52748
  return null;
52554
52749
  }
52555
52750
  function getCheckpointLogPath(directory) {
52556
- return path31.join(directory, CHECKPOINT_LOG_PATH);
52751
+ return path32.join(directory, CHECKPOINT_LOG_PATH);
52557
52752
  }
52558
52753
  function readCheckpointLog(directory) {
52559
52754
  const logPath = getCheckpointLogPath(directory);
@@ -52571,7 +52766,7 @@ function readCheckpointLog(directory) {
52571
52766
  }
52572
52767
  function writeCheckpointLog(log2, directory) {
52573
52768
  const logPath = getCheckpointLogPath(directory);
52574
- const dir = path31.dirname(logPath);
52769
+ const dir = path32.dirname(logPath);
52575
52770
  if (!fs19.existsSync(dir)) {
52576
52771
  fs19.mkdirSync(dir, { recursive: true });
52577
52772
  }
@@ -52779,7 +52974,7 @@ var checkpoint = createSwarmTool({
52779
52974
  init_dist();
52780
52975
  init_create_tool();
52781
52976
  import * as fs20 from "fs";
52782
- import * as path32 from "path";
52977
+ import * as path33 from "path";
52783
52978
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
52784
52979
  var DEFAULT_DAYS = 90;
52785
52980
  var DEFAULT_TOP_N = 20;
@@ -52923,7 +53118,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
52923
53118
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
52924
53119
  const filteredChurn = new Map;
52925
53120
  for (const [file3, count] of churnMap) {
52926
- const ext = path32.extname(file3).toLowerCase();
53121
+ const ext = path33.extname(file3).toLowerCase();
52927
53122
  if (extSet.has(ext)) {
52928
53123
  filteredChurn.set(file3, count);
52929
53124
  }
@@ -52934,7 +53129,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
52934
53129
  for (const [file3, churnCount] of filteredChurn) {
52935
53130
  let fullPath = file3;
52936
53131
  if (!fs20.existsSync(fullPath)) {
52937
- fullPath = path32.join(cwd, file3);
53132
+ fullPath = path33.join(cwd, file3);
52938
53133
  }
52939
53134
  const complexity = getComplexityForFile(fullPath);
52940
53135
  if (complexity !== null) {
@@ -53082,7 +53277,7 @@ var complexity_hotspots = createSwarmTool({
53082
53277
  // src/tools/declare-scope.ts
53083
53278
  init_tool();
53084
53279
  import * as fs21 from "fs";
53085
- import * as path33 from "path";
53280
+ import * as path34 from "path";
53086
53281
  init_create_tool();
53087
53282
  function validateTaskIdFormat(taskId) {
53088
53283
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -53161,8 +53356,8 @@ async function executeDeclareScope(args2, fallbackDir) {
53161
53356
  };
53162
53357
  }
53163
53358
  }
53164
- normalizedDir = path33.normalize(args2.working_directory);
53165
- const pathParts = normalizedDir.split(path33.sep);
53359
+ normalizedDir = path34.normalize(args2.working_directory);
53360
+ const pathParts = normalizedDir.split(path34.sep);
53166
53361
  if (pathParts.includes("..")) {
53167
53362
  return {
53168
53363
  success: false,
@@ -53172,10 +53367,10 @@ async function executeDeclareScope(args2, fallbackDir) {
53172
53367
  ]
53173
53368
  };
53174
53369
  }
53175
- const resolvedDir = path33.resolve(normalizedDir);
53370
+ const resolvedDir = path34.resolve(normalizedDir);
53176
53371
  try {
53177
53372
  const realPath = fs21.realpathSync(resolvedDir);
53178
- const planPath2 = path33.join(realPath, ".swarm", "plan.json");
53373
+ const planPath2 = path34.join(realPath, ".swarm", "plan.json");
53179
53374
  if (!fs21.existsSync(planPath2)) {
53180
53375
  return {
53181
53376
  success: false,
@@ -53196,7 +53391,7 @@ async function executeDeclareScope(args2, fallbackDir) {
53196
53391
  }
53197
53392
  }
53198
53393
  const directory = normalizedDir ?? fallbackDir ?? process.cwd();
53199
- const planPath = path33.resolve(directory, ".swarm", "plan.json");
53394
+ const planPath = path34.resolve(directory, ".swarm", "plan.json");
53200
53395
  if (!fs21.existsSync(planPath)) {
53201
53396
  return {
53202
53397
  success: false,
@@ -53286,20 +53481,20 @@ function validateBase(base) {
53286
53481
  function validatePaths(paths) {
53287
53482
  if (!paths)
53288
53483
  return null;
53289
- for (const path34 of paths) {
53290
- if (!path34 || path34.length === 0) {
53484
+ for (const path35 of paths) {
53485
+ if (!path35 || path35.length === 0) {
53291
53486
  return "empty path not allowed";
53292
53487
  }
53293
- if (path34.length > MAX_PATH_LENGTH) {
53488
+ if (path35.length > MAX_PATH_LENGTH) {
53294
53489
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
53295
53490
  }
53296
- if (SHELL_METACHARACTERS2.test(path34)) {
53491
+ if (SHELL_METACHARACTERS2.test(path35)) {
53297
53492
  return "path contains shell metacharacters";
53298
53493
  }
53299
- if (path34.startsWith("-")) {
53494
+ if (path35.startsWith("-")) {
53300
53495
  return 'path cannot start with "-" (option-like arguments not allowed)';
53301
53496
  }
53302
- if (CONTROL_CHAR_PATTERN2.test(path34)) {
53497
+ if (CONTROL_CHAR_PATTERN2.test(path35)) {
53303
53498
  return "path contains control characters";
53304
53499
  }
53305
53500
  }
@@ -53379,8 +53574,8 @@ var diff = tool({
53379
53574
  if (parts2.length >= 3) {
53380
53575
  const additions = parseInt(parts2[0], 10) || 0;
53381
53576
  const deletions = parseInt(parts2[1], 10) || 0;
53382
- const path34 = parts2[2];
53383
- files.push({ path: path34, additions, deletions });
53577
+ const path35 = parts2[2];
53578
+ files.push({ path: path35, additions, deletions });
53384
53579
  }
53385
53580
  }
53386
53581
  const contractChanges = [];
@@ -53610,7 +53805,7 @@ Use these as DOMAIN values when delegating to @sme.`;
53610
53805
  init_dist();
53611
53806
  init_create_tool();
53612
53807
  import * as fs22 from "fs";
53613
- import * as path34 from "path";
53808
+ import * as path35 from "path";
53614
53809
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
53615
53810
  var MAX_EVIDENCE_FILES = 1000;
53616
53811
  var EVIDENCE_DIR = ".swarm/evidence";
@@ -53633,9 +53828,9 @@ function validateRequiredTypes(input) {
53633
53828
  return null;
53634
53829
  }
53635
53830
  function isPathWithinSwarm(filePath, cwd) {
53636
- const normalizedCwd = path34.resolve(cwd);
53637
- const swarmPath = path34.join(normalizedCwd, ".swarm");
53638
- const normalizedPath = path34.resolve(filePath);
53831
+ const normalizedCwd = path35.resolve(cwd);
53832
+ const swarmPath = path35.join(normalizedCwd, ".swarm");
53833
+ const normalizedPath = path35.resolve(filePath);
53639
53834
  return normalizedPath.startsWith(swarmPath);
53640
53835
  }
53641
53836
  function parseCompletedTasks(planContent) {
@@ -53665,10 +53860,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
53665
53860
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
53666
53861
  continue;
53667
53862
  }
53668
- const filePath = path34.join(evidenceDir, filename);
53863
+ const filePath = path35.join(evidenceDir, filename);
53669
53864
  try {
53670
- const resolvedPath = path34.resolve(filePath);
53671
- const evidenceDirResolved = path34.resolve(evidenceDir);
53865
+ const resolvedPath = path35.resolve(filePath);
53866
+ const evidenceDirResolved = path35.resolve(evidenceDir);
53672
53867
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
53673
53868
  continue;
53674
53869
  }
@@ -53775,7 +53970,7 @@ var evidence_check = createSwarmTool({
53775
53970
  return JSON.stringify(errorResult, null, 2);
53776
53971
  }
53777
53972
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
53778
- const planPath = path34.join(cwd, PLAN_FILE);
53973
+ const planPath = path35.join(cwd, PLAN_FILE);
53779
53974
  if (!isPathWithinSwarm(planPath, cwd)) {
53780
53975
  const errorResult = {
53781
53976
  error: "plan file path validation failed",
@@ -53807,7 +54002,7 @@ var evidence_check = createSwarmTool({
53807
54002
  };
53808
54003
  return JSON.stringify(result2, null, 2);
53809
54004
  }
53810
- const evidenceDir = path34.join(cwd, EVIDENCE_DIR);
54005
+ const evidenceDir = path35.join(cwd, EVIDENCE_DIR);
53811
54006
  const evidence = readEvidenceFiles(evidenceDir, cwd);
53812
54007
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
53813
54008
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -53825,7 +54020,7 @@ var evidence_check = createSwarmTool({
53825
54020
  init_tool();
53826
54021
  init_create_tool();
53827
54022
  import * as fs23 from "fs";
53828
- import * as path35 from "path";
54023
+ import * as path36 from "path";
53829
54024
  var EXT_MAP = {
53830
54025
  python: ".py",
53831
54026
  py: ".py",
@@ -53906,12 +54101,12 @@ var extract_code_blocks = createSwarmTool({
53906
54101
  if (prefix) {
53907
54102
  filename = `${prefix}_${filename}`;
53908
54103
  }
53909
- let filepath = path35.join(targetDir, filename);
53910
- const base = path35.basename(filepath, path35.extname(filepath));
53911
- const ext = path35.extname(filepath);
54104
+ let filepath = path36.join(targetDir, filename);
54105
+ const base = path36.basename(filepath, path36.extname(filepath));
54106
+ const ext = path36.extname(filepath);
53912
54107
  let counter = 1;
53913
54108
  while (fs23.existsSync(filepath)) {
53914
- filepath = path35.join(targetDir, `${base}_${counter}${ext}`);
54109
+ filepath = path36.join(targetDir, `${base}_${counter}${ext}`);
53915
54110
  counter++;
53916
54111
  }
53917
54112
  try {
@@ -54029,7 +54224,7 @@ var gitingest = tool({
54029
54224
  // src/tools/imports.ts
54030
54225
  init_dist();
54031
54226
  import * as fs24 from "fs";
54032
- import * as path36 from "path";
54227
+ import * as path37 from "path";
54033
54228
  var MAX_FILE_PATH_LENGTH2 = 500;
54034
54229
  var MAX_SYMBOL_LENGTH = 256;
54035
54230
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -54083,7 +54278,7 @@ function validateSymbolInput(symbol3) {
54083
54278
  return null;
54084
54279
  }
54085
54280
  function isBinaryFile2(filePath, buffer) {
54086
- const ext = path36.extname(filePath).toLowerCase();
54281
+ const ext = path37.extname(filePath).toLowerCase();
54087
54282
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
54088
54283
  return false;
54089
54284
  }
@@ -54107,15 +54302,15 @@ function parseImports(content, targetFile, targetSymbol) {
54107
54302
  const imports = [];
54108
54303
  let _resolvedTarget;
54109
54304
  try {
54110
- _resolvedTarget = path36.resolve(targetFile);
54305
+ _resolvedTarget = path37.resolve(targetFile);
54111
54306
  } catch {
54112
54307
  _resolvedTarget = targetFile;
54113
54308
  }
54114
- const targetBasename = path36.basename(targetFile, path36.extname(targetFile));
54309
+ const targetBasename = path37.basename(targetFile, path37.extname(targetFile));
54115
54310
  const targetWithExt = targetFile;
54116
54311
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
54117
- const normalizedTargetWithExt = path36.normalize(targetWithExt).replace(/\\/g, "/");
54118
- const normalizedTargetWithoutExt = path36.normalize(targetWithoutExt).replace(/\\/g, "/");
54312
+ const normalizedTargetWithExt = path37.normalize(targetWithExt).replace(/\\/g, "/");
54313
+ const normalizedTargetWithoutExt = path37.normalize(targetWithoutExt).replace(/\\/g, "/");
54119
54314
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
54120
54315
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
54121
54316
  const modulePath = match[1] || match[2] || match[3];
@@ -54138,9 +54333,9 @@ function parseImports(content, targetFile, targetSymbol) {
54138
54333
  }
54139
54334
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
54140
54335
  let isMatch = false;
54141
- const _targetDir = path36.dirname(targetFile);
54142
- const targetExt = path36.extname(targetFile);
54143
- const targetBasenameNoExt = path36.basename(targetFile, targetExt);
54336
+ const _targetDir = path37.dirname(targetFile);
54337
+ const targetExt = path37.extname(targetFile);
54338
+ const targetBasenameNoExt = path37.basename(targetFile, targetExt);
54144
54339
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
54145
54340
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
54146
54341
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -54208,10 +54403,10 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
54208
54403
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
54209
54404
  for (const entry of entries) {
54210
54405
  if (SKIP_DIRECTORIES2.has(entry)) {
54211
- stats.skippedDirs.push(path36.join(dir, entry));
54406
+ stats.skippedDirs.push(path37.join(dir, entry));
54212
54407
  continue;
54213
54408
  }
54214
- const fullPath = path36.join(dir, entry);
54409
+ const fullPath = path37.join(dir, entry);
54215
54410
  let stat2;
54216
54411
  try {
54217
54412
  stat2 = fs24.statSync(fullPath);
@@ -54225,7 +54420,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
54225
54420
  if (stat2.isDirectory()) {
54226
54421
  findSourceFiles2(fullPath, files, stats);
54227
54422
  } else if (stat2.isFile()) {
54228
- const ext = path36.extname(fullPath).toLowerCase();
54423
+ const ext = path37.extname(fullPath).toLowerCase();
54229
54424
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
54230
54425
  files.push(fullPath);
54231
54426
  }
@@ -54281,7 +54476,7 @@ var imports = tool({
54281
54476
  return JSON.stringify(errorResult, null, 2);
54282
54477
  }
54283
54478
  try {
54284
- const targetFile = path36.resolve(file3);
54479
+ const targetFile = path37.resolve(file3);
54285
54480
  if (!fs24.existsSync(targetFile)) {
54286
54481
  const errorResult = {
54287
54482
  error: `target file not found: ${file3}`,
@@ -54303,7 +54498,7 @@ var imports = tool({
54303
54498
  };
54304
54499
  return JSON.stringify(errorResult, null, 2);
54305
54500
  }
54306
- const baseDir = path36.dirname(targetFile);
54501
+ const baseDir = path37.dirname(targetFile);
54307
54502
  const scanStats = {
54308
54503
  skippedDirs: [],
54309
54504
  skippedFiles: 0,
@@ -54625,7 +54820,7 @@ init_config();
54625
54820
  init_schema();
54626
54821
  init_manager();
54627
54822
  import * as fs25 from "fs";
54628
- import * as path37 from "path";
54823
+ import * as path38 from "path";
54629
54824
  init_utils2();
54630
54825
  init_create_tool();
54631
54826
  function safeWarn(message, error93) {
@@ -54820,7 +55015,7 @@ async function executePhaseComplete(args2, workingDirectory) {
54820
55015
  }
54821
55016
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
54822
55017
  try {
54823
- const projectName = path37.basename(dir);
55018
+ const projectName = path38.basename(dir);
54824
55019
  const knowledgeConfig = {
54825
55020
  enabled: true,
54826
55021
  swarm_max_entries: 100,
@@ -54984,7 +55179,7 @@ init_discovery();
54984
55179
  init_utils();
54985
55180
  init_create_tool();
54986
55181
  import * as fs26 from "fs";
54987
- import * as path38 from "path";
55182
+ import * as path39 from "path";
54988
55183
  var MAX_OUTPUT_BYTES5 = 52428800;
54989
55184
  var AUDIT_TIMEOUT_MS = 120000;
54990
55185
  function isValidEcosystem(value) {
@@ -55002,16 +55197,16 @@ function validateArgs3(args2) {
55002
55197
  function detectEcosystems(directory) {
55003
55198
  const ecosystems = [];
55004
55199
  const cwd = directory;
55005
- if (fs26.existsSync(path38.join(cwd, "package.json"))) {
55200
+ if (fs26.existsSync(path39.join(cwd, "package.json"))) {
55006
55201
  ecosystems.push("npm");
55007
55202
  }
55008
- if (fs26.existsSync(path38.join(cwd, "pyproject.toml")) || fs26.existsSync(path38.join(cwd, "requirements.txt"))) {
55203
+ if (fs26.existsSync(path39.join(cwd, "pyproject.toml")) || fs26.existsSync(path39.join(cwd, "requirements.txt"))) {
55009
55204
  ecosystems.push("pip");
55010
55205
  }
55011
- if (fs26.existsSync(path38.join(cwd, "Cargo.toml"))) {
55206
+ if (fs26.existsSync(path39.join(cwd, "Cargo.toml"))) {
55012
55207
  ecosystems.push("cargo");
55013
55208
  }
55014
- if (fs26.existsSync(path38.join(cwd, "go.mod"))) {
55209
+ if (fs26.existsSync(path39.join(cwd, "go.mod"))) {
55015
55210
  ecosystems.push("go");
55016
55211
  }
55017
55212
  try {
@@ -55020,10 +55215,10 @@ function detectEcosystems(directory) {
55020
55215
  ecosystems.push("dotnet");
55021
55216
  }
55022
55217
  } catch {}
55023
- if (fs26.existsSync(path38.join(cwd, "Gemfile")) || fs26.existsSync(path38.join(cwd, "Gemfile.lock"))) {
55218
+ if (fs26.existsSync(path39.join(cwd, "Gemfile")) || fs26.existsSync(path39.join(cwd, "Gemfile.lock"))) {
55024
55219
  ecosystems.push("ruby");
55025
55220
  }
55026
- if (fs26.existsSync(path38.join(cwd, "pubspec.yaml"))) {
55221
+ if (fs26.existsSync(path39.join(cwd, "pubspec.yaml"))) {
55027
55222
  ecosystems.push("dart");
55028
55223
  }
55029
55224
  return ecosystems;
@@ -56086,7 +56281,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
56086
56281
  // src/tools/pre-check-batch.ts
56087
56282
  init_dist();
56088
56283
  import * as fs29 from "fs";
56089
- import * as path41 from "path";
56284
+ import * as path42 from "path";
56090
56285
 
56091
56286
  // node_modules/yocto-queue/index.js
56092
56287
  class Node2 {
@@ -56254,7 +56449,7 @@ init_manager();
56254
56449
 
56255
56450
  // src/quality/metrics.ts
56256
56451
  import * as fs27 from "fs";
56257
- import * as path39 from "path";
56452
+ import * as path40 from "path";
56258
56453
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
56259
56454
  var MIN_DUPLICATION_LINES = 10;
56260
56455
  function estimateCyclomaticComplexity(content) {
@@ -56306,7 +56501,7 @@ async function computeComplexityDelta(files, workingDir) {
56306
56501
  let totalComplexity = 0;
56307
56502
  const analyzedFiles = [];
56308
56503
  for (const file3 of files) {
56309
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56504
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56310
56505
  if (!fs27.existsSync(fullPath)) {
56311
56506
  continue;
56312
56507
  }
@@ -56429,7 +56624,7 @@ function countGoExports(content) {
56429
56624
  function getExportCountForFile(filePath) {
56430
56625
  try {
56431
56626
  const content = fs27.readFileSync(filePath, "utf-8");
56432
- const ext = path39.extname(filePath).toLowerCase();
56627
+ const ext = path40.extname(filePath).toLowerCase();
56433
56628
  switch (ext) {
56434
56629
  case ".ts":
56435
56630
  case ".tsx":
@@ -56455,7 +56650,7 @@ async function computePublicApiDelta(files, workingDir) {
56455
56650
  let totalExports = 0;
56456
56651
  const analyzedFiles = [];
56457
56652
  for (const file3 of files) {
56458
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56653
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56459
56654
  if (!fs27.existsSync(fullPath)) {
56460
56655
  continue;
56461
56656
  }
@@ -56489,7 +56684,7 @@ async function computeDuplicationRatio(files, workingDir) {
56489
56684
  let duplicateLines = 0;
56490
56685
  const analyzedFiles = [];
56491
56686
  for (const file3 of files) {
56492
- const fullPath = path39.isAbsolute(file3) ? file3 : path39.join(workingDir, file3);
56687
+ const fullPath = path40.isAbsolute(file3) ? file3 : path40.join(workingDir, file3);
56493
56688
  if (!fs27.existsSync(fullPath)) {
56494
56689
  continue;
56495
56690
  }
@@ -56522,8 +56717,8 @@ function countCodeLines(content) {
56522
56717
  return lines.length;
56523
56718
  }
56524
56719
  function isTestFile(filePath) {
56525
- const basename8 = path39.basename(filePath);
56526
- const _ext = path39.extname(filePath).toLowerCase();
56720
+ const basename8 = path40.basename(filePath);
56721
+ const _ext = path40.extname(filePath).toLowerCase();
56527
56722
  const testPatterns = [
56528
56723
  ".test.",
56529
56724
  ".spec.",
@@ -56604,8 +56799,8 @@ function matchGlobSegment(globSegments, pathSegments) {
56604
56799
  }
56605
56800
  return gIndex === globSegments.length && pIndex === pathSegments.length;
56606
56801
  }
56607
- function matchesGlobSegment(path40, glob) {
56608
- const normalizedPath = path40.replace(/\\/g, "/");
56802
+ function matchesGlobSegment(path41, glob) {
56803
+ const normalizedPath = path41.replace(/\\/g, "/");
56609
56804
  const normalizedGlob = glob.replace(/\\/g, "/");
56610
56805
  if (normalizedPath.includes("//")) {
56611
56806
  return false;
@@ -56636,8 +56831,8 @@ function simpleGlobToRegex2(glob) {
56636
56831
  function hasGlobstar(glob) {
56637
56832
  return glob.includes("**");
56638
56833
  }
56639
- function globMatches(path40, glob) {
56640
- const normalizedPath = path40.replace(/\\/g, "/");
56834
+ function globMatches(path41, glob) {
56835
+ const normalizedPath = path41.replace(/\\/g, "/");
56641
56836
  if (!glob || glob === "") {
56642
56837
  if (normalizedPath.includes("//")) {
56643
56838
  return false;
@@ -56673,7 +56868,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
56673
56868
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56674
56869
  let testLines = 0;
56675
56870
  let codeLines = 0;
56676
- const srcDir = path39.join(workingDir, "src");
56871
+ const srcDir = path40.join(workingDir, "src");
56677
56872
  if (fs27.existsSync(srcDir)) {
56678
56873
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
56679
56874
  codeLines += lines;
@@ -56681,14 +56876,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56681
56876
  }
56682
56877
  const possibleSrcDirs = ["lib", "app", "source", "core"];
56683
56878
  for (const dir of possibleSrcDirs) {
56684
- const dirPath = path39.join(workingDir, dir);
56879
+ const dirPath = path40.join(workingDir, dir);
56685
56880
  if (fs27.existsSync(dirPath)) {
56686
56881
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
56687
56882
  codeLines += lines;
56688
56883
  });
56689
56884
  }
56690
56885
  }
56691
- const testsDir = path39.join(workingDir, "tests");
56886
+ const testsDir = path40.join(workingDir, "tests");
56692
56887
  if (fs27.existsSync(testsDir)) {
56693
56888
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
56694
56889
  testLines += lines;
@@ -56696,7 +56891,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
56696
56891
  }
56697
56892
  const possibleTestDirs = ["test", "__tests__", "specs"];
56698
56893
  for (const dir of possibleTestDirs) {
56699
- const dirPath = path39.join(workingDir, dir);
56894
+ const dirPath = path40.join(workingDir, dir);
56700
56895
  if (fs27.existsSync(dirPath) && dirPath !== testsDir) {
56701
56896
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
56702
56897
  testLines += lines;
@@ -56711,7 +56906,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
56711
56906
  try {
56712
56907
  const entries = fs27.readdirSync(dirPath, { withFileTypes: true });
56713
56908
  for (const entry of entries) {
56714
- const fullPath = path39.join(dirPath, entry.name);
56909
+ const fullPath = path40.join(dirPath, entry.name);
56715
56910
  if (entry.isDirectory()) {
56716
56911
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
56717
56912
  continue;
@@ -56719,7 +56914,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
56719
56914
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
56720
56915
  } else if (entry.isFile()) {
56721
56916
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
56722
- const ext = path39.extname(entry.name).toLowerCase();
56917
+ const ext = path40.extname(entry.name).toLowerCase();
56723
56918
  const validExts = [
56724
56919
  ".ts",
56725
56920
  ".tsx",
@@ -56970,7 +57165,7 @@ init_dist();
56970
57165
  init_manager();
56971
57166
  init_detector();
56972
57167
  import * as fs28 from "fs";
56973
- import * as path40 from "path";
57168
+ import * as path41 from "path";
56974
57169
  import { extname as extname9 } from "path";
56975
57170
 
56976
57171
  // src/sast/rules/c.ts
@@ -57933,7 +58128,7 @@ async function sastScan(input, directory, config3) {
57933
58128
  _filesSkipped++;
57934
58129
  continue;
57935
58130
  }
57936
- const resolvedPath = path40.isAbsolute(filePath) ? filePath : path40.resolve(directory, filePath);
58131
+ const resolvedPath = path41.isAbsolute(filePath) ? filePath : path41.resolve(directory, filePath);
57937
58132
  if (!fs28.existsSync(resolvedPath)) {
57938
58133
  _filesSkipped++;
57939
58134
  continue;
@@ -58132,18 +58327,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
58132
58327
  let resolved;
58133
58328
  const isWinAbs = isWindowsAbsolutePath(inputPath);
58134
58329
  if (isWinAbs) {
58135
- resolved = path41.win32.resolve(inputPath);
58136
- } else if (path41.isAbsolute(inputPath)) {
58137
- resolved = path41.resolve(inputPath);
58330
+ resolved = path42.win32.resolve(inputPath);
58331
+ } else if (path42.isAbsolute(inputPath)) {
58332
+ resolved = path42.resolve(inputPath);
58138
58333
  } else {
58139
- resolved = path41.resolve(baseDir, inputPath);
58334
+ resolved = path42.resolve(baseDir, inputPath);
58140
58335
  }
58141
- const workspaceResolved = path41.resolve(workspaceDir);
58336
+ const workspaceResolved = path42.resolve(workspaceDir);
58142
58337
  let relative5;
58143
58338
  if (isWinAbs) {
58144
- relative5 = path41.win32.relative(workspaceResolved, resolved);
58339
+ relative5 = path42.win32.relative(workspaceResolved, resolved);
58145
58340
  } else {
58146
- relative5 = path41.relative(workspaceResolved, resolved);
58341
+ relative5 = path42.relative(workspaceResolved, resolved);
58147
58342
  }
58148
58343
  if (relative5.startsWith("..")) {
58149
58344
  return "path traversal detected";
@@ -58204,13 +58399,13 @@ async function runLintWrapped(files, directory, _config) {
58204
58399
  }
58205
58400
  async function runLintOnFiles(linter, files, workspaceDir) {
58206
58401
  const isWindows = process.platform === "win32";
58207
- const binDir = path41.join(workspaceDir, "node_modules", ".bin");
58402
+ const binDir = path42.join(workspaceDir, "node_modules", ".bin");
58208
58403
  const validatedFiles = [];
58209
58404
  for (const file3 of files) {
58210
58405
  if (typeof file3 !== "string") {
58211
58406
  continue;
58212
58407
  }
58213
- const resolvedPath = path41.resolve(file3);
58408
+ const resolvedPath = path42.resolve(file3);
58214
58409
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
58215
58410
  if (validationError) {
58216
58411
  continue;
@@ -58228,10 +58423,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
58228
58423
  }
58229
58424
  let command;
58230
58425
  if (linter === "biome") {
58231
- const biomeBin = isWindows ? path41.join(binDir, "biome.EXE") : path41.join(binDir, "biome");
58426
+ const biomeBin = isWindows ? path42.join(binDir, "biome.EXE") : path42.join(binDir, "biome");
58232
58427
  command = [biomeBin, "check", ...validatedFiles];
58233
58428
  } else {
58234
- const eslintBin = isWindows ? path41.join(binDir, "eslint.cmd") : path41.join(binDir, "eslint");
58429
+ const eslintBin = isWindows ? path42.join(binDir, "eslint.cmd") : path42.join(binDir, "eslint");
58235
58430
  command = [eslintBin, ...validatedFiles];
58236
58431
  }
58237
58432
  try {
@@ -58368,7 +58563,7 @@ async function runSecretscanWithFiles(files, directory) {
58368
58563
  skippedFiles++;
58369
58564
  continue;
58370
58565
  }
58371
- const resolvedPath = path41.resolve(file3);
58566
+ const resolvedPath = path42.resolve(file3);
58372
58567
  const validationError = validatePath(resolvedPath, directory, directory);
58373
58568
  if (validationError) {
58374
58569
  skippedFiles++;
@@ -58386,7 +58581,7 @@ async function runSecretscanWithFiles(files, directory) {
58386
58581
  };
58387
58582
  }
58388
58583
  for (const file3 of validatedFiles) {
58389
- const ext = path41.extname(file3).toLowerCase();
58584
+ const ext = path42.extname(file3).toLowerCase();
58390
58585
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
58391
58586
  skippedFiles++;
58392
58587
  continue;
@@ -58545,7 +58740,7 @@ async function runPreCheckBatch(input, workspaceDir) {
58545
58740
  warn(`pre_check_batch: Invalid file path: ${file3}`);
58546
58741
  continue;
58547
58742
  }
58548
- changedFiles.push(path41.resolve(directory, file3));
58743
+ changedFiles.push(path42.resolve(directory, file3));
58549
58744
  }
58550
58745
  if (changedFiles.length === 0) {
58551
58746
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -58696,7 +58891,7 @@ var pre_check_batch = createSwarmTool({
58696
58891
  };
58697
58892
  return JSON.stringify(errorResult, null, 2);
58698
58893
  }
58699
- const resolvedDirectory = path41.resolve(typedArgs.directory);
58894
+ const resolvedDirectory = path42.resolve(typedArgs.directory);
58700
58895
  const workspaceAnchor = resolvedDirectory;
58701
58896
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
58702
58897
  if (dirError) {
@@ -58804,7 +58999,7 @@ init_tool();
58804
58999
  init_manager2();
58805
59000
  init_create_tool();
58806
59001
  import * as fs30 from "fs";
58807
- import * as path42 from "path";
59002
+ import * as path43 from "path";
58808
59003
  function detectPlaceholderContent(args2) {
58809
59004
  const issues = [];
58810
59005
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -58908,7 +59103,7 @@ async function executeSavePlan(args2, fallbackDir) {
58908
59103
  try {
58909
59104
  await savePlan(dir, plan);
58910
59105
  try {
58911
- const markerPath = path42.join(dir, ".swarm", ".plan-write-marker");
59106
+ const markerPath = path43.join(dir, ".swarm", ".plan-write-marker");
58912
59107
  const marker = JSON.stringify({
58913
59108
  source: "save_plan",
58914
59109
  timestamp: new Date().toISOString(),
@@ -58920,7 +59115,7 @@ async function executeSavePlan(args2, fallbackDir) {
58920
59115
  return {
58921
59116
  success: true,
58922
59117
  message: "Plan saved successfully",
58923
- plan_path: path42.join(dir, ".swarm", "plan.json"),
59118
+ plan_path: path43.join(dir, ".swarm", "plan.json"),
58924
59119
  phases_count: plan.phases.length,
58925
59120
  tasks_count: tasksCount
58926
59121
  };
@@ -58959,7 +59154,7 @@ var save_plan = createSwarmTool({
58959
59154
  init_dist();
58960
59155
  init_manager();
58961
59156
  import * as fs31 from "fs";
58962
- import * as path43 from "path";
59157
+ import * as path44 from "path";
58963
59158
 
58964
59159
  // src/sbom/detectors/index.ts
58965
59160
  init_utils();
@@ -59807,7 +60002,7 @@ function findManifestFiles(rootDir) {
59807
60002
  try {
59808
60003
  const entries = fs31.readdirSync(dir, { withFileTypes: true });
59809
60004
  for (const entry of entries) {
59810
- const fullPath = path43.join(dir, entry.name);
60005
+ const fullPath = path44.join(dir, entry.name);
59811
60006
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
59812
60007
  continue;
59813
60008
  }
@@ -59816,7 +60011,7 @@ function findManifestFiles(rootDir) {
59816
60011
  } else if (entry.isFile()) {
59817
60012
  for (const pattern of patterns) {
59818
60013
  if (simpleGlobToRegex(pattern).test(entry.name)) {
59819
- manifestFiles.push(path43.relative(rootDir, fullPath));
60014
+ manifestFiles.push(path44.relative(rootDir, fullPath));
59820
60015
  break;
59821
60016
  }
59822
60017
  }
@@ -59834,11 +60029,11 @@ function findManifestFilesInDirs(directories, workingDir) {
59834
60029
  try {
59835
60030
  const entries = fs31.readdirSync(dir, { withFileTypes: true });
59836
60031
  for (const entry of entries) {
59837
- const fullPath = path43.join(dir, entry.name);
60032
+ const fullPath = path44.join(dir, entry.name);
59838
60033
  if (entry.isFile()) {
59839
60034
  for (const pattern of patterns) {
59840
60035
  if (simpleGlobToRegex(pattern).test(entry.name)) {
59841
- found.push(path43.relative(workingDir, fullPath));
60036
+ found.push(path44.relative(workingDir, fullPath));
59842
60037
  break;
59843
60038
  }
59844
60039
  }
@@ -59851,11 +60046,11 @@ function findManifestFilesInDirs(directories, workingDir) {
59851
60046
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
59852
60047
  const dirs = new Set;
59853
60048
  for (const file3 of changedFiles) {
59854
- let currentDir = path43.dirname(file3);
60049
+ let currentDir = path44.dirname(file3);
59855
60050
  while (true) {
59856
- if (currentDir && currentDir !== "." && currentDir !== path43.sep) {
59857
- dirs.add(path43.join(workingDir, currentDir));
59858
- const parent = path43.dirname(currentDir);
60051
+ if (currentDir && currentDir !== "." && currentDir !== path44.sep) {
60052
+ dirs.add(path44.join(workingDir, currentDir));
60053
+ const parent = path44.dirname(currentDir);
59859
60054
  if (parent === currentDir)
59860
60055
  break;
59861
60056
  currentDir = parent;
@@ -59939,7 +60134,7 @@ var sbom_generate = createSwarmTool({
59939
60134
  const changedFiles = obj.changed_files;
59940
60135
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
59941
60136
  const workingDir = directory;
59942
- const outputDir = path43.isAbsolute(relativeOutputDir) ? relativeOutputDir : path43.join(workingDir, relativeOutputDir);
60137
+ const outputDir = path44.isAbsolute(relativeOutputDir) ? relativeOutputDir : path44.join(workingDir, relativeOutputDir);
59943
60138
  let manifestFiles = [];
59944
60139
  if (scope === "all") {
59945
60140
  manifestFiles = findManifestFiles(workingDir);
@@ -59962,7 +60157,7 @@ var sbom_generate = createSwarmTool({
59962
60157
  const processedFiles = [];
59963
60158
  for (const manifestFile of manifestFiles) {
59964
60159
  try {
59965
- const fullPath = path43.isAbsolute(manifestFile) ? manifestFile : path43.join(workingDir, manifestFile);
60160
+ const fullPath = path44.isAbsolute(manifestFile) ? manifestFile : path44.join(workingDir, manifestFile);
59966
60161
  if (!fs31.existsSync(fullPath)) {
59967
60162
  continue;
59968
60163
  }
@@ -59979,7 +60174,7 @@ var sbom_generate = createSwarmTool({
59979
60174
  const bom = generateCycloneDX(allComponents);
59980
60175
  const bomJson = serializeCycloneDX(bom);
59981
60176
  const filename = generateSbomFilename();
59982
- const outputPath = path43.join(outputDir, filename);
60177
+ const outputPath = path44.join(outputDir, filename);
59983
60178
  fs31.writeFileSync(outputPath, bomJson, "utf-8");
59984
60179
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
59985
60180
  try {
@@ -60023,7 +60218,7 @@ var sbom_generate = createSwarmTool({
60023
60218
  init_dist();
60024
60219
  init_create_tool();
60025
60220
  import * as fs32 from "fs";
60026
- import * as path44 from "path";
60221
+ import * as path45 from "path";
60027
60222
  var SPEC_CANDIDATES = [
60028
60223
  "openapi.json",
60029
60224
  "openapi.yaml",
@@ -60055,12 +60250,12 @@ function normalizePath2(p) {
60055
60250
  }
60056
60251
  function discoverSpecFile(cwd, specFileArg) {
60057
60252
  if (specFileArg) {
60058
- const resolvedPath = path44.resolve(cwd, specFileArg);
60059
- const normalizedCwd = cwd.endsWith(path44.sep) ? cwd : cwd + path44.sep;
60253
+ const resolvedPath = path45.resolve(cwd, specFileArg);
60254
+ const normalizedCwd = cwd.endsWith(path45.sep) ? cwd : cwd + path45.sep;
60060
60255
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
60061
60256
  throw new Error("Invalid spec_file: path traversal detected");
60062
60257
  }
60063
- const ext = path44.extname(resolvedPath).toLowerCase();
60258
+ const ext = path45.extname(resolvedPath).toLowerCase();
60064
60259
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
60065
60260
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
60066
60261
  }
@@ -60074,7 +60269,7 @@ function discoverSpecFile(cwd, specFileArg) {
60074
60269
  return resolvedPath;
60075
60270
  }
60076
60271
  for (const candidate of SPEC_CANDIDATES) {
60077
- const candidatePath = path44.resolve(cwd, candidate);
60272
+ const candidatePath = path45.resolve(cwd, candidate);
60078
60273
  if (fs32.existsSync(candidatePath)) {
60079
60274
  const stats = fs32.statSync(candidatePath);
60080
60275
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -60086,7 +60281,7 @@ function discoverSpecFile(cwd, specFileArg) {
60086
60281
  }
60087
60282
  function parseSpec(specFile) {
60088
60283
  const content = fs32.readFileSync(specFile, "utf-8");
60089
- const ext = path44.extname(specFile).toLowerCase();
60284
+ const ext = path45.extname(specFile).toLowerCase();
60090
60285
  if (ext === ".json") {
60091
60286
  return parseJsonSpec(content);
60092
60287
  }
@@ -60162,7 +60357,7 @@ function extractRoutes(cwd) {
60162
60357
  return;
60163
60358
  }
60164
60359
  for (const entry of entries) {
60165
- const fullPath = path44.join(dir, entry.name);
60360
+ const fullPath = path45.join(dir, entry.name);
60166
60361
  if (entry.isSymbolicLink()) {
60167
60362
  continue;
60168
60363
  }
@@ -60172,7 +60367,7 @@ function extractRoutes(cwd) {
60172
60367
  }
60173
60368
  walkDir(fullPath);
60174
60369
  } else if (entry.isFile()) {
60175
- const ext = path44.extname(entry.name).toLowerCase();
60370
+ const ext = path45.extname(entry.name).toLowerCase();
60176
60371
  const baseName = entry.name.toLowerCase();
60177
60372
  if (![".ts", ".js", ".mjs"].includes(ext)) {
60178
60373
  continue;
@@ -60342,7 +60537,7 @@ init_secretscan();
60342
60537
  init_tool();
60343
60538
  init_create_tool();
60344
60539
  import * as fs33 from "fs";
60345
- import * as path45 from "path";
60540
+ import * as path46 from "path";
60346
60541
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
60347
60542
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
60348
60543
  function containsControlCharacters(str) {
@@ -60371,11 +60566,11 @@ function containsWindowsAttacks(str) {
60371
60566
  }
60372
60567
  function isPathInWorkspace(filePath, workspace) {
60373
60568
  try {
60374
- const resolvedPath = path45.resolve(workspace, filePath);
60569
+ const resolvedPath = path46.resolve(workspace, filePath);
60375
60570
  const realWorkspace = fs33.realpathSync(workspace);
60376
60571
  const realResolvedPath = fs33.realpathSync(resolvedPath);
60377
- const relativePath = path45.relative(realWorkspace, realResolvedPath);
60378
- if (relativePath.startsWith("..") || path45.isAbsolute(relativePath)) {
60572
+ const relativePath = path46.relative(realWorkspace, realResolvedPath);
60573
+ if (relativePath.startsWith("..") || path46.isAbsolute(relativePath)) {
60379
60574
  return false;
60380
60575
  }
60381
60576
  return true;
@@ -60387,7 +60582,7 @@ function validatePathForRead(filePath, workspace) {
60387
60582
  return isPathInWorkspace(filePath, workspace);
60388
60583
  }
60389
60584
  function extractTSSymbols(filePath, cwd) {
60390
- const fullPath = path45.join(cwd, filePath);
60585
+ const fullPath = path46.join(cwd, filePath);
60391
60586
  if (!validatePathForRead(fullPath, cwd)) {
60392
60587
  return [];
60393
60588
  }
@@ -60539,7 +60734,7 @@ function extractTSSymbols(filePath, cwd) {
60539
60734
  });
60540
60735
  }
60541
60736
  function extractPythonSymbols(filePath, cwd) {
60542
- const fullPath = path45.join(cwd, filePath);
60737
+ const fullPath = path46.join(cwd, filePath);
60543
60738
  if (!validatePathForRead(fullPath, cwd)) {
60544
60739
  return [];
60545
60740
  }
@@ -60622,7 +60817,7 @@ var symbols = createSwarmTool({
60622
60817
  }, null, 2);
60623
60818
  }
60624
60819
  const cwd = directory;
60625
- const ext = path45.extname(file3);
60820
+ const ext = path46.extname(file3);
60626
60821
  if (containsControlCharacters(file3)) {
60627
60822
  return JSON.stringify({
60628
60823
  file: file3,
@@ -60694,7 +60889,7 @@ init_dist();
60694
60889
  init_utils();
60695
60890
  init_create_tool();
60696
60891
  import * as fs34 from "fs";
60697
- import * as path46 from "path";
60892
+ import * as path47 from "path";
60698
60893
  var MAX_TEXT_LENGTH = 200;
60699
60894
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
60700
60895
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -60765,9 +60960,9 @@ function validatePathsInput(paths, cwd) {
60765
60960
  return { error: "paths contains path traversal", resolvedPath: null };
60766
60961
  }
60767
60962
  try {
60768
- const resolvedPath = path46.resolve(paths);
60769
- const normalizedCwd = path46.resolve(cwd);
60770
- const normalizedResolved = path46.resolve(resolvedPath);
60963
+ const resolvedPath = path47.resolve(paths);
60964
+ const normalizedCwd = path47.resolve(cwd);
60965
+ const normalizedResolved = path47.resolve(resolvedPath);
60771
60966
  if (!normalizedResolved.startsWith(normalizedCwd)) {
60772
60967
  return {
60773
60968
  error: "paths must be within the current working directory",
@@ -60783,7 +60978,7 @@ function validatePathsInput(paths, cwd) {
60783
60978
  }
60784
60979
  }
60785
60980
  function isSupportedExtension(filePath) {
60786
- const ext = path46.extname(filePath).toLowerCase();
60981
+ const ext = path47.extname(filePath).toLowerCase();
60787
60982
  return SUPPORTED_EXTENSIONS2.has(ext);
60788
60983
  }
60789
60984
  function findSourceFiles3(dir, files = []) {
@@ -60798,7 +60993,7 @@ function findSourceFiles3(dir, files = []) {
60798
60993
  if (SKIP_DIRECTORIES3.has(entry)) {
60799
60994
  continue;
60800
60995
  }
60801
- const fullPath = path46.join(dir, entry);
60996
+ const fullPath = path47.join(dir, entry);
60802
60997
  let stat2;
60803
60998
  try {
60804
60999
  stat2 = fs34.statSync(fullPath);
@@ -60910,7 +61105,7 @@ var todo_extract = createSwarmTool({
60910
61105
  filesToScan.push(scanPath);
60911
61106
  } else {
60912
61107
  const errorResult = {
60913
- error: `unsupported file extension: ${path46.extname(scanPath)}`,
61108
+ error: `unsupported file extension: ${path47.extname(scanPath)}`,
60914
61109
  total: 0,
60915
61110
  byPriority: { high: 0, medium: 0, low: 0 },
60916
61111
  entries: []
@@ -60954,9 +61149,10 @@ var todo_extract = createSwarmTool({
60954
61149
  });
60955
61150
  // src/tools/update-task-status.ts
60956
61151
  init_tool();
61152
+ init_schema();
60957
61153
  init_manager2();
60958
61154
  import * as fs35 from "fs";
60959
- import * as path47 from "path";
61155
+ import * as path48 from "path";
60960
61156
  init_create_tool();
60961
61157
  var VALID_STATUSES2 = [
60962
61158
  "pending",
@@ -60979,6 +61175,23 @@ function validateTaskId(taskId) {
60979
61175
  }
60980
61176
  function checkReviewerGate(taskId, workingDirectory) {
60981
61177
  try {
61178
+ const resolvedDir = workingDirectory ?? process.cwd();
61179
+ try {
61180
+ const evidencePath = path48.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
61181
+ const raw = fs35.readFileSync(evidencePath, "utf-8");
61182
+ const evidence = JSON.parse(raw);
61183
+ if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
61184
+ const allGatesMet = evidence.required_gates.every((gate) => evidence.gates[gate] != null);
61185
+ if (allGatesMet) {
61186
+ return { blocked: false, reason: "" };
61187
+ }
61188
+ const missingGates = evidence.required_gates.filter((gate) => evidence.gates[gate] == null);
61189
+ return {
61190
+ blocked: true,
61191
+ reason: `Task ${taskId} is missing required gates: [${missingGates.join(", ")}]. ` + `Required: [${evidence.required_gates.join(", ")}]. ` + `Completed: [${Object.keys(evidence.gates).join(", ")}]. ` + `Delegate the missing gate agents before marking task as completed.`
61192
+ };
61193
+ }
61194
+ } catch {}
60982
61195
  if (swarmState.agentSessions.size === 0) {
60983
61196
  return { blocked: false, reason: "" };
60984
61197
  }
@@ -61003,13 +61216,9 @@ function checkReviewerGate(taskId, workingDirectory) {
61003
61216
  const state = getTaskState(session, taskId);
61004
61217
  stateEntries.push(`${sessionId}: ${state}`);
61005
61218
  }
61006
- const allIdle = stateEntries.length > 0 && stateEntries.every((e) => e.endsWith(": idle"));
61007
- if (allIdle) {
61008
- return { blocked: false, reason: "" };
61009
- }
61010
61219
  try {
61011
- const resolvedDir = workingDirectory ?? process.cwd();
61012
- const planPath = path47.join(resolvedDir, ".swarm", "plan.json");
61220
+ const resolvedDir2 = workingDirectory ?? process.cwd();
61221
+ const planPath = path48.join(resolvedDir2, ".swarm", "plan.json");
61013
61222
  const planRaw = fs35.readFileSync(planPath, "utf-8");
61014
61223
  const plan = JSON.parse(planRaw);
61015
61224
  for (const planPhase of plan.phases ?? []) {
@@ -61029,6 +61238,52 @@ function checkReviewerGate(taskId, workingDirectory) {
61029
61238
  return { blocked: false, reason: "" };
61030
61239
  }
61031
61240
  }
61241
+ function recoverTaskStateFromDelegations(taskId) {
61242
+ let hasReviewer = false;
61243
+ let hasTestEngineer = false;
61244
+ for (const [sessionId, chain] of swarmState.delegationChains) {
61245
+ const session = swarmState.agentSessions.get(sessionId);
61246
+ if (session && (session.currentTaskId === taskId || session.lastCoderDelegationTaskId === taskId)) {
61247
+ for (const delegation of chain) {
61248
+ const target = stripKnownSwarmPrefix(delegation.to);
61249
+ if (target === "reviewer")
61250
+ hasReviewer = true;
61251
+ if (target === "test_engineer")
61252
+ hasTestEngineer = true;
61253
+ }
61254
+ }
61255
+ }
61256
+ if (!hasReviewer && !hasTestEngineer)
61257
+ return;
61258
+ for (const [, session] of swarmState.agentSessions) {
61259
+ if (!(session.taskWorkflowStates instanceof Map))
61260
+ continue;
61261
+ const currentState = getTaskState(session, taskId);
61262
+ if (currentState === "tests_run" || currentState === "complete")
61263
+ continue;
61264
+ if (hasReviewer && currentState === "idle") {
61265
+ try {
61266
+ advanceTaskState(session, taskId, "coder_delegated");
61267
+ } catch {}
61268
+ }
61269
+ if (hasReviewer) {
61270
+ const stateNow = getTaskState(session, taskId);
61271
+ if (stateNow === "coder_delegated" || stateNow === "pre_check_passed") {
61272
+ try {
61273
+ advanceTaskState(session, taskId, "reviewer_run");
61274
+ } catch {}
61275
+ }
61276
+ }
61277
+ if (hasTestEngineer) {
61278
+ const stateNow = getTaskState(session, taskId);
61279
+ if (stateNow === "reviewer_run") {
61280
+ try {
61281
+ advanceTaskState(session, taskId, "tests_run");
61282
+ } catch {}
61283
+ }
61284
+ }
61285
+ }
61286
+ }
61032
61287
  async function executeUpdateTaskStatus(args2, fallbackDir) {
61033
61288
  const statusError = validateStatus(args2.status);
61034
61289
  if (statusError) {
@@ -61074,8 +61329,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
61074
61329
  };
61075
61330
  }
61076
61331
  }
61077
- normalizedDir = path47.normalize(args2.working_directory);
61078
- const pathParts = normalizedDir.split(path47.sep);
61332
+ normalizedDir = path48.normalize(args2.working_directory);
61333
+ const pathParts = normalizedDir.split(path48.sep);
61079
61334
  if (pathParts.includes("..")) {
61080
61335
  return {
61081
61336
  success: false,
@@ -61085,10 +61340,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
61085
61340
  ]
61086
61341
  };
61087
61342
  }
61088
- const resolvedDir = path47.resolve(normalizedDir);
61343
+ const resolvedDir = path48.resolve(normalizedDir);
61089
61344
  try {
61090
61345
  const realPath = fs35.realpathSync(resolvedDir);
61091
- const planPath = path47.join(realPath, ".swarm", "plan.json");
61346
+ const planPath = path48.join(realPath, ".swarm", "plan.json");
61092
61347
  if (!fs35.existsSync(planPath)) {
61093
61348
  return {
61094
61349
  success: false,
@@ -61112,6 +61367,7 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
61112
61367
  directory = fallbackDir ?? process.cwd();
61113
61368
  }
61114
61369
  if (args2.status === "completed") {
61370
+ recoverTaskStateFromDelegations(args2.task_id);
61115
61371
  const reviewerCheck = checkReviewerGate(args2.task_id, directory);
61116
61372
  if (reviewerCheck.blocked) {
61117
61373
  return {
@@ -61245,7 +61501,7 @@ var OpenCodeSwarm = async (ctx) => {
61245
61501
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
61246
61502
  preflightTriggerManager = new PTM(automationConfig);
61247
61503
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
61248
- const swarmDir = path48.resolve(ctx.directory, ".swarm");
61504
+ const swarmDir = path49.resolve(ctx.directory, ".swarm");
61249
61505
  statusArtifact = new ASA(swarmDir);
61250
61506
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
61251
61507
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {