@biffo/cli 0.317.2 → 0.318.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.
Files changed (2) hide show
  1. package/dist/index.js +310 -205
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command28 } from "commander";
4
+ import { Command as Command29 } from "commander";
5
5
 
6
6
  // src/commands/core.ts
7
7
  import { Command as Command4 } from "commander";
@@ -1934,7 +1934,7 @@ var GitHubAdapter = class {
1934
1934
  } catch (err) {
1935
1935
  if (err.status !== 404) throw err;
1936
1936
  }
1937
- await new Promise((resolve21) => setTimeout(resolve21, intervalMs));
1937
+ await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
1938
1938
  }
1939
1939
  throw new Error(
1940
1940
  `Branch "${branch}" not found in ${org}/${repo} after ${timeoutMs / 1e3}s \u2014 GitHub template generation may have stalled. Check the repository and re-run biffo init.`
@@ -1949,7 +1949,7 @@ var GitHubAdapter = class {
1949
1949
  } catch (err) {
1950
1950
  if (err.status !== 404) throw err;
1951
1951
  }
1952
- await new Promise((resolve21) => setTimeout(resolve21, intervalMs));
1952
+ await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
1953
1953
  }
1954
1954
  throw new Error(
1955
1955
  `Ref "${ref}" not found in ${org}/${repo} after ${timeoutMs / 1e3}s \u2014 GitHub template generation may have stalled. Check the repository and re-run biffo init.`
@@ -2183,7 +2183,7 @@ var GitHubAdapter = class {
2183
2183
  }
2184
2184
  if (status !== 404 || Date.now() >= deadline) throw err;
2185
2185
  log.info("Branch protection endpoint not yet ready, retrying...");
2186
- await new Promise((resolve21) => setTimeout(resolve21, protectionIntervalMs));
2186
+ await new Promise((resolve22) => setTimeout(resolve22, protectionIntervalMs));
2187
2187
  }
2188
2188
  }
2189
2189
  }
@@ -2356,7 +2356,7 @@ var GitHubAdapter = class {
2356
2356
  }
2357
2357
  if (status !== 404 || Date.now() >= deadline) throw err;
2358
2358
  log.info("Branch protection endpoint not yet ready, retrying...");
2359
- await new Promise((resolve21) => setTimeout(resolve21, protectionIntervalMs));
2359
+ await new Promise((resolve22) => setTimeout(resolve22, protectionIntervalMs));
2360
2360
  }
2361
2361
  }
2362
2362
  } catch (err) {
@@ -2629,7 +2629,7 @@ var GitHubAdapter = class {
2629
2629
  } catch (err) {
2630
2630
  if (err.status !== 404 || Date.now() >= deadline) throw err;
2631
2631
  log.info(`Workflow ${workflowId} not yet indexed by GitHub Actions, retrying...`);
2632
- await new Promise((resolve21) => setTimeout(resolve21, intervalMs));
2632
+ await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
2633
2633
  }
2634
2634
  }
2635
2635
  }
@@ -2648,7 +2648,7 @@ var GitHubAdapter = class {
2648
2648
  } catch (err) {
2649
2649
  if (err.status !== 404 || Date.now() >= deadline) throw err;
2650
2650
  log.info(`Workflow ${workflowId} not yet indexed by GitHub Actions, retrying...`);
2651
- await new Promise((resolve21) => setTimeout(resolve21, intervalMs));
2651
+ await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
2652
2652
  }
2653
2653
  }
2654
2654
  }
@@ -2672,7 +2672,7 @@ var GitHubAdapter = class {
2672
2672
  } else {
2673
2673
  log.info(" Waiting for run to be queued...");
2674
2674
  }
2675
- await new Promise((resolve21) => setTimeout(resolve21, intervalMs));
2675
+ await new Promise((resolve22) => setTimeout(resolve22, intervalMs));
2676
2676
  }
2677
2677
  throw new Error(
2678
2678
  `Workflow ${workflowId} did not complete within ${timeoutMs / 1e3 / 60} minutes`
@@ -5031,7 +5031,7 @@ var AwsAdapter = class {
5031
5031
  const code = err.Code;
5032
5032
  if (code === "OperationAborted" && attempt < maxAttempts) {
5033
5033
  log.info(` Waiting for S3 to release "${bucketName}"... (${attempt}/${maxAttempts})`);
5034
- await new Promise((resolve21) => setTimeout(resolve21, retryDelayMs));
5034
+ await new Promise((resolve22) => setTimeout(resolve22, retryDelayMs));
5035
5035
  } else if (code === "OperationAborted") {
5036
5036
  return false;
5037
5037
  } else {
@@ -8295,7 +8295,7 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
8295
8295
  }
8296
8296
 
8297
8297
  // src/commands/plugin.ts
8298
- import { Command as Command21 } from "commander";
8298
+ import { Command as Command22 } from "commander";
8299
8299
 
8300
8300
  // src/commands/plugin-create.ts
8301
8301
  import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
@@ -11233,8 +11233,132 @@ function printLocalDryRun(source, currentVersion, inTreeSource) {
11233
11233
  `);
11234
11234
  }
11235
11235
 
11236
+ // src/commands/plugin-verify.ts
11237
+ import { dirname as dirname11, resolve as resolve18 } from "path";
11238
+ import { fileURLToPath as fileURLToPath6 } from "url";
11239
+ import { Command as Command21 } from "commander";
11240
+
11241
+ // src/lib/plugin-verify/command-runner.ts
11242
+ import { spawnSync } from "child_process";
11243
+ var RealCommandRunner = class {
11244
+ run(cmd, args, opts) {
11245
+ const result = spawnSync(cmd, args, {
11246
+ cwd: opts.cwd,
11247
+ // Progress/errors always go to the terminal so a human watching a local
11248
+ // run (or CI's own log) sees them as they happen; stdout is captured only
11249
+ // when the caller needs the value back (pg-test-db.sh's DSN) — capturing
11250
+ // it unconditionally would silence the conformance checks' own printed
11251
+ // denominators, which are the whole point of this feature (#1363's shape).
11252
+ stdio: ["ignore", opts.captureStdout ? "pipe" : "inherit", "inherit"],
11253
+ encoding: "utf8"
11254
+ });
11255
+ return { status: result.status, stdout: result.stdout ?? "" };
11256
+ }
11257
+ };
11258
+
11259
+ // src/lib/packaged-scripts.ts
11260
+ import { existsSync as existsSync38 } from "fs";
11261
+ import { dirname as dirname10, join as join40 } from "path";
11262
+ function findPackagedScript(startDir, relativePath) {
11263
+ let dir = startDir;
11264
+ for (; ; ) {
11265
+ const candidate = join40(dir, relativePath);
11266
+ if (existsSync38(candidate)) return candidate;
11267
+ const parent = dirname10(dir);
11268
+ if (parent === dir) return null;
11269
+ dir = parent;
11270
+ }
11271
+ }
11272
+ function packagedScriptMissing(relativePath) {
11273
+ return `biffo: cannot find ${relativePath}.
11274
+ It ships with this package via cli/scripts/packaged-root-assets.mjs; if you are running from a checkout, run from inside the template repo. If you are running the published package, this is a packaging bug \u2014 the asset was not copied at prepack.`;
11275
+ }
11276
+
11277
+ // src/lib/plugin-verify/conformance-driver.ts
11278
+ var CONFORMANCE_MODULE = "biffo_plugin_sdk.conformance";
11279
+ function uvRunPython(runner, cwd, args, captureStdout = false) {
11280
+ return runner.run(
11281
+ "uv",
11282
+ ["run", "--no-sync", "--directory", cwd, "python", "-m", CONFORMANCE_MODULE, ...args],
11283
+ { cwd, captureStdout }
11284
+ );
11285
+ }
11286
+ function listChecks(runner, cwd) {
11287
+ const { status } = uvRunPython(runner, cwd, ["list-checks"]);
11288
+ return status ?? 2;
11289
+ }
11290
+ function runChecksOnce(runner, cwd, dsn) {
11291
+ const { status } = uvRunPython(runner, cwd, ["run", "--repo-root", cwd, "--dsn", dsn]);
11292
+ return status ?? 2;
11293
+ }
11294
+
11295
+ // src/lib/plugin-verify/raise-postgres.ts
11296
+ function raisePostgres(runner, script, cwd) {
11297
+ const { status, stdout } = runner.run(script, [], { cwd, captureStdout: true });
11298
+ if (status !== 0) {
11299
+ return { dsn: null, status: status ?? 2 };
11300
+ }
11301
+ const dsn = stdout.trim();
11302
+ if (!dsn) {
11303
+ return { dsn: null, status: 1 };
11304
+ }
11305
+ return { dsn, status: 0 };
11306
+ }
11307
+
11308
+ // src/lib/plugin-verify/run-plugin-verify.ts
11309
+ var PG_TEST_DB_SCRIPT = "scripts/pg-test-db.sh";
11310
+ async function runPluginVerify(options, deps) {
11311
+ if (options.listChecks) {
11312
+ return listChecks(deps.runner, options.cwd);
11313
+ }
11314
+ const script = deps.findScript(PG_TEST_DB_SCRIPT);
11315
+ if (!script) {
11316
+ log.error(packagedScriptMissing(PG_TEST_DB_SCRIPT));
11317
+ return 2;
11318
+ }
11319
+ const raised = raisePostgres(deps.runner, script, options.cwd);
11320
+ if (!raised.dsn) {
11321
+ log.error(
11322
+ `plugin verify: could not provision the local Postgres test database (${PG_TEST_DB_SCRIPT} exited ${raised.status})`
11323
+ );
11324
+ return raised.status;
11325
+ }
11326
+ log.info("plugin verify: pass 1 of 2");
11327
+ const first = runChecksOnce(deps.runner, options.cwd, raised.dsn);
11328
+ if (first !== 0) {
11329
+ log.error("plugin verify: the first run failed -- see the check output above");
11330
+ return first;
11331
+ }
11332
+ log.info("plugin verify: pass 2 of 2 (re-runability against the same database)");
11333
+ const second = runChecksOnce(deps.runner, options.cwd, raised.dsn);
11334
+ if (second !== 0) {
11335
+ log.error(
11336
+ "plugin verify: a second consecutive run against the same database failed -- a check that is not re-runnable fails the job (biffo-template#1924)"
11337
+ );
11338
+ return second;
11339
+ }
11340
+ log.success("plugin verify: green twice against the same database");
11341
+ return 0;
11342
+ }
11343
+
11344
+ // src/commands/plugin-verify.ts
11345
+ var pluginVerifyCommand = new Command21("verify").description(
11346
+ "Real-execution conformance harness for a plugin repo: raises a real Postgres, runs every implemented biffo_plugin_sdk.conformance check twice against it, and exits non-zero on the first failure (biffo-template#1523/#1924)."
11347
+ ).option("--cwd <path>", "Plugin repo root to check (defaults to the current directory)").option("--list-checks", "Print every declared #1523 seam and its implemented state, then exit").action(async (options) => {
11348
+ const cwd = options.cwd ? resolve18(options.cwd) : process.cwd();
11349
+ const here = dirname11(fileURLToPath6(import.meta.url));
11350
+ const exitCode = await runPluginVerify(
11351
+ { cwd, listChecks: options.listChecks ?? false },
11352
+ {
11353
+ runner: new RealCommandRunner(),
11354
+ findScript: (relativePath) => findPackagedScript(here, relativePath)
11355
+ }
11356
+ );
11357
+ process.exit(exitCode);
11358
+ });
11359
+
11236
11360
  // src/commands/plugin.ts
11237
- var pluginCommand = new Command21("plugin").description("Manage Biffo plugins");
11361
+ var pluginCommand = new Command22("plugin").description("Manage Biffo plugins");
11238
11362
  pluginCommand.addCommand(pluginCreateCommand);
11239
11363
  pluginCommand.addCommand(pluginListCommand);
11240
11364
  pluginCommand.addCommand(pluginInstallCommand);
@@ -11243,15 +11367,16 @@ pluginCommand.addCommand(pluginUpgradeCommand);
11243
11367
  pluginCommand.addCommand(pluginSyncMigrationsCommand);
11244
11368
  pluginCommand.addCommand(pluginInfoCommand);
11245
11369
  pluginCommand.addCommand(pluginStalenessCommand);
11370
+ pluginCommand.addCommand(pluginVerifyCommand);
11246
11371
 
11247
11372
  // src/commands/sibling.ts
11248
- import { Command as Command23 } from "commander";
11373
+ import { Command as Command24 } from "commander";
11249
11374
 
11250
11375
  // src/commands/sibling-check-identity.ts
11251
- import { existsSync as existsSync38, readFileSync as readFileSync29 } from "fs";
11252
- import { resolve as resolve18 } from "path";
11376
+ import { existsSync as existsSync39, readFileSync as readFileSync29 } from "fs";
11377
+ import { resolve as resolve19 } from "path";
11253
11378
  import chalk20 from "chalk";
11254
- import { Command as Command22 } from "commander";
11379
+ import { Command as Command23 } from "commander";
11255
11380
 
11256
11381
  // src/lib/sibling-identity-check.ts
11257
11382
  function checkSiblingIdentity(envs) {
@@ -11303,7 +11428,7 @@ function checkSiblingIdentity(envs) {
11303
11428
  // src/commands/sibling-check-identity.ts
11304
11429
  var VALID_ENVIRONMENTS2 = ["dev", "staging", "prod"];
11305
11430
  var SIBLING_CORE_POOL_VAR = "CORE_COGNITO_USER_POOL_ID";
11306
- var siblingCheckIdentityCommand = new Command22("check-identity").description(
11431
+ var siblingCheckIdentityCommand = new Command23("check-identity").description(
11307
11432
  "Detect when a core's Cognito pool has drifted from its published identity document or any sibling's baked-in CORE_COGNITO_USER_POOL_ID (#400). Run from the core repo; exits non-zero on drift so a scheduled/CI run goes red."
11308
11433
  ).option("--env <environment>", "Only check this environment (default: dev, staging, prod)").option("-p, --project <name>", "Project name (overrides biffo.config.json in current directory)").option("-c, --config <path>", "Path to biffo.config.json").action(async (options) => {
11309
11434
  if (options.env && !VALID_ENVIRONMENTS2.includes(options.env)) {
@@ -11442,7 +11567,7 @@ async function fetchPublishedIdentity(portalUrl) {
11442
11567
  }
11443
11568
  async function resolveConfig4(options) {
11444
11569
  if (options.config) {
11445
- const raw = JSON.parse(readFileSync29(resolve18(options.config), "utf8"));
11570
+ const raw = JSON.parse(readFileSync29(resolve19(options.config), "utf8"));
11446
11571
  const result = BiffoConfigSchema.safeParse(raw);
11447
11572
  if (!result.success) {
11448
11573
  log.error(`Invalid config at ${options.config}:`);
@@ -11461,8 +11586,8 @@ async function resolveConfig4(options) {
11461
11586
  }
11462
11587
  return cfg;
11463
11588
  }
11464
- const localConfigPath = resolve18(process.cwd(), "biffo.config.json");
11465
- if (existsSync38(localConfigPath)) {
11589
+ const localConfigPath = resolve19(process.cwd(), "biffo.config.json");
11590
+ if (existsSync39(localConfigPath)) {
11466
11591
  const raw = JSON.parse(readFileSync29(localConfigPath, "utf8"));
11467
11592
  const result = BiffoConfigSchema.safeParse(raw);
11468
11593
  if (result.success) return result.data;
@@ -11499,28 +11624,28 @@ async function resolveConfig4(options) {
11499
11624
  }
11500
11625
 
11501
11626
  // src/commands/sibling.ts
11502
- var siblingCommand = new Command23("sibling").description(
11627
+ var siblingCommand = new Command24("sibling").description(
11503
11628
  "Create and manage sibling apps that share a Biffo core project (ADR-0007)"
11504
11629
  );
11505
11630
  siblingCommand.addCommand(siblingCreateCommand);
11506
11631
  siblingCommand.addCommand(siblingCheckIdentityCommand);
11507
11632
 
11508
11633
  // src/commands/check.ts
11509
- import { Command as Command24 } from "commander";
11634
+ import { Command as Command25 } from "commander";
11510
11635
 
11511
11636
  // src/scripts/check-adr-numbering.ts
11512
- import { existsSync as existsSync40 } from "fs";
11513
- import { join as join41 } from "path";
11637
+ import { existsSync as existsSync41 } from "fs";
11638
+ import { join as join42 } from "path";
11514
11639
 
11515
11640
  // src/lib/adr-numbering-guard.ts
11516
- import { existsSync as existsSync39, readdirSync as readdirSync15, readFileSync as readFileSync30 } from "fs";
11517
- import { join as join40 } from "path";
11641
+ import { existsSync as existsSync40, readdirSync as readdirSync15, readFileSync as readFileSync30 } from "fs";
11642
+ import { join as join41 } from "path";
11518
11643
  var ADR_FILENAME = /^(\d{4})-.+\.md$/;
11519
11644
  var ALLOWLIST_FILENAME = ".numbering-allowlist";
11520
11645
  var TEMPLATE_ADR_RESERVED_UPTO = "0099";
11521
11646
  function readAdrNumberingAllowlist(adrDir) {
11522
- const path = join40(adrDir, ALLOWLIST_FILENAME);
11523
- if (!existsSync39(path)) return /* @__PURE__ */ new Set();
11647
+ const path = join41(adrDir, ALLOWLIST_FILENAME);
11648
+ if (!existsSync40(path)) return /* @__PURE__ */ new Set();
11524
11649
  const numbers = /* @__PURE__ */ new Set();
11525
11650
  for (const rawLine of readFileSync30(path, "utf8").split("\n")) {
11526
11651
  const line = rawLine.split("#")[0].trim();
@@ -11530,7 +11655,7 @@ function readAdrNumberingAllowlist(adrDir) {
11530
11655
  }
11531
11656
  function adrNumbersIn(adrDir) {
11532
11657
  const claims = /* @__PURE__ */ new Map();
11533
- if (!existsSync39(adrDir)) return claims;
11658
+ if (!existsSync40(adrDir)) return claims;
11534
11659
  for (const entry of readdirSync15(adrDir).sort()) {
11535
11660
  const match = ADR_FILENAME.exec(entry);
11536
11661
  if (!match) continue;
@@ -11585,8 +11710,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
11585
11710
  // src/scripts/check-adr-numbering.ts
11586
11711
  async function runAdrNumberingCheck() {
11587
11712
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11588
- const adrDir = join41(root, "docs", "ADR");
11589
- if (!existsSync40(adrDir)) {
11713
+ const adrDir = join42(root, "docs", "ADR");
11714
+ if (!existsSync41(adrDir)) {
11590
11715
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
11591
11716
  return;
11592
11717
  }
@@ -11625,7 +11750,7 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
11625
11750
 
11626
11751
  // src/lib/api-gateway-integration-guard.ts
11627
11752
  import { readFileSync as readFileSync31, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
11628
- import { join as join42 } from "path";
11753
+ import { join as join43 } from "path";
11629
11754
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
11630
11755
  var MODULE_TYPE = "module";
11631
11756
  var INTEGRATION_TYPE = "aws_apigatewayv2_integration";
@@ -11707,7 +11832,7 @@ function walkTerraformFiles(root) {
11707
11832
  return;
11708
11833
  }
11709
11834
  for (const entry of entries) {
11710
- const p = join42(dir, entry);
11835
+ const p = join43(dir, entry);
11711
11836
  let st;
11712
11837
  try {
11713
11838
  st = statSync9(p);
@@ -12123,17 +12248,17 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
12123
12248
  }
12124
12249
 
12125
12250
  // src/lib/claim-invocation-parity.ts
12126
- import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync17 } from "fs";
12127
- import { join as join43 } from "path";
12251
+ import { existsSync as existsSync42, readFileSync as readFileSync32, readdirSync as readdirSync17 } from "fs";
12252
+ import { join as join44 } from "path";
12128
12253
  function distributedAgentsDocs(root) {
12129
12254
  const docs = [];
12130
- const own = join43(root, "AGENTS.md");
12131
- if (existsSync41(own)) docs.push({ path: "AGENTS.md", text: readFileSync32(own, "utf8") });
12132
- const skeletons = join43(root, "_skeletons");
12133
- if (existsSync41(skeletons)) {
12255
+ const own = join44(root, "AGENTS.md");
12256
+ if (existsSync42(own)) docs.push({ path: "AGENTS.md", text: readFileSync32(own, "utf8") });
12257
+ const skeletons = join44(root, "_skeletons");
12258
+ if (existsSync42(skeletons)) {
12134
12259
  for (const name of readdirSync17(skeletons).sort()) {
12135
- const abs = join43(skeletons, name, "AGENTS.md");
12136
- if (!existsSync41(abs)) continue;
12260
+ const abs = join44(skeletons, name, "AGENTS.md");
12261
+ if (!existsSync42(abs)) continue;
12137
12262
  docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync32(abs, "utf8") });
12138
12263
  }
12139
12264
  }
@@ -12266,12 +12391,12 @@ async function runClaimInvocationCheck() {
12266
12391
  }
12267
12392
 
12268
12393
  // src/scripts/check-codeql-suppression.ts
12269
- import { existsSync as existsSync42 } from "fs";
12270
- import { join as join45, relative as relative8 } from "path";
12394
+ import { existsSync as existsSync43 } from "fs";
12395
+ import { join as join46, relative as relative8 } from "path";
12271
12396
 
12272
12397
  // src/lib/codeql-suppression-guard.ts
12273
12398
  import { readdirSync as readdirSync18, readFileSync as readFileSync33, statSync as statSync10 } from "fs";
12274
- import { join as join44 } from "path";
12399
+ import { join as join45 } from "path";
12275
12400
  var SKIP_DIRS2 = /* @__PURE__ */ new Set([
12276
12401
  ".git",
12277
12402
  ".worktrees",
@@ -12302,7 +12427,7 @@ function walkSourceFiles(root) {
12302
12427
  return;
12303
12428
  }
12304
12429
  for (const entry of entries) {
12305
- const p = join44(dir, entry);
12430
+ const p = join45(dir, entry);
12306
12431
  let st;
12307
12432
  try {
12308
12433
  st = statSync10(p);
@@ -12339,8 +12464,8 @@ function sweepCodeqlSuppressionComments(root) {
12339
12464
  // src/scripts/check-codeql-suppression.ts
12340
12465
  async function runCodeqlSuppressionCheck() {
12341
12466
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12342
- const scanRoot = join45(root, "cli", "src");
12343
- if (!existsSync42(scanRoot)) {
12467
+ const scanRoot = join46(root, "cli", "src");
12468
+ if (!existsSync43(scanRoot)) {
12344
12469
  console.log(
12345
12470
  "\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
12346
12471
  );
@@ -12364,12 +12489,12 @@ async function runCodeqlSuppressionCheck() {
12364
12489
  }
12365
12490
 
12366
12491
  // src/scripts/check-cognito-invite-template.ts
12367
- import { existsSync as existsSync43 } from "fs";
12368
- import { join as join47 } from "path";
12492
+ import { existsSync as existsSync44 } from "fs";
12493
+ import { join as join48 } from "path";
12369
12494
 
12370
12495
  // src/lib/cognito-invite-template-guard.ts
12371
12496
  import { readdirSync as readdirSync19, readFileSync as readFileSync34, statSync as statSync11 } from "fs";
12372
- import { join as join46 } from "path";
12497
+ import { join as join47 } from "path";
12373
12498
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
12374
12499
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
12375
12500
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -12453,7 +12578,7 @@ function findModuleTerraformFiles(repoRoot) {
12453
12578
  for (const entry of entries) {
12454
12579
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees" || entry === ".venv")
12455
12580
  continue;
12456
- const full = join46(dir, entry);
12581
+ const full = join47(dir, entry);
12457
12582
  const rel = `${relative11}/${entry}`;
12458
12583
  let isDir;
12459
12584
  try {
@@ -12468,12 +12593,12 @@ function findModuleTerraformFiles(repoRoot) {
12468
12593
  }
12469
12594
  }
12470
12595
  };
12471
- walk2(join46(repoRoot, "modules"), "modules");
12596
+ walk2(join47(repoRoot, "modules"), "modules");
12472
12597
  return found.sort();
12473
12598
  }
12474
12599
  function checkCognitoInviteTemplates(repoRoot) {
12475
12600
  return findModuleTerraformFiles(repoRoot).flatMap(
12476
- (file) => checkInviteTemplateSource(file, readFileSync34(join46(repoRoot, file), "utf8"))
12601
+ (file) => checkInviteTemplateSource(file, readFileSync34(join47(repoRoot, file), "utf8"))
12477
12602
  );
12478
12603
  }
12479
12604
 
@@ -12483,7 +12608,7 @@ async function runCognitoInviteTemplateCheck() {
12483
12608
  const files = findModuleTerraformFiles(root);
12484
12609
  console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
12485
12610
  if (files.length === 0) {
12486
- if (!existsSync43(join47(root, "modules"))) {
12611
+ if (!existsSync44(join48(root, "modules"))) {
12487
12612
  console.log(
12488
12613
  `\xB7 Cognito invite template guard: not applicable \u2014 no modules/ directory under ${root} \u2014 this is not a template/instance tree (a satellite repo never carries the template-owned Terraform modules). Skipping.`
12489
12614
  );
@@ -12507,11 +12632,11 @@ async function runCognitoInviteTemplateCheck() {
12507
12632
  }
12508
12633
 
12509
12634
  // src/scripts/check-core-direct-paths.ts
12510
- import { join as join49 } from "path";
12635
+ import { join as join50 } from "path";
12511
12636
 
12512
12637
  // src/lib/core-direct-paths-audit.ts
12513
- import { existsSync as existsSync44, readFileSync as readFileSync35, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12514
- import { join as join48 } from "path";
12638
+ import { existsSync as existsSync45, readFileSync as readFileSync35, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12639
+ import { join as join49 } from "path";
12515
12640
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
12516
12641
  var API_ROUTE_PREFIX = "/api/v1";
12517
12642
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -12675,7 +12800,7 @@ function walkFiles(root, accept, skipDir) {
12675
12800
  return;
12676
12801
  }
12677
12802
  for (const entry of entries) {
12678
- const p = join48(dir, entry);
12803
+ const p = join49(dir, entry);
12679
12804
  let st;
12680
12805
  try {
12681
12806
  st = statSync12(p);
@@ -12770,7 +12895,7 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
12770
12895
  }
12771
12896
  function resolveSiblingCoreSrc(params) {
12772
12897
  const { estateDir, sibling } = params;
12773
- const configPath = join48(estateDir, sibling, "biffo.sibling.json");
12898
+ const configPath = join49(estateDir, sibling, "biffo.sibling.json");
12774
12899
  let raw;
12775
12900
  try {
12776
12901
  raw = readFileSync35(configPath, "utf8");
@@ -12793,8 +12918,8 @@ function resolveSiblingCoreSrc(params) {
12793
12918
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
12794
12919
  );
12795
12920
  }
12796
- const coreApiSrcDir = join48(estateDir, coreProject, "services", "api", "src");
12797
- if (!existsSync44(coreApiSrcDir)) {
12921
+ const coreApiSrcDir = join49(estateDir, coreProject, "services", "api", "src");
12922
+ if (!existsSync45(coreApiSrcDir)) {
12798
12923
  throw new Error(
12799
12924
  `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.`
12800
12925
  );
@@ -12847,7 +12972,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
12847
12972
  }
12848
12973
  }
12849
12974
  const sibling = opts.sibling ?? "sibling-template (self-check)";
12850
- const frontendSrcDir = opts.frontendSrc ?? join49(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
12975
+ const frontendSrcDir = opts.frontendSrc ?? join50(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
12851
12976
  let coreApiSrcDir;
12852
12977
  let coreProject = null;
12853
12978
  if (opts.coreSrc) {
@@ -12863,7 +12988,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
12863
12988
  coreApiSrcDir = resolution.coreApiSrcDir;
12864
12989
  coreProject = resolution.coreProject;
12865
12990
  } else {
12866
- coreApiSrcDir = join49(root, "services", "api", "src");
12991
+ coreApiSrcDir = join50(root, "services", "api", "src");
12867
12992
  }
12868
12993
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
12869
12994
  console.log(
@@ -13114,16 +13239,16 @@ ${BOLD}If the divergence is deliberate${OFF}
13114
13239
  }
13115
13240
 
13116
13241
  // src/scripts/check-distribution-inventory.ts
13117
- import { existsSync as existsSync46 } from "fs";
13118
- import { join as join51 } from "path";
13242
+ import { existsSync as existsSync47 } from "fs";
13243
+ import { join as join52 } from "path";
13119
13244
 
13120
13245
  // src/lib/distribution-inventory.ts
13121
- import { existsSync as existsSync45, readFileSync as readFileSync36 } from "fs";
13122
- import { join as join50 } from "path";
13246
+ import { existsSync as existsSync46, readFileSync as readFileSync36 } from "fs";
13247
+ import { join as join51 } from "path";
13123
13248
  var INVENTORY_FILENAME = "distribution-inventory.json";
13124
13249
  function loadDistributionInventory(root) {
13125
- const path = join50(root, INVENTORY_FILENAME);
13126
- if (!existsSync45(path)) {
13250
+ const path = join51(root, INVENTORY_FILENAME);
13251
+ if (!existsSync46(path)) {
13127
13252
  throw new Error(
13128
13253
  `${INVENTORY_FILENAME} not found at ${root} -- expected it beside core-manifest.json`
13129
13254
  );
@@ -13288,7 +13413,7 @@ async function runDistributionInventoryCheck(root) {
13288
13413
  );
13289
13414
  return;
13290
13415
  }
13291
- if (!existsSync46(join51(repoRoot, INVENTORY_FILENAME))) {
13416
+ if (!existsSync47(join52(repoRoot, INVENTORY_FILENAME))) {
13292
13417
  console.log(
13293
13418
  `\u2713 distribution-inventory: skipped \u2014 no ${INVENTORY_FILENAME} at ${repoRoot}. This is a template-only registry; a checkout that legitimately has none has nothing for this guard to check.`
13294
13419
  );
@@ -13314,7 +13439,7 @@ async function runDistributionInventoryCheck(root) {
13314
13439
  }
13315
13440
 
13316
13441
  // src/scripts/check-distribution-remote-state.ts
13317
- import { join as join52 } from "path";
13442
+ import { join as join53 } from "path";
13318
13443
  async function ghExecCommand(file, args) {
13319
13444
  const result = await execa(file, args, { reject: false });
13320
13445
  return { stdout: String(result.stdout ?? ""), exitCode: result.exitCode ?? null };
@@ -13329,7 +13454,7 @@ async function runDistributionRemoteStateCheck(root) {
13329
13454
  }
13330
13455
  }
13331
13456
  console.log(
13332
- `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join52(repoRoot, "distribution-inventory.json")})`
13457
+ `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join53(repoRoot, "distribution-inventory.json")})`
13333
13458
  );
13334
13459
  if (assertions.length === 0) {
13335
13460
  console.log("\u2713 distribution-remote-state: nothing declared to check");
@@ -13375,7 +13500,7 @@ ${assertion.ref}`;
13375
13500
 
13376
13501
  // src/lib/eventbridge-log-permission-guard.ts
13377
13502
  import { readFileSync as readFileSync37, readdirSync as readdirSync21, statSync as statSync13 } from "fs";
13378
- import { join as join53 } from "path";
13503
+ import { join as join54 } from "path";
13379
13504
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
13380
13505
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
13381
13506
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -13452,7 +13577,7 @@ function walkTerraformFiles2(root) {
13452
13577
  return;
13453
13578
  }
13454
13579
  for (const entry of entries) {
13455
- const p = join53(dir, entry);
13580
+ const p = join54(dir, entry);
13456
13581
  let st;
13457
13582
  try {
13458
13583
  st = statSync13(p);
@@ -13592,8 +13717,8 @@ async function runEventBridgeLogPermissionCheck() {
13592
13717
  }
13593
13718
 
13594
13719
  // src/scripts/check-instance-adoption.ts
13595
- import { existsSync as existsSync47 } from "fs";
13596
- import { join as join54 } from "path";
13720
+ import { existsSync as existsSync48 } from "fs";
13721
+ import { join as join55 } from "path";
13597
13722
  async function runInstanceAdoptionCheck(opts = {}) {
13598
13723
  if (!opts.instanceDir) {
13599
13724
  console.error(
@@ -13601,7 +13726,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
13601
13726
  );
13602
13727
  process.exit(2);
13603
13728
  }
13604
- if (!existsSync47(opts.instanceDir)) {
13729
+ if (!existsSync48(opts.instanceDir)) {
13605
13730
  console.error(
13606
13731
  `\u2717 instance-adoption guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it is adopted, and that is not the same as a clean pass.`
13607
13732
  );
@@ -13609,7 +13734,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
13609
13734
  }
13610
13735
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13611
13736
  const theirsDir = opts.theirsDir ?? root;
13612
- const instanceLabel = opts.instance ?? join54(opts.instanceDir).split("/").filter(Boolean).pop();
13737
+ const instanceLabel = opts.instance ?? join55(opts.instanceDir).split("/").filter(Boolean).pop();
13613
13738
  const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
13614
13739
  console.log(
13615
13740
  `examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
@@ -13632,11 +13757,11 @@ async function runInstanceAdoptionCheck(opts = {}) {
13632
13757
 
13633
13758
  // src/lib/lambda-output-guard.ts
13634
13759
  import { readFileSync as readFileSync39 } from "fs";
13635
- import { join as join56 } from "path";
13760
+ import { join as join57 } from "path";
13636
13761
 
13637
13762
  // src/lib/terraform-input-guard.ts
13638
- import { existsSync as existsSync48, readdirSync as readdirSync22, readFileSync as readFileSync38, statSync as statSync14 } from "fs";
13639
- import { join as join55 } from "path";
13763
+ import { existsSync as existsSync49, readdirSync as readdirSync22, readFileSync as readFileSync38, statSync as statSync14 } from "fs";
13764
+ import { join as join56 } from "path";
13640
13765
  var GUARDED_SUBCOMMANDS = [
13641
13766
  "init",
13642
13767
  "plan",
@@ -13650,7 +13775,7 @@ function stripComments2(source) {
13650
13775
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
13651
13776
  }
13652
13777
  function vendoredPluginServiceDirs(repoRoot) {
13653
- const servicesDir = join55(repoRoot, "services");
13778
+ const servicesDir = join56(repoRoot, "services");
13654
13779
  const result = /* @__PURE__ */ new Set();
13655
13780
  let entries;
13656
13781
  try {
@@ -13659,9 +13784,9 @@ function vendoredPluginServiceDirs(repoRoot) {
13659
13784
  return result;
13660
13785
  }
13661
13786
  for (const entry of entries) {
13662
- const full = join55(servicesDir, entry);
13663
- if (!existsSync48(full) || !statSync14(full).isDirectory()) continue;
13664
- if (existsSync48(join55(full, "biffo.plugin.json"))) {
13787
+ const full = join56(servicesDir, entry);
13788
+ if (!existsSync49(full) || !statSync14(full).isDirectory()) continue;
13789
+ if (existsSync49(join56(full, "biffo.plugin.json"))) {
13665
13790
  result.add(entry);
13666
13791
  }
13667
13792
  }
@@ -13683,7 +13808,7 @@ function findWorkflowFiles(repoRoot) {
13683
13808
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
13684
13809
  continue;
13685
13810
  }
13686
- const full = join55(dir, entry);
13811
+ const full = join56(dir, entry);
13687
13812
  const rel = relative11 ? `${relative11}/${entry}` : entry;
13688
13813
  let isDir;
13689
13814
  try {
@@ -13733,7 +13858,7 @@ function checkWorkflowSource(file, rawSource) {
13733
13858
  }
13734
13859
  function checkTerraformInput(repoRoot) {
13735
13860
  return findWorkflowFiles(repoRoot).flatMap(
13736
- (file) => checkWorkflowSource(file, readFileSync38(join55(repoRoot, file), "utf8"))
13861
+ (file) => checkWorkflowSource(file, readFileSync38(join56(repoRoot, file), "utf8"))
13737
13862
  );
13738
13863
  }
13739
13864
 
@@ -13791,7 +13916,7 @@ function checkWorkflowSource2(file, rawSource) {
13791
13916
  }
13792
13917
  function checkLambdaOutput(repoRoot) {
13793
13918
  return findWorkflowFiles(repoRoot).flatMap(
13794
- (file) => checkWorkflowSource2(file, readFileSync39(join56(repoRoot, file), "utf8"))
13919
+ (file) => checkWorkflowSource2(file, readFileSync39(join57(repoRoot, file), "utf8"))
13795
13920
  );
13796
13921
  }
13797
13922
 
@@ -13819,8 +13944,8 @@ async function runLambdaOutputCheck() {
13819
13944
  }
13820
13945
 
13821
13946
  // src/scripts/check-migration-body-change.ts
13822
- import { existsSync as existsSync49 } from "fs";
13823
- import { join as join57 } from "path";
13947
+ import { existsSync as existsSync50 } from "fs";
13948
+ import { join as join58 } from "path";
13824
13949
 
13825
13950
  // src/lib/migration-body-change-guard.ts
13826
13951
  function checkMigrationBodyChangeMarkers(diffs) {
@@ -13902,7 +14027,7 @@ async function runMigrationBodyChangeCheck(argv) {
13902
14027
  );
13903
14028
  return;
13904
14029
  }
13905
- if (!existsSync49(join57(root, MIGRATIONS_VERSIONS_DIR))) {
14030
+ if (!existsSync50(join58(root, MIGRATIONS_VERSIONS_DIR))) {
13906
14031
  console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
13907
14032
  return;
13908
14033
  }
@@ -14008,8 +14133,8 @@ ${BOLD2}What to do${OFF2}
14008
14133
  }
14009
14134
 
14010
14135
  // src/scripts/check-orphan-ratchet.ts
14011
- import { existsSync as existsSync50 } from "fs";
14012
- import { basename as basename3, join as join58 } from "path";
14136
+ import { existsSync as existsSync51 } from "fs";
14137
+ import { basename as basename3, join as join59 } from "path";
14013
14138
  function reportOrphan(entry, manifest) {
14014
14139
  console.error(` ${entry.path}`);
14015
14140
  const { templateOwnedMatch, nearestUserOwnedEntries } = explainOwnership(entry.path, manifest);
@@ -14031,7 +14156,7 @@ function reportOrphan(entry, manifest) {
14031
14156
  }
14032
14157
  }
14033
14158
  async function runOrphanRatchetCheck(opts = {}) {
14034
- if (opts.instanceDir !== void 0 && !existsSync50(opts.instanceDir)) {
14159
+ if (opts.instanceDir !== void 0 && !existsSync51(opts.instanceDir)) {
14035
14160
  console.error(
14036
14161
  `\u2717 orphan-ratchet guard: --instance-dir ${opts.instanceDir} does not exist \u2014 cannot tell whether it carries unsanctioned files, and that is not the same as a clean pass.`
14037
14162
  );
@@ -14048,7 +14173,7 @@ async function runOrphanRatchetCheck(opts = {}) {
14048
14173
  "self-check mode: --instance-dir was not given, so it defaulted to --theirs-dir (this repo's own root) alongside --base-dir \u2014 all three trees are the same, so this run can only ever find zero orphans by construction. See this script's doc comment. Pass a real instance tree with --instance-dir for a check that can actually find something. biffo-template#1714."
14049
14174
  );
14050
14175
  }
14051
- if (!existsSync50(join58(theirsDir, CORE_MANIFEST_FILE))) {
14176
+ if (!existsSync51(join59(theirsDir, CORE_MANIFEST_FILE))) {
14052
14177
  console.log(
14053
14178
  `orphan-ratchet guard (${label}): no ${CORE_MANIFEST_FILE} in ${theirsDir} \u2014 this is not a Biffo template/instance tree (a satellite repo never carries one), so there is no ownership manifest to classify paths against. Not applicable; skipping.`
14054
14179
  );
@@ -14080,7 +14205,7 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
14080
14205
 
14081
14206
  // src/lib/ownership-header-claim-guard.ts
14082
14207
  import { readFileSync as readFileSync40 } from "fs";
14083
- import { join as join59 } from "path";
14208
+ import { join as join60 } from "path";
14084
14209
  var OWNERSHIP_HEADER_SWEEP_DIRS = [
14085
14210
  "scripts",
14086
14211
  ".githooks",
@@ -14289,7 +14414,7 @@ function sweepOwnershipHeaderClaims(root, options = {}) {
14289
14414
  for (const rel of files) {
14290
14415
  let content;
14291
14416
  try {
14292
- content = readFileSync40(join59(root, rel), "utf8");
14417
+ content = readFileSync40(join60(root, rel), "utf8");
14293
14418
  } catch {
14294
14419
  continue;
14295
14420
  }
@@ -14365,7 +14490,7 @@ async function runOwnershipHeaderClaimCheck() {
14365
14490
 
14366
14491
  // src/scripts/check-pipe-trap.ts
14367
14492
  import { readFileSync as readFileSync41, readdirSync as readdirSync23 } from "fs";
14368
- import { join as join60, relative as relative9 } from "path";
14493
+ import { join as join61, relative as relative9 } from "path";
14369
14494
 
14370
14495
  // src/lib/pipe-trap-guard.ts
14371
14496
  var STATUS_BEARING = [
@@ -14461,7 +14586,7 @@ function findPipeTraps(source) {
14461
14586
  function shellFiles(root) {
14462
14587
  const out = [];
14463
14588
  for (const dir of ["scripts", ".githooks"]) {
14464
- const full = join60(root, dir);
14589
+ const full = join61(root, dir);
14465
14590
  let entries;
14466
14591
  try {
14467
14592
  entries = readdirSync23(full, { withFileTypes: true });
@@ -14471,7 +14596,7 @@ function shellFiles(root) {
14471
14596
  for (const entry of entries) {
14472
14597
  if (!entry.isFile()) continue;
14473
14598
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
14474
- out.push(join60(full, entry.name));
14599
+ out.push(join61(full, entry.name));
14475
14600
  }
14476
14601
  }
14477
14602
  return out;
@@ -14505,7 +14630,7 @@ async function runPipeTrapCheck() {
14505
14630
 
14506
14631
  // src/lib/plugin-allowlist-convention.ts
14507
14632
  import { readFileSync as readFileSync42 } from "fs";
14508
- import { join as join61 } from "path";
14633
+ import { join as join62 } from "path";
14509
14634
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
14510
14635
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
14511
14636
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -14516,7 +14641,7 @@ var PLUGIN = "<plugin>";
14516
14641
  var ACCOUNT = "<account>";
14517
14642
  function read(repoRoot, relative11) {
14518
14643
  try {
14519
- return readFileSync42(join61(repoRoot, relative11), "utf8");
14644
+ return readFileSync42(join62(repoRoot, relative11), "utf8");
14520
14645
  } catch {
14521
14646
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
14522
14647
  }
@@ -14525,7 +14650,7 @@ function assignedString(source, name) {
14525
14650
  const match = new RegExp(`^\\s*${name}\\s*=\\s*"((?:[^"\\\\]|\\\\.)*)"\\s*$`, "m").exec(source);
14526
14651
  return match?.[1];
14527
14652
  }
14528
- function resolve19(expression, bindings) {
14653
+ function resolve20(expression, bindings) {
14529
14654
  let current = expression;
14530
14655
  for (let pass = 0; pass < 10; pass += 1) {
14531
14656
  const next = current.replace(/\$\{([^}]+)\}/g, (whole, ref) => {
@@ -14562,13 +14687,13 @@ function composeExpectedRoleName(repoRoot) {
14562
14687
  "var.project_name": PROJECT,
14563
14688
  "var.environment": ENV,
14564
14689
  "var.plugin_name": PLUGIN,
14565
- "var.function_name": resolve19(pluginFunctionName, {
14690
+ "var.function_name": resolve20(pluginFunctionName, {
14566
14691
  "var.plugin_name": PLUGIN
14567
14692
  })
14568
14693
  };
14569
- bindings["local.name_prefix"] = resolve19(namePrefix, bindings);
14570
- bindings["local.function_name"] = resolve19(functionName, bindings);
14571
- return resolve19(roleName, bindings);
14694
+ bindings["local.name_prefix"] = resolve20(namePrefix, bindings);
14695
+ bindings["local.function_name"] = resolve20(functionName, bindings);
14696
+ return resolve20(roleName, bindings);
14572
14697
  }
14573
14698
  function readAllowlistGlob(repoRoot) {
14574
14699
  const allowlist = read(repoRoot, ALLOWLIST_MAIN_TF);
@@ -14578,7 +14703,7 @@ function readAllowlistGlob(repoRoot) {
14578
14703
  `plugin-allowlist drift guard: could not find the "for name in var.enabled_plugins" glob in ${ALLOWLIST_MAIN_TF}.`
14579
14704
  );
14580
14705
  }
14581
- return resolve19(glob, {
14706
+ return resolve20(glob, {
14582
14707
  "data.aws_caller_identity.current.account_id": ACCOUNT,
14583
14708
  "var.project_name": PROJECT,
14584
14709
  "var.environment": ENV,
@@ -14644,32 +14769,32 @@ async function runPluginAllowlistConventionCheck() {
14644
14769
  }
14645
14770
 
14646
14771
  // src/scripts/check-plugin-collisions.ts
14647
- import { existsSync as existsSync52 } from "fs";
14648
- import { join as join63 } from "path";
14772
+ import { existsSync as existsSync53 } from "fs";
14773
+ import { join as join64 } from "path";
14649
14774
 
14650
14775
  // src/lib/plugin-collision-guard.ts
14651
- import { existsSync as existsSync51, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
14652
- import { join as join62 } from "path";
14776
+ import { existsSync as existsSync52, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
14777
+ import { join as join63 } from "path";
14653
14778
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
14654
14779
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
14655
14780
  function subdirectories(dir) {
14656
- if (!existsSync51(dir)) return [];
14781
+ if (!existsSync52(dir)) return [];
14657
14782
  return readdirSync24(dir).filter((entry) => {
14658
14783
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
14659
14784
  try {
14660
- return statSync15(join62(dir, entry)).isDirectory();
14785
+ return statSync15(join63(dir, entry)).isDirectory();
14661
14786
  } catch {
14662
14787
  return false;
14663
14788
  }
14664
14789
  });
14665
14790
  }
14666
14791
  function regularPackagesOf(pluginDir2) {
14667
- return subdirectories(pluginDir2).filter((name) => existsSync51(join62(pluginDir2, name, "__init__.py"))).sort();
14792
+ return subdirectories(pluginDir2).filter((name) => existsSync52(join63(pluginDir2, name, "__init__.py"))).sort();
14668
14793
  }
14669
14794
  function bareTestModulesOf(pluginDir2) {
14670
- const testsDir = join62(pluginDir2, "tests");
14671
- if (!existsSync51(testsDir)) return [];
14672
- if (existsSync51(join62(testsDir, "__init__.py"))) return [];
14795
+ const testsDir = join63(pluginDir2, "tests");
14796
+ if (!existsSync52(testsDir)) return [];
14797
+ if (existsSync52(join63(testsDir, "__init__.py"))) return [];
14673
14798
  return readdirSync24(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
14674
14799
  }
14675
14800
  function findCollisions(servicesDir, pluginDirs) {
@@ -14678,7 +14803,7 @@ function findCollisions(servicesDir, pluginDirs) {
14678
14803
  const gather = (kind, namesOf) => {
14679
14804
  const claims = /* @__PURE__ */ new Map();
14680
14805
  for (const plugin of plugins) {
14681
- for (const name of namesOf(join62(servicesDir, plugin))) {
14806
+ for (const name of namesOf(join63(servicesDir, plugin))) {
14682
14807
  claims.set(name, [...claims.get(name) ?? [], plugin]);
14683
14808
  }
14684
14809
  }
@@ -14716,8 +14841,8 @@ function formatCollisions(collisions) {
14716
14841
  // src/scripts/check-plugin-collisions.ts
14717
14842
  async function runPluginCollisionCheck() {
14718
14843
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14719
- const servicesDir = join63(root, "services");
14720
- if (!existsSync52(servicesDir)) {
14844
+ const servicesDir = join64(root, "services");
14845
+ if (!existsSync53(servicesDir)) {
14721
14846
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
14722
14847
  return;
14723
14848
  }
@@ -14734,8 +14859,8 @@ async function runPluginCollisionCheck() {
14734
14859
  }
14735
14860
 
14736
14861
  // src/lib/plugin-terraform-guard.ts
14737
- import { existsSync as existsSync53, readFileSync as readFileSync43, readdirSync as readdirSync25 } from "fs";
14738
- import { dirname as dirname10, join as join64, relative as relative10, sep as sep4 } from "path";
14862
+ import { existsSync as existsSync54, readFileSync as readFileSync43, readdirSync as readdirSync25 } from "fs";
14863
+ import { dirname as dirname12, join as join65, relative as relative10, sep as sep4 } from "path";
14739
14864
  var SKIP_DIRS4 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
14740
14865
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
14741
14866
  function findPluginManifests(root) {
@@ -14750,9 +14875,9 @@ function findPluginManifests(root) {
14750
14875
  for (const entry of entries) {
14751
14876
  if (entry.isDirectory()) {
14752
14877
  if (SKIP_DIRS4.has(entry.name)) continue;
14753
- walk2(join64(dir, entry.name));
14878
+ walk2(join65(dir, entry.name));
14754
14879
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
14755
- found.push(relative10(root, join64(dir, entry.name)).split(sep4).join("/"));
14880
+ found.push(relative10(root, join65(dir, entry.name)).split(sep4).join("/"));
14756
14881
  }
14757
14882
  }
14758
14883
  };
@@ -14777,14 +14902,14 @@ function readSubscriptions(absManifestPath) {
14777
14902
  }
14778
14903
  function checkPluginTerraform(root) {
14779
14904
  const violations = [];
14780
- const coreManifest = existsSync53(join64(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
14905
+ const coreManifest = existsSync54(join65(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
14781
14906
  for (const manifest of findPluginManifests(root)) {
14782
14907
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
14783
- const absManifest = join64(root, manifest);
14908
+ const absManifest = join65(root, manifest);
14784
14909
  const subscriptions = readSubscriptions(absManifest);
14785
14910
  if (subscriptions === null) continue;
14786
- const pluginDir2 = dirname10(absManifest);
14787
- if (existsSync53(join64(pluginDir2, "terraform"))) continue;
14911
+ const pluginDir2 = dirname12(absManifest);
14912
+ if (existsSync54(join65(pluginDir2, "terraform"))) continue;
14788
14913
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
14789
14914
  violations.push({
14790
14915
  manifest,
@@ -14815,12 +14940,12 @@ async function runPluginTerraformCheck() {
14815
14940
  }
14816
14941
 
14817
14942
  // src/scripts/check-plugin-tool-supply.ts
14818
- import { existsSync as existsSync55 } from "fs";
14819
- import { join as join66 } from "path";
14943
+ import { existsSync as existsSync56 } from "fs";
14944
+ import { join as join67 } from "path";
14820
14945
 
14821
14946
  // src/lib/plugin-tool-supply-audit.ts
14822
- import { existsSync as existsSync54, readFileSync as readFileSync44, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14823
- import { join as join65 } from "path";
14947
+ import { existsSync as existsSync55, readFileSync as readFileSync44, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14948
+ import { join as join66 } from "path";
14824
14949
 
14825
14950
  // src/lib/openrouter-model-snapshot.ts
14826
14951
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -15237,7 +15362,7 @@ function listDirs(root) {
15237
15362
  }
15238
15363
  return entries.filter((e) => {
15239
15364
  try {
15240
- return statSync16(join65(root, e)).isDirectory();
15365
+ return statSync16(join66(root, e)).isDirectory();
15241
15366
  } catch {
15242
15367
  return false;
15243
15368
  }
@@ -15253,7 +15378,7 @@ function walkFiles2(root, accept, skipDir) {
15253
15378
  return;
15254
15379
  }
15255
15380
  for (const entry of entries) {
15256
- const p = join65(dir, entry);
15381
+ const p = join66(dir, entry);
15257
15382
  let st;
15258
15383
  try {
15259
15384
  st = statSync16(p);
@@ -15279,14 +15404,14 @@ function pluginPythonFiles(pluginDir2) {
15279
15404
  );
15280
15405
  }
15281
15406
  function pluginTerraformFiles(pluginDir2) {
15282
- const tfDir = join65(pluginDir2, "terraform");
15407
+ const tfDir = join66(pluginDir2, "terraform");
15283
15408
  let entries;
15284
15409
  try {
15285
15410
  entries = readdirSync26(tfDir);
15286
15411
  } catch {
15287
15412
  return [];
15288
15413
  }
15289
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join65(tfDir, e)).sort();
15414
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join66(tfDir, e)).sort();
15290
15415
  }
15291
15416
  function extractManifestTools(manifestText) {
15292
15417
  let parsed;
@@ -15538,8 +15663,8 @@ function isSnapshotStale(fetchedAt, now) {
15538
15663
  function normalizeModelId(id) {
15539
15664
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
15540
15665
  }
15541
- var CONFIG_PY_PATH = join65("services", "api", "src", "api", "config.py");
15542
- var ORCHESTRATION_SCHEMA_PATH = join65(
15666
+ var CONFIG_PY_PATH = join66("services", "api", "src", "api", "config.py");
15667
+ var ORCHESTRATION_SCHEMA_PATH = join66(
15543
15668
  "services",
15544
15669
  "api",
15545
15670
  "src",
@@ -15551,10 +15676,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
15551
15676
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
15552
15677
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
15553
15678
  const now = options.now ?? /* @__PURE__ */ new Date();
15554
- const configPath = join65(repoRoot, CONFIG_PY_PATH);
15555
- const orchestrationPath = join65(repoRoot, ORCHESTRATION_SCHEMA_PATH);
15556
- const configMissing = !existsSync54(configPath);
15557
- const orchestrationSchemaMissing = !existsSync54(orchestrationPath);
15679
+ const configPath = join66(repoRoot, CONFIG_PY_PATH);
15680
+ const orchestrationPath = join66(repoRoot, ORCHESTRATION_SCHEMA_PATH);
15681
+ const configMissing = !existsSync55(configPath);
15682
+ const orchestrationSchemaMissing = !existsSync55(orchestrationPath);
15558
15683
  const knownSet = new Set(knownModelIds);
15559
15684
  const snapshotEmpty = knownModelIds.length === 0;
15560
15685
  const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
@@ -15625,7 +15750,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
15625
15750
  function discoverPluginDirs(pluginsRoot) {
15626
15751
  return listDirs(pluginsRoot).filter((name) => {
15627
15752
  try {
15628
- return statSync16(join65(pluginsRoot, name, "biffo.plugin.json")).isFile();
15753
+ return statSync16(join66(pluginsRoot, name, "biffo.plugin.json")).isFile();
15629
15754
  } catch {
15630
15755
  return false;
15631
15756
  }
@@ -15638,8 +15763,8 @@ function auditPluginToolSupply(pluginsRoot) {
15638
15763
  let terraformBlind = false;
15639
15764
  let totalDeclaredTools = 0;
15640
15765
  for (const name of pluginNames) {
15641
- const pluginDir2 = join65(pluginsRoot, name);
15642
- const manifestText = readFileSync44(join65(pluginDir2, "biffo.plugin.json"), "utf8");
15766
+ const pluginDir2 = join66(pluginsRoot, name);
15767
+ const manifestText = readFileSync44(join66(pluginDir2, "biffo.plugin.json"), "utf8");
15643
15768
  const manifest = extractManifestTools(manifestText);
15644
15769
  if (manifest.parseError) {
15645
15770
  findings.push({
@@ -15737,7 +15862,7 @@ function auditPluginToolSupply(pluginsRoot) {
15737
15862
  requiredEnvVars: envResult.envVars,
15738
15863
  missingEnvVars: anyWired ? [] : envResult.envVars,
15739
15864
  status: anyWired ? "ok" : "missing-env",
15740
- 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 ${join65(pluginDir2, "terraform")}, so this deployment can never supply it`
15865
+ 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 ${join66(pluginDir2, "terraform")}, so this deployment can never supply it`
15741
15866
  });
15742
15867
  }
15743
15868
  }
@@ -15777,8 +15902,8 @@ async function runPluginToolSupplyCheck() {
15777
15902
  return;
15778
15903
  }
15779
15904
  let allOk = true;
15780
- const pluginsRoot = join66(root, "services", "_plugins");
15781
- if (!existsSync55(pluginsRoot)) {
15905
+ const pluginsRoot = join67(root, "services", "_plugins");
15906
+ if (!existsSync56(pluginsRoot)) {
15782
15907
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
15783
15908
  } else {
15784
15909
  const report = auditPluginToolSupply(pluginsRoot);
@@ -15808,8 +15933,8 @@ async function runPluginToolSupplyCheck() {
15808
15933
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
15809
15934
  }
15810
15935
  }
15811
- const servicesApiRoot = join66(root, "services", "api");
15812
- if (!existsSync55(servicesApiRoot)) {
15936
+ const servicesApiRoot = join67(root, "services", "api");
15937
+ if (!existsSync56(servicesApiRoot)) {
15813
15938
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
15814
15939
  } else {
15815
15940
  const modelReport = auditDeclaredModelIds(root);
@@ -16186,12 +16311,12 @@ async function runSharedFileReductionCheck(args) {
16186
16311
  }
16187
16312
 
16188
16313
  // src/scripts/check-skeleton-drift.ts
16189
- import { existsSync as existsSync56, readdirSync as readdirSync28 } from "fs";
16190
- import { join as join68 } from "path";
16314
+ import { existsSync as existsSync57, readdirSync as readdirSync28 } from "fs";
16315
+ import { join as join69 } from "path";
16191
16316
 
16192
16317
  // src/lib/skeleton-drift-guard.ts
16193
16318
  import { readFileSync as readFileSync47, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
16194
- import { join as join67 } from "path";
16319
+ import { join as join68 } from "path";
16195
16320
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
16196
16321
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
16197
16322
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -16255,7 +16380,7 @@ function walk(dir, base = dir) {
16255
16380
  }
16256
16381
  for (const entry of entries) {
16257
16382
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
16258
- const abs = join67(dir, entry);
16383
+ const abs = join68(dir, entry);
16259
16384
  let isDir;
16260
16385
  try {
16261
16386
  isDir = statSync17(abs).isDirectory();
@@ -16277,7 +16402,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
16277
16402
  if (!rule.appliesTo(rel)) continue;
16278
16403
  let contents;
16279
16404
  try {
16280
- contents = readFileSync47(join67(skeletonRoot, rel), "utf8");
16405
+ contents = readFileSync47(join68(skeletonRoot, rel), "utf8");
16281
16406
  } catch {
16282
16407
  continue;
16283
16408
  }
@@ -16306,23 +16431,23 @@ function formatViolations2(violations) {
16306
16431
 
16307
16432
  // src/scripts/check-skeleton-drift.ts
16308
16433
  function discoverSkeletons(root) {
16309
- const skeletonsDir = join68(root, "_skeletons");
16434
+ const skeletonsDir = join69(root, "_skeletons");
16310
16435
  let entries;
16311
16436
  try {
16312
16437
  entries = readdirSync28(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
16313
16438
  } catch {
16314
16439
  return [];
16315
16440
  }
16316
- return entries.filter((name) => existsSync56(join68(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
16441
+ return entries.filter((name) => existsSync57(join69(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
16317
16442
  }
16318
16443
  async function runSkeletonDriftCheck() {
16319
16444
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
16320
16445
  const skeletons = discoverSkeletons(root);
16321
16446
  let filesConsidered = 0;
16322
16447
  for (const name of skeletons) {
16323
- const skeletonRoot = join68(root, "_skeletons", name);
16448
+ const skeletonRoot = join69(root, "_skeletons", name);
16324
16449
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
16325
- if (existsSync56(join68(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
16450
+ if (existsSync57(join69(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
16326
16451
  filesConsidered += 1;
16327
16452
  }
16328
16453
  }
@@ -16330,7 +16455,7 @@ async function runSkeletonDriftCheck() {
16330
16455
  `audited ${skeletons.length} skeleton(s) (${skeletons.join(", ") || "none"}), ${filesConsidered} file(s) considered, under ${root}/_skeletons`
16331
16456
  );
16332
16457
  if (skeletons.length === 0) {
16333
- if (!existsSync56(join68(root, "_skeletons"))) {
16458
+ if (!existsSync57(join69(root, "_skeletons"))) {
16334
16459
  console.log(
16335
16460
  `\xB7 Skeleton-drift guard: no _skeletons/ directory under ${root} \u2014 this is not a repo that ships scaffolding (a satellite repo never carries one). Not applicable; skipping.`
16336
16461
  );
@@ -16342,7 +16467,7 @@ async function runSkeletonDriftCheck() {
16342
16467
  process.exit(1);
16343
16468
  }
16344
16469
  const violations = skeletons.flatMap(
16345
- (name) => auditSkeleton(join68(root, "_skeletons", name), name)
16470
+ (name) => auditSkeleton(join69(root, "_skeletons", name), name)
16346
16471
  );
16347
16472
  if (violations.length > 0) {
16348
16473
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -16377,7 +16502,7 @@ async function runTerraformInputCheck() {
16377
16502
  }
16378
16503
 
16379
16504
  // src/commands/check.ts
16380
- var checkCommand = new Command24("check").description(
16505
+ var checkCommand = new Command25("check").description(
16381
16506
  "Repo guards (ownership, ownership-header-claim, release subject, plugin terraform, plugin collisions, eventbridge-log-permissions, plugin-tool-supply, core-direct-paths, instance-adoption, orphan-ratchet, cognito-invite-template, lambda-output, pipe-trap, codeql-suppression, skeleton-drift, terraform-input, plugin-allowlist-convention, migration-body-change, distribution-inventory, distribution-remote-state, api-gateway-integration) run in CI and git hooks, plus out-of-band audits (branch protection, plugin-staleness)"
16382
16507
  );
16383
16508
  checkCommand.command("ownership").description("Refuse changes to template-owned paths in an instance (#370)").argument("[base]", "Base branch to diff against; defaults to $GITHUB_BASE_REF").option("--staged <messageFile>", "Check staged changes instead of a branch diff (commit hook)").allowExcessArguments(true).action(async () => {
@@ -16543,10 +16668,10 @@ function rawArgsAfter(subcommand) {
16543
16668
  }
16544
16669
 
16545
16670
  // src/commands/doctor.ts
16546
- import { existsSync as existsSync57, readFileSync as readFileSync48 } from "fs";
16547
- import { join as join70, resolve as resolve20 } from "path";
16671
+ import { existsSync as existsSync58, readFileSync as readFileSync48 } from "fs";
16672
+ import { join as join71, resolve as resolve21 } from "path";
16548
16673
  import chalk21 from "chalk";
16549
- import { Command as Command25 } from "commander";
16674
+ import { Command as Command26 } from "commander";
16550
16675
 
16551
16676
  // src/adapters/github-cli/index.ts
16552
16677
  var GithubCliAdapter = class {
@@ -16885,11 +17010,11 @@ async function reapAllBareBranches(cwd, branches, worktrees, currentBranch, deps
16885
17010
 
16886
17011
  // src/lib/scratch-clone-scan.ts
16887
17012
  import { readdirSync as readdirSync29, statSync as statSync18 } from "fs";
16888
- import { join as join69 } from "path";
17013
+ import { join as join70 } from "path";
16889
17014
  var INTEGRATION_BRANCH = "dev";
16890
17015
  function isPlainCloneDir(path) {
16891
17016
  try {
16892
- return statSync18(join69(path, ".git")).isDirectory();
17017
+ return statSync18(join70(path, ".git")).isDirectory();
16893
17018
  } catch {
16894
17019
  return false;
16895
17020
  }
@@ -16904,7 +17029,7 @@ async function findScratchCloneCandidates(estateRoot, deps) {
16904
17029
  const names = readdirSync29(estateRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
16905
17030
  const candidates = [];
16906
17031
  for (const name of names) {
16907
- const path = join69(estateRoot, name);
17032
+ const path = join70(estateRoot, name);
16908
17033
  if (!isPlainCloneDir(path)) continue;
16909
17034
  let branch;
16910
17035
  try {
@@ -16953,7 +17078,7 @@ async function classifyScratchClones(candidates, deps) {
16953
17078
 
16954
17079
  // src/commands/doctor.ts
16955
17080
  var INTEGRATION_BRANCH2 = "dev";
16956
- var doctorCommand = new Command25("doctor").description(
17081
+ var doctorCommand = new Command26("doctor").description(
16957
17082
  "Report repo-state conditions that make everything read from this checkout unreliable"
16958
17083
  ).option("--cwd <path>", "Repo root to inspect (defaults to the current directory)").option("--no-fetch", "Skip the fetch; report against refs as they already are locally").option(
16959
17084
  "--fix",
@@ -16964,10 +17089,10 @@ var doctorCommand = new Command25("doctor").description(
16964
17089
  ).action(
16965
17090
  async (options) => {
16966
17091
  if (options.scratchClones !== void 0) {
16967
- await runScratchCloneScan(resolve20(options.scratchClones));
17092
+ await runScratchCloneScan(resolve21(options.scratchClones));
16968
17093
  return;
16969
17094
  }
16970
- const cwd = options.cwd ? resolve20(options.cwd) : process.cwd();
17095
+ const cwd = options.cwd ? resolve21(options.cwd) : process.cwd();
16971
17096
  const git = new GitAdapter();
16972
17097
  try {
16973
17098
  const facts = await gatherRepoFacts({ cwd, fetch: options.fetch !== false }, { git });
@@ -17084,8 +17209,8 @@ function printScratchCloneReports(estateRoot, reports) {
17084
17209
  );
17085
17210
  }
17086
17211
  function readLocalCoreVersion(cwd) {
17087
- const path = join70(cwd, INSTANCE_CORE_FILE);
17088
- if (!existsSync57(path)) return null;
17212
+ const path = join71(cwd, INSTANCE_CORE_FILE);
17213
+ if (!existsSync58(path)) return null;
17089
17214
  try {
17090
17215
  return extractVersionField(readFileSync48(path, "utf8"));
17091
17216
  } catch {
@@ -17107,8 +17232,8 @@ function extractVersionField(contents) {
17107
17232
  return match?.[1] ?? null;
17108
17233
  }
17109
17234
  function readFossil(cwd) {
17110
- const path = join70(cwd, CORE_VERSION_FILE);
17111
- if (!existsSync57(path)) return null;
17235
+ const path = join71(cwd, CORE_VERSION_FILE);
17236
+ if (!existsSync58(path)) return null;
17112
17237
  try {
17113
17238
  const value = readFileSync48(path, "utf8").trim();
17114
17239
  return value === "" ? null : value;
@@ -17231,9 +17356,9 @@ function printBranchReapOutcomes(outcomes) {
17231
17356
  import { execSync as execSync7 } from "child_process";
17232
17357
  import { GetCallerIdentityCommand as GetCallerIdentityCommand3, STSClient as STSClient3 } from "@aws-sdk/client-sts";
17233
17358
  import chalk22 from "chalk";
17234
- import { Command as Command26 } from "commander";
17359
+ import { Command as Command27 } from "commander";
17235
17360
  import inquirer8 from "inquirer";
17236
- var teardownCommand = new Command26("teardown").description(
17361
+ var teardownCommand = new Command27("teardown").description(
17237
17362
  "Destroy all infrastructure then remove the repo, IAM role, and state bucket \u2014 single command"
17238
17363
  ).option("--project <name>", "Project name to tear down (reads session if omitted)").option("--skip-destroy", "Skip terraform destroy (only use if infrastructure is already gone)").option(
17239
17364
  "--confirm <name>",
@@ -17641,31 +17766,11 @@ function resolveGithubToken4() {
17641
17766
  }
17642
17767
 
17643
17768
  // src/lib/packaged-script-command.ts
17644
- import { spawnSync } from "child_process";
17769
+ import { spawnSync as spawnSync2 } from "child_process";
17645
17770
  import { chmodSync as chmodSync2, statSync as statSync19 } from "fs";
17646
- import { dirname as dirname12 } from "path";
17647
- import { fileURLToPath as fileURLToPath6 } from "url";
17648
- import { Command as Command27 } from "commander";
17649
-
17650
- // src/lib/packaged-scripts.ts
17651
- import { existsSync as existsSync58 } from "fs";
17652
- import { dirname as dirname11, join as join71 } from "path";
17653
- function findPackagedScript(startDir, relativePath) {
17654
- let dir = startDir;
17655
- for (; ; ) {
17656
- const candidate = join71(dir, relativePath);
17657
- if (existsSync58(candidate)) return candidate;
17658
- const parent = dirname11(dir);
17659
- if (parent === dir) return null;
17660
- dir = parent;
17661
- }
17662
- }
17663
- function packagedScriptMissing(relativePath) {
17664
- return `biffo: cannot find ${relativePath}.
17665
- It ships with this package via cli/scripts/packaged-root-assets.mjs; if you are running from a checkout, run from inside the template repo. If you are running the published package, this is a packaging bug \u2014 the asset was not copied at prepack.`;
17666
- }
17667
-
17668
- // src/lib/packaged-script-command.ts
17771
+ import { dirname as dirname13 } from "path";
17772
+ import { fileURLToPath as fileURLToPath7 } from "url";
17773
+ import { Command as Command28 } from "commander";
17669
17774
  function ensureExecutable(script) {
17670
17775
  try {
17671
17776
  const { mode } = statSync19(script);
@@ -17675,14 +17780,14 @@ function ensureExecutable(script) {
17675
17780
  }
17676
17781
  function runPackagedScript(script, args, cwd) {
17677
17782
  ensureExecutable(script);
17678
- const result = spawnSync(script, args, { stdio: "inherit", cwd });
17783
+ const result = spawnSync2(script, args, { stdio: "inherit", cwd });
17679
17784
  return result.status === null ? 2 : result.status;
17680
17785
  }
17681
17786
  function packagedScriptCommand(spec) {
17682
- const command = new Command27(spec.name).description(spec.description).allowExcessArguments(true).allowUnknownOption(true);
17787
+ const command = new Command28(spec.name).description(spec.description).allowExcessArguments(true).allowUnknownOption(true);
17683
17788
  if (spec.argument) command.argument(`<${spec.argument.name}>`, spec.argument.description);
17684
17789
  return command.action(() => {
17685
- const here = dirname12(fileURLToPath6(import.meta.url));
17790
+ const here = dirname13(fileURLToPath7(import.meta.url));
17686
17791
  const script = findPackagedScript(here, spec.script);
17687
17792
  if (!script) {
17688
17793
  process.stderr.write(`${packagedScriptMissing(spec.script)}
@@ -17768,7 +17873,7 @@ var runnerDropForensicsCommand = packagedScriptCommand({
17768
17873
  });
17769
17874
 
17770
17875
  // src/index.ts
17771
- var program = new Command28();
17876
+ var program = new Command29();
17772
17877
  function cliVersion() {
17773
17878
  try {
17774
17879
  return getLatestCoreVersion();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.317.2",
3
+ "version": "0.318.0",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",