@biffo/cli 0.315.9 → 0.316.1

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 +483 -315
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9169,10 +9169,10 @@ var RegistryPluginEntrySchema = z8.object({
9169
9169
  ui_components: z8.array(UiComponentEntrySchema).optional(),
9170
9170
  status: z8.enum(["active", "disabled"])
9171
9171
  });
9172
- var PluginRegistrySchema = z8.object({
9172
+ var PluginRegistryEnvelopeSchema = z8.object({
9173
9173
  schema_version: z8.string(),
9174
9174
  last_updated: z8.string(),
9175
- plugins: z8.array(RegistryPluginEntrySchema)
9175
+ plugins: z8.array(z8.unknown())
9176
9176
  });
9177
9177
  var DEFAULT_REGISTRY_URL = "https://raw.githubusercontent.com/keiranholloway/biffo-plugins-registry/main/plugins.json";
9178
9178
  var RegistryAdapter = class {
@@ -9203,14 +9203,31 @@ var RegistryAdapter = class {
9203
9203
  `Plugin registry at ${this.registryUrl} did not return valid JSON: ${err.message}`
9204
9204
  );
9205
9205
  }
9206
- const result = PluginRegistrySchema.safeParse(raw);
9207
- if (!result.success) {
9208
- const messages = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
9206
+ const envelope = PluginRegistryEnvelopeSchema.safeParse(raw);
9207
+ if (!envelope.success) {
9208
+ const messages = envelope.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
9209
9209
  throw new Error(
9210
9210
  `Plugin registry at ${this.registryUrl} has an invalid shape: ${messages.join("; ")}`
9211
9211
  );
9212
9212
  }
9213
- return result.data;
9213
+ const plugins = [];
9214
+ for (const [index, entry] of envelope.data.plugins.entries()) {
9215
+ const entryResult = RegistryPluginEntrySchema.safeParse(entry);
9216
+ if (entryResult.success) {
9217
+ plugins.push(entryResult.data);
9218
+ continue;
9219
+ }
9220
+ const label = entry !== null && typeof entry === "object" && "name" in entry && typeof entry.name === "string" ? `'${entry.name}'` : `at index ${index}`;
9221
+ const messages = entryResult.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`);
9222
+ log.warn(
9223
+ `Plugin registry at ${this.registryUrl} has a malformed entry ${label} \u2014 skipped: ${messages.join("; ")}`
9224
+ );
9225
+ }
9226
+ return {
9227
+ schema_version: envelope.data.schema_version,
9228
+ last_updated: envelope.data.last_updated,
9229
+ plugins
9230
+ };
9214
9231
  }
9215
9232
  /**
9216
9233
  * Resolves `name@minorVersion` (e.g. "rbac", "1.0") against the registry.
@@ -9311,8 +9328,8 @@ function printScopeSeamEntitlement(name) {
9311
9328
  }
9312
9329
 
9313
9330
  // src/commands/plugin-install.ts
9314
- import { cpSync as cpSync5, existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync23, statSync as statSync7, writeFileSync as writeFileSync12 } from "fs";
9315
- import { join as join33, relative as relative4, resolve as resolve12 } from "path";
9331
+ import { cpSync as cpSync5, existsSync as existsSync32, mkdirSync as mkdirSync11, readFileSync as readFileSync24, statSync as statSync7, writeFileSync as writeFileSync13 } from "fs";
9332
+ import { join as join34, relative as relative4, resolve as resolve12 } from "path";
9316
9333
  import chalk15 from "chalk";
9317
9334
  import { Command as Command15 } from "commander";
9318
9335
 
@@ -9409,9 +9426,122 @@ ${lines.join("\n")}
9409
9426
  Supply each with --config <name>=<value> (repeatable) and re-run install. For a 'secret', pass the SSM parameter PATH holding the credential (create the parameter first, e.g. \`aws ssm put-parameter --type SecureString ...\`), never the credential itself.`;
9410
9427
  }
9411
9428
 
9412
- // src/lib/plugin-provenance.ts
9429
+ // src/lib/plugin-frontend-registry.ts
9413
9430
  import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
9414
9431
  import { join as join29 } from "path";
9432
+ var PLUGIN_REGISTRY_RELATIVE_PATH = "apps/frontend/src/lib/plugins.ts";
9433
+ var REGISTRY_START_MARKER = "// BIFFO-PLUGIN-REGISTRY:START \u2014 managed by `biffo plugin install`/`uninstall`. Do not hand-edit.";
9434
+ var REGISTRY_END_MARKER = "// BIFFO-PLUGIN-REGISTRY:END";
9435
+ function titleFromSlug(slug) {
9436
+ return slug.split("-").filter((word) => word.length > 0).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
9437
+ }
9438
+ function frontendUrlForSlug(slug) {
9439
+ return `/api/v1/plugins/${slug}/ui`;
9440
+ }
9441
+ function registryPath2(cwd) {
9442
+ return join29(cwd, PLUGIN_REGISTRY_RELATIVE_PATH);
9443
+ }
9444
+ function missingRegistryError(cwd, pluginSlug) {
9445
+ return new Error(
9446
+ `${PLUGIN_REGISTRY_RELATIVE_PATH} does not exist in ${cwd} \u2014 this sibling checkout has not adopted the dashboard dynamic-route pattern (ADR-0021 \xA72), so plugin "${pluginSlug}"'s \`user_frontend\` block has nowhere to register. Add the file with a managed \`INSTALLED_PLUGINS\` array (see plugin-frontend-registry.ts for the exact marker contract) before installing a user-facing plugin here, or install a plugin with no \`user_frontend\` block instead.`
9447
+ );
9448
+ }
9449
+ function malformedRegistryError(cwd, pluginSlug) {
9450
+ return new Error(
9451
+ `${PLUGIN_REGISTRY_RELATIVE_PATH} in ${cwd} does not carry the managed "${REGISTRY_START_MARKER}" / "${REGISTRY_END_MARKER}" region that plugin "${pluginSlug}"'s install needs to write into. Add that managed region around the INSTALLED_PLUGINS array contents (see plugin-frontend-registry.ts's module docstring) rather than hand-editing the array.`
9452
+ );
9453
+ }
9454
+ function findManagedRegion(source) {
9455
+ const startIdx = source.indexOf(REGISTRY_START_MARKER);
9456
+ if (startIdx === -1) return null;
9457
+ const bodyStart = startIdx + REGISTRY_START_MARKER.length;
9458
+ const endIdx = source.indexOf(REGISTRY_END_MARKER, bodyStart);
9459
+ if (endIdx === -1) return null;
9460
+ return {
9461
+ region: { before: source.slice(0, bodyStart), after: source.slice(endIdx) },
9462
+ body: source.slice(bodyStart, endIdx)
9463
+ };
9464
+ }
9465
+ var SLUG_FIELD_PATTERN = /\bslug\s*:\s*(['"])([a-zA-Z0-9_-]+)\1/;
9466
+ function extractSlug(entryRaw) {
9467
+ const match = SLUG_FIELD_PATTERN.exec(entryRaw);
9468
+ return match ? match[2] : null;
9469
+ }
9470
+ function splitEntries(body) {
9471
+ const entries = [];
9472
+ const n = body.length;
9473
+ let i = 0;
9474
+ while (i < n) {
9475
+ const ch = body[i];
9476
+ if (ch !== "{") {
9477
+ i++;
9478
+ continue;
9479
+ }
9480
+ const start = i + 1;
9481
+ let depth = 1;
9482
+ let j = start;
9483
+ let quote = null;
9484
+ while (j < n && depth > 0) {
9485
+ const c = body[j];
9486
+ if (quote) {
9487
+ if (c === "\\") {
9488
+ j += 2;
9489
+ continue;
9490
+ }
9491
+ if (c === quote) quote = null;
9492
+ } else if (c === '"' || c === "'" || c === "`") {
9493
+ quote = c;
9494
+ } else if (c === "{") {
9495
+ depth++;
9496
+ } else if (c === "}") {
9497
+ depth--;
9498
+ }
9499
+ j++;
9500
+ }
9501
+ const raw = body.slice(start, j - 1);
9502
+ entries.push({ raw, slug: extractSlug(raw) });
9503
+ i = j;
9504
+ }
9505
+ return entries;
9506
+ }
9507
+ function serializeEntryRaw(entry) {
9508
+ return `
9509
+ slug: ${JSON.stringify(entry.slug)},
9510
+ title: ${JSON.stringify(entry.title)},
9511
+ frontendUrl: ${JSON.stringify(entry.frontendUrl)},
9512
+ `;
9513
+ }
9514
+ function assertPluginRegistryReady(cwd, pluginSlug) {
9515
+ readManagedEntries(cwd, pluginSlug);
9516
+ }
9517
+ function readManagedEntries(cwd, pluginSlug) {
9518
+ const path = registryPath2(cwd);
9519
+ if (!existsSync28(path)) throw missingRegistryError(cwd, pluginSlug);
9520
+ const source = readFileSync21(path, "utf8");
9521
+ const found = findManagedRegion(source);
9522
+ if (!found) throw malformedRegistryError(cwd, pluginSlug);
9523
+ return { source, region: found.region, entries: splitEntries(found.body) };
9524
+ }
9525
+ function writeManagedEntries(cwd, region, entries) {
9526
+ const body = entries.length > 0 ? "\n" + entries.map((e) => ` {${e.raw}},
9527
+ `).join("") : "\n";
9528
+ writeFileSync10(registryPath2(cwd), region.before + body + region.after, "utf8");
9529
+ }
9530
+ function upsertPluginRegistryEntry(cwd, entry) {
9531
+ const { region, entries } = readManagedEntries(cwd, entry.slug);
9532
+ const preserved = entries.filter((e) => e.slug !== entry.slug);
9533
+ const next = { raw: serializeEntryRaw(entry), slug: entry.slug };
9534
+ writeManagedEntries(cwd, region, [...preserved, next]);
9535
+ }
9536
+ function removePluginRegistryEntry(cwd, pluginSlug) {
9537
+ const { region, entries } = readManagedEntries(cwd, pluginSlug);
9538
+ const next = entries.filter((e) => e.slug !== pluginSlug);
9539
+ writeManagedEntries(cwd, region, next);
9540
+ }
9541
+
9542
+ // src/lib/plugin-provenance.ts
9543
+ import { existsSync as existsSync29, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "fs";
9544
+ import { join as join30 } from "path";
9415
9545
  var PLUGIN_PROVENANCE_FILENAME = ".biffo-plugin-provenance.json";
9416
9546
  function isPluginProvenance(value) {
9417
9547
  if (typeof value !== "object" || value === null) return false;
@@ -9419,11 +9549,11 @@ function isPluginProvenance(value) {
9419
9549
  return typeof v["origin"] === "string" && (typeof v["ref"] === "string" || v["ref"] === null) && (typeof v["sha"] === "string" || v["sha"] === null) && typeof v["recordedAt"] === "string" && typeof v["inTree"] === "boolean";
9420
9550
  }
9421
9551
  function readProvenance(pluginDir2) {
9422
- const path = join29(pluginDir2, PLUGIN_PROVENANCE_FILENAME);
9423
- if (!existsSync28(path)) return { status: "absent" };
9552
+ const path = join30(pluginDir2, PLUGIN_PROVENANCE_FILENAME);
9553
+ if (!existsSync29(path)) return { status: "absent" };
9424
9554
  let parsed;
9425
9555
  try {
9426
- parsed = JSON.parse(readFileSync21(path, "utf8"));
9556
+ parsed = JSON.parse(readFileSync22(path, "utf8"));
9427
9557
  } catch (err) {
9428
9558
  return {
9429
9559
  status: "invalid",
@@ -9439,7 +9569,7 @@ function readProvenance(pluginDir2) {
9439
9569
  return { status: "present", record: parsed };
9440
9570
  }
9441
9571
  function writePluginProvenance(pluginDir2, record) {
9442
- writeFileSync10(join29(pluginDir2, PLUGIN_PROVENANCE_FILENAME), `${JSON.stringify(record, null, 2)}
9572
+ writeFileSync11(join30(pluginDir2, PLUGIN_PROVENANCE_FILENAME), `${JSON.stringify(record, null, 2)}
9443
9573
  `);
9444
9574
  }
9445
9575
  function reconcileProvenance(previous, next) {
@@ -9489,8 +9619,8 @@ async function tryGit(cwd, args) {
9489
9619
  }
9490
9620
 
9491
9621
  // src/lib/plugin-seed-vendor.ts
9492
- import { cpSync as cpSync3, existsSync as existsSync29, mkdirSync as mkdirSync9, readdirSync as readdirSync12, rmSync as rmSync8 } from "fs";
9493
- import { join as join30 } from "path";
9622
+ import { cpSync as cpSync3, existsSync as existsSync30, mkdirSync as mkdirSync9, readdirSync as readdirSync12, rmSync as rmSync8 } from "fs";
9623
+ import { join as join31 } from "path";
9494
9624
  var VENDOR_PREFIX = "_plugin-";
9495
9625
  function pluginSeedImportDir(pluginName) {
9496
9626
  return `db/imports/${VENDOR_PREFIX}${pluginName}`;
@@ -9499,8 +9629,8 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
9499
9629
  if (!manifest.seed) {
9500
9630
  return { vendored: false };
9501
9631
  }
9502
- const sourceSeedDir = join30(pluginSourceDir, manifest.seed.dir);
9503
- if (!existsSync29(sourceSeedDir)) {
9632
+ const sourceSeedDir = join31(pluginSourceDir, manifest.seed.dir);
9633
+ if (!existsSync30(sourceSeedDir)) {
9504
9634
  throw new Error(
9505
9635
  `${manifest.name}'s manifest declares seed.dir '${manifest.seed.dir}', but ${sourceSeedDir} does not exist in the plugin's source.`
9506
9636
  );
@@ -9512,11 +9642,11 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
9512
9642
  );
9513
9643
  }
9514
9644
  const relTargetDir = pluginSeedImportDir(manifest.name);
9515
- const targetDir = join30(cwd, relTargetDir);
9645
+ const targetDir = join31(cwd, relTargetDir);
9516
9646
  rmSync8(targetDir, { recursive: true, force: true });
9517
9647
  mkdirSync9(targetDir, { recursive: true });
9518
9648
  for (const file of sqlFiles) {
9519
- cpSync3(join30(sourceSeedDir, file), join30(targetDir, file));
9649
+ cpSync3(join31(sourceSeedDir, file), join31(targetDir, file));
9520
9650
  }
9521
9651
  log.success(
9522
9652
  `Vendored ${sqlFiles.length} seed file(s) to ${relTargetDir}/ (baseline_tables: ${manifest.seed.baseline_tables.join(", ") || "none declared"})`
@@ -9529,7 +9659,7 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
9529
9659
 
9530
9660
  // src/lib/plugin-source-copy.ts
9531
9661
  import { copyFileSync as copyFileSync2, cpSync as cpSync4, mkdirSync as mkdirSync10 } from "fs";
9532
- import { basename as basename2, dirname as dirname9, join as join31 } from "path";
9662
+ import { basename as basename2, dirname as dirname9, join as join32 } from "path";
9533
9663
  var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
9534
9664
  ".git",
9535
9665
  ".venv",
@@ -9545,9 +9675,9 @@ async function copyPluginSource(sourceDir, targetDir) {
9545
9675
  if (await isGitWorkingTree2(sourceDir)) {
9546
9676
  const files = await listGitFiles(sourceDir);
9547
9677
  for (const relPath of files) {
9548
- const destPath = join31(targetDir, relPath);
9678
+ const destPath = join32(targetDir, relPath);
9549
9679
  mkdirSync10(dirname9(destPath), { recursive: true });
9550
- copyFileSync2(join31(sourceDir, relPath), destPath);
9680
+ copyFileSync2(join32(sourceDir, relPath), destPath);
9551
9681
  }
9552
9682
  return { usedGitIgnoreRules: true };
9553
9683
  }
@@ -9579,8 +9709,8 @@ async function listGitFiles(dir) {
9579
9709
  }
9580
9710
 
9581
9711
  // src/lib/plugin-workspace-sources.ts
9582
- import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "fs";
9583
- import { join as join32 } from "path";
9712
+ import { existsSync as existsSync31, readdirSync as readdirSync13, readFileSync as readFileSync23, writeFileSync as writeFileSync12 } from "fs";
9713
+ import { join as join33 } from "path";
9584
9714
  function readTomlStringArray(text, key) {
9585
9715
  const open = new RegExp(`^${key}\\s*=\\s*\\[`, "m").exec(text);
9586
9716
  if (!open) return [];
@@ -9624,9 +9754,9 @@ function readDependencyNames(text) {
9624
9754
  return readTomlStringArray(text, "dependencies").map((dep) => /^\s*([A-Za-z0-9._-]+)/.exec(dep)?.[1] ?? "").filter(Boolean);
9625
9755
  }
9626
9756
  function workspaceMemberNames(instanceRoot) {
9627
- const rootPyproject = join32(instanceRoot, "pyproject.toml");
9628
- if (!existsSync30(rootPyproject)) return /* @__PURE__ */ new Set();
9629
- const text = readFileSync22(rootPyproject, "utf8");
9757
+ const rootPyproject = join33(instanceRoot, "pyproject.toml");
9758
+ if (!existsSync31(rootPyproject)) return /* @__PURE__ */ new Set();
9759
+ const text = readFileSync23(rootPyproject, "utf8");
9630
9760
  const members = readTomlStringArray(text, "members");
9631
9761
  const excluded = new Set(readTomlStringArray(text, "exclude"));
9632
9762
  const dirs = [];
@@ -9635,7 +9765,7 @@ function workspaceMemberNames(instanceRoot) {
9635
9765
  const base = member.slice(0, -2);
9636
9766
  let entries;
9637
9767
  try {
9638
- entries = readdirSync13(join32(instanceRoot, base), { withFileTypes: true });
9768
+ entries = readdirSync13(join33(instanceRoot, base), { withFileTypes: true });
9639
9769
  } catch {
9640
9770
  continue;
9641
9771
  }
@@ -9649,9 +9779,9 @@ function workspaceMemberNames(instanceRoot) {
9649
9779
  }
9650
9780
  const names = /* @__PURE__ */ new Set();
9651
9781
  for (const dir of dirs) {
9652
- const pp = join32(instanceRoot, dir, "pyproject.toml");
9653
- if (!existsSync30(pp)) continue;
9654
- const name = readProjectName(readFileSync22(pp, "utf8"));
9782
+ const pp = join33(instanceRoot, dir, "pyproject.toml");
9783
+ if (!existsSync31(pp)) continue;
9784
+ const name = readProjectName(readFileSync23(pp, "utf8"));
9655
9785
  if (name) names.add(name);
9656
9786
  }
9657
9787
  return names;
@@ -9662,8 +9792,8 @@ function existingWorkspaceSources(text) {
9662
9792
  );
9663
9793
  }
9664
9794
  function ensureWorkspaceSources(pluginPyprojectPath, memberNames) {
9665
- if (!existsSync30(pluginPyprojectPath) || memberNames.size === 0) return [];
9666
- const text = readFileSync22(pluginPyprojectPath, "utf8");
9795
+ if (!existsSync31(pluginPyprojectPath) || memberNames.size === 0) return [];
9796
+ const text = readFileSync23(pluginPyprojectPath, "utf8");
9667
9797
  const already = existingWorkspaceSources(text);
9668
9798
  const toAdd = readDependencyNames(text).filter((n) => memberNames.has(n) && !already.has(n));
9669
9799
  if (toAdd.length === 0) return [];
@@ -9683,12 +9813,12 @@ ${lines.join("\n")}${text.slice(insertAt)}`;
9683
9813
  ${lines.join("\n")}
9684
9814
  `;
9685
9815
  }
9686
- writeFileSync11(pluginPyprojectPath, updated);
9816
+ writeFileSync12(pluginPyprojectPath, updated);
9687
9817
  return toAdd;
9688
9818
  }
9689
9819
  function applyWorkspaceSources(targetDir, cwd, relTargetDir) {
9690
- const pluginPyproject = join32(targetDir, "pyproject.toml");
9691
- if (!existsSync30(pluginPyproject)) return;
9820
+ const pluginPyproject = join33(targetDir, "pyproject.toml");
9821
+ if (!existsSync31(pluginPyproject)) return;
9692
9822
  const sourced = ensureWorkspaceSources(pluginPyproject, workspaceMemberNames(cwd));
9693
9823
  if (sourced.length > 0) {
9694
9824
  log.info(
@@ -9734,14 +9864,14 @@ var pluginInstallCommand = new Command15("install").description(
9734
9864
  }
9735
9865
  );
9736
9866
  function resolveLocalPlugin(localPath) {
9737
- if (!existsSync31(localPath)) {
9867
+ if (!existsSync32(localPath)) {
9738
9868
  throw new Error(`--local path does not exist: ${localPath}`);
9739
9869
  }
9740
9870
  if (!statSync7(localPath).isDirectory()) {
9741
9871
  throw new Error(`--local path is not a directory: ${localPath}`);
9742
9872
  }
9743
- const manifestPath = join33(localPath, "biffo.plugin.json");
9744
- if (!existsSync31(manifestPath)) {
9873
+ const manifestPath = join34(localPath, "biffo.plugin.json");
9874
+ if (!existsSync32(manifestPath)) {
9745
9875
  throw new Error(
9746
9876
  `${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>\`.)`
9747
9877
  );
@@ -9767,8 +9897,8 @@ function parsePluginTarget(target) {
9767
9897
  async function cloneAndValidatePlugin(entry, git) {
9768
9898
  const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
9769
9899
  try {
9770
- const manifestPath = join33(tmpDir, "biffo.plugin.json");
9771
- if (!existsSync31(manifestPath)) {
9900
+ const manifestPath = join34(tmpDir, "biffo.plugin.json");
9901
+ if (!existsSync32(manifestPath)) {
9772
9902
  throw new Error(
9773
9903
  `Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
9774
9904
  );
@@ -9796,8 +9926,8 @@ async function runPluginInstall(target, options, deps) {
9796
9926
  `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\`).`
9797
9927
  );
9798
9928
  }
9799
- const servicesDir = join33(options.cwd, "services");
9800
- if (!existsSync31(servicesDir)) {
9929
+ const servicesDir = join34(options.cwd, "services");
9930
+ if (!existsSync32(servicesDir)) {
9801
9931
  throw new Error(
9802
9932
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
9803
9933
  );
@@ -9815,10 +9945,10 @@ async function runPluginInstall(target, options, deps) {
9815
9945
  }
9816
9946
  const pluginName = entry ? entry.name : source.name;
9817
9947
  const relTargetDir = pluginDir(pluginName, "third-party");
9818
- const targetDir = join33(options.cwd, relTargetDir);
9819
- const modulesDir = join33(options.cwd, "modules", "plugins", pluginName);
9948
+ const targetDir = join34(options.cwd, relTargetDir);
9949
+ const modulesDir = join34(options.cwd, "modules", "plugins", pluginName);
9820
9950
  const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
9821
- if (existsSync31(targetDir) && !inTreeSource) {
9951
+ if (existsSync32(targetDir) && !inTreeSource) {
9822
9952
  throw new Error(
9823
9953
  `Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
9824
9954
  );
@@ -9850,7 +9980,7 @@ async function runPluginInstall(target, options, deps) {
9850
9980
  log.success(
9851
9981
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9852
9982
  );
9853
- const retiredShapeReasons = findRetiredFrontendShape(join33(source.sourceDir, "terraform"));
9983
+ const retiredShapeReasons = findRetiredFrontendShape(join34(source.sourceDir, "terraform"));
9854
9984
  if (retiredShapeReasons.length > 0) {
9855
9985
  throw new Error(retiredFrontendShapeError(pluginName, retiredShapeReasons));
9856
9986
  }
@@ -9862,6 +9992,9 @@ async function runPluginInstall(target, options, deps) {
9862
9992
  if (configSupply.missingRequired.length > 0) {
9863
9993
  throw new Error(missingRequiredConfigMessage(pluginName, configSupply.missingRequired));
9864
9994
  }
9995
+ if (manifest.user_frontend) {
9996
+ assertPluginRegistryReady(options.cwd, pluginName);
9997
+ }
9865
9998
  if (inTreeSource) {
9866
9999
  log.info(`${relTargetDir}/ is already in this checkout \u2014 installing in place.`);
9867
10000
  } else {
@@ -9874,8 +10007,8 @@ async function runPluginInstall(target, options, deps) {
9874
10007
  writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
9875
10008
  applyWorkspaceSources(targetDir, options.cwd, relTargetDir);
9876
10009
  const stagePaths = [relTargetDir];
9877
- const tfSourceDir = join33(targetDir, "terraform");
9878
- if (existsSync31(tfSourceDir)) {
10010
+ const tfSourceDir = join34(targetDir, "terraform");
10011
+ if (existsSync32(tfSourceDir)) {
9879
10012
  mkdirSync11(modulesDir, { recursive: true });
9880
10013
  cpSync5(tfSourceDir, modulesDir, { recursive: true });
9881
10014
  stagePaths.push(`modules/plugins/${pluginName}`);
@@ -9921,8 +10054,8 @@ async function runPluginInstall(target, options, deps) {
9921
10054
  stagePaths.push(seedResult.stagedPath);
9922
10055
  }
9923
10056
  if (manifest.config.length > 0) {
9924
- const configFilePath = join33(targetDir, "biffo.plugin-config.json");
9925
- writeFileSync12(
10057
+ const configFilePath = join34(targetDir, "biffo.plugin-config.json");
10058
+ writeFileSync13(
9926
10059
  configFilePath,
9927
10060
  JSON.stringify(
9928
10061
  {
@@ -9945,6 +10078,15 @@ async function runPluginInstall(target, options, deps) {
9945
10078
  `Recorded ${configSupply.resolved.length}/${manifest.config.length} declared config value(s) at ${relative4(options.cwd, configFilePath)}`
9946
10079
  );
9947
10080
  }
10081
+ if (manifest.user_frontend) {
10082
+ upsertPluginRegistryEntry(options.cwd, {
10083
+ slug: pluginName,
10084
+ title: titleFromSlug(pluginName),
10085
+ frontendUrl: frontendUrlForSlug(pluginName)
10086
+ });
10087
+ stagePaths.push(PLUGIN_REGISTRY_RELATIVE_PATH);
10088
+ log.success(`Registered ${pluginName} in ${PLUGIN_REGISTRY_RELATIVE_PATH}`);
10089
+ }
9948
10090
  const commitMessage = `feat(plugins): install ${pluginName}@${source.version}`;
9949
10091
  await deps.git.add(options.cwd, stagePaths);
9950
10092
  await deps.git.commit(options.cwd, commitMessage);
@@ -9979,7 +10121,7 @@ function printConfigWiringInstructions(pluginName, resolved) {
9979
10121
  }
9980
10122
  function parseManifestFile(path) {
9981
10123
  try {
9982
- return JSON.parse(readFileSync23(path, "utf8"));
10124
+ return JSON.parse(readFileSync24(path, "utf8"));
9983
10125
  } catch (err) {
9984
10126
  throw new Error(`Could not parse ${path} as JSON: ${err.message}`);
9985
10127
  }
@@ -10033,8 +10175,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource, suppliedConfig
10033
10175
  }
10034
10176
 
10035
10177
  // src/commands/plugin-list.ts
10036
- import { existsSync as existsSync32, readFileSync as readFileSync24 } from "fs";
10037
- import { join as join34, resolve as resolve13 } from "path";
10178
+ import { existsSync as existsSync33, readFileSync as readFileSync25 } from "fs";
10179
+ import { join as join35, resolve as resolve13 } from "path";
10038
10180
  import chalk16 from "chalk";
10039
10181
  import { Command as Command16 } from "commander";
10040
10182
  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) => {
@@ -10047,8 +10189,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
10047
10189
  }
10048
10190
  });
10049
10191
  async function runPluginList(options) {
10050
- const servicesDir = join34(options.cwd, "services");
10051
- if (!existsSync32(servicesDir)) {
10192
+ const servicesDir = join35(options.cwd, "services");
10193
+ if (!existsSync33(servicesDir)) {
10052
10194
  throw new Error(
10053
10195
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
10054
10196
  );
@@ -10056,7 +10198,7 @@ async function runPluginList(options) {
10056
10198
  const plugins = [];
10057
10199
  for (const location of findInstalledPlugins(options.cwd)) {
10058
10200
  try {
10059
- const manifest = validateManifest(JSON.parse(readFileSync24(location.manifestPath, "utf8")));
10201
+ const manifest = validateManifest(JSON.parse(readFileSync25(location.manifestPath, "utf8")));
10060
10202
  plugins.push({
10061
10203
  name: manifest.name,
10062
10204
  version: manifest.version,
@@ -10097,30 +10239,35 @@ import { resolve as resolve14 } from "path";
10097
10239
  import { Command as Command17 } from "commander";
10098
10240
 
10099
10241
  // src/lib/plugin-staleness.ts
10100
- import { existsSync as existsSync33, readFileSync as readFileSync25, readdirSync as readdirSync14, statSync as statSync8 } from "fs";
10101
- import { join as join35, relative as relative5 } from "path";
10242
+ import { existsSync as existsSync34, readFileSync as readFileSync26, readdirSync as readdirSync14, statSync as statSync8 } from "fs";
10243
+ import { join as join36, relative as relative5 } from "path";
10102
10244
  function discoverVendoredPlugins(servicesDir) {
10103
- if (!existsSync33(servicesDir)) return [];
10104
- return readdirSync14(servicesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_") && e.name !== "api").map((e) => e.name).filter((name) => existsSync33(join35(servicesDir, name, "biffo.plugin.json"))).sort();
10245
+ if (!existsSync34(servicesDir)) return [];
10246
+ return readdirSync14(servicesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_") && e.name !== "api").map((e) => e.name).filter((name) => existsSync34(join36(servicesDir, name, "biffo.plugin.json"))).sort();
10105
10247
  }
10106
10248
  async function checkPluginStaleness(cwd, deps) {
10107
- const servicesDir = join35(cwd, "services");
10249
+ const servicesDir = join36(cwd, "services");
10108
10250
  const names = discoverVendoredPlugins(servicesDir);
10109
10251
  let registryRepoByName = null;
10252
+ let registryFetchFailure = null;
10110
10253
  const resolveRegistryRepo = async (name) => {
10111
- if (registryRepoByName === null) {
10112
- registryRepoByName = /* @__PURE__ */ new Map();
10254
+ if (registryRepoByName === null && registryFetchFailure === null) {
10113
10255
  try {
10114
10256
  const reg = await deps.registry.fetchRegistry();
10257
+ registryRepoByName = /* @__PURE__ */ new Map();
10115
10258
  for (const entry of reg.plugins) registryRepoByName.set(entry.name, entry.repo);
10116
- } catch {
10259
+ } catch (err) {
10260
+ registryFetchFailure = err.message;
10117
10261
  }
10118
10262
  }
10119
- return registryRepoByName.get(name) ?? null;
10263
+ if (registryFetchFailure !== null) {
10264
+ return { repo: null, fetchFailure: registryFetchFailure };
10265
+ }
10266
+ return { repo: registryRepoByName?.get(name) ?? null, fetchFailure: null };
10120
10267
  };
10121
10268
  const results = [];
10122
10269
  for (const name of names) {
10123
- results.push(await checkOnePlugin(join35(servicesDir, name), name, resolveRegistryRepo, deps.git));
10270
+ results.push(await checkOnePlugin(join36(servicesDir, name), name, resolveRegistryRepo, deps.git));
10124
10271
  }
10125
10272
  return results;
10126
10273
  }
@@ -10146,19 +10293,28 @@ async function checkOnePlugin(pluginDir2, name, resolveRegistryRepo, git) {
10146
10293
  if (record?.sha && isFetchableUrl(record.origin)) {
10147
10294
  return checkViaProvenance(name, record, record.origin, git);
10148
10295
  }
10149
- const localOrigin = record && !isFetchableUrl(record.origin) && existsSync33(record.origin) ? record.origin : null;
10296
+ const localOrigin = record && !isFetchableUrl(record.origin) && existsSync34(record.origin) ? record.origin : null;
10150
10297
  if (localOrigin) {
10151
10298
  return checkViaContentDiff(name, pluginDir2, localOrigin, { isLocalDir: true }, git);
10152
10299
  }
10153
- const registryRepo = await resolveRegistryRepo(name);
10154
- if (!registryRepo) {
10300
+ const lookup = await resolveRegistryRepo(name);
10301
+ if (!lookup.repo) {
10302
+ if (lookup.fetchFailure) {
10303
+ return {
10304
+ name,
10305
+ status: "cannot-tell",
10306
+ method: "unresolvable",
10307
+ detail: record ? `provenance records origin '${record.origin}', which is neither a reachable git URL nor a local directory that still exists, and the plugin registry could not be fetched to resolve '${name}' by name either: ${lookup.fetchFailure}` : `no provenance recorded (vendored before #1547) and the plugin registry could not be fetched to resolve '${name}' by name: ${lookup.fetchFailure}`
10308
+ };
10309
+ }
10155
10310
  return {
10156
10311
  name,
10157
10312
  status: "cannot-tell",
10158
10313
  method: "unresolvable",
10159
- detail: record ? `provenance records origin '${record.origin}', which is neither a reachable git URL nor a local directory that still exists, and '${name}' was not found in the plugin registry either` : `no provenance recorded (vendored before #1547, or an unreachable registry) and '${name}' was not found in the plugin registry \u2014 nothing to compare against`
10314
+ detail: record ? `provenance records origin '${record.origin}', which is neither a reachable git URL nor a local directory that still exists, and '${name}' was not found in the plugin registry either` : `no provenance recorded (vendored before #1547) and '${name}' was not found in the plugin registry \u2014 nothing to compare against`
10160
10315
  };
10161
10316
  }
10317
+ const registryRepo = lookup.repo;
10162
10318
  if (record?.sha) {
10163
10319
  return checkViaProvenance(name, record, registryRepo, git);
10164
10320
  }
@@ -10280,8 +10436,8 @@ async function countDifferingFiles(sourceDir, pluginDir2) {
10280
10436
  differing++;
10281
10437
  continue;
10282
10438
  }
10283
- const a = readFileSync25(join35(sourceDir, relPath));
10284
- const b = readFileSync25(join35(pluginDir2, relPath));
10439
+ const a = readFileSync26(join36(sourceDir, relPath));
10440
+ const b = readFileSync26(join36(pluginDir2, relPath));
10285
10441
  if (!a.equals(b)) differing++;
10286
10442
  }
10287
10443
  return differing;
@@ -10296,11 +10452,11 @@ function vendorFileList(dir) {
10296
10452
  return new Set(walkExcluding(dir, dir, LOCAL_COPY_EXCLUDES));
10297
10453
  }
10298
10454
  function walkExcluding(root, dir, excludes) {
10299
- if (!existsSync33(dir)) return [];
10455
+ if (!existsSync34(dir)) return [];
10300
10456
  const out = [];
10301
10457
  for (const entry of readdirSync14(dir)) {
10302
10458
  if (excludes.has(entry) || entry === ".git") continue;
10303
- const full = join35(dir, entry);
10459
+ const full = join36(dir, entry);
10304
10460
  let stat;
10305
10461
  try {
10306
10462
  stat = statSync8(full);
@@ -10357,8 +10513,8 @@ var pluginStalenessCommand = new Command17("staleness").description(
10357
10513
  });
10358
10514
 
10359
10515
  // src/commands/plugin-sync-migrations.ts
10360
- import { existsSync as existsSync34 } from "fs";
10361
- import { join as join36, relative as relative6, resolve as resolve15 } from "path";
10516
+ import { existsSync as existsSync35 } from "fs";
10517
+ import { join as join37, relative as relative6, resolve as resolve15 } from "path";
10362
10518
  import chalk17 from "chalk";
10363
10519
  import { Command as Command18 } from "commander";
10364
10520
  var pluginSyncMigrationsCommand = new Command18("sync-migrations").description(
@@ -10379,11 +10535,11 @@ var pluginSyncMigrationsCommand = new Command18("sync-migrations").description(
10379
10535
  }
10380
10536
  );
10381
10537
  async function runPluginSyncMigrations(name, options, deps) {
10382
- const servicesDir = join36(options.cwd, "services");
10383
- if (!existsSync34(servicesDir)) {
10538
+ const servicesDir = join37(options.cwd, "services");
10539
+ if (!existsSync35(servicesDir)) {
10384
10540
  throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
10385
10541
  }
10386
- if (name && !existsSync34(join36(servicesDir, name, "biffo.plugin.json"))) {
10542
+ if (name && !existsSync35(join37(servicesDir, name, "biffo.plugin.json"))) {
10387
10543
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
10388
10544
  }
10389
10545
  if (options.dryRun) {
@@ -10419,8 +10575,8 @@ async function runPluginSyncMigrations(name, options, deps) {
10419
10575
  }
10420
10576
 
10421
10577
  // src/commands/plugin-uninstall.ts
10422
- import { existsSync as existsSync35, readFileSync as readFileSync26, rmSync as rmSync9 } from "fs";
10423
- import { join as join37, resolve as resolve16 } from "path";
10578
+ import { existsSync as existsSync36, readFileSync as readFileSync27, rmSync as rmSync9 } from "fs";
10579
+ import { join as join38, resolve as resolve16 } from "path";
10424
10580
  import chalk18 from "chalk";
10425
10581
  import { Command as Command19 } from "commander";
10426
10582
  import inquirer6 from "inquirer";
@@ -10452,28 +10608,33 @@ async function runPluginUninstall(name, options, deps) {
10452
10608
  if (!NAME_PATTERN2.test(name)) {
10453
10609
  throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
10454
10610
  }
10455
- const servicesDir = join37(options.cwd, "services");
10456
- if (!existsSync35(servicesDir)) {
10611
+ const servicesDir = join38(options.cwd, "services");
10612
+ if (!existsSync36(servicesDir)) {
10457
10613
  throw new Error(
10458
10614
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
10459
10615
  );
10460
10616
  }
10461
- const targetDir = join37(servicesDir, name);
10462
- if (!existsSync35(targetDir)) {
10463
- const firstParty = join37(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
10464
- if (existsSync35(firstParty)) {
10617
+ const targetDir = join38(servicesDir, name);
10618
+ if (!existsSync36(targetDir)) {
10619
+ const firstParty = join38(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
10620
+ if (existsSync36(firstParty)) {
10465
10621
  throw new Error(
10466
10622
  `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.`
10467
10623
  );
10468
10624
  }
10469
10625
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
10470
10626
  }
10471
- const version = readInstalledVersion(targetDir);
10472
- const modulesDir = join37(options.cwd, "modules", "plugins", name);
10627
+ const installedManifest = readInstalledManifest(targetDir);
10628
+ const version = installedManifest?.version;
10629
+ const modulesDir = join38(options.cwd, "modules", "plugins", name);
10473
10630
  const stagePaths = [`services/${name}`];
10474
- if (existsSync35(modulesDir)) {
10631
+ if (existsSync36(modulesDir)) {
10475
10632
  stagePaths.push(`modules/plugins/${name}`);
10476
10633
  }
10634
+ const hasUserFrontend = installedManifest?.user_frontend !== void 0;
10635
+ if (hasUserFrontend) {
10636
+ stagePaths.push(PLUGIN_REGISTRY_RELATIVE_PATH);
10637
+ }
10477
10638
  if (options.dryRun) {
10478
10639
  printDryRun5(name, version, stagePaths, options.keepData);
10479
10640
  return;
@@ -10491,7 +10652,10 @@ async function runPluginUninstall(name, options, deps) {
10491
10652
  `${options.cwd} is not a git repository \u2014 biffo plugin uninstall must be run from a Biffo project checkout.`
10492
10653
  );
10493
10654
  }
10494
- if (existsSync35(modulesDir)) {
10655
+ if (hasUserFrontend) {
10656
+ assertPluginRegistryReady(options.cwd, name);
10657
+ }
10658
+ if (existsSync36(modulesDir)) {
10495
10659
  const refs = findPluginModuleReferences(options.cwd, name).filter(
10496
10660
  (r) => !r.file.endsWith(`/${GENERATED_TF_FILE}`) && r.file !== GENERATED_TF_FILE
10497
10661
  );
@@ -10506,7 +10670,7 @@ Remove the reference(s) above first, then re-run uninstall.`
10506
10670
  }
10507
10671
  rmSync9(targetDir, { recursive: true, force: true });
10508
10672
  log.success(`Removed services/${name}/`);
10509
- if (existsSync35(modulesDir)) {
10673
+ if (existsSync36(modulesDir)) {
10510
10674
  rmSync9(modulesDir, { recursive: true, force: true });
10511
10675
  log.success(`Removed modules/plugins/${name}/`);
10512
10676
  const wiring = syncPluginTerraform(options.cwd);
@@ -10517,6 +10681,10 @@ Remove the reference(s) above first, then re-run uninstall.`
10517
10681
  );
10518
10682
  }
10519
10683
  }
10684
+ if (hasUserFrontend) {
10685
+ removePluginRegistryEntry(options.cwd, name);
10686
+ log.success(`Removed ${name} from ${PLUGIN_REGISTRY_RELATIVE_PATH}`);
10687
+ }
10520
10688
  const label = version ? `${name}@${version}` : name;
10521
10689
  const commitMessage = `chore(plugins): uninstall ${label}`;
10522
10690
  await deps.git.add(options.cwd, stagePaths);
@@ -10541,17 +10709,17 @@ Remove the reference(s) above first, then re-run uninstall.`
10541
10709
  "Any tables this plugin created remain in the database, and its migration file at services/api/migrations/versions/ is NOT removed (it is a permanent historical record \u2014 see notes). Dropping tables, if desired, requires a manual Alembic migration written against the Core API."
10542
10710
  );
10543
10711
  }
10544
- if (existsSync35(join37(options.cwd, pluginSeedImportDir(name)))) {
10712
+ if (existsSync36(join38(options.cwd, pluginSeedImportDir(name)))) {
10545
10713
  log.warn(
10546
10714
  `${pluginSeedImportDir(name)}/ (this plugin's vendored baseline-row seed, biffo-template#1554) was NOT removed either, for the same reason \u2014 see notes. Delete it by hand if you are certain the rows it applied should go too, but note nothing drops rows already applied to the database; that still needs a manual migration.`
10547
10715
  );
10548
10716
  }
10549
10717
  }
10550
- function readInstalledVersion(targetDir) {
10551
- const manifestPath = join37(targetDir, "biffo.plugin.json");
10552
- if (!existsSync35(manifestPath)) return void 0;
10718
+ function readInstalledManifest(targetDir) {
10719
+ const manifestPath = join38(targetDir, "biffo.plugin.json");
10720
+ if (!existsSync36(manifestPath)) return void 0;
10553
10721
  try {
10554
- return validateManifest(JSON.parse(readFileSync26(manifestPath, "utf8"))).version;
10722
+ return validateManifest(JSON.parse(readFileSync27(manifestPath, "utf8")));
10555
10723
  } catch {
10556
10724
  return void 0;
10557
10725
  }
@@ -10584,8 +10752,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
10584
10752
  }
10585
10753
 
10586
10754
  // src/commands/plugin-upgrade.ts
10587
- import { cpSync as cpSync6, existsSync as existsSync36, mkdirSync as mkdirSync12, readFileSync as readFileSync27, rmSync as rmSync10 } from "fs";
10588
- import { join as join38, relative as relative7, resolve as resolve17 } from "path";
10755
+ import { cpSync as cpSync6, existsSync as existsSync37, mkdirSync as mkdirSync12, readFileSync as readFileSync28, rmSync as rmSync10 } from "fs";
10756
+ import { join as join39, relative as relative7, resolve as resolve17 } from "path";
10589
10757
  import chalk19 from "chalk";
10590
10758
  import { Command as Command20 } from "commander";
10591
10759
  import inquirer7 from "inquirer";
@@ -10632,8 +10800,8 @@ async function runPluginUpgrade(target, options, deps) {
10632
10800
  `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\`).`
10633
10801
  );
10634
10802
  }
10635
- const servicesDir = join38(options.cwd, "services");
10636
- if (!existsSync36(servicesDir)) {
10803
+ const servicesDir = join39(options.cwd, "services");
10804
+ if (!existsSync37(servicesDir)) {
10637
10805
  throw new Error(
10638
10806
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
10639
10807
  );
@@ -10642,13 +10810,13 @@ async function runPluginUpgrade(target, options, deps) {
10642
10810
  return runLocalPluginRefresh(options.local, options, deps);
10643
10811
  }
10644
10812
  const { name, minor } = parsePluginTarget(target);
10645
- const targetDir = join38(servicesDir, name);
10646
- if (!existsSync36(targetDir)) {
10813
+ const targetDir = join39(servicesDir, name);
10814
+ if (!existsSync37(targetDir)) {
10647
10815
  throw new Error(
10648
10816
  `Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
10649
10817
  );
10650
10818
  }
10651
- const currentVersion = readInstalledVersion2(targetDir);
10819
+ const currentVersion = readInstalledVersion(targetDir);
10652
10820
  log.info(`Resolving ${name}@${minor} from the plugin registry...`);
10653
10821
  const entry = await deps.registry.resolvePlugin(name, minor);
10654
10822
  log.success(`Resolved ${entry.name}@${entry.version} \u2014 ${entry.repo}`);
@@ -10657,7 +10825,7 @@ async function runPluginUpgrade(target, options, deps) {
10657
10825
  `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.`
10658
10826
  );
10659
10827
  }
10660
- const modulesDir = join38(options.cwd, "modules", "plugins", entry.name);
10828
+ const modulesDir = join39(options.cwd, "modules", "plugins", entry.name);
10661
10829
  if (options.dryRun) {
10662
10830
  printDryRun6(entry, currentVersion);
10663
10831
  return;
@@ -10685,11 +10853,11 @@ async function runPluginUpgrade(target, options, deps) {
10685
10853
  log.success(
10686
10854
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
10687
10855
  );
10688
- const retiredShapeReasons = findRetiredFrontendShape(join38(tmpDir, "terraform"));
10856
+ const retiredShapeReasons = findRetiredFrontendShape(join39(tmpDir, "terraform"));
10689
10857
  if (retiredShapeReasons.length > 0) {
10690
10858
  throw new Error(retiredFrontendShapeError(entry.name, retiredShapeReasons));
10691
10859
  }
10692
- if (!existsSync36(join38(tmpDir, "terraform"))) {
10860
+ if (!existsSync37(join39(tmpDir, "terraform"))) {
10693
10861
  refuseIfModuleStillReferenced(options.cwd, modulesDir, entry.name);
10694
10862
  }
10695
10863
  const previousProvenance = readProvenance(targetDir);
@@ -10706,11 +10874,11 @@ async function runPluginUpgrade(target, options, deps) {
10706
10874
  applyWorkspaceSources(targetDir, options.cwd, `services/${entry.name}`);
10707
10875
  const newPyproject = readPyprojectIfPresent(targetDir);
10708
10876
  const stagePaths = [`services/${entry.name}`];
10709
- if (existsSync36(modulesDir)) {
10877
+ if (existsSync37(modulesDir)) {
10710
10878
  rmSync10(modulesDir, { recursive: true, force: true });
10711
10879
  }
10712
- const tfSourceDir = join38(targetDir, "terraform");
10713
- if (existsSync36(tfSourceDir)) {
10880
+ const tfSourceDir = join39(targetDir, "terraform");
10881
+ if (existsSync37(tfSourceDir)) {
10714
10882
  mkdirSync12(modulesDir, { recursive: true });
10715
10883
  cpSync6(tfSourceDir, modulesDir, { recursive: true });
10716
10884
  stagePaths.push(`modules/plugins/${entry.name}`);
@@ -10771,16 +10939,16 @@ async function runPluginUpgrade(target, options, deps) {
10771
10939
  async function runLocalPluginRefresh(localPath, options, deps) {
10772
10940
  const source = resolveLocalPlugin(localPath);
10773
10941
  log.success(`Resolved ${source.name}@${source.version} from ${source.origin}`);
10774
- const servicesDir = join38(options.cwd, "services");
10775
- const targetDir = join38(servicesDir, source.name);
10776
- if (!existsSync36(targetDir)) {
10942
+ const servicesDir = join39(options.cwd, "services");
10943
+ const targetDir = join39(servicesDir, source.name);
10944
+ if (!existsSync37(targetDir)) {
10777
10945
  throw new Error(
10778
10946
  `Plugin '${source.name}' is not installed at services/${source.name}/. Use 'biffo plugin install --local ${localPath}' instead.`
10779
10947
  );
10780
10948
  }
10781
10949
  const inTreeSource = resolve17(source.sourceDir) === resolve17(targetDir);
10782
- const currentVersion = readInstalledVersion2(targetDir);
10783
- const modulesDir = join38(options.cwd, "modules", "plugins", source.name);
10950
+ const currentVersion = readInstalledVersion(targetDir);
10951
+ const modulesDir = join39(options.cwd, "modules", "plugins", source.name);
10784
10952
  if (options.dryRun) {
10785
10953
  printLocalDryRun(source, currentVersion, inTreeSource);
10786
10954
  return;
@@ -10803,11 +10971,11 @@ async function runLocalPluginRefresh(localPath, options, deps) {
10803
10971
  log.success(
10804
10972
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
10805
10973
  );
10806
- const retiredShapeReasons = findRetiredFrontendShape(join38(source.sourceDir, "terraform"));
10974
+ const retiredShapeReasons = findRetiredFrontendShape(join39(source.sourceDir, "terraform"));
10807
10975
  if (retiredShapeReasons.length > 0) {
10808
10976
  throw new Error(retiredFrontendShapeError(source.name, retiredShapeReasons));
10809
10977
  }
10810
- if (!existsSync36(join38(source.sourceDir, "terraform"))) {
10978
+ if (!existsSync37(join39(source.sourceDir, "terraform"))) {
10811
10979
  refuseIfModuleStillReferenced(options.cwd, modulesDir, source.name);
10812
10980
  }
10813
10981
  const previousProvenance = readProvenance(targetDir);
@@ -10827,11 +10995,11 @@ async function runLocalPluginRefresh(localPath, options, deps) {
10827
10995
  applyWorkspaceSources(targetDir, options.cwd, `services/${source.name}`);
10828
10996
  const newPyproject = readPyprojectIfPresent(targetDir);
10829
10997
  const stagePaths = [`services/${source.name}`];
10830
- if (existsSync36(modulesDir)) {
10998
+ if (existsSync37(modulesDir)) {
10831
10999
  rmSync10(modulesDir, { recursive: true, force: true });
10832
11000
  }
10833
- const tfSourceDir = join38(targetDir, "terraform");
10834
- if (existsSync36(tfSourceDir)) {
11001
+ const tfSourceDir = join39(targetDir, "terraform");
11002
+ if (existsSync37(tfSourceDir)) {
10835
11003
  mkdirSync12(modulesDir, { recursive: true });
10836
11004
  cpSync6(tfSourceDir, modulesDir, { recursive: true });
10837
11005
  stagePaths.push(`modules/plugins/${source.name}`);
@@ -10895,7 +11063,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
10895
11063
  }
10896
11064
  }
10897
11065
  function refuseIfModuleStillReferenced(cwd, modulesDir, name) {
10898
- if (!existsSync36(modulesDir)) return;
11066
+ if (!existsSync37(modulesDir)) return;
10899
11067
  const refs = findPluginModuleReferences(cwd, name);
10900
11068
  if (refs.length === 0) return;
10901
11069
  const refList = refs.map((r) => ` ${r.file}:${r.line} ${r.text}`).join("\n");
@@ -10956,8 +11124,8 @@ var defaultRunCommand2 = async (command, cwd) => {
10956
11124
  }
10957
11125
  };
10958
11126
  function readPyprojectIfPresent(targetDir) {
10959
- const path = join38(targetDir, "pyproject.toml");
10960
- return existsSync36(path) ? readFileSync27(path, "utf8") : null;
11127
+ const path = join39(targetDir, "pyproject.toml");
11128
+ return existsSync37(path) ? readFileSync28(path, "utf8") : null;
10961
11129
  }
10962
11130
  function dependenciesChanged(before, after) {
10963
11131
  if (before === after) return false;
@@ -10984,11 +11152,11 @@ function tomlTableBody(text, header) {
10984
11152
  const nextHeader = /^\[/m.exec(rest);
10985
11153
  return nextHeader ? rest.slice(0, nextHeader.index) : rest;
10986
11154
  }
10987
- function readInstalledVersion2(targetDir) {
10988
- const manifestPath = join38(targetDir, "biffo.plugin.json");
10989
- if (!existsSync36(manifestPath)) return void 0;
11155
+ function readInstalledVersion(targetDir) {
11156
+ const manifestPath = join39(targetDir, "biffo.plugin.json");
11157
+ if (!existsSync37(manifestPath)) return void 0;
10990
11158
  try {
10991
- return validateManifest(JSON.parse(readFileSync27(manifestPath, "utf8"))).version;
11159
+ return validateManifest(JSON.parse(readFileSync28(manifestPath, "utf8"))).version;
10992
11160
  } catch {
10993
11161
  return void 0;
10994
11162
  }
@@ -11071,7 +11239,7 @@ pluginCommand.addCommand(pluginStalenessCommand);
11071
11239
  import { Command as Command23 } from "commander";
11072
11240
 
11073
11241
  // src/commands/sibling-check-identity.ts
11074
- import { existsSync as existsSync37, readFileSync as readFileSync28 } from "fs";
11242
+ import { existsSync as existsSync38, readFileSync as readFileSync29 } from "fs";
11075
11243
  import { resolve as resolve18 } from "path";
11076
11244
  import chalk20 from "chalk";
11077
11245
  import { Command as Command22 } from "commander";
@@ -11265,7 +11433,7 @@ async function fetchPublishedIdentity(portalUrl) {
11265
11433
  }
11266
11434
  async function resolveConfig4(options) {
11267
11435
  if (options.config) {
11268
- const raw = JSON.parse(readFileSync28(resolve18(options.config), "utf8"));
11436
+ const raw = JSON.parse(readFileSync29(resolve18(options.config), "utf8"));
11269
11437
  const result = BiffoConfigSchema.safeParse(raw);
11270
11438
  if (!result.success) {
11271
11439
  log.error(`Invalid config at ${options.config}:`);
@@ -11285,8 +11453,8 @@ async function resolveConfig4(options) {
11285
11453
  return cfg;
11286
11454
  }
11287
11455
  const localConfigPath = resolve18(process.cwd(), "biffo.config.json");
11288
- if (existsSync37(localConfigPath)) {
11289
- const raw = JSON.parse(readFileSync28(localConfigPath, "utf8"));
11456
+ if (existsSync38(localConfigPath)) {
11457
+ const raw = JSON.parse(readFileSync29(localConfigPath, "utf8"));
11290
11458
  const result = BiffoConfigSchema.safeParse(raw);
11291
11459
  if (result.success) return result.data;
11292
11460
  if (isTemplatePlaceholderConfig(raw)) {
@@ -11332,20 +11500,20 @@ siblingCommand.addCommand(siblingCheckIdentityCommand);
11332
11500
  import { Command as Command24 } from "commander";
11333
11501
 
11334
11502
  // src/scripts/check-adr-numbering.ts
11335
- import { existsSync as existsSync39 } from "fs";
11336
- import { join as join40 } from "path";
11503
+ import { existsSync as existsSync40 } from "fs";
11504
+ import { join as join41 } from "path";
11337
11505
 
11338
11506
  // src/lib/adr-numbering-guard.ts
11339
- import { existsSync as existsSync38, readdirSync as readdirSync15, readFileSync as readFileSync29 } from "fs";
11340
- import { join as join39 } from "path";
11507
+ import { existsSync as existsSync39, readdirSync as readdirSync15, readFileSync as readFileSync30 } from "fs";
11508
+ import { join as join40 } from "path";
11341
11509
  var ADR_FILENAME = /^(\d{4})-.+\.md$/;
11342
11510
  var ALLOWLIST_FILENAME = ".numbering-allowlist";
11343
11511
  var TEMPLATE_ADR_RESERVED_UPTO = "0099";
11344
11512
  function readAdrNumberingAllowlist(adrDir) {
11345
- const path = join39(adrDir, ALLOWLIST_FILENAME);
11346
- if (!existsSync38(path)) return /* @__PURE__ */ new Set();
11513
+ const path = join40(adrDir, ALLOWLIST_FILENAME);
11514
+ if (!existsSync39(path)) return /* @__PURE__ */ new Set();
11347
11515
  const numbers = /* @__PURE__ */ new Set();
11348
- for (const rawLine of readFileSync29(path, "utf8").split("\n")) {
11516
+ for (const rawLine of readFileSync30(path, "utf8").split("\n")) {
11349
11517
  const line = rawLine.split("#")[0].trim();
11350
11518
  if (line) numbers.add(line);
11351
11519
  }
@@ -11353,7 +11521,7 @@ function readAdrNumberingAllowlist(adrDir) {
11353
11521
  }
11354
11522
  function adrNumbersIn(adrDir) {
11355
11523
  const claims = /* @__PURE__ */ new Map();
11356
- if (!existsSync38(adrDir)) return claims;
11524
+ if (!existsSync39(adrDir)) return claims;
11357
11525
  for (const entry of readdirSync15(adrDir).sort()) {
11358
11526
  const match = ADR_FILENAME.exec(entry);
11359
11527
  if (!match) continue;
@@ -11408,8 +11576,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
11408
11576
  // src/scripts/check-adr-numbering.ts
11409
11577
  async function runAdrNumberingCheck() {
11410
11578
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11411
- const adrDir = join40(root, "docs", "ADR");
11412
- if (!existsSync39(adrDir)) {
11579
+ const adrDir = join41(root, "docs", "ADR");
11580
+ if (!existsSync40(adrDir)) {
11413
11581
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
11414
11582
  return;
11415
11583
  }
@@ -11447,8 +11615,8 @@ Already accepted? List it in docs/ADR/${ALLOWLIST_FILENAME} instead of leaving t
11447
11615
  }
11448
11616
 
11449
11617
  // src/lib/api-gateway-integration-guard.ts
11450
- import { readFileSync as readFileSync30, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
11451
- import { join as join41 } from "path";
11618
+ import { readFileSync as readFileSync31, readdirSync as readdirSync16, statSync as statSync9 } from "fs";
11619
+ import { join as join42 } from "path";
11452
11620
  var SKIP_DIRS = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
11453
11621
  var MODULE_TYPE = "module";
11454
11622
  var INTEGRATION_TYPE = "aws_apigatewayv2_integration";
@@ -11530,7 +11698,7 @@ function walkTerraformFiles(root) {
11530
11698
  return;
11531
11699
  }
11532
11700
  for (const entry of entries) {
11533
- const p = join41(dir, entry);
11701
+ const p = join42(dir, entry);
11534
11702
  let st;
11535
11703
  try {
11536
11704
  st = statSync9(p);
@@ -11574,7 +11742,7 @@ function auditApiGatewayIntegrations(root) {
11574
11742
  let rawModuleCount = 0;
11575
11743
  let rawIntegrationCount = 0;
11576
11744
  for (const file of files) {
11577
- const text = readFileSync30(file, "utf8");
11745
+ const text = readFileSync31(file, "utf8");
11578
11746
  rawModuleCount += countRawResourceDeclarations(text, MODULE_TYPE);
11579
11747
  rawIntegrationCount += countRawResourceDeclarations(text, INTEGRATION_TYPE);
11580
11748
  moduleBlocks.push(...findModuleBlocks(text, file));
@@ -11946,18 +12114,18 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
11946
12114
  }
11947
12115
 
11948
12116
  // src/lib/claim-invocation-parity.ts
11949
- import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync17 } from "fs";
11950
- import { join as join42 } from "path";
12117
+ import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync17 } from "fs";
12118
+ import { join as join43 } from "path";
11951
12119
  function distributedAgentsDocs(root) {
11952
12120
  const docs = [];
11953
- const own = join42(root, "AGENTS.md");
11954
- if (existsSync40(own)) docs.push({ path: "AGENTS.md", text: readFileSync31(own, "utf8") });
11955
- const skeletons = join42(root, "_skeletons");
11956
- if (existsSync40(skeletons)) {
12121
+ const own = join43(root, "AGENTS.md");
12122
+ if (existsSync41(own)) docs.push({ path: "AGENTS.md", text: readFileSync32(own, "utf8") });
12123
+ const skeletons = join43(root, "_skeletons");
12124
+ if (existsSync41(skeletons)) {
11957
12125
  for (const name of readdirSync17(skeletons).sort()) {
11958
- const abs = join42(skeletons, name, "AGENTS.md");
11959
- if (!existsSync40(abs)) continue;
11960
- docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync31(abs, "utf8") });
12126
+ const abs = join43(skeletons, name, "AGENTS.md");
12127
+ if (!existsSync41(abs)) continue;
12128
+ docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync32(abs, "utf8") });
11961
12129
  }
11962
12130
  }
11963
12131
  return docs;
@@ -12089,12 +12257,12 @@ async function runClaimInvocationCheck() {
12089
12257
  }
12090
12258
 
12091
12259
  // src/scripts/check-codeql-suppression.ts
12092
- import { existsSync as existsSync41 } from "fs";
12093
- import { join as join44, relative as relative8 } from "path";
12260
+ import { existsSync as existsSync42 } from "fs";
12261
+ import { join as join45, relative as relative8 } from "path";
12094
12262
 
12095
12263
  // src/lib/codeql-suppression-guard.ts
12096
- import { readdirSync as readdirSync18, readFileSync as readFileSync32, statSync as statSync10 } from "fs";
12097
- import { join as join43 } from "path";
12264
+ import { readdirSync as readdirSync18, readFileSync as readFileSync33, statSync as statSync10 } from "fs";
12265
+ import { join as join44 } from "path";
12098
12266
  var SKIP_DIRS2 = /* @__PURE__ */ new Set([
12099
12267
  ".git",
12100
12268
  ".worktrees",
@@ -12125,7 +12293,7 @@ function walkSourceFiles(root) {
12125
12293
  return;
12126
12294
  }
12127
12295
  for (const entry of entries) {
12128
- const p = join43(dir, entry);
12296
+ const p = join44(dir, entry);
12129
12297
  let st;
12130
12298
  try {
12131
12299
  st = statSync10(p);
@@ -12151,7 +12319,7 @@ function countSourceFiles(root) {
12151
12319
  function sweepCodeqlSuppressionComments(root) {
12152
12320
  const hits = [];
12153
12321
  for (const path of walkSourceFiles(root)) {
12154
- const text = readFileSync32(path, "utf8");
12322
+ const text = readFileSync33(path, "utf8");
12155
12323
  for (const line of findCodeqlSuppressionComments(text)) {
12156
12324
  hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
12157
12325
  }
@@ -12162,8 +12330,8 @@ function sweepCodeqlSuppressionComments(root) {
12162
12330
  // src/scripts/check-codeql-suppression.ts
12163
12331
  async function runCodeqlSuppressionCheck() {
12164
12332
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12165
- const scanRoot = join44(root, "cli", "src");
12166
- if (!existsSync41(scanRoot)) {
12333
+ const scanRoot = join45(root, "cli", "src");
12334
+ if (!existsSync42(scanRoot)) {
12167
12335
  console.log(
12168
12336
  "\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
12169
12337
  );
@@ -12187,12 +12355,12 @@ async function runCodeqlSuppressionCheck() {
12187
12355
  }
12188
12356
 
12189
12357
  // src/scripts/check-cognito-invite-template.ts
12190
- import { existsSync as existsSync42 } from "fs";
12191
- import { join as join46 } from "path";
12358
+ import { existsSync as existsSync43 } from "fs";
12359
+ import { join as join47 } from "path";
12192
12360
 
12193
12361
  // src/lib/cognito-invite-template-guard.ts
12194
- import { readdirSync as readdirSync19, readFileSync as readFileSync33, statSync as statSync11 } from "fs";
12195
- import { join as join45 } from "path";
12362
+ import { readdirSync as readdirSync19, readFileSync as readFileSync34, statSync as statSync11 } from "fs";
12363
+ import { join as join46 } from "path";
12196
12364
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
12197
12365
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
12198
12366
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -12276,7 +12444,7 @@ function findModuleTerraformFiles(repoRoot) {
12276
12444
  for (const entry of entries) {
12277
12445
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees" || entry === ".venv")
12278
12446
  continue;
12279
- const full = join45(dir, entry);
12447
+ const full = join46(dir, entry);
12280
12448
  const rel = `${relative11}/${entry}`;
12281
12449
  let isDir;
12282
12450
  try {
@@ -12291,12 +12459,12 @@ function findModuleTerraformFiles(repoRoot) {
12291
12459
  }
12292
12460
  }
12293
12461
  };
12294
- walk2(join45(repoRoot, "modules"), "modules");
12462
+ walk2(join46(repoRoot, "modules"), "modules");
12295
12463
  return found.sort();
12296
12464
  }
12297
12465
  function checkCognitoInviteTemplates(repoRoot) {
12298
12466
  return findModuleTerraformFiles(repoRoot).flatMap(
12299
- (file) => checkInviteTemplateSource(file, readFileSync33(join45(repoRoot, file), "utf8"))
12467
+ (file) => checkInviteTemplateSource(file, readFileSync34(join46(repoRoot, file), "utf8"))
12300
12468
  );
12301
12469
  }
12302
12470
 
@@ -12306,7 +12474,7 @@ async function runCognitoInviteTemplateCheck() {
12306
12474
  const files = findModuleTerraformFiles(root);
12307
12475
  console.log(`audited ${files.length} .tf file(s) under modules/ under ${root}`);
12308
12476
  if (files.length === 0) {
12309
- if (!existsSync42(join46(root, "modules"))) {
12477
+ if (!existsSync43(join47(root, "modules"))) {
12310
12478
  console.log(
12311
12479
  `\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.`
12312
12480
  );
@@ -12330,11 +12498,11 @@ async function runCognitoInviteTemplateCheck() {
12330
12498
  }
12331
12499
 
12332
12500
  // src/scripts/check-core-direct-paths.ts
12333
- import { join as join48 } from "path";
12501
+ import { join as join49 } from "path";
12334
12502
 
12335
12503
  // src/lib/core-direct-paths-audit.ts
12336
- import { existsSync as existsSync43, readFileSync as readFileSync34, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12337
- import { join as join47 } from "path";
12504
+ import { existsSync as existsSync44, readFileSync as readFileSync35, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
12505
+ import { join as join48 } from "path";
12338
12506
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
12339
12507
  var API_ROUTE_PREFIX = "/api/v1";
12340
12508
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -12498,7 +12666,7 @@ function walkFiles(root, accept, skipDir) {
12498
12666
  return;
12499
12667
  }
12500
12668
  for (const entry of entries) {
12501
- const p = join47(dir, entry);
12669
+ const p = join48(dir, entry);
12502
12670
  let st;
12503
12671
  try {
12504
12672
  st = statSync12(p);
@@ -12528,7 +12696,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
12528
12696
  const extracted = [];
12529
12697
  let rawTotal = 0;
12530
12698
  for (const file of files) {
12531
- const text = readFileSync34(file, "utf8");
12699
+ const text = readFileSync35(file, "utf8");
12532
12700
  rawTotal += countRawExternalOccurrences(text, externalBases);
12533
12701
  extracted.push(...extractCoreDirectPaths(text, file, externalBases));
12534
12702
  }
@@ -12577,7 +12745,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
12577
12745
  const prefixSet = /* @__PURE__ */ new Set();
12578
12746
  let rawApiRouterCount = 0;
12579
12747
  for (const file of files) {
12580
- const text = readFileSync34(file, "utf8");
12748
+ const text = readFileSync35(file, "utf8");
12581
12749
  const extraction = extractCoreRoutePrefixes(text);
12582
12750
  rawApiRouterCount += extraction.rawApiRouterCount;
12583
12751
  for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
@@ -12593,10 +12761,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
12593
12761
  }
12594
12762
  function resolveSiblingCoreSrc(params) {
12595
12763
  const { estateDir, sibling } = params;
12596
- const configPath = join47(estateDir, sibling, "biffo.sibling.json");
12764
+ const configPath = join48(estateDir, sibling, "biffo.sibling.json");
12597
12765
  let raw;
12598
12766
  try {
12599
- raw = readFileSync34(configPath, "utf8");
12767
+ raw = readFileSync35(configPath, "utf8");
12600
12768
  } catch (err) {
12601
12769
  throw new Error(
12602
12770
  `cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
@@ -12616,8 +12784,8 @@ function resolveSiblingCoreSrc(params) {
12616
12784
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
12617
12785
  );
12618
12786
  }
12619
- const coreApiSrcDir = join47(estateDir, coreProject, "services", "api", "src");
12620
- if (!existsSync43(coreApiSrcDir)) {
12787
+ const coreApiSrcDir = join48(estateDir, coreProject, "services", "api", "src");
12788
+ if (!existsSync44(coreApiSrcDir)) {
12621
12789
  throw new Error(
12622
12790
  `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.`
12623
12791
  );
@@ -12670,7 +12838,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
12670
12838
  }
12671
12839
  }
12672
12840
  const sibling = opts.sibling ?? "sibling-template (self-check)";
12673
- const frontendSrcDir = opts.frontendSrc ?? join48(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
12841
+ const frontendSrcDir = opts.frontendSrc ?? join49(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
12674
12842
  let coreApiSrcDir;
12675
12843
  let coreProject = null;
12676
12844
  if (opts.coreSrc) {
@@ -12686,7 +12854,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
12686
12854
  coreApiSrcDir = resolution.coreApiSrcDir;
12687
12855
  coreProject = resolution.coreProject;
12688
12856
  } else {
12689
- coreApiSrcDir = join48(root, "services", "api", "src");
12857
+ coreApiSrcDir = join49(root, "services", "api", "src");
12690
12858
  }
12691
12859
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
12692
12860
  console.log(
@@ -12813,8 +12981,8 @@ async function runOwnershipCheck(argv) {
12813
12981
  const { stdout } = await execa("git", ["diff", "--cached", "--name-status"], { cwd: root });
12814
12982
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
12815
12983
  if (messageFile) {
12816
- const { readFileSync: readFileSync48, existsSync: existsSync58 } = await import("fs");
12817
- if (existsSync58(messageFile)) commitMessage = readFileSync48(messageFile, "utf8");
12984
+ const { readFileSync: readFileSync49, existsSync: existsSync59 } = await import("fs");
12985
+ if (existsSync59(messageFile)) commitMessage = readFileSync49(messageFile, "utf8");
12818
12986
  }
12819
12987
  } else {
12820
12988
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -12937,21 +13105,21 @@ ${BOLD}If the divergence is deliberate${OFF}
12937
13105
  }
12938
13106
 
12939
13107
  // src/scripts/check-distribution-inventory.ts
12940
- import { existsSync as existsSync45 } from "fs";
12941
- import { join as join50 } from "path";
13108
+ import { existsSync as existsSync46 } from "fs";
13109
+ import { join as join51 } from "path";
12942
13110
 
12943
13111
  // src/lib/distribution-inventory.ts
12944
- import { existsSync as existsSync44, readFileSync as readFileSync35 } from "fs";
12945
- import { join as join49 } from "path";
13112
+ import { existsSync as existsSync45, readFileSync as readFileSync36 } from "fs";
13113
+ import { join as join50 } from "path";
12946
13114
  var INVENTORY_FILENAME = "distribution-inventory.json";
12947
13115
  function loadDistributionInventory(root) {
12948
- const path = join49(root, INVENTORY_FILENAME);
12949
- if (!existsSync44(path)) {
13116
+ const path = join50(root, INVENTORY_FILENAME);
13117
+ if (!existsSync45(path)) {
12950
13118
  throw new Error(
12951
13119
  `${INVENTORY_FILENAME} not found at ${root} -- expected it beside core-manifest.json`
12952
13120
  );
12953
13121
  }
12954
- return JSON.parse(readFileSync35(path, "utf8"));
13122
+ return JSON.parse(readFileSync36(path, "utf8"));
12955
13123
  }
12956
13124
  function validateInventory(inventory) {
12957
13125
  const violations = [];
@@ -13111,7 +13279,7 @@ async function runDistributionInventoryCheck(root) {
13111
13279
  );
13112
13280
  return;
13113
13281
  }
13114
- if (!existsSync45(join50(repoRoot, INVENTORY_FILENAME))) {
13282
+ if (!existsSync46(join51(repoRoot, INVENTORY_FILENAME))) {
13115
13283
  console.log(
13116
13284
  `\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.`
13117
13285
  );
@@ -13137,7 +13305,7 @@ async function runDistributionInventoryCheck(root) {
13137
13305
  }
13138
13306
 
13139
13307
  // src/scripts/check-distribution-remote-state.ts
13140
- import { join as join51 } from "path";
13308
+ import { join as join52 } from "path";
13141
13309
  async function ghExecCommand(file, args) {
13142
13310
  const result = await execa(file, args, { reject: false });
13143
13311
  return { stdout: String(result.stdout ?? ""), exitCode: result.exitCode ?? null };
@@ -13152,7 +13320,7 @@ async function runDistributionRemoteStateCheck(root) {
13152
13320
  }
13153
13321
  }
13154
13322
  console.log(
13155
- `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join51(repoRoot, "distribution-inventory.json")})`
13323
+ `distribution-remote-state: examined ${assertions.length} remote content assertion(s) declared across ${inventory.entries.length} inventory entries (${join52(repoRoot, "distribution-inventory.json")})`
13156
13324
  );
13157
13325
  if (assertions.length === 0) {
13158
13326
  console.log("\u2713 distribution-remote-state: nothing declared to check");
@@ -13197,8 +13365,8 @@ ${assertion.ref}`;
13197
13365
  }
13198
13366
 
13199
13367
  // src/lib/eventbridge-log-permission-guard.ts
13200
- import { readFileSync as readFileSync36, readdirSync as readdirSync21, statSync as statSync13 } from "fs";
13201
- import { join as join52 } from "path";
13368
+ import { readFileSync as readFileSync37, readdirSync as readdirSync21, statSync as statSync13 } from "fs";
13369
+ import { join as join53 } from "path";
13202
13370
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
13203
13371
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
13204
13372
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -13275,7 +13443,7 @@ function walkTerraformFiles2(root) {
13275
13443
  return;
13276
13444
  }
13277
13445
  for (const entry of entries) {
13278
- const p = join52(dir, entry);
13446
+ const p = join53(dir, entry);
13279
13447
  let st;
13280
13448
  try {
13281
13449
  st = statSync13(p);
@@ -13318,7 +13486,7 @@ function auditEventBridgeLogPermissions(root) {
13318
13486
  let rawEventTargetCount = 0;
13319
13487
  let rawLogPolicyCount = 0;
13320
13488
  for (const file of files) {
13321
- const text = readFileSync36(file, "utf8");
13489
+ const text = readFileSync37(file, "utf8");
13322
13490
  rawEventTargetCount += countRawResourceDeclarations2(text, EVENT_TARGET_TYPE);
13323
13491
  rawLogPolicyCount += countRawResourceDeclarations2(text, LOG_RESOURCE_POLICY_TYPE);
13324
13492
  eventTargetBlocks.push(...findResourceBlocks2(text, file, EVENT_TARGET_TYPE));
@@ -13415,8 +13583,8 @@ async function runEventBridgeLogPermissionCheck() {
13415
13583
  }
13416
13584
 
13417
13585
  // src/scripts/check-instance-adoption.ts
13418
- import { existsSync as existsSync46 } from "fs";
13419
- import { join as join53 } from "path";
13586
+ import { existsSync as existsSync47 } from "fs";
13587
+ import { join as join54 } from "path";
13420
13588
  async function runInstanceAdoptionCheck(opts = {}) {
13421
13589
  if (!opts.instanceDir) {
13422
13590
  console.error(
@@ -13424,7 +13592,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
13424
13592
  );
13425
13593
  process.exit(2);
13426
13594
  }
13427
- if (!existsSync46(opts.instanceDir)) {
13595
+ if (!existsSync47(opts.instanceDir)) {
13428
13596
  console.error(
13429
13597
  `\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.`
13430
13598
  );
@@ -13432,7 +13600,7 @@ async function runInstanceAdoptionCheck(opts = {}) {
13432
13600
  }
13433
13601
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13434
13602
  const theirsDir = opts.theirsDir ?? root;
13435
- const instanceLabel = opts.instance ?? join53(opts.instanceDir).split("/").filter(Boolean).pop();
13603
+ const instanceLabel = opts.instance ?? join54(opts.instanceDir).split("/").filter(Boolean).pop();
13436
13604
  const report = checkInstanceAdoption(theirsDir, opts.instanceDir);
13437
13605
  console.log(
13438
13606
  `examined ${report.examinedInstances} instance (${instanceLabel}) against ${report.registeredPairs} registered pair(s), ${report.applicablePairs} applicable to this instance, against template tree ${theirsDir}`
@@ -13454,12 +13622,12 @@ async function runInstanceAdoptionCheck(opts = {}) {
13454
13622
  }
13455
13623
 
13456
13624
  // src/lib/lambda-output-guard.ts
13457
- import { readFileSync as readFileSync38 } from "fs";
13458
- import { join as join55 } from "path";
13625
+ import { readFileSync as readFileSync39 } from "fs";
13626
+ import { join as join56 } from "path";
13459
13627
 
13460
13628
  // src/lib/terraform-input-guard.ts
13461
- import { existsSync as existsSync47, readdirSync as readdirSync22, readFileSync as readFileSync37, statSync as statSync14 } from "fs";
13462
- import { join as join54 } from "path";
13629
+ import { existsSync as existsSync48, readdirSync as readdirSync22, readFileSync as readFileSync38, statSync as statSync14 } from "fs";
13630
+ import { join as join55 } from "path";
13463
13631
  var GUARDED_SUBCOMMANDS = [
13464
13632
  "init",
13465
13633
  "plan",
@@ -13473,7 +13641,7 @@ function stripComments2(source) {
13473
13641
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
13474
13642
  }
13475
13643
  function vendoredPluginServiceDirs(repoRoot) {
13476
- const servicesDir = join54(repoRoot, "services");
13644
+ const servicesDir = join55(repoRoot, "services");
13477
13645
  const result = /* @__PURE__ */ new Set();
13478
13646
  let entries;
13479
13647
  try {
@@ -13482,9 +13650,9 @@ function vendoredPluginServiceDirs(repoRoot) {
13482
13650
  return result;
13483
13651
  }
13484
13652
  for (const entry of entries) {
13485
- const full = join54(servicesDir, entry);
13486
- if (!existsSync47(full) || !statSync14(full).isDirectory()) continue;
13487
- if (existsSync47(join54(full, "biffo.plugin.json"))) {
13653
+ const full = join55(servicesDir, entry);
13654
+ if (!existsSync48(full) || !statSync14(full).isDirectory()) continue;
13655
+ if (existsSync48(join55(full, "biffo.plugin.json"))) {
13488
13656
  result.add(entry);
13489
13657
  }
13490
13658
  }
@@ -13506,7 +13674,7 @@ function findWorkflowFiles(repoRoot) {
13506
13674
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
13507
13675
  continue;
13508
13676
  }
13509
- const full = join54(dir, entry);
13677
+ const full = join55(dir, entry);
13510
13678
  const rel = relative11 ? `${relative11}/${entry}` : entry;
13511
13679
  let isDir;
13512
13680
  try {
@@ -13556,7 +13724,7 @@ function checkWorkflowSource(file, rawSource) {
13556
13724
  }
13557
13725
  function checkTerraformInput(repoRoot) {
13558
13726
  return findWorkflowFiles(repoRoot).flatMap(
13559
- (file) => checkWorkflowSource(file, readFileSync37(join54(repoRoot, file), "utf8"))
13727
+ (file) => checkWorkflowSource(file, readFileSync38(join55(repoRoot, file), "utf8"))
13560
13728
  );
13561
13729
  }
13562
13730
 
@@ -13614,7 +13782,7 @@ function checkWorkflowSource2(file, rawSource) {
13614
13782
  }
13615
13783
  function checkLambdaOutput(repoRoot) {
13616
13784
  return findWorkflowFiles(repoRoot).flatMap(
13617
- (file) => checkWorkflowSource2(file, readFileSync38(join55(repoRoot, file), "utf8"))
13785
+ (file) => checkWorkflowSource2(file, readFileSync39(join56(repoRoot, file), "utf8"))
13618
13786
  );
13619
13787
  }
13620
13788
 
@@ -13642,8 +13810,8 @@ async function runLambdaOutputCheck() {
13642
13810
  }
13643
13811
 
13644
13812
  // src/scripts/check-migration-body-change.ts
13645
- import { existsSync as existsSync48 } from "fs";
13646
- import { join as join56 } from "path";
13813
+ import { existsSync as existsSync49 } from "fs";
13814
+ import { join as join57 } from "path";
13647
13815
 
13648
13816
  // src/lib/migration-body-change-guard.ts
13649
13817
  function checkMigrationBodyChangeMarkers(diffs) {
@@ -13725,7 +13893,7 @@ async function runMigrationBodyChangeCheck(argv) {
13725
13893
  );
13726
13894
  return;
13727
13895
  }
13728
- if (!existsSync48(join56(root, MIGRATIONS_VERSIONS_DIR))) {
13896
+ if (!existsSync49(join57(root, MIGRATIONS_VERSIONS_DIR))) {
13729
13897
  console.log(`\u2713 migration body-change guard: no ${MIGRATIONS_VERSIONS_DIR} in this repo.`);
13730
13898
  return;
13731
13899
  }
@@ -13831,8 +13999,8 @@ ${BOLD2}What to do${OFF2}
13831
13999
  }
13832
14000
 
13833
14001
  // src/scripts/check-orphan-ratchet.ts
13834
- import { existsSync as existsSync49 } from "fs";
13835
- import { basename as basename3, join as join57 } from "path";
14002
+ import { existsSync as existsSync50 } from "fs";
14003
+ import { basename as basename3, join as join58 } from "path";
13836
14004
  function reportOrphan(entry, manifest) {
13837
14005
  console.error(` ${entry.path}`);
13838
14006
  const { templateOwnedMatch, nearestUserOwnedEntries } = explainOwnership(entry.path, manifest);
@@ -13854,7 +14022,7 @@ function reportOrphan(entry, manifest) {
13854
14022
  }
13855
14023
  }
13856
14024
  async function runOrphanRatchetCheck(opts = {}) {
13857
- if (opts.instanceDir !== void 0 && !existsSync49(opts.instanceDir)) {
14025
+ if (opts.instanceDir !== void 0 && !existsSync50(opts.instanceDir)) {
13858
14026
  console.error(
13859
14027
  `\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.`
13860
14028
  );
@@ -13871,7 +14039,7 @@ async function runOrphanRatchetCheck(opts = {}) {
13871
14039
  "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."
13872
14040
  );
13873
14041
  }
13874
- if (!existsSync49(join57(theirsDir, CORE_MANIFEST_FILE))) {
14042
+ if (!existsSync50(join58(theirsDir, CORE_MANIFEST_FILE))) {
13875
14043
  console.log(
13876
14044
  `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.`
13877
14045
  );
@@ -13902,8 +14070,8 @@ baseline was ${String(ratchet.baseline)}, now ${String(ratchet.count)}. See biff
13902
14070
  }
13903
14071
 
13904
14072
  // src/lib/ownership-header-claim-guard.ts
13905
- import { readFileSync as readFileSync39 } from "fs";
13906
- import { join as join58 } from "path";
14073
+ import { readFileSync as readFileSync40 } from "fs";
14074
+ import { join as join59 } from "path";
13907
14075
  var OWNERSHIP_HEADER_SWEEP_DIRS = [
13908
14076
  "scripts",
13909
14077
  ".githooks",
@@ -14112,7 +14280,7 @@ function sweepOwnershipHeaderClaims(root, options = {}) {
14112
14280
  for (const rel of files) {
14113
14281
  let content;
14114
14282
  try {
14115
- content = readFileSync39(join58(root, rel), "utf8");
14283
+ content = readFileSync40(join59(root, rel), "utf8");
14116
14284
  } catch {
14117
14285
  continue;
14118
14286
  }
@@ -14187,8 +14355,8 @@ async function runOwnershipHeaderClaimCheck() {
14187
14355
  }
14188
14356
 
14189
14357
  // src/scripts/check-pipe-trap.ts
14190
- import { readFileSync as readFileSync40, readdirSync as readdirSync23 } from "fs";
14191
- import { join as join59, relative as relative9 } from "path";
14358
+ import { readFileSync as readFileSync41, readdirSync as readdirSync23 } from "fs";
14359
+ import { join as join60, relative as relative9 } from "path";
14192
14360
 
14193
14361
  // src/lib/pipe-trap-guard.ts
14194
14362
  var STATUS_BEARING = [
@@ -14284,7 +14452,7 @@ function findPipeTraps(source) {
14284
14452
  function shellFiles(root) {
14285
14453
  const out = [];
14286
14454
  for (const dir of ["scripts", ".githooks"]) {
14287
- const full = join59(root, dir);
14455
+ const full = join60(root, dir);
14288
14456
  let entries;
14289
14457
  try {
14290
14458
  entries = readdirSync23(full, { withFileTypes: true });
@@ -14294,7 +14462,7 @@ function shellFiles(root) {
14294
14462
  for (const entry of entries) {
14295
14463
  if (!entry.isFile()) continue;
14296
14464
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
14297
- out.push(join59(full, entry.name));
14465
+ out.push(join60(full, entry.name));
14298
14466
  }
14299
14467
  }
14300
14468
  return out;
@@ -14310,7 +14478,7 @@ async function runPipeTrapCheck() {
14310
14478
  process.exit(1);
14311
14479
  }
14312
14480
  const findings = files.flatMap(
14313
- (file) => findPipeTraps(readFileSync40(file, "utf8")).map(
14481
+ (file) => findPipeTraps(readFileSync41(file, "utf8")).map(
14314
14482
  (t) => `${relative9(root, file)}:${t.line} ${t.text}
14315
14483
  ${t.reason}`
14316
14484
  )
@@ -14327,8 +14495,8 @@ async function runPipeTrapCheck() {
14327
14495
  }
14328
14496
 
14329
14497
  // src/lib/plugin-allowlist-convention.ts
14330
- import { readFileSync as readFileSync41 } from "fs";
14331
- import { join as join60 } from "path";
14498
+ import { readFileSync as readFileSync42 } from "fs";
14499
+ import { join as join61 } from "path";
14332
14500
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
14333
14501
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
14334
14502
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -14339,7 +14507,7 @@ var PLUGIN = "<plugin>";
14339
14507
  var ACCOUNT = "<account>";
14340
14508
  function read(repoRoot, relative11) {
14341
14509
  try {
14342
- return readFileSync41(join60(repoRoot, relative11), "utf8");
14510
+ return readFileSync42(join61(repoRoot, relative11), "utf8");
14343
14511
  } catch {
14344
14512
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
14345
14513
  }
@@ -14467,32 +14635,32 @@ async function runPluginAllowlistConventionCheck() {
14467
14635
  }
14468
14636
 
14469
14637
  // src/scripts/check-plugin-collisions.ts
14470
- import { existsSync as existsSync51 } from "fs";
14471
- import { join as join62 } from "path";
14638
+ import { existsSync as existsSync52 } from "fs";
14639
+ import { join as join63 } from "path";
14472
14640
 
14473
14641
  // src/lib/plugin-collision-guard.ts
14474
- import { existsSync as existsSync50, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
14475
- import { join as join61 } from "path";
14642
+ import { existsSync as existsSync51, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
14643
+ import { join as join62 } from "path";
14476
14644
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
14477
14645
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
14478
14646
  function subdirectories(dir) {
14479
- if (!existsSync50(dir)) return [];
14647
+ if (!existsSync51(dir)) return [];
14480
14648
  return readdirSync24(dir).filter((entry) => {
14481
14649
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
14482
14650
  try {
14483
- return statSync15(join61(dir, entry)).isDirectory();
14651
+ return statSync15(join62(dir, entry)).isDirectory();
14484
14652
  } catch {
14485
14653
  return false;
14486
14654
  }
14487
14655
  });
14488
14656
  }
14489
14657
  function regularPackagesOf(pluginDir2) {
14490
- return subdirectories(pluginDir2).filter((name) => existsSync50(join61(pluginDir2, name, "__init__.py"))).sort();
14658
+ return subdirectories(pluginDir2).filter((name) => existsSync51(join62(pluginDir2, name, "__init__.py"))).sort();
14491
14659
  }
14492
14660
  function bareTestModulesOf(pluginDir2) {
14493
- const testsDir = join61(pluginDir2, "tests");
14494
- if (!existsSync50(testsDir)) return [];
14495
- if (existsSync50(join61(testsDir, "__init__.py"))) return [];
14661
+ const testsDir = join62(pluginDir2, "tests");
14662
+ if (!existsSync51(testsDir)) return [];
14663
+ if (existsSync51(join62(testsDir, "__init__.py"))) return [];
14496
14664
  return readdirSync24(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
14497
14665
  }
14498
14666
  function findCollisions(servicesDir, pluginDirs) {
@@ -14501,7 +14669,7 @@ function findCollisions(servicesDir, pluginDirs) {
14501
14669
  const gather = (kind, namesOf) => {
14502
14670
  const claims = /* @__PURE__ */ new Map();
14503
14671
  for (const plugin of plugins) {
14504
- for (const name of namesOf(join61(servicesDir, plugin))) {
14672
+ for (const name of namesOf(join62(servicesDir, plugin))) {
14505
14673
  claims.set(name, [...claims.get(name) ?? [], plugin]);
14506
14674
  }
14507
14675
  }
@@ -14539,8 +14707,8 @@ function formatCollisions(collisions) {
14539
14707
  // src/scripts/check-plugin-collisions.ts
14540
14708
  async function runPluginCollisionCheck() {
14541
14709
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14542
- const servicesDir = join62(root, "services");
14543
- if (!existsSync51(servicesDir)) {
14710
+ const servicesDir = join63(root, "services");
14711
+ if (!existsSync52(servicesDir)) {
14544
14712
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
14545
14713
  return;
14546
14714
  }
@@ -14557,8 +14725,8 @@ async function runPluginCollisionCheck() {
14557
14725
  }
14558
14726
 
14559
14727
  // src/lib/plugin-terraform-guard.ts
14560
- import { existsSync as existsSync52, readFileSync as readFileSync42, readdirSync as readdirSync25 } from "fs";
14561
- import { dirname as dirname10, join as join63, relative as relative10, sep as sep4 } from "path";
14728
+ import { existsSync as existsSync53, readFileSync as readFileSync43, readdirSync as readdirSync25 } from "fs";
14729
+ import { dirname as dirname10, join as join64, relative as relative10, sep as sep4 } from "path";
14562
14730
  var SKIP_DIRS4 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
14563
14731
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
14564
14732
  function findPluginManifests(root) {
@@ -14573,9 +14741,9 @@ function findPluginManifests(root) {
14573
14741
  for (const entry of entries) {
14574
14742
  if (entry.isDirectory()) {
14575
14743
  if (SKIP_DIRS4.has(entry.name)) continue;
14576
- walk2(join63(dir, entry.name));
14744
+ walk2(join64(dir, entry.name));
14577
14745
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
14578
- found.push(relative10(root, join63(dir, entry.name)).split(sep4).join("/"));
14746
+ found.push(relative10(root, join64(dir, entry.name)).split(sep4).join("/"));
14579
14747
  }
14580
14748
  }
14581
14749
  };
@@ -14585,7 +14753,7 @@ function findPluginManifests(root) {
14585
14753
  function readSubscriptions(absManifestPath) {
14586
14754
  let parsed;
14587
14755
  try {
14588
- parsed = JSON.parse(readFileSync42(absManifestPath, "utf8"));
14756
+ parsed = JSON.parse(readFileSync43(absManifestPath, "utf8"));
14589
14757
  } catch {
14590
14758
  return null;
14591
14759
  }
@@ -14600,14 +14768,14 @@ function readSubscriptions(absManifestPath) {
14600
14768
  }
14601
14769
  function checkPluginTerraform(root) {
14602
14770
  const violations = [];
14603
- const coreManifest = existsSync52(join63(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
14771
+ const coreManifest = existsSync53(join64(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
14604
14772
  for (const manifest of findPluginManifests(root)) {
14605
14773
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
14606
- const absManifest = join63(root, manifest);
14774
+ const absManifest = join64(root, manifest);
14607
14775
  const subscriptions = readSubscriptions(absManifest);
14608
14776
  if (subscriptions === null) continue;
14609
14777
  const pluginDir2 = dirname10(absManifest);
14610
- if (existsSync52(join63(pluginDir2, "terraform"))) continue;
14778
+ if (existsSync53(join64(pluginDir2, "terraform"))) continue;
14611
14779
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
14612
14780
  violations.push({
14613
14781
  manifest,
@@ -14638,12 +14806,12 @@ async function runPluginTerraformCheck() {
14638
14806
  }
14639
14807
 
14640
14808
  // src/scripts/check-plugin-tool-supply.ts
14641
- import { existsSync as existsSync54 } from "fs";
14642
- import { join as join65 } from "path";
14809
+ import { existsSync as existsSync55 } from "fs";
14810
+ import { join as join66 } from "path";
14643
14811
 
14644
14812
  // src/lib/plugin-tool-supply-audit.ts
14645
- import { existsSync as existsSync53, readFileSync as readFileSync43, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14646
- import { join as join64 } from "path";
14813
+ import { existsSync as existsSync54, readFileSync as readFileSync44, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
14814
+ import { join as join65 } from "path";
14647
14815
 
14648
14816
  // src/lib/openrouter-model-snapshot.ts
14649
14817
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -15060,7 +15228,7 @@ function listDirs(root) {
15060
15228
  }
15061
15229
  return entries.filter((e) => {
15062
15230
  try {
15063
- return statSync16(join64(root, e)).isDirectory();
15231
+ return statSync16(join65(root, e)).isDirectory();
15064
15232
  } catch {
15065
15233
  return false;
15066
15234
  }
@@ -15076,7 +15244,7 @@ function walkFiles2(root, accept, skipDir) {
15076
15244
  return;
15077
15245
  }
15078
15246
  for (const entry of entries) {
15079
- const p = join64(dir, entry);
15247
+ const p = join65(dir, entry);
15080
15248
  let st;
15081
15249
  try {
15082
15250
  st = statSync16(p);
@@ -15102,14 +15270,14 @@ function pluginPythonFiles(pluginDir2) {
15102
15270
  );
15103
15271
  }
15104
15272
  function pluginTerraformFiles(pluginDir2) {
15105
- const tfDir = join64(pluginDir2, "terraform");
15273
+ const tfDir = join65(pluginDir2, "terraform");
15106
15274
  let entries;
15107
15275
  try {
15108
15276
  entries = readdirSync26(tfDir);
15109
15277
  } catch {
15110
15278
  return [];
15111
15279
  }
15112
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join64(tfDir, e)).sort();
15280
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join65(tfDir, e)).sort();
15113
15281
  }
15114
15282
  function extractManifestTools(manifestText) {
15115
15283
  let parsed;
@@ -15361,8 +15529,8 @@ function isSnapshotStale(fetchedAt, now) {
15361
15529
  function normalizeModelId(id) {
15362
15530
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
15363
15531
  }
15364
- var CONFIG_PY_PATH = join64("services", "api", "src", "api", "config.py");
15365
- var ORCHESTRATION_SCHEMA_PATH = join64(
15532
+ var CONFIG_PY_PATH = join65("services", "api", "src", "api", "config.py");
15533
+ var ORCHESTRATION_SCHEMA_PATH = join65(
15366
15534
  "services",
15367
15535
  "api",
15368
15536
  "src",
@@ -15374,10 +15542,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
15374
15542
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
15375
15543
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
15376
15544
  const now = options.now ?? /* @__PURE__ */ new Date();
15377
- const configPath = join64(repoRoot, CONFIG_PY_PATH);
15378
- const orchestrationPath = join64(repoRoot, ORCHESTRATION_SCHEMA_PATH);
15379
- const configMissing = !existsSync53(configPath);
15380
- const orchestrationSchemaMissing = !existsSync53(orchestrationPath);
15545
+ const configPath = join65(repoRoot, CONFIG_PY_PATH);
15546
+ const orchestrationPath = join65(repoRoot, ORCHESTRATION_SCHEMA_PATH);
15547
+ const configMissing = !existsSync54(configPath);
15548
+ const orchestrationSchemaMissing = !existsSync54(orchestrationPath);
15381
15549
  const knownSet = new Set(knownModelIds);
15382
15550
  const snapshotEmpty = knownModelIds.length === 0;
15383
15551
  const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
@@ -15395,13 +15563,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
15395
15563
  };
15396
15564
  let settingsBlind = false;
15397
15565
  if (!configMissing) {
15398
- const settingsFields = extractSettingsModelFields(readFileSync43(configPath, "utf8"));
15566
+ const settingsFields = extractSettingsModelFields(readFileSync44(configPath, "utf8"));
15399
15567
  if (settingsFields.length === 0) settingsBlind = true;
15400
15568
  for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
15401
15569
  }
15402
15570
  let curatedFieldsBlind = false;
15403
15571
  if (!orchestrationSchemaMissing) {
15404
- const curated = extractCuratedModelFields(readFileSync43(orchestrationPath, "utf8"));
15572
+ const curated = extractCuratedModelFields(readFileSync44(orchestrationPath, "utf8"));
15405
15573
  if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
15406
15574
  curatedFieldsBlind = true;
15407
15575
  }
@@ -15448,7 +15616,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
15448
15616
  function discoverPluginDirs(pluginsRoot) {
15449
15617
  return listDirs(pluginsRoot).filter((name) => {
15450
15618
  try {
15451
- return statSync16(join64(pluginsRoot, name, "biffo.plugin.json")).isFile();
15619
+ return statSync16(join65(pluginsRoot, name, "biffo.plugin.json")).isFile();
15452
15620
  } catch {
15453
15621
  return false;
15454
15622
  }
@@ -15461,8 +15629,8 @@ function auditPluginToolSupply(pluginsRoot) {
15461
15629
  let terraformBlind = false;
15462
15630
  let totalDeclaredTools = 0;
15463
15631
  for (const name of pluginNames) {
15464
- const pluginDir2 = join64(pluginsRoot, name);
15465
- const manifestText = readFileSync43(join64(pluginDir2, "biffo.plugin.json"), "utf8");
15632
+ const pluginDir2 = join65(pluginsRoot, name);
15633
+ const manifestText = readFileSync44(join65(pluginDir2, "biffo.plugin.json"), "utf8");
15466
15634
  const manifest = extractManifestTools(manifestText);
15467
15635
  if (manifest.parseError) {
15468
15636
  findings.push({
@@ -15480,13 +15648,13 @@ function auditPluginToolSupply(pluginsRoot) {
15480
15648
  totalDeclaredTools += manifest.tools.length;
15481
15649
  const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
15482
15650
  file: f,
15483
- text: readFileSync43(f, "utf8")
15651
+ text: readFileSync44(f, "utf8")
15484
15652
  }));
15485
15653
  const resolver = buildSymbolResolver(pySources);
15486
15654
  const registry = extractToolRegistryEntries(pySources, resolver);
15487
15655
  if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
15488
15656
  const tfFiles = pluginTerraformFiles(pluginDir2);
15489
- const tfText = tfFiles.map((f) => readFileSync43(f, "utf8")).join("\n");
15657
+ const tfText = tfFiles.map((f) => readFileSync44(f, "utf8")).join("\n");
15490
15658
  const terraform = extractTerraformEnvKeys(tfText);
15491
15659
  if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
15492
15660
  for (const toolName of manifest.tools) {
@@ -15560,7 +15728,7 @@ function auditPluginToolSupply(pluginsRoot) {
15560
15728
  requiredEnvVars: envResult.envVars,
15561
15729
  missingEnvVars: anyWired ? [] : envResult.envVars,
15562
15730
  status: anyWired ? "ok" : "missing-env",
15563
- 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 ${join64(pluginDir2, "terraform")}, so this deployment can never supply it`
15731
+ 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`
15564
15732
  });
15565
15733
  }
15566
15734
  }
@@ -15600,8 +15768,8 @@ async function runPluginToolSupplyCheck() {
15600
15768
  return;
15601
15769
  }
15602
15770
  let allOk = true;
15603
- const pluginsRoot = join65(root, "services", "_plugins");
15604
- if (!existsSync54(pluginsRoot)) {
15771
+ const pluginsRoot = join66(root, "services", "_plugins");
15772
+ if (!existsSync55(pluginsRoot)) {
15605
15773
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
15606
15774
  } else {
15607
15775
  const report = auditPluginToolSupply(pluginsRoot);
@@ -15631,8 +15799,8 @@ async function runPluginToolSupplyCheck() {
15631
15799
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
15632
15800
  }
15633
15801
  }
15634
- const servicesApiRoot = join65(root, "services", "api");
15635
- if (!existsSync54(servicesApiRoot)) {
15802
+ const servicesApiRoot = join66(root, "services", "api");
15803
+ if (!existsSync55(servicesApiRoot)) {
15636
15804
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
15637
15805
  } else {
15638
15806
  const modelReport = auditDeclaredModelIds(root);
@@ -15806,10 +15974,10 @@ async function runReleaseSubjectCheck(argv) {
15806
15974
  }
15807
15975
 
15808
15976
  // src/scripts/check-shared-file-reduction.ts
15809
- import { readFileSync as readFileSync45 } from "fs";
15977
+ import { readFileSync as readFileSync46 } from "fs";
15810
15978
 
15811
15979
  // src/lib/shared-file-reduction-guard.ts
15812
- import { readFileSync as readFileSync44 } from "fs";
15980
+ import { readFileSync as readFileSync45 } from "fs";
15813
15981
  var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
15814
15982
  var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
15815
15983
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
@@ -15936,7 +16104,7 @@ function formatReductionReport(report) {
15936
16104
  // src/scripts/check-shared-file-reduction.ts
15937
16105
  function readStdin() {
15938
16106
  try {
15939
- return readFileSync45(0, "utf8");
16107
+ return readFileSync46(0, "utf8");
15940
16108
  } catch {
15941
16109
  return "";
15942
16110
  }
@@ -15953,15 +16121,15 @@ function pairsFromTsv(tsv) {
15953
16121
  const [target, existingPath, incomingPath] = fields;
15954
16122
  pairs.push({
15955
16123
  target,
15956
- existing: readFileSync45(existingPath, "utf8"),
15957
- incoming: readFileSync45(incomingPath, "utf8")
16124
+ existing: readFileSync46(existingPath, "utf8"),
16125
+ incoming: readFileSync46(incomingPath, "utf8")
15958
16126
  });
15959
16127
  }
15960
16128
  return pairs;
15961
16129
  }
15962
16130
  function loadAccepted(manifestPath) {
15963
16131
  if (!manifestPath) return {};
15964
- const parsed = JSON.parse(readFileSync45(manifestPath, "utf8"));
16132
+ const parsed = JSON.parse(readFileSync46(manifestPath, "utf8"));
15965
16133
  return parsed.acceptedReductions ?? {};
15966
16134
  }
15967
16135
  async function runSharedFileReductionCheck(args) {
@@ -15969,13 +16137,13 @@ async function runSharedFileReductionCheck(args) {
15969
16137
  let accepted;
15970
16138
  try {
15971
16139
  if (args.pairs) {
15972
- pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync45(args.pairs, "utf8"));
16140
+ pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync46(args.pairs, "utf8"));
15973
16141
  } else if (args.target && args.existing && args.incoming) {
15974
16142
  pairs = [
15975
16143
  {
15976
16144
  target: args.target,
15977
- existing: readFileSync45(args.existing, "utf8"),
15978
- incoming: readFileSync45(args.incoming, "utf8")
16145
+ existing: readFileSync46(args.existing, "utf8"),
16146
+ incoming: readFileSync46(args.incoming, "utf8")
15979
16147
  }
15980
16148
  ];
15981
16149
  } else {
@@ -16009,12 +16177,12 @@ async function runSharedFileReductionCheck(args) {
16009
16177
  }
16010
16178
 
16011
16179
  // src/scripts/check-skeleton-drift.ts
16012
- import { existsSync as existsSync55, readdirSync as readdirSync28 } from "fs";
16013
- import { join as join67 } from "path";
16180
+ import { existsSync as existsSync56, readdirSync as readdirSync28 } from "fs";
16181
+ import { join as join68 } from "path";
16014
16182
 
16015
16183
  // src/lib/skeleton-drift-guard.ts
16016
- import { readFileSync as readFileSync46, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
16017
- import { join as join66 } from "path";
16184
+ import { readFileSync as readFileSync47, readdirSync as readdirSync27, statSync as statSync17 } from "fs";
16185
+ import { join as join67 } from "path";
16018
16186
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
16019
16187
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
16020
16188
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -16078,7 +16246,7 @@ function walk(dir, base = dir) {
16078
16246
  }
16079
16247
  for (const entry of entries) {
16080
16248
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
16081
- const abs = join66(dir, entry);
16249
+ const abs = join67(dir, entry);
16082
16250
  let isDir;
16083
16251
  try {
16084
16252
  isDir = statSync17(abs).isDirectory();
@@ -16100,7 +16268,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
16100
16268
  if (!rule.appliesTo(rel)) continue;
16101
16269
  let contents;
16102
16270
  try {
16103
- contents = readFileSync46(join66(skeletonRoot, rel), "utf8");
16271
+ contents = readFileSync47(join67(skeletonRoot, rel), "utf8");
16104
16272
  } catch {
16105
16273
  continue;
16106
16274
  }
@@ -16129,23 +16297,23 @@ function formatViolations2(violations) {
16129
16297
 
16130
16298
  // src/scripts/check-skeleton-drift.ts
16131
16299
  function discoverSkeletons(root) {
16132
- const skeletonsDir = join67(root, "_skeletons");
16300
+ const skeletonsDir = join68(root, "_skeletons");
16133
16301
  let entries;
16134
16302
  try {
16135
16303
  entries = readdirSync28(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
16136
16304
  } catch {
16137
16305
  return [];
16138
16306
  }
16139
- return entries.filter((name) => existsSync55(join67(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
16307
+ return entries.filter((name) => existsSync56(join68(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
16140
16308
  }
16141
16309
  async function runSkeletonDriftCheck() {
16142
16310
  const root = (await execa("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
16143
16311
  const skeletons = discoverSkeletons(root);
16144
16312
  let filesConsidered = 0;
16145
16313
  for (const name of skeletons) {
16146
- const skeletonRoot = join67(root, "_skeletons", name);
16314
+ const skeletonRoot = join68(root, "_skeletons", name);
16147
16315
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
16148
- if (existsSync55(join67(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
16316
+ if (existsSync56(join68(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
16149
16317
  filesConsidered += 1;
16150
16318
  }
16151
16319
  }
@@ -16153,7 +16321,7 @@ async function runSkeletonDriftCheck() {
16153
16321
  `audited ${skeletons.length} skeleton(s) (${skeletons.join(", ") || "none"}), ${filesConsidered} file(s) considered, under ${root}/_skeletons`
16154
16322
  );
16155
16323
  if (skeletons.length === 0) {
16156
- if (!existsSync55(join67(root, "_skeletons"))) {
16324
+ if (!existsSync56(join68(root, "_skeletons"))) {
16157
16325
  console.log(
16158
16326
  `\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.`
16159
16327
  );
@@ -16165,7 +16333,7 @@ async function runSkeletonDriftCheck() {
16165
16333
  process.exit(1);
16166
16334
  }
16167
16335
  const violations = skeletons.flatMap(
16168
- (name) => auditSkeleton(join67(root, "_skeletons", name), name)
16336
+ (name) => auditSkeleton(join68(root, "_skeletons", name), name)
16169
16337
  );
16170
16338
  if (violations.length > 0) {
16171
16339
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -16366,8 +16534,8 @@ function rawArgsAfter(subcommand) {
16366
16534
  }
16367
16535
 
16368
16536
  // src/commands/doctor.ts
16369
- import { existsSync as existsSync56, readFileSync as readFileSync47 } from "fs";
16370
- import { join as join69, resolve as resolve20 } from "path";
16537
+ import { existsSync as existsSync57, readFileSync as readFileSync48 } from "fs";
16538
+ import { join as join70, resolve as resolve20 } from "path";
16371
16539
  import chalk21 from "chalk";
16372
16540
  import { Command as Command25 } from "commander";
16373
16541
 
@@ -16708,11 +16876,11 @@ async function reapAllBareBranches(cwd, branches, worktrees, currentBranch, deps
16708
16876
 
16709
16877
  // src/lib/scratch-clone-scan.ts
16710
16878
  import { readdirSync as readdirSync29, statSync as statSync18 } from "fs";
16711
- import { join as join68 } from "path";
16879
+ import { join as join69 } from "path";
16712
16880
  var INTEGRATION_BRANCH = "dev";
16713
16881
  function isPlainCloneDir(path) {
16714
16882
  try {
16715
- return statSync18(join68(path, ".git")).isDirectory();
16883
+ return statSync18(join69(path, ".git")).isDirectory();
16716
16884
  } catch {
16717
16885
  return false;
16718
16886
  }
@@ -16727,7 +16895,7 @@ async function findScratchCloneCandidates(estateRoot, deps) {
16727
16895
  const names = readdirSync29(estateRoot, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
16728
16896
  const candidates = [];
16729
16897
  for (const name of names) {
16730
- const path = join68(estateRoot, name);
16898
+ const path = join69(estateRoot, name);
16731
16899
  if (!isPlainCloneDir(path)) continue;
16732
16900
  let branch;
16733
16901
  try {
@@ -16907,10 +17075,10 @@ function printScratchCloneReports(estateRoot, reports) {
16907
17075
  );
16908
17076
  }
16909
17077
  function readLocalCoreVersion(cwd) {
16910
- const path = join69(cwd, INSTANCE_CORE_FILE);
16911
- if (!existsSync56(path)) return null;
17078
+ const path = join70(cwd, INSTANCE_CORE_FILE);
17079
+ if (!existsSync57(path)) return null;
16912
17080
  try {
16913
- return extractVersionField(readFileSync47(path, "utf8"));
17081
+ return extractVersionField(readFileSync48(path, "utf8"));
16914
17082
  } catch {
16915
17083
  return null;
16916
17084
  }
@@ -16930,10 +17098,10 @@ function extractVersionField(contents) {
16930
17098
  return match?.[1] ?? null;
16931
17099
  }
16932
17100
  function readFossil(cwd) {
16933
- const path = join69(cwd, CORE_VERSION_FILE);
16934
- if (!existsSync56(path)) return null;
17101
+ const path = join70(cwd, CORE_VERSION_FILE);
17102
+ if (!existsSync57(path)) return null;
16935
17103
  try {
16936
- const value = readFileSync47(path, "utf8").trim();
17104
+ const value = readFileSync48(path, "utf8").trim();
16937
17105
  return value === "" ? null : value;
16938
17106
  } catch {
16939
17107
  return null;
@@ -17471,13 +17639,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
17471
17639
  import { Command as Command27 } from "commander";
17472
17640
 
17473
17641
  // src/lib/packaged-scripts.ts
17474
- import { existsSync as existsSync57 } from "fs";
17475
- import { dirname as dirname11, join as join70 } from "path";
17642
+ import { existsSync as existsSync58 } from "fs";
17643
+ import { dirname as dirname11, join as join71 } from "path";
17476
17644
  function findPackagedScript(startDir, relativePath) {
17477
17645
  let dir = startDir;
17478
17646
  for (; ; ) {
17479
- const candidate = join70(dir, relativePath);
17480
- if (existsSync57(candidate)) return candidate;
17647
+ const candidate = join71(dir, relativePath);
17648
+ if (existsSync58(candidate)) return candidate;
17481
17649
  const parent = dirname11(dir);
17482
17650
  if (parent === dir) return null;
17483
17651
  dir = parent;