opencode-swarm 6.29.2 → 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:
@@ -45541,6 +45575,58 @@ async function checkSteeringDirectives(directory) {
45541
45575
  };
45542
45576
  }
45543
45577
  }
45578
+ async function checkCurator(directory) {
45579
+ try {
45580
+ const config3 = loadPluginConfig(directory);
45581
+ if (!config3.curator?.enabled) {
45582
+ return {
45583
+ name: "Curator",
45584
+ status: "\u2705",
45585
+ detail: "Disabled (enable via curator.enabled)"
45586
+ };
45587
+ }
45588
+ const summaryPath = path16.join(directory, ".swarm/curator-summary.json");
45589
+ if (!existsSync6(summaryPath)) {
45590
+ return {
45591
+ name: "Curator",
45592
+ status: "\u2705",
45593
+ detail: "Enabled, no summary yet (waiting for first phase)"
45594
+ };
45595
+ }
45596
+ try {
45597
+ const content = readFileSync4(summaryPath, "utf-8");
45598
+ const parsed = JSON.parse(content);
45599
+ if (typeof parsed.schema_version !== "number" || parsed.schema_version !== 1) {
45600
+ return {
45601
+ name: "Curator",
45602
+ status: "\u274C",
45603
+ detail: `curator-summary.json has invalid schema_version (expected 1, got ${JSON.stringify(parsed.schema_version)})`
45604
+ };
45605
+ }
45606
+ const phaseInfo = parsed.last_phase_covered !== undefined ? `phase ${parsed.last_phase_covered}` : "unknown phase";
45607
+ const timeInfo = parsed.last_updated ? `, updated ${parsed.last_updated}` : "";
45608
+ return {
45609
+ name: "Curator",
45610
+ status: "\u2705",
45611
+ detail: `Summary present \u2014 covering ${phaseInfo}${timeInfo}`
45612
+ };
45613
+ } catch (err2) {
45614
+ const message = err2 instanceof Error ? err2.message : "Unknown error";
45615
+ return {
45616
+ name: "Curator",
45617
+ status: "\u274C",
45618
+ detail: `curator-summary.json is corrupt or invalid: ${message}`
45619
+ };
45620
+ }
45621
+ } catch (err2) {
45622
+ const message = err2 instanceof Error ? err2.message : "Unknown error";
45623
+ return {
45624
+ name: "Curator",
45625
+ status: "\u274C",
45626
+ detail: `Could not check curator state: ${message}`
45627
+ };
45628
+ }
45629
+ }
45544
45630
  async function getDiagnoseData(directory) {
45545
45631
  const checks5 = [];
45546
45632
  const plan = await loadPlanJsonOnly(directory);
@@ -45645,6 +45731,7 @@ async function getDiagnoseData(directory) {
45645
45731
  checks5.push(await checkCheckpointManifest(directory));
45646
45732
  checks5.push(await checkEventStreamIntegrity(directory));
45647
45733
  checks5.push(await checkSteeringDirectives(directory));
45734
+ checks5.push(await checkCurator(directory));
45648
45735
  const passCount = checks5.filter((c) => c.status === "\u2705").length;
45649
45736
  const totalCount = checks5.length;
45650
45737
  const allPassed = passCount === totalCount;
@@ -50713,6 +50800,7 @@ function consolidateSystemMessages(messages) {
50713
50800
  // src/hooks/phase-monitor.ts
50714
50801
  init_schema();
50715
50802
  init_manager2();
50803
+ import * as path31 from "path";
50716
50804
  init_utils2();
50717
50805
  function createPhaseMonitorHook(directory, preflightManager, curatorRunner = runCuratorInit) {
50718
50806
  let lastKnownPhase = null;
@@ -50728,7 +50816,13 @@ function createPhaseMonitorHook(directory, preflightManager, curatorRunner = run
50728
50816
  const { config: config3 } = loadPluginConfigWithMeta2(directory);
50729
50817
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
50730
50818
  if (curatorConfig.enabled && curatorConfig.init_enabled) {
50731
- 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
+ }
50732
50826
  }
50733
50827
  } catch {}
50734
50828
  return;
@@ -50844,7 +50938,7 @@ import * as fs19 from "fs";
50844
50938
  init_utils2();
50845
50939
  init_manager2();
50846
50940
  import * as fs18 from "fs";
50847
- import * as path31 from "path";
50941
+ import * as path32 from "path";
50848
50942
  var DEFAULT_DRIFT_CONFIG = {
50849
50943
  staleThresholdPhases: 1,
50850
50944
  detectContradictions: true,
@@ -50998,7 +51092,7 @@ async function analyzeDecisionDrift(directory, config3 = {}) {
50998
51092
  currentPhase = legacyPhase;
50999
51093
  }
51000
51094
  }
51001
- const contextPath = path31.join(directory, ".swarm", "context.md");
51095
+ const contextPath = path32.join(directory, ".swarm", "context.md");
51002
51096
  let contextContent = "";
51003
51097
  try {
51004
51098
  if (fs18.existsSync(contextPath)) {
@@ -52463,7 +52557,7 @@ function createDarkMatterDetectorHook(directory) {
52463
52557
 
52464
52558
  // src/hooks/incremental-verify.ts
52465
52559
  import * as fs20 from "fs";
52466
- import * as path32 from "path";
52560
+ import * as path33 from "path";
52467
52561
 
52468
52562
  // src/hooks/spawn-helper.ts
52469
52563
  import { spawn } from "child_process";
@@ -52519,7 +52613,7 @@ function spawnAsync(command, cwd, timeoutMs) {
52519
52613
  // src/hooks/incremental-verify.ts
52520
52614
  var emittedSkipAdvisories = new Set;
52521
52615
  function detectTypecheckCommand(projectDir) {
52522
- const pkgPath = path32.join(projectDir, "package.json");
52616
+ const pkgPath = path33.join(projectDir, "package.json");
52523
52617
  if (fs20.existsSync(pkgPath)) {
52524
52618
  try {
52525
52619
  const pkg = JSON.parse(fs20.readFileSync(pkgPath, "utf8"));
@@ -52535,21 +52629,22 @@ function detectTypecheckCommand(projectDir) {
52535
52629
  ...pkg.dependencies,
52536
52630
  ...pkg.devDependencies
52537
52631
  };
52538
- if (!deps?.typescript && !fs20.existsSync(path32.join(projectDir, "tsconfig.json"))) {
52539
- return null;
52632
+ if (!deps?.typescript && !fs20.existsSync(path33.join(projectDir, "tsconfig.json"))) {}
52633
+ const hasTSMarkers = deps?.typescript || fs20.existsSync(path33.join(projectDir, "tsconfig.json"));
52634
+ if (hasTSMarkers) {
52635
+ return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
52540
52636
  }
52541
- return { command: ["npx", "tsc", "--noEmit"], language: "typescript" };
52542
52637
  } catch {
52543
52638
  return null;
52544
52639
  }
52545
52640
  }
52546
- if (fs20.existsSync(path32.join(projectDir, "go.mod"))) {
52641
+ if (fs20.existsSync(path33.join(projectDir, "go.mod"))) {
52547
52642
  return { command: ["go", "vet", "./..."], language: "go" };
52548
52643
  }
52549
- if (fs20.existsSync(path32.join(projectDir, "Cargo.toml"))) {
52644
+ if (fs20.existsSync(path33.join(projectDir, "Cargo.toml"))) {
52550
52645
  return { command: ["cargo", "check"], language: "rust" };
52551
52646
  }
52552
- 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"))) {
52553
52648
  return { command: null, language: "python" };
52554
52649
  }
52555
52650
  try {
@@ -52620,7 +52715,7 @@ ${errorSummary}`);
52620
52715
  // src/hooks/knowledge-reader.ts
52621
52716
  import { existsSync as existsSync19 } from "fs";
52622
52717
  import { mkdir as mkdir4, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
52623
- import * as path33 from "path";
52718
+ import * as path34 from "path";
52624
52719
  var JACCARD_THRESHOLD = 0.6;
52625
52720
  var HIVE_TIER_BOOST = 0.05;
52626
52721
  var SAME_PROJECT_PENALTY = -0.05;
@@ -52668,7 +52763,7 @@ function inferCategoriesFromPhase(phaseDescription) {
52668
52763
  return ["process", "tooling"];
52669
52764
  }
52670
52765
  async function recordLessonsShown(directory, lessonIds, currentPhase) {
52671
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52766
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52672
52767
  try {
52673
52768
  let shownData = {};
52674
52769
  if (existsSync19(shownFile)) {
@@ -52676,7 +52771,7 @@ async function recordLessonsShown(directory, lessonIds, currentPhase) {
52676
52771
  shownData = JSON.parse(content);
52677
52772
  }
52678
52773
  shownData[currentPhase] = lessonIds;
52679
- await mkdir4(path33.dirname(shownFile), { recursive: true });
52774
+ await mkdir4(path34.dirname(shownFile), { recursive: true });
52680
52775
  await writeFile4(shownFile, JSON.stringify(shownData, null, 2), "utf-8");
52681
52776
  } catch {
52682
52777
  console.warn("[swarm] Knowledge: failed to record shown lessons");
@@ -52771,7 +52866,7 @@ async function readMergedKnowledge(directory, config3, context) {
52771
52866
  return topN;
52772
52867
  }
52773
52868
  async function updateRetrievalOutcome(directory, phaseInfo, phaseSucceeded) {
52774
- const shownFile = path33.join(directory, ".swarm", ".knowledge-shown.json");
52869
+ const shownFile = path34.join(directory, ".swarm", ".knowledge-shown.json");
52775
52870
  try {
52776
52871
  if (!existsSync19(shownFile)) {
52777
52872
  return;
@@ -53244,10 +53339,10 @@ Use this data to avoid repeating known failure patterns.`;
53244
53339
  init_event_bus();
53245
53340
  init_utils2();
53246
53341
  import * as fs21 from "fs";
53247
- import * as path34 from "path";
53342
+ import * as path35 from "path";
53248
53343
  var DRIFT_REPORT_PREFIX = "drift-report-phase-";
53249
53344
  async function readPriorDriftReports(directory) {
53250
- const swarmDir = path34.join(directory, ".swarm");
53345
+ const swarmDir = path35.join(directory, ".swarm");
53251
53346
  const entries = await fs21.promises.readdir(swarmDir).catch(() => null);
53252
53347
  if (entries === null)
53253
53348
  return [];
@@ -53274,7 +53369,7 @@ async function readPriorDriftReports(directory) {
53274
53369
  async function writeDriftReport(directory, report) {
53275
53370
  const filename = `${DRIFT_REPORT_PREFIX}${report.phase}.json`;
53276
53371
  const filePath = validateSwarmPath(directory, filename);
53277
- const swarmDir = path34.dirname(filePath);
53372
+ const swarmDir = path35.dirname(filePath);
53278
53373
  await fs21.promises.mkdir(swarmDir, { recursive: true });
53279
53374
  try {
53280
53375
  await fs21.promises.writeFile(filePath, JSON.stringify(report, null, 2), "utf-8");
@@ -53428,7 +53523,7 @@ function isOrchestratorAgent(agentName) {
53428
53523
  function injectKnowledgeMessage(output, text) {
53429
53524
  if (!output.messages)
53430
53525
  return;
53431
- 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>")));
53432
53527
  if (alreadyInjected)
53433
53528
  return;
53434
53529
  const systemIdx = output.messages.findIndex((m) => m.info?.role === "system");
@@ -53474,8 +53569,33 @@ function createKnowledgeInjectorHook(directory, config3) {
53474
53569
  currentPhase: phaseDescription
53475
53570
  };
53476
53571
  const entries = await readMergedKnowledge(directory, config3, context);
53477
- 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);
53478
53597
  return;
53598
+ }
53479
53599
  const runMemory = await getRunMemorySummary(directory);
53480
53600
  const lines = entries.map((entry) => {
53481
53601
  const stars = formatStars(entry.confidence);
@@ -53494,13 +53614,15 @@ function createKnowledgeInjectorHook(directory, config3) {
53494
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."
53495
53615
  ].join(`
53496
53616
  `);
53617
+ let injectionText = cachedInjectionText ? `${cachedInjectionText}
53618
+
53619
+ ${knowledgeSection}` : knowledgeSection;
53497
53620
  if (runMemory) {
53498
- cachedInjectionText = `${runMemory}
53621
+ injectionText = `${runMemory}
53499
53622
 
53500
- ${knowledgeSection}`;
53501
- } else {
53502
- cachedInjectionText = knowledgeSection;
53623
+ ${injectionText}`;
53503
53624
  }
53625
+ cachedInjectionText = injectionText;
53504
53626
  const rejected = await readRejectedLessons(directory);
53505
53627
  if (rejected.length > 0) {
53506
53628
  const recentRejected = rejected.slice(-3);
@@ -53511,25 +53633,13 @@ ${knowledgeSection}`;
53511
53633
  ` + rejectedLines.join(`
53512
53634
  `);
53513
53635
  }
53514
- try {
53515
- const driftReports = await readPriorDriftReports(directory);
53516
- if (driftReports.length > 0 && cachedInjectionText !== null) {
53517
- const latestReport = driftReports[driftReports.length - 1];
53518
- const driftText = buildDriftInjectionText(latestReport, 500);
53519
- if (driftText) {
53520
- cachedInjectionText = `${driftText}
53521
-
53522
- ${cachedInjectionText}`;
53523
- }
53524
- }
53525
- } catch {}
53526
53636
  injectKnowledgeMessage(output, cachedInjectionText);
53527
53637
  });
53528
53638
  }
53529
53639
 
53530
53640
  // src/hooks/slop-detector.ts
53531
53641
  import * as fs22 from "fs";
53532
- import * as path35 from "path";
53642
+ import * as path36 from "path";
53533
53643
  var WRITE_EDIT_TOOLS = new Set([
53534
53644
  "write",
53535
53645
  "edit",
@@ -53579,7 +53689,7 @@ function walkFiles(dir, exts, deadline) {
53579
53689
  break;
53580
53690
  if (entry.isSymbolicLink())
53581
53691
  continue;
53582
- const full = path35.join(dir, entry.name);
53692
+ const full = path36.join(dir, entry.name);
53583
53693
  if (entry.isDirectory()) {
53584
53694
  if (entry.name === "node_modules" || entry.name === ".git")
53585
53695
  continue;
@@ -53594,7 +53704,7 @@ function walkFiles(dir, exts, deadline) {
53594
53704
  return results;
53595
53705
  }
53596
53706
  function checkDeadExports(content, projectDir, startTime) {
53597
- const hasPackageJson = fs22.existsSync(path35.join(projectDir, "package.json"));
53707
+ const hasPackageJson = fs22.existsSync(path36.join(projectDir, "package.json"));
53598
53708
  if (!hasPackageJson)
53599
53709
  return null;
53600
53710
  const exportMatches = content.matchAll(/^\+(?:export)\s+(?:function|class|const|type|interface)\s+(\w{3,})/gm);
@@ -53744,7 +53854,7 @@ init_config_doctor();
53744
53854
 
53745
53855
  // src/session/snapshot-reader.ts
53746
53856
  init_utils2();
53747
- import path36 from "path";
53857
+ import path37 from "path";
53748
53858
  var VALID_TASK_WORKFLOW_STATES = [
53749
53859
  "idle",
53750
53860
  "coder_delegated",
@@ -53869,7 +53979,7 @@ function rehydrateState(snapshot) {
53869
53979
  async function reconcileTaskStatesFromPlan(directory) {
53870
53980
  let raw;
53871
53981
  try {
53872
- raw = await Bun.file(path36.join(directory, ".swarm/plan.json")).text();
53982
+ raw = await Bun.file(path37.join(directory, ".swarm/plan.json")).text();
53873
53983
  } catch {
53874
53984
  return;
53875
53985
  }
@@ -54092,7 +54202,7 @@ var build_check = createSwarmTool({
54092
54202
  init_dist();
54093
54203
  init_create_tool();
54094
54204
  import * as fs24 from "fs";
54095
- import * as path37 from "path";
54205
+ import * as path38 from "path";
54096
54206
  var EVIDENCE_DIR = ".swarm/evidence";
54097
54207
  var TASK_ID_PATTERN2 = /^\d+\.\d+(\.\d+)*$/;
54098
54208
  function isValidTaskId3(taskId) {
@@ -54109,9 +54219,9 @@ function isValidTaskId3(taskId) {
54109
54219
  return TASK_ID_PATTERN2.test(taskId);
54110
54220
  }
54111
54221
  function isPathWithinSwarm(filePath, workspaceRoot) {
54112
- const normalizedWorkspace = path37.resolve(workspaceRoot);
54113
- const swarmPath = path37.join(normalizedWorkspace, ".swarm", "evidence");
54114
- 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);
54115
54225
  return normalizedPath.startsWith(swarmPath);
54116
54226
  }
54117
54227
  function readEvidenceFile(evidencePath) {
@@ -54172,7 +54282,7 @@ var check_gate_status = createSwarmTool({
54172
54282
  };
54173
54283
  return JSON.stringify(errorResult, null, 2);
54174
54284
  }
54175
- const evidencePath = path37.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54285
+ const evidencePath = path38.join(directory, EVIDENCE_DIR, `${taskIdInput}.json`);
54176
54286
  if (!isPathWithinSwarm(evidencePath, directory)) {
54177
54287
  const errorResult = {
54178
54288
  taskId: taskIdInput,
@@ -54233,7 +54343,7 @@ init_tool();
54233
54343
  init_create_tool();
54234
54344
  import { spawnSync } from "child_process";
54235
54345
  import * as fs25 from "fs";
54236
- import * as path38 from "path";
54346
+ import * as path39 from "path";
54237
54347
  var CHECKPOINT_LOG_PATH = ".swarm/checkpoints.json";
54238
54348
  var MAX_LABEL_LENGTH = 100;
54239
54349
  var GIT_TIMEOUT_MS = 30000;
@@ -54284,7 +54394,7 @@ function validateLabel(label) {
54284
54394
  return null;
54285
54395
  }
54286
54396
  function getCheckpointLogPath(directory) {
54287
- return path38.join(directory, CHECKPOINT_LOG_PATH);
54397
+ return path39.join(directory, CHECKPOINT_LOG_PATH);
54288
54398
  }
54289
54399
  function readCheckpointLog(directory) {
54290
54400
  const logPath = getCheckpointLogPath(directory);
@@ -54302,7 +54412,7 @@ function readCheckpointLog(directory) {
54302
54412
  }
54303
54413
  function writeCheckpointLog(log2, directory) {
54304
54414
  const logPath = getCheckpointLogPath(directory);
54305
- const dir = path38.dirname(logPath);
54415
+ const dir = path39.dirname(logPath);
54306
54416
  if (!fs25.existsSync(dir)) {
54307
54417
  fs25.mkdirSync(dir, { recursive: true });
54308
54418
  }
@@ -54510,7 +54620,7 @@ var checkpoint = createSwarmTool({
54510
54620
  init_dist();
54511
54621
  init_create_tool();
54512
54622
  import * as fs26 from "fs";
54513
- import * as path39 from "path";
54623
+ import * as path40 from "path";
54514
54624
  var MAX_FILE_SIZE_BYTES2 = 256 * 1024;
54515
54625
  var DEFAULT_DAYS = 90;
54516
54626
  var DEFAULT_TOP_N = 20;
@@ -54654,7 +54764,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54654
54764
  const extSet = new Set(extensions.map((e) => e.startsWith(".") ? e : `.${e}`));
54655
54765
  const filteredChurn = new Map;
54656
54766
  for (const [file3, count] of churnMap) {
54657
- const ext = path39.extname(file3).toLowerCase();
54767
+ const ext = path40.extname(file3).toLowerCase();
54658
54768
  if (extSet.has(ext)) {
54659
54769
  filteredChurn.set(file3, count);
54660
54770
  }
@@ -54665,7 +54775,7 @@ async function analyzeHotspots(days, topN, extensions, directory) {
54665
54775
  for (const [file3, churnCount] of filteredChurn) {
54666
54776
  let fullPath = file3;
54667
54777
  if (!fs26.existsSync(fullPath)) {
54668
- fullPath = path39.join(cwd, file3);
54778
+ fullPath = path40.join(cwd, file3);
54669
54779
  }
54670
54780
  const complexity = getComplexityForFile(fullPath);
54671
54781
  if (complexity !== null) {
@@ -54813,7 +54923,7 @@ var complexity_hotspots = createSwarmTool({
54813
54923
  // src/tools/declare-scope.ts
54814
54924
  init_tool();
54815
54925
  import * as fs27 from "fs";
54816
- import * as path40 from "path";
54926
+ import * as path41 from "path";
54817
54927
  init_create_tool();
54818
54928
  function validateTaskIdFormat(taskId) {
54819
54929
  const taskIdPattern = /^\d+\.\d+(\.\d+)*$/;
@@ -54892,8 +55002,8 @@ async function executeDeclareScope(args2, fallbackDir) {
54892
55002
  };
54893
55003
  }
54894
55004
  }
54895
- normalizedDir = path40.normalize(args2.working_directory);
54896
- const pathParts = normalizedDir.split(path40.sep);
55005
+ normalizedDir = path41.normalize(args2.working_directory);
55006
+ const pathParts = normalizedDir.split(path41.sep);
54897
55007
  if (pathParts.includes("..")) {
54898
55008
  return {
54899
55009
  success: false,
@@ -54903,10 +55013,10 @@ async function executeDeclareScope(args2, fallbackDir) {
54903
55013
  ]
54904
55014
  };
54905
55015
  }
54906
- const resolvedDir = path40.resolve(normalizedDir);
55016
+ const resolvedDir = path41.resolve(normalizedDir);
54907
55017
  try {
54908
55018
  const realPath = fs27.realpathSync(resolvedDir);
54909
- const planPath2 = path40.join(realPath, ".swarm", "plan.json");
55019
+ const planPath2 = path41.join(realPath, ".swarm", "plan.json");
54910
55020
  if (!fs27.existsSync(planPath2)) {
54911
55021
  return {
54912
55022
  success: false,
@@ -54927,7 +55037,7 @@ async function executeDeclareScope(args2, fallbackDir) {
54927
55037
  }
54928
55038
  }
54929
55039
  const directory = normalizedDir ?? fallbackDir ?? process.cwd();
54930
- const planPath = path40.resolve(directory, ".swarm", "plan.json");
55040
+ const planPath = path41.resolve(directory, ".swarm", "plan.json");
54931
55041
  if (!fs27.existsSync(planPath)) {
54932
55042
  return {
54933
55043
  success: false,
@@ -55017,20 +55127,20 @@ function validateBase(base) {
55017
55127
  function validatePaths(paths) {
55018
55128
  if (!paths)
55019
55129
  return null;
55020
- for (const path41 of paths) {
55021
- if (!path41 || path41.length === 0) {
55130
+ for (const path42 of paths) {
55131
+ if (!path42 || path42.length === 0) {
55022
55132
  return "empty path not allowed";
55023
55133
  }
55024
- if (path41.length > MAX_PATH_LENGTH) {
55134
+ if (path42.length > MAX_PATH_LENGTH) {
55025
55135
  return `path exceeds maximum length of ${MAX_PATH_LENGTH}`;
55026
55136
  }
55027
- if (SHELL_METACHARACTERS2.test(path41)) {
55137
+ if (SHELL_METACHARACTERS2.test(path42)) {
55028
55138
  return "path contains shell metacharacters";
55029
55139
  }
55030
- if (path41.startsWith("-")) {
55140
+ if (path42.startsWith("-")) {
55031
55141
  return 'path cannot start with "-" (option-like arguments not allowed)';
55032
55142
  }
55033
- if (CONTROL_CHAR_PATTERN2.test(path41)) {
55143
+ if (CONTROL_CHAR_PATTERN2.test(path42)) {
55034
55144
  return "path contains control characters";
55035
55145
  }
55036
55146
  }
@@ -55110,8 +55220,8 @@ var diff = tool({
55110
55220
  if (parts2.length >= 3) {
55111
55221
  const additions = parseInt(parts2[0], 10) || 0;
55112
55222
  const deletions = parseInt(parts2[1], 10) || 0;
55113
- const path41 = parts2[2];
55114
- files.push({ path: path41, additions, deletions });
55223
+ const path42 = parts2[2];
55224
+ files.push({ path: path42, additions, deletions });
55115
55225
  }
55116
55226
  }
55117
55227
  const contractChanges = [];
@@ -55341,7 +55451,7 @@ Use these as DOMAIN values when delegating to @sme.`;
55341
55451
  init_dist();
55342
55452
  init_create_tool();
55343
55453
  import * as fs28 from "fs";
55344
- import * as path41 from "path";
55454
+ import * as path42 from "path";
55345
55455
  var MAX_FILE_SIZE_BYTES3 = 1024 * 1024;
55346
55456
  var MAX_EVIDENCE_FILES = 1000;
55347
55457
  var EVIDENCE_DIR2 = ".swarm/evidence";
@@ -55371,9 +55481,9 @@ function validateRequiredTypes(input) {
55371
55481
  return null;
55372
55482
  }
55373
55483
  function isPathWithinSwarm2(filePath, cwd) {
55374
- const normalizedCwd = path41.resolve(cwd);
55375
- const swarmPath = path41.join(normalizedCwd, ".swarm");
55376
- const normalizedPath = path41.resolve(filePath);
55484
+ const normalizedCwd = path42.resolve(cwd);
55485
+ const swarmPath = path42.join(normalizedCwd, ".swarm");
55486
+ const normalizedPath = path42.resolve(filePath);
55377
55487
  return normalizedPath.startsWith(swarmPath);
55378
55488
  }
55379
55489
  function parseCompletedTasks(planContent) {
@@ -55403,10 +55513,10 @@ function readEvidenceFiles(evidenceDir, _cwd) {
55403
55513
  if (!VALID_EVIDENCE_FILENAME_REGEX.test(filename)) {
55404
55514
  continue;
55405
55515
  }
55406
- const filePath = path41.join(evidenceDir, filename);
55516
+ const filePath = path42.join(evidenceDir, filename);
55407
55517
  try {
55408
- const resolvedPath = path41.resolve(filePath);
55409
- const evidenceDirResolved = path41.resolve(evidenceDir);
55518
+ const resolvedPath = path42.resolve(filePath);
55519
+ const evidenceDirResolved = path42.resolve(evidenceDir);
55410
55520
  if (!resolvedPath.startsWith(evidenceDirResolved)) {
55411
55521
  continue;
55412
55522
  }
@@ -55524,7 +55634,7 @@ var evidence_check = createSwarmTool({
55524
55634
  return JSON.stringify(errorResult, null, 2);
55525
55635
  }
55526
55636
  const requiredTypes = requiredTypesValue.split(",").map((t) => t.trim()).filter((t) => t.length > 0).map(normalizeEvidenceType);
55527
- const planPath = path41.join(cwd, PLAN_FILE);
55637
+ const planPath = path42.join(cwd, PLAN_FILE);
55528
55638
  if (!isPathWithinSwarm2(planPath, cwd)) {
55529
55639
  const errorResult = {
55530
55640
  error: "plan file path validation failed",
@@ -55556,7 +55666,7 @@ var evidence_check = createSwarmTool({
55556
55666
  };
55557
55667
  return JSON.stringify(result2, null, 2);
55558
55668
  }
55559
- const evidenceDir = path41.join(cwd, EVIDENCE_DIR2);
55669
+ const evidenceDir = path42.join(cwd, EVIDENCE_DIR2);
55560
55670
  const evidence = readEvidenceFiles(evidenceDir, cwd);
55561
55671
  const { tasksWithFullEvidence, gaps } = analyzeGaps(completedTasks, evidence, requiredTypes);
55562
55672
  const completeness = completedTasks.length > 0 ? Math.round(tasksWithFullEvidence.length / completedTasks.length * 100) / 100 : 1;
@@ -55574,7 +55684,7 @@ var evidence_check = createSwarmTool({
55574
55684
  init_tool();
55575
55685
  init_create_tool();
55576
55686
  import * as fs29 from "fs";
55577
- import * as path42 from "path";
55687
+ import * as path43 from "path";
55578
55688
  var EXT_MAP = {
55579
55689
  python: ".py",
55580
55690
  py: ".py",
@@ -55655,12 +55765,12 @@ var extract_code_blocks = createSwarmTool({
55655
55765
  if (prefix) {
55656
55766
  filename = `${prefix}_${filename}`;
55657
55767
  }
55658
- let filepath = path42.join(targetDir, filename);
55659
- const base = path42.basename(filepath, path42.extname(filepath));
55660
- 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);
55661
55771
  let counter = 1;
55662
55772
  while (fs29.existsSync(filepath)) {
55663
- filepath = path42.join(targetDir, `${base}_${counter}${ext}`);
55773
+ filepath = path43.join(targetDir, `${base}_${counter}${ext}`);
55664
55774
  counter++;
55665
55775
  }
55666
55776
  try {
@@ -55778,7 +55888,7 @@ var gitingest = tool({
55778
55888
  // src/tools/imports.ts
55779
55889
  init_dist();
55780
55890
  import * as fs30 from "fs";
55781
- import * as path43 from "path";
55891
+ import * as path44 from "path";
55782
55892
  var MAX_FILE_PATH_LENGTH2 = 500;
55783
55893
  var MAX_SYMBOL_LENGTH = 256;
55784
55894
  var MAX_FILE_SIZE_BYTES4 = 1024 * 1024;
@@ -55832,7 +55942,7 @@ function validateSymbolInput(symbol3) {
55832
55942
  return null;
55833
55943
  }
55834
55944
  function isBinaryFile2(filePath, buffer) {
55835
- const ext = path43.extname(filePath).toLowerCase();
55945
+ const ext = path44.extname(filePath).toLowerCase();
55836
55946
  if (ext === ".json" || ext === ".md" || ext === ".txt") {
55837
55947
  return false;
55838
55948
  }
@@ -55856,15 +55966,15 @@ function parseImports(content, targetFile, targetSymbol) {
55856
55966
  const imports = [];
55857
55967
  let _resolvedTarget;
55858
55968
  try {
55859
- _resolvedTarget = path43.resolve(targetFile);
55969
+ _resolvedTarget = path44.resolve(targetFile);
55860
55970
  } catch {
55861
55971
  _resolvedTarget = targetFile;
55862
55972
  }
55863
- const targetBasename = path43.basename(targetFile, path43.extname(targetFile));
55973
+ const targetBasename = path44.basename(targetFile, path44.extname(targetFile));
55864
55974
  const targetWithExt = targetFile;
55865
55975
  const targetWithoutExt = targetFile.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
55866
- const normalizedTargetWithExt = path43.normalize(targetWithExt).replace(/\\/g, "/");
55867
- const normalizedTargetWithoutExt = path43.normalize(targetWithoutExt).replace(/\\/g, "/");
55976
+ const normalizedTargetWithExt = path44.normalize(targetWithExt).replace(/\\/g, "/");
55977
+ const normalizedTargetWithoutExt = path44.normalize(targetWithoutExt).replace(/\\/g, "/");
55868
55978
  const importRegex = /import\s+(?:\{[\s\S]*?\}|(?:\*\s+as\s+\w+)|\w+)\s+from\s+['"`]([^'"`]+)['"`]|import\s+['"`]([^'"`]+)['"`]|require\s*\(\s*['"`]([^'"`]+)['"`]\s*\)/g;
55869
55979
  for (let match = importRegex.exec(content);match !== null; match = importRegex.exec(content)) {
55870
55980
  const modulePath = match[1] || match[2] || match[3];
@@ -55887,9 +55997,9 @@ function parseImports(content, targetFile, targetSymbol) {
55887
55997
  }
55888
55998
  const _normalizedModule = modulePath.replace(/^\.\//, "").replace(/^\.\.\\/, "../");
55889
55999
  let isMatch = false;
55890
- const _targetDir = path43.dirname(targetFile);
55891
- const targetExt = path43.extname(targetFile);
55892
- 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);
55893
56003
  const moduleNormalized = modulePath.replace(/\\/g, "/").replace(/^\.\//, "");
55894
56004
  const moduleName = modulePath.split(/[/\\]/).pop() || "";
55895
56005
  const moduleNameNoExt = moduleName.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/i, "");
@@ -55957,10 +56067,10 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
55957
56067
  entries.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
55958
56068
  for (const entry of entries) {
55959
56069
  if (SKIP_DIRECTORIES2.has(entry)) {
55960
- stats.skippedDirs.push(path43.join(dir, entry));
56070
+ stats.skippedDirs.push(path44.join(dir, entry));
55961
56071
  continue;
55962
56072
  }
55963
- const fullPath = path43.join(dir, entry);
56073
+ const fullPath = path44.join(dir, entry);
55964
56074
  let stat2;
55965
56075
  try {
55966
56076
  stat2 = fs30.statSync(fullPath);
@@ -55974,7 +56084,7 @@ function findSourceFiles(dir, files = [], stats = { skippedDirs: [], skippedFile
55974
56084
  if (stat2.isDirectory()) {
55975
56085
  findSourceFiles(fullPath, files, stats);
55976
56086
  } else if (stat2.isFile()) {
55977
- const ext = path43.extname(fullPath).toLowerCase();
56087
+ const ext = path44.extname(fullPath).toLowerCase();
55978
56088
  if (SUPPORTED_EXTENSIONS.includes(ext)) {
55979
56089
  files.push(fullPath);
55980
56090
  }
@@ -56030,7 +56140,7 @@ var imports = tool({
56030
56140
  return JSON.stringify(errorResult, null, 2);
56031
56141
  }
56032
56142
  try {
56033
- const targetFile = path43.resolve(file3);
56143
+ const targetFile = path44.resolve(file3);
56034
56144
  if (!fs30.existsSync(targetFile)) {
56035
56145
  const errorResult = {
56036
56146
  error: `target file not found: ${file3}`,
@@ -56052,7 +56162,7 @@ var imports = tool({
56052
56162
  };
56053
56163
  return JSON.stringify(errorResult, null, 2);
56054
56164
  }
56055
- const baseDir = path43.dirname(targetFile);
56165
+ const baseDir = path44.dirname(targetFile);
56056
56166
  const scanStats = {
56057
56167
  skippedDirs: [],
56058
56168
  skippedFiles: 0,
@@ -56374,7 +56484,7 @@ init_config();
56374
56484
  init_schema();
56375
56485
  init_manager();
56376
56486
  import * as fs31 from "fs";
56377
- import * as path44 from "path";
56487
+ import * as path45 from "path";
56378
56488
  init_utils2();
56379
56489
  init_create_tool();
56380
56490
  function safeWarn(message, error93) {
@@ -56569,7 +56679,7 @@ async function executePhaseComplete(args2, workingDirectory) {
56569
56679
  }
56570
56680
  if (retroFound && retroEntry?.lessons_learned && retroEntry.lessons_learned.length > 0) {
56571
56681
  try {
56572
- const projectName = path44.basename(dir);
56682
+ const projectName = path45.basename(dir);
56573
56683
  const knowledgeConfig = {
56574
56684
  enabled: true,
56575
56685
  swarm_max_entries: 100,
@@ -56598,12 +56708,17 @@ async function executePhaseComplete(args2, workingDirectory) {
56598
56708
  safeWarn("[phase_complete] Failed to curate lessons from retrospective:", error93);
56599
56709
  }
56600
56710
  }
56711
+ let complianceWarnings = [];
56601
56712
  try {
56602
56713
  const curatorConfig = CuratorConfigSchema.parse(config3.curator ?? {});
56603
56714
  if (curatorConfig.enabled && curatorConfig.phase_enabled) {
56604
56715
  const curatorResult = await runCuratorPhase(dir, phase, agentsDispatched, curatorConfig, {});
56605
56716
  await applyCuratorKnowledgeUpdates(dir, curatorResult.knowledge_recommendations, {});
56606
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
+ }
56607
56722
  }
56608
56723
  } catch (curatorError) {
56609
56724
  safeWarn("[phase_complete] Curator pipeline error (non-blocking):", curatorError);
@@ -56689,6 +56804,9 @@ async function executePhaseComplete(args2, workingDirectory) {
56689
56804
  warnings.push(`Warning: failed to update plan.json phase status: ${error93 instanceof Error ? error93.message : String(error93)}`);
56690
56805
  }
56691
56806
  }
56807
+ if (complianceWarnings.length > 0) {
56808
+ warnings.push(`Curator compliance: ${complianceWarnings.join("; ")}`);
56809
+ }
56692
56810
  const result = {
56693
56811
  success: success3,
56694
56812
  phase,
@@ -56733,7 +56851,7 @@ init_discovery();
56733
56851
  init_utils();
56734
56852
  init_create_tool();
56735
56853
  import * as fs32 from "fs";
56736
- import * as path45 from "path";
56854
+ import * as path46 from "path";
56737
56855
  var MAX_OUTPUT_BYTES5 = 52428800;
56738
56856
  var AUDIT_TIMEOUT_MS = 120000;
56739
56857
  function isValidEcosystem(value) {
@@ -56751,16 +56869,16 @@ function validateArgs3(args2) {
56751
56869
  function detectEcosystems(directory) {
56752
56870
  const ecosystems = [];
56753
56871
  const cwd = directory;
56754
- if (fs32.existsSync(path45.join(cwd, "package.json"))) {
56872
+ if (fs32.existsSync(path46.join(cwd, "package.json"))) {
56755
56873
  ecosystems.push("npm");
56756
56874
  }
56757
- 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"))) {
56758
56876
  ecosystems.push("pip");
56759
56877
  }
56760
- if (fs32.existsSync(path45.join(cwd, "Cargo.toml"))) {
56878
+ if (fs32.existsSync(path46.join(cwd, "Cargo.toml"))) {
56761
56879
  ecosystems.push("cargo");
56762
56880
  }
56763
- if (fs32.existsSync(path45.join(cwd, "go.mod"))) {
56881
+ if (fs32.existsSync(path46.join(cwd, "go.mod"))) {
56764
56882
  ecosystems.push("go");
56765
56883
  }
56766
56884
  try {
@@ -56769,10 +56887,10 @@ function detectEcosystems(directory) {
56769
56887
  ecosystems.push("dotnet");
56770
56888
  }
56771
56889
  } catch {}
56772
- 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"))) {
56773
56891
  ecosystems.push("ruby");
56774
56892
  }
56775
- if (fs32.existsSync(path45.join(cwd, "pubspec.yaml"))) {
56893
+ if (fs32.existsSync(path46.join(cwd, "pubspec.yaml"))) {
56776
56894
  ecosystems.push("dart");
56777
56895
  }
56778
56896
  return ecosystems;
@@ -57835,7 +57953,7 @@ var SUPPORTED_PARSER_EXTENSIONS = new Set([
57835
57953
  // src/tools/pre-check-batch.ts
57836
57954
  init_dist();
57837
57955
  import * as fs35 from "fs";
57838
- import * as path48 from "path";
57956
+ import * as path49 from "path";
57839
57957
 
57840
57958
  // node_modules/yocto-queue/index.js
57841
57959
  class Node2 {
@@ -58003,7 +58121,7 @@ init_manager();
58003
58121
 
58004
58122
  // src/quality/metrics.ts
58005
58123
  import * as fs33 from "fs";
58006
- import * as path46 from "path";
58124
+ import * as path47 from "path";
58007
58125
  var MAX_FILE_SIZE_BYTES5 = 256 * 1024;
58008
58126
  var MIN_DUPLICATION_LINES = 10;
58009
58127
  function estimateCyclomaticComplexity(content) {
@@ -58055,7 +58173,7 @@ async function computeComplexityDelta(files, workingDir) {
58055
58173
  let totalComplexity = 0;
58056
58174
  const analyzedFiles = [];
58057
58175
  for (const file3 of files) {
58058
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58176
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58059
58177
  if (!fs33.existsSync(fullPath)) {
58060
58178
  continue;
58061
58179
  }
@@ -58178,7 +58296,7 @@ function countGoExports(content) {
58178
58296
  function getExportCountForFile(filePath) {
58179
58297
  try {
58180
58298
  const content = fs33.readFileSync(filePath, "utf-8");
58181
- const ext = path46.extname(filePath).toLowerCase();
58299
+ const ext = path47.extname(filePath).toLowerCase();
58182
58300
  switch (ext) {
58183
58301
  case ".ts":
58184
58302
  case ".tsx":
@@ -58204,7 +58322,7 @@ async function computePublicApiDelta(files, workingDir) {
58204
58322
  let totalExports = 0;
58205
58323
  const analyzedFiles = [];
58206
58324
  for (const file3 of files) {
58207
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58325
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58208
58326
  if (!fs33.existsSync(fullPath)) {
58209
58327
  continue;
58210
58328
  }
@@ -58238,7 +58356,7 @@ async function computeDuplicationRatio(files, workingDir) {
58238
58356
  let duplicateLines = 0;
58239
58357
  const analyzedFiles = [];
58240
58358
  for (const file3 of files) {
58241
- const fullPath = path46.isAbsolute(file3) ? file3 : path46.join(workingDir, file3);
58359
+ const fullPath = path47.isAbsolute(file3) ? file3 : path47.join(workingDir, file3);
58242
58360
  if (!fs33.existsSync(fullPath)) {
58243
58361
  continue;
58244
58362
  }
@@ -58271,8 +58389,8 @@ function countCodeLines(content) {
58271
58389
  return lines.length;
58272
58390
  }
58273
58391
  function isTestFile(filePath) {
58274
- const basename8 = path46.basename(filePath);
58275
- const _ext = path46.extname(filePath).toLowerCase();
58392
+ const basename8 = path47.basename(filePath);
58393
+ const _ext = path47.extname(filePath).toLowerCase();
58276
58394
  const testPatterns = [
58277
58395
  ".test.",
58278
58396
  ".spec.",
@@ -58353,8 +58471,8 @@ function matchGlobSegment(globSegments, pathSegments) {
58353
58471
  }
58354
58472
  return gIndex === globSegments.length && pIndex === pathSegments.length;
58355
58473
  }
58356
- function matchesGlobSegment(path47, glob) {
58357
- const normalizedPath = path47.replace(/\\/g, "/");
58474
+ function matchesGlobSegment(path48, glob) {
58475
+ const normalizedPath = path48.replace(/\\/g, "/");
58358
58476
  const normalizedGlob = glob.replace(/\\/g, "/");
58359
58477
  if (normalizedPath.includes("//")) {
58360
58478
  return false;
@@ -58385,8 +58503,8 @@ function simpleGlobToRegex2(glob) {
58385
58503
  function hasGlobstar(glob) {
58386
58504
  return glob.includes("**");
58387
58505
  }
58388
- function globMatches(path47, glob) {
58389
- const normalizedPath = path47.replace(/\\/g, "/");
58506
+ function globMatches(path48, glob) {
58507
+ const normalizedPath = path48.replace(/\\/g, "/");
58390
58508
  if (!glob || glob === "") {
58391
58509
  if (normalizedPath.includes("//")) {
58392
58510
  return false;
@@ -58422,7 +58540,7 @@ function shouldExcludeFile(filePath, excludeGlobs) {
58422
58540
  async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58423
58541
  let testLines = 0;
58424
58542
  let codeLines = 0;
58425
- const srcDir = path46.join(workingDir, "src");
58543
+ const srcDir = path47.join(workingDir, "src");
58426
58544
  if (fs33.existsSync(srcDir)) {
58427
58545
  await scanDirectoryForLines(srcDir, enforceGlobs, excludeGlobs, false, (lines) => {
58428
58546
  codeLines += lines;
@@ -58430,14 +58548,14 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58430
58548
  }
58431
58549
  const possibleSrcDirs = ["lib", "app", "source", "core"];
58432
58550
  for (const dir of possibleSrcDirs) {
58433
- const dirPath = path46.join(workingDir, dir);
58551
+ const dirPath = path47.join(workingDir, dir);
58434
58552
  if (fs33.existsSync(dirPath)) {
58435
58553
  await scanDirectoryForLines(dirPath, enforceGlobs, excludeGlobs, false, (lines) => {
58436
58554
  codeLines += lines;
58437
58555
  });
58438
58556
  }
58439
58557
  }
58440
- const testsDir = path46.join(workingDir, "tests");
58558
+ const testsDir = path47.join(workingDir, "tests");
58441
58559
  if (fs33.existsSync(testsDir)) {
58442
58560
  await scanDirectoryForLines(testsDir, ["**"], ["node_modules", "dist"], true, (lines) => {
58443
58561
  testLines += lines;
@@ -58445,7 +58563,7 @@ async function computeTestToCodeRatio(workingDir, enforceGlobs, excludeGlobs) {
58445
58563
  }
58446
58564
  const possibleTestDirs = ["test", "__tests__", "specs"];
58447
58565
  for (const dir of possibleTestDirs) {
58448
- const dirPath = path46.join(workingDir, dir);
58566
+ const dirPath = path47.join(workingDir, dir);
58449
58567
  if (fs33.existsSync(dirPath) && dirPath !== testsDir) {
58450
58568
  await scanDirectoryForLines(dirPath, ["**"], ["node_modules", "dist"], true, (lines) => {
58451
58569
  testLines += lines;
@@ -58460,7 +58578,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58460
58578
  try {
58461
58579
  const entries = fs33.readdirSync(dirPath, { withFileTypes: true });
58462
58580
  for (const entry of entries) {
58463
- const fullPath = path46.join(dirPath, entry.name);
58581
+ const fullPath = path47.join(dirPath, entry.name);
58464
58582
  if (entry.isDirectory()) {
58465
58583
  if (entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === ".git") {
58466
58584
  continue;
@@ -58468,7 +58586,7 @@ async function scanDirectoryForLines(dirPath, includeGlobs, excludeGlobs, isTest
58468
58586
  await scanDirectoryForLines(fullPath, includeGlobs, excludeGlobs, isTestScan, callback);
58469
58587
  } else if (entry.isFile()) {
58470
58588
  const relativePath = fullPath.replace(`${process.cwd()}/`, "");
58471
- const ext = path46.extname(entry.name).toLowerCase();
58589
+ const ext = path47.extname(entry.name).toLowerCase();
58472
58590
  const validExts = [
58473
58591
  ".ts",
58474
58592
  ".tsx",
@@ -58719,7 +58837,7 @@ init_dist();
58719
58837
  init_manager();
58720
58838
  init_detector();
58721
58839
  import * as fs34 from "fs";
58722
- import * as path47 from "path";
58840
+ import * as path48 from "path";
58723
58841
  import { extname as extname9 } from "path";
58724
58842
 
58725
58843
  // src/sast/rules/c.ts
@@ -59682,7 +59800,7 @@ async function sastScan(input, directory, config3) {
59682
59800
  _filesSkipped++;
59683
59801
  continue;
59684
59802
  }
59685
- const resolvedPath = path47.isAbsolute(filePath) ? filePath : path47.resolve(directory, filePath);
59803
+ const resolvedPath = path48.isAbsolute(filePath) ? filePath : path48.resolve(directory, filePath);
59686
59804
  if (!fs34.existsSync(resolvedPath)) {
59687
59805
  _filesSkipped++;
59688
59806
  continue;
@@ -59881,18 +59999,18 @@ function validatePath(inputPath, baseDir, workspaceDir) {
59881
59999
  let resolved;
59882
60000
  const isWinAbs = isWindowsAbsolutePath(inputPath);
59883
60001
  if (isWinAbs) {
59884
- resolved = path48.win32.resolve(inputPath);
59885
- } else if (path48.isAbsolute(inputPath)) {
59886
- resolved = path48.resolve(inputPath);
60002
+ resolved = path49.win32.resolve(inputPath);
60003
+ } else if (path49.isAbsolute(inputPath)) {
60004
+ resolved = path49.resolve(inputPath);
59887
60005
  } else {
59888
- resolved = path48.resolve(baseDir, inputPath);
60006
+ resolved = path49.resolve(baseDir, inputPath);
59889
60007
  }
59890
- const workspaceResolved = path48.resolve(workspaceDir);
60008
+ const workspaceResolved = path49.resolve(workspaceDir);
59891
60009
  let relative5;
59892
60010
  if (isWinAbs) {
59893
- relative5 = path48.win32.relative(workspaceResolved, resolved);
60011
+ relative5 = path49.win32.relative(workspaceResolved, resolved);
59894
60012
  } else {
59895
- relative5 = path48.relative(workspaceResolved, resolved);
60013
+ relative5 = path49.relative(workspaceResolved, resolved);
59896
60014
  }
59897
60015
  if (relative5.startsWith("..")) {
59898
60016
  return "path traversal detected";
@@ -59953,13 +60071,13 @@ async function runLintWrapped(files, directory, _config) {
59953
60071
  }
59954
60072
  async function runLintOnFiles(linter, files, workspaceDir) {
59955
60073
  const isWindows = process.platform === "win32";
59956
- const binDir = path48.join(workspaceDir, "node_modules", ".bin");
60074
+ const binDir = path49.join(workspaceDir, "node_modules", ".bin");
59957
60075
  const validatedFiles = [];
59958
60076
  for (const file3 of files) {
59959
60077
  if (typeof file3 !== "string") {
59960
60078
  continue;
59961
60079
  }
59962
- const resolvedPath = path48.resolve(file3);
60080
+ const resolvedPath = path49.resolve(file3);
59963
60081
  const validationError = validatePath(resolvedPath, workspaceDir, workspaceDir);
59964
60082
  if (validationError) {
59965
60083
  continue;
@@ -59977,10 +60095,10 @@ async function runLintOnFiles(linter, files, workspaceDir) {
59977
60095
  }
59978
60096
  let command;
59979
60097
  if (linter === "biome") {
59980
- 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");
59981
60099
  command = [biomeBin, "check", ...validatedFiles];
59982
60100
  } else {
59983
- 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");
59984
60102
  command = [eslintBin, ...validatedFiles];
59985
60103
  }
59986
60104
  try {
@@ -60117,7 +60235,7 @@ async function runSecretscanWithFiles(files, directory) {
60117
60235
  skippedFiles++;
60118
60236
  continue;
60119
60237
  }
60120
- const resolvedPath = path48.resolve(file3);
60238
+ const resolvedPath = path49.resolve(file3);
60121
60239
  const validationError = validatePath(resolvedPath, directory, directory);
60122
60240
  if (validationError) {
60123
60241
  skippedFiles++;
@@ -60135,7 +60253,7 @@ async function runSecretscanWithFiles(files, directory) {
60135
60253
  };
60136
60254
  }
60137
60255
  for (const file3 of validatedFiles) {
60138
- const ext = path48.extname(file3).toLowerCase();
60256
+ const ext = path49.extname(file3).toLowerCase();
60139
60257
  if (DEFAULT_EXCLUDE_EXTENSIONS2.has(ext)) {
60140
60258
  skippedFiles++;
60141
60259
  continue;
@@ -60294,7 +60412,7 @@ async function runPreCheckBatch(input, workspaceDir) {
60294
60412
  warn(`pre_check_batch: Invalid file path: ${file3}`);
60295
60413
  continue;
60296
60414
  }
60297
- changedFiles.push(path48.resolve(directory, file3));
60415
+ changedFiles.push(path49.resolve(directory, file3));
60298
60416
  }
60299
60417
  if (changedFiles.length === 0) {
60300
60418
  warn("pre_check_batch: No valid files after validation, skipping all tools (fail-closed)");
@@ -60445,7 +60563,7 @@ var pre_check_batch = createSwarmTool({
60445
60563
  };
60446
60564
  return JSON.stringify(errorResult, null, 2);
60447
60565
  }
60448
- const resolvedDirectory = path48.resolve(typedArgs.directory);
60566
+ const resolvedDirectory = path49.resolve(typedArgs.directory);
60449
60567
  const workspaceAnchor = resolvedDirectory;
60450
60568
  const dirError = validateDirectory3(resolvedDirectory, workspaceAnchor);
60451
60569
  if (dirError) {
@@ -60553,7 +60671,7 @@ init_tool();
60553
60671
  init_manager2();
60554
60672
  init_create_tool();
60555
60673
  import * as fs36 from "fs";
60556
- import * as path49 from "path";
60674
+ import * as path50 from "path";
60557
60675
  function detectPlaceholderContent(args2) {
60558
60676
  const issues = [];
60559
60677
  const placeholderPattern = /^\[\w[\w\s]*\]$/;
@@ -60657,7 +60775,7 @@ async function executeSavePlan(args2, fallbackDir) {
60657
60775
  try {
60658
60776
  await savePlan(dir, plan);
60659
60777
  try {
60660
- const markerPath = path49.join(dir, ".swarm", ".plan-write-marker");
60778
+ const markerPath = path50.join(dir, ".swarm", ".plan-write-marker");
60661
60779
  const marker = JSON.stringify({
60662
60780
  source: "save_plan",
60663
60781
  timestamp: new Date().toISOString(),
@@ -60669,7 +60787,7 @@ async function executeSavePlan(args2, fallbackDir) {
60669
60787
  return {
60670
60788
  success: true,
60671
60789
  message: "Plan saved successfully",
60672
- plan_path: path49.join(dir, ".swarm", "plan.json"),
60790
+ plan_path: path50.join(dir, ".swarm", "plan.json"),
60673
60791
  phases_count: plan.phases.length,
60674
60792
  tasks_count: tasksCount
60675
60793
  };
@@ -60708,7 +60826,7 @@ var save_plan = createSwarmTool({
60708
60826
  init_dist();
60709
60827
  init_manager();
60710
60828
  import * as fs37 from "fs";
60711
- import * as path50 from "path";
60829
+ import * as path51 from "path";
60712
60830
 
60713
60831
  // src/sbom/detectors/index.ts
60714
60832
  init_utils();
@@ -61556,7 +61674,7 @@ function findManifestFiles(rootDir) {
61556
61674
  try {
61557
61675
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61558
61676
  for (const entry of entries) {
61559
- const fullPath = path50.join(dir, entry.name);
61677
+ const fullPath = path51.join(dir, entry.name);
61560
61678
  if (entry.name.startsWith(".") || entry.name === "node_modules" || entry.name === "dist" || entry.name === "build" || entry.name === "target") {
61561
61679
  continue;
61562
61680
  }
@@ -61565,7 +61683,7 @@ function findManifestFiles(rootDir) {
61565
61683
  } else if (entry.isFile()) {
61566
61684
  for (const pattern of patterns) {
61567
61685
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61568
- manifestFiles.push(path50.relative(rootDir, fullPath));
61686
+ manifestFiles.push(path51.relative(rootDir, fullPath));
61569
61687
  break;
61570
61688
  }
61571
61689
  }
@@ -61583,11 +61701,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61583
61701
  try {
61584
61702
  const entries = fs37.readdirSync(dir, { withFileTypes: true });
61585
61703
  for (const entry of entries) {
61586
- const fullPath = path50.join(dir, entry.name);
61704
+ const fullPath = path51.join(dir, entry.name);
61587
61705
  if (entry.isFile()) {
61588
61706
  for (const pattern of patterns) {
61589
61707
  if (simpleGlobToRegex(pattern).test(entry.name)) {
61590
- found.push(path50.relative(workingDir, fullPath));
61708
+ found.push(path51.relative(workingDir, fullPath));
61591
61709
  break;
61592
61710
  }
61593
61711
  }
@@ -61600,11 +61718,11 @@ function findManifestFilesInDirs(directories, workingDir) {
61600
61718
  function getDirectoriesFromChangedFiles(changedFiles, workingDir) {
61601
61719
  const dirs = new Set;
61602
61720
  for (const file3 of changedFiles) {
61603
- let currentDir = path50.dirname(file3);
61721
+ let currentDir = path51.dirname(file3);
61604
61722
  while (true) {
61605
- if (currentDir && currentDir !== "." && currentDir !== path50.sep) {
61606
- dirs.add(path50.join(workingDir, currentDir));
61607
- 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);
61608
61726
  if (parent === currentDir)
61609
61727
  break;
61610
61728
  currentDir = parent;
@@ -61688,7 +61806,7 @@ var sbom_generate = createSwarmTool({
61688
61806
  const changedFiles = obj.changed_files;
61689
61807
  const relativeOutputDir = obj.output_dir || DEFAULT_OUTPUT_DIR;
61690
61808
  const workingDir = directory;
61691
- const outputDir = path50.isAbsolute(relativeOutputDir) ? relativeOutputDir : path50.join(workingDir, relativeOutputDir);
61809
+ const outputDir = path51.isAbsolute(relativeOutputDir) ? relativeOutputDir : path51.join(workingDir, relativeOutputDir);
61692
61810
  let manifestFiles = [];
61693
61811
  if (scope === "all") {
61694
61812
  manifestFiles = findManifestFiles(workingDir);
@@ -61711,7 +61829,7 @@ var sbom_generate = createSwarmTool({
61711
61829
  const processedFiles = [];
61712
61830
  for (const manifestFile of manifestFiles) {
61713
61831
  try {
61714
- const fullPath = path50.isAbsolute(manifestFile) ? manifestFile : path50.join(workingDir, manifestFile);
61832
+ const fullPath = path51.isAbsolute(manifestFile) ? manifestFile : path51.join(workingDir, manifestFile);
61715
61833
  if (!fs37.existsSync(fullPath)) {
61716
61834
  continue;
61717
61835
  }
@@ -61728,7 +61846,7 @@ var sbom_generate = createSwarmTool({
61728
61846
  const bom = generateCycloneDX(allComponents);
61729
61847
  const bomJson = serializeCycloneDX(bom);
61730
61848
  const filename = generateSbomFilename();
61731
- const outputPath = path50.join(outputDir, filename);
61849
+ const outputPath = path51.join(outputDir, filename);
61732
61850
  fs37.writeFileSync(outputPath, bomJson, "utf-8");
61733
61851
  const verdict = processedFiles.length > 0 ? "pass" : "pass";
61734
61852
  try {
@@ -61772,7 +61890,7 @@ var sbom_generate = createSwarmTool({
61772
61890
  init_dist();
61773
61891
  init_create_tool();
61774
61892
  import * as fs38 from "fs";
61775
- import * as path51 from "path";
61893
+ import * as path52 from "path";
61776
61894
  var SPEC_CANDIDATES = [
61777
61895
  "openapi.json",
61778
61896
  "openapi.yaml",
@@ -61804,12 +61922,12 @@ function normalizePath2(p) {
61804
61922
  }
61805
61923
  function discoverSpecFile(cwd, specFileArg) {
61806
61924
  if (specFileArg) {
61807
- const resolvedPath = path51.resolve(cwd, specFileArg);
61808
- 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;
61809
61927
  if (!resolvedPath.startsWith(normalizedCwd) && resolvedPath !== cwd) {
61810
61928
  throw new Error("Invalid spec_file: path traversal detected");
61811
61929
  }
61812
- const ext = path51.extname(resolvedPath).toLowerCase();
61930
+ const ext = path52.extname(resolvedPath).toLowerCase();
61813
61931
  if (!ALLOWED_EXTENSIONS.includes(ext)) {
61814
61932
  throw new Error(`Invalid spec_file: must end in .json, .yaml, or .yml, got ${ext}`);
61815
61933
  }
@@ -61823,7 +61941,7 @@ function discoverSpecFile(cwd, specFileArg) {
61823
61941
  return resolvedPath;
61824
61942
  }
61825
61943
  for (const candidate of SPEC_CANDIDATES) {
61826
- const candidatePath = path51.resolve(cwd, candidate);
61944
+ const candidatePath = path52.resolve(cwd, candidate);
61827
61945
  if (fs38.existsSync(candidatePath)) {
61828
61946
  const stats = fs38.statSync(candidatePath);
61829
61947
  if (stats.size <= MAX_SPEC_SIZE) {
@@ -61835,7 +61953,7 @@ function discoverSpecFile(cwd, specFileArg) {
61835
61953
  }
61836
61954
  function parseSpec(specFile) {
61837
61955
  const content = fs38.readFileSync(specFile, "utf-8");
61838
- const ext = path51.extname(specFile).toLowerCase();
61956
+ const ext = path52.extname(specFile).toLowerCase();
61839
61957
  if (ext === ".json") {
61840
61958
  return parseJsonSpec(content);
61841
61959
  }
@@ -61911,7 +62029,7 @@ function extractRoutes(cwd) {
61911
62029
  return;
61912
62030
  }
61913
62031
  for (const entry of entries) {
61914
- const fullPath = path51.join(dir, entry.name);
62032
+ const fullPath = path52.join(dir, entry.name);
61915
62033
  if (entry.isSymbolicLink()) {
61916
62034
  continue;
61917
62035
  }
@@ -61921,7 +62039,7 @@ function extractRoutes(cwd) {
61921
62039
  }
61922
62040
  walkDir(fullPath);
61923
62041
  } else if (entry.isFile()) {
61924
- const ext = path51.extname(entry.name).toLowerCase();
62042
+ const ext = path52.extname(entry.name).toLowerCase();
61925
62043
  const baseName = entry.name.toLowerCase();
61926
62044
  if (![".ts", ".js", ".mjs"].includes(ext)) {
61927
62045
  continue;
@@ -62091,7 +62209,7 @@ init_secretscan();
62091
62209
  init_tool();
62092
62210
  init_create_tool();
62093
62211
  import * as fs39 from "fs";
62094
- import * as path52 from "path";
62212
+ import * as path53 from "path";
62095
62213
  var MAX_FILE_SIZE_BYTES7 = 1024 * 1024;
62096
62214
  var WINDOWS_RESERVED_NAMES = /^(con|prn|aux|nul|com[1-9]|lpt[1-9])(\.|:|$)/i;
62097
62215
  function containsControlCharacters(str) {
@@ -62120,11 +62238,11 @@ function containsWindowsAttacks(str) {
62120
62238
  }
62121
62239
  function isPathInWorkspace(filePath, workspace) {
62122
62240
  try {
62123
- const resolvedPath = path52.resolve(workspace, filePath);
62241
+ const resolvedPath = path53.resolve(workspace, filePath);
62124
62242
  const realWorkspace = fs39.realpathSync(workspace);
62125
62243
  const realResolvedPath = fs39.realpathSync(resolvedPath);
62126
- const relativePath = path52.relative(realWorkspace, realResolvedPath);
62127
- if (relativePath.startsWith("..") || path52.isAbsolute(relativePath)) {
62244
+ const relativePath = path53.relative(realWorkspace, realResolvedPath);
62245
+ if (relativePath.startsWith("..") || path53.isAbsolute(relativePath)) {
62128
62246
  return false;
62129
62247
  }
62130
62248
  return true;
@@ -62136,7 +62254,7 @@ function validatePathForRead(filePath, workspace) {
62136
62254
  return isPathInWorkspace(filePath, workspace);
62137
62255
  }
62138
62256
  function extractTSSymbols(filePath, cwd) {
62139
- const fullPath = path52.join(cwd, filePath);
62257
+ const fullPath = path53.join(cwd, filePath);
62140
62258
  if (!validatePathForRead(fullPath, cwd)) {
62141
62259
  return [];
62142
62260
  }
@@ -62288,7 +62406,7 @@ function extractTSSymbols(filePath, cwd) {
62288
62406
  });
62289
62407
  }
62290
62408
  function extractPythonSymbols(filePath, cwd) {
62291
- const fullPath = path52.join(cwd, filePath);
62409
+ const fullPath = path53.join(cwd, filePath);
62292
62410
  if (!validatePathForRead(fullPath, cwd)) {
62293
62411
  return [];
62294
62412
  }
@@ -62371,7 +62489,7 @@ var symbols = createSwarmTool({
62371
62489
  }, null, 2);
62372
62490
  }
62373
62491
  const cwd = directory;
62374
- const ext = path52.extname(file3);
62492
+ const ext = path53.extname(file3);
62375
62493
  if (containsControlCharacters(file3)) {
62376
62494
  return JSON.stringify({
62377
62495
  file: file3,
@@ -62443,7 +62561,7 @@ init_dist();
62443
62561
  init_utils();
62444
62562
  init_create_tool();
62445
62563
  import * as fs40 from "fs";
62446
- import * as path53 from "path";
62564
+ import * as path54 from "path";
62447
62565
  var MAX_TEXT_LENGTH = 200;
62448
62566
  var MAX_FILE_SIZE_BYTES8 = 1024 * 1024;
62449
62567
  var SUPPORTED_EXTENSIONS2 = new Set([
@@ -62514,9 +62632,9 @@ function validatePathsInput(paths, cwd) {
62514
62632
  return { error: "paths contains path traversal", resolvedPath: null };
62515
62633
  }
62516
62634
  try {
62517
- const resolvedPath = path53.resolve(paths);
62518
- const normalizedCwd = path53.resolve(cwd);
62519
- const normalizedResolved = path53.resolve(resolvedPath);
62635
+ const resolvedPath = path54.resolve(paths);
62636
+ const normalizedCwd = path54.resolve(cwd);
62637
+ const normalizedResolved = path54.resolve(resolvedPath);
62520
62638
  if (!normalizedResolved.startsWith(normalizedCwd)) {
62521
62639
  return {
62522
62640
  error: "paths must be within the current working directory",
@@ -62532,7 +62650,7 @@ function validatePathsInput(paths, cwd) {
62532
62650
  }
62533
62651
  }
62534
62652
  function isSupportedExtension(filePath) {
62535
- const ext = path53.extname(filePath).toLowerCase();
62653
+ const ext = path54.extname(filePath).toLowerCase();
62536
62654
  return SUPPORTED_EXTENSIONS2.has(ext);
62537
62655
  }
62538
62656
  function findSourceFiles2(dir, files = []) {
@@ -62547,7 +62665,7 @@ function findSourceFiles2(dir, files = []) {
62547
62665
  if (SKIP_DIRECTORIES3.has(entry)) {
62548
62666
  continue;
62549
62667
  }
62550
- const fullPath = path53.join(dir, entry);
62668
+ const fullPath = path54.join(dir, entry);
62551
62669
  let stat2;
62552
62670
  try {
62553
62671
  stat2 = fs40.statSync(fullPath);
@@ -62659,7 +62777,7 @@ var todo_extract = createSwarmTool({
62659
62777
  filesToScan.push(scanPath);
62660
62778
  } else {
62661
62779
  const errorResult = {
62662
- error: `unsupported file extension: ${path53.extname(scanPath)}`,
62780
+ error: `unsupported file extension: ${path54.extname(scanPath)}`,
62663
62781
  total: 0,
62664
62782
  byPriority: { high: 0, medium: 0, low: 0 },
62665
62783
  entries: []
@@ -62705,14 +62823,14 @@ var todo_extract = createSwarmTool({
62705
62823
  init_tool();
62706
62824
  init_schema();
62707
62825
  import * as fs42 from "fs";
62708
- import * as path55 from "path";
62826
+ import * as path56 from "path";
62709
62827
 
62710
62828
  // src/hooks/diff-scope.ts
62711
62829
  import * as fs41 from "fs";
62712
- import * as path54 from "path";
62830
+ import * as path55 from "path";
62713
62831
  function getDeclaredScope(taskId, directory) {
62714
62832
  try {
62715
- const planPath = path54.join(directory, ".swarm", "plan.json");
62833
+ const planPath = path55.join(directory, ".swarm", "plan.json");
62716
62834
  if (!fs41.existsSync(planPath))
62717
62835
  return null;
62718
62836
  const raw = fs41.readFileSync(planPath, "utf-8");
@@ -62827,7 +62945,7 @@ var TIER_3_PATTERNS = [
62827
62945
  ];
62828
62946
  function matchesTier3Pattern(files) {
62829
62947
  for (const file3 of files) {
62830
- const fileName = path55.basename(file3);
62948
+ const fileName = path56.basename(file3);
62831
62949
  for (const pattern of TIER_3_PATTERNS) {
62832
62950
  if (pattern.test(fileName)) {
62833
62951
  return true;
@@ -62849,7 +62967,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62849
62967
  if (hasActiveTurboMode2()) {
62850
62968
  const resolvedDir2 = workingDirectory ?? process.cwd();
62851
62969
  try {
62852
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
62970
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62853
62971
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62854
62972
  const plan = JSON.parse(planRaw);
62855
62973
  for (const planPhase of plan.phases ?? []) {
@@ -62869,7 +62987,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62869
62987
  }
62870
62988
  const resolvedDir = workingDirectory ?? process.cwd();
62871
62989
  try {
62872
- const evidencePath = path55.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62990
+ const evidencePath = path56.join(resolvedDir, ".swarm", "evidence", `${taskId}.json`);
62873
62991
  const raw = fs42.readFileSync(evidencePath, "utf-8");
62874
62992
  const evidence = JSON.parse(raw);
62875
62993
  if (evidence?.required_gates && Array.isArray(evidence.required_gates) && evidence?.gates) {
@@ -62910,7 +63028,7 @@ function checkReviewerGate(taskId, workingDirectory) {
62910
63028
  }
62911
63029
  try {
62912
63030
  const resolvedDir2 = workingDirectory ?? process.cwd();
62913
- const planPath = path55.join(resolvedDir2, ".swarm", "plan.json");
63031
+ const planPath = path56.join(resolvedDir2, ".swarm", "plan.json");
62914
63032
  const planRaw = fs42.readFileSync(planPath, "utf-8");
62915
63033
  const plan = JSON.parse(planRaw);
62916
63034
  for (const planPhase of plan.phases ?? []) {
@@ -63092,8 +63210,8 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63092
63210
  };
63093
63211
  }
63094
63212
  }
63095
- normalizedDir = path55.normalize(args2.working_directory);
63096
- const pathParts = normalizedDir.split(path55.sep);
63213
+ normalizedDir = path56.normalize(args2.working_directory);
63214
+ const pathParts = normalizedDir.split(path56.sep);
63097
63215
  if (pathParts.includes("..")) {
63098
63216
  return {
63099
63217
  success: false,
@@ -63103,10 +63221,10 @@ async function executeUpdateTaskStatus(args2, fallbackDir) {
63103
63221
  ]
63104
63222
  };
63105
63223
  }
63106
- const resolvedDir = path55.resolve(normalizedDir);
63224
+ const resolvedDir = path56.resolve(normalizedDir);
63107
63225
  try {
63108
63226
  const realPath = fs42.realpathSync(resolvedDir);
63109
- const planPath = path55.join(realPath, ".swarm", "plan.json");
63227
+ const planPath = path56.join(realPath, ".swarm", "plan.json");
63110
63228
  if (!fs42.existsSync(planPath)) {
63111
63229
  return {
63112
63230
  success: false,
@@ -63301,7 +63419,7 @@ var OpenCodeSwarm = async (ctx) => {
63301
63419
  const { PreflightTriggerManager: PTM } = await Promise.resolve().then(() => (init_trigger(), exports_trigger));
63302
63420
  preflightTriggerManager = new PTM(automationConfig);
63303
63421
  const { AutomationStatusArtifact: ASA } = await Promise.resolve().then(() => (init_status_artifact(), exports_status_artifact));
63304
- const swarmDir = path56.resolve(ctx.directory, ".swarm");
63422
+ const swarmDir = path57.resolve(ctx.directory, ".swarm");
63305
63423
  statusArtifact = new ASA(swarmDir);
63306
63424
  statusArtifact.updateConfig(automationConfig.mode, automationConfig.capabilities);
63307
63425
  if (automationConfig.capabilities?.evidence_auto_summaries === true) {