opencode-swarm 6.13.2 → 6.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -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"),
@@ -30184,9 +30200,10 @@ function hasCompoundTestExtension(filename) {
30184
30200
  function getTestFilesFromConvention(sourceFiles) {
30185
30201
  const testFiles = [];
30186
30202
  for (const file3 of sourceFiles) {
30203
+ const normalizedPath = file3.replace(/\\/g, "/");
30187
30204
  const basename2 = path12.basename(file3);
30188
30205
  const dirname4 = path12.dirname(file3);
30189
- if (hasCompoundTestExtension(basename2) || basename2.includes(".spec.") || basename2.includes(".test.")) {
30206
+ if (hasCompoundTestExtension(basename2) || basename2.includes(".spec.") || basename2.includes(".test.") || normalizedPath.includes("/__tests__/") || normalizedPath.includes("/tests/") || normalizedPath.includes("/test/")) {
30190
30207
  if (!testFiles.includes(file3)) {
30191
30208
  testFiles.push(file3);
30192
30209
  }
@@ -31423,7 +31440,7 @@ var init_preflight_integration = __esm(() => {
31423
31440
  });
31424
31441
 
31425
31442
  // src/index.ts
31426
- import * as path32 from "path";
31443
+ import * as path31 from "path";
31427
31444
 
31428
31445
  // src/tools/tool-names.ts
31429
31446
  var TOOL_NAMES = [
@@ -31566,16 +31583,15 @@ for (const [agentName, tools] of Object.entries(AGENT_TOOL_MAP)) {
31566
31583
  }
31567
31584
  }
31568
31585
  var DEFAULT_MODELS = {
31569
- architect: "anthropic/claude-sonnet-4-20250514",
31570
- explorer: "google/gemini-2.5-flash",
31571
- coder: "anthropic/claude-sonnet-4-20250514",
31572
- test_engineer: "google/gemini-2.5-flash",
31573
- sme: "google/gemini-2.5-flash",
31574
- reviewer: "google/gemini-2.5-flash",
31575
- critic: "google/gemini-2.5-flash",
31576
- docs: "google/gemini-2.5-flash",
31577
- designer: "google/gemini-2.5-flash",
31578
- default: "google/gemini-2.5-flash"
31586
+ explorer: "opencode/trinity-large-preview-free",
31587
+ coder: "opencode/minimax-m2.5-free",
31588
+ reviewer: "opencode/big-pickle",
31589
+ test_engineer: "opencode/gpt-5-nano",
31590
+ sme: "opencode/trinity-large-preview-free",
31591
+ critic: "opencode/trinity-large-preview-free",
31592
+ docs: "opencode/trinity-large-preview-free",
31593
+ designer: "opencode/trinity-large-preview-free",
31594
+ default: "opencode/trinity-large-preview-free"
31579
31595
  };
31580
31596
  var DEFAULT_SCORING_CONFIG = {
31581
31597
  enabled: false,
@@ -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:
@@ -36786,6 +36890,26 @@ function isOutsideSwarmDir(filePath) {
36786
36890
  const relative2 = path15.relative(swarmDir, resolved);
36787
36891
  return relative2.startsWith("..") || path15.isAbsolute(relative2);
36788
36892
  }
36893
+ function isSourceCodePath(filePath) {
36894
+ if (!filePath)
36895
+ return false;
36896
+ const normalized = filePath.replace(/\\/g, "/");
36897
+ const nonSourcePatterns = [
36898
+ /^README(\..+)?$/i,
36899
+ /\/README(\..+)?$/i,
36900
+ /^CHANGELOG(\..+)?$/i,
36901
+ /\/CHANGELOG(\..+)?$/i,
36902
+ /^package\.json$/,
36903
+ /\/package\.json$/,
36904
+ /^\.github\//,
36905
+ /\/\.github\//,
36906
+ /^docs\//,
36907
+ /\/docs\//,
36908
+ /^\.swarm\//,
36909
+ /\/\.swarm\//
36910
+ ];
36911
+ return !nonSourcePatterns.some((pattern) => pattern.test(normalized));
36912
+ }
36789
36913
  function isGateTool(toolName) {
36790
36914
  const normalized = toolName.replace(/^[^:]+[:.]/, "");
36791
36915
  const gateTools = [
@@ -36831,7 +36955,7 @@ function createGuardrailsHooks(config3) {
36831
36955
  if (isArchitect(input.sessionID) && isWriteTool(input.tool)) {
36832
36956
  const args2 = output.args;
36833
36957
  const targetPath = args2?.filePath ?? args2?.path ?? args2?.file ?? args2?.target;
36834
- if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath)) {
36958
+ if (typeof targetPath === "string" && isOutsideSwarmDir(targetPath) && isSourceCodePath(targetPath)) {
36835
36959
  const session2 = swarmState.agentSessions.get(input.sessionID);
36836
36960
  if (session2) {
36837
36961
  session2.architectWriteCount++;
@@ -37344,8 +37468,7 @@ ${originalText}`;
37344
37468
  };
37345
37469
  }
37346
37470
  // src/hooks/system-enhancer.ts
37347
- import * as fs11 from "fs";
37348
- import * as path17 from "path";
37471
+ init_manager();
37349
37472
  init_manager2();
37350
37473
 
37351
37474
  // src/services/decision-drift-analyzer.ts
@@ -37722,6 +37845,170 @@ function estimateContentType(text) {
37722
37845
  }
37723
37846
  return "prose";
37724
37847
  }
37848
+ async function buildRetroInjection(directory, currentPhaseNumber, currentPlanTitle) {
37849
+ try {
37850
+ const prevPhase = currentPhaseNumber - 1;
37851
+ if (prevPhase >= 1) {
37852
+ const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
37853
+ if (bundle && bundle.entries.length > 0) {
37854
+ const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
37855
+ if (retroEntry && retroEntry.verdict !== "fail") {
37856
+ const lessons = retroEntry.lessons_learned ?? [];
37857
+ const rejections = retroEntry.top_rejection_reasons ?? [];
37858
+ const nonSessionDirectives = (retroEntry.user_directives ?? []).filter((d) => d.scope !== "session");
37859
+ let block = `## Previous Phase Retrospective (Phase ${prevPhase})
37860
+ **Outcome:** ${retroEntry.summary ?? "Phase completed."}
37861
+ **Rejection reasons:** ${rejections.join(", ") || "None"}
37862
+ **Lessons learned:**
37863
+ ${lessons.map((l) => `- ${l}`).join(`
37864
+ `)}
37865
+
37866
+ \u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
37867
+ if (nonSessionDirectives.length > 0) {
37868
+ const top5 = nonSessionDirectives.slice(0, 5);
37869
+ block += `
37870
+
37871
+ ## User Directives (from Phase ${prevPhase})
37872
+ ${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
37873
+ `)}`;
37874
+ }
37875
+ return block;
37876
+ }
37877
+ }
37878
+ const taskIds = await listEvidenceTaskIds(directory);
37879
+ const retroIds = taskIds.filter((id) => id.startsWith("retro-"));
37880
+ let latestRetro = null;
37881
+ for (const taskId of retroIds) {
37882
+ const b = await loadEvidence(directory, taskId);
37883
+ if (b && b.entries.length > 0) {
37884
+ for (const entry of b.entries) {
37885
+ if (entry.type === "retrospective") {
37886
+ const retro = entry;
37887
+ if (retro.verdict !== "fail") {
37888
+ if (latestRetro === null || retro.phase_number > latestRetro.phase) {
37889
+ latestRetro = { entry: retro, phase: retro.phase_number };
37890
+ }
37891
+ }
37892
+ }
37893
+ }
37894
+ }
37895
+ }
37896
+ if (latestRetro) {
37897
+ const { entry, phase } = latestRetro;
37898
+ const lessons = entry.lessons_learned ?? [];
37899
+ const rejections = entry.top_rejection_reasons ?? [];
37900
+ const nonSessionDirectives = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
37901
+ let block = `## Previous Phase Retrospective (Phase ${phase})
37902
+ **Outcome:** ${entry.summary ?? "Phase completed."}
37903
+ **Rejection reasons:** ${rejections.join(", ") || "None"}
37904
+ **Lessons learned:**
37905
+ ${lessons.map((l) => `- ${l}`).join(`
37906
+ `)}
37907
+
37908
+ \u26A0\uFE0F Apply these lessons to the current phase. Do not repeat the same mistakes.`;
37909
+ if (nonSessionDirectives.length > 0) {
37910
+ const top5 = nonSessionDirectives.slice(0, 5);
37911
+ block += `
37912
+
37913
+ ## User Directives (from Phase ${phase})
37914
+ ${top5.map((d) => `- [${d.category}] ${d.directive}`).join(`
37915
+ `)}`;
37916
+ }
37917
+ return block;
37918
+ }
37919
+ return null;
37920
+ }
37921
+ const allTaskIds = await listEvidenceTaskIds(directory);
37922
+ const allRetroIds = allTaskIds.filter((id) => id.startsWith("retro-"));
37923
+ if (allRetroIds.length === 0) {
37924
+ return null;
37925
+ }
37926
+ const allRetros = [];
37927
+ const cutoffMs = 30 * 24 * 60 * 60 * 1000;
37928
+ const now = Date.now();
37929
+ for (const taskId of allRetroIds) {
37930
+ const b = await loadEvidence(directory, taskId);
37931
+ if (!b)
37932
+ continue;
37933
+ for (const e of b.entries) {
37934
+ if (e.type === "retrospective") {
37935
+ const retro = e;
37936
+ if (retro.verdict === "fail")
37937
+ continue;
37938
+ if (currentPlanTitle && typeof retro.metadata === "object" && retro.metadata !== null && "plan_id" in retro.metadata && retro.metadata.plan_id === currentPlanTitle)
37939
+ continue;
37940
+ const ts = retro.timestamp ?? b.created_at;
37941
+ const ageMs = now - new Date(ts).getTime();
37942
+ if (isNaN(ageMs) || ageMs > cutoffMs)
37943
+ continue;
37944
+ allRetros.push({ entry: retro, timestamp: ts });
37945
+ }
37946
+ }
37947
+ }
37948
+ if (allRetros.length === 0) {
37949
+ return null;
37950
+ }
37951
+ allRetros.sort((a, b) => {
37952
+ const ta = new Date(a.timestamp).getTime();
37953
+ const tb = new Date(b.timestamp).getTime();
37954
+ if (isNaN(ta) && isNaN(tb))
37955
+ return 0;
37956
+ if (isNaN(ta))
37957
+ return 1;
37958
+ if (isNaN(tb))
37959
+ return -1;
37960
+ return tb - ta;
37961
+ });
37962
+ const top3 = allRetros.slice(0, 3);
37963
+ const lines = [
37964
+ "## Historical Lessons (from recent prior projects)"
37965
+ ];
37966
+ lines.push("Most recent retrospectives in this workspace:");
37967
+ const allCarriedDirectives = [];
37968
+ for (const { entry, timestamp } of top3) {
37969
+ const date9 = timestamp.split("T")[0] ?? "unknown";
37970
+ const summary = entry.summary ?? `Phase ${entry.phase_number} completed`;
37971
+ const topLesson = entry.lessons_learned?.[0] ?? "No lessons recorded";
37972
+ lines.push(`- Phase ${entry.phase_number} (${date9}): ${summary}`);
37973
+ lines.push(` Key lesson: ${topLesson}`);
37974
+ const nonSession = (entry.user_directives ?? []).filter((d) => d.scope !== "session");
37975
+ allCarriedDirectives.push(...nonSession);
37976
+ }
37977
+ if (allCarriedDirectives.length > 0) {
37978
+ const top5 = allCarriedDirectives.slice(0, 5);
37979
+ lines.push("User directives carried forward:");
37980
+ for (const d of top5) {
37981
+ lines.push(`- [${d.category}] ${d.directive}`);
37982
+ }
37983
+ }
37984
+ const tier2Block = lines.join(`
37985
+ `);
37986
+ return tier2Block.length <= 800 ? tier2Block : `${tier2Block.substring(0, 797)}...`;
37987
+ } catch {
37988
+ return null;
37989
+ }
37990
+ }
37991
+ async function buildCoderRetroInjection(directory, currentPhaseNumber) {
37992
+ try {
37993
+ const prevPhase = currentPhaseNumber - 1;
37994
+ if (prevPhase < 1)
37995
+ return null;
37996
+ const bundle = await loadEvidence(directory, `retro-${prevPhase}`);
37997
+ if (!bundle || bundle.entries.length === 0)
37998
+ return null;
37999
+ const retroEntry = bundle.entries.find((entry) => entry.type === "retrospective");
38000
+ if (!retroEntry || retroEntry.verdict === "fail")
38001
+ return null;
38002
+ const lessons = retroEntry.lessons_learned ?? [];
38003
+ const summaryLine = `[SWARM RETROSPECTIVE] From Phase ${prevPhase}:${retroEntry.summary ? " " + retroEntry.summary : ""}`;
38004
+ const allLines = [summaryLine, ...lessons];
38005
+ const text = allLines.join(`
38006
+ `);
38007
+ return text.length <= 400 ? text : `${text.substring(0, 397)}...`;
38008
+ } catch {
38009
+ return null;
38010
+ }
38011
+ }
37725
38012
  function createSystemEnhancerHook(config3, directory) {
37726
38013
  const enabled = config3.hooks?.system_enhancer !== false;
37727
38014
  if (!enabled) {
@@ -37841,43 +38128,27 @@ function createSystemEnhancerHook(config3, directory) {
37841
38128
  }
37842
38129
  }
37843
38130
  }
38131
+ if (baseRole === "coder") {
38132
+ try {
38133
+ const currentPhaseNum_coder = plan2?.current_phase ?? 1;
38134
+ const coderRetro = await buildCoderRetroInjection(directory, currentPhaseNum_coder);
38135
+ if (coderRetro) {
38136
+ tryInject(coderRetro);
38137
+ }
38138
+ } catch {}
38139
+ }
37844
38140
  const sessionId_retro = _input.sessionID;
37845
38141
  const activeAgent_retro = swarmState.activeAgent.get(sessionId_retro ?? "");
37846
38142
  const isArchitect2 = !activeAgent_retro || stripKnownSwarmPrefix(activeAgent_retro) === "architect";
37847
38143
  if (isArchitect2) {
37848
38144
  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
- }
38145
+ const currentPhaseNum = plan2?.current_phase ?? 1;
38146
+ const retroText = await buildRetroInjection(directory, currentPhaseNum, plan2?.title ?? undefined);
38147
+ if (retroText) {
38148
+ if (retroText.length <= 1600) {
38149
+ tryInject(retroText);
38150
+ } else {
38151
+ tryInject(`${retroText.substring(0, 1600)}...`);
37881
38152
  }
37882
38153
  }
37883
38154
  } catch {}
@@ -38137,43 +38408,18 @@ function createSystemEnhancerHook(config3, directory) {
38137
38408
  const isArchitect_b = !activeAgent_retro_b || stripKnownSwarmPrefix(activeAgent_retro_b) === "architect";
38138
38409
  if (isArchitect_b) {
38139
38410
  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
- }
38411
+ const currentPhaseNum_b = plan?.current_phase ?? 1;
38412
+ const retroText_b = await buildRetroInjection(directory, currentPhaseNum_b, plan?.title ?? undefined);
38413
+ if (retroText_b) {
38414
+ const text = retroText_b.length <= 1600 ? retroText_b : `${retroText_b.substring(0, 1597)}...`;
38415
+ candidates.push({
38416
+ id: `candidate-${idCounter++}`,
38417
+ kind: "phase",
38418
+ text,
38419
+ tokens: estimateTokens(text),
38420
+ priority: 2,
38421
+ metadata: { contentType: "prose" }
38422
+ });
38177
38423
  }
38178
38424
  } catch {}
38179
38425
  if (mode_b !== "DISCOVER") {
@@ -38211,6 +38457,24 @@ function createSystemEnhancerHook(config3, directory) {
38211
38457
  }
38212
38458
  }
38213
38459
  }
38460
+ const activeAgent_coder_b = swarmState.activeAgent.get(_input.sessionID ?? "");
38461
+ const isCoder_b = activeAgent_coder_b && stripKnownSwarmPrefix(activeAgent_coder_b) === "coder";
38462
+ if (isCoder_b) {
38463
+ try {
38464
+ const currentPhaseNum_coder_b = plan?.current_phase ?? 1;
38465
+ const coderRetro_b = await buildCoderRetroInjection(directory, currentPhaseNum_coder_b);
38466
+ if (coderRetro_b) {
38467
+ candidates.push({
38468
+ id: `candidate-${idCounter++}`,
38469
+ kind: "agent_context",
38470
+ text: coderRetro_b,
38471
+ tokens: estimateTokens(coderRetro_b),
38472
+ priority: 2,
38473
+ metadata: { contentType: "prose" }
38474
+ });
38475
+ }
38476
+ } catch {}
38477
+ }
38214
38478
  const automationCapabilities_b = config3.automation?.capabilities;
38215
38479
  if (automationCapabilities_b?.decision_drift_detection === true && sessionId_retro_b) {
38216
38480
  const activeAgentForDrift_b = swarmState.activeAgent.get(sessionId_retro_b ?? "");
@@ -38465,8 +38729,8 @@ init_dist();
38465
38729
 
38466
38730
  // src/build/discovery.ts
38467
38731
  init_dist();
38468
- import * as fs12 from "fs";
38469
- import * as path18 from "path";
38732
+ import * as fs11 from "fs";
38733
+ import * as path17 from "path";
38470
38734
  var ECOSYSTEMS = [
38471
38735
  {
38472
38736
  ecosystem: "node",
@@ -38514,7 +38778,10 @@ var ECOSYSTEMS = [
38514
38778
  buildFiles: ["build.gradle", "build.gradle.kts", "gradle.properties"],
38515
38779
  toolchainCommands: ["gradle", "gradlew"],
38516
38780
  commands: [
38517
- { command: "./gradlew build", priority: 1 },
38781
+ {
38782
+ command: process.platform === "win32" ? "gradlew.bat build" : "./gradlew build",
38783
+ priority: 1
38784
+ },
38518
38785
  { command: "gradle build", priority: 2 }
38519
38786
  ]
38520
38787
  },
@@ -38578,18 +38845,18 @@ function findBuildFiles(workingDir, patterns) {
38578
38845
  if (pattern.includes("*")) {
38579
38846
  const dir = workingDir;
38580
38847
  try {
38581
- const files = fs12.readdirSync(dir);
38848
+ const files = fs11.readdirSync(dir);
38582
38849
  const matches = files.filter((f) => {
38583
38850
  const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
38584
38851
  return regex.test(f);
38585
38852
  });
38586
38853
  if (matches.length > 0) {
38587
- return path18.join(dir, matches[0]);
38854
+ return path17.join(dir, matches[0]);
38588
38855
  }
38589
38856
  } catch {}
38590
38857
  } else {
38591
- const filePath = path18.join(workingDir, pattern);
38592
- if (fs12.existsSync(filePath)) {
38858
+ const filePath = path17.join(workingDir, pattern);
38859
+ if (fs11.existsSync(filePath)) {
38593
38860
  return filePath;
38594
38861
  }
38595
38862
  }
@@ -38597,12 +38864,12 @@ function findBuildFiles(workingDir, patterns) {
38597
38864
  return null;
38598
38865
  }
38599
38866
  function getRepoDefinedScripts(workingDir, scripts) {
38600
- const packageJsonPath = path18.join(workingDir, "package.json");
38601
- if (!fs12.existsSync(packageJsonPath)) {
38867
+ const packageJsonPath = path17.join(workingDir, "package.json");
38868
+ if (!fs11.existsSync(packageJsonPath)) {
38602
38869
  return [];
38603
38870
  }
38604
38871
  try {
38605
- const content = fs12.readFileSync(packageJsonPath, "utf-8");
38872
+ const content = fs11.readFileSync(packageJsonPath, "utf-8");
38606
38873
  const pkg = JSON.parse(content);
38607
38874
  if (!pkg.scripts || typeof pkg.scripts !== "object") {
38608
38875
  return [];
@@ -38638,8 +38905,8 @@ function findAllBuildFiles(workingDir) {
38638
38905
  const regex = new RegExp(`^${pattern.replace(/\*/g, ".*")}$`);
38639
38906
  findFilesRecursive(workingDir, regex, allBuildFiles);
38640
38907
  } else {
38641
- const filePath = path18.join(workingDir, pattern);
38642
- if (fs12.existsSync(filePath)) {
38908
+ const filePath = path17.join(workingDir, pattern);
38909
+ if (fs11.existsSync(filePath)) {
38643
38910
  allBuildFiles.add(filePath);
38644
38911
  }
38645
38912
  }
@@ -38649,9 +38916,9 @@ function findAllBuildFiles(workingDir) {
38649
38916
  }
38650
38917
  function findFilesRecursive(dir, regex, results) {
38651
38918
  try {
38652
- const entries = fs12.readdirSync(dir, { withFileTypes: true });
38919
+ const entries = fs11.readdirSync(dir, { withFileTypes: true });
38653
38920
  for (const entry of entries) {
38654
- const fullPath = path18.join(dir, entry.name);
38921
+ const fullPath = path17.join(dir, entry.name);
38655
38922
  if (entry.isDirectory() && !["node_modules", ".git", "dist", "build", "target"].includes(entry.name)) {
38656
38923
  findFilesRecursive(fullPath, regex, results);
38657
38924
  } else if (entry.isFile() && regex.test(entry.name)) {
@@ -38892,8 +39159,8 @@ var build_check = tool({
38892
39159
  // src/tools/checkpoint.ts
38893
39160
  init_tool();
38894
39161
  import { spawnSync } from "child_process";
38895
- import * as fs13 from "fs";
38896
- import * as path19 from "path";
39162
+ import * as fs12 from "fs";
39163
+ import * as path18 from "path";
38897
39164
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
38898
39165
  var MAX_LABEL_LENGTH = 100;
38899
39166
  var GIT_TIMEOUT_MS = 30000;
@@ -38944,13 +39211,13 @@ function validateLabel(label) {
38944
39211
  return null;
38945
39212
  }
38946
39213
  function getCheckpointLogPath() {
38947
- return path19.join(process.cwd(), CHECKPOINT_LOG_PATH);
39214
+ return path18.join(process.cwd(), CHECKPOINT_LOG_PATH);
38948
39215
  }
38949
39216
  function readCheckpointLog() {
38950
39217
  const logPath = getCheckpointLogPath();
38951
39218
  try {
38952
- if (fs13.existsSync(logPath)) {
38953
- const content = fs13.readFileSync(logPath, "utf-8");
39219
+ if (fs12.existsSync(logPath)) {
39220
+ const content = fs12.readFileSync(logPath, "utf-8");
38954
39221
  const parsed = JSON.parse(content);
38955
39222
  if (!parsed.checkpoints || !Array.isArray(parsed.checkpoints)) {
38956
39223
  return { version: 1, checkpoints: [] };
@@ -38962,13 +39229,13 @@ function readCheckpointLog() {
38962
39229
  }
38963
39230
  function writeCheckpointLog(log2) {
38964
39231
  const logPath = getCheckpointLogPath();
38965
- const dir = path19.dirname(logPath);
38966
- if (!fs13.existsSync(dir)) {
38967
- fs13.mkdirSync(dir, { recursive: true });
39232
+ const dir = path18.dirname(logPath);
39233
+ if (!fs12.existsSync(dir)) {
39234
+ fs12.mkdirSync(dir, { recursive: true });
38968
39235
  }
38969
39236
  const tempPath = `${logPath}.tmp`;
38970
- fs13.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
38971
- fs13.renameSync(tempPath, logPath);
39237
+ fs12.writeFileSync(tempPath, JSON.stringify(log2, null, 2), "utf-8");
39238
+ fs12.renameSync(tempPath, logPath);
38972
39239
  }
38973
39240
  function gitExec(args2) {
38974
39241
  const result = spawnSync("git", args2, {
@@ -39168,8 +39435,8 @@ var checkpoint = tool({
39168
39435
  });
39169
39436
  // src/tools/complexity-hotspots.ts
39170
39437
  init_dist();
39171
- import * as fs14 from "fs";
39172
- import * as path20 from "path";
39438
+ import * as fs13 from "fs";
39439
+ import * as path19 from "path";
39173
39440
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
39174
39441
  var DEFAULT_DAYS = 90;
39175
39442
  var DEFAULT_TOP_N = 20;
@@ -39297,11 +39564,11 @@ function estimateComplexity(content) {
39297
39564
  }
39298
39565
  function getComplexityForFile(filePath) {
39299
39566
  try {
39300
- const stat = fs14.statSync(filePath);
39567
+ const stat = fs13.statSync(filePath);
39301
39568
  if (stat.size > MAX_FILE_SIZE_BYTES2) {
39302
39569
  return null;
39303
39570
  }
39304
- const content = fs14.readFileSync(filePath, "utf-8");
39571
+ const content = fs13.readFileSync(filePath, "utf-8");
39305
39572
  return estimateComplexity(content);
39306
39573
  } catch {
39307
39574
  return null;
@@ -39312,7 +39579,7 @@ async function analyzeHotspots(days, topN, extensions) {
39312
39579
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
39313
39580
  const filteredChurn = new Map;
39314
39581
  for (const [file3, count] of churnMap) {
39315
- const ext = path20.extname(file3).toLowerCase();
39582
+ const ext = path19.extname(file3).toLowerCase();
39316
39583
  if (extSet.has(ext)) {
39317
39584
  filteredChurn.set(file3, count);
39318
39585
  }
@@ -39322,8 +39589,8 @@ async function analyzeHotspots(days, topN, extensions) {
39322
39589
  let analyzedFiles = 0;
39323
39590
  for (const [file3, churnCount] of filteredChurn) {
39324
39591
  let fullPath = file3;
39325
- if (!fs14.existsSync(fullPath)) {
39326
- fullPath = path20.join(cwd, file3);
39592
+ if (!fs13.existsSync(fullPath)) {
39593
+ fullPath = path19.join(cwd, file3);
39327
39594
  }
39328
39595
  const complexity = getComplexityForFile(fullPath);
39329
39596
  if (complexity !== null) {
@@ -39481,14 +39748,14 @@ function validateBase(base) {
39481
39748
  function validatePaths(paths) {
39482
39749
  if (!paths)
39483
39750
  return null;
39484
- for (const path21 of paths) {
39485
- if (!path21 || path21.length === 0) {
39751
+ for (const path20 of paths) {
39752
+ if (!path20 || path20.length === 0) {
39486
39753
  return "empty path not allowed";
39487
39754
  }
39488
- if (path21.length > MAX_PATH_LENGTH) {
39755
+ if (path20.length > MAX_PATH_LENGTH) {
39489
39756
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
39490
39757
  }
39491
- if (SHELL_METACHARACTERS2.test(path21)) {
39758
+ if (SHELL_METACHARACTERS2.test(path20)) {
39492
39759
  return "path contains shell metacharacters";
39493
39760
  }
39494
39761
  }
@@ -39551,8 +39818,8 @@ var diff = tool({
39551
39818
  if (parts2.length >= 3) {
39552
39819
  const additions = parseInt(parts2[0], 10) || 0;
39553
39820
  const deletions = parseInt(parts2[1], 10) || 0;
39554
- const path21 = parts2[2];
39555
- files.push({ path: path21, additions, deletions });
39821
+ const path20 = parts2[2];
39822
+ files.push({ path: path20, additions, deletions });
39556
39823
  }
39557
39824
  }
39558
39825
  const contractChanges = [];
@@ -39780,8 +40047,8 @@ Use these as DOMAIN values when delegating to @sme.`;
39780
40047
  });
39781
40048
  // src/tools/evidence-check.ts
39782
40049
  init_dist();
39783
- import * as fs15 from "fs";
39784
- import * as path21 from "path";
40050
+ import * as fs14 from "fs";
40051
+ import * as path20 from "path";
39785
40052
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
39786
40053
  var MAX_EVIDENCE_FILES = 1000;
39787
40054
  var EVIDENCE_DIR = ".swarm/evidence";
@@ -39804,9 +40071,9 @@ function validateRequiredTypes(input) {
39804
40071
  return null;
39805
40072
  }
39806
40073
  function isPathWithinSwarm(filePath, cwd) {
39807
- const normalizedCwd = path21.resolve(cwd);
39808
- const swarmPath = path21.join(normalizedCwd, ".swarm");
39809
- const normalizedPath = path21.resolve(filePath);
40074
+ const normalizedCwd = path20.resolve(cwd);
40075
+ const swarmPath = path20.join(normalizedCwd, ".swarm");
40076
+ const normalizedPath = path20.resolve(filePath);
39810
40077
  return normalizedPath.startsWith(swarmPath);
39811
40078
  }
39812
40079
  function parseCompletedTasks(planContent) {
@@ -39822,12 +40089,12 @@ function parseCompletedTasks(planContent) {
39822
40089
  }
39823
40090
  function readEvidenceFiles(evidenceDir, _cwd) {
39824
40091
  const evidence = [];
39825
- if (!fs15.existsSync(evidenceDir) || !fs15.statSync(evidenceDir).isDirectory()) {
40092
+ if (!fs14.existsSync(evidenceDir) || !fs14.statSync(evidenceDir).isDirectory()) {
39826
40093
  return evidence;
39827
40094
  }
39828
40095
  let files;
39829
40096
  try {
39830
- files = fs15.readdirSync(evidenceDir);
40097
+ files = fs14.readdirSync(evidenceDir);
39831
40098
  } catch {
39832
40099
  return evidence;
39833
40100
  }
@@ -39836,14 +40103,14 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39836
40103
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
39837
40104
  continue;
39838
40105
  }
39839
- const filePath = path21.join(evidenceDir, filename);
40106
+ const filePath = path20.join(evidenceDir, filename);
39840
40107
  try {
39841
- const resolvedPath = path21.resolve(filePath);
39842
- const evidenceDirResolved = path21.resolve(evidenceDir);
40108
+ const resolvedPath = path20.resolve(filePath);
40109
+ const evidenceDirResolved = path20.resolve(evidenceDir);
39843
40110
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
39844
40111
  continue;
39845
40112
  }
39846
- const stat = fs15.lstatSync(filePath);
40113
+ const stat = fs14.lstatSync(filePath);
39847
40114
  if (!stat.isFile()) {
39848
40115
  continue;
39849
40116
  }
@@ -39852,7 +40119,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39852
40119
  }
39853
40120
  let fileStat;
39854
40121
  try {
39855
- fileStat = fs15.statSync(filePath);
40122
+ fileStat = fs14.statSync(filePath);
39856
40123
  if (fileStat.size > MAX_FILE_SIZE_BYTES3) {
39857
40124
  continue;
39858
40125
  }
@@ -39861,7 +40128,7 @@ function readEvidenceFiles(evidenceDir, _cwd) {
39861
40128
  }
39862
40129
  let content;
39863
40130
  try {
39864
- content = fs15.readFileSync(filePath, "utf-8");
40131
+ content = fs14.readFileSync(filePath, "utf-8");
39865
40132
  } catch {
39866
40133
  continue;
39867
40134
  }
@@ -39946,7 +40213,7 @@ var evidence_check = tool({
39946
40213
  return JSON.stringify(errorResult, null, 2);
39947
40214
  }
39948
40215
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0);
39949
- const planPath = path21.join(cwd, PLAN_FILE);
40216
+ const planPath = path20.join(cwd, PLAN_FILE);
39950
40217
  if (!isPathWithinSwarm(planPath, cwd)) {
39951
40218
  const errorResult = {
39952
40219
  error: "plan file path validation failed",
@@ -39960,7 +40227,7 @@ var evidence_check = tool({
39960
40227
  }
39961
40228
  let planContent;
39962
40229
  try {
39963
- planContent = fs15.readFileSync(planPath, "utf-8");
40230
+ planContent = fs14.readFileSync(planPath, "utf-8");
39964
40231
  } catch {
39965
40232
  const result2 = {
39966
40233
  message: "No completed tasks found in plan.",
@@ -39978,7 +40245,7 @@ var evidence_check = tool({
39978
40245
  };
39979
40246
  return JSON.stringify(result2, null, 2);
39980
40247
  }
39981
- const evidenceDir = path21.join(cwd, EVIDENCE_DIR);
40248
+ const evidenceDir = path20.join(cwd, EVIDENCE_DIR);
39982
40249
  const evidence = readEvidenceFiles(evidenceDir, cwd);
39983
40250
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
39984
40251
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -39994,8 +40261,8 @@ var evidence_check = tool({
39994
40261
  });
39995
40262
  // src/tools/file-extractor.ts
39996
40263
  init_tool();
39997
- import * as fs16 from "fs";
39998
- import * as path22 from "path";
40264
+ import * as fs15 from "fs";
40265
+ import * as path21 from "path";
39999
40266
  var EXT_MAP = {
40000
40267
  python: ".py",
40001
40268
  py: ".py",
@@ -40057,8 +40324,8 @@ var extract_code_blocks = tool({
40057
40324
  execute: async (args2) => {
40058
40325
  const { content, output_dir, prefix } = args2;
40059
40326
  const targetDir = output_dir || process.cwd();
40060
- if (!fs16.existsSync(targetDir)) {
40061
- fs16.mkdirSync(targetDir, { recursive: true });
40327
+ if (!fs15.existsSync(targetDir)) {
40328
+ fs15.mkdirSync(targetDir, { recursive: true });
40062
40329
  }
40063
40330
  const pattern = /```(\w*)\n([\s\S]*?)```/g;
40064
40331
  const matches = [...content.matchAll(pattern)];
@@ -40073,16 +40340,16 @@ var extract_code_blocks = tool({
40073
40340
  if (prefix) {
40074
40341
  filename = `${prefix}_${filename}`;
40075
40342
  }
40076
- let filepath = path22.join(targetDir, filename);
40077
- const base = path22.basename(filepath, path22.extname(filepath));
40078
- const ext = path22.extname(filepath);
40343
+ let filepath = path21.join(targetDir, filename);
40344
+ const base = path21.basename(filepath, path21.extname(filepath));
40345
+ const ext = path21.extname(filepath);
40079
40346
  let counter = 1;
40080
- while (fs16.existsSync(filepath)) {
40081
- filepath = path22.join(targetDir, `${base}_${counter}${ext}`);
40347
+ while (fs15.existsSync(filepath)) {
40348
+ filepath = path21.join(targetDir, `${base}_${counter}${ext}`);
40082
40349
  counter++;
40083
40350
  }
40084
40351
  try {
40085
- fs16.writeFileSync(filepath, code.trim(), "utf-8");
40352
+ fs15.writeFileSync(filepath, code.trim(), "utf-8");
40086
40353
  savedFiles.push(filepath);
40087
40354
  } catch (error93) {
40088
40355
  errors5.push(`Failed to save ${filename}: ${error93 instanceof Error ? error93.message : String(error93)}`);
@@ -40190,8 +40457,8 @@ var gitingest = tool({
40190
40457
  });
40191
40458
  // src/tools/imports.ts
40192
40459
  init_dist();
40193
- import * as fs17 from "fs";
40194
- import * as path23 from "path";
40460
+ import * as fs16 from "fs";
40461
+ import * as path22 from "path";
40195
40462
  var MAX_FILE_PATH_LENGTH2 = 500;
40196
40463
  var MAX_SYMBOL_LENGTH = 256;
40197
40464
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -40245,7 +40512,7 @@ function validateSymbolInput(symbol3) {
40245
40512
  return null;
40246
40513
  }
40247
40514
  function isBinaryFile2(filePath, buffer) {
40248
- const ext = path23.extname(filePath).toLowerCase();
40515
+ const ext = path22.extname(filePath).toLowerCase();
40249
40516
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
40250
40517
  return false;
40251
40518
  }
@@ -40269,15 +40536,15 @@ function parseImports(content, targetFile, targetSymbol) {
40269
40536
  const imports = [];
40270
40537
  let _resolvedTarget;
40271
40538
  try {
40272
- _resolvedTarget = path23.resolve(targetFile);
40539
+ _resolvedTarget = path22.resolve(targetFile);
40273
40540
  } catch {
40274
40541
  _resolvedTarget = targetFile;
40275
40542
  }
40276
- const targetBasename = path23.basename(targetFile, path23.extname(targetFile));
40543
+ const targetBasename = path22.basename(targetFile, path22.extname(targetFile));
40277
40544
  const targetWithExt = targetFile;
40278
40545
  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, "/");
40546
+ const normalizedTargetWithExt = path22.normalize(targetWithExt).replace(/\\/g, "/");
40547
+ const normalizedTargetWithoutExt = path22.normalize(targetWithoutExt).replace(/\\/g, "/");
40281
40548
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
40282
40549
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
40283
40550
  const modulePath = match[1] || match[2] || match[3];
@@ -40300,9 +40567,9 @@ function parseImports(content, targetFile, targetSymbol) {
40300
40567
  }
40301
40568
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
40302
40569
  let isMatch = false;
40303
- const _targetDir = path23.dirname(targetFile);
40304
- const targetExt = path23.extname(targetFile);
40305
- const targetBasenameNoExt = path23.basename(targetFile, targetExt);
40570
+ const _targetDir = path22.dirname(targetFile);
40571
+ const targetExt = path22.extname(targetFile);
40572
+ const targetBasenameNoExt = path22.basename(targetFile, targetExt);
40306
40573
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
40307
40574
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
40308
40575
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -40359,7 +40626,7 @@ var SKIP_DIRECTORIES2 = new Set([
40359
40626
  function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFiles: 0, fileErrors: [] }) {
40360
40627
  let entries;
40361
40628
  try {
40362
- entries = fs17.readdirSync(dir);
40629
+ entries = fs16.readdirSync(dir);
40363
40630
  } catch (e) {
40364
40631
  stats.fileErrors.push({
40365
40632
  path: dir,
@@ -40370,13 +40637,13 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
40370
40637
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
40371
40638
  for (const entry of entries) {
40372
40639
  if (SKIP_DIRECTORIES2.has(entry)) {
40373
- stats.skippedDirs.push(path23.join(dir, entry));
40640
+ stats.skippedDirs.push(path22.join(dir, entry));
40374
40641
  continue;
40375
40642
  }
40376
- const fullPath = path23.join(dir, entry);
40643
+ const fullPath = path22.join(dir, entry);
40377
40644
  let stat;
40378
40645
  try {
40379
- stat = fs17.statSync(fullPath);
40646
+ stat = fs16.statSync(fullPath);
40380
40647
  } catch (e) {
40381
40648
  stats.fileErrors.push({
40382
40649
  path: fullPath,
@@ -40387,7 +40654,7 @@ function findSourceFiles2(dir, files = [], stats = { skippedDirs: [], skippedFil
40387
40654
  if (stat.isDirectory()) {
40388
40655
  findSourceFiles2(fullPath, files, stats);
40389
40656
  } else if (stat.isFile()) {
40390
- const ext = path23.extname(fullPath).toLowerCase();
40657
+ const ext = path22.extname(fullPath).toLowerCase();
40391
40658
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
40392
40659
  files.push(fullPath);
40393
40660
  }
@@ -40443,8 +40710,8 @@ var imports = tool({
40443
40710
  return JSON.stringify(errorResult, null, 2);
40444
40711
  }
40445
40712
  try {
40446
- const targetFile = path23.resolve(file3);
40447
- if (!fs17.existsSync(targetFile)) {
40713
+ const targetFile = path22.resolve(file3);
40714
+ if (!fs16.existsSync(targetFile)) {
40448
40715
  const errorResult = {
40449
40716
  error: `target file not found: ${file3}`,
40450
40717
  target: file3,
@@ -40454,7 +40721,7 @@ var imports = tool({
40454
40721
  };
40455
40722
  return JSON.stringify(errorResult, null, 2);
40456
40723
  }
40457
- const targetStat = fs17.statSync(targetFile);
40724
+ const targetStat = fs16.statSync(targetFile);
40458
40725
  if (!targetStat.isFile()) {
40459
40726
  const errorResult = {
40460
40727
  error: "target must be a file, not a directory",
@@ -40465,7 +40732,7 @@ var imports = tool({
40465
40732
  };
40466
40733
  return JSON.stringify(errorResult, null, 2);
40467
40734
  }
40468
- const baseDir = path23.dirname(targetFile);
40735
+ const baseDir = path22.dirname(targetFile);
40469
40736
  const scanStats = {
40470
40737
  skippedDirs: [],
40471
40738
  skippedFiles: 0,
@@ -40480,12 +40747,12 @@ var imports = tool({
40480
40747
  if (consumers.length >= MAX_CONSUMERS)
40481
40748
  break;
40482
40749
  try {
40483
- const stat = fs17.statSync(filePath);
40750
+ const stat = fs16.statSync(filePath);
40484
40751
  if (stat.size > MAX_FILE_SIZE_BYTES4) {
40485
40752
  skippedFileCount++;
40486
40753
  continue;
40487
40754
  }
40488
- const buffer = fs17.readFileSync(filePath);
40755
+ const buffer = fs16.readFileSync(filePath);
40489
40756
  if (isBinaryFile2(filePath, buffer)) {
40490
40757
  skippedFileCount++;
40491
40758
  continue;
@@ -40554,7 +40821,8 @@ init_lint();
40554
40821
 
40555
40822
  // src/tools/phase-complete.ts
40556
40823
  init_tool();
40557
- import * as fs18 from "fs";
40824
+ import * as fs17 from "fs";
40825
+ init_manager();
40558
40826
  init_utils2();
40559
40827
  function getDelegationsSince(sessionID, sinceTimestamp) {
40560
40828
  const chain = swarmState.delegationChains.get(sessionID);
@@ -40576,7 +40844,10 @@ function normalizeAgentsFromDelegations(delegations) {
40576
40844
  }
40577
40845
  return agents;
40578
40846
  }
40579
- async function executePhaseComplete(args2) {
40847
+ function isValidRetroEntry(entry, phase) {
40848
+ return entry.type === "retrospective" && "phase_number" in entry && entry.phase_number === phase && "verdict" in entry && entry.verdict === "pass";
40849
+ }
40850
+ async function executePhaseComplete(args2, workingDirectory) {
40580
40851
  const phase = Number(args2.phase);
40581
40852
  const summary = args2.summary;
40582
40853
  const sessionID = args2.sessionID;
@@ -40607,8 +40878,8 @@ async function executePhaseComplete(args2) {
40607
40878
  const trackedAgents = session.phaseAgentsDispatched ?? new Set;
40608
40879
  const allAgents = new Set([...delegationAgents, ...trackedAgents]);
40609
40880
  const agentsDispatched = Array.from(allAgents).sort();
40610
- const directory = process.cwd();
40611
- const { config: config3 } = loadPluginConfigWithMeta(directory);
40881
+ const dir = workingDirectory ?? process.cwd();
40882
+ const { config: config3 } = loadPluginConfigWithMeta(dir);
40612
40883
  let phaseCompleteConfig;
40613
40884
  try {
40614
40885
  phaseCompleteConfig = PhaseCompleteConfigSchema.parse(config3.phase_complete ?? {});
@@ -40634,6 +40905,37 @@ async function executePhaseComplete(args2) {
40634
40905
  warnings: []
40635
40906
  }, null, 2);
40636
40907
  }
40908
+ const retroBundle = await loadEvidence(dir, `retro-${phase}`);
40909
+ let retroFound = false;
40910
+ if (retroBundle !== null) {
40911
+ retroFound = retroBundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
40912
+ }
40913
+ if (!retroFound) {
40914
+ const allTaskIds = await listEvidenceTaskIds(dir);
40915
+ const retroTaskIds = allTaskIds.filter((id) => id.startsWith("retro-"));
40916
+ for (const taskId of retroTaskIds) {
40917
+ const bundle = await loadEvidence(dir, taskId);
40918
+ if (bundle === null)
40919
+ continue;
40920
+ retroFound = bundle.entries?.some((entry) => isValidRetroEntry(entry, phase)) ?? false;
40921
+ if (retroFound)
40922
+ break;
40923
+ }
40924
+ }
40925
+ if (!retroFound) {
40926
+ return JSON.stringify({
40927
+ success: false,
40928
+ phase,
40929
+ status: "blocked",
40930
+ reason: "RETROSPECTIVE_MISSING",
40931
+ 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.`,
40932
+ agentsDispatched: [],
40933
+ agentsMissing: [],
40934
+ warnings: [
40935
+ `Retrospective missing for phase ${phase}. Write a retro bundle with verdict='pass' at .swarm/evidence/retro-${phase}/evidence.json`
40936
+ ]
40937
+ }, null, 2);
40938
+ }
40637
40939
  const effectiveRequired = [...phaseCompleteConfig.required_agents];
40638
40940
  if (phaseCompleteConfig.require_docs && !effectiveRequired.includes("docs")) {
40639
40941
  effectiveRequired.push("docs");
@@ -40666,8 +40968,8 @@ async function executePhaseComplete(args2) {
40666
40968
  summary: safeSummary ?? null
40667
40969
  };
40668
40970
  try {
40669
- const eventsPath = validateSwarmPath(directory, "events.jsonl");
40670
- fs18.appendFileSync(eventsPath, `${JSON.stringify(event)}
40971
+ const eventsPath = validateSwarmPath(dir, "events.jsonl");
40972
+ fs17.appendFileSync(eventsPath, `${JSON.stringify(event)}
40671
40973
  `, "utf-8");
40672
40974
  } catch (writeError) {
40673
40975
  warnings.push(`Warning: failed to write phase complete event: ${writeError instanceof Error ? writeError.message : String(writeError)}`);
@@ -40717,8 +41019,8 @@ var phase_complete = tool({
40717
41019
  });
40718
41020
  // src/tools/pkg-audit.ts
40719
41021
  init_dist();
40720
- import * as fs19 from "fs";
40721
- import * as path24 from "path";
41022
+ import * as fs18 from "fs";
41023
+ import * as path23 from "path";
40722
41024
  var MAX_OUTPUT_BYTES5 = 52428800;
40723
41025
  var AUDIT_TIMEOUT_MS = 120000;
40724
41026
  function isValidEcosystem(value) {
@@ -40736,13 +41038,13 @@ function validateArgs3(args2) {
40736
41038
  function detectEcosystems() {
40737
41039
  const ecosystems = [];
40738
41040
  const cwd = process.cwd();
40739
- if (fs19.existsSync(path24.join(cwd, "package.json"))) {
41041
+ if (fs18.existsSync(path23.join(cwd, "package.json"))) {
40740
41042
  ecosystems.push("npm");
40741
41043
  }
40742
- if (fs19.existsSync(path24.join(cwd, "pyproject.toml")) || fs19.existsSync(path24.join(cwd, "requirements.txt"))) {
41044
+ if (fs18.existsSync(path23.join(cwd, "pyproject.toml")) || fs18.existsSync(path23.join(cwd, "requirements.txt"))) {
40743
41045
  ecosystems.push("pip");
40744
41046
  }
40745
- if (fs19.existsSync(path24.join(cwd, "Cargo.toml"))) {
41047
+ if (fs18.existsSync(path23.join(cwd, "Cargo.toml"))) {
40746
41048
  ecosystems.push("cargo");
40747
41049
  }
40748
41050
  return ecosystems;
@@ -42650,11 +42952,11 @@ var Module2 = (() => {
42650
42952
  throw toThrow;
42651
42953
  }, "quit_");
42652
42954
  var scriptDirectory = "";
42653
- function locateFile(path25) {
42955
+ function locateFile(path24) {
42654
42956
  if (Module["locateFile"]) {
42655
- return Module["locateFile"](path25, scriptDirectory);
42957
+ return Module["locateFile"](path24, scriptDirectory);
42656
42958
  }
42657
- return scriptDirectory + path25;
42959
+ return scriptDirectory + path24;
42658
42960
  }
42659
42961
  __name(locateFile, "locateFile");
42660
42962
  var readAsync, readBinary;
@@ -44468,7 +44770,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
44468
44770
  ]);
44469
44771
  // src/tools/pre-check-batch.ts
44470
44772
  init_dist();
44471
- import * as path27 from "path";
44773
+ import * as path26 from "path";
44472
44774
 
44473
44775
  // node_modules/yocto-queue/index.js
44474
44776
  class Node2 {
@@ -44634,8 +44936,8 @@ init_lint();
44634
44936
  init_manager();
44635
44937
 
44636
44938
  // src/quality/metrics.ts
44637
- import * as fs20 from "fs";
44638
- import * as path25 from "path";
44939
+ import * as fs19 from "fs";
44940
+ import * as path24 from "path";
44639
44941
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
44640
44942
  var MIN_DUPLICATION_LINES = 10;
44641
44943
  function estimateCyclomaticComplexity(content) {
@@ -44673,11 +44975,11 @@ function estimateCyclomaticComplexity(content) {
44673
44975
  }
44674
44976
  function getComplexityForFile2(filePath) {
44675
44977
  try {
44676
- const stat = fs20.statSync(filePath);
44978
+ const stat = fs19.statSync(filePath);
44677
44979
  if (stat.size > MAX_FILE_SIZE_BYTES5) {
44678
44980
  return null;
44679
44981
  }
44680
- const content = fs20.readFileSync(filePath, "utf-8");
44982
+ const content = fs19.readFileSync(filePath, "utf-8");
44681
44983
  return estimateCyclomaticComplexity(content);
44682
44984
  } catch {
44683
44985
  return null;
@@ -44687,8 +44989,8 @@ async function computeComplexityDelta(files, workingDir) {
44687
44989
  let totalComplexity = 0;
44688
44990
  const analyzedFiles = [];
44689
44991
  for (const file3 of files) {
44690
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44691
- if (!fs20.existsSync(fullPath)) {
44992
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
44993
+ if (!fs19.existsSync(fullPath)) {
44692
44994
  continue;
44693
44995
  }
44694
44996
  const complexity = getComplexityForFile2(fullPath);
@@ -44809,8 +45111,8 @@ function countGoExports(content) {
44809
45111
  }
44810
45112
  function getExportCountForFile(filePath) {
44811
45113
  try {
44812
- const content = fs20.readFileSync(filePath, "utf-8");
44813
- const ext = path25.extname(filePath).toLowerCase();
45114
+ const content = fs19.readFileSync(filePath, "utf-8");
45115
+ const ext = path24.extname(filePath).toLowerCase();
44814
45116
  switch (ext) {
44815
45117
  case ".ts":
44816
45118
  case ".tsx":
@@ -44836,8 +45138,8 @@ async function computePublicApiDelta(files, workingDir) {
44836
45138
  let totalExports = 0;
44837
45139
  const analyzedFiles = [];
44838
45140
  for (const file3 of files) {
44839
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44840
- if (!fs20.existsSync(fullPath)) {
45141
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
45142
+ if (!fs19.existsSync(fullPath)) {
44841
45143
  continue;
44842
45144
  }
44843
45145
  const exports = getExportCountForFile(fullPath);
@@ -44870,16 +45172,16 @@ async function computeDuplicationRatio(files, workingDir) {
44870
45172
  let duplicateLines = 0;
44871
45173
  const analyzedFiles = [];
44872
45174
  for (const file3 of files) {
44873
- const fullPath = path25.isAbsolute(file3) ? file3 : path25.join(workingDir, file3);
44874
- if (!fs20.existsSync(fullPath)) {
45175
+ const fullPath = path24.isAbsolute(file3) ? file3 : path24.join(workingDir, file3);
45176
+ if (!fs19.existsSync(fullPath)) {
44875
45177
  continue;
44876
45178
  }
44877
45179
  try {
44878
- const stat = fs20.statSync(fullPath);
45180
+ const stat = fs19.statSync(fullPath);
44879
45181
  if (stat.size > MAX_FILE_SIZE_BYTES5) {
44880
45182
  continue;
44881
45183
  }
44882
- const content = fs20.readFileSync(fullPath, "utf-8");
45184
+ const content = fs19.readFileSync(fullPath, "utf-8");
44883
45185
  const lines = content.split(`
44884
45186
  `).filter((line) => line.trim().length > 0);
44885
45187
  if (lines.length < MIN_DUPLICATION_LINES) {
@@ -44903,8 +45205,8 @@ function countCodeLines(content) {
44903
45205
  return lines.length;
44904
45206
  }
44905
45207
  function isTestFile(filePath) {
44906
- const basename5 = path25.basename(filePath);
44907
- const _ext = path25.extname(filePath).toLowerCase();
45208
+ const basename5 = path24.basename(filePath);
45209
+ const _ext = path24.extname(filePath).toLowerCase();
44908
45210
  const testPatterns = [
44909
45211
  ".test.",
44910
45212
  ".spec.",
@@ -44946,31 +45248,31 @@ function shouldExcludeFile(filePath, excludeGlobs) {
44946
45248
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
44947
45249
  let testLines = 0;
44948
45250
  let codeLines = 0;
44949
- const srcDir = path25.join(workingDir, "src");
44950
- if (fs20.existsSync(srcDir)) {
45251
+ const srcDir = path24.join(workingDir, "src");
45252
+ if (fs19.existsSync(srcDir)) {
44951
45253
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
44952
45254
  codeLines += lines;
44953
45255
  });
44954
45256
  }
44955
45257
  const possibleSrcDirs = ["lib", "app", "source", "core"];
44956
45258
  for (const dir of possibleSrcDirs) {
44957
- const dirPath = path25.join(workingDir, dir);
44958
- if (fs20.existsSync(dirPath)) {
45259
+ const dirPath = path24.join(workingDir, dir);
45260
+ if (fs19.existsSync(dirPath)) {
44959
45261
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
44960
45262
  codeLines += lines;
44961
45263
  });
44962
45264
  }
44963
45265
  }
44964
- const testsDir = path25.join(workingDir, "tests");
44965
- if (fs20.existsSync(testsDir)) {
45266
+ const testsDir = path24.join(workingDir, "tests");
45267
+ if (fs19.existsSync(testsDir)) {
44966
45268
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
44967
45269
  testLines += lines;
44968
45270
  });
44969
45271
  }
44970
45272
  const possibleTestDirs = ["test", "__tests__", "specs"];
44971
45273
  for (const dir of possibleTestDirs) {
44972
- const dirPath = path25.join(workingDir, dir);
44973
- if (fs20.existsSync(dirPath) && dirPath !== testsDir) {
45274
+ const dirPath = path24.join(workingDir, dir);
45275
+ if (fs19.existsSync(dirPath) && dirPath !== testsDir) {
44974
45276
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
44975
45277
  testLines += lines;
44976
45278
  });
@@ -44982,9 +45284,9 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
44982
45284
  }
44983
45285
  async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTestScan, callback) {
44984
45286
  try {
44985
- const entries = fs20.readdirSync(dirPath, { withFileTypes: true });
45287
+ const entries = fs19.readdirSync(dirPath, { withFileTypes: true });
44986
45288
  for (const entry of entries) {
44987
- const fullPath = path25.join(dirPath, entry.name);
45289
+ const fullPath = path24.join(dirPath, entry.name);
44988
45290
  if (entry.isDirectory()) {
44989
45291
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
44990
45292
  continue;
@@ -44992,7 +45294,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
44992
45294
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
44993
45295
  } else if (entry.isFile()) {
44994
45296
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
44995
- const ext = path25.extname(entry.name).toLowerCase();
45297
+ const ext = path24.extname(entry.name).toLowerCase();
44996
45298
  const validExts = [
44997
45299
  ".ts",
44998
45300
  ".tsx",
@@ -45030,7 +45332,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
45030
45332
  continue;
45031
45333
  }
45032
45334
  try {
45033
- const content = fs20.readFileSync(fullPath, "utf-8");
45335
+ const content = fs19.readFileSync(fullPath, "utf-8");
45034
45336
  const lines = countCodeLines(content);
45035
45337
  callback(lines);
45036
45338
  } catch {}
@@ -45246,8 +45548,8 @@ async function qualityBudget(input, directory) {
45246
45548
 
45247
45549
  // src/tools/sast-scan.ts
45248
45550
  init_manager();
45249
- import * as fs21 from "fs";
45250
- import * as path26 from "path";
45551
+ import * as fs20 from "fs";
45552
+ import * as path25 from "path";
45251
45553
  import { extname as extname7 } from "path";
45252
45554
 
45253
45555
  // src/sast/rules/c.ts
@@ -46110,17 +46412,17 @@ var SEVERITY_ORDER = {
46110
46412
  };
46111
46413
  function shouldSkipFile(filePath) {
46112
46414
  try {
46113
- const stats = fs21.statSync(filePath);
46415
+ const stats = fs20.statSync(filePath);
46114
46416
  if (stats.size > MAX_FILE_SIZE_BYTES6) {
46115
46417
  return { skip: true, reason: "file too large" };
46116
46418
  }
46117
46419
  if (stats.size === 0) {
46118
46420
  return { skip: true, reason: "empty file" };
46119
46421
  }
46120
- const fd = fs21.openSync(filePath, "r");
46422
+ const fd = fs20.openSync(filePath, "r");
46121
46423
  const buffer = Buffer.alloc(8192);
46122
- const bytesRead = fs21.readSync(fd, buffer, 0, 8192, 0);
46123
- fs21.closeSync(fd);
46424
+ const bytesRead = fs20.readSync(fd, buffer, 0, 8192, 0);
46425
+ fs20.closeSync(fd);
46124
46426
  if (bytesRead > 0) {
46125
46427
  let nullCount = 0;
46126
46428
  for (let i2 = 0;i2 < bytesRead; i2++) {
@@ -46159,7 +46461,7 @@ function countBySeverity(findings) {
46159
46461
  }
46160
46462
  function scanFileWithTierA(filePath, language) {
46161
46463
  try {
46162
- const content = fs21.readFileSync(filePath, "utf-8");
46464
+ const content = fs20.readFileSync(filePath, "utf-8");
46163
46465
  const findings = executeRulesSync(filePath, content, language);
46164
46466
  return findings.map((f) => ({
46165
46467
  rule_id: f.rule_id,
@@ -46202,8 +46504,8 @@ async function sastScan(input, directory, config3) {
46202
46504
  const engine = semgrepAvailable ? "tier_a+tier_b" : "tier_a";
46203
46505
  const filesByLanguage = new Map;
46204
46506
  for (const filePath of changed_files) {
46205
- const resolvedPath = path26.isAbsolute(filePath) ? filePath : path26.resolve(directory, filePath);
46206
- if (!fs21.existsSync(resolvedPath)) {
46507
+ const resolvedPath = path25.isAbsolute(filePath) ? filePath : path25.resolve(directory, filePath);
46508
+ if (!fs20.existsSync(resolvedPath)) {
46207
46509
  _filesSkipped++;
46208
46510
  continue;
46209
46511
  }
@@ -46311,10 +46613,10 @@ function validatePath(inputPath, baseDir) {
46311
46613
  if (!inputPath || inputPath.length === 0) {
46312
46614
  return "path is required";
46313
46615
  }
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)) {
46616
+ const resolved = path26.resolve(baseDir, inputPath);
46617
+ const baseResolved = path26.resolve(baseDir);
46618
+ const relative3 = path26.relative(baseResolved, resolved);
46619
+ if (relative3.startsWith("..") || path26.isAbsolute(relative3)) {
46318
46620
  return "path traversal detected";
46319
46621
  }
46320
46622
  return null;
@@ -46436,7 +46738,7 @@ async function runPreCheckBatch(input) {
46436
46738
  warn(`pre_check_batch: Invalid file path: ${file3}`);
46437
46739
  continue;
46438
46740
  }
46439
- changedFiles.push(path27.resolve(directory, file3));
46741
+ changedFiles.push(path26.resolve(directory, file3));
46440
46742
  }
46441
46743
  } else {
46442
46744
  changedFiles = [];
@@ -46626,8 +46928,8 @@ var retrieve_summary = tool({
46626
46928
  // src/tools/sbom-generate.ts
46627
46929
  init_dist();
46628
46930
  init_manager();
46629
- import * as fs22 from "fs";
46630
- import * as path28 from "path";
46931
+ import * as fs21 from "fs";
46932
+ import * as path27 from "path";
46631
46933
 
46632
46934
  // src/sbom/detectors/dart.ts
46633
46935
  function parsePubspecLock(content) {
@@ -47472,9 +47774,9 @@ function findManifestFiles(rootDir) {
47472
47774
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
47473
47775
  function searchDir(dir) {
47474
47776
  try {
47475
- const entries = fs22.readdirSync(dir, { withFileTypes: true });
47777
+ const entries = fs21.readdirSync(dir, { withFileTypes: true });
47476
47778
  for (const entry of entries) {
47477
- const fullPath = path28.join(dir, entry.name);
47779
+ const fullPath = path27.join(dir, entry.name);
47478
47780
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
47479
47781
  continue;
47480
47782
  }
@@ -47484,7 +47786,7 @@ function findManifestFiles(rootDir) {
47484
47786
  for (const pattern of patterns) {
47485
47787
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
47486
47788
  if (new RegExp(regex, "i").test(entry.name)) {
47487
- manifestFiles.push(path28.relative(cwd, fullPath));
47789
+ manifestFiles.push(path27.relative(cwd, fullPath));
47488
47790
  break;
47489
47791
  }
47490
47792
  }
@@ -47501,14 +47803,14 @@ function findManifestFilesInDirs(directories, workingDir) {
47501
47803
  const patterns = [...new Set(allDetectors.flatMap((d) => d.patterns))];
47502
47804
  for (const dir of directories) {
47503
47805
  try {
47504
- const entries = fs22.readdirSync(dir, { withFileTypes: true });
47806
+ const entries = fs21.readdirSync(dir, { withFileTypes: true });
47505
47807
  for (const entry of entries) {
47506
- const fullPath = path28.join(dir, entry.name);
47808
+ const fullPath = path27.join(dir, entry.name);
47507
47809
  if (entry.isFile()) {
47508
47810
  for (const pattern of patterns) {
47509
47811
  const regex = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*").replace(/\?/g, ".");
47510
47812
  if (new RegExp(regex, "i").test(entry.name)) {
47511
- found.push(path28.relative(workingDir, fullPath));
47813
+ found.push(path27.relative(workingDir, fullPath));
47512
47814
  break;
47513
47815
  }
47514
47816
  }
@@ -47521,11 +47823,11 @@ function findManifestFilesInDirs(directories, workingDir) {
47521
47823
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
47522
47824
  const dirs = new Set;
47523
47825
  for (const file3 of changedFiles) {
47524
- let currentDir = path28.dirname(file3);
47826
+ let currentDir = path27.dirname(file3);
47525
47827
  while (true) {
47526
- if (currentDir && currentDir !== "." && currentDir !== path28.sep) {
47527
- dirs.add(path28.join(workingDir, currentDir));
47528
- const parent = path28.dirname(currentDir);
47828
+ if (currentDir && currentDir !== "." && currentDir !== path27.sep) {
47829
+ dirs.add(path27.join(workingDir, currentDir));
47830
+ const parent = path27.dirname(currentDir);
47529
47831
  if (parent === currentDir)
47530
47832
  break;
47531
47833
  currentDir = parent;
@@ -47539,7 +47841,7 @@ function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
47539
47841
  }
47540
47842
  function ensureOutputDir(outputDir) {
47541
47843
  try {
47542
- fs22.mkdirSync(outputDir, { recursive: true });
47844
+ fs21.mkdirSync(outputDir, { recursive: true });
47543
47845
  } catch (error93) {
47544
47846
  if (!error93 || error93.code !== "EEXIST") {
47545
47847
  throw error93;
@@ -47632,11 +47934,11 @@ var sbom_generate = tool({
47632
47934
  const processedFiles = [];
47633
47935
  for (const manifestFile of manifestFiles) {
47634
47936
  try {
47635
- const fullPath = path28.isAbsolute(manifestFile) ? manifestFile : path28.join(workingDir, manifestFile);
47636
- if (!fs22.existsSync(fullPath)) {
47937
+ const fullPath = path27.isAbsolute(manifestFile) ? manifestFile : path27.join(workingDir, manifestFile);
47938
+ if (!fs21.existsSync(fullPath)) {
47637
47939
  continue;
47638
47940
  }
47639
- const content = fs22.readFileSync(fullPath, "utf-8");
47941
+ const content = fs21.readFileSync(fullPath, "utf-8");
47640
47942
  const components = detectComponents(manifestFile, content);
47641
47943
  processedFiles.push(manifestFile);
47642
47944
  if (components.length > 0) {
@@ -47649,8 +47951,8 @@ var sbom_generate = tool({
47649
47951
  const bom = generateCycloneDX(allComponents);
47650
47952
  const bomJson = serializeCycloneDX(bom);
47651
47953
  const filename = generateSbomFilename();
47652
- const outputPath = path28.join(outputDir, filename);
47653
- fs22.writeFileSync(outputPath, bomJson, "utf-8");
47954
+ const outputPath = path27.join(outputDir, filename);
47955
+ fs21.writeFileSync(outputPath, bomJson, "utf-8");
47654
47956
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
47655
47957
  try {
47656
47958
  const timestamp = new Date().toISOString();
@@ -47691,8 +47993,8 @@ var sbom_generate = tool({
47691
47993
  });
47692
47994
  // src/tools/schema-drift.ts
47693
47995
  init_dist();
47694
- import * as fs23 from "fs";
47695
- import * as path29 from "path";
47996
+ import * as fs22 from "fs";
47997
+ import * as path28 from "path";
47696
47998
  var SPEC_CANDIDATES = [
47697
47999
  "openapi.json",
47698
48000
  "openapi.yaml",
@@ -47724,28 +48026,28 @@ function normalizePath(p) {
47724
48026
  }
47725
48027
  function discoverSpecFile(cwd, specFileArg) {
47726
48028
  if (specFileArg) {
47727
- const resolvedPath = path29.resolve(cwd, specFileArg);
47728
- const normalizedCwd = cwd.endsWith(path29.sep) ? cwd : cwd + path29.sep;
48029
+ const resolvedPath = path28.resolve(cwd, specFileArg);
48030
+ const normalizedCwd = cwd.endsWith(path28.sep) ? cwd : cwd + path28.sep;
47729
48031
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
47730
48032
  throw new Error("Invalid spec_file: path traversal detected");
47731
48033
  }
47732
- const ext = path29.extname(resolvedPath).toLowerCase();
48034
+ const ext = path28.extname(resolvedPath).toLowerCase();
47733
48035
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
47734
48036
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
47735
48037
  }
47736
- const stats = fs23.statSync(resolvedPath);
48038
+ const stats = fs22.statSync(resolvedPath);
47737
48039
  if (stats.size > MAX_SPEC_SIZE) {
47738
48040
  throw new Error(`Invalid spec_file: file exceeds ${MAX_SPEC_SIZE / 1024 / 1024}MB limit`);
47739
48041
  }
47740
- if (!fs23.existsSync(resolvedPath)) {
48042
+ if (!fs22.existsSync(resolvedPath)) {
47741
48043
  throw new Error(`Spec file not found: ${resolvedPath}`);
47742
48044
  }
47743
48045
  return resolvedPath;
47744
48046
  }
47745
48047
  for (const candidate of SPEC_CANDIDATES) {
47746
- const candidatePath = path29.resolve(cwd, candidate);
47747
- if (fs23.existsSync(candidatePath)) {
47748
- const stats = fs23.statSync(candidatePath);
48048
+ const candidatePath = path28.resolve(cwd, candidate);
48049
+ if (fs22.existsSync(candidatePath)) {
48050
+ const stats = fs22.statSync(candidatePath);
47749
48051
  if (stats.size <= MAX_SPEC_SIZE) {
47750
48052
  return candidatePath;
47751
48053
  }
@@ -47754,8 +48056,8 @@ function discoverSpecFile(cwd, specFileArg) {
47754
48056
  return null;
47755
48057
  }
47756
48058
  function parseSpec(specFile) {
47757
- const content = fs23.readFileSync(specFile, "utf-8");
47758
- const ext = path29.extname(specFile).toLowerCase();
48059
+ const content = fs22.readFileSync(specFile, "utf-8");
48060
+ const ext = path28.extname(specFile).toLowerCase();
47759
48061
  if (ext === ".json") {
47760
48062
  return parseJsonSpec(content);
47761
48063
  }
@@ -47821,12 +48123,12 @@ function extractRoutes(cwd) {
47821
48123
  function walkDir(dir) {
47822
48124
  let entries;
47823
48125
  try {
47824
- entries = fs23.readdirSync(dir, { withFileTypes: true });
48126
+ entries = fs22.readdirSync(dir, { withFileTypes: true });
47825
48127
  } catch {
47826
48128
  return;
47827
48129
  }
47828
48130
  for (const entry of entries) {
47829
- const fullPath = path29.join(dir, entry.name);
48131
+ const fullPath = path28.join(dir, entry.name);
47830
48132
  if (entry.isSymbolicLink()) {
47831
48133
  continue;
47832
48134
  }
@@ -47836,7 +48138,7 @@ function extractRoutes(cwd) {
47836
48138
  }
47837
48139
  walkDir(fullPath);
47838
48140
  } else if (entry.isFile()) {
47839
- const ext = path29.extname(entry.name).toLowerCase();
48141
+ const ext = path28.extname(entry.name).toLowerCase();
47840
48142
  const baseName = entry.name.toLowerCase();
47841
48143
  if (![".ts", ".js", ".mjs"].includes(ext)) {
47842
48144
  continue;
@@ -47854,7 +48156,7 @@ function extractRoutes(cwd) {
47854
48156
  }
47855
48157
  function extractRoutesFromFile(filePath) {
47856
48158
  const routes = [];
47857
- const content = fs23.readFileSync(filePath, "utf-8");
48159
+ const content = fs22.readFileSync(filePath, "utf-8");
47858
48160
  const lines = content.split(/\r?\n/);
47859
48161
  const expressRegex = /(?:app|router|server|express)\.(get|post|put|patch|delete|options|head)\s*\(\s*['"`]([^'"`]+)['"`]/g;
47860
48162
  const flaskRegex = /@(?:app|blueprint|bp)\.route\s*\(\s*['"]([^'"]+)['"]/g;
@@ -48004,8 +48306,8 @@ init_secretscan();
48004
48306
 
48005
48307
  // src/tools/symbols.ts
48006
48308
  init_tool();
48007
- import * as fs24 from "fs";
48008
- import * as path30 from "path";
48309
+ import * as fs23 from "fs";
48310
+ import * as path29 from "path";
48009
48311
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
48010
48312
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
48011
48313
  function containsControlCharacters(str) {
@@ -48034,11 +48336,11 @@ function containsWindowsAttacks(str) {
48034
48336
  }
48035
48337
  function isPathInWorkspace(filePath, workspace) {
48036
48338
  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)) {
48339
+ const resolvedPath = path29.resolve(workspace, filePath);
48340
+ const realWorkspace = fs23.realpathSync(workspace);
48341
+ const realResolvedPath = fs23.realpathSync(resolvedPath);
48342
+ const relativePath = path29.relative(realWorkspace, realResolvedPath);
48343
+ if (relativePath.startsWith("..") || path29.isAbsolute(relativePath)) {
48042
48344
  return false;
48043
48345
  }
48044
48346
  return true;
@@ -48050,17 +48352,17 @@ function validatePathForRead(filePath, workspace) {
48050
48352
  return isPathInWorkspace(filePath, workspace);
48051
48353
  }
48052
48354
  function extractTSSymbols(filePath, cwd) {
48053
- const fullPath = path30.join(cwd, filePath);
48355
+ const fullPath = path29.join(cwd, filePath);
48054
48356
  if (!validatePathForRead(fullPath, cwd)) {
48055
48357
  return [];
48056
48358
  }
48057
48359
  let content;
48058
48360
  try {
48059
- const stats = fs24.statSync(fullPath);
48361
+ const stats = fs23.statSync(fullPath);
48060
48362
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
48061
48363
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
48062
48364
  }
48063
- content = fs24.readFileSync(fullPath, "utf-8");
48365
+ content = fs23.readFileSync(fullPath, "utf-8");
48064
48366
  } catch {
48065
48367
  return [];
48066
48368
  }
@@ -48202,17 +48504,17 @@ function extractTSSymbols(filePath, cwd) {
48202
48504
  });
48203
48505
  }
48204
48506
  function extractPythonSymbols(filePath, cwd) {
48205
- const fullPath = path30.join(cwd, filePath);
48507
+ const fullPath = path29.join(cwd, filePath);
48206
48508
  if (!validatePathForRead(fullPath, cwd)) {
48207
48509
  return [];
48208
48510
  }
48209
48511
  let content;
48210
48512
  try {
48211
- const stats = fs24.statSync(fullPath);
48513
+ const stats = fs23.statSync(fullPath);
48212
48514
  if (stats.size > MAX_FILE_SIZE_BYTES7) {
48213
48515
  throw new Error(`File too large: ${stats.size} bytes (max: ${MAX_FILE_SIZE_BYTES7})`);
48214
48516
  }
48215
- content = fs24.readFileSync(fullPath, "utf-8");
48517
+ content = fs23.readFileSync(fullPath, "utf-8");
48216
48518
  } catch {
48217
48519
  return [];
48218
48520
  }
@@ -48284,7 +48586,7 @@ var symbols = tool({
48284
48586
  }, null, 2);
48285
48587
  }
48286
48588
  const cwd = process.cwd();
48287
- const ext = path30.extname(file3);
48589
+ const ext = path29.extname(file3);
48288
48590
  if (containsControlCharacters(file3)) {
48289
48591
  return JSON.stringify({
48290
48592
  file: file3,
@@ -48352,8 +48654,8 @@ init_test_runner();
48352
48654
 
48353
48655
  // src/tools/todo-extract.ts
48354
48656
  init_dist();
48355
- import * as fs25 from "fs";
48356
- import * as path31 from "path";
48657
+ import * as fs24 from "fs";
48658
+ import * as path30 from "path";
48357
48659
  var MAX_TEXT_LENGTH = 200;
48358
48660
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
48359
48661
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -48424,9 +48726,9 @@ function validatePathsInput(paths, cwd) {
48424
48726
  return { error: "paths contains path traversal", resolvedPath: null };
48425
48727
  }
48426
48728
  try {
48427
- const resolvedPath = path31.resolve(paths);
48428
- const normalizedCwd = path31.resolve(cwd);
48429
- const normalizedResolved = path31.resolve(resolvedPath);
48729
+ const resolvedPath = path30.resolve(paths);
48730
+ const normalizedCwd = path30.resolve(cwd);
48731
+ const normalizedResolved = path30.resolve(resolvedPath);
48430
48732
  if (!normalizedResolved.startsWith(normalizedCwd)) {
48431
48733
  return {
48432
48734
  error: "paths must be within the current working directory",
@@ -48442,13 +48744,13 @@ function validatePathsInput(paths, cwd) {
48442
48744
  }
48443
48745
  }
48444
48746
  function isSupportedExtension(filePath) {
48445
- const ext = path31.extname(filePath).toLowerCase();
48747
+ const ext = path30.extname(filePath).toLowerCase();
48446
48748
  return SUPPORTED_EXTENSIONS2.has(ext);
48447
48749
  }
48448
48750
  function findSourceFiles3(dir, files = []) {
48449
48751
  let entries;
48450
48752
  try {
48451
- entries = fs25.readdirSync(dir);
48753
+ entries = fs24.readdirSync(dir);
48452
48754
  } catch {
48453
48755
  return files;
48454
48756
  }
@@ -48457,10 +48759,10 @@ function findSourceFiles3(dir, files = []) {
48457
48759
  if (SKIP_DIRECTORIES3.has(entry)) {
48458
48760
  continue;
48459
48761
  }
48460
- const fullPath = path31.join(dir, entry);
48762
+ const fullPath = path30.join(dir, entry);
48461
48763
  let stat;
48462
48764
  try {
48463
- stat = fs25.statSync(fullPath);
48765
+ stat = fs24.statSync(fullPath);
48464
48766
  } catch {
48465
48767
  continue;
48466
48768
  }
@@ -48553,7 +48855,7 @@ var todo_extract = tool({
48553
48855
  return JSON.stringify(errorResult, null, 2);
48554
48856
  }
48555
48857
  const scanPath = resolvedPath;
48556
- if (!fs25.existsSync(scanPath)) {
48858
+ if (!fs24.existsSync(scanPath)) {
48557
48859
  const errorResult = {
48558
48860
  error: `path not found: ${pathsInput}`,
48559
48861
  total: 0,
@@ -48563,13 +48865,13 @@ var todo_extract = tool({
48563
48865
  return JSON.stringify(errorResult, null, 2);
48564
48866
  }
48565
48867
  const filesToScan = [];
48566
- const stat = fs25.statSync(scanPath);
48868
+ const stat = fs24.statSync(scanPath);
48567
48869
  if (stat.isFile()) {
48568
48870
  if (isSupportedExtension(scanPath)) {
48569
48871
  filesToScan.push(scanPath);
48570
48872
  } else {
48571
48873
  const errorResult = {
48572
- error: `unsupported file extension: ${path31.extname(scanPath)}`,
48874
+ error: `unsupported file extension: ${path30.extname(scanPath)}`,
48573
48875
  total: 0,
48574
48876
  byPriority: { high: 0, medium: 0, low: 0 },
48575
48877
  entries: []
@@ -48582,11 +48884,11 @@ var todo_extract = tool({
48582
48884
  const allEntries = [];
48583
48885
  for (const filePath of filesToScan) {
48584
48886
  try {
48585
- const fileStat = fs25.statSync(filePath);
48887
+ const fileStat = fs24.statSync(filePath);
48586
48888
  if (fileStat.size > MAX_FILE_SIZE_BYTES8) {
48587
48889
  continue;
48588
48890
  }
48589
- const content = fs25.readFileSync(filePath, "utf-8");
48891
+ const content = fs24.readFileSync(filePath, "utf-8");
48590
48892
  const entries = parseTodoComments(content, filePath, tagsSet);
48591
48893
  allEntries.push(...entries);
48592
48894
  } catch {}
@@ -48684,7 +48986,7 @@ var OpenCodeSwarm = async (ctx) => {
48684
48986
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
48685
48987
  preflightTriggerManager = new PTM(automationConfig);
48686
48988
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
48687
- const swarmDir = path32.resolve(ctx.directory, ".swarm");
48989
+ const swarmDir = path31.resolve(ctx.directory, ".swarm");
48688
48990
  statusArtifact = new ASA(swarmDir);
48689
48991
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
48690
48992
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {