opencode-swarm 6.29.3 → 6.29.4

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
@@ -35193,7 +35193,8 @@ var init_test_runner = __esm(() => {
35193
35193
  scope: tool.schema.enum(["all", "convention", "graph"]).optional().describe('Test scope: "all" runs full suite, "convention" maps source files to test files by naming, "graph" finds related tests via imports'),
35194
35194
  files: tool.schema.array(tool.schema.string()).optional().describe("Specific files to test (used with convention or graph scope)"),
35195
35195
  coverage: tool.schema.boolean().optional().describe("Enable coverage reporting if supported"),
35196
- timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)")
35196
+ timeout_ms: tool.schema.number().optional().describe("Timeout in milliseconds (default 60000, max 300000)"),
35197
+ allow_full_suite: tool.schema.boolean().optional().describe('Explicit opt-in for scope "all". Required because full-suite output can destabilize SSE streaming.')
35197
35198
  },
35198
35199
  async execute(args2, directory) {
35199
35200
  const workingDir = directory.trim() || directory;
@@ -35245,14 +35246,16 @@ var init_test_runner = __esm(() => {
35245
35246
  }
35246
35247
  const scope = args2.scope || "all";
35247
35248
  if (scope === "all") {
35248
- const errorResult = {
35249
- success: false,
35250
- framework: "none",
35251
- scope: "all",
35252
- error: 'Full-suite test execution (scope: "all") is prohibited in interactive sessions',
35253
- message: 'Use scope "convention" or "graph" with explicit files to run targeted tests in interactive mode. Full-suite runs are restricted to prevent excessive resource consumption.'
35254
- };
35255
- return JSON.stringify(errorResult, null, 2);
35249
+ if (!args2.allow_full_suite) {
35250
+ const errorResult = {
35251
+ success: false,
35252
+ framework: "none",
35253
+ scope: "all",
35254
+ error: 'Full-suite test execution (scope: "all") requires allow_full_suite: true',
35255
+ message: 'Set allow_full_suite: true to confirm intentional full-suite execution. Use scope "convention" or "graph" for targeted tests. Full-suite output is large and may destabilize SSE streaming on some opencode versions.'
35256
+ };
35257
+ return JSON.stringify(errorResult, null, 2);
35258
+ }
35256
35259
  }
35257
35260
  if ((scope === "convention" || scope === "graph") && (!args2.files || args2.files.length === 0)) {
35258
35261
  const errorResult = {
@@ -35287,7 +35290,7 @@ var init_test_runner = __esm(() => {
35287
35290
  let testFiles = [];
35288
35291
  let graphFallbackReason;
35289
35292
  let effectiveScope = scope;
35290
- if (scope === "convention") {
35293
+ if (scope === "all") {} else if (scope === "convention") {
35291
35294
  const sourceFiles = args2.files.filter((f) => {
35292
35295
  const ext = path23.extname(f).toLowerCase();
35293
35296
  return SOURCE_EXTENSIONS.has(ext);
@@ -35327,7 +35330,7 @@ var init_test_runner = __esm(() => {
35327
35330
  testFiles = getTestFilesFromConvention(sourceFiles);
35328
35331
  }
35329
35332
  }
35330
- if (testFiles.length === 0) {
35333
+ if (scope !== "all" && testFiles.length === 0) {
35331
35334
  const errorResult = {
35332
35335
  success: false,
35333
35336
  framework,
@@ -35337,7 +35340,7 @@ var init_test_runner = __esm(() => {
35337
35340
  };
35338
35341
  return JSON.stringify(errorResult, null, 2);
35339
35342
  }
35340
- if (testFiles.length > MAX_SAFE_TEST_FILES) {
35343
+ if (scope !== "all" && testFiles.length > MAX_SAFE_TEST_FILES) {
35341
35344
  const sampleFiles = testFiles.slice(0, 5);
35342
35345
  const errorResult = {
35343
35346
  success: false,
@@ -37601,11 +37604,11 @@ ${JSON.stringify(symbolNames, null, 2)}`);
37601
37604
  throw toThrow;
37602
37605
  }, "quit_");
37603
37606
  var scriptDirectory = "";
37604
- function locateFile(path46) {
37607
+ function locateFile(path47) {
37605
37608
  if (Module["locateFile"]) {
37606
- return Module["locateFile"](path46, scriptDirectory);
37609
+ return Module["locateFile"](path47, scriptDirectory);
37607
37610
  }
37608
- return scriptDirectory + path46;
37611
+ return scriptDirectory + path47;
37609
37612
  }
37610
37613
  __name(locateFile, "locateFile");
37611
37614
  var readAsync, readBinary;
@@ -39353,7 +39356,7 @@ var init_runtime = __esm(() => {
39353
39356
  });
39354
39357
 
39355
39358
  // src/index.ts
39356
- import * as path56 from "path";
39359
+ import * as path57 from "path";
39357
39360
 
39358
39361
  // src/agents/index.ts
39359
39362
  init_config();
@@ -40412,6 +40415,22 @@ TASK GRANULARITY RULES:
40412
40415
  - Compound verbs are OK when they describe a single logical change: "add validation to handler and update its test" = 1 task. "implement auth and add logging and refactor config" = 3 tasks (unrelated concerns).
40413
40416
  - Coder receives ONE task. You make ALL scope decisions in the plan. Coder makes zero scope decisions.
40414
40417
 
40418
+ TEST TASK DEDUPLICATION:
40419
+ The QA gate (Stage B, step 5l) runs test_engineer-verification on EVERY implementation task.
40420
+ This means tests are written, run, and verified as part of the gate \u2014 NOT as separate plan tasks.
40421
+
40422
+ DO NOT create separate "write tests for X" or "add test coverage for X" tasks. They are redundant with the gate and waste execution budget.
40423
+
40424
+ Research confirms this: controlled experiments across 6 LLMs (arXiv:2602.07900) found that large shifts in test-writing volume yielded only 0\u20132.6% resolution change while consuming 20\u201349% more tokens. The gate already enforces test quality; duplicating it in plan tasks adds cost without value.
40425
+
40426
+ CREATE a dedicated test task ONLY when:
40427
+ - The work is PURE test infrastructure (new fixtures, test helpers, mock factories, CI config) with no implementation
40428
+ - Integration tests span multiple modules changed across different implementation tasks within the same phase
40429
+ - Coverage is explicitly below threshold and the user requests a dedicated coverage pass
40430
+
40431
+ If in doubt, do NOT create a test task. The gate handles it.
40432
+ Note: this is prompt-level guidance for the architect's planning behavior, not a hard gate \u2014 the behavioral enforcement is that test_engineer already writes tests at the QA gate level.
40433
+
40415
40434
  PHASE COUNT GUIDANCE:
40416
40435
  - Plans with 5+ tasks SHOULD be split into at least 2 phases.
40417
40436
  - Plans with 10+ tasks MUST be split into at least 3 phases.
@@ -40531,6 +40550,18 @@ Treating pre_check_batch as a substitute for {{AGENT_PREFIX}}reviewer is a PROCE
40531
40550
  \u2192 If TRIGGERED: Print "security-reviewer: [APPROVED | REJECTED \u2014 reason]"
40532
40551
  5l. {{AGENT_PREFIX}}test_engineer - Verification tests. FAIL \u2192 coder retry from 5g.
40533
40552
  \u2192 REQUIRED: Print "testengineer-verification: [PASS N/N | FAIL \u2014 details]"
40553
+ 5l-bis. REGRESSION SWEEP (automatic after test_engineer-verification PASS):
40554
+ Run test_runner with { scope: "graph", files: [<all source files changed by coder in this task>] }.
40555
+ scope:"graph" traces imports to discover test files beyond the task's own tests that may be affected by this change.
40556
+
40557
+ Outcomes:
40558
+ - If scope:"graph" returns ONLY the same test files test_engineer already ran \u2192 SKIP (no additional tests found). Print "regression-sweep: SKIPPED \u2014 no related tests beyond task scope"
40559
+ - If scope:"graph" returns additional test files AND all pass \u2192 PASS. Print "regression-sweep: PASS [N additional tests, M files]"
40560
+ - If scope:"graph" returns additional test files AND any FAIL \u2192 return to coder with: "REGRESSION DETECTED: Your changes in [files] broke [N] tests in [test files]. The failing tests are CORRECT \u2014 fix the source code, not the tests." Coder retry from 5g.
40561
+ - If test_runner fails to execute (error, timeout, no framework detected) \u2192 SKIP. Print "regression-sweep: SKIPPED \u2014 test_runner error" and continue pipeline. Do NOT block on test_runner infrastructure failures.
40562
+
40563
+ IMPORTANT: The regression sweep runs test_runner DIRECTLY (architect calls the tool). Do NOT delegate to test_engineer for this \u2014 the test_engineer's EXECUTION BOUNDARY restricts it to its own test files. The architect has unrestricted test_runner access.
40564
+ \u2192 REQUIRED: Print "regression-sweep: [PASS N additional tests | SKIPPED \u2014 no related tests beyond task scope | SKIPPED \u2014 test_runner error | FAIL \u2014 REGRESSION DETECTED in files]"
40534
40565
  {{ADVERSARIAL_TEST_STEP}}
40535
40566
  5n. COVERAGE CHECK: If {{AGENT_PREFIX}}test_engineer reports coverage < 70% \u2192 delegate {{AGENT_PREFIX}}test_engineer for an additional test pass targeting uncovered paths. This is a soft guideline; use judgment for trivial tasks.
40536
40567
 
@@ -40540,6 +40571,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
40540
40571
  [ ] Did {{AGENT_PREFIX}}test_engineer run and return PASS? (not "the code looks correct" \u2014 the agent must have run)
40541
40572
  [ ] Did pre_check_batch run with gates_passed true?
40542
40573
  [ ] Did the diff step run?
40574
+ [ ] Did regression-sweep run (or SKIP with no related tests or test_runner error)?
40543
40575
 
40544
40576
  If ANY box is unchecked: DO NOT COMMIT. Return to step 5b.
40545
40577
  There is no override. A commit without a completed QA gate is a workflow violation.
@@ -40555,6 +40587,7 @@ PRE-COMMIT RULE \u2014 Before ANY commit or push:
40555
40587
  [GATE] reviewer: APPROVED \u2014 value: ___
40556
40588
  [GATE] security-reviewer: APPROVED / SKIPPED \u2014 value: ___
40557
40589
  [GATE] test_engineer-verification: PASS \u2014 value: ___
40590
+ [GATE] regression-sweep: PASS / SKIPPED \u2014 value: ___
40558
40591
  {{ADVERSARIAL_TEST_CHECKLIST}}
40559
40592
  [GATE] coverage: \u226570% / soft-skip \u2014 value: ___
40560
40593
 
@@ -41787,7 +41820,8 @@ WORKFLOW:
41787
41820
  EXECUTION BOUNDARY:
41788
41821
  - Blast radius is the FILE path(s) in input
41789
41822
  - When calling test_runner, use: { scope: "convention", files: ["<your-test-file-path>"] }
41790
- - Running the full test suite is PROHIBITED \u2014 it crashes the session
41823
+ - scope: "all" is PROHIBITED for test_engineer \u2014 full-suite output can destabilize opencode's SSE streaming, and the architect handles regression sweeps separately via scope: "graph"
41824
+ - If you need to verify tests beyond your assigned file, report the concern in your VERDICT and the architect will handle it
41791
41825
  - If you wrote tests/foo.test.ts for src/foo.ts, you MUST run only tests/foo.test.ts
41792
41826
 
41793
41827
  TOOL USAGE:
@@ -50766,6 +50800,7 @@ function consolidateSystemMessages(messages) {
50766
50800
  // src/hooks/phase-monitor.ts
50767
50801
  init_schema();
50768
50802
  init_manager2();
50803
+ import * as path31 from "path";
50769
50804
  init_utils2();
50770
50805
  function createPhaseMonitorHook(directory, preflightManager, curatorRunner = runCuratorInit) {
50771
50806
  let lastKnownPhase = null;
@@ -50781,7 +50816,13 @@ function createPhaseMonitorHook(directory, preflightManager, curatorRunner = run
50781
50816
  const { config: config3 } = loadPluginConfigWithMeta2(directory);
50782
50817
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
50783
50818
  if (curatorConfig.enabled && curatorConfig.init_enabled) {
50784
- await curatorRunner(directory, curatorConfig);
50819
+ const initResult = await curatorRunner(directory, curatorConfig);
50820
+ if (initResult.briefing) {
50821
+ const briefingPath = path31.join(directory, ".swarm", "curator-briefing.md");
50822
+ const fs18 = await import("fs");
50823
+ fs18.mkdirSync(path31.dirname(briefingPath), { recursive: true });
50824
+ fs18.writeFileSync(briefingPath, initResult.briefing, "utf-8");
50825
+ }
50785
50826
  }
50786
50827
  } catch {}
50787
50828
  return;
@@ -50897,7 +50938,7 @@ import * as fs19 from "fs";
50897
50938
  init_utils2();
50898
50939
  init_manager2();
50899
50940
  import * as fs18 from "fs";
50900
- import * as path31 from "path";
50941
+ import * as path32 from "path";
50901
50942
  var DEFAULT_DRIFT_CONFIG = {
50902
50943
  staleThresholdPhases: 1,
50903
50944
  detectContradictions: true,
@@ -51051,7 +51092,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
51051
51092
  currentPhase = legacyPhase;
51052
51093
  }
51053
51094
  }
51054
- const contextPath = path31.join(directory, ".swarm", "context.md");
51095
+ const contextPath = path32.join(directory, ".swarm", "context.md");
51055
51096
  let contextContent = "";
51056
51097
  try {
51057
51098
  if (fs18.existsSync(contextPath)) {
@@ -52516,7 +52557,7 @@ function createDarkMatterDetectorHook(directory) {
52516
52557
 
52517
52558
  // src/hooks/incremental-verify.ts
52518
52559
  import * as fs20 from "fs";
52519
- import * as path32 from "path";
52560
+ import * as path33 from "path";
52520
52561
 
52521
52562
  // src/hooks/spawn-helper.ts
52522
52563
  import { spawn } from "child_process";
@@ -52572,7 +52613,7 @@ function spawnAsync(command, cwd, timeoutMs) {
52572
52613
  // src/hooks/incremental-verify.ts
52573
52614
  var emittedSkipAdvisories = new Set;
52574
52615
  function detectTypecheckCommand(projectDir) {
52575
- const pkgPath = path32.join(projectDir, "package.json");
52616
+ const pkgPath = path33.join(projectDir, "package.json");
52576
52617
  if (fs20.existsSync(pkgPath)) {
52577
52618
  try {
52578
52619
  const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
@@ -52588,8 +52629,8 @@ function detectTypecheckCommand(projectDir) {
52588
52629
  ...pkg.dependencies,
52589
52630
  ...pkg.devDependencies
52590
52631
  };
52591
- if (!deps?.typescript && !fs20.existsSync(path32.join(projectDir, "tsconfig.json"))) {}
52592
- const hasTSMarkers = deps?.typescript || fs20.existsSync(path32.join(projectDir, "tsconfig.json"));
52632
+ if (!deps?.typescript && !fs20.existsSync(path33.join(projectDir, "tsconfig.json"))) {}
52633
+ const hasTSMarkers = deps?.typescript || fs20.existsSync(path33.join(projectDir, "tsconfig.json"));
52593
52634
  if (hasTSMarkers) {
52594
52635
  return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
52595
52636
  }
@@ -52597,13 +52638,13 @@ function detectTypecheckCommand(projectDir) {
52597
52638
  return null;
52598
52639
  }
52599
52640
  }
52600
- if (fs20.existsSync(path32.join(projectDir, "go.mod"))) {
52641
+ if (fs20.existsSync(path33.join(projectDir, "go.mod"))) {
52601
52642
  return { command: ["go", "vet", "./..."], language: "go" };
52602
52643
  }
52603
- if (fs20.existsSync(path32.join(projectDir, "Cargo.toml"))) {
52644
+ if (fs20.existsSync(path33.join(projectDir, "Cargo.toml"))) {
52604
52645
  return { command: ["cargo", "check"], language: "rust" };
52605
52646
  }
52606
- if (fs20.existsSync(path32.join(projectDir, "pyproject.toml")) || fs20.existsSync(path32.join(projectDir, "requirements.txt")) || fs20.existsSync(path32.join(projectDir, "setup.py"))) {
52647
+ if (fs20.existsSync(path33.join(projectDir, "pyproject.toml")) || fs20.existsSync(path33.join(projectDir, "requirements.txt")) || fs20.existsSync(path33.join(projectDir, "setup.py"))) {
52607
52648
  return { command: null, language: "python" };
52608
52649
  }
52609
52650
  try {
@@ -52674,7 +52715,7 @@ ${errorSummary}`);
52674
52715
  // src/hooks/knowledge-reader.ts
52675
52716
  import { existsSync as existsSync19 } from "fs";
52676
52717
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
52677
- import * as path33 from "path";
52718
+ import * as path34 from "path";
52678
52719
  var JACCARD_THRESHOLD = 0.6;
52679
52720
  var HIVE_TIER_BOOST = 0.05;
52680
52721
  var SAME_PROJECT_PENALTY = -0.05;
@@ -52722,7 +52763,7 @@ function inferCategoriesFromPhase(phaseDescription) {
52722
52763
  return ["process", "tooling"];
52723
52764
  }
52724
52765
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
52725
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52766
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52726
52767
  try {
52727
52768
  let shownData = {};
52728
52769
  if (existsSync19(shownFile)) {
@@ -52730,7 +52771,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
52730
52771
  shownData = JSON.parse(content);
52731
52772
  }
52732
52773
  shownData[currentPhase] = lessonIds;
52733
- await mkdir4(path33.dirname(shownFile), { recursive: true });
52774
+ await mkdir4(path34.dirname(shownFile), { recursive: true });
52734
52775
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
52735
52776
  } catch {
52736
52777
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -52825,7 +52866,7 @@ async function readMergedKnowledge(directory, config3, context) {
52825
52866
  return topN;
52826
52867
  }
52827
52868
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
52828
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52869
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52829
52870
  try {
52830
52871
  if (!existsSync19(shownFile)) {
52831
52872
  return;
@@ -53298,10 +53339,10 @@ Use this data to avoid repeating known failure patterns.`;
53298
53339
  init_event_bus();
53299
53340
  init_utils2();
53300
53341
  import * as fs21 from "fs";
53301
- import * as path34 from "path";
53342
+ import * as path35 from "path";
53302
53343
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
53303
53344
  async function readPriorDriftReports(directory) {
53304
- const swarmDir = path34.join(directory, ".swarm");
53345
+ const swarmDir = path35.join(directory, ".swarm");
53305
53346
  const entries = await fs21.promises.readdir(swarmDir).catch(() => null);
53306
53347
  if (entries === null)
53307
53348
  return [];
@@ -53328,7 +53369,7 @@ async function readPriorDriftReports(directory) {
53328
53369
  async function writeDriftReport(directory, report) {
53329
53370
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
53330
53371
  const filePath = validateSwarmPath(directory, filename);
53331
- const swarmDir = path34.dirname(filePath);
53372
+ const swarmDir = path35.dirname(filePath);
53332
53373
  await fs21.promises.mkdir(swarmDir, { recursive: true });
53333
53374
  try {
53334
53375
  await fs21.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -53482,7 +53523,7 @@ function isOrchestratorAgent(agentName) {
53482
53523
  function injectKnowledgeMessage(output, text) {
53483
53524
  if (!output.messages)
53484
53525
  return;
53485
- const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes("\uD83D\uDCDA Knowledge")));
53526
+ const alreadyInjected = output.messages.some((m) => m.parts?.some((p) => p.text?.includes("\uD83D\uDCDA Knowledge") || p.text?.includes("<drift_report>") || p.text?.includes("<curator_briefing>")));
53486
53527
  if (alreadyInjected)
53487
53528
  return;
53488
53529
  const systemIdx = output.messages.findIndex((m) => m.info?.role === "system");
@@ -53528,8 +53569,33 @@ function createKnowledgeInjectorHook(directory, config3) {
53528
53569
  currentPhase: phaseDescription
53529
53570
  };
53530
53571
  const entries = await readMergedKnowledge(directory, config3, context);
53531
- if (entries.length === 0)
53572
+ try {
53573
+ const driftReports = await readPriorDriftReports(directory);
53574
+ if (driftReports.length > 0) {
53575
+ const latestReport = driftReports[driftReports.length - 1];
53576
+ const driftText = buildDriftInjectionText(latestReport, 500);
53577
+ if (driftText) {
53578
+ cachedInjectionText = cachedInjectionText ? `${driftText}
53579
+
53580
+ ${cachedInjectionText}` : driftText;
53581
+ }
53582
+ }
53583
+ } catch {}
53584
+ try {
53585
+ const briefingContent = await readSwarmFileAsync(directory, "curator-briefing.md");
53586
+ if (briefingContent) {
53587
+ const truncatedBriefing = briefingContent.slice(0, 500);
53588
+ cachedInjectionText = cachedInjectionText ? `<curator_briefing>${truncatedBriefing}</curator_briefing>
53589
+
53590
+ ${cachedInjectionText}` : `<curator_briefing>${truncatedBriefing}</curator_briefing>`;
53591
+ }
53592
+ } catch {}
53593
+ if (entries.length === 0) {
53594
+ if (cachedInjectionText === null)
53595
+ return;
53596
+ injectKnowledgeMessage(output, cachedInjectionText);
53532
53597
  return;
53598
+ }
53533
53599
  const runMemory = await getRunMemorySummary(directory);
53534
53600
  const lines = entries.map((entry) => {
53535
53601
  const stars = formatStars(entry.confidence);
@@ -53548,13 +53614,15 @@ function createKnowledgeInjectorHook(directory, config3) {
53548
53614
  "These are lessons learned from this project and past projects. Consider them as context but use your judgment \u2014 they may not all apply."
53549
53615
  ].join(`
53550
53616
  `);
53617
+ let injectionText = cachedInjectionText ? `${cachedInjectionText}
53618
+
53619
+ ${knowledgeSection}` : knowledgeSection;
53551
53620
  if (runMemory) {
53552
- cachedInjectionText = `${runMemory}
53621
+ injectionText = `${runMemory}
53553
53622
 
53554
- ${knowledgeSection}`;
53555
- } else {
53556
- cachedInjectionText = knowledgeSection;
53623
+ ${injectionText}`;
53557
53624
  }
53625
+ cachedInjectionText = injectionText;
53558
53626
  const rejected = await readRejectedLessons(directory);
53559
53627
  if (rejected.length > 0) {
53560
53628
  const recentRejected = rejected.slice(-3);
@@ -53565,25 +53633,13 @@ ${knowledgeSection}`;
53565
53633
  ` + rejectedLines.join(`
53566
53634
  `);
53567
53635
  }
53568
- try {
53569
- const driftReports = await readPriorDriftReports(directory);
53570
- if (driftReports.length > 0 && cachedInjectionText !== null) {
53571
- const latestReport = driftReports[driftReports.length - 1];
53572
- const driftText = buildDriftInjectionText(latestReport, 500);
53573
- if (driftText) {
53574
- cachedInjectionText = `${driftText}
53575
-
53576
- ${cachedInjectionText}`;
53577
- }
53578
- }
53579
- } catch {}
53580
53636
  injectKnowledgeMessage(output, cachedInjectionText);
53581
53637
  });
53582
53638
  }
53583
53639
 
53584
53640
  // src/hooks/slop-detector.ts
53585
53641
  import * as fs22 from "fs";
53586
- import * as path35 from "path";
53642
+ import * as path36 from "path";
53587
53643
  var WRITE_EDIT_TOOLS = new Set([
53588
53644
  "write",
53589
53645
  "edit",
@@ -53633,7 +53689,7 @@ function walkFiles(dir, exts, deadline) {
53633
53689
  break;
53634
53690
  if (entry.isSymbolicLink())
53635
53691
  continue;
53636
- const full = path35.join(dir, entry.name);
53692
+ const full = path36.join(dir, entry.name);
53637
53693
  if (entry.isDirectory()) {
53638
53694
  if (entry.name === "node_modules" || entry.name === ".git")
53639
53695
  continue;
@@ -53648,7 +53704,7 @@ function walkFiles(dir, exts, deadline) {
53648
53704
  return results;
53649
53705
  }
53650
53706
  function checkDeadExports(content, projectDir, startTime) {
53651
- const hasPackageJson = fs22.existsSync(path35.join(projectDir, "package.json"));
53707
+ const hasPackageJson = fs22.existsSync(path36.join(projectDir, "package.json"));
53652
53708
  if (!hasPackageJson)
53653
53709
  return null;
53654
53710
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -53798,7 +53854,7 @@ init_config_doctor();
53798
53854
 
53799
53855
  // src/session/snapshot-reader.ts
53800
53856
  init_utils2();
53801
- import path36 from "path";
53857
+ import path37 from "path";
53802
53858
  var VALID_TASK_WORKFLOW_STATES = [
53803
53859
  "idle",
53804
53860
  "coder_delegated",
@@ -53923,7 +53979,7 @@ function rehydrateState(snapshot) {
53923
53979
  async function reconcileTaskStatesFromPlan(directory) {
53924
53980
  let raw;
53925
53981
  try {
53926
- raw = await Bun.file(path36.join(directory, ".swarm/plan.json")).text();
53982
+ raw = await Bun.file(path37.join(directory, ".swarm/plan.json")).text();
53927
53983
  } catch {
53928
53984
  return;
53929
53985
  }
@@ -54146,7 +54202,7 @@ var build_check = createSwarmTool({
54146
54202
  init_dist();
54147
54203
  init_create_tool();
54148
54204
  import * as fs24 from "fs";
54149
- import * as path37 from "path";
54205
+ import * as path38 from "path";
54150
54206
  var EVIDENCE_DIR = ".swarm/evidence";
54151
54207
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
54152
54208
  function isValidTaskId3(taskId) {
@@ -54163,9 +54219,9 @@ function isValidTaskId3(taskId) {
54163
54219
  return TASK_ID_PATTERN2.test(taskId);
54164
54220
  }
54165
54221
  function isPathWithinSwarm(filePath, workspaceRoot) {
54166
- const normalizedWorkspace = path37.resolve(workspaceRoot);
54167
- const swarmPath = path37.join(normalizedWorkspace, ".swarm", "evidence");
54168
- const normalizedPath = path37.resolve(filePath);
54222
+ const normalizedWorkspace = path38.resolve(workspaceRoot);
54223
+ const swarmPath = path38.join(normalizedWorkspace, ".swarm", "evidence");
54224
+ const normalizedPath = path38.resolve(filePath);
54169
54225
  return normalizedPath.startsWith(swarmPath);
54170
54226
  }
54171
54227
  function readEvidenceFile(evidencePath) {
@@ -54226,7 +54282,7 @@ var check_gate_status = createSwarmTool({
54226
54282
  };
54227
54283
  return JSON.stringify(errorResult, null, 2);
54228
54284
  }
54229
- const evidencePath = path37.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54285
+ const evidencePath = path38.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54230
54286
  if (!isPathWithinSwarm(evidencePath, directory)) {
54231
54287
  const errorResult = {
54232
54288
  taskId: taskIdInput,
@@ -54287,7 +54343,7 @@ init_tool();
54287
54343
  init_create_tool();
54288
54344
  import { spawnSync } from "child_process";
54289
54345
  import * as fs25 from "fs";
54290
- import * as path38 from "path";
54346
+ import * as path39 from "path";
54291
54347
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
54292
54348
  var MAX_LABEL_LENGTH = 100;
54293
54349
  var GIT_TIMEOUT_MS = 30000;
@@ -54338,7 +54394,7 @@ function validateLabel(label) {
54338
54394
  return null;
54339
54395
  }
54340
54396
  function getCheckpointLogPath(directory) {
54341
- return path38.join(directory, CHECKPOINT_LOG_PATH);
54397
+ return path39.join(directory, CHECKPOINT_LOG_PATH);
54342
54398
  }
54343
54399
  function readCheckpointLog(directory) {
54344
54400
  const logPath = getCheckpointLogPath(directory);
@@ -54356,7 +54412,7 @@ function readCheckpointLog(directory) {
54356
54412
  }
54357
54413
  function writeCheckpointLog(log2, directory) {
54358
54414
  const logPath = getCheckpointLogPath(directory);
54359
- const dir = path38.dirname(logPath);
54415
+ const dir = path39.dirname(logPath);
54360
54416
  if (!fs25.existsSync(dir)) {
54361
54417
  fs25.mkdirSync(dir, { recursive: true });
54362
54418
  }
@@ -54564,7 +54620,7 @@ var checkpoint = createSwarmTool({
54564
54620
  init_dist();
54565
54621
  init_create_tool();
54566
54622
  import * as fs26 from "fs";
54567
- import * as path39 from "path";
54623
+ import * as path40 from "path";
54568
54624
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
54569
54625
  var DEFAULT_DAYS = 90;
54570
54626
  var DEFAULT_TOP_N = 20;
@@ -54708,7 +54764,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54708
54764
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
54709
54765
  const filteredChurn = new Map;
54710
54766
  for (const [file3, count] of churnMap) {
54711
- const ext = path39.extname(file3).toLowerCase();
54767
+ const ext = path40.extname(file3).toLowerCase();
54712
54768
  if (extSet.has(ext)) {
54713
54769
  filteredChurn.set(file3, count);
54714
54770
  }
@@ -54719,7 +54775,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54719
54775
  for (const [file3, churnCount] of filteredChurn) {
54720
54776
  let fullPath = file3;
54721
54777
  if (!fs26.existsSync(fullPath)) {
54722
- fullPath = path39.join(cwd, file3);
54778
+ fullPath = path40.join(cwd, file3);
54723
54779
  }
54724
54780
  const complexity = getComplexityForFile(fullPath);
54725
54781
  if (complexity !== null) {
@@ -54867,7 +54923,7 @@ var complexity_hotspots = createSwarmTool({
54867
54923
  // src/tools/declare-scope.ts
54868
54924
  init_tool();
54869
54925
  import * as fs27 from "fs";
54870
- import * as path40 from "path";
54926
+ import * as path41 from "path";
54871
54927
  init_create_tool();
54872
54928
  function validateTaskIdFormat(taskId) {
54873
54929
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -54946,8 +55002,8 @@ async function executeDeclareScope(args2, fallbackDir) {
54946
55002
  };
54947
55003
  }
54948
55004
  }
54949
- normalizedDir = path40.normalize(args2.working_directory);
54950
- const pathParts = normalizedDir.split(path40.sep);
55005
+ normalizedDir = path41.normalize(args2.working_directory);
55006
+ const pathParts = normalizedDir.split(path41.sep);
54951
55007
  if (pathParts.includes("..")) {
54952
55008
  return {
54953
55009
  success: false,
@@ -54957,10 +55013,10 @@ async function executeDeclareScope(args2, fallbackDir) {
54957
55013
  ]
54958
55014
  };
54959
55015
  }
54960
- const resolvedDir = path40.resolve(normalizedDir);
55016
+ const resolvedDir = path41.resolve(normalizedDir);
54961
55017
  try {
54962
55018
  const realPath = fs27.realpathSync(resolvedDir);
54963
- const planPath2 = path40.join(realPath, ".swarm", "plan.json");
55019
+ const planPath2 = path41.join(realPath, ".swarm", "plan.json");
54964
55020
  if (!fs27.existsSync(planPath2)) {
54965
55021
  return {
54966
55022
  success: false,
@@ -54981,7 +55037,7 @@ async function executeDeclareScope(args2, fallbackDir) {
54981
55037
  }
54982
55038
  }
54983
55039
  const directory = normalizedDir ?? fallbackDir ?? process.cwd();
54984
- const planPath = path40.resolve(directory, ".swarm", "plan.json");
55040
+ const planPath = path41.resolve(directory, ".swarm", "plan.json");
54985
55041
  if (!fs27.existsSync(planPath)) {
54986
55042
  return {
54987
55043
  success: false,
@@ -55071,20 +55127,20 @@ function validateBase(base) {
55071
55127
  function validatePaths(paths) {
55072
55128
  if (!paths)
55073
55129
  return null;
55074
- for (const path41 of paths) {
55075
- if (!path41 || path41.length === 0) {
55130
+ for (const path42 of paths) {
55131
+ if (!path42 || path42.length === 0) {
55076
55132
  return "empty path not allowed";
55077
55133
  }
55078
- if (path41.length > MAX_PATH_LENGTH) {
55134
+ if (path42.length > MAX_PATH_LENGTH) {
55079
55135
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
55080
55136
  }
55081
- if (SHELL_METACHARACTERS2.test(path41)) {
55137
+ if (SHELL_METACHARACTERS2.test(path42)) {
55082
55138
  return "path contains shell metacharacters";
55083
55139
  }
55084
- if (path41.startsWith("-")) {
55140
+ if (path42.startsWith("-")) {
55085
55141
  return 'path cannot start with "-" (option-like arguments not allowed)';
55086
55142
  }
55087
- if (CONTROL_CHAR_PATTERN2.test(path41)) {
55143
+ if (CONTROL_CHAR_PATTERN2.test(path42)) {
55088
55144
  return "path contains control characters";
55089
55145
  }
55090
55146
  }
@@ -55164,8 +55220,8 @@ var diff = tool({
55164
55220
  if (parts2.length >= 3) {
55165
55221
  const additions = parseInt(parts2[0], 10) || 0;
55166
55222
  const deletions = parseInt(parts2[1], 10) || 0;
55167
- const path41 = parts2[2];
55168
- files.push({ path: path41, additions, deletions });
55223
+ const path42 = parts2[2];
55224
+ files.push({ path: path42, additions, deletions });
55169
55225
  }
55170
55226
  }
55171
55227
  const contractChanges = [];
@@ -55395,7 +55451,7 @@ Use these as DOMAIN values when delegating to @sme.`;
55395
55451
  init_dist();
55396
55452
  init_create_tool();
55397
55453
  import * as fs28 from "fs";
55398
- import * as path41 from "path";
55454
+ import * as path42 from "path";
55399
55455
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
55400
55456
  var MAX_EVIDENCE_FILES = 1000;
55401
55457
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -55425,9 +55481,9 @@ function validateRequiredTypes(input) {
55425
55481
  return null;
55426
55482
  }
55427
55483
  function isPathWithinSwarm2(filePath, cwd) {
55428
- const normalizedCwd = path41.resolve(cwd);
55429
- const swarmPath = path41.join(normalizedCwd, ".swarm");
55430
- const normalizedPath = path41.resolve(filePath);
55484
+ const normalizedCwd = path42.resolve(cwd);
55485
+ const swarmPath = path42.join(normalizedCwd, ".swarm");
55486
+ const normalizedPath = path42.resolve(filePath);
55431
55487
  return normalizedPath.startsWith(swarmPath);
55432
55488
  }
55433
55489
  function parseCompletedTasks(planContent) {
@@ -55457,10 +55513,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
55457
55513
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
55458
55514
  continue;
55459
55515
  }
55460
- const filePath = path41.join(evidenceDir, filename);
55516
+ const filePath = path42.join(evidenceDir, filename);
55461
55517
  try {
55462
- const resolvedPath = path41.resolve(filePath);
55463
- const evidenceDirResolved = path41.resolve(evidenceDir);
55518
+ const resolvedPath = path42.resolve(filePath);
55519
+ const evidenceDirResolved = path42.resolve(evidenceDir);
55464
55520
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
55465
55521
  continue;
55466
55522
  }
@@ -55578,7 +55634,7 @@ var evidence_check = createSwarmTool({
55578
55634
  return JSON.stringify(errorResult, null, 2);
55579
55635
  }
55580
55636
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
55581
- const planPath = path41.join(cwd, PLAN_FILE);
55637
+ const planPath = path42.join(cwd, PLAN_FILE);
55582
55638
  if (!isPathWithinSwarm2(planPath, cwd)) {
55583
55639
  const errorResult = {
55584
55640
  error: "plan file path validation failed",
@@ -55610,7 +55666,7 @@ var evidence_check = createSwarmTool({
55610
55666
  };
55611
55667
  return JSON.stringify(result2, null, 2);
55612
55668
  }
55613
- const evidenceDir = path41.join(cwd, EVIDENCE_DIR2);
55669
+ const evidenceDir = path42.join(cwd, EVIDENCE_DIR2);
55614
55670
  const evidence = readEvidenceFiles(evidenceDir, cwd);
55615
55671
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
55616
55672
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -55628,7 +55684,7 @@ var evidence_check = createSwarmTool({
55628
55684
  init_tool();
55629
55685
  init_create_tool();
55630
55686
  import * as fs29 from "fs";
55631
- import * as path42 from "path";
55687
+ import * as path43 from "path";
55632
55688
  var EXT_MAP = {
55633
55689
  python: ".py",
55634
55690
  py: ".py",
@@ -55709,12 +55765,12 @@ var extract_code_blocks = createSwarmTool({
55709
55765
  if (prefix) {
55710
55766
  filename = `${prefix}_${filename}`;
55711
55767
  }
55712
- let filepath = path42.join(targetDir, filename);
55713
- const base = path42.basename(filepath, path42.extname(filepath));
55714
- const ext = path42.extname(filepath);
55768
+ let filepath = path43.join(targetDir, filename);
55769
+ const base = path43.basename(filepath, path43.extname(filepath));
55770
+ const ext = path43.extname(filepath);
55715
55771
  let counter = 1;
55716
55772
  while (fs29.existsSync(filepath)) {
55717
- filepath = path42.join(targetDir, `${base}_${counter}${ext}`);
55773
+ filepath = path43.join(targetDir, `${base}_${counter}${ext}`);
55718
55774
  counter++;
55719
55775
  }
55720
55776
  try {
@@ -55832,7 +55888,7 @@ var gitingest = tool({
55832
55888
  // src/tools/imports.ts
55833
55889
  init_dist();
55834
55890
  import * as fs30 from "fs";
55835
- import * as path43 from "path";
55891
+ import * as path44 from "path";
55836
55892
  var MAX_FILE_PATH_LENGTH2 = 500;
55837
55893
  var MAX_SYMBOL_LENGTH = 256;
55838
55894
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -55886,7 +55942,7 @@ function validateSymbolInput(symbol3) {
55886
55942
  return null;
55887
55943
  }
55888
55944
  function isBinaryFile2(filePath, buffer) {
55889
- const ext = path43.extname(filePath).toLowerCase();
55945
+ const ext = path44.extname(filePath).toLowerCase();
55890
55946
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
55891
55947
  return false;
55892
55948
  }
@@ -55910,15 +55966,15 @@ function parseImports(content, targetFile, targetSymbol) {
55910
55966
  const imports = [];
55911
55967
  let _resolvedTarget;
55912
55968
  try {
55913
- _resolvedTarget = path43.resolve(targetFile);
55969
+ _resolvedTarget = path44.resolve(targetFile);
55914
55970
  } catch {
55915
55971
  _resolvedTarget = targetFile;
55916
55972
  }
55917
- const targetBasename = path43.basename(targetFile, path43.extname(targetFile));
55973
+ const targetBasename = path44.basename(targetFile, path44.extname(targetFile));
55918
55974
  const targetWithExt = targetFile;
55919
55975
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
55920
- const normalizedTargetWithExt = path43.normalize(targetWithExt).replace(/\\/g, "/");
55921
- const normalizedTargetWithoutExt = path43.normalize(targetWithoutExt).replace(/\\/g, "/");
55976
+ const normalizedTargetWithExt = path44.normalize(targetWithExt).replace(/\\/g, "/");
55977
+ const normalizedTargetWithoutExt = path44.normalize(targetWithoutExt).replace(/\\/g, "/");
55922
55978
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
55923
55979
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
55924
55980
  const modulePath = match[1] || match[2] || match[3];
@@ -55941,9 +55997,9 @@ function parseImports(content, targetFile, targetSymbol) {
55941
55997
  }
55942
55998
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
55943
55999
  let isMatch = false;
55944
- const _targetDir = path43.dirname(targetFile);
55945
- const targetExt = path43.extname(targetFile);
55946
- const targetBasenameNoExt = path43.basename(targetFile, targetExt);
56000
+ const _targetDir = path44.dirname(targetFile);
56001
+ const targetExt = path44.extname(targetFile);
56002
+ const targetBasenameNoExt = path44.basename(targetFile, targetExt);
55947
56003
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
55948
56004
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
55949
56005
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -56011,10 +56067,10 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56011
56067
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
56012
56068
  for (const entry of entries) {
56013
56069
  if (SKIP_DIRECTORIES2.has(entry)) {
56014
- stats.skippedDirs.push(path43.join(dir, entry));
56070
+ stats.skippedDirs.push(path44.join(dir, entry));
56015
56071
  continue;
56016
56072
  }
56017
- const fullPath = path43.join(dir, entry);
56073
+ const fullPath = path44.join(dir, entry);
56018
56074
  let stat2;
56019
56075
  try {
56020
56076
  stat2 = fs30.statSync(fullPath);
@@ -56028,7 +56084,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
56028
56084
  if (stat2.isDirectory()) {
56029
56085
  findSourceFiles(fullPath, files, stats);
56030
56086
  } else if (stat2.isFile()) {
56031
- const ext = path43.extname(fullPath).toLowerCase();
56087
+ const ext = path44.extname(fullPath).toLowerCase();
56032
56088
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
56033
56089
  files.push(fullPath);
56034
56090
  }
@@ -56084,7 +56140,7 @@ var imports = tool({
56084
56140
  return JSON.stringify(errorResult, null, 2);
56085
56141
  }
56086
56142
  try {
56087
- const targetFile = path43.resolve(file3);
56143
+ const targetFile = path44.resolve(file3);
56088
56144
  if (!fs30.existsSync(targetFile)) {
56089
56145
  const errorResult = {
56090
56146
  error: `target file not found: ${file3}`,
@@ -56106,7 +56162,7 @@ var imports = tool({
56106
56162
  };
56107
56163
  return JSON.stringify(errorResult, null, 2);
56108
56164
  }
56109
- const baseDir = path43.dirname(targetFile);
56165
+ const baseDir = path44.dirname(targetFile);
56110
56166
  const scanStats = {
56111
56167
  skippedDirs: [],
56112
56168
  skippedFiles: 0,
@@ -56428,7 +56484,7 @@ init_config();
56428
56484
  init_schema();
56429
56485
  init_manager();
56430
56486
  import * as fs31 from "fs";
56431
- import * as path44 from "path";
56487
+ import * as path45 from "path";
56432
56488
  init_utils2();
56433
56489
  init_create_tool();
56434
56490
  function safeWarn(message, error93) {
@@ -56623,7 +56679,7 @@ async function executePhaseComplete(args2, workingDirectory) {
56623
56679
  }
56624
56680
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
56625
56681
  try {
56626
- const projectName = path44.basename(dir);
56682
+ const projectName = path45.basename(dir);
56627
56683
  const knowledgeConfig = {
56628
56684
  enabled: true,
56629
56685
  swarm_max_entries: 100,
@@ -56652,12 +56708,17 @@ async function executePhaseComplete(args2, workingDirectory) {
56652
56708
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
56653
56709
  }
56654
56710
  }
56711
+ let complianceWarnings = [];
56655
56712
  try {
56656
56713
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
56657
56714
  if (curatorConfig.enabled && curatorConfig.phase_enabled) {
56658
56715
  const curatorResult = await runCuratorPhase(dir, phase, agentsDispatched, curatorConfig, {});
56659
56716
  await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, {});
56660
56717
  await runCriticDriftCheck(dir, phase, curatorResult, curatorConfig);
56718
+ if (curatorResult.compliance.length > 0 && !curatorConfig.suppress_warnings) {
56719
+ const complianceLines = curatorResult.compliance.map((obs) => `[${obs.severity.toUpperCase()}] ${obs.description}`).slice(0, 5);
56720
+ complianceWarnings = complianceLines;
56721
+ }
56661
56722
  }
56662
56723
  } catch (curatorError) {
56663
56724
  safeWarn("[phase_complete] Curator pipeline error (non-blocking):", curatorError);
@@ -56743,6 +56804,9 @@ async function executePhaseComplete(args2, workingDirectory) {
56743
56804
  warnings.push(`Warning: failed to update plan.json phase status: ${error93 instanceof Error ? error93.message : String(error93)}`);
56744
56805
  }
56745
56806
  }
56807
+ if (complianceWarnings.length > 0) {
56808
+ warnings.push(`Curator compliance: ${complianceWarnings.join("; ")}`);
56809
+ }
56746
56810
  const result = {
56747
56811
  success: success3,
56748
56812
  phase,
@@ -56787,7 +56851,7 @@ init_discovery();
56787
56851
  init_utils();
56788
56852
  init_create_tool();
56789
56853
  import * as fs32 from "fs";
56790
- import * as path45 from "path";
56854
+ import * as path46 from "path";
56791
56855
  var MAX_OUTPUT_BYTES5 = 52428800;
56792
56856
  var AUDIT_TIMEOUT_MS = 120000;
56793
56857
  function isValidEcosystem(value) {
@@ -56805,16 +56869,16 @@ function validateArgs3(args2) {
56805
56869
  function detectEcosystems(directory) {
56806
56870
  const ecosystems = [];
56807
56871
  const cwd = directory;
56808
- if (fs32.existsSync(path45.join(cwd, "package.json"))) {
56872
+ if (fs32.existsSync(path46.join(cwd, "package.json"))) {
56809
56873
  ecosystems.push("npm");
56810
56874
  }
56811
- if (fs32.existsSync(path45.join(cwd, "pyproject.toml")) || fs32.existsSync(path45.join(cwd, "requirements.txt"))) {
56875
+ if (fs32.existsSync(path46.join(cwd, "pyproject.toml")) || fs32.existsSync(path46.join(cwd, "requirements.txt"))) {
56812
56876
  ecosystems.push("pip");
56813
56877
  }
56814
- if (fs32.existsSync(path45.join(cwd, "Cargo.toml"))) {
56878
+ if (fs32.existsSync(path46.join(cwd, "Cargo.toml"))) {
56815
56879
  ecosystems.push("cargo");
56816
56880
  }
56817
- if (fs32.existsSync(path45.join(cwd, "go.mod"))) {
56881
+ if (fs32.existsSync(path46.join(cwd, "go.mod"))) {
56818
56882
  ecosystems.push("go");
56819
56883
  }
56820
56884
  try {
@@ -56823,10 +56887,10 @@ function detectEcosystems(directory) {
56823
56887
  ecosystems.push("dotnet");
56824
56888
  }
56825
56889
  } catch {}
56826
- if (fs32.existsSync(path45.join(cwd, "Gemfile")) || fs32.existsSync(path45.join(cwd, "Gemfile.lock"))) {
56890
+ if (fs32.existsSync(path46.join(cwd, "Gemfile")) || fs32.existsSync(path46.join(cwd, "Gemfile.lock"))) {
56827
56891
  ecosystems.push("ruby");
56828
56892
  }
56829
- if (fs32.existsSync(path45.join(cwd, "pubspec.yaml"))) {
56893
+ if (fs32.existsSync(path46.join(cwd, "pubspec.yaml"))) {
56830
56894
  ecosystems.push("dart");
56831
56895
  }
56832
56896
  return ecosystems;
@@ -57889,7 +57953,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
57889
57953
  // src/tools/pre-check-batch.ts
57890
57954
  init_dist();
57891
57955
  import * as fs35 from "fs";
57892
- import * as path48 from "path";
57956
+ import * as path49 from "path";
57893
57957
 
57894
57958
  // node_modules/yocto-queue/index.js
57895
57959
  class Node2 {
@@ -58057,7 +58121,7 @@ init_manager();
58057
58121
 
58058
58122
  // src/quality/metrics.ts
58059
58123
  import * as fs33 from "fs";
58060
- import * as path46 from "path";
58124
+ import * as path47 from "path";
58061
58125
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
58062
58126
  var MIN_DUPLICATION_LINES = 10;
58063
58127
  function estimateCyclomaticComplexity(content) {
@@ -58109,7 +58173,7 @@ async function computeComplexityDelta(files, workingDir) {
58109
58173
  let totalComplexity = 0;
58110
58174
  const analyzedFiles = [];
58111
58175
  for (const file3 of files) {
58112
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58176
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58113
58177
  if (!fs33.existsSync(fullPath)) {
58114
58178
  continue;
58115
58179
  }
@@ -58232,7 +58296,7 @@ function countGoExports(content) {
58232
58296
  function getExportCountForFile(filePath) {
58233
58297
  try {
58234
58298
  const content = fs33.readFileSync(filePath, "utf-8");
58235
- const ext = path46.extname(filePath).toLowerCase();
58299
+ const ext = path47.extname(filePath).toLowerCase();
58236
58300
  switch (ext) {
58237
58301
  case ".ts":
58238
58302
  case ".tsx":
@@ -58258,7 +58322,7 @@ async function computePublicApiDelta(files, workingDir) {
58258
58322
  let totalExports = 0;
58259
58323
  const analyzedFiles = [];
58260
58324
  for (const file3 of files) {
58261
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58325
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58262
58326
  if (!fs33.existsSync(fullPath)) {
58263
58327
  continue;
58264
58328
  }
@@ -58292,7 +58356,7 @@ async function computeDuplicationRatio(files, workingDir) {
58292
58356
  let duplicateLines = 0;
58293
58357
  const analyzedFiles = [];
58294
58358
  for (const file3 of files) {
58295
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58359
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58296
58360
  if (!fs33.existsSync(fullPath)) {
58297
58361
  continue;
58298
58362
  }
@@ -58325,8 +58389,8 @@ function countCodeLines(content) {
58325
58389
  return lines.length;
58326
58390
  }
58327
58391
  function isTestFile(filePath) {
58328
- const basename8 = path46.basename(filePath);
58329
- const _ext = path46.extname(filePath).toLowerCase();
58392
+ const basename8 = path47.basename(filePath);
58393
+ const _ext = path47.extname(filePath).toLowerCase();
58330
58394
  const testPatterns = [
58331
58395
  ".test.",
58332
58396
  ".spec.",
@@ -58407,8 +58471,8 @@ function matchGlobSegment(globSegments, pathSegments) {
58407
58471
  }
58408
58472
  return gIndex === globSegments.length && pIndex === pathSegments.length;
58409
58473
  }
58410
- function matchesGlobSegment(path47, glob) {
58411
- const normalizedPath = path47.replace(/\\/g, "/");
58474
+ function matchesGlobSegment(path48, glob) {
58475
+ const normalizedPath = path48.replace(/\\/g, "/");
58412
58476
  const normalizedGlob = glob.replace(/\\/g, "/");
58413
58477
  if (normalizedPath.includes("//")) {
58414
58478
  return false;
@@ -58439,8 +58503,8 @@ function simpleGlobToRegex2(glob) {
58439
58503
  function hasGlobstar(glob) {
58440
58504
  return glob.includes("**");
58441
58505
  }
58442
- function globMatches(path47, glob) {
58443
- const normalizedPath = path47.replace(/\\/g, "/");
58506
+ function globMatches(path48, glob) {
58507
+ const normalizedPath = path48.replace(/\\/g, "/");
58444
58508
  if (!glob || glob === "") {
58445
58509
  if (normalizedPath.includes("//")) {
58446
58510
  return false;
@@ -58476,7 +58540,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
58476
58540
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58477
58541
  let testLines = 0;
58478
58542
  let codeLines = 0;
58479
- const srcDir = path46.join(workingDir, "src");
58543
+ const srcDir = path47.join(workingDir, "src");
58480
58544
  if (fs33.existsSync(srcDir)) {
58481
58545
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
58482
58546
  codeLines += lines;
@@ -58484,14 +58548,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58484
58548
  }
58485
58549
  const possibleSrcDirs = ["lib", "app", "source", "core"];
58486
58550
  for (const dir of possibleSrcDirs) {
58487
- const dirPath = path46.join(workingDir, dir);
58551
+ const dirPath = path47.join(workingDir, dir);
58488
58552
  if (fs33.existsSync(dirPath)) {
58489
58553
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
58490
58554
  codeLines += lines;
58491
58555
  });
58492
58556
  }
58493
58557
  }
58494
- const testsDir = path46.join(workingDir, "tests");
58558
+ const testsDir = path47.join(workingDir, "tests");
58495
58559
  if (fs33.existsSync(testsDir)) {
58496
58560
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
58497
58561
  testLines += lines;
@@ -58499,7 +58563,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58499
58563
  }
58500
58564
  const possibleTestDirs = ["test", "__tests__", "specs"];
58501
58565
  for (const dir of possibleTestDirs) {
58502
- const dirPath = path46.join(workingDir, dir);
58566
+ const dirPath = path47.join(workingDir, dir);
58503
58567
  if (fs33.existsSync(dirPath) && dirPath !== testsDir) {
58504
58568
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
58505
58569
  testLines += lines;
@@ -58514,7 +58578,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58514
58578
  try {
58515
58579
  const entries = fs33.readdirSync(dirPath, { withFileTypes: true });
58516
58580
  for (const entry of entries) {
58517
- const fullPath = path46.join(dirPath, entry.name);
58581
+ const fullPath = path47.join(dirPath, entry.name);
58518
58582
  if (entry.isDirectory()) {
58519
58583
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
58520
58584
  continue;
@@ -58522,7 +58586,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58522
58586
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
58523
58587
  } else if (entry.isFile()) {
58524
58588
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
58525
- const ext = path46.extname(entry.name).toLowerCase();
58589
+ const ext = path47.extname(entry.name).toLowerCase();
58526
58590
  const validExts = [
58527
58591
  ".ts",
58528
58592
  ".tsx",
@@ -58773,7 +58837,7 @@ init_dist();
58773
58837
  init_manager();
58774
58838
  init_detector();
58775
58839
  import * as fs34 from "fs";
58776
- import * as path47 from "path";
58840
+ import * as path48 from "path";
58777
58841
  import { extname as extname9 } from "path";
58778
58842
 
58779
58843
  // src/sast/rules/c.ts
@@ -59736,7 +59800,7 @@ async function sastScan(input, directory, config3) {
59736
59800
  _filesSkipped++;
59737
59801
  continue;
59738
59802
  }
59739
- const resolvedPath = path47.isAbsolute(filePath) ? filePath : path47.resolve(directory, filePath);
59803
+ const resolvedPath = path48.isAbsolute(filePath) ? filePath : path48.resolve(directory, filePath);
59740
59804
  if (!fs34.existsSync(resolvedPath)) {
59741
59805
  _filesSkipped++;
59742
59806
  continue;
@@ -59935,18 +59999,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
59935
59999
  let resolved;
59936
60000
  const isWinAbs = isWindowsAbsolutePath(inputPath);
59937
60001
  if (isWinAbs) {
59938
- resolved = path48.win32.resolve(inputPath);
59939
- } else if (path48.isAbsolute(inputPath)) {
59940
- resolved = path48.resolve(inputPath);
60002
+ resolved = path49.win32.resolve(inputPath);
60003
+ } else if (path49.isAbsolute(inputPath)) {
60004
+ resolved = path49.resolve(inputPath);
59941
60005
  } else {
59942
- resolved = path48.resolve(baseDir, inputPath);
60006
+ resolved = path49.resolve(baseDir, inputPath);
59943
60007
  }
59944
- const workspaceResolved = path48.resolve(workspaceDir);
60008
+ const workspaceResolved = path49.resolve(workspaceDir);
59945
60009
  let relative5;
59946
60010
  if (isWinAbs) {
59947
- relative5 = path48.win32.relative(workspaceResolved, resolved);
60011
+ relative5 = path49.win32.relative(workspaceResolved, resolved);
59948
60012
  } else {
59949
- relative5 = path48.relative(workspaceResolved, resolved);
60013
+ relative5 = path49.relative(workspaceResolved, resolved);
59950
60014
  }
59951
60015
  if (relative5.startsWith("..")) {
59952
60016
  return "path traversal detected";
@@ -60007,13 +60071,13 @@ async function runLintWrapped(files, directory, _config) {
60007
60071
  }
60008
60072
  async function runLintOnFiles(linter, files, workspaceDir) {
60009
60073
  const isWindows = process.platform === "win32";
60010
- const binDir = path48.join(workspaceDir, "node_modules", ".bin");
60074
+ const binDir = path49.join(workspaceDir, "node_modules", ".bin");
60011
60075
  const validatedFiles = [];
60012
60076
  for (const file3 of files) {
60013
60077
  if (typeof file3 !== "string") {
60014
60078
  continue;
60015
60079
  }
60016
- const resolvedPath = path48.resolve(file3);
60080
+ const resolvedPath = path49.resolve(file3);
60017
60081
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
60018
60082
  if (validationError) {
60019
60083
  continue;
@@ -60031,10 +60095,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
60031
60095
  }
60032
60096
  let command;
60033
60097
  if (linter === "biome") {
60034
- const biomeBin = isWindows ? path48.join(binDir, "biome.EXE") : path48.join(binDir, "biome");
60098
+ const biomeBin = isWindows ? path49.join(binDir, "biome.EXE") : path49.join(binDir, "biome");
60035
60099
  command = [biomeBin, "check", ...validatedFiles];
60036
60100
  } else {
60037
- const eslintBin = isWindows ? path48.join(binDir, "eslint.cmd") : path48.join(binDir, "eslint");
60101
+ const eslintBin = isWindows ? path49.join(binDir, "eslint.cmd") : path49.join(binDir, "eslint");
60038
60102
  command = [eslintBin, ...validatedFiles];
60039
60103
  }
60040
60104
  try {
@@ -60171,7 +60235,7 @@ async function runSecretscanWithFiles(files, directory) {
60171
60235
  skippedFiles++;
60172
60236
  continue;
60173
60237
  }
60174
- const resolvedPath = path48.resolve(file3);
60238
+ const resolvedPath = path49.resolve(file3);
60175
60239
  const validationError = validatePath(resolvedPath, directory, directory);
60176
60240
  if (validationError) {
60177
60241
  skippedFiles++;
@@ -60189,7 +60253,7 @@ async function runSecretscanWithFiles(files, directory) {
60189
60253
  };
60190
60254
  }
60191
60255
  for (const file3 of validatedFiles) {
60192
- const ext = path48.extname(file3).toLowerCase();
60256
+ const ext = path49.extname(file3).toLowerCase();
60193
60257
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
60194
60258
  skippedFiles++;
60195
60259
  continue;
@@ -60348,7 +60412,7 @@ async function runPreCheckBatch(input, workspaceDir) {
60348
60412
  warn(`pre_check_batch: Invalid file path: ${file3}`);
60349
60413
  continue;
60350
60414
  }
60351
- changedFiles.push(path48.resolve(directory, file3));
60415
+ changedFiles.push(path49.resolve(directory, file3));
60352
60416
  }
60353
60417
  if (changedFiles.length === 0) {
60354
60418
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -60499,7 +60563,7 @@ var pre_check_batch = createSwarmTool({
60499
60563
  };
60500
60564
  return JSON.stringify(errorResult, null, 2);
60501
60565
  }
60502
- const resolvedDirectory = path48.resolve(typedArgs.directory);
60566
+ const resolvedDirectory = path49.resolve(typedArgs.directory);
60503
60567
  const workspaceAnchor = resolvedDirectory;
60504
60568
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
60505
60569
  if (dirError) {
@@ -60607,7 +60671,7 @@ init_tool();
60607
60671
  init_manager2();
60608
60672
  init_create_tool();
60609
60673
  import * as fs36 from "fs";
60610
- import * as path49 from "path";
60674
+ import * as path50 from "path";
60611
60675
  function detectPlaceholderContent(args2) {
60612
60676
  const issues = [];
60613
60677
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -60711,7 +60775,7 @@ async function executeSavePlan(args2, fallbackDir) {
60711
60775
  try {
60712
60776
  await savePlan(dir, plan);
60713
60777
  try {
60714
- const markerPath = path49.join(dir, ".swarm", ".plan-write-marker");
60778
+ const markerPath = path50.join(dir, ".swarm", ".plan-write-marker");
60715
60779
  const marker = JSON.stringify({
60716
60780
  source: "save_plan",
60717
60781
  timestamp: new Date().toISOString(),
@@ -60723,7 +60787,7 @@ async function executeSavePlan(args2, fallbackDir) {
60723
60787
  return {
60724
60788
  success: true,
60725
60789
  message: "Plan saved successfully",
60726
- plan_path: path49.join(dir, ".swarm", "plan.json"),
60790
+ plan_path: path50.join(dir, ".swarm", "plan.json"),
60727
60791
  phases_count: plan.phases.length,
60728
60792
  tasks_count: tasksCount
60729
60793
  };
@@ -60762,7 +60826,7 @@ var save_plan = createSwarmTool({
60762
60826
  init_dist();
60763
60827
  init_manager();
60764
60828
  import * as fs37 from "fs";
60765
- import * as path50 from "path";
60829
+ import * as path51 from "path";
60766
60830
 
60767
60831
  // src/sbom/detectors/index.ts
60768
60832
  init_utils();
@@ -61610,7 +61674,7 @@ function findManifestFiles(rootDir) {
61610
61674
  try {
61611
61675
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61612
61676
  for (const entry of entries) {
61613
- const fullPath = path50.join(dir, entry.name);
61677
+ const fullPath = path51.join(dir, entry.name);
61614
61678
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
61615
61679
  continue;
61616
61680
  }
@@ -61619,7 +61683,7 @@ function findManifestFiles(rootDir) {
61619
61683
  } else if (entry.isFile()) {
61620
61684
  for (const pattern of patterns) {
61621
61685
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61622
- manifestFiles.push(path50.relative(rootDir, fullPath));
61686
+ manifestFiles.push(path51.relative(rootDir, fullPath));
61623
61687
  break;
61624
61688
  }
61625
61689
  }
@@ -61637,11 +61701,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61637
61701
  try {
61638
61702
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61639
61703
  for (const entry of entries) {
61640
- const fullPath = path50.join(dir, entry.name);
61704
+ const fullPath = path51.join(dir, entry.name);
61641
61705
  if (entry.isFile()) {
61642
61706
  for (const pattern of patterns) {
61643
61707
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61644
- found.push(path50.relative(workingDir, fullPath));
61708
+ found.push(path51.relative(workingDir, fullPath));
61645
61709
  break;
61646
61710
  }
61647
61711
  }
@@ -61654,11 +61718,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61654
61718
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
61655
61719
  const dirs = new Set;
61656
61720
  for (const file3 of changedFiles) {
61657
- let currentDir = path50.dirname(file3);
61721
+ let currentDir = path51.dirname(file3);
61658
61722
  while (true) {
61659
- if (currentDir && currentDir !== "." && currentDir !== path50.sep) {
61660
- dirs.add(path50.join(workingDir, currentDir));
61661
- const parent = path50.dirname(currentDir);
61723
+ if (currentDir && currentDir !== "." && currentDir !== path51.sep) {
61724
+ dirs.add(path51.join(workingDir, currentDir));
61725
+ const parent = path51.dirname(currentDir);
61662
61726
  if (parent === currentDir)
61663
61727
  break;
61664
61728
  currentDir = parent;
@@ -61742,7 +61806,7 @@ var sbom_generate = createSwarmTool({
61742
61806
  const changedFiles = obj.changed_files;
61743
61807
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
61744
61808
  const workingDir = directory;
61745
- const outputDir = path50.isAbsolute(relativeOutputDir) ? relativeOutputDir : path50.join(workingDir, relativeOutputDir);
61809
+ const outputDir = path51.isAbsolute(relativeOutputDir) ? relativeOutputDir : path51.join(workingDir, relativeOutputDir);
61746
61810
  let manifestFiles = [];
61747
61811
  if (scope === "all") {
61748
61812
  manifestFiles = findManifestFiles(workingDir);
@@ -61765,7 +61829,7 @@ var sbom_generate = createSwarmTool({
61765
61829
  const processedFiles = [];
61766
61830
  for (const manifestFile of manifestFiles) {
61767
61831
  try {
61768
- const fullPath = path50.isAbsolute(manifestFile) ? manifestFile : path50.join(workingDir, manifestFile);
61832
+ const fullPath = path51.isAbsolute(manifestFile) ? manifestFile : path51.join(workingDir, manifestFile);
61769
61833
  if (!fs37.existsSync(fullPath)) {
61770
61834
  continue;
61771
61835
  }
@@ -61782,7 +61846,7 @@ var sbom_generate = createSwarmTool({
61782
61846
  const bom = generateCycloneDX(allComponents);
61783
61847
  const bomJson = serializeCycloneDX(bom);
61784
61848
  const filename = generateSbomFilename();
61785
- const outputPath = path50.join(outputDir, filename);
61849
+ const outputPath = path51.join(outputDir, filename);
61786
61850
  fs37.writeFileSync(outputPath, bomJson, "utf-8");
61787
61851
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
61788
61852
  try {
@@ -61826,7 +61890,7 @@ var sbom_generate = createSwarmTool({
61826
61890
  init_dist();
61827
61891
  init_create_tool();
61828
61892
  import * as fs38 from "fs";
61829
- import * as path51 from "path";
61893
+ import * as path52 from "path";
61830
61894
  var SPEC_CANDIDATES = [
61831
61895
  "openapi.json",
61832
61896
  "openapi.yaml",
@@ -61858,12 +61922,12 @@ function normalizePath2(p) {
61858
61922
  }
61859
61923
  function discoverSpecFile(cwd, specFileArg) {
61860
61924
  if (specFileArg) {
61861
- const resolvedPath = path51.resolve(cwd, specFileArg);
61862
- const normalizedCwd = cwd.endsWith(path51.sep) ? cwd : cwd + path51.sep;
61925
+ const resolvedPath = path52.resolve(cwd, specFileArg);
61926
+ const normalizedCwd = cwd.endsWith(path52.sep) ? cwd : cwd + path52.sep;
61863
61927
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
61864
61928
  throw new Error("Invalid spec_file: path traversal detected");
61865
61929
  }
61866
- const ext = path51.extname(resolvedPath).toLowerCase();
61930
+ const ext = path52.extname(resolvedPath).toLowerCase();
61867
61931
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
61868
61932
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
61869
61933
  }
@@ -61877,7 +61941,7 @@ function discoverSpecFile(cwd, specFileArg) {
61877
61941
  return resolvedPath;
61878
61942
  }
61879
61943
  for (const candidate of SPEC_CANDIDATES) {
61880
- const candidatePath = path51.resolve(cwd, candidate);
61944
+ const candidatePath = path52.resolve(cwd, candidate);
61881
61945
  if (fs38.existsSync(candidatePath)) {
61882
61946
  const stats = fs38.statSync(candidatePath);
61883
61947
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -61889,7 +61953,7 @@ function discoverSpecFile(cwd, specFileArg) {
61889
61953
  }
61890
61954
  function parseSpec(specFile) {
61891
61955
  const content = fs38.readFileSync(specFile, "utf-8");
61892
- const ext = path51.extname(specFile).toLowerCase();
61956
+ const ext = path52.extname(specFile).toLowerCase();
61893
61957
  if (ext === ".json") {
61894
61958
  return parseJsonSpec(content);
61895
61959
  }
@@ -61965,7 +62029,7 @@ function extractRoutes(cwd) {
61965
62029
  return;
61966
62030
  }
61967
62031
  for (const entry of entries) {
61968
- const fullPath = path51.join(dir, entry.name);
62032
+ const fullPath = path52.join(dir, entry.name);
61969
62033
  if (entry.isSymbolicLink()) {
61970
62034
  continue;
61971
62035
  }
@@ -61975,7 +62039,7 @@ function extractRoutes(cwd) {
61975
62039
  }
61976
62040
  walkDir(fullPath);
61977
62041
  } else if (entry.isFile()) {
61978
- const ext = path51.extname(entry.name).toLowerCase();
62042
+ const ext = path52.extname(entry.name).toLowerCase();
61979
62043
  const baseName = entry.name.toLowerCase();
61980
62044
  if (![".ts", ".js", ".mjs"].includes(ext)) {
61981
62045
  continue;
@@ -62145,7 +62209,7 @@ init_secretscan();
62145
62209
  init_tool();
62146
62210
  init_create_tool();
62147
62211
  import * as fs39 from "fs";
62148
- import * as path52 from "path";
62212
+ import * as path53 from "path";
62149
62213
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
62150
62214
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
62151
62215
  function containsControlCharacters(str) {
@@ -62174,11 +62238,11 @@ function containsWindowsAttacks(str) {
62174
62238
  }
62175
62239
  function isPathInWorkspace(filePath, workspace) {
62176
62240
  try {
62177
- const resolvedPath = path52.resolve(workspace, filePath);
62241
+ const resolvedPath = path53.resolve(workspace, filePath);
62178
62242
  const realWorkspace = fs39.realpathSync(workspace);
62179
62243
  const realResolvedPath = fs39.realpathSync(resolvedPath);
62180
- const relativePath = path52.relative(realWorkspace, realResolvedPath);
62181
- if (relativePath.startsWith("..") || path52.isAbsolute(relativePath)) {
62244
+ const relativePath = path53.relative(realWorkspace, realResolvedPath);
62245
+ if (relativePath.startsWith("..") || path53.isAbsolute(relativePath)) {
62182
62246
  return false;
62183
62247
  }
62184
62248
  return true;
@@ -62190,7 +62254,7 @@ function validatePathForRead(filePath, workspace) {
62190
62254
  return isPathInWorkspace(filePath, workspace);
62191
62255
  }
62192
62256
  function extractTSSymbols(filePath, cwd) {
62193
- const fullPath = path52.join(cwd, filePath);
62257
+ const fullPath = path53.join(cwd, filePath);
62194
62258
  if (!validatePathForRead(fullPath, cwd)) {
62195
62259
  return [];
62196
62260
  }
@@ -62342,7 +62406,7 @@ function extractTSSymbols(filePath, cwd) {
62342
62406
  });
62343
62407
  }
62344
62408
  function extractPythonSymbols(filePath, cwd) {
62345
- const fullPath = path52.join(cwd, filePath);
62409
+ const fullPath = path53.join(cwd, filePath);
62346
62410
  if (!validatePathForRead(fullPath, cwd)) {
62347
62411
  return [];
62348
62412
  }
@@ -62425,7 +62489,7 @@ var symbols = createSwarmTool({
62425
62489
  }, null, 2);
62426
62490
  }
62427
62491
  const cwd = directory;
62428
- const ext = path52.extname(file3);
62492
+ const ext = path53.extname(file3);
62429
62493
  if (containsControlCharacters(file3)) {
62430
62494
  return JSON.stringify({
62431
62495
  file: file3,
@@ -62497,7 +62561,7 @@ init_dist();
62497
62561
  init_utils();
62498
62562
  init_create_tool();
62499
62563
  import * as fs40 from "fs";
62500
- import * as path53 from "path";
62564
+ import * as path54 from "path";
62501
62565
  var MAX_TEXT_LENGTH = 200;
62502
62566
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
62503
62567
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -62568,9 +62632,9 @@ function validatePathsInput(paths, cwd) {
62568
62632
  return { error: "paths contains path traversal", resolvedPath: null };
62569
62633
  }
62570
62634
  try {
62571
- const resolvedPath = path53.resolve(paths);
62572
- const normalizedCwd = path53.resolve(cwd);
62573
- const normalizedResolved = path53.resolve(resolvedPath);
62635
+ const resolvedPath = path54.resolve(paths);
62636
+ const normalizedCwd = path54.resolve(cwd);
62637
+ const normalizedResolved = path54.resolve(resolvedPath);
62574
62638
  if (!normalizedResolved.startsWith(normalizedCwd)) {
62575
62639
  return {
62576
62640
  error: "paths must be within the current working directory",
@@ -62586,7 +62650,7 @@ function validatePathsInput(paths, cwd) {
62586
62650
  }
62587
62651
  }
62588
62652
  function isSupportedExtension(filePath) {
62589
- const ext = path53.extname(filePath).toLowerCase();
62653
+ const ext = path54.extname(filePath).toLowerCase();
62590
62654
  return SUPPORTED_EXTENSIONS2.has(ext);
62591
62655
  }
62592
62656
  function findSourceFiles2(dir, files = []) {
@@ -62601,7 +62665,7 @@ function findSourceFiles2(dir, files = []) {
62601
62665
  if (SKIP_DIRECTORIES3.has(entry)) {
62602
62666
  continue;
62603
62667
  }
62604
- const fullPath = path53.join(dir, entry);
62668
+ const fullPath = path54.join(dir, entry);
62605
62669
  let stat2;
62606
62670
  try {
62607
62671
  stat2 = fs40.statSync(fullPath);
@@ -62713,7 +62777,7 @@ var todo_extract = createSwarmTool({
62713
62777
  filesToScan.push(scanPath);
62714
62778
  } else {
62715
62779
  const errorResult = {
62716
- error: `unsupported file extension: ${path53.extname(scanPath)}`,
62780
+ error: `unsupported file extension: ${path54.extname(scanPath)}`,
62717
62781
  total: 0,
62718
62782
  byPriority: { high: 0, medium: 0, low: 0 },
62719
62783
  entries: []
@@ -62759,14 +62823,14 @@ var todo_extract = createSwarmTool({
62759
62823
  init_tool();
62760
62824
  init_schema();
62761
62825
  import * as fs42 from "fs";
62762
- import * as path55 from "path";
62826
+ import * as path56 from "path";
62763
62827
 
62764
62828
  // src/hooks/diff-scope.ts
62765
62829
  import * as fs41 from "fs";
62766
- import * as path54 from "path";
62830
+ import * as path55 from "path";
62767
62831
  function getDeclaredScope(taskId, directory) {
62768
62832
  try {
62769
- const planPath = path54.join(directory, ".swarm", "plan.json");
62833
+ const planPath = path55.join(directory, ".swarm", "plan.json");
62770
62834
  if (!fs41.existsSync(planPath))
62771
62835
  return null;
62772
62836
  const raw = fs41.readFileSync(planPath, "utf-8");
@@ -62881,7 +62945,7 @@ var TIER_3_PATTERNS = [
62881
62945
  ];
62882
62946
  function matchesTier3Pattern(files) {
62883
62947
  for (const file3 of files) {
62884
- const fileName = path55.basename(file3);
62948
+ const fileName = path56.basename(file3);
62885
62949
  for (const pattern of TIER_3_PATTERNS) {
62886
62950
  if (pattern.test(fileName)) {
62887
62951
  return true;
@@ -62903,7 +62967,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62903
62967
  if (hasActiveTurboMode2()) {
62904
62968
  const resolvedDir2 = workingDirectory ?? process.cwd();
62905
62969
  try {
62906
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
62970
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62907
62971
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62908
62972
  const plan = JSON.parse(planRaw);
62909
62973
  for (const planPhase of plan.phases ?? []) {
@@ -62923,7 +62987,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62923
62987
  }
62924
62988
  const resolvedDir = workingDirectory ?? process.cwd();
62925
62989
  try {
62926
- const evidencePath = path55.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62990
+ const evidencePath = path56.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62927
62991
  const raw = fs42.readFileSync(evidencePath, "utf-8");
62928
62992
  const evidence = JSON.parse(raw);
62929
62993
  if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
@@ -62964,7 +63028,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62964
63028
  }
62965
63029
  try {
62966
63030
  const resolvedDir2 = workingDirectory ?? process.cwd();
62967
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
63031
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62968
63032
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62969
63033
  const plan = JSON.parse(planRaw);
62970
63034
  for (const planPhase of plan.phases ?? []) {
@@ -63146,8 +63210,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63146
63210
  };
63147
63211
  }
63148
63212
  }
63149
- normalizedDir = path55.normalize(args2.working_directory);
63150
- const pathParts = normalizedDir.split(path55.sep);
63213
+ normalizedDir = path56.normalize(args2.working_directory);
63214
+ const pathParts = normalizedDir.split(path56.sep);
63151
63215
  if (pathParts.includes("..")) {
63152
63216
  return {
63153
63217
  success: false,
@@ -63157,10 +63221,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63157
63221
  ]
63158
63222
  };
63159
63223
  }
63160
- const resolvedDir = path55.resolve(normalizedDir);
63224
+ const resolvedDir = path56.resolve(normalizedDir);
63161
63225
  try {
63162
63226
  const realPath = fs42.realpathSync(resolvedDir);
63163
- const planPath = path55.join(realPath, ".swarm", "plan.json");
63227
+ const planPath = path56.join(realPath, ".swarm", "plan.json");
63164
63228
  if (!fs42.existsSync(planPath)) {
63165
63229
  return {
63166
63230
  success: false,
@@ -63355,7 +63419,7 @@ var OpenCodeSwarm = async (ctx) => {
63355
63419
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
63356
63420
  preflightTriggerManager = new PTM(automationConfig);
63357
63421
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
63358
- const swarmDir = path56.resolve(ctx.directory, ".swarm");
63422
+ const swarmDir = path57.resolve(ctx.directory, ".swarm");
63359
63423
  statusArtifact = new ASA(swarmDir);
63360
63424
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
63361
63425
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {