opencode-swarm 6.13.2 → 6.13.3

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
@@ -14001,7 +14001,23 @@ var init_evidence_schema = __esm(() => {
14001
14001
  task_count: exports_external.number().int().min(1),
14002
14002
  task_complexity: exports_external.enum(["trivial", "simple", "moderate", "complex"]),
14003
14003
  top_rejection_reasons: exports_external.array(exports_external.string()).default([]),
14004
- lessons_learned: exports_external.array(exports_external.string()).max(5).default([])
14004
+ lessons_learned: exports_external.array(exports_external.string()).max(5).default([]),
14005
+ user_directives: exports_external.array(exports_external.object({
14006
+ directive: exports_external.string().min(1),
14007
+ category: exports_external.enum([
14008
+ "tooling",
14009
+ "code_style",
14010
+ "architecture",
14011
+ "process",
14012
+ "other"
14013
+ ]),
14014
+ scope: exports_external.enum(["session", "project", "global"])
14015
+ })).default([]),
14016
+ approaches_tried: exports_external.array(exports_external.object({
14017
+ approach: exports_external.string().min(1),
14018
+ result: exports_external.enum(["success", "failure", "partial"]),
14019
+ abandoned_reason: exports_external.string().optional()
14020
+ })).max(10).default([])
14005
14021
  });
14006
14022
  SyntaxEvidenceSchema = BaseEvidenceSchema.extend({
14007
14023
  type: exports_external.literal("syntax"),
@@ -31423,7 +31439,7 @@ var init_preflight_integration = __esm(() => {
31423
31439
  });
31424
31440
 
31425
31441
  // src/index.ts
31426
- import * as path32 from "path";
31442
+ import * as path31 from "path";
31427
31443
 
31428
31444
  // src/tools/tool-names.ts
31429
31445
  var TOOL_NAMES = [
@@ -32481,6 +32497,37 @@ Identify 1-3 relevant domains from the task requirements.
32481
32497
  Call {{AGENT_PREFIX}}sme once per domain, serially. Max 3 SME calls per project phase.
32482
32498
  Re-consult if a new domain emerges or if significant changes require fresh evaluation.
32483
32499
  Cache guidance in context.md.
32500
+ ### MODE: PRE-PHASE BRIEFING (Required Before Starting Any Phase)
32501
+
32502
+ Before creating or resuming any plan, you MUST read the previous phase's retrospective.
32503
+
32504
+ **Phase 2+ (continuing a multi-phase project):**
32505
+ 1. Check \`.swarm/evidence/retro-{N-1}/evidence.json\` for the previous phase's retrospective
32506
+ 2. If it exists: read and internalize \`lessons_learned\` and \`top_rejection_reasons\`
32507
+ 3. If it does NOT exist: note this as a process gap, but proceed
32508
+ 4. Print a briefing acknowledgment:
32509
+ \`\`\`
32510
+ \u2192 BRIEFING: Read Phase {N-1} retrospective.
32511
+ Key lessons: {list 1-3 most relevant lessons}
32512
+ Applying to Phase {N}: {one sentence on how you'll apply them}
32513
+ \`\`\`
32514
+
32515
+ **Phase 1 (starting any new project):**
32516
+ 1. Scan \`.swarm/evidence/\` for any \`retro-*\` bundles from prior projects
32517
+ 2. If found: review the 1-3 most recent retrospectives for relevant lessons
32518
+ 3. Pay special attention to \`user_directives\` \u2014 these carry across projects
32519
+ 4. Print a briefing acknowledgment:
32520
+ \`\`\`
32521
+ \u2192 BRIEFING: Reviewed {N} historical retrospectives from this workspace.
32522
+ Relevant lessons: {list applicable lessons}
32523
+ User directives carried forward: {list any persistent directives}
32524
+ \`\`\`
32525
+ OR if no historical retros exist:
32526
+ \`\`\`
32527
+ \u2192 BRIEFING: No historical retrospectives found. Starting fresh.
32528
+ \`\`\`
32529
+
32530
+ This briefing is a HARD REQUIREMENT for ALL phases. Skipping it is a process violation.
32484
32531
 
32485
32532
  ### MODE: PLAN
32486
32533
 
@@ -32497,6 +32544,15 @@ TASK GRANULARITY RULES:
32497
32544
  - NEVER write a task with compound verbs: "implement X and add Y and update Z" = 3 tasks, not 1. Split before writing to plan.
32498
32545
  - Coder receives ONE task. You make ALL scope decisions in the plan. Coder makes zero scope decisions.
32499
32546
 
32547
+ PHASE COUNT GUIDANCE:
32548
+ - Plans with 5+ tasks SHOULD be split into at least 2 phases.
32549
+ - Plans with 10+ tasks MUST be split into at least 3 phases.
32550
+ - Each phase should be a coherent unit of work that can be reviewed and learned from
32551
+ before proceeding to the next.
32552
+ - Single-phase plans are acceptable ONLY for small projects (1-4 tasks).
32553
+ - Rationale: Retrospectives at phase boundaries capture lessons that improve subsequent
32554
+ phases. A single-phase plan gets zero iterative learning benefit.
32555
+
32500
32556
  Create .swarm/context.md
32501
32557
  - Decisions, patterns, SME cache, file map
32502
32558
 
@@ -32634,6 +32690,54 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
32634
32690
 
32635
32691
  5o. Update plan.md [x], proceed to next task.
32636
32692
 
32693
+ ## \u26D4 RETROSPECTIVE GATE
32694
+
32695
+ **MANDATORY before calling phase_complete.** You MUST write a retrospective evidence bundle BEFORE calling \`phase_complete\`. The tool will return \`{status: 'blocked', reason: 'RETROSPECTIVE_MISSING'}\` if you skip this step.
32696
+
32697
+ **How to write the retrospective:**
32698
+
32699
+ Use the evidence manager tool to write a bundle at \`retro-{N}\` (where N is the phase number being completed):
32700
+
32701
+ \`\`\`json
32702
+ {
32703
+ "type": "retrospective",
32704
+ "phase_number": <N>,
32705
+ "verdict": "pass",
32706
+ "reviewer_rejections": <count>,
32707
+ "coder_revisions": <count>,
32708
+ "test_failures": <count>,
32709
+ "security_findings": <count>,
32710
+ "lessons_learned": ["lesson 1 (max 5)", "lesson 2"],
32711
+ "top_rejection_reasons": ["reason 1"],
32712
+ "user_directives": [],
32713
+ "approaches_tried": [],
32714
+ "task_complexity": "low|medium|high",
32715
+ "timestamp": "<ISO 8601>",
32716
+ "agent": "architect",
32717
+ "metadata": { "plan_id": "<current plan title from .swarm/plan.json>" }
32718
+ }
32719
+ \`\`\`
32720
+
32721
+ **Required field rules:**
32722
+ - \`verdict\` MUST be \`"pass"\` \u2014 a verdict of \`"fail"\` or missing verdict blocks phase_complete
32723
+ - \`phase_number\` MUST match the phase number you are completing
32724
+ - \`lessons_learned\` should be 3-5 concrete, actionable items from this phase
32725
+ - Write the bundle as task_id \`retro-{N}\` (e.g., \`retro-1\` for Phase 1, \`retro-2\` for Phase 2)
32726
+ - \`metadata.plan_id\` should be set to the current project's plan title (from \`.swarm/plan.json\` header). This enables cross-project filtering in the retrospective injection system.
32727
+
32728
+ ### Additional retrospective fields (capture when applicable):
32729
+ - \`user_directives\`: Any corrections or preferences the user expressed during this phase
32730
+ - \`directive\`: what the user said (non-empty string)
32731
+ - \`category\`: \`tooling\` | \`code_style\` | \`architecture\` | \`process\` | \`other\`
32732
+ - \`scope\`: \`session\` (one-time, do not carry forward) | \`project\` (persist to context.md) | \`global\` (user preference)
32733
+ - \`approaches_tried\`: Approaches attempted during this phase (max 10)
32734
+ - \`approach\`: what was tried (non-empty string)
32735
+ - \`result\`: \`success\` | \`failure\` | \`partial\`
32736
+ - \`abandoned_reason\`: why it was abandoned (required when result is \`failure\` or \`partial\`)
32737
+
32738
+ **\u26A0\uFE0F WARNING:** Calling \`phase_complete(N)\` without a valid \`retro-N\` bundle will be BLOCKED. The error response will be:
32739
+ \`{ "status": "blocked", "reason": "RETROSPECTIVE_MISSING" }\`
32740
+
32637
32741
  ### MODE: PHASE-WRAP
32638
32742
  1. {{AGENT_PREFIX}}explorer - Rescan
32639
32743
  2. {{AGENT_PREFIX}}docs - Update documentation for all changes in this phase. Provide:
@@ -37344,8 +37448,7 @@ ${originalText}`;
37344
37448
  };
37345
37449
  }
37346
37450
  // src/hooks/system-enhancer.ts
37347
- import * as fs11 from "fs";
37348
- import * as path17 from "path";
37451
+ init_manager();
37349
37452
  init_manager2();
37350
37453
 
37351
37454
  // src/services/decision-drift-analyzer.ts
@@ -37722,6 +37825,170 @@ function estimateContentType(text) {
37722
37825
  }
37723
37826
  return "prose";
37724
37827
  }
37828
+ async function buildRetroInjection(directory, currentPhaseNumber, currentPlanTitle) {
37829
+ try {
37830
+ const prevPhase = currentPhaseNumber - 1;
37831
+ if (prevPhase >= 1) {
37832
+ const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
37833
+ if (bundle && bundle.entries.length > 0) {
37834
+ const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
37835
+ if (retroEntry && retroEntry.verdict !== "fail") {
37836
+ const lessons = retroEntry.lessons_learned ?? [];
37837
+ const rejections = retroEntry.top_rejection_reasons ?? [];
37838
+ const nonSessionDirectives = (retroEntry.user_directives ?? []).filter((d) => d.scope !== "session");
37839
+ let block = `## Previous Phase Retrospective (Phase ${prevPhase})
37840
+ **Outcome:** ${retroEntry.summary ?? "Phase completed."}
37841
+ **Rejection reasons:** ${rejections.join(", ") || "None"}
37842
+ **Lessons learned:**
37843
+ ${lessons.map((l) => `- ${l}`).join(`
37844
+ `)}
37845
+
37846
+ \u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
37847
+ if (nonSessionDirectives.length > 0) {
37848
+ const top5 = nonSessionDirectives.slice(0, 5);
37849
+ block += `
37850
+
37851
+ ## User Directives (from Phase ${prevPhase})
37852
+ ${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
37853
+ `)}`;
37854
+ }
37855
+ return block;
37856
+ }
37857
+ }
37858
+ const taskIds = await listEvidenceTaskIds(directory);
37859
+ const retroIds = taskIds.filter((id) => id.startsWith("retro-"));
37860
+ let latestRetro = null;
37861
+ for (const taskId of retroIds) {
37862
+ const b = await loadEvidence(directory, taskId);
37863
+ if (b && b.entries.length > 0) {
37864
+ for (const entry of b.entries) {
37865
+ if (entry.type === "retrospective") {
37866
+ const retro = entry;
37867
+ if (retro.verdict !== "fail") {
37868
+ if (latestRetro === null || retro.phase_number > latestRetro.phase) {
37869
+ latestRetro = { entry: retro, phase: retro.phase_number };
37870
+ }
37871
+ }
37872
+ }
37873
+ }
37874
+ }
37875
+ }
37876
+ if (latestRetro) {
37877
+ const { entry, phase } = latestRetro;
37878
+ const lessons = entry.lessons_learned ?? [];
37879
+ const rejections = entry.top_rejection_reasons ?? [];
37880
+ const nonSessionDirectives = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
37881
+ let block = `## Previous Phase Retrospective (Phase ${phase})
37882
+ **Outcome:** ${entry.summary ?? "Phase completed."}
37883
+ **Rejection reasons:** ${rejections.join(", ") || "None"}
37884
+ **Lessons learned:**
37885
+ ${lessons.map((l) => `- ${l}`).join(`
37886
+ `)}
37887
+
37888
+ \u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
37889
+ if (nonSessionDirectives.length > 0) {
37890
+ const top5 = nonSessionDirectives.slice(0, 5);
37891
+ block += `
37892
+
37893
+ ## User Directives (from Phase ${phase})
37894
+ ${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
37895
+ `)}`;
37896
+ }
37897
+ return block;
37898
+ }
37899
+ return null;
37900
+ }
37901
+ const allTaskIds = await listEvidenceTaskIds(directory);
37902
+ const allRetroIds = allTaskIds.filter((id) => id.startsWith("retro-"));
37903
+ if (allRetroIds.length === 0) {
37904
+ return null;
37905
+ }
37906
+ const allRetros = [];
37907
+ const cutoffMs = 30 * 24 * 60 * 60 * 1000;
37908
+ const now = Date.now();
37909
+ for (const taskId of allRetroIds) {
37910
+ const b = await loadEvidence(directory, taskId);
37911
+ if (!b)
37912
+ continue;
37913
+ for (const e of b.entries) {
37914
+ if (e.type === "retrospective") {
37915
+ const retro = e;
37916
+ if (retro.verdict === "fail")
37917
+ continue;
37918
+ if (currentPlanTitle && typeof retro.metadata === "object" && retro.metadata !== null && "plan_id" in retro.metadata && retro.metadata.plan_id === currentPlanTitle)
37919
+ continue;
37920
+ const ts = retro.timestamp ?? b.created_at;
37921
+ const ageMs = now - new Date(ts).getTime();
37922
+ if (isNaN(ageMs) || ageMs > cutoffMs)
37923
+ continue;
37924
+ allRetros.push({ entry: retro, timestamp: ts });
37925
+ }
37926
+ }
37927
+ }
37928
+ if (allRetros.length === 0) {
37929
+ return null;
37930
+ }
37931
+ allRetros.sort((a, b) => {
37932
+ const ta = new Date(a.timestamp).getTime();
37933
+ const tb = new Date(b.timestamp).getTime();
37934
+ if (isNaN(ta) && isNaN(tb))
37935
+ return 0;
37936
+ if (isNaN(ta))
37937
+ return 1;
37938
+ if (isNaN(tb))
37939
+ return -1;
37940
+ return tb - ta;
37941
+ });
37942
+ const top3 = allRetros.slice(0, 3);
37943
+ const lines = [
37944
+ "## Historical Lessons (from recent prior projects)"
37945
+ ];
37946
+ lines.push("Most recent retrospectives in this workspace:");
37947
+ const allCarriedDirectives = [];
37948
+ for (const { entry, timestamp } of top3) {
37949
+ const date9 = timestamp.split("T")[0] ?? "unknown";
37950
+ const summary = entry.summary ?? `Phase ${entry.phase_number} completed`;
37951
+ const topLesson = entry.lessons_learned?.[0] ?? "No lessons recorded";
37952
+ lines.push(`- Phase ${entry.phase_number} (${date9}): ${summary}`);
37953
+ lines.push(` Key lesson: ${topLesson}`);
37954
+ const nonSession = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
37955
+ allCarriedDirectives.push(...nonSession);
37956
+ }
37957
+ if (allCarriedDirectives.length > 0) {
37958
+ const top5 = allCarriedDirectives.slice(0, 5);
37959
+ lines.push("User directives carried forward:");
37960
+ for (const d of top5) {
37961
+ lines.push(`- [${d.category}] ${d.directive}`);
37962
+ }
37963
+ }
37964
+ const tier2Block = lines.join(`
37965
+ `);
37966
+ return tier2Block.length <= 800 ? tier2Block : `${tier2Block.substring(0, 797)}...`;
37967
+ } catch {
37968
+ return null;
37969
+ }
37970
+ }
37971
+ async function buildCoderRetroInjection(directory, currentPhaseNumber) {
37972
+ try {
37973
+ const prevPhase = currentPhaseNumber - 1;
37974
+ if (prevPhase < 1)
37975
+ return null;
37976
+ const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
37977
+ if (!bundle || bundle.entries.length === 0)
37978
+ return null;
37979
+ const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
37980
+ if (!retroEntry || retroEntry.verdict === "fail")
37981
+ return null;
37982
+ const lessons = retroEntry.lessons_learned ?? [];
37983
+ const summaryLine = `[SWARM RETROSPECTIVE] From Phase ${prevPhase}:${retroEntry.summary ? " " + retroEntry.summary : ""}`;
37984
+ const allLines = [summaryLine, ...lessons];
37985
+ const text = allLines.join(`
37986
+ `);
37987
+ return text.length <= 400 ? text : `${text.substring(0, 397)}...`;
37988
+ } catch {
37989
+ return null;
37990
+ }
37991
+ }
37725
37992
  function createSystemEnhancerHook(config3, directory) {
37726
37993
  const enabled = config3.hooks?.system_enhancer !== false;
37727
37994
  if (!enabled) {
@@ -37841,43 +38108,27 @@ function createSystemEnhancerHook(config3, directory) {
37841
38108
  }
37842
38109
  }
37843
38110
  }
38111
+ if (baseRole === "coder") {
38112
+ try {
38113
+ const currentPhaseNum_coder = plan2?.current_phase ?? 1;
38114
+ const coderRetro = await buildCoderRetroInjection(directory, currentPhaseNum_coder);
38115
+ if (coderRetro) {
38116
+ tryInject(coderRetro);
38117
+ }
38118
+ } catch {}
38119
+ }
37844
38120
  const sessionId_retro = _input.sessionID;
37845
38121
  const activeAgent_retro = swarmState.activeAgent.get(sessionId_retro ?? "");
37846
38122
  const isArchitect2 = !activeAgent_retro || stripKnownSwarmPrefix(activeAgent_retro) === "architect";
37847
38123
  if (isArchitect2) {
37848
38124
  try {
37849
- const evidenceDir = path17.join(directory, ".swarm", "evidence");
37850
- if (fs11.existsSync(evidenceDir)) {
37851
- const files = fs11.readdirSync(evidenceDir).filter((f) => f.endsWith(".json")).sort().reverse();
37852
- for (const file3 of files.slice(0, 5)) {
37853
- let content;
37854
- try {
37855
- content = JSON.parse(fs11.readFileSync(path17.join(evidenceDir, file3), "utf-8"));
37856
- } catch {
37857
- continue;
37858
- }
37859
- if (content !== null && typeof content === "object" && content.type === "retrospective") {
37860
- const retro = content;
37861
- const hints = [];
37862
- if (retro.reviewer_rejections > 2) {
37863
- hints.push(`Phase ${retro.phase_number} had ${retro.reviewer_rejections} reviewer rejections.`);
37864
- }
37865
- if (retro.top_rejection_reasons.length > 0) {
37866
- hints.push(`Common rejection reasons: ${retro.top_rejection_reasons.join(", ")}.`);
37867
- }
37868
- if (retro.lessons_learned.length > 0) {
37869
- hints.push(`Lessons: ${retro.lessons_learned.join("; ")}.`);
37870
- }
37871
- if (hints.length > 0) {
37872
- const retroHint = `[SWARM RETROSPECTIVE] From Phase ${retro.phase_number}: ${hints.join(" ")}`;
37873
- if (retroHint.length <= 800) {
37874
- tryInject(retroHint);
37875
- } else {
37876
- tryInject(`${retroHint.substring(0, 800)}...`);
37877
- }
37878
- }
37879
- break;
37880
- }
38125
+ const currentPhaseNum = plan2?.current_phase ?? 1;
38126
+ const retroText = await buildRetroInjection(directory, currentPhaseNum, plan2?.title ?? undefined);
38127
+ if (retroText) {
38128
+ if (retroText.length <= 1600) {
38129
+ tryInject(retroText);
38130
+ } else {
38131
+ tryInject(`${retroText.substring(0, 1600)}...`);
37881
38132
  }
37882
38133
  }
37883
38134
  } catch {}
@@ -38137,43 +38388,18 @@ function createSystemEnhancerHook(config3, directory) {
38137
38388
  const isArchitect_b = !activeAgent_retro_b || stripKnownSwarmPrefix(activeAgent_retro_b) === "architect";
38138
38389
  if (isArchitect_b) {
38139
38390
  try {
38140
- const evidenceDir_b = path17.join(directory, ".swarm", "evidence");
38141
- if (fs11.existsSync(evidenceDir_b)) {
38142
- const files_b = fs11.readdirSync(evidenceDir_b).filter((f) => f.endsWith(".json")).sort().reverse();
38143
- for (const file3 of files_b.slice(0, 5)) {
38144
- let content_b;
38145
- try {
38146
- content_b = JSON.parse(fs11.readFileSync(path17.join(evidenceDir_b, file3), "utf-8"));
38147
- } catch {
38148
- continue;
38149
- }
38150
- if (content_b !== null && typeof content_b === "object" && content_b.type === "retrospective") {
38151
- const retro_b = content_b;
38152
- const hints_b = [];
38153
- if (retro_b.reviewer_rejections > 2) {
38154
- hints_b.push(`Phase ${retro_b.phase_number} had ${retro_b.reviewer_rejections} reviewer rejections.`);
38155
- }
38156
- if (retro_b.top_rejection_reasons.length > 0) {
38157
- hints_b.push(`Common rejection reasons: ${retro_b.top_rejection_reasons.join(", ")}.`);
38158
- }
38159
- if (retro_b.lessons_learned.length > 0) {
38160
- hints_b.push(`Lessons: ${retro_b.lessons_learned.join("; ")}.`);
38161
- }
38162
- if (hints_b.length > 0) {
38163
- const retroHint_b = `[SWARM RETROSPECTIVE] From Phase ${retro_b.phase_number}: ${hints_b.join(" ")}`;
38164
- const retroText = retroHint_b.length <= 800 ? retroHint_b : `${retroHint_b.substring(0, 800)}...`;
38165
- candidates.push({
38166
- id: `candidate-${idCounter++}`,
38167
- kind: "phase",
38168
- text: retroText,
38169
- tokens: estimateTokens(retroText),
38170
- priority: 2,
38171
- metadata: { contentType: "prose" }
38172
- });
38173
- }
38174
- break;
38175
- }
38176
- }
38391
+ const currentPhaseNum_b = plan?.current_phase ?? 1;
38392
+ const retroText_b = await buildRetroInjection(directory, currentPhaseNum_b, plan?.title ?? undefined);
38393
+ if (retroText_b) {
38394
+ const text = retroText_b.length <= 1600 ? retroText_b : `${retroText_b.substring(0, 1597)}...`;
38395
+ candidates.push({
38396
+ id: `candidate-${idCounter++}`,
38397
+ kind: "phase",
38398
+ text,
38399
+ tokens: estimateTokens(text),
38400
+ priority: 2,
38401
+ metadata: { contentType: "prose" }
38402
+ });
38177
38403
  }
38178
38404
  } catch {}
38179
38405
  if (mode_b !== "DISCOVER") {
@@ -38211,6 +38437,24 @@ function createSystemEnhancerHook(config3, directory) {
38211
38437
  }
38212
38438
  }
38213
38439
  }
38440
+ const activeAgent_coder_b = swarmState.activeAgent.get(_input.sessionID ?? "");
38441
+ const isCoder_b = activeAgent_coder_b && stripKnownSwarmPrefix(activeAgent_coder_b) === "coder";
38442
+ if (isCoder_b) {
38443
+ try {
38444
+ const currentPhaseNum_coder_b = plan?.current_phase ?? 1;
38445
+ const coderRetro_b = await buildCoderRetroInjection(directory, currentPhaseNum_coder_b);
38446
+ if (coderRetro_b) {
38447
+ candidates.push({
38448
+ id: `candidate-${idCounter++}`,
38449
+ kind: "agent_context",
38450
+ text: coderRetro_b,
38451
+ tokens: estimateTokens(coderRetro_b),
38452
+ priority: 2,
38453
+ metadata: { contentType: "prose" }
38454
+ });
38455
+ }
38456
+ } catch {}
38457
+ }
38214
38458
  const automationCapabilities_b = config3.automation?.capabilities;
38215
38459
  if (automationCapabilities_b?.decision_drift_detection === true && sessionId_retro_b) {
38216
38460
  const activeAgentForDrift_b = swarmState.activeAgent.get(sessionId_retro_b ?? "");
@@ -38465,8 +38709,8 @@ init_dist();
38465
38709
 
38466
38710
  // src/build/discovery.ts
38467
38711
  init_dist();
38468
- import * as fs12 from "fs";
38469
- import * as path18 from "path";
38712
+ import * as fs11 from "fs";
38713
+ import * as path17 from "path";
38470
38714
  var ECOSYSTEMS = [
38471
38715
  {
38472
38716
  ecosystem: "node",
@@ -38578,18 +38822,18 @@ function findBuildFiles(workingDir, patterns) {
38578
38822
  if (pattern.includes("*")) {
38579
38823
  const dir = workingDir;
38580
38824
  try {
38581
- const files = fs12.readdirSync(dir);
38825
+ const files = fs11.readdirSync(dir);
38582
38826
  const matches = files.filter((f) => {
38583
38827
  const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
38584
38828
  return regex.test(f);
38585
38829
  });
38586
38830
  if (matches.length > 0) {
38587
- return path18.join(dir, matches[0]);
38831
+ return path17.join(dir, matches[0]);
38588
38832
  }
38589
38833
  } catch {}
38590
38834
  } else {
38591
- const filePath = path18.join(workingDir, pattern);
38592
- if (fs12.existsSync(filePath)) {
38835
+ const filePath = path17.join(workingDir, pattern);
38836
+ if (fs11.existsSync(filePath)) {
38593
38837
  return filePath;
38594
38838
  }
38595
38839
  }
@@ -38597,12 +38841,12 @@ function findBuildFiles(workingDir, patterns) {
38597
38841
  return null;
38598
38842
  }
38599
38843
  function getRepoDefinedScripts(workingDir, scripts) {
38600
- const packageJsonPath = path18.join(workingDir, "package.json");
38601
- if (!fs12.existsSync(packageJsonPath)) {
38844
+ const packageJsonPath = path17.join(workingDir, "package.json");
38845
+ if (!fs11.existsSync(packageJsonPath)) {
38602
38846
  return [];
38603
38847
  }
38604
38848
  try {
38605
- const content = fs12.readFileSync(packageJsonPath, "utf-8");
38849
+ const content = fs11.readFileSync(packageJsonPath, "utf-8");
38606
38850
  const pkg = JSON.parse(content);
38607
38851
  if (!pkg.scripts || typeof pkg.scripts !== "object") {
38608
38852
  return [];
@@ -38638,8 +38882,8 @@ function findAllBuildFiles(workingDir) {
38638
38882
  const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
38639
38883
  findFilesRecursive(workingDir, regex, allBuildFiles);
38640
38884
  } else {
38641
- const filePath = path18.join(workingDir, pattern);
38642
- if (fs12.existsSync(filePath)) {
38885
+ const filePath = path17.join(workingDir, pattern);
38886
+ if (fs11.existsSync(filePath)) {
38643
38887
  allBuildFiles.add(filePath);
38644
38888
  }
38645
38889
  }
@@ -38649,9 +38893,9 @@ function findAllBuildFiles(workingDir) {
38649
38893
  }
38650
38894
  function findFilesRecursive(dir, regex, results) {
38651
38895
  try {
38652
- const entries = fs12.readdirSync(dir, { withFileTypes: true });
38896
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
38653
38897
  for (const entry of entries) {
38654
- const fullPath = path18.join(dir, entry.name);
38898
+ const fullPath = path17.join(dir, entry.name);
38655
38899
  if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
38656
38900
  findFilesRecursive(fullPath, regex, results);
38657
38901
  } else if (entry.isFile() && regex.test(entry.name)) {
@@ -38892,8 +39136,8 @@ var build_check = tool({
38892
39136
  // src/tools/checkpoint.ts
38893
39137
  init_tool();
38894
39138
  import { spawnSync } from "child_process";
38895
- import * as fs13 from "fs";
38896
- import * as path19 from "path";
39139
+ import * as fs12 from "fs";
39140
+ import * as path18 from "path";
38897
39141
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
38898
39142
  var MAX_LABEL_LENGTH = 100;
38899
39143
  var GIT_TIMEOUT_MS = 30000;
@@ -38944,13 +39188,13 @@ function validateLabel(label) {
38944
39188
  return null;
38945
39189
  }
38946
39190
  function getCheckpointLogPath() {
38947
- return path19.join(process.cwd(), CHECKPOINT_LOG_PATH);
39191
+ return path18.join(process.cwd(), CHECKPOINT_LOG_PATH);
38948
39192
  }
38949
39193
  function readCheckpointLog() {
38950
39194
  const logPath = getCheckpointLogPath();
38951
39195
  try {
38952
- if (fs13.existsSync(logPath)) {
38953
- const content = fs13.readFileSync(logPath, "utf-8");
39196
+ if (fs12.existsSync(logPath)) {
39197
+ const content = fs12.readFileSync(logPath, "utf-8");
38954
39198
  const parsed = JSON.parse(content);
38955
39199
  if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
38956
39200
  return { version: 1, checkpoints: [] };
@@ -38962,13 +39206,13 @@ function readCheckpointLog() {
38962
39206
  }
38963
39207
  function writeCheckpointLog(log2) {
38964
39208
  const logPath = getCheckpointLogPath();
38965
- const dir = path19.dirname(logPath);
38966
- if (!fs13.existsSync(dir)) {
38967
- fs13.mkdirSync(dir, { recursive: true });
39209
+ const dir = path18.dirname(logPath);
39210
+ if (!fs12.existsSync(dir)) {
39211
+ fs12.mkdirSync(dir, { recursive: true });
38968
39212
  }
38969
39213
  const tempPath = `${logPath}.tmp`;
38970
- fs13.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
38971
- fs13.renameSync(tempPath, logPath);
39214
+ fs12.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
39215
+ fs12.renameSync(tempPath, logPath);
38972
39216
  }
38973
39217
  function gitExec(args2) {
38974
39218
  const result = spawnSync("git", args2, {
@@ -39168,8 +39412,8 @@ var checkpoint = tool({
39168
39412
  });
39169
39413
  // src/tools/complexity-hotspots.ts
39170
39414
  init_dist();
39171
- import * as fs14 from "fs";
39172
- import * as path20 from "path";
39415
+ import * as fs13 from "fs";
39416
+ import * as path19 from "path";
39173
39417
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
39174
39418
  var DEFAULT_DAYS = 90;
39175
39419
  var DEFAULT_TOP_N = 20;
@@ -39297,11 +39541,11 @@ function estimateComplexity(content) {
39297
39541
  }
39298
39542
  function getComplexityForFile(filePath) {
39299
39543
  try {
39300
- const stat = fs14.statSync(filePath);
39544
+ const stat = fs13.statSync(filePath);
39301
39545
  if (stat.size > MAX_FILE_SIZE_BYTES2) {
39302
39546
  return null;
39303
39547
  }
39304
- const content = fs14.readFileSync(filePath, "utf-8");
39548
+ const content = fs13.readFileSync(filePath, "utf-8");
39305
39549
  return estimateComplexity(content);
39306
39550
  } catch {
39307
39551
  return null;
@@ -39312,7 +39556,7 @@ async function analyzeHotspots(days, topN, extensions) {
39312
39556
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
39313
39557
  const filteredChurn = new Map;
39314
39558
  for (const [file3, count] of churnMap) {
39315
- const ext = path20.extname(file3).toLowerCase();
39559
+ const ext = path19.extname(file3).toLowerCase();
39316
39560
  if (extSet.has(ext)) {
39317
39561
  filteredChurn.set(file3, count);
39318
39562
  }
@@ -39322,8 +39566,8 @@ async function analyzeHotspots(days, topN, extensions) {
39322
39566
  let analyzedFiles = 0;
39323
39567
  for (const [file3, churnCount] of filteredChurn) {
39324
39568
  let fullPath = file3;
39325
- if (!fs14.existsSync(fullPath)) {
39326
- fullPath = path20.join(cwd, file3);
39569
+ if (!fs13.existsSync(fullPath)) {
39570
+ fullPath = path19.join(cwd, file3);
39327
39571
  }
39328
39572
  const complexity = getComplexityForFile(fullPath);
39329
39573
  if (complexity !== null) {
@@ -39481,14 +39725,14 @@ function validateBase(base) {
39481
39725
  function validatePaths(paths) {
39482
39726
  if (!paths)
39483
39727
  return null;
39484
- for (const path21 of paths) {
39485
- if (!path21 || path21.length === 0) {
39728
+ for (const path20 of paths) {
39729
+ if (!path20 || path20.length === 0) {
39486
39730
  return "empty path not allowed";
39487
39731
  }
39488
- if (path21.length > MAX_PATH_LENGTH) {
39732
+ if (path20.length > MAX_PATH_LENGTH) {
39489
39733
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
39490
39734
  }
39491
- if (SHELL_METACHARACTERS2.test(path21)) {
39735
+ if (SHELL_METACHARACTERS2.test(path20)) {
39492
39736
  return "path contains shell metacharacters";
39493
39737
  }
39494
39738
  }
@@ -39551,8 +39795,8 @@ var diff = tool({
39551
39795
  if (parts2.length >= 3) {
39552
39796
  const additions = parseInt(parts2[0], 10) || 0;
39553
39797
  const deletions = parseInt(parts2[1], 10) || 0;
39554
- const path21 = parts2[2];
39555
- files.push({ path: path21, additions, deletions });
39798
+ const path20 = parts2[2];
39799
+ files.push({ path: path20, additions, deletions });
39556
39800
  }
39557
39801
  }
39558
39802
  const contractChanges = [];
@@ -39780,8 +40024,8 @@ Use these as DOMAIN values when delegating to @sme.`;
39780
40024
  });
39781
40025
  // src/tools/evidence-check.ts
39782
40026
  init_dist();
39783
- import * as fs15 from "fs";
39784
- import * as path21 from "path";
40027
+ import * as fs14 from "fs";
40028
+ import * as path20 from "path";
39785
40029
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
39786
40030
  var MAX_EVIDENCE_FILES = 1000;
39787
40031
  var EVIDENCE_DIR = ".swarm/evidence";
@@ -39804,9 +40048,9 @@ function validateRequiredTypes(input) {
39804
40048
  return null;
39805
40049
  }
39806
40050
  function isPathWithinSwarm(filePath, cwd) {
39807
- const normalizedCwd = path21.resolve(cwd);
39808
- const swarmPath = path21.join(normalizedCwd, ".swarm");
39809
- const normalizedPath = path21.resolve(filePath);
40051
+ const normalizedCwd = path20.resolve(cwd);
40052
+ const swarmPath = path20.join(normalizedCwd, ".swarm");
40053
+ const normalizedPath = path20.resolve(filePath);
39810
40054
  return normalizedPath.startsWith(swarmPath);
39811
40055
  }
39812
40056
  function parseCompletedTasks(planContent) {
@@ -39822,12 +40066,12 @@ function parseCompletedTasks(planContent) {
39822
40066
  }
39823
40067
  function readEvidenceFiles(evidenceDir, _cwd) {
39824
40068
  const evidence = [];
39825
- if (!fs15.existsSync(evidenceDir) || !fs15.statSync(evidenceDir).isDirectory()) {
40069
+ if (!fs14.existsSync(evidenceDir) || !fs14.statSync(evidenceDir).isDirectory()) {
39826
40070
  return evidence;
39827
40071
  }
39828
40072
  let files;
39829
40073
  try {
39830
- files = fs15.readdirSync(evidenceDir);
40074
+ files = fs14.readdirSync(evidenceDir);
39831
40075
  } catch {
39832
40076
  return evidence;
39833
40077
  }
@@ -39836,14 +40080,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39836
40080
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
39837
40081
  continue;
39838
40082
  }
39839
- const filePath = path21.join(evidenceDir, filename);
40083
+ const filePath = path20.join(evidenceDir, filename);
39840
40084
  try {
39841
- const resolvedPath = path21.resolve(filePath);
39842
- const evidenceDirResolved = path21.resolve(evidenceDir);
40085
+ const resolvedPath = path20.resolve(filePath);
40086
+ const evidenceDirResolved = path20.resolve(evidenceDir);
39843
40087
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
39844
40088
  continue;
39845
40089
  }
39846
- const stat = fs15.lstatSync(filePath);
40090
+ const stat = fs14.lstatSync(filePath);
39847
40091
  if (!stat.isFile()) {
39848
40092
  continue;
39849
40093
  }
@@ -39852,7 +40096,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39852
40096
  }
39853
40097
  let fileStat;
39854
40098
  try {
39855
- fileStat = fs15.statSync(filePath);
40099
+ fileStat = fs14.statSync(filePath);
39856
40100
  if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
39857
40101
  continue;
39858
40102
  }
@@ -39861,7 +40105,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39861
40105
  }
39862
40106
  let content;
39863
40107
  try {
39864
- content = fs15.readFileSync(filePath, "utf-8");
40108
+ content = fs14.readFileSync(filePath, "utf-8");
39865
40109
  } catch {
39866
40110
  continue;
39867
40111
  }
@@ -39946,7 +40190,7 @@ var evidence_check = tool({
39946
40190
  return JSON.stringify(errorResult, null, 2);
39947
40191
  }
39948
40192
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
39949
- const planPath = path21.join(cwd, PLAN_FILE);
40193
+ const planPath = path20.join(cwd, PLAN_FILE);
39950
40194
  if (!isPathWithinSwarm(planPath, cwd)) {
39951
40195
  const errorResult = {
39952
40196
  error: "plan file path validation failed",
@@ -39960,7 +40204,7 @@ var evidence_check = tool({
39960
40204
  }
39961
40205
  let planContent;
39962
40206
  try {
39963
- planContent = fs15.readFileSync(planPath, "utf-8");
40207
+ planContent = fs14.readFileSync(planPath, "utf-8");
39964
40208
  } catch {
39965
40209
  const result2 = {
39966
40210
  message: "No completed tasks found in plan.",
@@ -39978,7 +40222,7 @@ var evidence_check = tool({
39978
40222
  };
39979
40223
  return JSON.stringify(result2, null, 2);
39980
40224
  }
39981
- const evidenceDir = path21.join(cwd, EVIDENCE_DIR);
40225
+ const evidenceDir = path20.join(cwd, EVIDENCE_DIR);
39982
40226
  const evidence = readEvidenceFiles(evidenceDir, cwd);
39983
40227
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
39984
40228
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -39994,8 +40238,8 @@ var evidence_check = tool({
39994
40238
  });
39995
40239
  // src/tools/file-extractor.ts
39996
40240
  init_tool();
39997
- import * as fs16 from "fs";
39998
- import * as path22 from "path";
40241
+ import * as fs15 from "fs";
40242
+ import * as path21 from "path";
39999
40243
  var EXT_MAP = {
40000
40244
  python: ".py",
40001
40245
  py: ".py",
@@ -40057,8 +40301,8 @@ var extract_code_blocks = tool({
40057
40301
  execute: async (args2) => {
40058
40302
  const { content, output_dir, prefix } = args2;
40059
40303
  const targetDir = output_dir || process.cwd();
40060
- if (!fs16.existsSync(targetDir)) {
40061
- fs16.mkdirSync(targetDir, { recursive: true });
40304
+ if (!fs15.existsSync(targetDir)) {
40305
+ fs15.mkdirSync(targetDir, { recursive: true });
40062
40306
  }
40063
40307
  const pattern = /```(\w*)\n([\s\S]*?)```/g;
40064
40308
  const matches = [...content.matchAll(pattern)];
@@ -40073,16 +40317,16 @@ var extract_code_blocks = tool({
40073
40317
  if (prefix) {
40074
40318
  filename = `${prefix}_${filename}`;
40075
40319
  }
40076
- let filepath = path22.join(targetDir, filename);
40077
- const base = path22.basename(filepath, path22.extname(filepath));
40078
- const ext = path22.extname(filepath);
40320
+ let filepath = path21.join(targetDir, filename);
40321
+ const base = path21.basename(filepath, path21.extname(filepath));
40322
+ const ext = path21.extname(filepath);
40079
40323
  let counter = 1;
40080
- while (fs16.existsSync(filepath)) {
40081
- filepath = path22.join(targetDir, `${base}_${counter}${ext}`);
40324
+ while (fs15.existsSync(filepath)) {
40325
+ filepath = path21.join(targetDir, `${base}_${counter}${ext}`);
40082
40326
  counter++;
40083
40327
  }
40084
40328
  try {
40085
- fs16.writeFileSync(filepath, code.trim(), "utf-8");
40329
+ fs15.writeFileSync(filepath, code.trim(), "utf-8");
40086
40330
  savedFiles.push(filepath);
40087
40331
  } catch (error93) {
40088
40332
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -40190,8 +40434,8 @@ var gitingest = tool({
40190
40434
  });
40191
40435
  // src/tools/imports.ts
40192
40436
  init_dist();
40193
- import * as fs17 from "fs";
40194
- import * as path23 from "path";
40437
+ import * as fs16 from "fs";
40438
+ import * as path22 from "path";
40195
40439
  var MAX_FILE_PATH_LENGTH2 = 500;
40196
40440
  var MAX_SYMBOL_LENGTH = 256;
40197
40441
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -40245,7 +40489,7 @@ function validateSymbolInput(symbol3) {
40245
40489
  return null;
40246
40490
  }
40247
40491
  function isBinaryFile2(filePath, buffer) {
40248
- const ext = path23.extname(filePath).toLowerCase();
40492
+ const ext = path22.extname(filePath).toLowerCase();
40249
40493
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
40250
40494
  return false;
40251
40495
  }
@@ -40269,15 +40513,15 @@ function parseImports(content, targetFile, targetSymbol) {
40269
40513
  const imports = [];
40270
40514
  let _resolvedTarget;
40271
40515
  try {
40272
- _resolvedTarget = path23.resolve(targetFile);
40516
+ _resolvedTarget = path22.resolve(targetFile);
40273
40517
  } catch {
40274
40518
  _resolvedTarget = targetFile;
40275
40519
  }
40276
- const targetBasename = path23.basename(targetFile, path23.extname(targetFile));
40520
+ const targetBasename = path22.basename(targetFile, path22.extname(targetFile));
40277
40521
  const targetWithExt = targetFile;
40278
40522
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
40279
- const normalizedTargetWithExt = path23.normalize(targetWithExt).replace(/\\/g, "/");
40280
- const normalizedTargetWithoutExt = path23.normalize(targetWithoutExt).replace(/\\/g, "/");
40523
+ const normalizedTargetWithExt = path22.normalize(targetWithExt).replace(/\\/g, "/");
40524
+ const normalizedTargetWithoutExt = path22.normalize(targetWithoutExt).replace(/\\/g, "/");
40281
40525
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
40282
40526
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
40283
40527
  const modulePath = match[1] || match[2] || match[3];
@@ -40300,9 +40544,9 @@ function parseImports(content, targetFile, targetSymbol) {
40300
40544
  }
40301
40545
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
40302
40546
  let isMatch = false;
40303
- const _targetDir = path23.dirname(targetFile);
40304
- const targetExt = path23.extname(targetFile);
40305
- const targetBasenameNoExt = path23.basename(targetFile, targetExt);
40547
+ const _targetDir = path22.dirname(targetFile);
40548
+ const targetExt = path22.extname(targetFile);
40549
+ const targetBasenameNoExt = path22.basename(targetFile, targetExt);
40306
40550
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
40307
40551
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
40308
40552
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -40359,7 +40603,7 @@ var SKIP_DIRECTORIES2 = new Set([
40359
40603
  function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
40360
40604
  let entries;
40361
40605
  try {
40362
- entries = fs17.readdirSync(dir);
40606
+ entries = fs16.readdirSync(dir);
40363
40607
  } catch (e) {
40364
40608
  stats.fileErrors.push({
40365
40609
  path: dir,
@@ -40370,13 +40614,13 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
40370
40614
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
40371
40615
  for (const entry of entries) {
40372
40616
  if (SKIP_DIRECTORIES2.has(entry)) {
40373
- stats.skippedDirs.push(path23.join(dir, entry));
40617
+ stats.skippedDirs.push(path22.join(dir, entry));
40374
40618
  continue;
40375
40619
  }
40376
- const fullPath = path23.join(dir, entry);
40620
+ const fullPath = path22.join(dir, entry);
40377
40621
  let stat;
40378
40622
  try {
40379
- stat = fs17.statSync(fullPath);
40623
+ stat = fs16.statSync(fullPath);
40380
40624
  } catch (e) {
40381
40625
  stats.fileErrors.push({
40382
40626
  path: fullPath,
@@ -40387,7 +40631,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
40387
40631
  if (stat.isDirectory()) {
40388
40632
  findSourceFiles2(fullPath, files, stats);
40389
40633
  } else if (stat.isFile()) {
40390
- const ext = path23.extname(fullPath).toLowerCase();
40634
+ const ext = path22.extname(fullPath).toLowerCase();
40391
40635
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
40392
40636
  files.push(fullPath);
40393
40637
  }
@@ -40443,8 +40687,8 @@ var imports = tool({
40443
40687
  return JSON.stringify(errorResult, null, 2);
40444
40688
  }
40445
40689
  try {
40446
- const targetFile = path23.resolve(file3);
40447
- if (!fs17.existsSync(targetFile)) {
40690
+ const targetFile = path22.resolve(file3);
40691
+ if (!fs16.existsSync(targetFile)) {
40448
40692
  const errorResult = {
40449
40693
  error: `target file not found: ${file3}`,
40450
40694
  target: file3,
@@ -40454,7 +40698,7 @@ var imports = tool({
40454
40698
  };
40455
40699
  return JSON.stringify(errorResult, null, 2);
40456
40700
  }
40457
- const targetStat = fs17.statSync(targetFile);
40701
+ const targetStat = fs16.statSync(targetFile);
40458
40702
  if (!targetStat.isFile()) {
40459
40703
  const errorResult = {
40460
40704
  error: "target must be a file, not a directory",
@@ -40465,7 +40709,7 @@ var imports = tool({
40465
40709
  };
40466
40710
  return JSON.stringify(errorResult, null, 2);
40467
40711
  }
40468
- const baseDir = path23.dirname(targetFile);
40712
+ const baseDir = path22.dirname(targetFile);
40469
40713
  const scanStats = {
40470
40714
  skippedDirs: [],
40471
40715
  skippedFiles: 0,
@@ -40480,12 +40724,12 @@ var imports = tool({
40480
40724
  if (consumers.length >= MAX_CONSUMERS)
40481
40725
  break;
40482
40726
  try {
40483
- const stat = fs17.statSync(filePath);
40727
+ const stat = fs16.statSync(filePath);
40484
40728
  if (stat.size > MAX_FILE_SIZE_BYTES4) {
40485
40729
  skippedFileCount++;
40486
40730
  continue;
40487
40731
  }
40488
- const buffer = fs17.readFileSync(filePath);
40732
+ const buffer = fs16.readFileSync(filePath);
40489
40733
  if (isBinaryFile2(filePath, buffer)) {
40490
40734
  skippedFileCount++;
40491
40735
  continue;
@@ -40554,7 +40798,8 @@ init_lint();
40554
40798
 
40555
40799
  // src/tools/phase-complete.ts
40556
40800
  init_tool();
40557
- import * as fs18 from "fs";
40801
+ import * as fs17 from "fs";
40802
+ init_manager();
40558
40803
  init_utils2();
40559
40804
  function getDelegationsSince(sessionID, sinceTimestamp) {
40560
40805
  const chain = swarmState.delegationChains.get(sessionID);
@@ -40576,6 +40821,9 @@ function normalizeAgentsFromDelegations(delegations) {
40576
40821
  }
40577
40822
  return agents;
40578
40823
  }
40824
+ function isValidRetroEntry(entry, phase) {
40825
+ return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
40826
+ }
40579
40827
  async function executePhaseComplete(args2) {
40580
40828
  const phase = Number(args2.phase);
40581
40829
  const summary = args2.summary;
@@ -40634,6 +40882,37 @@ async function executePhaseComplete(args2) {
40634
40882
  warnings: []
40635
40883
  }, null, 2);
40636
40884
  }
40885
+ const retroBundle = await loadEvidence(directory, `retro-${phase}`);
40886
+ let retroFound = false;
40887
+ if (retroBundle !== null) {
40888
+ retroFound = retroBundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
40889
+ }
40890
+ if (!retroFound) {
40891
+ const allTaskIds = await listEvidenceTaskIds(directory);
40892
+ const retroTaskIds = allTaskIds.filter((id) => id.startsWith("retro-"));
40893
+ for (const taskId of retroTaskIds) {
40894
+ const bundle = await loadEvidence(directory, taskId);
40895
+ if (bundle === null)
40896
+ continue;
40897
+ retroFound = bundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
40898
+ if (retroFound)
40899
+ break;
40900
+ }
40901
+ }
40902
+ if (!retroFound) {
40903
+ return JSON.stringify({
40904
+ success: false,
40905
+ phase,
40906
+ status: "blocked",
40907
+ reason: "RETROSPECTIVE_MISSING",
40908
+ message: `Phase ${phase} cannot be completed: no valid retrospective evidence found. Write a retrospective bundle at .swarm/evidence/retro-${phase}/evidence.json with type='retrospective', phase_number=${phase}, verdict='pass' before calling phase_complete.`,
40909
+ agentsDispatched: [],
40910
+ agentsMissing: [],
40911
+ warnings: [
40912
+ `Retrospective missing for phase ${phase}. Write a retro bundle with verdict='pass' at .swarm/evidence/retro-${phase}/evidence.json`
40913
+ ]
40914
+ }, null, 2);
40915
+ }
40637
40916
  const effectiveRequired = [...phaseCompleteConfig.required_agents];
40638
40917
  if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
40639
40918
  effectiveRequired.push("docs");
@@ -40667,7 +40946,7 @@ async function executePhaseComplete(args2) {
40667
40946
  };
40668
40947
  try {
40669
40948
  const eventsPath = validateSwarmPath(directory, "events.jsonl");
40670
- fs18.appendFileSync(eventsPath, `${JSON.stringify(event)}
40949
+ fs17.appendFileSync(eventsPath, `${JSON.stringify(event)}
40671
40950
  `, "utf-8");
40672
40951
  } catch (writeError) {
40673
40952
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -40717,8 +40996,8 @@ var phase_complete = tool({
40717
40996
  });
40718
40997
  // src/tools/pkg-audit.ts
40719
40998
  init_dist();
40720
- import * as fs19 from "fs";
40721
- import * as path24 from "path";
40999
+ import * as fs18 from "fs";
41000
+ import * as path23 from "path";
40722
41001
  var MAX_OUTPUT_BYTES5 = 52428800;
40723
41002
  var AUDIT_TIMEOUT_MS = 120000;
40724
41003
  function isValidEcosystem(value) {
@@ -40736,13 +41015,13 @@ function validateArgs3(args2) {
40736
41015
  function detectEcosystems() {
40737
41016
  const ecosystems = [];
40738
41017
  const cwd = process.cwd();
40739
- if (fs19.existsSync(path24.join(cwd, "package.json"))) {
41018
+ if (fs18.existsSync(path23.join(cwd, "package.json"))) {
40740
41019
  ecosystems.push("npm");
40741
41020
  }
40742
- if (fs19.existsSync(path24.join(cwd, "pyproject.toml")) || fs19.existsSync(path24.join(cwd, "requirements.txt"))) {
41021
+ if (fs18.existsSync(path23.join(cwd, "pyproject.toml")) || fs18.existsSync(path23.join(cwd, "requirements.txt"))) {
40743
41022
  ecosystems.push("pip");
40744
41023
  }
40745
- if (fs19.existsSync(path24.join(cwd, "Cargo.toml"))) {
41024
+ if (fs18.existsSync(path23.join(cwd, "Cargo.toml"))) {
40746
41025
  ecosystems.push("cargo");
40747
41026
  }
40748
41027
  return ecosystems;
@@ -42650,11 +42929,11 @@ var Module2 = (() => {
42650
42929
  throw toThrow;
42651
42930
  }, "quit_");
42652
42931
  var scriptDirectory = "";
42653
- function locateFile(path25) {
42932
+ function locateFile(path24) {
42654
42933
  if (Module["locateFile"]) {
42655
- return Module["locateFile"](path25, scriptDirectory);
42934
+ return Module["locateFile"](path24, scriptDirectory);
42656
42935
  }
42657
- return scriptDirectory + path25;
42936
+ return scriptDirectory + path24;
42658
42937
  }
42659
42938
  __name(locateFile, "locateFile");
42660
42939
  var readAsync, readBinary;
@@ -44468,7 +44747,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
44468
44747
  ]);
44469
44748
  // src/tools/pre-check-batch.ts
44470
44749
  init_dist();
44471
- import * as path27 from "path";
44750
+ import * as path26 from "path";
44472
44751
 
44473
44752
  // node_modules/yocto-queue/index.js
44474
44753
  class Node2 {
@@ -44634,8 +44913,8 @@ init_lint();
44634
44913
  init_manager();
44635
44914
 
44636
44915
  // src/quality/metrics.ts
44637
- import * as fs20 from "fs";
44638
- import * as path25 from "path";
44916
+ import * as fs19 from "fs";
44917
+ import * as path24 from "path";
44639
44918
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
44640
44919
  var MIN_DUPLICATION_LINES = 10;
44641
44920
  function estimateCyclomaticComplexity(content) {
@@ -44673,11 +44952,11 @@ function estimateCyclomaticComplexity(content) {
44673
44952
  }
44674
44953
  function getComplexityForFile2(filePath) {
44675
44954
  try {
44676
- const stat = fs20.statSync(filePath);
44955
+ const stat = fs19.statSync(filePath);
44677
44956
  if (stat.size > MAX_FILE_SIZE_BYTES5) {
44678
44957
  return null;
44679
44958
  }
44680
- const content = fs20.readFileSync(filePath, "utf-8");
44959
+ const content = fs19.readFileSync(filePath, "utf-8");
44681
44960
  return estimateCyclomaticComplexity(content);
44682
44961
  } catch {
44683
44962
  return null;
@@ -44687,8 +44966,8 @@ async function computeComplexityDelta(files, workingDir) {
44687
44966
  let totalComplexity = 0;
44688
44967
  const analyzedFiles = [];
44689
44968
  for (const file3 of files) {
44690
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44691
- if (!fs20.existsSync(fullPath)) {
44969
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
44970
+ if (!fs19.existsSync(fullPath)) {
44692
44971
  continue;
44693
44972
  }
44694
44973
  const complexity = getComplexityForFile2(fullPath);
@@ -44809,8 +45088,8 @@ function countGoExports(content) {
44809
45088
  }
44810
45089
  function getExportCountForFile(filePath) {
44811
45090
  try {
44812
- const content = fs20.readFileSync(filePath, "utf-8");
44813
- const ext = path25.extname(filePath).toLowerCase();
45091
+ const content = fs19.readFileSync(filePath, "utf-8");
45092
+ const ext = path24.extname(filePath).toLowerCase();
44814
45093
  switch (ext) {
44815
45094
  case ".ts":
44816
45095
  case ".tsx":
@@ -44836,8 +45115,8 @@ async function computePublicApiDelta(files, workingDir) {
44836
45115
  let totalExports = 0;
44837
45116
  const analyzedFiles = [];
44838
45117
  for (const file3 of files) {
44839
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44840
- if (!fs20.existsSync(fullPath)) {
45118
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
45119
+ if (!fs19.existsSync(fullPath)) {
44841
45120
  continue;
44842
45121
  }
44843
45122
  const exports = getExportCountForFile(fullPath);
@@ -44870,16 +45149,16 @@ async function computeDuplicationRatio(files, workingDir) {
44870
45149
  let duplicateLines = 0;
44871
45150
  const analyzedFiles = [];
44872
45151
  for (const file3 of files) {
44873
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44874
- if (!fs20.existsSync(fullPath)) {
45152
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
45153
+ if (!fs19.existsSync(fullPath)) {
44875
45154
  continue;
44876
45155
  }
44877
45156
  try {
44878
- const stat = fs20.statSync(fullPath);
45157
+ const stat = fs19.statSync(fullPath);
44879
45158
  if (stat.size > MAX_FILE_SIZE_BYTES5) {
44880
45159
  continue;
44881
45160
  }
44882
- const content = fs20.readFileSync(fullPath, "utf-8");
45161
+ const content = fs19.readFileSync(fullPath, "utf-8");
44883
45162
  const lines = content.split(`
44884
45163
  `).filter((line) => line.trim().length > 0);
44885
45164
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -44903,8 +45182,8 @@ function countCodeLines(content) {
44903
45182
  return lines.length;
44904
45183
  }
44905
45184
  function isTestFile(filePath) {
44906
- const basename5 = path25.basename(filePath);
44907
- const _ext = path25.extname(filePath).toLowerCase();
45185
+ const basename5 = path24.basename(filePath);
45186
+ const _ext = path24.extname(filePath).toLowerCase();
44908
45187
  const testPatterns = [
44909
45188
  ".test.",
44910
45189
  ".spec.",
@@ -44946,31 +45225,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
44946
45225
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
44947
45226
  let testLines = 0;
44948
45227
  let codeLines = 0;
44949
- const srcDir = path25.join(workingDir, "src");
44950
- if (fs20.existsSync(srcDir)) {
45228
+ const srcDir = path24.join(workingDir, "src");
45229
+ if (fs19.existsSync(srcDir)) {
44951
45230
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
44952
45231
  codeLines += lines;
44953
45232
  });
44954
45233
  }
44955
45234
  const possibleSrcDirs = ["lib", "app", "source", "core"];
44956
45235
  for (const dir of possibleSrcDirs) {
44957
- const dirPath = path25.join(workingDir, dir);
44958
- if (fs20.existsSync(dirPath)) {
45236
+ const dirPath = path24.join(workingDir, dir);
45237
+ if (fs19.existsSync(dirPath)) {
44959
45238
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
44960
45239
  codeLines += lines;
44961
45240
  });
44962
45241
  }
44963
45242
  }
44964
- const testsDir = path25.join(workingDir, "tests");
44965
- if (fs20.existsSync(testsDir)) {
45243
+ const testsDir = path24.join(workingDir, "tests");
45244
+ if (fs19.existsSync(testsDir)) {
44966
45245
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
44967
45246
  testLines += lines;
44968
45247
  });
44969
45248
  }
44970
45249
  const possibleTestDirs = ["test", "__tests__", "specs"];
44971
45250
  for (const dir of possibleTestDirs) {
44972
- const dirPath = path25.join(workingDir, dir);
44973
- if (fs20.existsSync(dirPath) && dirPath !== testsDir) {
45251
+ const dirPath = path24.join(workingDir, dir);
45252
+ if (fs19.existsSync(dirPath) && dirPath !== testsDir) {
44974
45253
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
44975
45254
  testLines += lines;
44976
45255
  });
@@ -44982,9 +45261,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
44982
45261
  }
44983
45262
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
44984
45263
  try {
44985
- const entries = fs20.readdirSync(dirPath, { withFileTypes: true });
45264
+ const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
44986
45265
  for (const entry of entries) {
44987
- const fullPath = path25.join(dirPath, entry.name);
45266
+ const fullPath = path24.join(dirPath, entry.name);
44988
45267
  if (entry.isDirectory()) {
44989
45268
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
44990
45269
  continue;
@@ -44992,7 +45271,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
44992
45271
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
44993
45272
  } else if (entry.isFile()) {
44994
45273
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
44995
- const ext = path25.extname(entry.name).toLowerCase();
45274
+ const ext = path24.extname(entry.name).toLowerCase();
44996
45275
  const validExts = [
44997
45276
  ".ts",
44998
45277
  ".tsx",
@@ -45030,7 +45309,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
45030
45309
  continue;
45031
45310
  }
45032
45311
  try {
45033
- const content = fs20.readFileSync(fullPath, "utf-8");
45312
+ const content = fs19.readFileSync(fullPath, "utf-8");
45034
45313
  const lines = countCodeLines(content);
45035
45314
  callback(lines);
45036
45315
  } catch {}
@@ -45246,8 +45525,8 @@ async function qualityBudget(input, directory) {
45246
45525
 
45247
45526
  // src/tools/sast-scan.ts
45248
45527
  init_manager();
45249
- import * as fs21 from "fs";
45250
- import * as path26 from "path";
45528
+ import * as fs20 from "fs";
45529
+ import * as path25 from "path";
45251
45530
  import { extname as extname7 } from "path";
45252
45531
 
45253
45532
  // src/sast/rules/c.ts
@@ -46110,17 +46389,17 @@ var SEVERITY_ORDER = {
46110
46389
  };
46111
46390
  function shouldSkipFile(filePath) {
46112
46391
  try {
46113
- const stats = fs21.statSync(filePath);
46392
+ const stats = fs20.statSync(filePath);
46114
46393
  if (stats.size > MAX_FILE_SIZE_BYTES6) {
46115
46394
  return { skip: true, reason: "file too large" };
46116
46395
  }
46117
46396
  if (stats.size === 0) {
46118
46397
  return { skip: true, reason: "empty file" };
46119
46398
  }
46120
- const fd = fs21.openSync(filePath, "r");
46399
+ const fd = fs20.openSync(filePath, "r");
46121
46400
  const buffer = Buffer.alloc(8192);
46122
- const bytesRead = fs21.readSync(fd, buffer, 0, 8192, 0);
46123
- fs21.closeSync(fd);
46401
+ const bytesRead = fs20.readSync(fd, buffer, 0, 8192, 0);
46402
+ fs20.closeSync(fd);
46124
46403
  if (bytesRead > 0) {
46125
46404
  let nullCount = 0;
46126
46405
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -46159,7 +46438,7 @@ function countBySeverity(findings) {
46159
46438
  }
46160
46439
  function scanFileWithTierA(filePath, language) {
46161
46440
  try {
46162
- const content = fs21.readFileSync(filePath, "utf-8");
46441
+ const content = fs20.readFileSync(filePath, "utf-8");
46163
46442
  const findings = executeRulesSync(filePath, content, language);
46164
46443
  return findings.map((f) => ({
46165
46444
  rule_id: f.rule_id,
@@ -46202,8 +46481,8 @@ async function sastScan(input, directory, config3) {
46202
46481
  const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
46203
46482
  const filesByLanguage = new Map;
46204
46483
  for (const filePath of changed_files) {
46205
- const resolvedPath = path26.isAbsolute(filePath) ? filePath : path26.resolve(directory, filePath);
46206
- if (!fs21.existsSync(resolvedPath)) {
46484
+ const resolvedPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(directory, filePath);
46485
+ if (!fs20.existsSync(resolvedPath)) {
46207
46486
  _filesSkipped++;
46208
46487
  continue;
46209
46488
  }
@@ -46311,10 +46590,10 @@ function validatePath(inputPath, baseDir) {
46311
46590
  if (!inputPath || inputPath.length === 0) {
46312
46591
  return "path is required";
46313
46592
  }
46314
- const resolved = path27.resolve(baseDir, inputPath);
46315
- const baseResolved = path27.resolve(baseDir);
46316
- const relative3 = path27.relative(baseResolved, resolved);
46317
- if (relative3.startsWith("..") || path27.isAbsolute(relative3)) {
46593
+ const resolved = path26.resolve(baseDir, inputPath);
46594
+ const baseResolved = path26.resolve(baseDir);
46595
+ const relative3 = path26.relative(baseResolved, resolved);
46596
+ if (relative3.startsWith("..") || path26.isAbsolute(relative3)) {
46318
46597
  return "path traversal detected";
46319
46598
  }
46320
46599
  return null;
@@ -46436,7 +46715,7 @@ async function runPreCheckBatch(input) {
46436
46715
  warn(`pre_check_batch: Invalid file path: ${file3}`);
46437
46716
  continue;
46438
46717
  }
46439
- changedFiles.push(path27.resolve(directory, file3));
46718
+ changedFiles.push(path26.resolve(directory, file3));
46440
46719
  }
46441
46720
  } else {
46442
46721
  changedFiles = [];
@@ -46626,8 +46905,8 @@ var retrieve_summary = tool({
46626
46905
  // src/tools/sbom-generate.ts
46627
46906
  init_dist();
46628
46907
  init_manager();
46629
- import * as fs22 from "fs";
46630
- import * as path28 from "path";
46908
+ import * as fs21 from "fs";
46909
+ import * as path27 from "path";
46631
46910
 
46632
46911
  // src/sbom/detectors/dart.ts
46633
46912
  function parsePubspecLock(content) {
@@ -47472,9 +47751,9 @@ function findManifestFiles(rootDir) {
47472
47751
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
47473
47752
  function searchDir(dir) {
47474
47753
  try {
47475
- const entries = fs22.readdirSync(dir, { withFileTypes: true });
47754
+ const entries = fs21.readdirSync(dir, { withFileTypes: true });
47476
47755
  for (const entry of entries) {
47477
- const fullPath = path28.join(dir, entry.name);
47756
+ const fullPath = path27.join(dir, entry.name);
47478
47757
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
47479
47758
  continue;
47480
47759
  }
@@ -47484,7 +47763,7 @@ function findManifestFiles(rootDir) {
47484
47763
  for (const pattern of patterns) {
47485
47764
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
47486
47765
  if (new RegExp(regex, "i").test(entry.name)) {
47487
- manifestFiles.push(path28.relative(cwd, fullPath));
47766
+ manifestFiles.push(path27.relative(cwd, fullPath));
47488
47767
  break;
47489
47768
  }
47490
47769
  }
@@ -47501,14 +47780,14 @@ function findManifestFilesInDirs(directories, workingDir) {
47501
47780
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
47502
47781
  for (const dir of directories) {
47503
47782
  try {
47504
- const entries = fs22.readdirSync(dir, { withFileTypes: true });
47783
+ const entries = fs21.readdirSync(dir, { withFileTypes: true });
47505
47784
  for (const entry of entries) {
47506
- const fullPath = path28.join(dir, entry.name);
47785
+ const fullPath = path27.join(dir, entry.name);
47507
47786
  if (entry.isFile()) {
47508
47787
  for (const pattern of patterns) {
47509
47788
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
47510
47789
  if (new RegExp(regex, "i").test(entry.name)) {
47511
- found.push(path28.relative(workingDir, fullPath));
47790
+ found.push(path27.relative(workingDir, fullPath));
47512
47791
  break;
47513
47792
  }
47514
47793
  }
@@ -47521,11 +47800,11 @@ function findManifestFilesInDirs(directories, workingDir) {
47521
47800
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
47522
47801
  const dirs = new Set;
47523
47802
  for (const file3 of changedFiles) {
47524
- let currentDir = path28.dirname(file3);
47803
+ let currentDir = path27.dirname(file3);
47525
47804
  while (true) {
47526
- if (currentDir && currentDir !== "." && currentDir !== path28.sep) {
47527
- dirs.add(path28.join(workingDir, currentDir));
47528
- const parent = path28.dirname(currentDir);
47805
+ if (currentDir && currentDir !== "." && currentDir !== path27.sep) {
47806
+ dirs.add(path27.join(workingDir, currentDir));
47807
+ const parent = path27.dirname(currentDir);
47529
47808
  if (parent === currentDir)
47530
47809
  break;
47531
47810
  currentDir = parent;
@@ -47539,7 +47818,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
47539
47818
  }
47540
47819
  function ensureOutputDir(outputDir) {
47541
47820
  try {
47542
- fs22.mkdirSync(outputDir, { recursive: true });
47821
+ fs21.mkdirSync(outputDir, { recursive: true });
47543
47822
  } catch (error93) {
47544
47823
  if (!error93 || error93.code !== "EEXIST") {
47545
47824
  throw error93;
@@ -47632,11 +47911,11 @@ var sbom_generate = tool({
47632
47911
  const processedFiles = [];
47633
47912
  for (const manifestFile of manifestFiles) {
47634
47913
  try {
47635
- const fullPath = path28.isAbsolute(manifestFile) ? manifestFile : path28.join(workingDir, manifestFile);
47636
- if (!fs22.existsSync(fullPath)) {
47914
+ const fullPath = path27.isAbsolute(manifestFile) ? manifestFile : path27.join(workingDir, manifestFile);
47915
+ if (!fs21.existsSync(fullPath)) {
47637
47916
  continue;
47638
47917
  }
47639
- const content = fs22.readFileSync(fullPath, "utf-8");
47918
+ const content = fs21.readFileSync(fullPath, "utf-8");
47640
47919
  const components = detectComponents(manifestFile, content);
47641
47920
  processedFiles.push(manifestFile);
47642
47921
  if (components.length > 0) {
@@ -47649,8 +47928,8 @@ var sbom_generate = tool({
47649
47928
  const bom = generateCycloneDX(allComponents);
47650
47929
  const bomJson = serializeCycloneDX(bom);
47651
47930
  const filename = generateSbomFilename();
47652
- const outputPath = path28.join(outputDir, filename);
47653
- fs22.writeFileSync(outputPath, bomJson, "utf-8");
47931
+ const outputPath = path27.join(outputDir, filename);
47932
+ fs21.writeFileSync(outputPath, bomJson, "utf-8");
47654
47933
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
47655
47934
  try {
47656
47935
  const timestamp = new Date().toISOString();
@@ -47691,8 +47970,8 @@ var sbom_generate = tool({
47691
47970
  });
47692
47971
  // src/tools/schema-drift.ts
47693
47972
  init_dist();
47694
- import * as fs23 from "fs";
47695
- import * as path29 from "path";
47973
+ import * as fs22 from "fs";
47974
+ import * as path28 from "path";
47696
47975
  var SPEC_CANDIDATES = [
47697
47976
  "openapi.json",
47698
47977
  "openapi.yaml",
@@ -47724,28 +48003,28 @@ function normalizePath(p) {
47724
48003
  }
47725
48004
  function discoverSpecFile(cwd, specFileArg) {
47726
48005
  if (specFileArg) {
47727
- const resolvedPath = path29.resolve(cwd, specFileArg);
47728
- const normalizedCwd = cwd.endsWith(path29.sep) ? cwd : cwd + path29.sep;
48006
+ const resolvedPath = path28.resolve(cwd, specFileArg);
48007
+ const normalizedCwd = cwd.endsWith(path28.sep) ? cwd : cwd + path28.sep;
47729
48008
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
47730
48009
  throw new Error("Invalid spec_file: path traversal detected");
47731
48010
  }
47732
- const ext = path29.extname(resolvedPath).toLowerCase();
48011
+ const ext = path28.extname(resolvedPath).toLowerCase();
47733
48012
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
47734
48013
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
47735
48014
  }
47736
- const stats = fs23.statSync(resolvedPath);
48015
+ const stats = fs22.statSync(resolvedPath);
47737
48016
  if (stats.size > MAX_SPEC_SIZE) {
47738
48017
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
47739
48018
  }
47740
- if (!fs23.existsSync(resolvedPath)) {
48019
+ if (!fs22.existsSync(resolvedPath)) {
47741
48020
  throw new Error(`Spec file not found: ${resolvedPath}`);
47742
48021
  }
47743
48022
  return resolvedPath;
47744
48023
  }
47745
48024
  for (const candidate of SPEC_CANDIDATES) {
47746
- const candidatePath = path29.resolve(cwd, candidate);
47747
- if (fs23.existsSync(candidatePath)) {
47748
- const stats = fs23.statSync(candidatePath);
48025
+ const candidatePath = path28.resolve(cwd, candidate);
48026
+ if (fs22.existsSync(candidatePath)) {
48027
+ const stats = fs22.statSync(candidatePath);
47749
48028
  if (stats.size <= MAX_SPEC_SIZE) {
47750
48029
  return candidatePath;
47751
48030
  }
@@ -47754,8 +48033,8 @@ function discoverSpecFile(cwd, specFileArg) {
47754
48033
  return null;
47755
48034
  }
47756
48035
  function parseSpec(specFile) {
47757
- const content = fs23.readFileSync(specFile, "utf-8");
47758
- const ext = path29.extname(specFile).toLowerCase();
48036
+ const content = fs22.readFileSync(specFile, "utf-8");
48037
+ const ext = path28.extname(specFile).toLowerCase();
47759
48038
  if (ext === ".json") {
47760
48039
  return parseJsonSpec(content);
47761
48040
  }
@@ -47821,12 +48100,12 @@ function extractRoutes(cwd) {
47821
48100
  function walkDir(dir) {
47822
48101
  let entries;
47823
48102
  try {
47824
- entries = fs23.readdirSync(dir, { withFileTypes: true });
48103
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
47825
48104
  } catch {
47826
48105
  return;
47827
48106
  }
47828
48107
  for (const entry of entries) {
47829
- const fullPath = path29.join(dir, entry.name);
48108
+ const fullPath = path28.join(dir, entry.name);
47830
48109
  if (entry.isSymbolicLink()) {
47831
48110
  continue;
47832
48111
  }
@@ -47836,7 +48115,7 @@ function extractRoutes(cwd) {
47836
48115
  }
47837
48116
  walkDir(fullPath);
47838
48117
  } else if (entry.isFile()) {
47839
- const ext = path29.extname(entry.name).toLowerCase();
48118
+ const ext = path28.extname(entry.name).toLowerCase();
47840
48119
  const baseName = entry.name.toLowerCase();
47841
48120
  if (![".ts", ".js", ".mjs"].includes(ext)) {
47842
48121
  continue;
@@ -47854,7 +48133,7 @@ function extractRoutes(cwd) {
47854
48133
  }
47855
48134
  function extractRoutesFromFile(filePath) {
47856
48135
  const routes = [];
47857
- const content = fs23.readFileSync(filePath, "utf-8");
48136
+ const content = fs22.readFileSync(filePath, "utf-8");
47858
48137
  const lines = content.split(/\r?\n/);
47859
48138
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
47860
48139
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -48004,8 +48283,8 @@ init_secretscan();
48004
48283
 
48005
48284
  // src/tools/symbols.ts
48006
48285
  init_tool();
48007
- import * as fs24 from "fs";
48008
- import * as path30 from "path";
48286
+ import * as fs23 from "fs";
48287
+ import * as path29 from "path";
48009
48288
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
48010
48289
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
48011
48290
  function containsControlCharacters(str) {
@@ -48034,11 +48313,11 @@ function containsWindowsAttacks(str) {
48034
48313
  }
48035
48314
  function isPathInWorkspace(filePath, workspace) {
48036
48315
  try {
48037
- const resolvedPath = path30.resolve(workspace, filePath);
48038
- const realWorkspace = fs24.realpathSync(workspace);
48039
- const realResolvedPath = fs24.realpathSync(resolvedPath);
48040
- const relativePath = path30.relative(realWorkspace, realResolvedPath);
48041
- if (relativePath.startsWith("..") || path30.isAbsolute(relativePath)) {
48316
+ const resolvedPath = path29.resolve(workspace, filePath);
48317
+ const realWorkspace = fs23.realpathSync(workspace);
48318
+ const realResolvedPath = fs23.realpathSync(resolvedPath);
48319
+ const relativePath = path29.relative(realWorkspace, realResolvedPath);
48320
+ if (relativePath.startsWith("..") || path29.isAbsolute(relativePath)) {
48042
48321
  return false;
48043
48322
  }
48044
48323
  return true;
@@ -48050,17 +48329,17 @@ function validatePathForRead(filePath, workspace) {
48050
48329
  return isPathInWorkspace(filePath, workspace);
48051
48330
  }
48052
48331
  function extractTSSymbols(filePath, cwd) {
48053
- const fullPath = path30.join(cwd, filePath);
48332
+ const fullPath = path29.join(cwd, filePath);
48054
48333
  if (!validatePathForRead(fullPath, cwd)) {
48055
48334
  return [];
48056
48335
  }
48057
48336
  let content;
48058
48337
  try {
48059
- const stats = fs24.statSync(fullPath);
48338
+ const stats = fs23.statSync(fullPath);
48060
48339
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
48061
48340
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
48062
48341
  }
48063
- content = fs24.readFileSync(fullPath, "utf-8");
48342
+ content = fs23.readFileSync(fullPath, "utf-8");
48064
48343
  } catch {
48065
48344
  return [];
48066
48345
  }
@@ -48202,17 +48481,17 @@ function extractTSSymbols(filePath, cwd) {
48202
48481
  });
48203
48482
  }
48204
48483
  function extractPythonSymbols(filePath, cwd) {
48205
- const fullPath = path30.join(cwd, filePath);
48484
+ const fullPath = path29.join(cwd, filePath);
48206
48485
  if (!validatePathForRead(fullPath, cwd)) {
48207
48486
  return [];
48208
48487
  }
48209
48488
  let content;
48210
48489
  try {
48211
- const stats = fs24.statSync(fullPath);
48490
+ const stats = fs23.statSync(fullPath);
48212
48491
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
48213
48492
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
48214
48493
  }
48215
- content = fs24.readFileSync(fullPath, "utf-8");
48494
+ content = fs23.readFileSync(fullPath, "utf-8");
48216
48495
  } catch {
48217
48496
  return [];
48218
48497
  }
@@ -48284,7 +48563,7 @@ var symbols = tool({
48284
48563
  }, null, 2);
48285
48564
  }
48286
48565
  const cwd = process.cwd();
48287
- const ext = path30.extname(file3);
48566
+ const ext = path29.extname(file3);
48288
48567
  if (containsControlCharacters(file3)) {
48289
48568
  return JSON.stringify({
48290
48569
  file: file3,
@@ -48352,8 +48631,8 @@ init_test_runner();
48352
48631
 
48353
48632
  // src/tools/todo-extract.ts
48354
48633
  init_dist();
48355
- import * as fs25 from "fs";
48356
- import * as path31 from "path";
48634
+ import * as fs24 from "fs";
48635
+ import * as path30 from "path";
48357
48636
  var MAX_TEXT_LENGTH = 200;
48358
48637
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
48359
48638
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -48424,9 +48703,9 @@ function validatePathsInput(paths, cwd) {
48424
48703
  return { error: "paths contains path traversal", resolvedPath: null };
48425
48704
  }
48426
48705
  try {
48427
- const resolvedPath = path31.resolve(paths);
48428
- const normalizedCwd = path31.resolve(cwd);
48429
- const normalizedResolved = path31.resolve(resolvedPath);
48706
+ const resolvedPath = path30.resolve(paths);
48707
+ const normalizedCwd = path30.resolve(cwd);
48708
+ const normalizedResolved = path30.resolve(resolvedPath);
48430
48709
  if (!normalizedResolved.startsWith(normalizedCwd)) {
48431
48710
  return {
48432
48711
  error: "paths must be within the current working directory",
@@ -48442,13 +48721,13 @@ function validatePathsInput(paths, cwd) {
48442
48721
  }
48443
48722
  }
48444
48723
  function isSupportedExtension(filePath) {
48445
- const ext = path31.extname(filePath).toLowerCase();
48724
+ const ext = path30.extname(filePath).toLowerCase();
48446
48725
  return SUPPORTED_EXTENSIONS2.has(ext);
48447
48726
  }
48448
48727
  function findSourceFiles3(dir, files = []) {
48449
48728
  let entries;
48450
48729
  try {
48451
- entries = fs25.readdirSync(dir);
48730
+ entries = fs24.readdirSync(dir);
48452
48731
  } catch {
48453
48732
  return files;
48454
48733
  }
@@ -48457,10 +48736,10 @@ function findSourceFiles3(dir, files = []) {
48457
48736
  if (SKIP_DIRECTORIES3.has(entry)) {
48458
48737
  continue;
48459
48738
  }
48460
- const fullPath = path31.join(dir, entry);
48739
+ const fullPath = path30.join(dir, entry);
48461
48740
  let stat;
48462
48741
  try {
48463
- stat = fs25.statSync(fullPath);
48742
+ stat = fs24.statSync(fullPath);
48464
48743
  } catch {
48465
48744
  continue;
48466
48745
  }
@@ -48553,7 +48832,7 @@ var todo_extract = tool({
48553
48832
  return JSON.stringify(errorResult, null, 2);
48554
48833
  }
48555
48834
  const scanPath = resolvedPath;
48556
- if (!fs25.existsSync(scanPath)) {
48835
+ if (!fs24.existsSync(scanPath)) {
48557
48836
  const errorResult = {
48558
48837
  error: `path not found: ${pathsInput}`,
48559
48838
  total: 0,
@@ -48563,13 +48842,13 @@ var todo_extract = tool({
48563
48842
  return JSON.stringify(errorResult, null, 2);
48564
48843
  }
48565
48844
  const filesToScan = [];
48566
- const stat = fs25.statSync(scanPath);
48845
+ const stat = fs24.statSync(scanPath);
48567
48846
  if (stat.isFile()) {
48568
48847
  if (isSupportedExtension(scanPath)) {
48569
48848
  filesToScan.push(scanPath);
48570
48849
  } else {
48571
48850
  const errorResult = {
48572
- error: `unsupported file extension: ${path31.extname(scanPath)}`,
48851
+ error: `unsupported file extension: ${path30.extname(scanPath)}`,
48573
48852
  total: 0,
48574
48853
  byPriority: { high: 0, medium: 0, low: 0 },
48575
48854
  entries: []
@@ -48582,11 +48861,11 @@ var todo_extract = tool({
48582
48861
  const allEntries = [];
48583
48862
  for (const filePath of filesToScan) {
48584
48863
  try {
48585
- const fileStat = fs25.statSync(filePath);
48864
+ const fileStat = fs24.statSync(filePath);
48586
48865
  if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
48587
48866
  continue;
48588
48867
  }
48589
- const content = fs25.readFileSync(filePath, "utf-8");
48868
+ const content = fs24.readFileSync(filePath, "utf-8");
48590
48869
  const entries = parseTodoComments(content, filePath, tagsSet);
48591
48870
  allEntries.push(...entries);
48592
48871
  } catch {}
@@ -48684,7 +48963,7 @@ var OpenCodeSwarm = async (ctx) => {
48684
48963
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
48685
48964
  preflightTriggerManager = new PTM(automationConfig);
48686
48965
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
48687
- const swarmDir = path32.resolve(ctx.directory, ".swarm");
48966
+ const swarmDir = path31.resolve(ctx.directory, ".swarm");
48688
48967
  statusArtifact = new ASA(swarmDir);
48689
48968
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
48690
48969
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {