@biffo/cli 0.273.6 → 0.273.8

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 +209 -174
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -8380,8 +8380,8 @@ function printEntry(entry) {
8380
8380
  }
8381
8381
 
8382
8382
  // src/commands/plugin-install.ts
8383
- import { cpSync as cpSync3, existsSync as existsSync28, mkdirSync as mkdirSync9, readFileSync as readFileSync21, statSync as statSync6 } from "fs";
8384
- import { basename, join as join29, relative as relative3, resolve as resolve12 } from "path";
8383
+ import { cpSync as cpSync4, existsSync as existsSync28, mkdirSync as mkdirSync10, readFileSync as readFileSync21, statSync as statSync6 } from "fs";
8384
+ import { join as join30, relative as relative3, resolve as resolve12 } from "path";
8385
8385
  import chalk15 from "chalk";
8386
8386
  import { Command as Command15 } from "commander";
8387
8387
 
@@ -8427,9 +8427,61 @@ var PluginMigrationsAdapter = class {
8427
8427
  }
8428
8428
  };
8429
8429
 
8430
+ // src/lib/plugin-source-copy.ts
8431
+ import { copyFileSync as copyFileSync2, cpSync as cpSync3, mkdirSync as mkdirSync9 } from "fs";
8432
+ import { basename, dirname as dirname9, join as join28 } from "path";
8433
+ import { execa as execa5 } from "execa";
8434
+ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
8435
+ ".git",
8436
+ ".venv",
8437
+ "node_modules",
8438
+ "__pycache__",
8439
+ ".ruff_cache",
8440
+ ".pytest_cache",
8441
+ ".mypy_cache",
8442
+ "dist",
8443
+ ".terraform"
8444
+ ]);
8445
+ async function copyPluginSource(sourceDir, targetDir) {
8446
+ if (await isGitWorkingTree(sourceDir)) {
8447
+ const files = await listGitFiles(sourceDir);
8448
+ for (const relPath of files) {
8449
+ const destPath = join28(targetDir, relPath);
8450
+ mkdirSync9(dirname9(destPath), { recursive: true });
8451
+ copyFileSync2(join28(sourceDir, relPath), destPath);
8452
+ }
8453
+ return { usedGitIgnoreRules: true };
8454
+ }
8455
+ log.warn(
8456
+ `${sourceDir} is not a git working tree \u2014 cannot honour .gitignore. Falling back to a fixed exclude list (.git, .venv, node_modules, caches); anything else it does not know about (e.g. an unfamiliar cache directory) will be copied.`
8457
+ );
8458
+ mkdirSync9(targetDir, { recursive: true });
8459
+ cpSync3(sourceDir, targetDir, {
8460
+ recursive: true,
8461
+ filter: (src) => !LOCAL_COPY_EXCLUDES.has(basename(src))
8462
+ });
8463
+ return { usedGitIgnoreRules: false };
8464
+ }
8465
+ async function isGitWorkingTree(dir) {
8466
+ try {
8467
+ await execa5("git", ["rev-parse", "--is-inside-work-tree"], { cwd: dir });
8468
+ return true;
8469
+ } catch {
8470
+ return false;
8471
+ }
8472
+ }
8473
+ async function listGitFiles(dir) {
8474
+ const { stdout } = await execa5(
8475
+ "git",
8476
+ ["ls-files", "--cached", "--others", "--exclude-standard", "-z"],
8477
+ { cwd: dir }
8478
+ );
8479
+ return stdout.split("\0").filter((p) => p.length > 0);
8480
+ }
8481
+
8430
8482
  // src/lib/plugin-workspace-sources.ts
8431
8483
  import { existsSync as existsSync27, readdirSync as readdirSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
8432
- import { join as join28 } from "path";
8484
+ import { join as join29 } from "path";
8433
8485
  function readTomlStringArray(text, key) {
8434
8486
  const open = new RegExp(`^${key}\\s*=\\s*\\[`, "m").exec(text);
8435
8487
  if (!open) return [];
@@ -8473,7 +8525,7 @@ function readDependencyNames(text) {
8473
8525
  return readTomlStringArray(text, "dependencies").map((dep) => /^\s*([A-Za-z0-9._-]+)/.exec(dep)?.[1] ?? "").filter(Boolean);
8474
8526
  }
8475
8527
  function workspaceMemberNames(instanceRoot) {
8476
- const rootPyproject = join28(instanceRoot, "pyproject.toml");
8528
+ const rootPyproject = join29(instanceRoot, "pyproject.toml");
8477
8529
  if (!existsSync27(rootPyproject)) return /* @__PURE__ */ new Set();
8478
8530
  const text = readFileSync20(rootPyproject, "utf8");
8479
8531
  const members = readTomlStringArray(text, "members");
@@ -8484,7 +8536,7 @@ function workspaceMemberNames(instanceRoot) {
8484
8536
  const base = member.slice(0, -2);
8485
8537
  let entries;
8486
8538
  try {
8487
- entries = readdirSync12(join28(instanceRoot, base), { withFileTypes: true });
8539
+ entries = readdirSync12(join29(instanceRoot, base), { withFileTypes: true });
8488
8540
  } catch {
8489
8541
  continue;
8490
8542
  }
@@ -8498,7 +8550,7 @@ function workspaceMemberNames(instanceRoot) {
8498
8550
  }
8499
8551
  const names = /* @__PURE__ */ new Set();
8500
8552
  for (const dir of dirs) {
8501
- const pp = join28(instanceRoot, dir, "pyproject.toml");
8553
+ const pp = join29(instanceRoot, dir, "pyproject.toml");
8502
8554
  if (!existsSync27(pp)) continue;
8503
8555
  const name = readProjectName(readFileSync20(pp, "utf8"));
8504
8556
  if (name) names.add(name);
@@ -8536,7 +8588,7 @@ ${lines.join("\n")}
8536
8588
  return toAdd;
8537
8589
  }
8538
8590
  function applyWorkspaceSources(targetDir, cwd, relTargetDir) {
8539
- const pluginPyproject = join28(targetDir, "pyproject.toml");
8591
+ const pluginPyproject = join29(targetDir, "pyproject.toml");
8540
8592
  if (!existsSync27(pluginPyproject)) return;
8541
8593
  const sourced = ensureWorkspaceSources(pluginPyproject, workspaceMemberNames(cwd));
8542
8594
  if (sourced.length > 0) {
@@ -8576,17 +8628,6 @@ var pluginInstallCommand = new Command15("install").description(
8576
8628
  }
8577
8629
  }
8578
8630
  );
8579
- var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
8580
- ".git",
8581
- ".venv",
8582
- "node_modules",
8583
- "__pycache__",
8584
- ".ruff_cache",
8585
- ".pytest_cache",
8586
- ".mypy_cache",
8587
- "dist",
8588
- ".terraform"
8589
- ]);
8590
8631
  function resolveLocalPlugin(localPath) {
8591
8632
  if (!existsSync28(localPath)) {
8592
8633
  throw new Error(`--local path does not exist: ${localPath}`);
@@ -8594,7 +8635,7 @@ function resolveLocalPlugin(localPath) {
8594
8635
  if (!statSync6(localPath).isDirectory()) {
8595
8636
  throw new Error(`--local path is not a directory: ${localPath}`);
8596
8637
  }
8597
- const manifestPath = join29(localPath, "biffo.plugin.json");
8638
+ const manifestPath = join30(localPath, "biffo.plugin.json");
8598
8639
  if (!existsSync28(manifestPath)) {
8599
8640
  throw new Error(
8600
8641
  `${localPath} does not contain a biffo.plugin.json manifest at its root \u2014 is it a plugin directory? (Scaffold one with \`biffo plugin create <name>\`.)`
@@ -8621,7 +8662,7 @@ function parsePluginTarget(target) {
8621
8662
  async function cloneAndValidatePlugin(entry, git) {
8622
8663
  const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
8623
8664
  try {
8624
- const manifestPath = join29(tmpDir, "biffo.plugin.json");
8665
+ const manifestPath = join30(tmpDir, "biffo.plugin.json");
8625
8666
  if (!existsSync28(manifestPath)) {
8626
8667
  throw new Error(
8627
8668
  `Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
@@ -8650,7 +8691,7 @@ async function runPluginInstall(target, options, deps) {
8650
8691
  `Nothing to install. Pass a registry target (e.g. \`biffo plugin install acme-crm@1.0\`) or a local plugin directory (\`biffo plugin install --local services/acme-crm\`).`
8651
8692
  );
8652
8693
  }
8653
- const servicesDir = join29(options.cwd, "services");
8694
+ const servicesDir = join30(options.cwd, "services");
8654
8695
  if (!existsSync28(servicesDir)) {
8655
8696
  throw new Error(
8656
8697
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
@@ -8669,8 +8710,8 @@ async function runPluginInstall(target, options, deps) {
8669
8710
  }
8670
8711
  const pluginName = entry ? entry.name : source.name;
8671
8712
  const relTargetDir = pluginDir(pluginName, "third-party");
8672
- const targetDir = join29(options.cwd, relTargetDir);
8673
- const modulesDir = join29(options.cwd, "modules", "plugins", pluginName);
8713
+ const targetDir = join30(options.cwd, relTargetDir);
8714
+ const modulesDir = join30(options.cwd, "modules", "plugins", pluginName);
8674
8715
  const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
8675
8716
  if (existsSync28(targetDir) && !inTreeSource) {
8676
8717
  throw new Error(
@@ -8707,19 +8748,16 @@ async function runPluginInstall(target, options, deps) {
8707
8748
  if (inTreeSource) {
8708
8749
  log.info(`${relTargetDir}/ is already in this checkout \u2014 installing in place.`);
8709
8750
  } else {
8710
- mkdirSync9(targetDir, { recursive: true });
8711
- cpSync3(source.sourceDir, targetDir, {
8712
- recursive: true,
8713
- filter: (src) => !LOCAL_COPY_EXCLUDES.has(basename(src))
8714
- });
8751
+ mkdirSync10(targetDir, { recursive: true });
8752
+ await copyPluginSource(source.sourceDir, targetDir);
8715
8753
  log.success(`Installed plugin source at ${relTargetDir}/`);
8716
8754
  }
8717
8755
  applyWorkspaceSources(targetDir, options.cwd, relTargetDir);
8718
8756
  const stagePaths = [relTargetDir];
8719
- const tfSourceDir = join29(targetDir, "terraform");
8757
+ const tfSourceDir = join30(targetDir, "terraform");
8720
8758
  if (existsSync28(tfSourceDir)) {
8721
- mkdirSync9(modulesDir, { recursive: true });
8722
- cpSync3(tfSourceDir, modulesDir, { recursive: true });
8759
+ mkdirSync10(modulesDir, { recursive: true });
8760
+ cpSync4(tfSourceDir, modulesDir, { recursive: true });
8723
8761
  stagePaths.push(`modules/plugins/${pluginName}`);
8724
8762
  log.success(`Copied Terraform module to modules/plugins/${pluginName}/`);
8725
8763
  const wiring = syncPluginTerraform(options.cwd);
@@ -8812,7 +8850,7 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
8812
8850
 
8813
8851
  // src/commands/plugin-list.ts
8814
8852
  import { existsSync as existsSync29, readFileSync as readFileSync22 } from "fs";
8815
- import { join as join30, resolve as resolve13 } from "path";
8853
+ import { join as join31, resolve as resolve13 } from "path";
8816
8854
  import chalk16 from "chalk";
8817
8855
  import { Command as Command16 } from "commander";
8818
8856
  var pluginListCommand = new Command16("list").description("List plugins installed in this project checkout").option("--cwd <path>", "Project root to scan (defaults to the current directory)").action(async (options) => {
@@ -8825,7 +8863,7 @@ var pluginListCommand = new Command16("list").description("List plugins installe
8825
8863
  }
8826
8864
  });
8827
8865
  async function runPluginList(options) {
8828
- const servicesDir = join30(options.cwd, "services");
8866
+ const servicesDir = join31(options.cwd, "services");
8829
8867
  if (!existsSync29(servicesDir)) {
8830
8868
  throw new Error(
8831
8869
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
@@ -8872,7 +8910,7 @@ async function runPluginList(options) {
8872
8910
 
8873
8911
  // src/commands/plugin-sync-migrations.ts
8874
8912
  import { existsSync as existsSync30 } from "fs";
8875
- import { join as join31, relative as relative4, resolve as resolve14 } from "path";
8913
+ import { join as join32, relative as relative4, resolve as resolve14 } from "path";
8876
8914
  import chalk17 from "chalk";
8877
8915
  import { Command as Command17 } from "commander";
8878
8916
  var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
@@ -8893,11 +8931,11 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
8893
8931
  }
8894
8932
  );
8895
8933
  async function runPluginSyncMigrations(name, options, deps) {
8896
- const servicesDir = join31(options.cwd, "services");
8934
+ const servicesDir = join32(options.cwd, "services");
8897
8935
  if (!existsSync30(servicesDir)) {
8898
8936
  throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
8899
8937
  }
8900
- if (name && !existsSync30(join31(servicesDir, name, "biffo.plugin.json"))) {
8938
+ if (name && !existsSync30(join32(servicesDir, name, "biffo.plugin.json"))) {
8901
8939
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
8902
8940
  }
8903
8941
  if (options.dryRun) {
@@ -8934,7 +8972,7 @@ async function runPluginSyncMigrations(name, options, deps) {
8934
8972
 
8935
8973
  // src/commands/plugin-uninstall.ts
8936
8974
  import { existsSync as existsSync31, readFileSync as readFileSync23, rmSync as rmSync8 } from "fs";
8937
- import { join as join32, resolve as resolve15 } from "path";
8975
+ import { join as join33, resolve as resolve15 } from "path";
8938
8976
  import chalk18 from "chalk";
8939
8977
  import { Command as Command18 } from "commander";
8940
8978
  import inquirer6 from "inquirer";
@@ -8966,15 +9004,15 @@ async function runPluginUninstall(name, options, deps) {
8966
9004
  if (!NAME_PATTERN2.test(name)) {
8967
9005
  throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
8968
9006
  }
8969
- const servicesDir = join32(options.cwd, "services");
9007
+ const servicesDir = join33(options.cwd, "services");
8970
9008
  if (!existsSync31(servicesDir)) {
8971
9009
  throw new Error(
8972
9010
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
8973
9011
  );
8974
9012
  }
8975
- const targetDir = join32(servicesDir, name);
9013
+ const targetDir = join33(servicesDir, name);
8976
9014
  if (!existsSync31(targetDir)) {
8977
- const firstParty = join32(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
9015
+ const firstParty = join33(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
8978
9016
  if (existsSync31(firstParty)) {
8979
9017
  throw new Error(
8980
9018
  `Plugin '${name}' is a first-party plugin at ${pluginDir(name, "first-party")}/, which is template-owned \u2014 \`biffo core upgrade\` would restore it on the next upgrade. Disable it instead by removing '${name}' from \`enabled_plugins\` in infra/environments/<env>/main.tf and re-applying.`
@@ -8983,7 +9021,7 @@ async function runPluginUninstall(name, options, deps) {
8983
9021
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
8984
9022
  }
8985
9023
  const version = readInstalledVersion(targetDir);
8986
- const modulesDir = join32(options.cwd, "modules", "plugins", name);
9024
+ const modulesDir = join33(options.cwd, "modules", "plugins", name);
8987
9025
  const stagePaths = [`services/${name}`];
8988
9026
  if (existsSync31(modulesDir)) {
8989
9027
  stagePaths.push(`modules/plugins/${name}`);
@@ -9044,7 +9082,7 @@ async function runPluginUninstall(name, options, deps) {
9044
9082
  }
9045
9083
  }
9046
9084
  function readInstalledVersion(targetDir) {
9047
- const manifestPath = join32(targetDir, "biffo.plugin.json");
9085
+ const manifestPath = join33(targetDir, "biffo.plugin.json");
9048
9086
  if (!existsSync31(manifestPath)) return void 0;
9049
9087
  try {
9050
9088
  return validateManifest(JSON.parse(readFileSync23(manifestPath, "utf8"))).version;
@@ -9080,8 +9118,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
9080
9118
  }
9081
9119
 
9082
9120
  // src/commands/plugin-upgrade.ts
9083
- import { cpSync as cpSync4, existsSync as existsSync32, mkdirSync as mkdirSync10, readFileSync as readFileSync24, rmSync as rmSync9 } from "fs";
9084
- import { basename as basename2, join as join33, relative as relative5, resolve as resolve16 } from "path";
9121
+ import { cpSync as cpSync5, existsSync as existsSync32, mkdirSync as mkdirSync11, readFileSync as readFileSync24, rmSync as rmSync9 } from "fs";
9122
+ import { join as join34, relative as relative5, resolve as resolve16 } from "path";
9085
9123
  import chalk19 from "chalk";
9086
9124
  import { Command as Command19 } from "commander";
9087
9125
  import inquirer7 from "inquirer";
@@ -9128,7 +9166,7 @@ async function runPluginUpgrade(target, options, deps) {
9128
9166
  `Nothing to upgrade. Pass a registry target (e.g. \`biffo plugin upgrade acme-crm@1.1\`) or a local checkout to refresh from (\`biffo plugin upgrade --local ../acme-crm\`).`
9129
9167
  );
9130
9168
  }
9131
- const servicesDir = join33(options.cwd, "services");
9169
+ const servicesDir = join34(options.cwd, "services");
9132
9170
  if (!existsSync32(servicesDir)) {
9133
9171
  throw new Error(
9134
9172
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
@@ -9138,7 +9176,7 @@ async function runPluginUpgrade(target, options, deps) {
9138
9176
  return runLocalPluginRefresh(options.local, options, deps);
9139
9177
  }
9140
9178
  const { name, minor } = parsePluginTarget(target);
9141
- const targetDir = join33(servicesDir, name);
9179
+ const targetDir = join34(servicesDir, name);
9142
9180
  if (!existsSync32(targetDir)) {
9143
9181
  throw new Error(
9144
9182
  `Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
@@ -9153,7 +9191,7 @@ async function runPluginUpgrade(target, options, deps) {
9153
9191
  `Plugin declares required_core_version '${entry.required_core_version}'. The CLI cannot verify this against your deployment \u2014 the Core API exposes no version endpoint and services/api/pyproject.toml's version is a static placeholder, not a real release marker. Confirm compatibility yourself before deploying.`
9154
9192
  );
9155
9193
  }
9156
- const modulesDir = join33(options.cwd, "modules", "plugins", entry.name);
9194
+ const modulesDir = join34(options.cwd, "modules", "plugins", entry.name);
9157
9195
  if (options.dryRun) {
9158
9196
  printDryRun6(entry, currentVersion);
9159
9197
  return;
@@ -9182,18 +9220,18 @@ async function runPluginUpgrade(target, options, deps) {
9182
9220
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9183
9221
  );
9184
9222
  rmSync9(targetDir, { recursive: true, force: true });
9185
- mkdirSync10(targetDir, { recursive: true });
9186
- cpSync4(tmpDir, targetDir, { recursive: true });
9223
+ mkdirSync11(targetDir, { recursive: true });
9224
+ cpSync5(tmpDir, targetDir, { recursive: true });
9187
9225
  log.success(`Upgraded plugin source at services/${entry.name}/`);
9188
9226
  applyWorkspaceSources(targetDir, options.cwd, `services/${entry.name}`);
9189
9227
  const stagePaths = [`services/${entry.name}`];
9190
9228
  if (existsSync32(modulesDir)) {
9191
9229
  rmSync9(modulesDir, { recursive: true, force: true });
9192
9230
  }
9193
- const tfSourceDir = join33(targetDir, "terraform");
9231
+ const tfSourceDir = join34(targetDir, "terraform");
9194
9232
  if (existsSync32(tfSourceDir)) {
9195
- mkdirSync10(modulesDir, { recursive: true });
9196
- cpSync4(tfSourceDir, modulesDir, { recursive: true });
9233
+ mkdirSync11(modulesDir, { recursive: true });
9234
+ cpSync5(tfSourceDir, modulesDir, { recursive: true });
9197
9235
  stagePaths.push(`modules/plugins/${entry.name}`);
9198
9236
  log.success(`Copied Terraform module to modules/plugins/${entry.name}/`);
9199
9237
  }
@@ -9227,8 +9265,8 @@ async function runPluginUpgrade(target, options, deps) {
9227
9265
  async function runLocalPluginRefresh(localPath, options, deps) {
9228
9266
  const source = resolveLocalPlugin(localPath);
9229
9267
  log.success(`Resolved ${source.name}@${source.version} from ${source.origin}`);
9230
- const servicesDir = join33(options.cwd, "services");
9231
- const targetDir = join33(servicesDir, source.name);
9268
+ const servicesDir = join34(options.cwd, "services");
9269
+ const targetDir = join34(servicesDir, source.name);
9232
9270
  if (!existsSync32(targetDir)) {
9233
9271
  throw new Error(
9234
9272
  `Plugin '${source.name}' is not installed at services/${source.name}/. Use 'biffo plugin install --local ${localPath}' instead.`
@@ -9236,7 +9274,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9236
9274
  }
9237
9275
  const inTreeSource = resolve16(source.sourceDir) === resolve16(targetDir);
9238
9276
  const currentVersion = readInstalledVersion2(targetDir);
9239
- const modulesDir = join33(options.cwd, "modules", "plugins", source.name);
9277
+ const modulesDir = join34(options.cwd, "modules", "plugins", source.name);
9240
9278
  if (options.dryRun) {
9241
9279
  printLocalDryRun(source, currentVersion, inTreeSource);
9242
9280
  return;
@@ -9265,11 +9303,8 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9265
9303
  );
9266
9304
  } else {
9267
9305
  rmSync9(targetDir, { recursive: true, force: true });
9268
- mkdirSync10(targetDir, { recursive: true });
9269
- cpSync4(source.sourceDir, targetDir, {
9270
- recursive: true,
9271
- filter: (src) => !LOCAL_COPY_EXCLUDES.has(basename2(src))
9272
- });
9306
+ mkdirSync11(targetDir, { recursive: true });
9307
+ await copyPluginSource(source.sourceDir, targetDir);
9273
9308
  log.success(`Refreshed plugin source at services/${source.name}/ from ${source.origin}`);
9274
9309
  }
9275
9310
  applyWorkspaceSources(targetDir, options.cwd, `services/${source.name}`);
@@ -9277,10 +9312,10 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9277
9312
  if (existsSync32(modulesDir)) {
9278
9313
  rmSync9(modulesDir, { recursive: true, force: true });
9279
9314
  }
9280
- const tfSourceDir = join33(targetDir, "terraform");
9315
+ const tfSourceDir = join34(targetDir, "terraform");
9281
9316
  if (existsSync32(tfSourceDir)) {
9282
- mkdirSync10(modulesDir, { recursive: true });
9283
- cpSync4(tfSourceDir, modulesDir, { recursive: true });
9317
+ mkdirSync11(modulesDir, { recursive: true });
9318
+ cpSync5(tfSourceDir, modulesDir, { recursive: true });
9284
9319
  stagePaths.push(`modules/plugins/${source.name}`);
9285
9320
  log.success(`Refreshed Terraform module at modules/plugins/${source.name}/`);
9286
9321
  }
@@ -9319,7 +9354,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9319
9354
  }
9320
9355
  }
9321
9356
  function readInstalledVersion2(targetDir) {
9322
- const manifestPath = join33(targetDir, "biffo.plugin.json");
9357
+ const manifestPath = join34(targetDir, "biffo.plugin.json");
9323
9358
  if (!existsSync32(manifestPath)) return void 0;
9324
9359
  try {
9325
9360
  return validateManifest(JSON.parse(readFileSync24(manifestPath, "utf8"))).version;
@@ -9656,17 +9691,17 @@ import { Command as Command23 } from "commander";
9656
9691
 
9657
9692
  // src/scripts/check-adr-numbering.ts
9658
9693
  import { existsSync as existsSync35 } from "fs";
9659
- import { join as join35 } from "path";
9660
- import { execa as execa5 } from "execa";
9694
+ import { join as join36 } from "path";
9695
+ import { execa as execa6 } from "execa";
9661
9696
 
9662
9697
  // src/lib/adr-numbering-guard.ts
9663
9698
  import { existsSync as existsSync34, readdirSync as readdirSync13, readFileSync as readFileSync26 } from "fs";
9664
- import { join as join34 } from "path";
9699
+ import { join as join35 } from "path";
9665
9700
  var ADR_FILENAME = /^(\d{4})-.+\.md$/;
9666
9701
  var ALLOWLIST_FILENAME = ".numbering-allowlist";
9667
9702
  var TEMPLATE_ADR_RESERVED_UPTO = "0099";
9668
9703
  function readAdrNumberingAllowlist(adrDir) {
9669
- const path = join34(adrDir, ALLOWLIST_FILENAME);
9704
+ const path = join35(adrDir, ALLOWLIST_FILENAME);
9670
9705
  if (!existsSync34(path)) return /* @__PURE__ */ new Set();
9671
9706
  const numbers = /* @__PURE__ */ new Set();
9672
9707
  for (const rawLine of readFileSync26(path, "utf8").split("\n")) {
@@ -9731,8 +9766,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
9731
9766
 
9732
9767
  // src/scripts/check-adr-numbering.ts
9733
9768
  async function runAdrNumberingCheck() {
9734
- const root = (await execa5("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
9735
- const adrDir = join35(root, "docs", "ADR");
9769
+ const root = (await execa6("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
9770
+ const adrDir = join36(root, "docs", "ADR");
9736
9771
  if (!existsSync35(adrDir)) {
9737
9772
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
9738
9773
  return;
@@ -9772,7 +9807,7 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
9772
9807
 
9773
9808
  // src/scripts/check-branch-protection.ts
9774
9809
  import { Octokit as Octokit2 } from "@octokit/rest";
9775
- import { execa as execa6 } from "execa";
9810
+ import { execa as execa7 } from "execa";
9776
9811
 
9777
9812
  // src/lib/branch-protection-apply.ts
9778
9813
  var CONTEXT_CONSISTENCY_THRESHOLD = 2 / 3;
@@ -9899,7 +9934,7 @@ async function resolveRepo(explicit) {
9899
9934
  }
9900
9935
  return { owner, repo };
9901
9936
  }
9902
- const { stdout } = await execa6("git", ["remote", "get-url", "origin"]);
9937
+ const { stdout } = await execa7("git", ["remote", "get-url", "origin"]);
9903
9938
  const m = /github\.com[:/]([^/]+)\/(.+?)(?:\.git)?$/.exec(stdout.trim());
9904
9939
  if (!m?.[1] || !m[2]) {
9905
9940
  console.error(
@@ -10031,11 +10066,11 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
10031
10066
  }
10032
10067
 
10033
10068
  // src/scripts/check-cognito-invite-template.ts
10034
- import { execa as execa7 } from "execa";
10069
+ import { execa as execa8 } from "execa";
10035
10070
 
10036
10071
  // src/lib/cognito-invite-template-guard.ts
10037
10072
  import { readdirSync as readdirSync14, readFileSync as readFileSync27, statSync as statSync7 } from "fs";
10038
- import { join as join36 } from "path";
10073
+ import { join as join37 } from "path";
10039
10074
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
10040
10075
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
10041
10076
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -10118,7 +10153,7 @@ function findModuleTerraformFiles(repoRoot) {
10118
10153
  }
10119
10154
  for (const entry of entries) {
10120
10155
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
10121
- const full = join36(dir, entry);
10156
+ const full = join37(dir, entry);
10122
10157
  const rel = `${relative8}/${entry}`;
10123
10158
  if (statSync7(full).isDirectory()) {
10124
10159
  walk2(full, rel);
@@ -10127,18 +10162,18 @@ function findModuleTerraformFiles(repoRoot) {
10127
10162
  }
10128
10163
  }
10129
10164
  };
10130
- walk2(join36(repoRoot, "modules"), "modules");
10165
+ walk2(join37(repoRoot, "modules"), "modules");
10131
10166
  return found.sort();
10132
10167
  }
10133
10168
  function checkCognitoInviteTemplates(repoRoot) {
10134
10169
  return findModuleTerraformFiles(repoRoot).flatMap(
10135
- (file) => checkInviteTemplateSource(file, readFileSync27(join36(repoRoot, file), "utf8"))
10170
+ (file) => checkInviteTemplateSource(file, readFileSync27(join37(repoRoot, file), "utf8"))
10136
10171
  );
10137
10172
  }
10138
10173
 
10139
10174
  // src/scripts/check-cognito-invite-template.ts
10140
10175
  async function runCognitoInviteTemplateCheck() {
10141
- const root = (await execa7("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10176
+ const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10142
10177
  const files = findModuleTerraformFiles(root);
10143
10178
  console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
10144
10179
  if (files.length === 0) {
@@ -10160,12 +10195,12 @@ async function runCognitoInviteTemplateCheck() {
10160
10195
  }
10161
10196
 
10162
10197
  // src/scripts/check-core-direct-paths.ts
10163
- import { join as join38 } from "path";
10164
- import { execa as execa8 } from "execa";
10198
+ import { join as join39 } from "path";
10199
+ import { execa as execa9 } from "execa";
10165
10200
 
10166
10201
  // src/lib/core-direct-paths-audit.ts
10167
10202
  import { existsSync as existsSync36, readFileSync as readFileSync28, readdirSync as readdirSync15, statSync as statSync8 } from "fs";
10168
- import { join as join37 } from "path";
10203
+ import { join as join38 } from "path";
10169
10204
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
10170
10205
  var API_ROUTE_PREFIX = "/api/v1";
10171
10206
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -10329,7 +10364,7 @@ function walkFiles(root, accept, skipDir) {
10329
10364
  return;
10330
10365
  }
10331
10366
  for (const entry of entries) {
10332
- const p = join37(dir, entry);
10367
+ const p = join38(dir, entry);
10333
10368
  let st;
10334
10369
  try {
10335
10370
  st = statSync8(p);
@@ -10424,7 +10459,7 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
10424
10459
  }
10425
10460
  function resolveSiblingCoreSrc(params) {
10426
10461
  const { estateDir, sibling } = params;
10427
- const configPath = join37(estateDir, sibling, "biffo.sibling.json");
10462
+ const configPath = join38(estateDir, sibling, "biffo.sibling.json");
10428
10463
  let raw;
10429
10464
  try {
10430
10465
  raw = readFileSync28(configPath, "utf8");
@@ -10447,7 +10482,7 @@ function resolveSiblingCoreSrc(params) {
10447
10482
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
10448
10483
  );
10449
10484
  }
10450
- const coreApiSrcDir = join37(estateDir, coreProject, "services", "api", "src");
10485
+ const coreApiSrcDir = join38(estateDir, coreProject, "services", "api", "src");
10451
10486
  if (!existsSync36(coreApiSrcDir)) {
10452
10487
  throw new Error(
10453
10488
  `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.`
@@ -10489,9 +10524,9 @@ function auditSiblingCoreDirectPaths(params) {
10489
10524
 
10490
10525
  // src/scripts/check-core-direct-paths.ts
10491
10526
  async function runCoreDirectPathsCheck(opts = {}) {
10492
- const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10527
+ const root = (await execa9("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10493
10528
  const sibling = opts.sibling ?? "sibling-template (self-check)";
10494
- const frontendSrcDir = opts.frontendSrc ?? join38(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
10529
+ const frontendSrcDir = opts.frontendSrc ?? join39(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
10495
10530
  let coreApiSrcDir;
10496
10531
  let coreProject = null;
10497
10532
  if (opts.coreSrc) {
@@ -10507,7 +10542,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
10507
10542
  coreApiSrcDir = resolution.coreApiSrcDir;
10508
10543
  coreProject = resolution.coreProject;
10509
10544
  } else {
10510
- coreApiSrcDir = join38(root, "services", "api", "src");
10545
+ coreApiSrcDir = join39(root, "services", "api", "src");
10511
10546
  }
10512
10547
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
10513
10548
  console.log(
@@ -10543,7 +10578,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
10543
10578
  }
10544
10579
 
10545
10580
  // src/scripts/check-core-ownership.ts
10546
- import { execa as execa9 } from "execa";
10581
+ import { execa as execa10 } from "execa";
10547
10582
  var BOLD = "\x1B[1m";
10548
10583
  var DIM = "\x1B[2m";
10549
10584
  var RED = "\x1B[31m";
@@ -10554,7 +10589,7 @@ async function runOwnershipCheck(argv) {
10554
10589
  const stagedFlag = args.indexOf("--staged");
10555
10590
  const staged = stagedFlag !== -1;
10556
10591
  const messageFile = staged ? args[stagedFlag + 1] : void 0;
10557
- const root = (await execa9("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10592
+ const root = (await execa10("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10558
10593
  const ownership = classifyRepoOwnership(root);
10559
10594
  if (ownership === "template") {
10560
10595
  console.log("\u2713 core ownership guard: skipped \u2014 this is the template, which owns these paths.");
@@ -10570,7 +10605,7 @@ async function runOwnershipCheck(argv) {
10570
10605
  let deletedFiles = [];
10571
10606
  let commitMessage = "";
10572
10607
  if (staged) {
10573
- const { stdout } = await execa9("git", ["diff", "--cached", "--name-status"], { cwd: root });
10608
+ const { stdout } = await execa10("git", ["diff", "--cached", "--name-status"], { cwd: root });
10574
10609
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
10575
10610
  if (messageFile) {
10576
10611
  const { readFileSync: readFileSync37, existsSync: existsSync45 } = await import("fs");
@@ -10582,18 +10617,18 @@ async function runOwnershipCheck(argv) {
10582
10617
  console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
10583
10618
  process.exit(2);
10584
10619
  }
10585
- await execa9("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
10586
- const { stdout } = await execa9("git", ["diff", "--name-status", `origin/${base}...HEAD`], {
10620
+ await execa10("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
10621
+ const { stdout } = await execa10("git", ["diff", "--name-status", `origin/${base}...HEAD`], {
10587
10622
  cwd: root
10588
10623
  });
10589
10624
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
10590
- const { stdout: log2 } = await execa9("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
10625
+ const { stdout: log2 } = await execa10("git", ["log", "--format=%B", `origin/${base}..HEAD`], {
10591
10626
  cwd: root,
10592
10627
  reject: false
10593
10628
  });
10594
10629
  commitMessage = log2;
10595
10630
  }
10596
- const { stdout: gitBranch } = await execa9("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
10631
+ const { stdout: gitBranch } = await execa10("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
10597
10632
  cwd: root,
10598
10633
  reject: false
10599
10634
  });
@@ -10675,11 +10710,11 @@ ${BOLD}If the divergence is deliberate${OFF}
10675
10710
  }
10676
10711
 
10677
10712
  // src/scripts/check-eventbridge-log-permissions.ts
10678
- import { execa as execa10 } from "execa";
10713
+ import { execa as execa11 } from "execa";
10679
10714
 
10680
10715
  // src/lib/eventbridge-log-permission-guard.ts
10681
10716
  import { readFileSync as readFileSync29, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
10682
- import { join as join39 } from "path";
10717
+ import { join as join40 } from "path";
10683
10718
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
10684
10719
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
10685
10720
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -10756,7 +10791,7 @@ function walkTerraformFiles(root) {
10756
10791
  return;
10757
10792
  }
10758
10793
  for (const entry of entries) {
10759
- const p = join39(dir, entry);
10794
+ const p = join40(dir, entry);
10760
10795
  let st;
10761
10796
  try {
10762
10797
  st = statSync9(p);
@@ -10852,7 +10887,7 @@ function auditEventBridgeLogPermissions(root) {
10852
10887
 
10853
10888
  // src/scripts/check-eventbridge-log-permissions.ts
10854
10889
  async function runEventBridgeLogPermissionCheck() {
10855
- const root = (await execa10("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10890
+ const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10856
10891
  let report;
10857
10892
  try {
10858
10893
  report = auditEventBridgeLogPermissions(root);
@@ -10889,15 +10924,15 @@ async function runEventBridgeLogPermissionCheck() {
10889
10924
  }
10890
10925
 
10891
10926
  // src/scripts/check-lambda-output.ts
10892
- import { execa as execa11 } from "execa";
10927
+ import { execa as execa12 } from "execa";
10893
10928
 
10894
10929
  // src/lib/lambda-output-guard.ts
10895
10930
  import { readFileSync as readFileSync31 } from "fs";
10896
- import { join as join41 } from "path";
10931
+ import { join as join42 } from "path";
10897
10932
 
10898
10933
  // src/lib/terraform-input-guard.ts
10899
10934
  import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync10 } from "fs";
10900
- import { join as join40 } from "path";
10935
+ import { join as join41 } from "path";
10901
10936
  var GUARDED_SUBCOMMANDS = [
10902
10937
  "init",
10903
10938
  "plan",
@@ -10921,7 +10956,7 @@ function findWorkflowFiles(repoRoot) {
10921
10956
  }
10922
10957
  for (const entry of entries) {
10923
10958
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
10924
- const full = join40(dir, entry);
10959
+ const full = join41(dir, entry);
10925
10960
  const rel = relative8 ? `${relative8}/${entry}` : entry;
10926
10961
  if (statSync10(full).isDirectory()) {
10927
10962
  walk2(full, rel);
@@ -10965,7 +11000,7 @@ function checkWorkflowSource(file, rawSource) {
10965
11000
  }
10966
11001
  function checkTerraformInput(repoRoot) {
10967
11002
  return findWorkflowFiles(repoRoot).flatMap(
10968
- (file) => checkWorkflowSource(file, readFileSync30(join40(repoRoot, file), "utf8"))
11003
+ (file) => checkWorkflowSource(file, readFileSync30(join41(repoRoot, file), "utf8"))
10969
11004
  );
10970
11005
  }
10971
11006
 
@@ -11023,13 +11058,13 @@ function checkWorkflowSource2(file, rawSource) {
11023
11058
  }
11024
11059
  function checkLambdaOutput(repoRoot) {
11025
11060
  return findWorkflowFiles(repoRoot).flatMap(
11026
- (file) => checkWorkflowSource2(file, readFileSync31(join41(repoRoot, file), "utf8"))
11061
+ (file) => checkWorkflowSource2(file, readFileSync31(join42(repoRoot, file), "utf8"))
11027
11062
  );
11028
11063
  }
11029
11064
 
11030
11065
  // src/scripts/check-lambda-output.ts
11031
11066
  async function runLambdaOutputCheck() {
11032
- const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11067
+ const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11033
11068
  const files = findWorkflowFiles(root);
11034
11069
  console.log(`audited ${files.length} workflow file(s) under ${root}`);
11035
11070
  if (files.length === 0) {
@@ -11052,8 +11087,8 @@ async function runLambdaOutputCheck() {
11052
11087
 
11053
11088
  // src/scripts/check-pipe-trap.ts
11054
11089
  import { readFileSync as readFileSync32, readdirSync as readdirSync18 } from "fs";
11055
- import { join as join42, relative as relative6 } from "path";
11056
- import { execa as execa12 } from "execa";
11090
+ import { join as join43, relative as relative6 } from "path";
11091
+ import { execa as execa13 } from "execa";
11057
11092
 
11058
11093
  // src/lib/pipe-trap-guard.ts
11059
11094
  var STATUS_BEARING = [
@@ -11149,7 +11184,7 @@ function findPipeTraps(source) {
11149
11184
  function shellFiles(root) {
11150
11185
  const out = [];
11151
11186
  for (const dir of ["scripts", ".githooks"]) {
11152
- const full = join42(root, dir);
11187
+ const full = join43(root, dir);
11153
11188
  let entries;
11154
11189
  try {
11155
11190
  entries = readdirSync18(full, { withFileTypes: true });
@@ -11159,13 +11194,13 @@ function shellFiles(root) {
11159
11194
  for (const entry of entries) {
11160
11195
  if (!entry.isFile()) continue;
11161
11196
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
11162
- out.push(join42(full, entry.name));
11197
+ out.push(join43(full, entry.name));
11163
11198
  }
11164
11199
  }
11165
11200
  return out;
11166
11201
  }
11167
11202
  async function runPipeTrapCheck() {
11168
- const root = (await execa12("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11203
+ const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11169
11204
  const files = shellFiles(root);
11170
11205
  console.log(`audited ${files.length} shell file(s) under scripts/ and .githooks/ under ${root}`);
11171
11206
  if (files.length === 0) {
@@ -11193,12 +11228,12 @@ async function runPipeTrapCheck() {
11193
11228
 
11194
11229
  // src/scripts/check-plugin-collisions.ts
11195
11230
  import { existsSync as existsSync38 } from "fs";
11196
- import { join as join44 } from "path";
11197
- import { execa as execa13 } from "execa";
11231
+ import { join as join45 } from "path";
11232
+ import { execa as execa14 } from "execa";
11198
11233
 
11199
11234
  // src/lib/plugin-collision-guard.ts
11200
11235
  import { existsSync as existsSync37, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11201
- import { join as join43 } from "path";
11236
+ import { join as join44 } from "path";
11202
11237
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
11203
11238
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
11204
11239
  function subdirectories(dir) {
@@ -11206,19 +11241,19 @@ function subdirectories(dir) {
11206
11241
  return readdirSync19(dir).filter((entry) => {
11207
11242
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
11208
11243
  try {
11209
- return statSync11(join43(dir, entry)).isDirectory();
11244
+ return statSync11(join44(dir, entry)).isDirectory();
11210
11245
  } catch {
11211
11246
  return false;
11212
11247
  }
11213
11248
  });
11214
11249
  }
11215
11250
  function regularPackagesOf(pluginDir2) {
11216
- return subdirectories(pluginDir2).filter((name) => existsSync37(join43(pluginDir2, name, "__init__.py"))).sort();
11251
+ return subdirectories(pluginDir2).filter((name) => existsSync37(join44(pluginDir2, name, "__init__.py"))).sort();
11217
11252
  }
11218
11253
  function bareTestModulesOf(pluginDir2) {
11219
- const testsDir = join43(pluginDir2, "tests");
11254
+ const testsDir = join44(pluginDir2, "tests");
11220
11255
  if (!existsSync37(testsDir)) return [];
11221
- if (existsSync37(join43(testsDir, "__init__.py"))) return [];
11256
+ if (existsSync37(join44(testsDir, "__init__.py"))) return [];
11222
11257
  return readdirSync19(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
11223
11258
  }
11224
11259
  function findCollisions(servicesDir, pluginDirs) {
@@ -11227,7 +11262,7 @@ function findCollisions(servicesDir, pluginDirs) {
11227
11262
  const gather = (kind, namesOf) => {
11228
11263
  const claims = /* @__PURE__ */ new Map();
11229
11264
  for (const plugin of plugins) {
11230
- for (const name of namesOf(join43(servicesDir, plugin))) {
11265
+ for (const name of namesOf(join44(servicesDir, plugin))) {
11231
11266
  claims.set(name, [...claims.get(name) ?? [], plugin]);
11232
11267
  }
11233
11268
  }
@@ -11264,8 +11299,8 @@ function formatCollisions(collisions) {
11264
11299
 
11265
11300
  // src/scripts/check-plugin-collisions.ts
11266
11301
  async function runPluginCollisionCheck() {
11267
- const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11268
- const servicesDir = join44(root, "services");
11302
+ const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11303
+ const servicesDir = join45(root, "services");
11269
11304
  if (!existsSync38(servicesDir)) {
11270
11305
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
11271
11306
  return;
@@ -11283,11 +11318,11 @@ async function runPluginCollisionCheck() {
11283
11318
  }
11284
11319
 
11285
11320
  // src/scripts/check-plugin-terraform.ts
11286
- import { execa as execa14 } from "execa";
11321
+ import { execa as execa15 } from "execa";
11287
11322
 
11288
11323
  // src/lib/plugin-terraform-guard.ts
11289
11324
  import { existsSync as existsSync39, readFileSync as readFileSync33, readdirSync as readdirSync20 } from "fs";
11290
- import { dirname as dirname9, join as join45, relative as relative7, sep as sep3 } from "path";
11325
+ import { dirname as dirname10, join as join46, relative as relative7, sep as sep3 } from "path";
11291
11326
  var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
11292
11327
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
11293
11328
  function findPluginManifests(root) {
@@ -11302,9 +11337,9 @@ function findPluginManifests(root) {
11302
11337
  for (const entry of entries) {
11303
11338
  if (entry.isDirectory()) {
11304
11339
  if (SKIP_DIRS2.has(entry.name)) continue;
11305
- walk2(join45(dir, entry.name));
11340
+ walk2(join46(dir, entry.name));
11306
11341
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
11307
- found.push(relative7(root, join45(dir, entry.name)).split(sep3).join("/"));
11342
+ found.push(relative7(root, join46(dir, entry.name)).split(sep3).join("/"));
11308
11343
  }
11309
11344
  }
11310
11345
  };
@@ -11329,14 +11364,14 @@ function readSubscriptions(absManifestPath) {
11329
11364
  }
11330
11365
  function checkPluginTerraform(root) {
11331
11366
  const violations = [];
11332
- const coreManifest = existsSync39(join45(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
11367
+ const coreManifest = existsSync39(join46(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
11333
11368
  for (const manifest of findPluginManifests(root)) {
11334
11369
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
11335
- const absManifest = join45(root, manifest);
11370
+ const absManifest = join46(root, manifest);
11336
11371
  const subscriptions = readSubscriptions(absManifest);
11337
11372
  if (subscriptions === null) continue;
11338
- const pluginDir2 = dirname9(absManifest);
11339
- if (existsSync39(join45(pluginDir2, "terraform"))) continue;
11373
+ const pluginDir2 = dirname10(absManifest);
11374
+ if (existsSync39(join46(pluginDir2, "terraform"))) continue;
11340
11375
  const relPluginDir = relative7(root, pluginDir2).split(sep3).join("/");
11341
11376
  violations.push({
11342
11377
  manifest,
@@ -11356,7 +11391,7 @@ function formatViolations(violations) {
11356
11391
 
11357
11392
  // src/scripts/check-plugin-terraform.ts
11358
11393
  async function runPluginTerraformCheck() {
11359
- const root = (await execa14("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11394
+ const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11360
11395
  const violations = checkPluginTerraform(root);
11361
11396
  if (violations.length > 0) {
11362
11397
  console.error("\u2717 plugin Terraform guard: event subscriptions with no infrastructure\n");
@@ -11368,12 +11403,12 @@ async function runPluginTerraformCheck() {
11368
11403
 
11369
11404
  // src/scripts/check-plugin-tool-supply.ts
11370
11405
  import { existsSync as existsSync41 } from "fs";
11371
- import { join as join47 } from "path";
11372
- import { execa as execa15 } from "execa";
11406
+ import { join as join48 } from "path";
11407
+ import { execa as execa16 } from "execa";
11373
11408
 
11374
11409
  // src/lib/plugin-tool-supply-audit.ts
11375
11410
  import { existsSync as existsSync40, readFileSync as readFileSync34, readdirSync as readdirSync21, statSync as statSync12 } from "fs";
11376
- import { join as join46 } from "path";
11411
+ import { join as join47 } from "path";
11377
11412
 
11378
11413
  // src/lib/openrouter-model-snapshot.ts
11379
11414
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -11790,7 +11825,7 @@ function listDirs(root) {
11790
11825
  }
11791
11826
  return entries.filter((e) => {
11792
11827
  try {
11793
- return statSync12(join46(root, e)).isDirectory();
11828
+ return statSync12(join47(root, e)).isDirectory();
11794
11829
  } catch {
11795
11830
  return false;
11796
11831
  }
@@ -11806,7 +11841,7 @@ function walkFiles2(root, accept, skipDir) {
11806
11841
  return;
11807
11842
  }
11808
11843
  for (const entry of entries) {
11809
- const p = join46(dir, entry);
11844
+ const p = join47(dir, entry);
11810
11845
  let st;
11811
11846
  try {
11812
11847
  st = statSync12(p);
@@ -11832,14 +11867,14 @@ function pluginPythonFiles(pluginDir2) {
11832
11867
  );
11833
11868
  }
11834
11869
  function pluginTerraformFiles(pluginDir2) {
11835
- const tfDir = join46(pluginDir2, "terraform");
11870
+ const tfDir = join47(pluginDir2, "terraform");
11836
11871
  let entries;
11837
11872
  try {
11838
11873
  entries = readdirSync21(tfDir);
11839
11874
  } catch {
11840
11875
  return [];
11841
11876
  }
11842
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join46(tfDir, e)).sort();
11877
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join47(tfDir, e)).sort();
11843
11878
  }
11844
11879
  function extractManifestTools(manifestText) {
11845
11880
  let parsed;
@@ -12091,8 +12126,8 @@ function isSnapshotStale(fetchedAt, now) {
12091
12126
  function normalizeModelId(id) {
12092
12127
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
12093
12128
  }
12094
- var CONFIG_PY_PATH = join46("services", "api", "src", "api", "config.py");
12095
- var ORCHESTRATION_SCHEMA_PATH = join46(
12129
+ var CONFIG_PY_PATH = join47("services", "api", "src", "api", "config.py");
12130
+ var ORCHESTRATION_SCHEMA_PATH = join47(
12096
12131
  "services",
12097
12132
  "api",
12098
12133
  "src",
@@ -12104,8 +12139,8 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
12104
12139
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
12105
12140
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
12106
12141
  const now = options.now ?? /* @__PURE__ */ new Date();
12107
- const configPath = join46(repoRoot, CONFIG_PY_PATH);
12108
- const orchestrationPath = join46(repoRoot, ORCHESTRATION_SCHEMA_PATH);
12142
+ const configPath = join47(repoRoot, CONFIG_PY_PATH);
12143
+ const orchestrationPath = join47(repoRoot, ORCHESTRATION_SCHEMA_PATH);
12109
12144
  const configMissing = !existsSync40(configPath);
12110
12145
  const orchestrationSchemaMissing = !existsSync40(orchestrationPath);
12111
12146
  const knownSet = new Set(knownModelIds);
@@ -12178,7 +12213,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
12178
12213
  function discoverPluginDirs(pluginsRoot) {
12179
12214
  return listDirs(pluginsRoot).filter((name) => {
12180
12215
  try {
12181
- return statSync12(join46(pluginsRoot, name, "biffo.plugin.json")).isFile();
12216
+ return statSync12(join47(pluginsRoot, name, "biffo.plugin.json")).isFile();
12182
12217
  } catch {
12183
12218
  return false;
12184
12219
  }
@@ -12191,8 +12226,8 @@ function auditPluginToolSupply(pluginsRoot) {
12191
12226
  let terraformBlind = false;
12192
12227
  let totalDeclaredTools = 0;
12193
12228
  for (const name of pluginNames) {
12194
- const pluginDir2 = join46(pluginsRoot, name);
12195
- const manifestText = readFileSync34(join46(pluginDir2, "biffo.plugin.json"), "utf8");
12229
+ const pluginDir2 = join47(pluginsRoot, name);
12230
+ const manifestText = readFileSync34(join47(pluginDir2, "biffo.plugin.json"), "utf8");
12196
12231
  const manifest = extractManifestTools(manifestText);
12197
12232
  if (manifest.parseError) {
12198
12233
  findings.push({
@@ -12290,7 +12325,7 @@ function auditPluginToolSupply(pluginsRoot) {
12290
12325
  requiredEnvVars: envResult.envVars,
12291
12326
  missingEnvVars: anyWired ? [] : envResult.envVars,
12292
12327
  status: anyWired ? "ok" : "missing-env",
12293
- 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 ${join46(pluginDir2, "terraform")}, so this deployment can never supply it`
12328
+ 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 ${join47(pluginDir2, "terraform")}, so this deployment can never supply it`
12294
12329
  });
12295
12330
  }
12296
12331
  }
@@ -12321,9 +12356,9 @@ function auditPluginToolSupply(pluginsRoot) {
12321
12356
 
12322
12357
  // src/scripts/check-plugin-tool-supply.ts
12323
12358
  async function runPluginToolSupplyCheck() {
12324
- const root = (await execa15("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12359
+ const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12325
12360
  let allOk = true;
12326
- const pluginsRoot = join47(root, "services", "_plugins");
12361
+ const pluginsRoot = join48(root, "services", "_plugins");
12327
12362
  if (!existsSync41(pluginsRoot)) {
12328
12363
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
12329
12364
  } else {
@@ -12354,7 +12389,7 @@ async function runPluginToolSupplyCheck() {
12354
12389
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
12355
12390
  }
12356
12391
  }
12357
- const servicesApiRoot = join47(root, "services", "api");
12392
+ const servicesApiRoot = join48(root, "services", "api");
12358
12393
  if (!existsSync41(servicesApiRoot)) {
12359
12394
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
12360
12395
  } else {
@@ -12401,7 +12436,7 @@ async function runPluginToolSupplyCheck() {
12401
12436
  }
12402
12437
 
12403
12438
  // src/scripts/check-release-subject.ts
12404
- import { execa as execa16 } from "execa";
12439
+ import { execa as execa17 } from "execa";
12405
12440
 
12406
12441
  // src/lib/release-version.ts
12407
12442
  var MINOR_TYPES = /* @__PURE__ */ new Set(["feat"]);
@@ -12438,7 +12473,7 @@ async function fetchPrTitleViaGh({
12438
12473
  PR_NUMBER,
12439
12474
  GH_REPO
12440
12475
  }) {
12441
- const { stdout } = await execa16(
12476
+ const { stdout } = await execa17(
12442
12477
  "gh",
12443
12478
  ["pr", "view", PR_NUMBER, "--repo", GH_REPO, "--json", "title", "--jq", ".title"],
12444
12479
  { env: { ...process.env, GH_TOKEN } }
@@ -12474,7 +12509,7 @@ async function resolveReleaseSubject({
12474
12509
  );
12475
12510
  }
12476
12511
  }
12477
- return (await execa16("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
12512
+ return (await execa17("git", ["log", "-1", "--format=%s"], { cwd })).stdout.trim();
12478
12513
  }
12479
12514
  async function runReleaseSubjectCheck(argv) {
12480
12515
  const base = process.env["GITHUB_BASE_REF"] ?? argv[0];
@@ -12482,9 +12517,9 @@ async function runReleaseSubjectCheck(argv) {
12482
12517
  console.error("No base ref: set GITHUB_BASE_REF or pass a base branch as the first argument.");
12483
12518
  process.exit(2);
12484
12519
  }
12485
- const root = (await execa16("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12486
- await execa16("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
12487
- const { stdout } = await execa16("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
12520
+ const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12521
+ await execa17("git", ["fetch", "--quiet", "origin", base], { cwd: root, reject: false });
12522
+ const { stdout } = await execa17("git", ["diff", "--name-only", `origin/${base}...HEAD`], {
12488
12523
  cwd: root
12489
12524
  });
12490
12525
  const changedFiles = stdout.split("\n").map((s) => s.trim()).filter(Boolean);
@@ -12533,12 +12568,12 @@ async function runReleaseSubjectCheck(argv) {
12533
12568
 
12534
12569
  // src/scripts/check-skeleton-drift.ts
12535
12570
  import { existsSync as existsSync42, readdirSync as readdirSync23 } from "fs";
12536
- import { join as join49 } from "path";
12537
- import { execa as execa17 } from "execa";
12571
+ import { join as join50 } from "path";
12572
+ import { execa as execa18 } from "execa";
12538
12573
 
12539
12574
  // src/lib/skeleton-drift-guard.ts
12540
12575
  import { readFileSync as readFileSync35, readdirSync as readdirSync22, statSync as statSync13 } from "fs";
12541
- import { join as join48 } from "path";
12576
+ import { join as join49 } from "path";
12542
12577
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
12543
12578
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
12544
12579
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -12602,7 +12637,7 @@ function walk(dir, base = dir) {
12602
12637
  }
12603
12638
  for (const entry of entries) {
12604
12639
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
12605
- const abs = join48(dir, entry);
12640
+ const abs = join49(dir, entry);
12606
12641
  let isDir;
12607
12642
  try {
12608
12643
  isDir = statSync13(abs).isDirectory();
@@ -12624,7 +12659,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
12624
12659
  if (!rule.appliesTo(rel)) continue;
12625
12660
  let contents;
12626
12661
  try {
12627
- contents = readFileSync35(join48(skeletonRoot, rel), "utf8");
12662
+ contents = readFileSync35(join49(skeletonRoot, rel), "utf8");
12628
12663
  } catch {
12629
12664
  continue;
12630
12665
  }
@@ -12653,23 +12688,23 @@ function formatViolations2(violations) {
12653
12688
 
12654
12689
  // src/scripts/check-skeleton-drift.ts
12655
12690
  function discoverSkeletons(root) {
12656
- const skeletonsDir = join49(root, "_skeletons");
12691
+ const skeletonsDir = join50(root, "_skeletons");
12657
12692
  let entries;
12658
12693
  try {
12659
12694
  entries = readdirSync23(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
12660
12695
  } catch {
12661
12696
  return [];
12662
12697
  }
12663
- return entries.filter((name) => existsSync42(join49(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
12698
+ return entries.filter((name) => existsSync42(join50(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
12664
12699
  }
12665
12700
  async function runSkeletonDriftCheck() {
12666
- const root = (await execa17("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12701
+ const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12667
12702
  const skeletons = discoverSkeletons(root);
12668
12703
  let filesConsidered = 0;
12669
12704
  for (const name of skeletons) {
12670
- const skeletonRoot = join49(root, "_skeletons", name);
12705
+ const skeletonRoot = join50(root, "_skeletons", name);
12671
12706
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
12672
- if (existsSync42(join49(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
12707
+ if (existsSync42(join50(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
12673
12708
  filesConsidered += 1;
12674
12709
  }
12675
12710
  }
@@ -12683,7 +12718,7 @@ async function runSkeletonDriftCheck() {
12683
12718
  process.exit(1);
12684
12719
  }
12685
12720
  const violations = skeletons.flatMap(
12686
- (name) => auditSkeleton(join49(root, "_skeletons", name), name)
12721
+ (name) => auditSkeleton(join50(root, "_skeletons", name), name)
12687
12722
  );
12688
12723
  if (violations.length > 0) {
12689
12724
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -12695,9 +12730,9 @@ async function runSkeletonDriftCheck() {
12695
12730
  }
12696
12731
 
12697
12732
  // src/scripts/check-terraform-input.ts
12698
- import { execa as execa18 } from "execa";
12733
+ import { execa as execa19 } from "execa";
12699
12734
  async function runTerraformInputCheck() {
12700
- const root = (await execa18("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12735
+ const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12701
12736
  const files = findWorkflowFiles(root);
12702
12737
  console.log(`audited ${files.length} workflow file(s) under ${root}`);
12703
12738
  if (files.length === 0) {
@@ -12802,7 +12837,7 @@ function rawArgsAfter(subcommand) {
12802
12837
 
12803
12838
  // src/commands/doctor.ts
12804
12839
  import { existsSync as existsSync43, readFileSync as readFileSync36 } from "fs";
12805
- import { join as join50, resolve as resolve18 } from "path";
12840
+ import { join as join51, resolve as resolve18 } from "path";
12806
12841
  import chalk21 from "chalk";
12807
12842
  import { Command as Command24 } from "commander";
12808
12843
 
@@ -12977,7 +13012,7 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
12977
13012
  return runDoctorChecks(facts);
12978
13013
  }
12979
13014
  function readLocalCoreVersion(cwd) {
12980
- const path = join50(cwd, INSTANCE_CORE_FILE);
13015
+ const path = join51(cwd, INSTANCE_CORE_FILE);
12981
13016
  if (!existsSync43(path)) return null;
12982
13017
  try {
12983
13018
  return parseCoreRecord(readFileSync36(path, "utf8"));
@@ -12995,7 +13030,7 @@ function parseCoreRecord(contents) {
12995
13030
  }
12996
13031
  }
12997
13032
  function readFossil(cwd) {
12998
- const path = join50(cwd, CORE_VERSION_FILE);
13033
+ const path = join51(cwd, CORE_VERSION_FILE);
12999
13034
  if (!existsSync43(path)) return null;
13000
13035
  try {
13001
13036
  const value = readFileSync36(path, "utf8").trim();
@@ -13442,19 +13477,19 @@ function resolveGithubToken4() {
13442
13477
 
13443
13478
  // src/lib/packaged-script-command.ts
13444
13479
  import { spawnSync } from "child_process";
13445
- import { dirname as dirname11 } from "path";
13480
+ import { dirname as dirname12 } from "path";
13446
13481
  import { fileURLToPath as fileURLToPath6 } from "url";
13447
13482
  import { Command as Command26 } from "commander";
13448
13483
 
13449
13484
  // src/lib/packaged-scripts.ts
13450
13485
  import { existsSync as existsSync44 } from "fs";
13451
- import { dirname as dirname10, join as join51 } from "path";
13486
+ import { dirname as dirname11, join as join52 } from "path";
13452
13487
  function findPackagedScript(startDir, relativePath) {
13453
13488
  let dir = startDir;
13454
13489
  for (; ; ) {
13455
- const candidate = join51(dir, relativePath);
13490
+ const candidate = join52(dir, relativePath);
13456
13491
  if (existsSync44(candidate)) return candidate;
13457
- const parent = dirname10(dir);
13492
+ const parent = dirname11(dir);
13458
13493
  if (parent === dir) return null;
13459
13494
  dir = parent;
13460
13495
  }
@@ -13476,7 +13511,7 @@ function packagedScriptCommand(spec) {
13476
13511
  const command = new Command26(spec.name).description(spec.description).allowExcessArguments(true).allowUnknownOption(true);
13477
13512
  if (spec.argument) command.argument(`<${spec.argument.name}>`, spec.argument.description);
13478
13513
  return command.action(() => {
13479
- const here = dirname11(fileURLToPath6(import.meta.url));
13514
+ const here = dirname12(fileURLToPath6(import.meta.url));
13480
13515
  const script = findPackagedScript(here, spec.script);
13481
13516
  if (!script) {
13482
13517
  process.stderr.write(`${packagedScriptMissing(spec.script)}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.273.6",
3
+ "version": "0.273.8",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",