@biffo/cli 0.283.3 → 0.284.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -9693,6 +9693,7 @@ import { cpSync as cpSync6, existsSync as existsSync35, mkdirSync as mkdirSync12
9693
9693
  import { join as join37, relative as relative7, resolve as resolve17 } from "path";
9694
9694
  import chalk19 from "chalk";
9695
9695
  import { Command as Command20 } from "commander";
9696
+ import { execa as execa7 } from "execa";
9696
9697
  import inquirer7 from "inquirer";
9697
9698
  var pluginUpgradeCommand = new Command20("upgrade").description(
9698
9699
  "Upgrade an installed plugin to a new minor version (biffo plugin upgrade <name>@<new-minor>) or refresh it in place from a local, unpublished checkout (biffo plugin upgrade --local <path>)"
@@ -9794,6 +9795,7 @@ async function runPluginUpgrade(target, options, deps) {
9794
9795
  refuseIfModuleStillReferenced(options.cwd, modulesDir, entry.name);
9795
9796
  }
9796
9797
  const previousProvenance = readProvenance(targetDir);
9798
+ const previousPyproject = readPyprojectIfPresent(targetDir);
9797
9799
  rmSync10(targetDir, { recursive: true, force: true });
9798
9800
  mkdirSync12(targetDir, { recursive: true });
9799
9801
  cpSync6(tmpDir, targetDir, { recursive: true });
@@ -9804,6 +9806,7 @@ async function runPluginUpgrade(target, options, deps) {
9804
9806
  );
9805
9807
  writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
9806
9808
  applyWorkspaceSources(targetDir, options.cwd, `services/${entry.name}`);
9809
+ const newPyproject = readPyprojectIfPresent(targetDir);
9807
9810
  const stagePaths = [`services/${entry.name}`];
9808
9811
  if (existsSync35(modulesDir)) {
9809
9812
  rmSync10(modulesDir, { recursive: true, force: true });
@@ -9835,12 +9838,29 @@ async function runPluginUpgrade(target, options, deps) {
9835
9838
  if (seedResult.vendored) {
9836
9839
  stagePaths.push(seedResult.stagedPath);
9837
9840
  }
9841
+ const lockOutcomes = await relockIfDependenciesChanged(
9842
+ options.cwd,
9843
+ `services/${entry.name}/pyproject.toml`,
9844
+ previousPyproject,
9845
+ newPyproject,
9846
+ deps
9847
+ );
9848
+ for (const outcome of lockOutcomes) {
9849
+ if (outcome.ok) stagePaths.push(outcome.trigger.lockfile);
9850
+ }
9838
9851
  const label = currentVersion ? `${entry.name} ${currentVersion} -> ${entry.version}` : `${entry.name} to ${entry.version}`;
9839
9852
  const commitMessage = `feat(plugins): upgrade ${label}`;
9840
9853
  await deps.git.add(options.cwd, stagePaths);
9841
9854
  await deps.git.commit(options.cwd, commitMessage);
9842
9855
  log.success(`Committed: ${commitMessage}`);
9843
- console.log(chalk19.bold("\n Plugin upgraded!\n"));
9856
+ const lockFailures = describeFailures(lockOutcomes);
9857
+ if (lockFailures.length > 0) {
9858
+ console.log(chalk19.yellow.bold("\n \u26A0 Plugin upgraded, but uv.lock needs attention\n"));
9859
+ for (const failure of lockFailures) console.log(chalk19.yellow(` ${failure}`));
9860
+ console.log();
9861
+ } else {
9862
+ console.log(chalk19.bold("\n Plugin upgraded!\n"));
9863
+ }
9844
9864
  console.log(` ${entry.name}@${entry.version} is committed at services/${entry.name}/`);
9845
9865
  console.log(" Push and redeploy to apply its updated tables and routes:");
9846
9866
  console.log(chalk19.dim(` git push`));
@@ -9889,6 +9909,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9889
9909
  refuseIfModuleStillReferenced(options.cwd, modulesDir, source.name);
9890
9910
  }
9891
9911
  const previousProvenance = readProvenance(targetDir);
9912
+ const previousPyproject = readPyprojectIfPresent(targetDir);
9892
9913
  if (inTreeSource) {
9893
9914
  log.info(
9894
9915
  `services/${source.name}/ is already the local checkout \u2014 nothing to copy; re-syncing its Terraform module and checking for a migration.`
@@ -9902,6 +9923,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9902
9923
  const nextProvenance = inTreeSource ? inTreePluginProvenance(`services/${source.name}`) : await resolveLocalProvenance(source.sourceDir, source.origin);
9903
9924
  writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
9904
9925
  applyWorkspaceSources(targetDir, options.cwd, `services/${source.name}`);
9926
+ const newPyproject = readPyprojectIfPresent(targetDir);
9905
9927
  const stagePaths = [`services/${source.name}`];
9906
9928
  if (existsSync35(modulesDir)) {
9907
9929
  rmSync10(modulesDir, { recursive: true, force: true });
@@ -9935,6 +9957,16 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9935
9957
  if (seedResult.vendored) {
9936
9958
  stagePaths.push(seedResult.stagedPath);
9937
9959
  }
9960
+ const lockOutcomes = await relockIfDependenciesChanged(
9961
+ options.cwd,
9962
+ `services/${source.name}/pyproject.toml`,
9963
+ previousPyproject,
9964
+ newPyproject,
9965
+ deps
9966
+ );
9967
+ for (const outcome of lockOutcomes) {
9968
+ if (outcome.ok) stagePaths.push(outcome.trigger.lockfile);
9969
+ }
9938
9970
  await deps.git.add(options.cwd, stagePaths);
9939
9971
  if (!await deps.git.hasUncommittedChanges(options.cwd)) {
9940
9972
  log.warn(`services/${source.name}/ already matches ${source.origin} \u2014 nothing to commit.`);
@@ -9943,7 +9975,14 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9943
9975
  const commitMessage = `chore(plugins): refresh ${source.name} from local checkout`;
9944
9976
  await deps.git.commit(options.cwd, commitMessage);
9945
9977
  log.success(`Committed: ${commitMessage}`);
9946
- console.log(chalk19.bold("\n Plugin refreshed!\n"));
9978
+ const lockFailures = describeFailures(lockOutcomes);
9979
+ if (lockFailures.length > 0) {
9980
+ console.log(chalk19.yellow.bold("\n \u26A0 Plugin refreshed, but uv.lock needs attention\n"));
9981
+ for (const failure of lockFailures) console.log(chalk19.yellow(` ${failure}`));
9982
+ console.log();
9983
+ } else {
9984
+ console.log(chalk19.bold("\n Plugin refreshed!\n"));
9985
+ }
9947
9986
  console.log(` services/${source.name}/ now matches ${source.origin}`);
9948
9987
  console.log(" Push and redeploy to apply any updated tables and routes:");
9949
9988
  console.log(chalk19.dim(` git push`));
@@ -9964,6 +10003,62 @@ ${refList}
9964
10003
  A plugin repo without terraform/ is missing the directory, not declaring that the module should go, and a wrong destructive action here is worse than no action. Either remove the reference(s) above yourself, or run 'biffo plugin uninstall ${name}', which removes the module and unwires the reference together.`
9965
10004
  );
9966
10005
  }
10006
+ async function relockIfDependenciesChanged(cwd, relPyprojectPath, previousPyproject, newPyproject, deps) {
10007
+ if (!dependenciesChanged(previousPyproject, newPyproject)) return [];
10008
+ const triggers = lockfilesNeedingRefresh([relPyprojectPath], cwd);
10009
+ if (triggers.length === 0) return [];
10010
+ const run = deps.runCommand ?? defaultRunCommand2;
10011
+ const outcomes = await refreshLockfiles(cwd, triggers, run);
10012
+ const refreshed = outcomes.filter((o) => o.ok);
10013
+ if (refreshed.length > 0) {
10014
+ log.success(
10015
+ `Refreshed ${refreshed.map((o) => o.trigger.lockfile).join(", ")} \u2014 this refresh changed a dependency it locks.`
10016
+ );
10017
+ }
10018
+ for (const message of describeFailures(outcomes)) log.warn(message);
10019
+ return outcomes;
10020
+ }
10021
+ var defaultRunCommand2 = async (command, cwd) => {
10022
+ const [bin, ...args] = command;
10023
+ if (!bin) return { ok: false, error: "empty command" };
10024
+ try {
10025
+ await execa7(bin, args, { cwd });
10026
+ return { ok: true };
10027
+ } catch (err) {
10028
+ const cause = err;
10029
+ const detail = cause.stderr?.trim() || cause.shortMessage || cause.message || "failed";
10030
+ return { ok: false, error: detail.split("\n")[0] ?? "failed" };
10031
+ }
10032
+ };
10033
+ function readPyprojectIfPresent(targetDir) {
10034
+ const path = join37(targetDir, "pyproject.toml");
10035
+ return existsSync35(path) ? readFileSync26(path, "utf8") : null;
10036
+ }
10037
+ function dependenciesChanged(before, after) {
10038
+ if (before === after) return false;
10039
+ const beforeItems = before ? dependencySurface(before) : [];
10040
+ const afterItems = after ? dependencySurface(after) : [];
10041
+ return JSON.stringify(beforeItems) !== JSON.stringify(afterItems);
10042
+ }
10043
+ function dependencySurface(text) {
10044
+ const items = [...readTomlStringArray(text, "dependencies")];
10045
+ for (const header of ["dependency-groups", "project.optional-dependencies"]) {
10046
+ const body = tomlTableBody(text, header);
10047
+ if (!body) continue;
10048
+ for (const m of body.matchAll(/^([A-Za-z0-9_.-]+)\s*=\s*\[/gm)) {
10049
+ items.push(...readTomlStringArray(body, m[1]));
10050
+ }
10051
+ }
10052
+ return items.sort();
10053
+ }
10054
+ function tomlTableBody(text, header) {
10055
+ const escaped = header.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
10056
+ const headerMatch = new RegExp(`^\\[${escaped}\\]\\s*$`, "m").exec(text);
10057
+ if (!headerMatch) return null;
10058
+ const rest = text.slice(headerMatch.index + headerMatch[0].length);
10059
+ const nextHeader = /^\[/m.exec(rest);
10060
+ return nextHeader ? rest.slice(0, nextHeader.index) : rest;
10061
+ }
9967
10062
  function readInstalledVersion2(targetDir) {
9968
10063
  const manifestPath = join37(targetDir, "biffo.plugin.json");
9969
10064
  if (!existsSync35(manifestPath)) return void 0;
@@ -10314,7 +10409,7 @@ import { Command as Command24 } from "commander";
10314
10409
  // src/scripts/check-adr-numbering.ts
10315
10410
  import { existsSync as existsSync38 } from "fs";
10316
10411
  import { join as join39 } from "path";
10317
- import { execa as execa7 } from "execa";
10412
+ import { execa as execa8 } from "execa";
10318
10413
 
10319
10414
  // src/lib/adr-numbering-guard.ts
10320
10415
  import { existsSync as existsSync37, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "fs";
@@ -10388,7 +10483,7 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
10388
10483
 
10389
10484
  // src/scripts/check-adr-numbering.ts
10390
10485
  async function runAdrNumberingCheck() {
10391
- const root = (await execa7("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10486
+ const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10392
10487
  const adrDir = join39(root, "docs", "ADR");
10393
10488
  if (!existsSync38(adrDir)) {
10394
10489
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
@@ -10429,7 +10524,7 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
10429
10524
 
10430
10525
  // src/scripts/check-branch-protection.ts
10431
10526
  import { Octokit as Octokit2 } from "@octokit/rest";
10432
- import { execa as execa8 } from "execa";
10527
+ import { execa as execa9 } from "execa";
10433
10528
 
10434
10529
  // src/lib/branch-protection-apply.ts
10435
10530
  var CONTEXT_CONSISTENCY_THRESHOLD = 2 / 3;
@@ -10556,7 +10651,7 @@ async function resolveRepo(explicit) {
10556
10651
  }
10557
10652
  return { owner, repo };
10558
10653
  }
10559
- const { stdout } = await execa8("git", ["remote", "get-url", "origin"]);
10654
+ const { stdout } = await execa9("git", ["remote", "get-url", "origin"]);
10560
10655
  const m = /github\.com[:/]([^/]+)\/(.+?)(?:\.git)?$/.exec(stdout.trim());
10561
10656
  if (!m?.[1] || !m[2]) {
10562
10657
  console.error(
@@ -10687,14 +10782,160 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
10687
10782
  console.log(`\u2713 branch-protection guard: ${owner}/${repo} (${audited.join(", ")}) OK`);
10688
10783
  }
10689
10784
 
10785
+ // src/scripts/check-claim-invocation.ts
10786
+ import { execa as execa10 } from "execa";
10787
+
10788
+ // src/lib/claim-invocation-parity.ts
10789
+ import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync16 } from "fs";
10790
+ import { join as join40 } from "path";
10791
+ function distributedAgentsDocs(root) {
10792
+ const docs = [];
10793
+ const own = join40(root, "AGENTS.md");
10794
+ if (existsSync39(own)) docs.push({ path: "AGENTS.md", text: readFileSync29(own, "utf8") });
10795
+ const skeletons = join40(root, "_skeletons");
10796
+ if (existsSync39(skeletons)) {
10797
+ for (const name of readdirSync16(skeletons).sort()) {
10798
+ const abs = join40(skeletons, name, "AGENTS.md");
10799
+ if (!existsSync39(abs)) continue;
10800
+ docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync29(abs, "utf8") });
10801
+ }
10802
+ }
10803
+ return docs;
10804
+ }
10805
+ function isClaimInvocation(line) {
10806
+ return /\b(?:biffo\.sh|claim\.sh)\s+claim\b|\bclaim\.sh\s+\d|\bclaim\s+<issue-number>/.test(line);
10807
+ }
10808
+ function claimBlock(text) {
10809
+ const lines = text.split("\n");
10810
+ const out = [];
10811
+ let fenced = false;
10812
+ for (const raw of lines) {
10813
+ if (/^\s*```/.test(raw)) {
10814
+ fenced = !fenced;
10815
+ continue;
10816
+ }
10817
+ if (!fenced) continue;
10818
+ if (isClaimInvocation(raw)) out.push(raw.trimEnd());
10819
+ }
10820
+ return out;
10821
+ }
10822
+ function claimInvocations(text) {
10823
+ const found = [];
10824
+ let fenced = false;
10825
+ for (const raw of text.split("\n")) {
10826
+ if (/^\s*```/.test(raw)) {
10827
+ fenced = !fenced;
10828
+ continue;
10829
+ }
10830
+ if (fenced) {
10831
+ if (isClaimInvocation(raw)) found.push(raw.trimEnd());
10832
+ continue;
10833
+ }
10834
+ for (const match of raw.matchAll(/`([^`]+)`/g)) {
10835
+ const span = match[1];
10836
+ if (span !== void 0 && isClaimInvocation(span)) found.push(span.trim());
10837
+ }
10838
+ }
10839
+ return found;
10840
+ }
10841
+ function isTokened(invocation) {
10842
+ return /--as\b|--release\b|--guard\b/.test(invocation);
10843
+ }
10844
+ function auditClaimInvocationParity(docs) {
10845
+ const violations = [];
10846
+ if (docs.length === 0) {
10847
+ return [
10848
+ {
10849
+ rule: "no-copies",
10850
+ path: "(none)",
10851
+ detail: "no distributed AGENTS.md found \u2014 a guard with an empty input set passes against anything"
10852
+ }
10853
+ ];
10854
+ }
10855
+ const [canonical, ...rest] = docs;
10856
+ const canonicalBlock = claimBlock(canonical.text);
10857
+ if (canonicalBlock.length === 0) {
10858
+ violations.push({
10859
+ rule: "missing-form",
10860
+ path: canonical.path,
10861
+ detail: "documents no claim invocation at all"
10862
+ });
10863
+ }
10864
+ for (const doc of rest) {
10865
+ const block = claimBlock(doc.text);
10866
+ if (block.join("\n") !== canonicalBlock.join("\n")) {
10867
+ violations.push({
10868
+ rule: "block-drift",
10869
+ path: doc.path,
10870
+ detail: `claim block differs from ${canonical.path}
10871
+ ${canonical.path}:
10872
+ ${canonicalBlock.map((l) => ` ${l}`).join("\n")}
10873
+ ${doc.path}:
10874
+ ${block.map((l) => ` ${l}`).join("\n")}`
10875
+ });
10876
+ }
10877
+ }
10878
+ for (const doc of docs) {
10879
+ const invocations = claimInvocations(doc.text);
10880
+ for (const invocation of invocations) {
10881
+ if (!isTokened(invocation)) {
10882
+ violations.push({
10883
+ rule: "untokened-form",
10884
+ path: doc.path,
10885
+ detail: `documents a claim with no --as token: ${invocation.trim()}`
10886
+ });
10887
+ }
10888
+ }
10889
+ if (!invocations.some((i) => /--as\b/.test(i))) {
10890
+ violations.push({
10891
+ rule: "missing-form",
10892
+ path: doc.path,
10893
+ detail: "never documents `claim <issue-number> --as <token>`"
10894
+ });
10895
+ }
10896
+ if (!invocations.some((i) => /--release\b/.test(i))) {
10897
+ violations.push({
10898
+ rule: "missing-form",
10899
+ path: doc.path,
10900
+ detail: "never documents `claim <issue-number> --release <token>`"
10901
+ });
10902
+ }
10903
+ }
10904
+ return violations;
10905
+ }
10906
+ function formatParityViolations(violations) {
10907
+ return violations.map((v) => ` [${v.rule}] ${v.path}: ${v.detail}`).join("\n");
10908
+ }
10909
+
10910
+ // src/scripts/check-claim-invocation.ts
10911
+ async function runClaimInvocationCheck() {
10912
+ const root = (await execa10("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10913
+ const docs = distributedAgentsDocs(root);
10914
+ console.log(
10915
+ `audited ${docs.length} distributed AGENTS.md (${docs.map((d) => d.path).join(", ") || "none"}) under ${root}`
10916
+ );
10917
+ const violations = auditClaimInvocationParity(docs);
10918
+ if (violations.length > 0) {
10919
+ console.error("\u2717 Claim-invocation guard: the distributed AGENTS.md copies disagree\n");
10920
+ console.error(formatParityViolations(violations));
10921
+ console.error(
10922
+ "\nEvery copy must document the same invocation, and `--as <token>` is mandatory (#1562). Fix the skeletons too \u2014 they are what satellites receive."
10923
+ );
10924
+ process.exit(1);
10925
+ }
10926
+ console.log(
10927
+ "\u2713 Claim-invocation guard: every distributed AGENTS.md documents the same, tokened, claim"
10928
+ );
10929
+ }
10930
+
10690
10931
  // src/scripts/check-codeql-suppression.ts
10691
- import { existsSync as existsSync39 } from "fs";
10692
- import { join as join41, relative as relative8 } from "path";
10693
- import { execa as execa9 } from "execa";
10932
+ import { existsSync as existsSync40 } from "fs";
10933
+ import { join as join42, relative as relative8 } from "path";
10934
+ import { execa as execa11 } from "execa";
10694
10935
 
10695
10936
  // src/lib/codeql-suppression-guard.ts
10696
- import { readdirSync as readdirSync16, readFileSync as readFileSync29, statSync as statSync9 } from "fs";
10697
- import { join as join40 } from "path";
10937
+ import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync9 } from "fs";
10938
+ import { join as join41 } from "path";
10698
10939
  var SKIP_DIRS = /* @__PURE__ */ new Set([
10699
10940
  ".git",
10700
10941
  ".worktrees",
@@ -10720,12 +10961,12 @@ function walkSourceFiles(root) {
10720
10961
  const walk2 = (dir) => {
10721
10962
  let entries;
10722
10963
  try {
10723
- entries = readdirSync16(dir);
10964
+ entries = readdirSync17(dir);
10724
10965
  } catch {
10725
10966
  return;
10726
10967
  }
10727
10968
  for (const entry of entries) {
10728
- const p = join40(dir, entry);
10969
+ const p = join41(dir, entry);
10729
10970
  let st;
10730
10971
  try {
10731
10972
  st = statSync9(p);
@@ -10751,7 +10992,7 @@ function countSourceFiles(root) {
10751
10992
  function sweepCodeqlSuppressionComments(root) {
10752
10993
  const hits = [];
10753
10994
  for (const path of walkSourceFiles(root)) {
10754
- const text = readFileSync29(path, "utf8");
10995
+ const text = readFileSync30(path, "utf8");
10755
10996
  for (const line of findCodeqlSuppressionComments(text)) {
10756
10997
  hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
10757
10998
  }
@@ -10761,9 +11002,9 @@ function sweepCodeqlSuppressionComments(root) {
10761
11002
 
10762
11003
  // src/scripts/check-codeql-suppression.ts
10763
11004
  async function runCodeqlSuppressionCheck() {
10764
- const root = (await execa9("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10765
- const scanRoot = join41(root, "cli", "src");
10766
- if (!existsSync39(scanRoot)) {
11005
+ const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11006
+ const scanRoot = join42(root, "cli", "src");
11007
+ if (!existsSync40(scanRoot)) {
10767
11008
  console.log(
10768
11009
  "\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
10769
11010
  );
@@ -10787,11 +11028,11 @@ async function runCodeqlSuppressionCheck() {
10787
11028
  }
10788
11029
 
10789
11030
  // src/scripts/check-cognito-invite-template.ts
10790
- import { execa as execa10 } from "execa";
11031
+ import { execa as execa12 } from "execa";
10791
11032
 
10792
11033
  // src/lib/cognito-invite-template-guard.ts
10793
- import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync10 } from "fs";
10794
- import { join as join42 } from "path";
11034
+ import { readdirSync as readdirSync18, readFileSync as readFileSync31, statSync as statSync10 } from "fs";
11035
+ import { join as join43 } from "path";
10795
11036
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
10796
11037
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
10797
11038
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -10868,13 +11109,13 @@ function findModuleTerraformFiles(repoRoot) {
10868
11109
  const walk2 = (dir, relative11) => {
10869
11110
  let entries;
10870
11111
  try {
10871
- entries = readdirSync17(dir);
11112
+ entries = readdirSync18(dir);
10872
11113
  } catch {
10873
11114
  return;
10874
11115
  }
10875
11116
  for (const entry of entries) {
10876
11117
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
10877
- const full = join42(dir, entry);
11118
+ const full = join43(dir, entry);
10878
11119
  const rel = `${relative11}/${entry}`;
10879
11120
  if (statSync10(full).isDirectory()) {
10880
11121
  walk2(full, rel);
@@ -10883,18 +11124,18 @@ function findModuleTerraformFiles(repoRoot) {
10883
11124
  }
10884
11125
  }
10885
11126
  };
10886
- walk2(join42(repoRoot, "modules"), "modules");
11127
+ walk2(join43(repoRoot, "modules"), "modules");
10887
11128
  return found.sort();
10888
11129
  }
10889
11130
  function checkCognitoInviteTemplates(repoRoot) {
10890
11131
  return findModuleTerraformFiles(repoRoot).flatMap(
10891
- (file) => checkInviteTemplateSource(file, readFileSync30(join42(repoRoot, file), "utf8"))
11132
+ (file) => checkInviteTemplateSource(file, readFileSync31(join43(repoRoot, file), "utf8"))
10892
11133
  );
10893
11134
  }
10894
11135
 
10895
11136
  // src/scripts/check-cognito-invite-template.ts
10896
11137
  async function runCognitoInviteTemplateCheck() {
10897
- const root = (await execa10("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11138
+ const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10898
11139
  const files = findModuleTerraformFiles(root);
10899
11140
  console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
10900
11141
  if (files.length === 0) {
@@ -10916,12 +11157,12 @@ async function runCognitoInviteTemplateCheck() {
10916
11157
  }
10917
11158
 
10918
11159
  // src/scripts/check-core-direct-paths.ts
10919
- import { join as join44 } from "path";
10920
- import { execa as execa11 } from "execa";
11160
+ import { join as join45 } from "path";
11161
+ import { execa as execa13 } from "execa";
10921
11162
 
10922
11163
  // src/lib/core-direct-paths-audit.ts
10923
- import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync18, statSync as statSync11 } from "fs";
10924
- import { join as join43 } from "path";
11164
+ import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11165
+ import { join as join44 } from "path";
10925
11166
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
10926
11167
  var API_ROUTE_PREFIX = "/api/v1";
10927
11168
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -11080,12 +11321,12 @@ function walkFiles(root, accept, skipDir) {
11080
11321
  const walk2 = (dir) => {
11081
11322
  let entries;
11082
11323
  try {
11083
- entries = readdirSync18(dir);
11324
+ entries = readdirSync19(dir);
11084
11325
  } catch {
11085
11326
  return;
11086
11327
  }
11087
11328
  for (const entry of entries) {
11088
- const p = join43(dir, entry);
11329
+ const p = join44(dir, entry);
11089
11330
  let st;
11090
11331
  try {
11091
11332
  st = statSync11(p);
@@ -11115,7 +11356,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
11115
11356
  const extracted = [];
11116
11357
  let rawTotal = 0;
11117
11358
  for (const file of files) {
11118
- const text = readFileSync31(file, "utf8");
11359
+ const text = readFileSync32(file, "utf8");
11119
11360
  rawTotal += countRawExternalOccurrences(text, externalBases);
11120
11361
  extracted.push(...extractCoreDirectPaths(text, file, externalBases));
11121
11362
  }
@@ -11164,7 +11405,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
11164
11405
  const prefixSet = /* @__PURE__ */ new Set();
11165
11406
  let rawApiRouterCount = 0;
11166
11407
  for (const file of files) {
11167
- const text = readFileSync31(file, "utf8");
11408
+ const text = readFileSync32(file, "utf8");
11168
11409
  const extraction = extractCoreRoutePrefixes(text);
11169
11410
  rawApiRouterCount += extraction.rawApiRouterCount;
11170
11411
  for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
@@ -11180,10 +11421,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
11180
11421
  }
11181
11422
  function resolveSiblingCoreSrc(params) {
11182
11423
  const { estateDir, sibling } = params;
11183
- const configPath = join43(estateDir, sibling, "biffo.sibling.json");
11424
+ const configPath = join44(estateDir, sibling, "biffo.sibling.json");
11184
11425
  let raw;
11185
11426
  try {
11186
- raw = readFileSync31(configPath, "utf8");
11427
+ raw = readFileSync32(configPath, "utf8");
11187
11428
  } catch (err) {
11188
11429
  throw new Error(
11189
11430
  `cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
@@ -11203,8 +11444,8 @@ function resolveSiblingCoreSrc(params) {
11203
11444
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
11204
11445
  );
11205
11446
  }
11206
- const coreApiSrcDir = join43(estateDir, coreProject, "services", "api", "src");
11207
- if (!existsSync40(coreApiSrcDir)) {
11447
+ const coreApiSrcDir = join44(estateDir, coreProject, "services", "api", "src");
11448
+ if (!existsSync41(coreApiSrcDir)) {
11208
11449
  throw new Error(
11209
11450
  `cannot resolve ${sibling}'s core: biffo.sibling.json names core_project "${coreProject}", but ${coreApiSrcDir} does not exist -- the instance is missing from this estate checkout, not merely unmatched. Refusing to silently skip ${sibling} and shrink the audit's denominator.`
11210
11451
  );
@@ -11245,9 +11486,9 @@ function auditSiblingCoreDirectPaths(params) {
11245
11486
 
11246
11487
  // src/scripts/check-core-direct-paths.ts
11247
11488
  async function runCoreDirectPathsCheck(opts = {}) {
11248
- const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11489
+ const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11249
11490
  const sibling = opts.sibling ?? "sibling-template (self-check)";
11250
- const frontendSrcDir = opts.frontendSrc ?? join44(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
11491
+ const frontendSrcDir = opts.frontendSrc ?? join45(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
11251
11492
  let coreApiSrcDir;
11252
11493
  let coreProject = null;
11253
11494
  if (opts.coreSrc) {
@@ -11263,7 +11504,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
11263
11504
  coreApiSrcDir = resolution.coreApiSrcDir;
11264
11505
  coreProject = resolution.coreProject;
11265
11506
  } else {
11266
- coreApiSrcDir = join44(root, "services", "api", "src");
11507
+ coreApiSrcDir = join45(root, "services", "api", "src");
11267
11508
  }
11268
11509
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
11269
11510
  console.log(
@@ -11299,7 +11540,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
11299
11540
  }
11300
11541
 
11301
11542
  // src/scripts/check-core-ownership.ts
11302
- import { execa as execa12 } from "execa";
11543
+ import { execa as execa14 } from "execa";
11303
11544
  var BOLD = "\x1B[1m";
11304
11545
  var DIM = "\x1B[2m";
11305
11546
  var RED = "\x1B[31m";
@@ -11310,7 +11551,7 @@ async function runOwnershipCheck(argv) {
11310
11551
  const stagedFlag = args.indexOf("--staged");
11311
11552
  const staged = stagedFlag !== -1;
11312
11553
  const messageFile = staged ? args[stagedFlag + 1] : void 0;
11313
- const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11554
+ const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11314
11555
  const ownership = classifyRepoOwnership(root);
11315
11556
  if (ownership === "template") {
11316
11557
  console.log("\u2713 core ownership guard: skipped \u2014 this is the template, which owns these paths.");
@@ -11326,11 +11567,11 @@ async function runOwnershipCheck(argv) {
11326
11567
  let deletedFiles = [];
11327
11568
  let commitMessage = "";
11328
11569
  if (staged) {
11329
- const { stdout } = await execa12("git", ["diff", "--cached", "--name-status"], { cwd: root });
11570
+ const { stdout } = await execa14("git", ["diff", "--cached", "--name-status"], { cwd: root });
11330
11571
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
11331
11572
  if (messageFile) {
11332
- const { readFileSync: readFileSync41, existsSync: existsSync50 } = await import("fs");
11333
- if (existsSync50(messageFile)) commitMessage = readFileSync41(messageFile, "utf8");
11573
+ const { readFileSync: readFileSync42, existsSync: existsSync51 } = await import("fs");
11574
+ if (existsSync51(messageFile)) commitMessage = readFileSync42(messageFile, "utf8");
11334
11575
  }
11335
11576
  } else {
11336
11577
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -11338,18 +11579,18 @@ async function runOwnershipCheck(argv) {
11338
11579
  console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
11339
11580
  process.exit(2);
11340
11581
  }
11341
- await execa12("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
11342
- const { stdout } = await execa12("git", ["diff", "--name-status", `origin/${base}...HEAD`], {
11582
+ await execa14("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
11583
+ const { stdout } = await execa14("git", ["diff", "--name-status", `origin/${base}...HEAD`], {
11343
11584
  cwd: root
11344
11585
  });
11345
11586
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
11346
- const { stdout: log2 } = await execa12("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
11587
+ const { stdout: log2 } = await execa14("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
11347
11588
  cwd: root,
11348
11589
  reject: false
11349
11590
  });
11350
11591
  commitMessage = log2;
11351
11592
  }
11352
- const { stdout: gitBranch } = await execa12("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
11593
+ const { stdout: gitBranch } = await execa14("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
11353
11594
  cwd: root,
11354
11595
  reject: false
11355
11596
  });
@@ -11431,11 +11672,11 @@ ${BOLD}If the divergence is deliberate${OFF}
11431
11672
  }
11432
11673
 
11433
11674
  // src/scripts/check-eventbridge-log-permissions.ts
11434
- import { execa as execa13 } from "execa";
11675
+ import { execa as execa15 } from "execa";
11435
11676
 
11436
11677
  // src/lib/eventbridge-log-permission-guard.ts
11437
- import { readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
11438
- import { join as join45 } from "path";
11678
+ import { readFileSync as readFileSync33, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
11679
+ import { join as join46 } from "path";
11439
11680
  var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
11440
11681
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
11441
11682
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -11507,12 +11748,12 @@ function walkTerraformFiles(root) {
11507
11748
  const walk2 = (dir) => {
11508
11749
  let entries;
11509
11750
  try {
11510
- entries = readdirSync19(dir);
11751
+ entries = readdirSync20(dir);
11511
11752
  } catch {
11512
11753
  return;
11513
11754
  }
11514
11755
  for (const entry of entries) {
11515
- const p = join45(dir, entry);
11756
+ const p = join46(dir, entry);
11516
11757
  let st;
11517
11758
  try {
11518
11759
  st = statSync12(p);
@@ -11555,7 +11796,7 @@ function auditEventBridgeLogPermissions(root) {
11555
11796
  let rawEventTargetCount = 0;
11556
11797
  let rawLogPolicyCount = 0;
11557
11798
  for (const file of files) {
11558
- const text = readFileSync32(file, "utf8");
11799
+ const text = readFileSync33(file, "utf8");
11559
11800
  rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
11560
11801
  rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
11561
11802
  eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
@@ -11608,7 +11849,7 @@ function auditEventBridgeLogPermissions(root) {
11608
11849
 
11609
11850
  // src/scripts/check-eventbridge-log-permissions.ts
11610
11851
  async function runEventBridgeLogPermissionCheck() {
11611
- const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11852
+ const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11612
11853
  let report;
11613
11854
  try {
11614
11855
  report = auditEventBridgeLogPermissions(root);
@@ -11645,15 +11886,15 @@ async function runEventBridgeLogPermissionCheck() {
11645
11886
  }
11646
11887
 
11647
11888
  // src/scripts/check-lambda-output.ts
11648
- import { execa as execa14 } from "execa";
11889
+ import { execa as execa16 } from "execa";
11649
11890
 
11650
11891
  // src/lib/lambda-output-guard.ts
11651
- import { readFileSync as readFileSync34 } from "fs";
11652
- import { join as join47 } from "path";
11892
+ import { readFileSync as readFileSync35 } from "fs";
11893
+ import { join as join48 } from "path";
11653
11894
 
11654
11895
  // src/lib/terraform-input-guard.ts
11655
- import { existsSync as existsSync41, readdirSync as readdirSync20, readFileSync as readFileSync33, statSync as statSync13 } from "fs";
11656
- import { join as join46 } from "path";
11896
+ import { existsSync as existsSync42, readdirSync as readdirSync21, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
11897
+ import { join as join47 } from "path";
11657
11898
  var GUARDED_SUBCOMMANDS = [
11658
11899
  "init",
11659
11900
  "plan",
@@ -11667,18 +11908,18 @@ function stripComments2(source) {
11667
11908
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
11668
11909
  }
11669
11910
  function vendoredPluginServiceDirs(repoRoot) {
11670
- const servicesDir = join46(repoRoot, "services");
11911
+ const servicesDir = join47(repoRoot, "services");
11671
11912
  const result = /* @__PURE__ */ new Set();
11672
11913
  let entries;
11673
11914
  try {
11674
- entries = readdirSync20(servicesDir);
11915
+ entries = readdirSync21(servicesDir);
11675
11916
  } catch {
11676
11917
  return result;
11677
11918
  }
11678
11919
  for (const entry of entries) {
11679
- const full = join46(servicesDir, entry);
11680
- if (!existsSync41(full) || !statSync13(full).isDirectory()) continue;
11681
- if (existsSync41(join46(full, "biffo.plugin.json"))) {
11920
+ const full = join47(servicesDir, entry);
11921
+ if (!existsSync42(full) || !statSync13(full).isDirectory()) continue;
11922
+ if (existsSync42(join47(full, "biffo.plugin.json"))) {
11682
11923
  result.add(entry);
11683
11924
  }
11684
11925
  }
@@ -11690,7 +11931,7 @@ function findWorkflowFiles(repoRoot) {
11690
11931
  const walk2 = (dir, relative11) => {
11691
11932
  let entries;
11692
11933
  try {
11693
- entries = readdirSync20(dir);
11934
+ entries = readdirSync21(dir);
11694
11935
  } catch {
11695
11936
  return;
11696
11937
  }
@@ -11699,7 +11940,7 @@ function findWorkflowFiles(repoRoot) {
11699
11940
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
11700
11941
  continue;
11701
11942
  }
11702
- const full = join46(dir, entry);
11943
+ const full = join47(dir, entry);
11703
11944
  const rel = relative11 ? `${relative11}/${entry}` : entry;
11704
11945
  if (statSync13(full).isDirectory()) {
11705
11946
  walk2(full, rel);
@@ -11743,7 +11984,7 @@ function checkWorkflowSource(file, rawSource) {
11743
11984
  }
11744
11985
  function checkTerraformInput(repoRoot) {
11745
11986
  return findWorkflowFiles(repoRoot).flatMap(
11746
- (file) => checkWorkflowSource(file, readFileSync33(join46(repoRoot, file), "utf8"))
11987
+ (file) => checkWorkflowSource(file, readFileSync34(join47(repoRoot, file), "utf8"))
11747
11988
  );
11748
11989
  }
11749
11990
 
@@ -11801,13 +12042,13 @@ function checkWorkflowSource2(file, rawSource) {
11801
12042
  }
11802
12043
  function checkLambdaOutput(repoRoot) {
11803
12044
  return findWorkflowFiles(repoRoot).flatMap(
11804
- (file) => checkWorkflowSource2(file, readFileSync34(join47(repoRoot, file), "utf8"))
12045
+ (file) => checkWorkflowSource2(file, readFileSync35(join48(repoRoot, file), "utf8"))
11805
12046
  );
11806
12047
  }
11807
12048
 
11808
12049
  // src/scripts/check-lambda-output.ts
11809
12050
  async function runLambdaOutputCheck() {
11810
- const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12051
+ const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11811
12052
  const files = findWorkflowFiles(root);
11812
12053
  console.log(`audited ${files.length} workflow file(s) under ${root}`);
11813
12054
  if (files.length === 0) {
@@ -11829,9 +12070,9 @@ async function runLambdaOutputCheck() {
11829
12070
  }
11830
12071
 
11831
12072
  // src/scripts/check-pipe-trap.ts
11832
- import { readFileSync as readFileSync35, readdirSync as readdirSync21 } from "fs";
11833
- import { join as join48, relative as relative9 } from "path";
11834
- import { execa as execa15 } from "execa";
12073
+ import { readFileSync as readFileSync36, readdirSync as readdirSync22 } from "fs";
12074
+ import { join as join49, relative as relative9 } from "path";
12075
+ import { execa as execa17 } from "execa";
11835
12076
 
11836
12077
  // src/lib/pipe-trap-guard.ts
11837
12078
  var STATUS_BEARING = [
@@ -11927,23 +12168,23 @@ function findPipeTraps(source) {
11927
12168
  function shellFiles(root) {
11928
12169
  const out = [];
11929
12170
  for (const dir of ["scripts", ".githooks"]) {
11930
- const full = join48(root, dir);
12171
+ const full = join49(root, dir);
11931
12172
  let entries;
11932
12173
  try {
11933
- entries = readdirSync21(full, { withFileTypes: true });
12174
+ entries = readdirSync22(full, { withFileTypes: true });
11934
12175
  } catch {
11935
12176
  continue;
11936
12177
  }
11937
12178
  for (const entry of entries) {
11938
12179
  if (!entry.isFile()) continue;
11939
12180
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
11940
- out.push(join48(full, entry.name));
12181
+ out.push(join49(full, entry.name));
11941
12182
  }
11942
12183
  }
11943
12184
  return out;
11944
12185
  }
11945
12186
  async function runPipeTrapCheck() {
11946
- const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12187
+ const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11947
12188
  const files = shellFiles(root);
11948
12189
  console.log(`audited ${files.length} shell file(s) under scripts/ and .githooks/ under ${root}`);
11949
12190
  if (files.length === 0) {
@@ -11953,7 +12194,7 @@ async function runPipeTrapCheck() {
11953
12194
  process.exit(1);
11954
12195
  }
11955
12196
  const findings = files.flatMap(
11956
- (file) => findPipeTraps(readFileSync35(file, "utf8")).map(
12197
+ (file) => findPipeTraps(readFileSync36(file, "utf8")).map(
11957
12198
  (t) => `${relative9(root, file)}:${t.line} ${t.text}
11958
12199
  ${t.reason}`
11959
12200
  )
@@ -11970,11 +12211,11 @@ async function runPipeTrapCheck() {
11970
12211
  }
11971
12212
 
11972
12213
  // src/scripts/check-plugin-allowlist-convention.ts
11973
- import { execa as execa16 } from "execa";
12214
+ import { execa as execa18 } from "execa";
11974
12215
 
11975
12216
  // src/lib/plugin-allowlist-convention.ts
11976
- import { readFileSync as readFileSync36 } from "fs";
11977
- import { join as join49 } from "path";
12217
+ import { readFileSync as readFileSync37 } from "fs";
12218
+ import { join as join50 } from "path";
11978
12219
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
11979
12220
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
11980
12221
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -11985,7 +12226,7 @@ var PLUGIN = "<plugin>";
11985
12226
  var ACCOUNT = "<account>";
11986
12227
  function read(repoRoot, relative11) {
11987
12228
  try {
11988
- return readFileSync36(join49(repoRoot, relative11), "utf8");
12229
+ return readFileSync37(join50(repoRoot, relative11), "utf8");
11989
12230
  } catch {
11990
12231
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
11991
12232
  }
@@ -12081,7 +12322,7 @@ Plugins would be rejected by require_service_principal (ADR-0009). Fix the glob,
12081
12322
 
12082
12323
  // src/scripts/check-plugin-allowlist-convention.ts
12083
12324
  async function runPluginAllowlistConventionCheck() {
12084
- const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12325
+ const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12085
12326
  let violations;
12086
12327
  try {
12087
12328
  violations = checkAllowlistConvention(root);
@@ -12106,34 +12347,34 @@ async function runPluginAllowlistConventionCheck() {
12106
12347
  }
12107
12348
 
12108
12349
  // src/scripts/check-plugin-collisions.ts
12109
- import { existsSync as existsSync43 } from "fs";
12110
- import { join as join51 } from "path";
12111
- import { execa as execa17 } from "execa";
12350
+ import { existsSync as existsSync44 } from "fs";
12351
+ import { join as join52 } from "path";
12352
+ import { execa as execa19 } from "execa";
12112
12353
 
12113
12354
  // src/lib/plugin-collision-guard.ts
12114
- import { existsSync as existsSync42, readdirSync as readdirSync22, statSync as statSync14 } from "fs";
12115
- import { join as join50 } from "path";
12355
+ import { existsSync as existsSync43, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
12356
+ import { join as join51 } from "path";
12116
12357
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
12117
12358
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
12118
12359
  function subdirectories(dir) {
12119
- if (!existsSync42(dir)) return [];
12120
- return readdirSync22(dir).filter((entry) => {
12360
+ if (!existsSync43(dir)) return [];
12361
+ return readdirSync23(dir).filter((entry) => {
12121
12362
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
12122
12363
  try {
12123
- return statSync14(join50(dir, entry)).isDirectory();
12364
+ return statSync14(join51(dir, entry)).isDirectory();
12124
12365
  } catch {
12125
12366
  return false;
12126
12367
  }
12127
12368
  });
12128
12369
  }
12129
12370
  function regularPackagesOf(pluginDir2) {
12130
- return subdirectories(pluginDir2).filter((name) => existsSync42(join50(pluginDir2, name, "__init__.py"))).sort();
12371
+ return subdirectories(pluginDir2).filter((name) => existsSync43(join51(pluginDir2, name, "__init__.py"))).sort();
12131
12372
  }
12132
12373
  function bareTestModulesOf(pluginDir2) {
12133
- const testsDir = join50(pluginDir2, "tests");
12134
- if (!existsSync42(testsDir)) return [];
12135
- if (existsSync42(join50(testsDir, "__init__.py"))) return [];
12136
- return readdirSync22(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
12374
+ const testsDir = join51(pluginDir2, "tests");
12375
+ if (!existsSync43(testsDir)) return [];
12376
+ if (existsSync43(join51(testsDir, "__init__.py"))) return [];
12377
+ return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
12137
12378
  }
12138
12379
  function findCollisions(servicesDir, pluginDirs) {
12139
12380
  const plugins = (pluginDirs ?? subdirectories(servicesDir)).filter((name) => !name.startsWith("_")).filter((name) => name !== "api").sort();
@@ -12141,7 +12382,7 @@ function findCollisions(servicesDir, pluginDirs) {
12141
12382
  const gather = (kind, namesOf) => {
12142
12383
  const claims = /* @__PURE__ */ new Map();
12143
12384
  for (const plugin of plugins) {
12144
- for (const name of namesOf(join50(servicesDir, plugin))) {
12385
+ for (const name of namesOf(join51(servicesDir, plugin))) {
12145
12386
  claims.set(name, [...claims.get(name) ?? [], plugin]);
12146
12387
  }
12147
12388
  }
@@ -12178,9 +12419,9 @@ function formatCollisions(collisions) {
12178
12419
 
12179
12420
  // src/scripts/check-plugin-collisions.ts
12180
12421
  async function runPluginCollisionCheck() {
12181
- const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12182
- const servicesDir = join51(root, "services");
12183
- if (!existsSync43(servicesDir)) {
12422
+ const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12423
+ const servicesDir = join52(root, "services");
12424
+ if (!existsSync44(servicesDir)) {
12184
12425
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
12185
12426
  return;
12186
12427
  }
@@ -12197,11 +12438,11 @@ async function runPluginCollisionCheck() {
12197
12438
  }
12198
12439
 
12199
12440
  // src/scripts/check-plugin-terraform.ts
12200
- import { execa as execa18 } from "execa";
12441
+ import { execa as execa20 } from "execa";
12201
12442
 
12202
12443
  // src/lib/plugin-terraform-guard.ts
12203
- import { existsSync as existsSync44, readFileSync as readFileSync37, readdirSync as readdirSync23 } from "fs";
12204
- import { dirname as dirname10, join as join52, relative as relative10, sep as sep4 } from "path";
12444
+ import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24 } from "fs";
12445
+ import { dirname as dirname10, join as join53, relative as relative10, sep as sep4 } from "path";
12205
12446
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
12206
12447
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
12207
12448
  function findPluginManifests(root) {
@@ -12209,16 +12450,16 @@ function findPluginManifests(root) {
12209
12450
  const walk2 = (dir) => {
12210
12451
  let entries;
12211
12452
  try {
12212
- entries = readdirSync23(dir, { withFileTypes: true });
12453
+ entries = readdirSync24(dir, { withFileTypes: true });
12213
12454
  } catch {
12214
12455
  return;
12215
12456
  }
12216
12457
  for (const entry of entries) {
12217
12458
  if (entry.isDirectory()) {
12218
12459
  if (SKIP_DIRS3.has(entry.name)) continue;
12219
- walk2(join52(dir, entry.name));
12460
+ walk2(join53(dir, entry.name));
12220
12461
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
12221
- found.push(relative10(root, join52(dir, entry.name)).split(sep4).join("/"));
12462
+ found.push(relative10(root, join53(dir, entry.name)).split(sep4).join("/"));
12222
12463
  }
12223
12464
  }
12224
12465
  };
@@ -12228,7 +12469,7 @@ function findPluginManifests(root) {
12228
12469
  function readSubscriptions(absManifestPath) {
12229
12470
  let parsed;
12230
12471
  try {
12231
- parsed = JSON.parse(readFileSync37(absManifestPath, "utf8"));
12472
+ parsed = JSON.parse(readFileSync38(absManifestPath, "utf8"));
12232
12473
  } catch {
12233
12474
  return null;
12234
12475
  }
@@ -12243,14 +12484,14 @@ function readSubscriptions(absManifestPath) {
12243
12484
  }
12244
12485
  function checkPluginTerraform(root) {
12245
12486
  const violations = [];
12246
- const coreManifest = existsSync44(join52(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
12487
+ const coreManifest = existsSync45(join53(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
12247
12488
  for (const manifest of findPluginManifests(root)) {
12248
12489
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
12249
- const absManifest = join52(root, manifest);
12490
+ const absManifest = join53(root, manifest);
12250
12491
  const subscriptions = readSubscriptions(absManifest);
12251
12492
  if (subscriptions === null) continue;
12252
12493
  const pluginDir2 = dirname10(absManifest);
12253
- if (existsSync44(join52(pluginDir2, "terraform"))) continue;
12494
+ if (existsSync45(join53(pluginDir2, "terraform"))) continue;
12254
12495
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
12255
12496
  violations.push({
12256
12497
  manifest,
@@ -12270,7 +12511,7 @@ function formatViolations(violations) {
12270
12511
 
12271
12512
  // src/scripts/check-plugin-terraform.ts
12272
12513
  async function runPluginTerraformCheck() {
12273
- const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12514
+ const root = (await execa20("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12274
12515
  const violations = checkPluginTerraform(root);
12275
12516
  if (violations.length > 0) {
12276
12517
  console.error("\u2717 plugin Terraform guard: event subscriptions with no infrastructure\n");
@@ -12281,13 +12522,13 @@ async function runPluginTerraformCheck() {
12281
12522
  }
12282
12523
 
12283
12524
  // src/scripts/check-plugin-tool-supply.ts
12284
- import { existsSync as existsSync46 } from "fs";
12285
- import { join as join54 } from "path";
12286
- import { execa as execa19 } from "execa";
12525
+ import { existsSync as existsSync47 } from "fs";
12526
+ import { join as join55 } from "path";
12527
+ import { execa as execa21 } from "execa";
12287
12528
 
12288
12529
  // src/lib/plugin-tool-supply-audit.ts
12289
- import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
12290
- import { join as join53 } from "path";
12530
+ import { existsSync as existsSync46, readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
12531
+ import { join as join54 } from "path";
12291
12532
 
12292
12533
  // src/lib/openrouter-model-snapshot.ts
12293
12534
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -12698,13 +12939,13 @@ var OPENROUTER_MODEL_IDS = [
12698
12939
  function listDirs(root) {
12699
12940
  let entries;
12700
12941
  try {
12701
- entries = readdirSync24(root);
12942
+ entries = readdirSync25(root);
12702
12943
  } catch {
12703
12944
  return [];
12704
12945
  }
12705
12946
  return entries.filter((e) => {
12706
12947
  try {
12707
- return statSync15(join53(root, e)).isDirectory();
12948
+ return statSync15(join54(root, e)).isDirectory();
12708
12949
  } catch {
12709
12950
  return false;
12710
12951
  }
@@ -12715,12 +12956,12 @@ function walkFiles2(root, accept, skipDir) {
12715
12956
  const walk2 = (dir) => {
12716
12957
  let entries;
12717
12958
  try {
12718
- entries = readdirSync24(dir);
12959
+ entries = readdirSync25(dir);
12719
12960
  } catch {
12720
12961
  return;
12721
12962
  }
12722
12963
  for (const entry of entries) {
12723
- const p = join53(dir, entry);
12964
+ const p = join54(dir, entry);
12724
12965
  let st;
12725
12966
  try {
12726
12967
  st = statSync15(p);
@@ -12746,14 +12987,14 @@ function pluginPythonFiles(pluginDir2) {
12746
12987
  );
12747
12988
  }
12748
12989
  function pluginTerraformFiles(pluginDir2) {
12749
- const tfDir = join53(pluginDir2, "terraform");
12990
+ const tfDir = join54(pluginDir2, "terraform");
12750
12991
  let entries;
12751
12992
  try {
12752
- entries = readdirSync24(tfDir);
12993
+ entries = readdirSync25(tfDir);
12753
12994
  } catch {
12754
12995
  return [];
12755
12996
  }
12756
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join53(tfDir, e)).sort();
12997
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join54(tfDir, e)).sort();
12757
12998
  }
12758
12999
  function extractManifestTools(manifestText) {
12759
13000
  let parsed;
@@ -13005,8 +13246,8 @@ function isSnapshotStale(fetchedAt, now) {
13005
13246
  function normalizeModelId(id) {
13006
13247
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
13007
13248
  }
13008
- var CONFIG_PY_PATH = join53("services", "api", "src", "api", "config.py");
13009
- var ORCHESTRATION_SCHEMA_PATH = join53(
13249
+ var CONFIG_PY_PATH = join54("services", "api", "src", "api", "config.py");
13250
+ var ORCHESTRATION_SCHEMA_PATH = join54(
13010
13251
  "services",
13011
13252
  "api",
13012
13253
  "src",
@@ -13018,10 +13259,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13018
13259
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
13019
13260
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
13020
13261
  const now = options.now ?? /* @__PURE__ */ new Date();
13021
- const configPath = join53(repoRoot, CONFIG_PY_PATH);
13022
- const orchestrationPath = join53(repoRoot, ORCHESTRATION_SCHEMA_PATH);
13023
- const configMissing = !existsSync45(configPath);
13024
- const orchestrationSchemaMissing = !existsSync45(orchestrationPath);
13262
+ const configPath = join54(repoRoot, CONFIG_PY_PATH);
13263
+ const orchestrationPath = join54(repoRoot, ORCHESTRATION_SCHEMA_PATH);
13264
+ const configMissing = !existsSync46(configPath);
13265
+ const orchestrationSchemaMissing = !existsSync46(orchestrationPath);
13025
13266
  const knownSet = new Set(knownModelIds);
13026
13267
  const snapshotEmpty = knownModelIds.length === 0;
13027
13268
  const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
@@ -13039,13 +13280,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13039
13280
  };
13040
13281
  let settingsBlind = false;
13041
13282
  if (!configMissing) {
13042
- const settingsFields = extractSettingsModelFields(readFileSync38(configPath, "utf8"));
13283
+ const settingsFields = extractSettingsModelFields(readFileSync39(configPath, "utf8"));
13043
13284
  if (settingsFields.length === 0) settingsBlind = true;
13044
13285
  for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
13045
13286
  }
13046
13287
  let curatedFieldsBlind = false;
13047
13288
  if (!orchestrationSchemaMissing) {
13048
- const curated = extractCuratedModelFields(readFileSync38(orchestrationPath, "utf8"));
13289
+ const curated = extractCuratedModelFields(readFileSync39(orchestrationPath, "utf8"));
13049
13290
  if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
13050
13291
  curatedFieldsBlind = true;
13051
13292
  }
@@ -13092,7 +13333,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13092
13333
  function discoverPluginDirs(pluginsRoot) {
13093
13334
  return listDirs(pluginsRoot).filter((name) => {
13094
13335
  try {
13095
- return statSync15(join53(pluginsRoot, name, "biffo.plugin.json")).isFile();
13336
+ return statSync15(join54(pluginsRoot, name, "biffo.plugin.json")).isFile();
13096
13337
  } catch {
13097
13338
  return false;
13098
13339
  }
@@ -13105,8 +13346,8 @@ function auditPluginToolSupply(pluginsRoot) {
13105
13346
  let terraformBlind = false;
13106
13347
  let totalDeclaredTools = 0;
13107
13348
  for (const name of pluginNames) {
13108
- const pluginDir2 = join53(pluginsRoot, name);
13109
- const manifestText = readFileSync38(join53(pluginDir2, "biffo.plugin.json"), "utf8");
13349
+ const pluginDir2 = join54(pluginsRoot, name);
13350
+ const manifestText = readFileSync39(join54(pluginDir2, "biffo.plugin.json"), "utf8");
13110
13351
  const manifest = extractManifestTools(manifestText);
13111
13352
  if (manifest.parseError) {
13112
13353
  findings.push({
@@ -13124,13 +13365,13 @@ function auditPluginToolSupply(pluginsRoot) {
13124
13365
  totalDeclaredTools += manifest.tools.length;
13125
13366
  const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
13126
13367
  file: f,
13127
- text: readFileSync38(f, "utf8")
13368
+ text: readFileSync39(f, "utf8")
13128
13369
  }));
13129
13370
  const resolver = buildSymbolResolver(pySources);
13130
13371
  const registry = extractToolRegistryEntries(pySources, resolver);
13131
13372
  if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
13132
13373
  const tfFiles = pluginTerraformFiles(pluginDir2);
13133
- const tfText = tfFiles.map((f) => readFileSync38(f, "utf8")).join("\n");
13374
+ const tfText = tfFiles.map((f) => readFileSync39(f, "utf8")).join("\n");
13134
13375
  const terraform = extractTerraformEnvKeys(tfText);
13135
13376
  if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
13136
13377
  for (const toolName of manifest.tools) {
@@ -13204,7 +13445,7 @@ function auditPluginToolSupply(pluginsRoot) {
13204
13445
  requiredEnvVars: envResult.envVars,
13205
13446
  missingEnvVars: anyWired ? [] : envResult.envVars,
13206
13447
  status: anyWired ? "ok" : "missing-env",
13207
- detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join53(pluginDir2, "terraform")}, so this deployment can never supply it`
13448
+ detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join54(pluginDir2, "terraform")}, so this deployment can never supply it`
13208
13449
  });
13209
13450
  }
13210
13451
  }
@@ -13235,10 +13476,10 @@ function auditPluginToolSupply(pluginsRoot) {
13235
13476
 
13236
13477
  // src/scripts/check-plugin-tool-supply.ts
13237
13478
  async function runPluginToolSupplyCheck() {
13238
- const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13479
+ const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13239
13480
  let allOk = true;
13240
- const pluginsRoot = join54(root, "services", "_plugins");
13241
- if (!existsSync46(pluginsRoot)) {
13481
+ const pluginsRoot = join55(root, "services", "_plugins");
13482
+ if (!existsSync47(pluginsRoot)) {
13242
13483
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
13243
13484
  } else {
13244
13485
  const report = auditPluginToolSupply(pluginsRoot);
@@ -13268,8 +13509,8 @@ async function runPluginToolSupplyCheck() {
13268
13509
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
13269
13510
  }
13270
13511
  }
13271
- const servicesApiRoot = join54(root, "services", "api");
13272
- if (!existsSync46(servicesApiRoot)) {
13512
+ const servicesApiRoot = join55(root, "services", "api");
13513
+ if (!existsSync47(servicesApiRoot)) {
13273
13514
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
13274
13515
  } else {
13275
13516
  const modelReport = auditDeclaredModelIds(root);
@@ -13315,7 +13556,7 @@ async function runPluginToolSupplyCheck() {
13315
13556
  }
13316
13557
 
13317
13558
  // src/scripts/check-release-subject.ts
13318
- import { execa as execa20 } from "execa";
13559
+ import { execa as execa22 } from "execa";
13319
13560
 
13320
13561
  // src/lib/release-version.ts
13321
13562
  var MINOR_TYPES = /* @__PURE__ */ new Set(["feat"]);
@@ -13352,7 +13593,7 @@ async function fetchPrTitleViaGh({
13352
13593
  PR_NUMBER,
13353
13594
  GH_REPO
13354
13595
  }) {
13355
- const { stdout } = await execa20(
13596
+ const { stdout } = await execa22(
13356
13597
  "gh",
13357
13598
  ["pr", "view", PR_NUMBER, "--repo", GH_REPO, "--json", "title", "--jq", ".title"],
13358
13599
  { env: { ...process.env, GH_TOKEN } }
@@ -13388,7 +13629,7 @@ async function resolveReleaseSubject({
13388
13629
  );
13389
13630
  }
13390
13631
  }
13391
- return (await execa20("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
13632
+ return (await execa22("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
13392
13633
  }
13393
13634
  async function runReleaseSubjectCheck(argv) {
13394
13635
  const base = process.env["GITHUB_BASE_REF"] ?? argv[0];
@@ -13396,9 +13637,9 @@ async function runReleaseSubjectCheck(argv) {
13396
13637
  console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
13397
13638
  process.exit(2);
13398
13639
  }
13399
- const root = (await execa20("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13400
- await execa20("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
13401
- const { stdout } = await execa20("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
13640
+ const root = (await execa22("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13641
+ await execa22("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
13642
+ const { stdout } = await execa22("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
13402
13643
  cwd: root
13403
13644
  });
13404
13645
  const changedFiles = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
@@ -13446,13 +13687,13 @@ async function runReleaseSubjectCheck(argv) {
13446
13687
  }
13447
13688
 
13448
13689
  // src/scripts/check-skeleton-drift.ts
13449
- import { existsSync as existsSync47, readdirSync as readdirSync26 } from "fs";
13450
- import { join as join56 } from "path";
13451
- import { execa as execa21 } from "execa";
13690
+ import { existsSync as existsSync48, readdirSync as readdirSync27 } from "fs";
13691
+ import { join as join57 } from "path";
13692
+ import { execa as execa23 } from "execa";
13452
13693
 
13453
13694
  // src/lib/skeleton-drift-guard.ts
13454
- import { readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync16 } from "fs";
13455
- import { join as join55 } from "path";
13695
+ import { readFileSync as readFileSync40, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
13696
+ import { join as join56 } from "path";
13456
13697
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
13457
13698
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
13458
13699
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -13510,13 +13751,13 @@ function walk(dir, base = dir) {
13510
13751
  const out = [];
13511
13752
  let entries;
13512
13753
  try {
13513
- entries = readdirSync25(dir);
13754
+ entries = readdirSync26(dir);
13514
13755
  } catch {
13515
13756
  return out;
13516
13757
  }
13517
13758
  for (const entry of entries) {
13518
13759
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
13519
- const abs = join55(dir, entry);
13760
+ const abs = join56(dir, entry);
13520
13761
  let isDir;
13521
13762
  try {
13522
13763
  isDir = statSync16(abs).isDirectory();
@@ -13538,7 +13779,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
13538
13779
  if (!rule.appliesTo(rel)) continue;
13539
13780
  let contents;
13540
13781
  try {
13541
- contents = readFileSync39(join55(skeletonRoot, rel), "utf8");
13782
+ contents = readFileSync40(join56(skeletonRoot, rel), "utf8");
13542
13783
  } catch {
13543
13784
  continue;
13544
13785
  }
@@ -13567,23 +13808,23 @@ function formatViolations2(violations) {
13567
13808
 
13568
13809
  // src/scripts/check-skeleton-drift.ts
13569
13810
  function discoverSkeletons(root) {
13570
- const skeletonsDir = join56(root, "_skeletons");
13811
+ const skeletonsDir = join57(root, "_skeletons");
13571
13812
  let entries;
13572
13813
  try {
13573
- entries = readdirSync26(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
13814
+ entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
13574
13815
  } catch {
13575
13816
  return [];
13576
13817
  }
13577
- return entries.filter((name) => existsSync47(join56(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
13818
+ return entries.filter((name) => existsSync48(join57(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
13578
13819
  }
13579
13820
  async function runSkeletonDriftCheck() {
13580
- const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13821
+ const root = (await execa23("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13581
13822
  const skeletons = discoverSkeletons(root);
13582
13823
  let filesConsidered = 0;
13583
13824
  for (const name of skeletons) {
13584
- const skeletonRoot = join56(root, "_skeletons", name);
13825
+ const skeletonRoot = join57(root, "_skeletons", name);
13585
13826
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
13586
- if (existsSync47(join56(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
13827
+ if (existsSync48(join57(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
13587
13828
  filesConsidered += 1;
13588
13829
  }
13589
13830
  }
@@ -13597,7 +13838,7 @@ async function runSkeletonDriftCheck() {
13597
13838
  process.exit(1);
13598
13839
  }
13599
13840
  const violations = skeletons.flatMap(
13600
- (name) => auditSkeleton(join56(root, "_skeletons", name), name)
13841
+ (name) => auditSkeleton(join57(root, "_skeletons", name), name)
13601
13842
  );
13602
13843
  if (violations.length > 0) {
13603
13844
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -13609,9 +13850,9 @@ async function runSkeletonDriftCheck() {
13609
13850
  }
13610
13851
 
13611
13852
  // src/scripts/check-terraform-input.ts
13612
- import { execa as execa22 } from "execa";
13853
+ import { execa as execa24 } from "execa";
13613
13854
  async function runTerraformInputCheck() {
13614
- const root = (await execa22("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13855
+ const root = (await execa24("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13615
13856
  const files = findWorkflowFiles(root);
13616
13857
  console.log(`audited ${files.length} workflow file(s) under ${root}`);
13617
13858
  if (files.length === 0) {
@@ -13701,6 +13942,11 @@ checkCommand.command("codeql-suppression").description(
13701
13942
  ).action(async () => {
13702
13943
  await runCodeqlSuppressionCheck();
13703
13944
  });
13945
+ checkCommand.command("claim-invocation").description(
13946
+ "Refuse a distributed AGENTS.md that documents a different claim invocation from the others, or an untokened `claim <issue>` (#1562) \u2014 `--as` reached one of three copies, so it was documented in zero satellites while working perfectly"
13947
+ ).action(async () => {
13948
+ await runClaimInvocationCheck();
13949
+ });
13704
13950
  checkCommand.command("skeleton-drift").description(
13705
13951
  "Refuse a fix this repo made for itself that never reached _skeletons/ \u2014 a hardcoded runner, the paid gitleaks action, an unhardened dependency audit, a hard-coded app title"
13706
13952
  ).action(async () => {
@@ -13735,8 +13981,8 @@ function rawArgsAfter(subcommand) {
13735
13981
  }
13736
13982
 
13737
13983
  // src/commands/doctor.ts
13738
- import { existsSync as existsSync48, readFileSync as readFileSync40 } from "fs";
13739
- import { join as join57, resolve as resolve20 } from "path";
13984
+ import { existsSync as existsSync49, readFileSync as readFileSync41 } from "fs";
13985
+ import { join as join58, resolve as resolve20 } from "path";
13740
13986
  import chalk21 from "chalk";
13741
13987
  import { Command as Command25 } from "commander";
13742
13988
 
@@ -13915,10 +14161,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
13915
14161
  return runDoctorChecks(facts);
13916
14162
  }
13917
14163
  function readLocalCoreVersion(cwd) {
13918
- const path = join57(cwd, INSTANCE_CORE_FILE);
13919
- if (!existsSync48(path)) return null;
14164
+ const path = join58(cwd, INSTANCE_CORE_FILE);
14165
+ if (!existsSync49(path)) return null;
13920
14166
  try {
13921
- return extractVersionField(readFileSync40(path, "utf8"));
14167
+ return extractVersionField(readFileSync41(path, "utf8"));
13922
14168
  } catch {
13923
14169
  return null;
13924
14170
  }
@@ -13938,10 +14184,10 @@ function extractVersionField(contents) {
13938
14184
  return match?.[1] ?? null;
13939
14185
  }
13940
14186
  function readFossil(cwd) {
13941
- const path = join57(cwd, CORE_VERSION_FILE);
13942
- if (!existsSync48(path)) return null;
14187
+ const path = join58(cwd, CORE_VERSION_FILE);
14188
+ if (!existsSync49(path)) return null;
13943
14189
  try {
13944
- const value = readFileSync40(path, "utf8").trim();
14190
+ const value = readFileSync41(path, "utf8").trim();
13945
14191
  return value === "" ? null : value;
13946
14192
  } catch {
13947
14193
  return null;
@@ -14390,13 +14636,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
14390
14636
  import { Command as Command27 } from "commander";
14391
14637
 
14392
14638
  // src/lib/packaged-scripts.ts
14393
- import { existsSync as existsSync49 } from "fs";
14394
- import { dirname as dirname11, join as join58 } from "path";
14639
+ import { existsSync as existsSync50 } from "fs";
14640
+ import { dirname as dirname11, join as join59 } from "path";
14395
14641
  function findPackagedScript(startDir, relativePath) {
14396
14642
  let dir = startDir;
14397
14643
  for (; ; ) {
14398
- const candidate = join58(dir, relativePath);
14399
- if (existsSync49(candidate)) return candidate;
14644
+ const candidate = join59(dir, relativePath);
14645
+ if (existsSync50(candidate)) return candidate;
14400
14646
  const parent = dirname11(dir);
14401
14647
  if (parent === dir) return null;
14402
14648
  dir = parent;