@biffo/cli 0.287.12 → 0.288.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +531 -442
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -570,8 +570,8 @@ async function runCoreStatus(options) {
570
570
 
571
571
  // src/commands/core-upgrade.ts
572
572
  import { execSync as execSync2 } from "child_process";
573
- import { existsSync as existsSync14, rmSync as rmSync5 } from "fs";
574
- import { join as join15, resolve as resolve3 } from "path";
573
+ import { existsSync as existsSync15, rmSync as rmSync5 } from "fs";
574
+ import { join as join16, resolve as resolve3 } from "path";
575
575
  import chalk4 from "chalk";
576
576
  import { execa as execa3 } from "execa";
577
577
  import { Command as Command3 } from "commander";
@@ -2961,9 +2961,48 @@ function findNewUndeclaredSeams(baseDir, theirsDir, oursDir, portalRelDir = "app
2961
2961
  return undeclared.sort((a, b) => a.specifier.localeCompare(b.specifier));
2962
2962
  }
2963
2963
 
2964
- // src/lib/breaking-changes.ts
2964
+ // src/lib/instance-adoption.ts
2965
2965
  import { existsSync as existsSync10, readFileSync as readFileSync8 } from "fs";
2966
2966
  import { join as join11 } from "path";
2967
+ var REGISTERED_ADOPTION_PAIRS = [
2968
+ {
2969
+ id: "core-api-environment",
2970
+ description: '`infra/environments/dev/core-api-environment.core.tf` declares `local.core_api_environment` (BIFFO_PLUGIN_MEDIA_BUCKET, BIFFO_PR_SIGNER_FUNCTION_NAME, ...), but `module "core_api"` is a user-owned block Terraform cannot inject an argument into from another file \u2014 so the channel only takes effect once `main.tf` itself merges it in (#1538/#1540/#1579).',
2971
+ templateFile: "infra/environments/dev/core-api-environment.core.tf",
2972
+ userFile: "infra/environments/dev/main.tf",
2973
+ adoptedPattern: /environment_variables\s*=\s*merge\(\s*local\.core_api_environment\s*,/,
2974
+ remedy: 'In `module "core_api"` (infra/environments/dev/main.tf), change `environment_variables = {` to `environment_variables = merge(local.core_api_environment, {` and close the extra paren on the block\u2019s closing `}` \u2014 then `terraform apply`.'
2975
+ }
2976
+ ];
2977
+ function isAdopted(pair, userFileContent) {
2978
+ if (userFileContent === null) return false;
2979
+ return pair.adoptedPattern.test(userFileContent);
2980
+ }
2981
+ function checkInstanceAdoption(theirsDir, oursDir, pairs = REGISTERED_ADOPTION_PAIRS) {
2982
+ const findings = [];
2983
+ let applicablePairs = 0;
2984
+ for (const pair of pairs) {
2985
+ const shipped = existsSync10(join11(theirsDir, pair.templateFile));
2986
+ if (!shipped) {
2987
+ findings.push({ pair, status: "not-applicable" });
2988
+ continue;
2989
+ }
2990
+ applicablePairs++;
2991
+ const userPath = join11(oursDir, pair.userFile);
2992
+ const content = existsSync10(userPath) ? readFileSync8(userPath, "utf8") : null;
2993
+ findings.push({ pair, status: isAdopted(pair, content) ? "adopted" : "unadopted" });
2994
+ }
2995
+ return {
2996
+ examinedInstances: 1,
2997
+ registeredPairs: pairs.length,
2998
+ applicablePairs,
2999
+ findings
3000
+ };
3001
+ }
3002
+
3003
+ // src/lib/breaking-changes.ts
3004
+ import { existsSync as existsSync11, readFileSync as readFileSync9 } from "fs";
3005
+ import { join as join12 } from "path";
2967
3006
  var UPGRADE_GUIDE_PATH = "docs/guides/core-upgrade.md";
2968
3007
  var SECTION_HEADING = "## Breaking changes by version";
2969
3008
  var ENTRY_HEADING = /^###\s+(\d+\.\d+\.\d+)\s*[—-]\s*(.+?)\s*$/;
@@ -2988,9 +3027,9 @@ function parseBreakingChanges(guide) {
2988
3027
  return entries;
2989
3028
  }
2990
3029
  function readBreakingChanges(templateRoot) {
2991
- const path = join11(templateRoot, UPGRADE_GUIDE_PATH);
2992
- if (!existsSync10(path)) return [];
2993
- return parseBreakingChanges(readFileSync8(path, "utf8"));
3030
+ const path = join12(templateRoot, UPGRADE_GUIDE_PATH);
3031
+ if (!existsSync11(path)) return [];
3032
+ return parseBreakingChanges(readFileSync9(path, "utf8"));
2994
3033
  }
2995
3034
  function breakingChangesBetween(from, to, entries) {
2996
3035
  parseCoreVersion(from);
@@ -3008,15 +3047,15 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
3008
3047
 
3009
3048
  // src/lib/plugin-terraform-wiring.ts
3010
3049
  import {
3011
- existsSync as existsSync11,
3050
+ existsSync as existsSync12,
3012
3051
  mkdirSync as mkdirSync3,
3013
- readFileSync as readFileSync9,
3052
+ readFileSync as readFileSync10,
3014
3053
  readdirSync as readdirSync3,
3015
3054
  rmSync as rmSync4,
3016
3055
  statSync as statSync2,
3017
3056
  writeFileSync as writeFileSync4
3018
3057
  } from "fs";
3019
- import { join as join12, relative as relative2, sep as sep2 } from "path";
3058
+ import { join as join13, relative as relative2, sep as sep2 } from "path";
3020
3059
  var TEMPLATE_MODULE_DIR = "_template";
3021
3060
  var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
3022
3061
  var GENERATED_TF_FILE = "plugins.generated.tf";
@@ -3070,7 +3109,7 @@ function standardArguments(pluginName, handler) {
3070
3109
  ];
3071
3110
  }
3072
3111
  function listPluginModules(cwd) {
3073
- const dir = join12(cwd, "modules", "plugins");
3112
+ const dir = join13(cwd, "modules", "plugins");
3074
3113
  let entries;
3075
3114
  try {
3076
3115
  entries = readdirSync3(dir, { withFileTypes: true });
@@ -3082,7 +3121,7 @@ function listPluginModules(cwd) {
3082
3121
  var FIRST_PARTY_TERRAFORM = (name) => `../../../services/_plugins/${name}/terraform`;
3083
3122
  var THIRD_PARTY_TERRAFORM = (name) => `../../../modules/plugins/${name}`;
3084
3123
  function isFirstPartyPlugin(cwd, name) {
3085
- return existsSync11(join12(cwd, "services", "_plugins", name, "terraform", "main.tf"));
3124
+ return existsSync12(join13(cwd, "services", "_plugins", name, "terraform", "main.tf"));
3086
3125
  }
3087
3126
  function pluginModuleSource(cwd, name) {
3088
3127
  return isFirstPartyPlugin(cwd, name) ? FIRST_PARTY_TERRAFORM(name) : THIRD_PARTY_TERRAFORM(name);
@@ -3091,7 +3130,7 @@ function listWireablePlugins(cwd) {
3091
3130
  return listPluginModules(cwd).filter((name) => !isFirstPartyPlugin(cwd, name)).sort();
3092
3131
  }
3093
3132
  function firstPartyPluginNames(cwd) {
3094
- const dir = join12(cwd, "services", "_plugins");
3133
+ const dir = join13(cwd, "services", "_plugins");
3095
3134
  let entries;
3096
3135
  try {
3097
3136
  entries = readdirSync3(dir, { withFileTypes: true });
@@ -3114,7 +3153,7 @@ function walkTfFiles(root) {
3114
3153
  return;
3115
3154
  }
3116
3155
  for (const entry of entries) {
3117
- const p = join12(dir, entry);
3156
+ const p = join13(dir, entry);
3118
3157
  let st;
3119
3158
  try {
3120
3159
  st = statSync2(p);
@@ -3135,7 +3174,7 @@ function escapeRegExp(value) {
3135
3174
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3136
3175
  }
3137
3176
  function findPluginModuleReferences(cwd, name) {
3138
- const infraDir = join12(cwd, "infra");
3177
+ const infraDir = join13(cwd, "infra");
3139
3178
  const sourceSegments = ["modules", "plugins", name];
3140
3179
  const sourceLinePattern = /^\s*source\s*=\s*"([^"]+)"/;
3141
3180
  const moduleRefPattern = new RegExp(`module\\.plugin_${escapeRegExp(name)}(?![A-Za-z0-9_-])`);
@@ -3143,7 +3182,7 @@ function findPluginModuleReferences(cwd, name) {
3143
3182
  for (const absPath of walkTfFiles(infraDir)) {
3144
3183
  let contents;
3145
3184
  try {
3146
- contents = readFileSync9(absPath, "utf8");
3185
+ contents = readFileSync10(absPath, "utf8");
3147
3186
  } catch {
3148
3187
  continue;
3149
3188
  }
@@ -3161,7 +3200,7 @@ function findPluginModuleReferences(cwd, name) {
3161
3200
  return refs;
3162
3201
  }
3163
3202
  function listEnvironments(cwd) {
3164
- const dir = join12(cwd, "infra", "environments");
3203
+ const dir = join13(cwd, "infra", "environments");
3165
3204
  let entries;
3166
3205
  try {
3167
3206
  entries = readdirSync3(dir, { withFileTypes: true });
@@ -3169,12 +3208,12 @@ function listEnvironments(cwd) {
3169
3208
  return [];
3170
3209
  }
3171
3210
  return entries.filter((e) => {
3172
- if (!e.isDirectory() || !existsSync11(join12(dir, e.name, "main.tf"))) return false;
3173
- return declaredVariables(join12(dir, e.name)).has("enabled_plugins");
3211
+ if (!e.isDirectory() || !existsSync12(join13(dir, e.name, "main.tf"))) return false;
3212
+ return declaredVariables(join13(dir, e.name)).has("enabled_plugins");
3174
3213
  }).map((e) => e.name).sort();
3175
3214
  }
3176
3215
  function listUnwirableEnvironments(cwd) {
3177
- const dir = join12(cwd, "infra", "environments");
3216
+ const dir = join13(cwd, "infra", "environments");
3178
3217
  let entries;
3179
3218
  try {
3180
3219
  entries = readdirSync3(dir, { withFileTypes: true });
@@ -3182,7 +3221,7 @@ function listUnwirableEnvironments(cwd) {
3182
3221
  return [];
3183
3222
  }
3184
3223
  return entries.filter(
3185
- (e) => e.isDirectory() && existsSync11(join12(dir, e.name, "main.tf")) && !declaredVariables(join12(dir, e.name)).has("enabled_plugins")
3224
+ (e) => e.isDirectory() && existsSync12(join13(dir, e.name, "main.tf")) && !declaredVariables(join13(dir, e.name)).has("enabled_plugins")
3186
3225
  ).map((e) => e.name).sort();
3187
3226
  }
3188
3227
  function declaredVariables(moduleDir) {
@@ -3197,7 +3236,7 @@ function declaredVariables(moduleDir) {
3197
3236
  if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
3198
3237
  let contents;
3199
3238
  try {
3200
- contents = readFileSync9(join12(moduleDir, entry.name), "utf8");
3239
+ contents = readFileSync10(join13(moduleDir, entry.name), "utf8");
3201
3240
  } catch {
3202
3241
  continue;
3203
3242
  }
@@ -3219,7 +3258,7 @@ function declaredOutputs(moduleDir) {
3219
3258
  if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
3220
3259
  let contents;
3221
3260
  try {
3222
- contents = readFileSync9(join12(moduleDir, entry.name), "utf8");
3261
+ contents = readFileSync10(join13(moduleDir, entry.name), "utf8");
3223
3262
  } catch {
3224
3263
  continue;
3225
3264
  }
@@ -3301,7 +3340,7 @@ function syncPluginTerraform(cwd) {
3301
3340
  const skippedEnvironments = listUnwirableEnvironments(cwd);
3302
3341
  const changedPaths = [];
3303
3342
  const rendered = plugins.map((name) => {
3304
- const moduleDir = join12(cwd, "modules", "plugins", name);
3343
+ const moduleDir = join13(cwd, "modules", "plugins", name);
3305
3344
  return {
3306
3345
  name,
3307
3346
  declaredVariables: declaredVariables(moduleDir),
@@ -3310,16 +3349,16 @@ function syncPluginTerraform(cwd) {
3310
3349
  };
3311
3350
  });
3312
3351
  for (const env of environments) {
3313
- const envDir = join12(cwd, "infra", "environments", env);
3314
- const tfPath = join12(envDir, GENERATED_TF_FILE);
3315
- const tfvarsPath = join12(envDir, GENERATED_TFVARS_FILE);
3352
+ const envDir = join13(cwd, "infra", "environments", env);
3353
+ const tfPath = join13(envDir, GENERATED_TF_FILE);
3354
+ const tfvarsPath = join13(envDir, GENERATED_TFVARS_FILE);
3316
3355
  const relBase = `infra/environments/${env}`;
3317
3356
  if (plugins.length === 0) {
3318
3357
  for (const [abs, rel] of [
3319
3358
  [tfPath, `${relBase}/${GENERATED_TF_FILE}`],
3320
3359
  [tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
3321
3360
  ]) {
3322
- if (existsSync11(abs)) {
3361
+ if (existsSync12(abs)) {
3323
3362
  rmSync4(abs);
3324
3363
  changedPaths.push(rel);
3325
3364
  }
@@ -3335,8 +3374,8 @@ function syncPluginTerraform(cwd) {
3335
3374
  }
3336
3375
 
3337
3376
  // src/lib/lockfile-refresh.ts
3338
- import { existsSync as existsSync12 } from "fs";
3339
- import { join as join13 } from "path";
3377
+ import { existsSync as existsSync13 } from "fs";
3378
+ import { join as join14 } from "path";
3340
3379
  var LOCKFILE_TRIGGERS = [
3341
3380
  {
3342
3381
  manifest: "package.json",
@@ -3359,7 +3398,7 @@ function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_
3359
3398
  const locked = changedPaths.filter((p) => !isForeignManifest(p));
3360
3399
  return triggers.filter((t) => {
3361
3400
  const touched = locked.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
3362
- return touched && existsSync12(join13(instanceDir, t.lockfile));
3401
+ return touched && existsSync13(join14(instanceDir, t.lockfile));
3363
3402
  });
3364
3403
  }
3365
3404
  async function refreshLockfiles(instanceDir, triggers, run) {
@@ -3379,11 +3418,11 @@ function describeFailures(outcomes) {
3379
3418
  }
3380
3419
 
3381
3420
  // src/lib/instance-dependency-install.ts
3382
- import { existsSync as existsSync13 } from "fs";
3383
- import { join as join14 } from "path";
3421
+ import { existsSync as existsSync14 } from "fs";
3422
+ import { join as join15 } from "path";
3384
3423
  function dependencyInstallSteps(instanceDir) {
3385
3424
  const steps = [{ ecosystem: "pnpm", command: ["pnpm", "install"] }];
3386
- if (existsSync13(join14(instanceDir, "pyproject.toml"))) {
3425
+ if (existsSync14(join15(instanceDir, "pyproject.toml"))) {
3387
3426
  steps.push({ ecosystem: "uv", command: ["uv", "sync"] });
3388
3427
  }
3389
3428
  return steps;
@@ -3595,6 +3634,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
3595
3634
  });
3596
3635
  const orphanRatchet = checkOrphanRatchet(plan.orphaned.length, readOrphanBaseline(options.cwd));
3597
3636
  const newSeams = findNewUndeclaredSeams(baseDir, theirsDir, options.cwd);
3637
+ const adoption = checkInstanceAdoption(theirsDir, options.cwd);
3598
3638
  const migrations = planMigrationCarry({
3599
3639
  templateDir: theirsDir,
3600
3640
  instanceDir: options.cwd,
@@ -3621,6 +3661,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
3621
3661
  throw new Error(fidelityFailure(fidelity, toVersion));
3622
3662
  }
3623
3663
  printNewInstanceSeams(newSeams);
3664
+ printAdoptionReport(adoption);
3624
3665
  printOrphanReport(plan.orphaned, orphanRatchet);
3625
3666
  if (orphanRatchet.increased) {
3626
3667
  throw new Error(
@@ -3679,10 +3720,11 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
3679
3720
  theirsDir,
3680
3721
  coreVersionCleanup,
3681
3722
  orphanRatchet,
3682
- newSeams
3723
+ newSeams,
3724
+ adoption
3683
3725
  );
3684
3726
  }
3685
- async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams) {
3727
+ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams, adoption) {
3686
3728
  if (breaking.length > 0 && !options.acknowledgeBreaking) {
3687
3729
  throw new Error(
3688
3730
  `This upgrade crosses ${breaking.length} documented breaking change(s): ${breaking.map((b) => b.version).join(", ")}. They are printed above and in ${UPGRADE_GUIDE_PATH}. Read what each one requires \u2014 some destroy data or need manual work after the deploy \u2014 then re-run with --acknowledge-breaking.`
@@ -3715,6 +3757,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
3715
3757
  coreVersionCleanup,
3716
3758
  orphanRatchet,
3717
3759
  newSeams,
3760
+ adoption,
3718
3761
  branch,
3719
3762
  token
3720
3763
  );
@@ -3749,7 +3792,7 @@ async function restoreCallerBranch(git, cwd, callerBranch, upgradeBranch) {
3749
3792
  );
3750
3793
  }
3751
3794
  }
3752
- async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams, branch, token) {
3795
+ async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion, toVersion, breaking, theirsDir, coreVersionCleanup, orphanRatchet, newSeams, adoption, branch, token) {
3753
3796
  const { git } = deps;
3754
3797
  const applied = applyUpgradePlan(options.cwd, plan, theirsDir);
3755
3798
  const carried = applyMigrationCarry(options.cwd, migrations);
@@ -3762,7 +3805,7 @@ async function buildCommitAndOpenPr(options, deps, plan, migrations, fromVersion
3762
3805
  );
3763
3806
  }
3764
3807
  const cleanedCoreVersion = coreVersionCleanup?.action === "delete";
3765
- if (cleanedCoreVersion && existsSync14(coreVersionCleanup.path)) {
3808
+ if (cleanedCoreVersion && existsSync15(coreVersionCleanup.path)) {
3766
3809
  rmSync5(coreVersionCleanup.path);
3767
3810
  log.info(
3768
3811
  coreVersionCleanup.stale ? `Deleted stale ${CORE_VERSION_FILE} (recorded ${coreVersionCleanup.found}, behind the version biffo.core.json records \u2014 this instance has moved past it) (#842).` : `Deleted orphaned ${CORE_VERSION_FILE} (inherited copy recording ${coreVersionCleanup.found}, superseded by biffo.core.json) \u2014 nothing reads it as an authority (#434).`
@@ -3819,7 +3862,8 @@ ${marker}` : "this upgrade carries no template PRs, so no provenance marker is n
3819
3862
  cleanedCoreVersion ? coreVersionCleanup : null,
3820
3863
  carriedPrs,
3821
3864
  newSeams,
3822
- installFailures
3865
+ installFailures,
3866
+ adoption.findings
3823
3867
  )
3824
3868
  });
3825
3869
  if (plan.conflicts.length > 0) {
@@ -3875,7 +3919,7 @@ function carriedPrNumbers(subjects) {
3875
3919
  }
3876
3920
  return [...new Set(numbers)].sort((a, b) => a - b);
3877
3921
  }
3878
- function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = [], breaking = [], coreVersionCleanup = null, carriedPrs = [], newSeams = [], installFailures = []) {
3922
+ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = [], breaking = [], coreVersionCleanup = null, carriedPrs = [], newSeams = [], installFailures = [], adoptionFindings = []) {
3879
3923
  const lines = [];
3880
3924
  if (breaking.length > 0) {
3881
3925
  lines.push(
@@ -3907,6 +3951,27 @@ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, loc
3907
3951
  ""
3908
3952
  );
3909
3953
  }
3954
+ const unadopted = adoptionFindings.filter((f) => f.status === "unadopted");
3955
+ if (unadopted.length > 0) {
3956
+ lines.push(
3957
+ `## \u26A0 ${unadopted.length} instance adoption gap(s) \u2014 shipped but not consumed (#1538/#1570)`,
3958
+ "",
3959
+ "The template-owned file(s) below already exist in this instance, but the **user-owned** file that must read them has not been edited to do so. The mechanism ships, CI is green, and it does nothing at runtime \u2014 `biffo core upgrade` cannot fix this itself (the file it would need to edit is user-owned), so it is surfaced here on every upgrade until it is adopted by hand.",
3960
+ "",
3961
+ ...unadopted.flatMap((f) => [
3962
+ `### \`${f.pair.id}\``,
3963
+ "",
3964
+ f.pair.description,
3965
+ "",
3966
+ `- Template-owned: \`${f.pair.templateFile}\` (already present in this instance)`,
3967
+ `- Needs editing: \`${f.pair.userFile}\``,
3968
+ `- Fix: ${f.pair.remedy}`,
3969
+ ""
3970
+ ]),
3971
+ "---",
3972
+ ""
3973
+ );
3974
+ }
3910
3975
  lines.push(
3911
3976
  "Automated core upgrade generated by `biffo core upgrade` (ADR-0006).",
3912
3977
  "",
@@ -4107,6 +4172,30 @@ function printNewInstanceSeams(seams) {
4107
4172
  }
4108
4173
  console.log();
4109
4174
  }
4175
+ function printAdoptionReport(report) {
4176
+ console.log(
4177
+ chalk4.dim(
4178
+ ` Instance adoption: examined ${String(report.examinedInstances)} instance against ${String(report.registeredPairs)} registered pair(s), ${String(report.applicablePairs)} applicable to this upgrade.`
4179
+ )
4180
+ );
4181
+ const unadopted = report.findings.filter((f) => f.status === "unadopted");
4182
+ if (unadopted.length === 0) {
4183
+ console.log();
4184
+ return;
4185
+ }
4186
+ console.log(
4187
+ chalk4.red.bold(
4188
+ ` \u26A0 ${String(unadopted.length)} adoption gap(s) \u2014 shipped but not consumed (#1538/#1570):`
4189
+ )
4190
+ );
4191
+ for (const f of unadopted) {
4192
+ console.log(
4193
+ ` ${chalk4.bold(f.pair.id)} \u2014 ${chalk4.yellow(f.pair.userFile)} does not consume ` + chalk4.yellow(f.pair.templateFile)
4194
+ );
4195
+ console.log(chalk4.dim(` ${f.pair.remedy}`));
4196
+ }
4197
+ console.log();
4198
+ }
4110
4199
  function printTargetFidelity(report, toVersion) {
4111
4200
  if (report.unverifiable !== null) {
4112
4201
  console.log(chalk4.yellow(` Target fidelity: NOT VERIFIED \u2014 ${report.unverifiable}`));
@@ -4223,8 +4312,8 @@ function printBreakingChanges(breaking, applying) {
4223
4312
  }
4224
4313
  function versionOfCheckout(dir, explicit) {
4225
4314
  if (explicit) return explicit;
4226
- const file = join15(dir, CORE_VERSION_FILE);
4227
- if (existsSync14(file)) return readCoreVersionFile(file);
4315
+ const file = join16(dir, CORE_VERSION_FILE);
4316
+ if (existsSync15(file)) return readCoreVersionFile(file);
4228
4317
  throw new Error(
4229
4318
  `Cannot determine the core version of ${dir}: it has no ${CORE_VERSION_FILE}, and a checkout supplied explicitly is not resolved from a tag. Pass --to to state which version this tree is.`
4230
4319
  );
@@ -4232,8 +4321,8 @@ function versionOfCheckout(dir, explicit) {
4232
4321
  function latestCoreVersion(repo) {
4233
4322
  const fromTags = latestCoreVersionFromTags(repo);
4234
4323
  if (fromTags) return fromTags;
4235
- const file = join15(repo, CORE_VERSION_FILE);
4236
- if (existsSync14(file)) return readCoreVersionFile(file);
4324
+ const file = join16(repo, CORE_VERSION_FILE);
4325
+ if (existsSync15(file)) return readCoreVersionFile(file);
4237
4326
  throw new Error(
4238
4327
  `Cannot determine the template's core version: ${repo} has no core-v* tags and no ${CORE_VERSION_FILE}. Fetch tags (\`git fetch --tags\`) or pass --to explicitly.`
4239
4328
  );
@@ -4251,7 +4340,7 @@ coreCommand.addCommand(coreUpgradeCommand);
4251
4340
  import { Command as Command8 } from "commander";
4252
4341
 
4253
4342
  // src/commands/data-apply.ts
4254
- import { existsSync as existsSync16, readFileSync as readFileSync11 } from "fs";
4343
+ import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
4255
4344
  import { resolve as resolve4 } from "path";
4256
4345
  import chalk5 from "chalk";
4257
4346
  import { Command as Command5 } from "commander";
@@ -4702,16 +4791,16 @@ function isTemplatePlaceholderConfig(raw) {
4702
4791
 
4703
4792
  // src/lib/session.ts
4704
4793
  import {
4705
- existsSync as existsSync15,
4794
+ existsSync as existsSync16,
4706
4795
  mkdirSync as mkdirSync4,
4707
4796
  readdirSync as readdirSync4,
4708
- readFileSync as readFileSync10,
4797
+ readFileSync as readFileSync11,
4709
4798
  rmSync as rmSync6,
4710
4799
  statSync as statSync3,
4711
4800
  writeFileSync as writeFileSync5
4712
4801
  } from "fs";
4713
4802
  import { homedir } from "os";
4714
- import { join as join16 } from "path";
4803
+ import { join as join17 } from "path";
4715
4804
  var LEGACY_STEP_ALIASES = {
4716
4805
  github_config: ["github_branches", "github_instance_files", "github_settings"]
4717
4806
  };
@@ -4720,39 +4809,39 @@ function hasCompleted(session, step) {
4720
4809
  return session.completedSteps.some((done) => LEGACY_STEP_ALIASES[done]?.includes(step) ?? false);
4721
4810
  }
4722
4811
  function sessionsDir() {
4723
- return process.env["BIFFO_SESSIONS_DIR"] ?? join16(homedir(), ".biffo", "sessions");
4812
+ return process.env["BIFFO_SESSIONS_DIR"] ?? join17(homedir(), ".biffo", "sessions");
4724
4813
  }
4725
4814
  function sessionPath(projectName) {
4726
- return join16(sessionsDir(), `${projectName}.json`);
4815
+ return join17(sessionsDir(), `${projectName}.json`);
4727
4816
  }
4728
4817
  function loadSession(projectName) {
4729
4818
  const path = sessionPath(projectName);
4730
- if (!existsSync15(path)) return null;
4819
+ if (!existsSync16(path)) return null;
4731
4820
  try {
4732
- return JSON.parse(readFileSync10(path, "utf8"));
4821
+ return JSON.parse(readFileSync11(path, "utf8"));
4733
4822
  } catch {
4734
4823
  return null;
4735
4824
  }
4736
4825
  }
4737
4826
  function findLatestSession() {
4738
4827
  const dir = sessionsDir();
4739
- if (!existsSync15(dir)) return null;
4828
+ if (!existsSync16(dir)) return null;
4740
4829
  const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
4741
4830
  if (files.length === 0) return null;
4742
4831
  const sorted = files.map((f) => {
4743
- const fullPath = join16(dir, f);
4744
- const mtime = existsSync15(fullPath) ? statSync3(fullPath).mtimeMs : -1;
4832
+ const fullPath = join17(dir, f);
4833
+ const mtime = existsSync16(fullPath) ? statSync3(fullPath).mtimeMs : -1;
4745
4834
  return { f, mtime };
4746
4835
  }).sort((a, b) => b.mtime - a.mtime);
4747
4836
  try {
4748
- return JSON.parse(readFileSync10(join16(dir, sorted[0].f), "utf8"));
4837
+ return JSON.parse(readFileSync11(join17(dir, sorted[0].f), "utf8"));
4749
4838
  } catch {
4750
4839
  return null;
4751
4840
  }
4752
4841
  }
4753
4842
  function saveSession(session) {
4754
4843
  const dir = sessionsDir();
4755
- if (!existsSync15(dir)) mkdirSync4(dir, { recursive: true });
4844
+ if (!existsSync16(dir)) mkdirSync4(dir, { recursive: true });
4756
4845
  const name = session.config.project?.name ?? "unknown";
4757
4846
  const prior = loadSession(name);
4758
4847
  if (prior) {
@@ -4774,36 +4863,36 @@ function markStepComplete(session, step) {
4774
4863
  }
4775
4864
  function deleteSession(projectName) {
4776
4865
  const path = sessionPath(projectName);
4777
- if (existsSync15(path)) rmSync6(path);
4866
+ if (existsSync16(path)) rmSync6(path);
4778
4867
  }
4779
4868
  function projectsDir() {
4780
- return process.env["BIFFO_PROJECTS_DIR"] ?? join16(homedir(), ".biffo", "projects");
4869
+ return process.env["BIFFO_PROJECTS_DIR"] ?? join17(homedir(), ".biffo", "projects");
4781
4870
  }
4782
4871
  function saveProjectConfig(config) {
4783
4872
  const dir = projectsDir();
4784
- if (!existsSync15(dir)) mkdirSync4(dir, { recursive: true });
4785
- writeFileSync5(join16(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
4873
+ if (!existsSync16(dir)) mkdirSync4(dir, { recursive: true });
4874
+ writeFileSync5(join17(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
4786
4875
  }
4787
4876
  function loadProjectConfig(name) {
4788
- const path = join16(projectsDir(), `${name}.json`);
4789
- if (!existsSync15(path)) return null;
4877
+ const path = join17(projectsDir(), `${name}.json`);
4878
+ if (!existsSync16(path)) return null;
4790
4879
  try {
4791
- const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync10(path, "utf8")));
4880
+ const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync11(path, "utf8")));
4792
4881
  return result.success ? result.data : null;
4793
4882
  } catch {
4794
4883
  return null;
4795
4884
  }
4796
4885
  }
4797
4886
  function deleteProjectConfig(name) {
4798
- const path = join16(projectsDir(), `${name}.json`);
4799
- if (existsSync15(path)) rmSync6(path);
4887
+ const path = join17(projectsDir(), `${name}.json`);
4888
+ if (existsSync16(path)) rmSync6(path);
4800
4889
  }
4801
4890
  function listProjectConfigs() {
4802
4891
  const dir = projectsDir();
4803
- if (!existsSync15(dir)) return [];
4892
+ if (!existsSync16(dir)) return [];
4804
4893
  return readdirSync4(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
4805
4894
  try {
4806
- const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync10(join16(dir, f), "utf8")));
4895
+ const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync11(join17(dir, f), "utf8")));
4807
4896
  return result.success ? [result.data] : [];
4808
4897
  } catch {
4809
4898
  return [];
@@ -4872,7 +4961,7 @@ async function runDataApply(name, environment, config, aws) {
4872
4961
  }
4873
4962
  async function resolveConfig(options) {
4874
4963
  if (options.config) {
4875
- const raw = JSON.parse(readFileSync11(resolve4(options.config), "utf8"));
4964
+ const raw = JSON.parse(readFileSync12(resolve4(options.config), "utf8"));
4876
4965
  const result = BiffoConfigSchema.safeParse(raw);
4877
4966
  if (!result.success) {
4878
4967
  log.error(`Invalid config at ${options.config}:`);
@@ -4892,8 +4981,8 @@ async function resolveConfig(options) {
4892
4981
  return cfg;
4893
4982
  }
4894
4983
  const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
4895
- if (existsSync16(localConfigPath)) {
4896
- const raw = JSON.parse(readFileSync11(localConfigPath, "utf8"));
4984
+ if (existsSync17(localConfigPath)) {
4985
+ const raw = JSON.parse(readFileSync12(localConfigPath, "utf8"));
4897
4986
  const result = BiffoConfigSchema.safeParse(raw);
4898
4987
  if (result.success) return result.data;
4899
4988
  if (isTemplatePlaceholderConfig(raw)) {
@@ -4939,8 +5028,8 @@ async function resolveConfig(options) {
4939
5028
 
4940
5029
  // src/commands/data-import.ts
4941
5030
  import { execSync as execSync3 } from "child_process";
4942
- import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4943
- import { join as join17, resolve as resolve5 } from "path";
5031
+ import { cpSync, existsSync as existsSync18, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
5032
+ import { join as join18, resolve as resolve5 } from "path";
4944
5033
  import chalk6 from "chalk";
4945
5034
  import { Command as Command6 } from "commander";
4946
5035
  import inquirer2 from "inquirer";
@@ -4980,23 +5069,23 @@ async function runDataImport(name, options, deps) {
4980
5069
  `Invalid import name '${name}'. Use lowercase letters, numbers, and hyphens, starting with a letter.`
4981
5070
  );
4982
5071
  }
4983
- const servicesDir = join17(options.cwd, "services");
4984
- if (!existsSync17(servicesDir)) {
5072
+ const servicesDir = join18(options.cwd, "services");
5073
+ if (!existsSync18(servicesDir)) {
4985
5074
  throw new Error(
4986
5075
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
4987
5076
  );
4988
5077
  }
4989
- const targetDir = join17(options.cwd, "db", "imports", name);
4990
- if (existsSync17(targetDir)) {
5078
+ const targetDir = join18(options.cwd, "db", "imports", name);
5079
+ if (existsSync18(targetDir)) {
4991
5080
  throw new Error(
4992
5081
  `DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
4993
5082
  );
4994
5083
  }
4995
- const isLocalDir = existsSync17(options.source) && statSync4(options.source).isDirectory();
5084
+ const isLocalDir = existsSync18(options.source) && statSync4(options.source).isDirectory();
4996
5085
  let sourceDir;
4997
5086
  let cleanupClone = null;
4998
5087
  if (isLocalDir) {
4999
- sourceDir = options.path ? join17(options.source, options.path) : options.source;
5088
+ sourceDir = options.path ? join18(options.source, options.path) : options.source;
5000
5089
  } else {
5001
5090
  const token = options.token ?? await resolveDdlImportToken();
5002
5091
  log.info(`Cloning ${options.source}...`);
@@ -5004,10 +5093,10 @@ async function runDataImport(name, options, deps) {
5004
5093
  cleanupClone = () => {
5005
5094
  deps.git.cleanup(tmpDir);
5006
5095
  };
5007
- sourceDir = options.path ? join17(tmpDir, options.path) : tmpDir;
5096
+ sourceDir = options.path ? join18(tmpDir, options.path) : tmpDir;
5008
5097
  }
5009
5098
  try {
5010
- if (!existsSync17(sourceDir)) {
5099
+ if (!existsSync18(sourceDir)) {
5011
5100
  throw new Error(`Source directory does not exist: ${sourceDir}`);
5012
5101
  }
5013
5102
  const sqlFiles = readdirSync5(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
@@ -5032,7 +5121,7 @@ async function runDataImport(name, options, deps) {
5032
5121
  }
5033
5122
  mkdirSync5(targetDir, { recursive: true });
5034
5123
  for (const file of sqlFiles) {
5035
- cpSync(join17(sourceDir, file), join17(targetDir, file));
5124
+ cpSync(join18(sourceDir, file), join18(targetDir, file));
5036
5125
  }
5037
5126
  log.success(`Imported ${String(sqlFiles.length)} .sql file(s) to db/imports/${name}/`);
5038
5127
  const commitMessage = `feat(data): import ${name} (${String(sqlFiles.length)} SQL file(s))`;
@@ -5084,8 +5173,8 @@ function printDryRun(name, sqlFiles) {
5084
5173
  }
5085
5174
 
5086
5175
  // src/commands/data-list.ts
5087
- import { existsSync as existsSync18, readdirSync as readdirSync6 } from "fs";
5088
- import { join as join18, resolve as resolve6 } from "path";
5176
+ import { existsSync as existsSync19, readdirSync as readdirSync6 } from "fs";
5177
+ import { join as join19, resolve as resolve6 } from "path";
5089
5178
  import chalk7 from "chalk";
5090
5179
  import { Command as Command7 } from "commander";
5091
5180
  var dataListCommand = new Command7("list").description("List DDL imports vendored in this project checkout").option("--cwd <path>", "Project root to scan (defaults to the current directory)").action(async (options) => {
@@ -5098,15 +5187,15 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
5098
5187
  }
5099
5188
  });
5100
5189
  async function runDataList(options) {
5101
- const importsDir = join18(options.cwd, "db", "imports");
5102
- if (!existsSync18(importsDir)) {
5190
+ const importsDir = join19(options.cwd, "db", "imports");
5191
+ if (!existsSync19(importsDir)) {
5103
5192
  console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
5104
5193
  return;
5105
5194
  }
5106
5195
  const candidates = readdirSync6(importsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
5107
5196
  const imports = [];
5108
5197
  for (const name of candidates) {
5109
- const fileCount = readdirSync6(join18(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
5198
+ const fileCount = readdirSync6(join19(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
5110
5199
  if (fileCount > 0) imports.push({ name, fileCount });
5111
5200
  }
5112
5201
  if (imports.length === 0) {
@@ -5136,7 +5225,7 @@ dataCommand.addCommand(dataListCommand);
5136
5225
 
5137
5226
  // src/commands/deploy.ts
5138
5227
  import { execSync as execSync4 } from "child_process";
5139
- import { existsSync as existsSync19, readFileSync as readFileSync12 } from "fs";
5228
+ import { existsSync as existsSync20, readFileSync as readFileSync13 } from "fs";
5140
5229
  import { resolve as resolve7 } from "path";
5141
5230
  import chalk8 from "chalk";
5142
5231
  import { Command as Command9 } from "commander";
@@ -5493,7 +5582,7 @@ var deployCommand = new Command9("deploy").description("Deploy infrastructure an
5493
5582
  );
5494
5583
  async function resolveConfig2(options) {
5495
5584
  if (options.config) {
5496
- const raw = JSON.parse(readFileSync12(resolve7(options.config), "utf8"));
5585
+ const raw = JSON.parse(readFileSync13(resolve7(options.config), "utf8"));
5497
5586
  const result = BiffoConfigSchema.safeParse(raw);
5498
5587
  if (!result.success) {
5499
5588
  log.error(`Invalid config at ${options.config}:`);
@@ -5513,8 +5602,8 @@ async function resolveConfig2(options) {
5513
5602
  return cfg;
5514
5603
  }
5515
5604
  const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
5516
- if (existsSync19(localConfigPath)) {
5517
- const raw = JSON.parse(readFileSync12(localConfigPath, "utf8"));
5605
+ if (existsSync20(localConfigPath)) {
5606
+ const raw = JSON.parse(readFileSync13(localConfigPath, "utf8"));
5518
5607
  const result = BiffoConfigSchema.safeParse(raw);
5519
5608
  if (result.success) return result.data;
5520
5609
  if (isTemplatePlaceholderConfig(raw)) {
@@ -5910,7 +5999,7 @@ function resolveGithubToken() {
5910
5999
 
5911
6000
  // src/commands/destroy.ts
5912
6001
  import { execSync as execSync5 } from "child_process";
5913
- import { readFileSync as readFileSync13 } from "fs";
6002
+ import { readFileSync as readFileSync14 } from "fs";
5914
6003
  import { resolve as resolve8 } from "path";
5915
6004
  import chalk9 from "chalk";
5916
6005
  import { Command as Command10 } from "commander";
@@ -6000,7 +6089,7 @@ var destroyCommand = new Command10("destroy").description("Destroy infrastructur
6000
6089
  });
6001
6090
  async function resolveConfig3(options) {
6002
6091
  if (options.config) {
6003
- const raw = JSON.parse(readFileSync13(resolve8(options.config), "utf8"));
6092
+ const raw = JSON.parse(readFileSync14(resolve8(options.config), "utf8"));
6004
6093
  const result = BiffoConfigSchema.safeParse(raw);
6005
6094
  if (!result.success) {
6006
6095
  log.error(`Invalid config at ${options.config}:`);
@@ -6020,7 +6109,7 @@ async function resolveConfig3(options) {
6020
6109
  return cfg;
6021
6110
  }
6022
6111
  try {
6023
- const raw = JSON.parse(readFileSync13(resolve8(process.cwd(), "biffo.config.json"), "utf8"));
6112
+ const raw = JSON.parse(readFileSync14(resolve8(process.cwd(), "biffo.config.json"), "utf8"));
6024
6113
  const result = BiffoConfigSchema.safeParse(raw);
6025
6114
  if (result.success) return result.data;
6026
6115
  } catch {
@@ -6070,15 +6159,15 @@ function resolveGithubToken2() {
6070
6159
  }
6071
6160
 
6072
6161
  // src/commands/init.ts
6073
- import { readFileSync as readFileSync17 } from "fs";
6162
+ import { readFileSync as readFileSync18 } from "fs";
6074
6163
  import { resolve as resolve10 } from "path";
6075
6164
  import chalk12 from "chalk";
6076
6165
  import { Command as Command12 } from "commander";
6077
6166
  import inquirer5 from "inquirer";
6078
6167
 
6079
6168
  // src/lib/build-freshness.ts
6080
- import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
6081
- import { dirname as dirname5, join as join19, relative as relative3, sep as sep3 } from "path";
6169
+ import { existsSync as existsSync21, readdirSync as readdirSync7, statSync as statSync5 } from "fs";
6170
+ import { dirname as dirname5, join as join20, relative as relative3, sep as sep3 } from "path";
6082
6171
  import { fileURLToPath as fileURLToPath3 } from "url";
6083
6172
  var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
6084
6173
  function checkBuildFreshness(options = {}) {
@@ -6092,7 +6181,7 @@ function checkBuildFreshness(options = {}) {
6092
6181
  if (!packageRoot) {
6093
6182
  return { status: "skipped", reason: `no package.json above ${moduleDir}`, newerSources: [] };
6094
6183
  }
6095
- const distDir = join19(packageRoot, "dist");
6184
+ const distDir = join20(packageRoot, "dist");
6096
6185
  if (!isInside(distDir, moduleDir)) {
6097
6186
  return {
6098
6187
  status: "skipped",
@@ -6100,16 +6189,16 @@ function checkBuildFreshness(options = {}) {
6100
6189
  newerSources: []
6101
6190
  };
6102
6191
  }
6103
- const srcDir = join19(packageRoot, "src");
6104
- if (!existsSync20(srcDir)) {
6192
+ const srcDir = join20(packageRoot, "src");
6193
+ if (!existsSync21(srcDir)) {
6105
6194
  return {
6106
6195
  status: "skipped",
6107
6196
  reason: "no src/ alongside dist/ \u2014 this is a shipped package",
6108
6197
  newerSources: []
6109
6198
  };
6110
6199
  }
6111
- const entry = join19(distDir, "index.js");
6112
- if (!existsSync20(entry)) {
6200
+ const entry = join20(distDir, "index.js");
6201
+ if (!existsSync21(entry)) {
6113
6202
  return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
6114
6203
  }
6115
6204
  const builtAt = statSync5(entry).mtimeMs;
@@ -6153,7 +6242,7 @@ function collectSourceFiles(srcDir) {
6153
6242
  const found = [];
6154
6243
  const walk2 = (dir) => {
6155
6244
  for (const entry of readdirSync7(dir, { withFileTypes: true })) {
6156
- const full = join19(dir, entry.name);
6245
+ const full = join20(dir, entry.name);
6157
6246
  if (entry.isDirectory()) {
6158
6247
  if (entry.name === "node_modules") continue;
6159
6248
  walk2(full);
@@ -6172,7 +6261,7 @@ function collectSourceFiles(srcDir) {
6172
6261
  function findPackageRoot(from) {
6173
6262
  let dir = from;
6174
6263
  for (; ; ) {
6175
- if (existsSync20(join19(dir, "package.json"))) return dir;
6264
+ if (existsSync21(join20(dir, "package.json"))) return dir;
6176
6265
  const parent = dirname5(dir);
6177
6266
  if (parent === dir) return null;
6178
6267
  dir = parent;
@@ -6186,9 +6275,9 @@ function isInside(parent, child) {
6186
6275
 
6187
6276
  // src/lib/credentials.ts
6188
6277
  import { execSync as execSync6 } from "child_process";
6189
- import { existsSync as existsSync21, readFileSync as readFileSync14 } from "fs";
6278
+ import { existsSync as existsSync22, readFileSync as readFileSync15 } from "fs";
6190
6279
  import { homedir as homedir2 } from "os";
6191
- import { join as join20 } from "path";
6280
+ import { join as join21 } from "path";
6192
6281
  import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
6193
6282
  import chalk10 from "chalk";
6194
6283
  import inquirer4 from "inquirer";
@@ -6367,11 +6456,11 @@ async function verifySelectedAwsCredentials(profile, region) {
6367
6456
  return sts.send(new GetCallerIdentityCommand2({}));
6368
6457
  }
6369
6458
  function discoverAwsProfiles() {
6370
- const files = [join20(homedir2(), ".aws", "credentials"), join20(homedir2(), ".aws", "config")];
6459
+ const files = [join21(homedir2(), ".aws", "credentials"), join21(homedir2(), ".aws", "config")];
6371
6460
  const profiles = /* @__PURE__ */ new Set();
6372
6461
  for (const file of files) {
6373
- if (!existsSync21(file)) continue;
6374
- const content = readFileSync14(file, "utf8");
6462
+ if (!existsSync22(file)) continue;
6463
+ const content = readFileSync15(file, "utf8");
6375
6464
  for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
6376
6465
  const section = match[1]?.trim();
6377
6466
  if (!section) continue;
@@ -6461,34 +6550,34 @@ var SiblingConfigSchema = z6.object({
6461
6550
 
6462
6551
  // src/lib/sibling-session.ts
6463
6552
  import {
6464
- existsSync as existsSync22,
6553
+ existsSync as existsSync23,
6465
6554
  mkdirSync as mkdirSync6,
6466
6555
  readdirSync as readdirSync8,
6467
- readFileSync as readFileSync15,
6556
+ readFileSync as readFileSync16,
6468
6557
  rmSync as rmSync7,
6469
6558
  statSync as statSync6,
6470
6559
  writeFileSync as writeFileSync6
6471
6560
  } from "fs";
6472
6561
  import { homedir as homedir3 } from "os";
6473
- import { join as join21 } from "path";
6562
+ import { join as join22 } from "path";
6474
6563
  function sessionsDir2() {
6475
- return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join21(homedir3(), ".biffo", "sibling-sessions");
6564
+ return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join22(homedir3(), ".biffo", "sibling-sessions");
6476
6565
  }
6477
6566
  function sessionPath2(projectName) {
6478
- return join21(sessionsDir2(), `${projectName}.json`);
6567
+ return join22(sessionsDir2(), `${projectName}.json`);
6479
6568
  }
6480
6569
  function loadSiblingSession(projectName) {
6481
6570
  const path = sessionPath2(projectName);
6482
- if (!existsSync22(path)) return null;
6571
+ if (!existsSync23(path)) return null;
6483
6572
  try {
6484
- return JSON.parse(readFileSync15(path, "utf8"));
6573
+ return JSON.parse(readFileSync16(path, "utf8"));
6485
6574
  } catch {
6486
6575
  return null;
6487
6576
  }
6488
6577
  }
6489
6578
  function saveSiblingSession(session) {
6490
6579
  const dir = sessionsDir2();
6491
- if (!existsSync22(dir)) mkdirSync6(dir, { recursive: true });
6580
+ if (!existsSync23(dir)) mkdirSync6(dir, { recursive: true });
6492
6581
  const name = session.config.project?.name ?? "unknown";
6493
6582
  const prior = loadSiblingSession(name);
6494
6583
  if (prior) {
@@ -6510,30 +6599,30 @@ function markSiblingStepComplete(session, step) {
6510
6599
  }
6511
6600
  function deleteSiblingSession(projectName) {
6512
6601
  const path = sessionPath2(projectName);
6513
- if (existsSync22(path)) rmSync7(path);
6602
+ if (existsSync23(path)) rmSync7(path);
6514
6603
  }
6515
6604
 
6516
6605
  // src/commands/sibling-create.ts
6517
- import { cpSync as cpSync2, existsSync as existsSync23, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync16, writeFileSync as writeFileSync7 } from "fs";
6606
+ import { cpSync as cpSync2, existsSync as existsSync24, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync4, readFileSync as readFileSync17, writeFileSync as writeFileSync7 } from "fs";
6518
6607
  import { tmpdir as tmpdir4 } from "os";
6519
- import { dirname as dirname6, join as join23, resolve as resolve9 } from "path";
6608
+ import { dirname as dirname6, join as join24, resolve as resolve9 } from "path";
6520
6609
  import { fileURLToPath as fileURLToPath4 } from "url";
6521
6610
  import chalk11 from "chalk";
6522
6611
  import { Command as Command11 } from "commander";
6523
6612
 
6524
6613
  // src/lib/skeleton-dotfiles.ts
6525
6614
  import { readdirSync as readdirSync9, renameSync } from "fs";
6526
- import { join as join22 } from "path";
6615
+ import { join as join23 } from "path";
6527
6616
  var PACKAGED_GITIGNORE = "_gitignore";
6528
6617
  var REAL_GITIGNORE = ".gitignore";
6529
6618
  function restorePackagedDotfiles(dir) {
6530
6619
  const restored = [];
6531
6620
  for (const entry of readdirSync9(dir, { withFileTypes: true })) {
6532
- const full = join22(dir, entry.name);
6621
+ const full = join23(dir, entry.name);
6533
6622
  if (entry.isDirectory()) {
6534
6623
  restored.push(...restorePackagedDotfiles(full));
6535
6624
  } else if (entry.name === PACKAGED_GITIGNORE) {
6536
- const target = join22(dir, REAL_GITIGNORE);
6625
+ const target = join23(dir, REAL_GITIGNORE);
6537
6626
  renameSync(full, target);
6538
6627
  restored.push(target);
6539
6628
  }
@@ -6578,7 +6667,7 @@ async function runSiblingCreateCommand(name, options) {
6578
6667
  printDryRun2(config, coreConfig, options.templateRoot);
6579
6668
  return;
6580
6669
  }
6581
- if (!existsSync23(options.templateRoot)) {
6670
+ if (!existsSync24(options.templateRoot)) {
6582
6671
  throw new Error(`Sibling template not found at ${options.templateRoot}`);
6583
6672
  }
6584
6673
  let session = null;
@@ -6795,7 +6884,7 @@ function assertPathPrefixIsAllowed(pathPrefix) {
6795
6884
  }
6796
6885
  }
6797
6886
  function readSiblingConfig(path, root = false) {
6798
- const raw = JSON.parse(readFileSync16(path, "utf8"));
6887
+ const raw = JSON.parse(readFileSync17(path, "utf8"));
6799
6888
  const withDefaults = raw && typeof raw === "object" && "project" in raw && "core" in raw ? {
6800
6889
  ...raw,
6801
6890
  core: {
@@ -6829,7 +6918,7 @@ function resolveCoreConfig(config, configPath) {
6829
6918
  throw new Error("Either core.project_name or core.config_path is required.");
6830
6919
  }
6831
6920
  function parseCoreConfig(path) {
6832
- const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync16(path, "utf8")));
6921
+ const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync17(path, "utf8")));
6833
6922
  if (!result.success) {
6834
6923
  throw new Error(
6835
6924
  `Invalid core configuration at ${path}:
@@ -6868,7 +6957,7 @@ async function resolveCoreIdentity(coreAws, coreConfig, environments) {
6868
6957
  return coreIdentity;
6869
6958
  }
6870
6959
  async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, githubToken) {
6871
- const workDir = mkdtempSync4(join23(tmpdir4(), `biffo-sibling-${config.project.name}-`));
6960
+ const workDir = mkdtempSync4(join24(tmpdir4(), `biffo-sibling-${config.project.name}-`));
6872
6961
  try {
6873
6962
  writeSiblingTemplate(skeletonRoot, workDir, config, {
6874
6963
  coreProjectName: coreConfig.project.name,
@@ -6893,13 +6982,13 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
6893
6982
  }
6894
6983
  }
6895
6984
  function writeSiblingTemplate(templateRoot, targetDir, config, context) {
6896
- if (!existsSync23(templateRoot)) {
6985
+ if (!existsSync24(templateRoot)) {
6897
6986
  throw new Error(`Sibling template not found at ${templateRoot}`);
6898
6987
  }
6899
6988
  cpSync2(templateRoot, targetDir, { recursive: true });
6900
6989
  restorePackagedDotfiles(targetDir);
6901
6990
  writeFileSync7(
6902
- join23(targetDir, "biffo.sibling.json"),
6991
+ join24(targetDir, "biffo.sibling.json"),
6903
6992
  JSON.stringify(
6904
6993
  {
6905
6994
  name: config.project.name,
@@ -6918,14 +7007,14 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
6918
7007
  ) + "\n"
6919
7008
  );
6920
7009
  writeFileSync7(
6921
- join23(targetDir, ".biffo-shared-version"),
7010
+ join24(targetDir, ".biffo-shared-version"),
6922
7011
  `core-v${context.templateVersion.replace(/^core-v/, "")}
6923
7012
  `
6924
7013
  );
6925
- const envPath = join23(targetDir, "apps", "frontend", ".env.example");
7014
+ const envPath = join24(targetDir, "apps", "frontend", ".env.example");
6926
7015
  try {
6927
7016
  const path = basePathFor(context.pathPrefix);
6928
- const content = readFileSync16(envPath, "utf8").replace(/^NEXT_PUBLIC_SIBLING_NAME=.*$/m, `NEXT_PUBLIC_SIBLING_NAME=${config.project.name}`).replace(/^NEXT_PUBLIC_SIBLING_PATH_PREFIX=.*$/m, `NEXT_PUBLIC_SIBLING_PATH_PREFIX=${path}`).replace(/^NEXT_PUBLIC_BASE_PATH=.*$/m, `NEXT_PUBLIC_BASE_PATH=${path}`);
7017
+ const content = readFileSync17(envPath, "utf8").replace(/^NEXT_PUBLIC_SIBLING_NAME=.*$/m, `NEXT_PUBLIC_SIBLING_NAME=${config.project.name}`).replace(/^NEXT_PUBLIC_SIBLING_PATH_PREFIX=.*$/m, `NEXT_PUBLIC_SIBLING_PATH_PREFIX=${path}`).replace(/^NEXT_PUBLIC_BASE_PATH=.*$/m, `NEXT_PUBLIC_BASE_PATH=${path}`);
6929
7018
  writeFileSync7(envPath, content);
6930
7019
  } catch (err) {
6931
7020
  if (err.code !== "ENOENT") throw err;
@@ -6972,7 +7061,7 @@ async function configureSiblingGithub(github, config, coreConfig, session, coreI
6972
7061
  }
6973
7062
  function readExistingSiblingOrigins(filePath) {
6974
7063
  try {
6975
- return JSON.parse(readFileSync16(filePath, "utf8"));
7064
+ return JSON.parse(readFileSync17(filePath, "utf8"));
6976
7065
  } catch (err) {
6977
7066
  if (err.code === "ENOENT") return {};
6978
7067
  throw err;
@@ -6991,10 +7080,10 @@ function assertGitIdentity(identity) {
6991
7080
  );
6992
7081
  }
6993
7082
  function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x") {
6994
- const cdnVarsPath = join23(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
7083
+ const cdnVarsPath = join24(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
6995
7084
  let declaresSiblingOrigins = false;
6996
7085
  try {
6997
- declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync16(cdnVarsPath, "utf8"));
7086
+ declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync17(cdnVarsPath, "utf8"));
6998
7087
  } catch {
6999
7088
  declaresSiblingOrigins = false;
7000
7089
  }
@@ -7004,10 +7093,10 @@ function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x")
7004
7093
  );
7005
7094
  }
7006
7095
  if (!isRootPathPrefix(pathPrefix)) return;
7007
- const cdnMainPath = join23(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
7096
+ const cdnMainPath = join24(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
7008
7097
  let supportsRoot = false;
7009
7098
  try {
7010
- supportsRoot = /root_sibling_registered/.test(readFileSync16(cdnMainPath, "utf8"));
7099
+ supportsRoot = /root_sibling_registered/.test(readFileSync17(cdnMainPath, "utf8"));
7011
7100
  } catch {
7012
7101
  supportsRoot = false;
7013
7102
  }
@@ -7037,8 +7126,8 @@ async function registerWithCore(git, github, config, coreConfig, pathPrefix, git
7037
7126
  for (const env of config.environments) {
7038
7127
  const bucketName = siteBucketName(config.project.name, env, siblingAccountId);
7039
7128
  const domain = bucketRegionalDomain(bucketName, coreAwsRegion);
7040
- const relativePath = join23("infra", "environments", env, "siblings.auto.tfvars.json");
7041
- const filePath = join23(cloneDir, relativePath);
7129
+ const relativePath = join24("infra", "environments", env, "siblings.auto.tfvars.json");
7130
+ const filePath = join24(cloneDir, relativePath);
7042
7131
  const existing = readExistingSiblingOrigins(filePath);
7043
7132
  const siblings = upsertSiblingOrigin(existing.sibling_origins ?? [], {
7044
7133
  name,
@@ -7118,8 +7207,8 @@ function defaultSiblingTemplateRoot() {
7118
7207
  const start = dirname6(fileURLToPath4(import.meta.url));
7119
7208
  let dir = start;
7120
7209
  for (; ; ) {
7121
- const candidate = join23(dir, "_skeletons", "sibling-template");
7122
- if (existsSync23(candidate)) return candidate;
7210
+ const candidate = join24(dir, "_skeletons", "sibling-template");
7211
+ if (existsSync24(candidate)) return candidate;
7123
7212
  const parent = dirname6(dir);
7124
7213
  if (parent === dir) break;
7125
7214
  dir = parent;
@@ -7143,7 +7232,7 @@ var initCommand = new Command12("init").description("Scaffold a new project from
7143
7232
  let config;
7144
7233
  let githubToken;
7145
7234
  if (options.config) {
7146
- const rawConfig = JSON.parse(readFileSync17(resolve10(options.config), "utf8"));
7235
+ const rawConfig = JSON.parse(readFileSync18(resolve10(options.config), "utf8"));
7147
7236
  config = parseConfig(rawConfig);
7148
7237
  const { account_id: accountId, region } = config.cloud.config;
7149
7238
  session = resolveConfigFileSession(config, accountId, region, options.fresh === true);
@@ -7588,8 +7677,8 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
7588
7677
  import { Command as Command21 } from "commander";
7589
7678
 
7590
7679
  // src/commands/plugin-create.ts
7591
- import { existsSync as existsSync26, readFileSync as readFileSync19, writeFileSync as writeFileSync9 } from "fs";
7592
- import { dirname as dirname8, join as join26, resolve as resolve11 } from "path";
7680
+ import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync9 } from "fs";
7681
+ import { dirname as dirname8, join as join27, resolve as resolve11 } from "path";
7593
7682
  import { fileURLToPath as fileURLToPath5 } from "url";
7594
7683
  import chalk13 from "chalk";
7595
7684
  import { Command as Command13 } from "commander";
@@ -7653,21 +7742,21 @@ function workflowCheckContexts(workflow) {
7653
7742
  }
7654
7743
 
7655
7744
  // src/lib/plugin-locations.ts
7656
- import { existsSync as existsSync24, readdirSync as readdirSync10 } from "fs";
7657
- import { join as join24 } from "path";
7745
+ import { existsSync as existsSync25, readdirSync as readdirSync10 } from "fs";
7746
+ import { join as join25 } from "path";
7658
7747
  var FIRST_PARTY_PLUGINS_DIR = "_plugins";
7659
7748
  var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
7660
7749
  function pluginDir(name, channel) {
7661
7750
  return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
7662
7751
  }
7663
7752
  function scanDir(absDir, relDir, channel) {
7664
- if (!existsSync24(absDir)) return [];
7753
+ if (!existsSync25(absDir)) return [];
7665
7754
  const found = [];
7666
7755
  for (const entry of readdirSync10(absDir, { withFileTypes: true })) {
7667
7756
  if (!entry.isDirectory()) continue;
7668
7757
  if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
7669
- const manifestPath = join24(absDir, entry.name, PLUGIN_MANIFEST_FILE);
7670
- if (!existsSync24(manifestPath)) continue;
7758
+ const manifestPath = join25(absDir, entry.name, PLUGIN_MANIFEST_FILE);
7759
+ if (!existsSync25(manifestPath)) continue;
7671
7760
  found.push({
7672
7761
  dirName: entry.name,
7673
7762
  relDir: `${relDir}/${entry.name}`,
@@ -7678,11 +7767,11 @@ function scanDir(absDir, relDir, channel) {
7678
7767
  return found;
7679
7768
  }
7680
7769
  function findInstalledPlugins(cwd) {
7681
- const servicesDir = join24(cwd, "services");
7770
+ const servicesDir = join25(cwd, "services");
7682
7771
  return [
7683
7772
  ...scanDir(servicesDir, "services", "third-party"),
7684
7773
  ...scanDir(
7685
- join24(servicesDir, FIRST_PARTY_PLUGINS_DIR),
7774
+ join25(servicesDir, FIRST_PARTY_PLUGINS_DIR),
7686
7775
  `services/${FIRST_PARTY_PLUGINS_DIR}`,
7687
7776
  "first-party"
7688
7777
  )
@@ -7911,13 +8000,13 @@ function validateManifest(raw) {
7911
8000
  // src/lib/plugin-scaffold.ts
7912
8001
  import {
7913
8002
  copyFileSync,
7914
- existsSync as existsSync25,
8003
+ existsSync as existsSync26,
7915
8004
  mkdirSync as mkdirSync8,
7916
- readFileSync as readFileSync18,
8005
+ readFileSync as readFileSync19,
7917
8006
  readdirSync as readdirSync11,
7918
8007
  writeFileSync as writeFileSync8
7919
8008
  } from "fs";
7920
- import { dirname as dirname7, join as join25 } from "path";
8009
+ import { dirname as dirname7, join as join26 } from "path";
7921
8010
  var STANDALONE_ONLY_ENTRIES = {
7922
8011
  ".github": "standalone-repo CI/release workflows \u2014 the host monorepo already runs lint/type/test/security over services/",
7923
8012
  "registry-schema.json": "the plugin-registry publishing schema, used when submitting a *published* plugin to the registry repo, not by an in-tree plugin"
@@ -7971,10 +8060,10 @@ function applySubstitutions(text, names) {
7971
8060
  var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
7972
8061
  function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
7973
8062
  const layout = options.layout ?? "in-tree";
7974
- if (!existsSync25(skeletonRoot)) {
8063
+ if (!existsSync26(skeletonRoot)) {
7975
8064
  throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
7976
8065
  }
7977
- if (!existsSync25(join25(skeletonRoot, "terraform"))) {
8066
+ if (!existsSync26(join26(skeletonRoot, "terraform"))) {
7978
8067
  throw new Error(
7979
8068
  `Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
7980
8069
  );
@@ -7982,7 +8071,7 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
7982
8071
  const skipped = [];
7983
8072
  const files = [];
7984
8073
  const walk2 = (relDir) => {
7985
- const absDir = join25(skeletonRoot, relDir);
8074
+ const absDir = join26(skeletonRoot, relDir);
7986
8075
  for (const entry of readdirSync11(absDir, { withFileTypes: true }).sort(
7987
8076
  (a, b) => a.name.localeCompare(b.name)
7988
8077
  )) {
@@ -7997,14 +8086,14 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
7997
8086
  continue;
7998
8087
  }
7999
8088
  const destRel = applySubstitutions(relPath, names);
8000
- const destPath = join25(destDir, destRel);
8089
+ const destPath = join26(destDir, destRel);
8001
8090
  mkdirSync8(dirname7(destPath), { recursive: true });
8002
8091
  if (BINARY_EXTENSIONS.test(entry.name)) {
8003
- copyFileSync(join25(skeletonRoot, relPath), destPath);
8092
+ copyFileSync(join26(skeletonRoot, relPath), destPath);
8004
8093
  } else {
8005
8094
  writeFileSync8(
8006
8095
  destPath,
8007
- applySubstitutions(readFileSync18(join25(skeletonRoot, relPath), "utf8"), names)
8096
+ applySubstitutions(readFileSync19(join26(skeletonRoot, relPath), "utf8"), names)
8008
8097
  );
8009
8098
  }
8010
8099
  files.push(destRel);
@@ -8021,8 +8110,8 @@ function scaffoldPlugin(skeletonRoot, destDir, names, options = {}) {
8021
8110
  function findSkeletonRoot(startDir, skeleton) {
8022
8111
  let dir = startDir;
8023
8112
  for (; ; ) {
8024
- const candidate = join25(dir, "_skeletons", skeleton);
8025
- if (existsSync25(candidate)) return candidate;
8113
+ const candidate = join26(dir, "_skeletons", skeleton);
8114
+ if (existsSync26(candidate)) return candidate;
8026
8115
  const parent = dirname7(dir);
8027
8116
  if (parent === dir) return null;
8028
8117
  dir = parent;
@@ -8090,7 +8179,7 @@ async function runPluginCreate(name, options, deps) {
8090
8179
  reportBranchProtectionSummary();
8091
8180
  return;
8092
8181
  }
8093
- const isInstance = existsSync26(join26(options.cwd, INSTANCE_CORE_FILE));
8182
+ const isInstance = existsSync27(join27(options.cwd, INSTANCE_CORE_FILE));
8094
8183
  if (options.firstParty && isInstance) {
8095
8184
  throw new Error(
8096
8185
  `--first-party scaffolds into services/_plugins/, which is template-owned: \`biffo core upgrade\` three-way-merges it against the template on every upgrade, and the template has no '${names.slug}'. This checkout is a Biffo instance (${INSTANCE_CORE_FILE} is present), so your plugin belongs in the user-owned ${pluginDir(names.slug, "third-party")}/ \u2014 re-run without --first-party.`
@@ -8098,19 +8187,19 @@ async function runPluginCreate(name, options, deps) {
8098
8187
  }
8099
8188
  const channel = options.firstParty ? "first-party" : "third-party";
8100
8189
  const relDir = pluginDir(names.slug, channel);
8101
- const destDir = join26(options.cwd, relDir);
8102
- const servicesDir = join26(options.cwd, "services");
8103
- if (!existsSync26(servicesDir)) {
8190
+ const destDir = join27(options.cwd, relDir);
8191
+ const servicesDir = join27(options.cwd, "services");
8192
+ if (!existsSync27(servicesDir)) {
8104
8193
  throw new Error(
8105
8194
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
8106
8195
  );
8107
8196
  }
8108
- if (existsSync26(destDir)) {
8197
+ if (existsSync27(destDir)) {
8109
8198
  throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
8110
8199
  }
8111
8200
  const here = dirname8(fileURLToPath5(import.meta.url));
8112
- const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join26(options.cwd, "_skeletons", "plugin-template");
8113
- if (!existsSync26(skeletonRoot)) {
8201
+ const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join27(options.cwd, "_skeletons", "plugin-template");
8202
+ if (!existsSync27(skeletonRoot)) {
8114
8203
  throw new Error(
8115
8204
  `Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
8116
8205
  );
@@ -8125,8 +8214,8 @@ async function runPluginCreate(name, options, deps) {
8125
8214
  for (const { entry, reason } of skipped) {
8126
8215
  log.info(`Skipped ${entry} \u2014 ${reason}`);
8127
8216
  }
8128
- const manifestPath = join26(destDir, "biffo.plugin.json");
8129
- const manifest = validateManifest(JSON.parse(readFileSync19(manifestPath, "utf8")));
8217
+ const manifestPath = join27(destDir, "biffo.plugin.json");
8218
+ const manifest = validateManifest(JSON.parse(readFileSync20(manifestPath, "utf8")));
8130
8219
  if (manifest.name !== names.slug) {
8131
8220
  throw new Error(
8132
8221
  `Scaffolded manifest declares name '${manifest.name}', expected '${names.slug}'. The skeleton's manifest name may have diverged from 'example-plugin'.`
@@ -8149,8 +8238,8 @@ async function runPluginCreate(name, options, deps) {
8149
8238
  printNextSteps(names, relDir, channel);
8150
8239
  }
8151
8240
  async function runStandaloneCreate(names, options, deps) {
8152
- const destDir = join26(options.cwd, names.dist);
8153
- if (existsSync26(destDir)) {
8241
+ const destDir = join27(options.cwd, names.dist);
8242
+ if (existsSync27(destDir)) {
8154
8243
  throw new Error(`${names.dist}/ already exists. Choose a different name, or remove it first.`);
8155
8244
  }
8156
8245
  const skeletonRoot = resolveSkeletonRoot(options);
@@ -8168,7 +8257,7 @@ async function runStandaloneCreate(names, options, deps) {
8168
8257
  restorePackagedDotfiles(destDir);
8169
8258
  log.success(`Scaffolded ${String(files.length)} file(s) into ${names.dist}/`);
8170
8259
  const manifest = validateManifest(
8171
- JSON.parse(readFileSync19(join26(destDir, "biffo.plugin.json"), "utf8"))
8260
+ JSON.parse(readFileSync20(join27(destDir, "biffo.plugin.json"), "utf8"))
8172
8261
  );
8173
8262
  if (manifest.name !== names.slug) {
8174
8263
  throw new Error(
@@ -8179,7 +8268,7 @@ async function runStandaloneCreate(names, options, deps) {
8179
8268
  `Manifest valid \u2014 ${String(manifest.tables.length)} table(s), ${String(manifest.api_routes.length)} route(s)`
8180
8269
  );
8181
8270
  writeFileSync9(
8182
- join26(destDir, ".biffo-shared-version"),
8271
+ join27(destDir, ".biffo-shared-version"),
8183
8272
  `core-v${getLatestCoreVersion().replace(/^core-v/, "")}
8184
8273
  `
8185
8274
  );
@@ -8212,8 +8301,8 @@ async function createAndPushStandaloneRepo(org, names, destDir, options, deps) {
8212
8301
  await deps.git.push(destDir, "dev", { token });
8213
8302
  log.success(`Pushed dev to ${org}/${names.dist}`);
8214
8303
  await github.setDefaultBranch(org, names.dist, "dev");
8215
- const ciPath = join26(destDir, ".github", "workflows", "ci.yml");
8216
- const contexts = existsSync26(ciPath) ? workflowCheckContexts(readFileSync19(ciPath, "utf8")) : [];
8304
+ const ciPath = join27(destDir, ".github", "workflows", "ci.yml");
8305
+ const contexts = existsSync27(ciPath) ? workflowCheckContexts(readFileSync20(ciPath, "utf8")) : [];
8217
8306
  if (contexts.length === 0) {
8218
8307
  log.warn(
8219
8308
  `Could not determine required status checks from ${ciPath} \u2014 skipping branch protection. Configure it manually on dev once you know the CI job names.`
@@ -8242,8 +8331,8 @@ async function registerInRegistrySources(names, cloneUrl, token, deps) {
8242
8331
  let dir;
8243
8332
  try {
8244
8333
  dir = await deps.git.cloneForEditing(REGISTRY_REPO, "biffo-registry", token);
8245
- const path = join26(dir, "sources.json");
8246
- const file = JSON.parse(readFileSync19(path, "utf8"));
8334
+ const path = join27(dir, "sources.json");
8335
+ const file = JSON.parse(readFileSync20(path, "utf8"));
8247
8336
  const next = addSource(file, {
8248
8337
  name: names.slug,
8249
8338
  repo: cloneUrl.replace(/\.git$/, ""),
@@ -8351,8 +8440,8 @@ function printStandaloneNextSteps(names, minor) {
8351
8440
  }
8352
8441
  function resolveSkeletonRoot(options) {
8353
8442
  const here = dirname8(fileURLToPath5(import.meta.url));
8354
- const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join26(options.cwd, "_skeletons", "plugin-template");
8355
- if (!existsSync26(skeletonRoot)) {
8443
+ const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join27(options.cwd, "_skeletons", "plugin-template");
8444
+ if (!existsSync27(skeletonRoot)) {
8356
8445
  throw new Error(
8357
8446
  `Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
8358
8447
  );
@@ -8543,14 +8632,14 @@ function printEntry(entry) {
8543
8632
  }
8544
8633
 
8545
8634
  // src/commands/plugin-install.ts
8546
- import { cpSync as cpSync5, existsSync as existsSync30, mkdirSync as mkdirSync11, readFileSync as readFileSync22, statSync as statSync7 } from "fs";
8547
- import { join as join32, relative as relative4, resolve as resolve12 } from "path";
8635
+ import { cpSync as cpSync5, existsSync as existsSync31, mkdirSync as mkdirSync11, readFileSync as readFileSync23, statSync as statSync7 } from "fs";
8636
+ import { join as join33, relative as relative4, resolve as resolve12 } from "path";
8548
8637
  import chalk15 from "chalk";
8549
8638
  import { Command as Command15 } from "commander";
8550
8639
 
8551
8640
  // src/adapters/plugin-migrations/index.ts
8552
8641
  import { execa as execa4 } from "execa";
8553
- import { join as join27 } from "path";
8642
+ import { join as join28 } from "path";
8554
8643
  var PluginMigrationsAdapter = class {
8555
8644
  /**
8556
8645
  * Generates migration file(s) for `pluginNames` (every discovered
@@ -8559,22 +8648,22 @@ var PluginMigrationsAdapter = class {
8559
8648
  * or declared no tables.
8560
8649
  */
8561
8650
  async generate(cwd, pluginNames) {
8562
- const scriptPath = join27(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
8651
+ const scriptPath = join28(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
8563
8652
  const args = [
8564
8653
  "run",
8565
8654
  "python",
8566
8655
  scriptPath,
8567
8656
  "--services-root",
8568
- join27(cwd, "services"),
8657
+ join28(cwd, "services"),
8569
8658
  "--versions-dir",
8570
- join27(cwd, "services", "api", "migrations", "versions")
8659
+ join28(cwd, "services", "api", "migrations", "versions")
8571
8660
  ];
8572
8661
  for (const name of pluginNames ?? []) {
8573
8662
  args.push("--plugin", name);
8574
8663
  }
8575
8664
  let result;
8576
8665
  try {
8577
- result = await execa4("uv", args, { cwd: join27(cwd, "services", "api") });
8666
+ result = await execa4("uv", args, { cwd: join28(cwd, "services", "api") });
8578
8667
  } catch (err) {
8579
8668
  const cause = err;
8580
8669
  if (cause.code === "ENOENT") {
@@ -8591,8 +8680,8 @@ var PluginMigrationsAdapter = class {
8591
8680
  };
8592
8681
 
8593
8682
  // src/lib/plugin-provenance.ts
8594
- import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
8595
- import { join as join28 } from "path";
8683
+ import { existsSync as existsSync28, readFileSync as readFileSync21, writeFileSync as writeFileSync10 } from "fs";
8684
+ import { join as join29 } from "path";
8596
8685
  import { execa as execa5 } from "execa";
8597
8686
  var PLUGIN_PROVENANCE_FILENAME = ".biffo-plugin-provenance.json";
8598
8687
  function isPluginProvenance(value) {
@@ -8601,11 +8690,11 @@ function isPluginProvenance(value) {
8601
8690
  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";
8602
8691
  }
8603
8692
  function readProvenance(pluginDir2) {
8604
- const path = join28(pluginDir2, PLUGIN_PROVENANCE_FILENAME);
8605
- if (!existsSync27(path)) return { status: "absent" };
8693
+ const path = join29(pluginDir2, PLUGIN_PROVENANCE_FILENAME);
8694
+ if (!existsSync28(path)) return { status: "absent" };
8606
8695
  let parsed;
8607
8696
  try {
8608
- parsed = JSON.parse(readFileSync20(path, "utf8"));
8697
+ parsed = JSON.parse(readFileSync21(path, "utf8"));
8609
8698
  } catch (err) {
8610
8699
  return {
8611
8700
  status: "invalid",
@@ -8621,7 +8710,7 @@ function readProvenance(pluginDir2) {
8621
8710
  return { status: "present", record: parsed };
8622
8711
  }
8623
8712
  function writePluginProvenance(pluginDir2, record) {
8624
- writeFileSync10(join28(pluginDir2, PLUGIN_PROVENANCE_FILENAME), `${JSON.stringify(record, null, 2)}
8713
+ writeFileSync10(join29(pluginDir2, PLUGIN_PROVENANCE_FILENAME), `${JSON.stringify(record, null, 2)}
8625
8714
  `);
8626
8715
  }
8627
8716
  function reconcileProvenance(previous, next) {
@@ -8671,8 +8760,8 @@ async function tryGit(cwd, args) {
8671
8760
  }
8672
8761
 
8673
8762
  // src/lib/plugin-seed-vendor.ts
8674
- import { cpSync as cpSync3, existsSync as existsSync28, mkdirSync as mkdirSync9, readdirSync as readdirSync12, rmSync as rmSync8 } from "fs";
8675
- import { join as join29 } from "path";
8763
+ import { cpSync as cpSync3, existsSync as existsSync29, mkdirSync as mkdirSync9, readdirSync as readdirSync12, rmSync as rmSync8 } from "fs";
8764
+ import { join as join30 } from "path";
8676
8765
  var VENDOR_PREFIX = "_plugin-";
8677
8766
  function pluginSeedImportDir(pluginName) {
8678
8767
  return `db/imports/${VENDOR_PREFIX}${pluginName}`;
@@ -8681,8 +8770,8 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
8681
8770
  if (!manifest.seed) {
8682
8771
  return { vendored: false };
8683
8772
  }
8684
- const sourceSeedDir = join29(pluginSourceDir, manifest.seed.dir);
8685
- if (!existsSync28(sourceSeedDir)) {
8773
+ const sourceSeedDir = join30(pluginSourceDir, manifest.seed.dir);
8774
+ if (!existsSync29(sourceSeedDir)) {
8686
8775
  throw new Error(
8687
8776
  `${manifest.name}'s manifest declares seed.dir '${manifest.seed.dir}', but ${sourceSeedDir} does not exist in the plugin's source.`
8688
8777
  );
@@ -8694,11 +8783,11 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
8694
8783
  );
8695
8784
  }
8696
8785
  const relTargetDir = pluginSeedImportDir(manifest.name);
8697
- const targetDir = join29(cwd, relTargetDir);
8786
+ const targetDir = join30(cwd, relTargetDir);
8698
8787
  rmSync8(targetDir, { recursive: true, force: true });
8699
8788
  mkdirSync9(targetDir, { recursive: true });
8700
8789
  for (const file of sqlFiles) {
8701
- cpSync3(join29(sourceSeedDir, file), join29(targetDir, file));
8790
+ cpSync3(join30(sourceSeedDir, file), join30(targetDir, file));
8702
8791
  }
8703
8792
  log.success(
8704
8793
  `Vendored ${sqlFiles.length} seed file(s) to ${relTargetDir}/ (baseline_tables: ${manifest.seed.baseline_tables.join(", ") || "none declared"})`
@@ -8711,7 +8800,7 @@ function vendorPluginSeed(pluginSourceDir, manifest, cwd) {
8711
8800
 
8712
8801
  // src/lib/plugin-source-copy.ts
8713
8802
  import { copyFileSync as copyFileSync2, cpSync as cpSync4, mkdirSync as mkdirSync10 } from "fs";
8714
- import { basename, dirname as dirname9, join as join30 } from "path";
8803
+ import { basename, dirname as dirname9, join as join31 } from "path";
8715
8804
  import { execa as execa6 } from "execa";
8716
8805
  var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
8717
8806
  ".git",
@@ -8728,9 +8817,9 @@ async function copyPluginSource(sourceDir, targetDir) {
8728
8817
  if (await isGitWorkingTree2(sourceDir)) {
8729
8818
  const files = await listGitFiles(sourceDir);
8730
8819
  for (const relPath of files) {
8731
- const destPath = join30(targetDir, relPath);
8820
+ const destPath = join31(targetDir, relPath);
8732
8821
  mkdirSync10(dirname9(destPath), { recursive: true });
8733
- copyFileSync2(join30(sourceDir, relPath), destPath);
8822
+ copyFileSync2(join31(sourceDir, relPath), destPath);
8734
8823
  }
8735
8824
  return { usedGitIgnoreRules: true };
8736
8825
  }
@@ -8762,8 +8851,8 @@ async function listGitFiles(dir) {
8762
8851
  }
8763
8852
 
8764
8853
  // src/lib/plugin-workspace-sources.ts
8765
- import { existsSync as existsSync29, readdirSync as readdirSync13, readFileSync as readFileSync21, writeFileSync as writeFileSync11 } from "fs";
8766
- import { join as join31 } from "path";
8854
+ import { existsSync as existsSync30, readdirSync as readdirSync13, readFileSync as readFileSync22, writeFileSync as writeFileSync11 } from "fs";
8855
+ import { join as join32 } from "path";
8767
8856
  function readTomlStringArray(text, key) {
8768
8857
  const open = new RegExp(`^${key}\\s*=\\s*\\[`, "m").exec(text);
8769
8858
  if (!open) return [];
@@ -8807,9 +8896,9 @@ function readDependencyNames(text) {
8807
8896
  return readTomlStringArray(text, "dependencies").map((dep) => /^\s*([A-Za-z0-9._-]+)/.exec(dep)?.[1] ?? "").filter(Boolean);
8808
8897
  }
8809
8898
  function workspaceMemberNames(instanceRoot) {
8810
- const rootPyproject = join31(instanceRoot, "pyproject.toml");
8811
- if (!existsSync29(rootPyproject)) return /* @__PURE__ */ new Set();
8812
- const text = readFileSync21(rootPyproject, "utf8");
8899
+ const rootPyproject = join32(instanceRoot, "pyproject.toml");
8900
+ if (!existsSync30(rootPyproject)) return /* @__PURE__ */ new Set();
8901
+ const text = readFileSync22(rootPyproject, "utf8");
8813
8902
  const members = readTomlStringArray(text, "members");
8814
8903
  const excluded = new Set(readTomlStringArray(text, "exclude"));
8815
8904
  const dirs = [];
@@ -8818,7 +8907,7 @@ function workspaceMemberNames(instanceRoot) {
8818
8907
  const base = member.slice(0, -2);
8819
8908
  let entries;
8820
8909
  try {
8821
- entries = readdirSync13(join31(instanceRoot, base), { withFileTypes: true });
8910
+ entries = readdirSync13(join32(instanceRoot, base), { withFileTypes: true });
8822
8911
  } catch {
8823
8912
  continue;
8824
8913
  }
@@ -8832,9 +8921,9 @@ function workspaceMemberNames(instanceRoot) {
8832
8921
  }
8833
8922
  const names = /* @__PURE__ */ new Set();
8834
8923
  for (const dir of dirs) {
8835
- const pp = join31(instanceRoot, dir, "pyproject.toml");
8836
- if (!existsSync29(pp)) continue;
8837
- const name = readProjectName(readFileSync21(pp, "utf8"));
8924
+ const pp = join32(instanceRoot, dir, "pyproject.toml");
8925
+ if (!existsSync30(pp)) continue;
8926
+ const name = readProjectName(readFileSync22(pp, "utf8"));
8838
8927
  if (name) names.add(name);
8839
8928
  }
8840
8929
  return names;
@@ -8845,8 +8934,8 @@ function existingWorkspaceSources(text) {
8845
8934
  );
8846
8935
  }
8847
8936
  function ensureWorkspaceSources(pluginPyprojectPath, memberNames) {
8848
- if (!existsSync29(pluginPyprojectPath) || memberNames.size === 0) return [];
8849
- const text = readFileSync21(pluginPyprojectPath, "utf8");
8937
+ if (!existsSync30(pluginPyprojectPath) || memberNames.size === 0) return [];
8938
+ const text = readFileSync22(pluginPyprojectPath, "utf8");
8850
8939
  const already = existingWorkspaceSources(text);
8851
8940
  const toAdd = readDependencyNames(text).filter((n) => memberNames.has(n) && !already.has(n));
8852
8941
  if (toAdd.length === 0) return [];
@@ -8870,8 +8959,8 @@ ${lines.join("\n")}
8870
8959
  return toAdd;
8871
8960
  }
8872
8961
  function applyWorkspaceSources(targetDir, cwd, relTargetDir) {
8873
- const pluginPyproject = join31(targetDir, "pyproject.toml");
8874
- if (!existsSync29(pluginPyproject)) return;
8962
+ const pluginPyproject = join32(targetDir, "pyproject.toml");
8963
+ if (!existsSync30(pluginPyproject)) return;
8875
8964
  const sourced = ensureWorkspaceSources(pluginPyproject, workspaceMemberNames(cwd));
8876
8965
  if (sourced.length > 0) {
8877
8966
  log.info(
@@ -8911,14 +9000,14 @@ var pluginInstallCommand = new Command15("install").description(
8911
9000
  }
8912
9001
  );
8913
9002
  function resolveLocalPlugin(localPath) {
8914
- if (!existsSync30(localPath)) {
9003
+ if (!existsSync31(localPath)) {
8915
9004
  throw new Error(`--local path does not exist: ${localPath}`);
8916
9005
  }
8917
9006
  if (!statSync7(localPath).isDirectory()) {
8918
9007
  throw new Error(`--local path is not a directory: ${localPath}`);
8919
9008
  }
8920
- const manifestPath = join32(localPath, "biffo.plugin.json");
8921
- if (!existsSync30(manifestPath)) {
9009
+ const manifestPath = join33(localPath, "biffo.plugin.json");
9010
+ if (!existsSync31(manifestPath)) {
8922
9011
  throw new Error(
8923
9012
  `${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>\`.)`
8924
9013
  );
@@ -8944,8 +9033,8 @@ function parsePluginTarget(target) {
8944
9033
  async function cloneAndValidatePlugin(entry, git) {
8945
9034
  const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
8946
9035
  try {
8947
- const manifestPath = join32(tmpDir, "biffo.plugin.json");
8948
- if (!existsSync30(manifestPath)) {
9036
+ const manifestPath = join33(tmpDir, "biffo.plugin.json");
9037
+ if (!existsSync31(manifestPath)) {
8949
9038
  throw new Error(
8950
9039
  `Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
8951
9040
  );
@@ -8973,8 +9062,8 @@ async function runPluginInstall(target, options, deps) {
8973
9062
  `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\`).`
8974
9063
  );
8975
9064
  }
8976
- const servicesDir = join32(options.cwd, "services");
8977
- if (!existsSync30(servicesDir)) {
9065
+ const servicesDir = join33(options.cwd, "services");
9066
+ if (!existsSync31(servicesDir)) {
8978
9067
  throw new Error(
8979
9068
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
8980
9069
  );
@@ -8992,10 +9081,10 @@ async function runPluginInstall(target, options, deps) {
8992
9081
  }
8993
9082
  const pluginName = entry ? entry.name : source.name;
8994
9083
  const relTargetDir = pluginDir(pluginName, "third-party");
8995
- const targetDir = join32(options.cwd, relTargetDir);
8996
- const modulesDir = join32(options.cwd, "modules", "plugins", pluginName);
9084
+ const targetDir = join33(options.cwd, relTargetDir);
9085
+ const modulesDir = join33(options.cwd, "modules", "plugins", pluginName);
8997
9086
  const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
8998
- if (existsSync30(targetDir) && !inTreeSource) {
9087
+ if (existsSync31(targetDir) && !inTreeSource) {
8999
9088
  throw new Error(
9000
9089
  `Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
9001
9090
  );
@@ -9039,8 +9128,8 @@ async function runPluginInstall(target, options, deps) {
9039
9128
  writePluginProvenance(targetDir, reconcileProvenance(previousProvenance, nextProvenance));
9040
9129
  applyWorkspaceSources(targetDir, options.cwd, relTargetDir);
9041
9130
  const stagePaths = [relTargetDir];
9042
- const tfSourceDir = join32(targetDir, "terraform");
9043
- if (existsSync30(tfSourceDir)) {
9131
+ const tfSourceDir = join33(targetDir, "terraform");
9132
+ if (existsSync31(tfSourceDir)) {
9044
9133
  mkdirSync11(modulesDir, { recursive: true });
9045
9134
  cpSync5(tfSourceDir, modulesDir, { recursive: true });
9046
9135
  stagePaths.push(`modules/plugins/${pluginName}`);
@@ -9101,7 +9190,7 @@ async function runPluginInstall(target, options, deps) {
9101
9190
  }
9102
9191
  function parseManifestFile(path) {
9103
9192
  try {
9104
- return JSON.parse(readFileSync22(path, "utf8"));
9193
+ return JSON.parse(readFileSync23(path, "utf8"));
9105
9194
  } catch (err) {
9106
9195
  throw new Error(`Could not parse ${path} as JSON: ${err.message}`);
9107
9196
  }
@@ -9143,8 +9232,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
9143
9232
  }
9144
9233
 
9145
9234
  // src/commands/plugin-list.ts
9146
- import { existsSync as existsSync31, readFileSync as readFileSync23 } from "fs";
9147
- import { join as join33, resolve as resolve13 } from "path";
9235
+ import { existsSync as existsSync32, readFileSync as readFileSync24 } from "fs";
9236
+ import { join as join34, resolve as resolve13 } from "path";
9148
9237
  import chalk16 from "chalk";
9149
9238
  import { Command as Command16 } from "commander";
9150
9239
  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) => {
@@ -9157,8 +9246,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
9157
9246
  }
9158
9247
  });
9159
9248
  async function runPluginList(options) {
9160
- const servicesDir = join33(options.cwd, "services");
9161
- if (!existsSync31(servicesDir)) {
9249
+ const servicesDir = join34(options.cwd, "services");
9250
+ if (!existsSync32(servicesDir)) {
9162
9251
  throw new Error(
9163
9252
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
9164
9253
  );
@@ -9166,7 +9255,7 @@ async function runPluginList(options) {
9166
9255
  const plugins = [];
9167
9256
  for (const location of findInstalledPlugins(options.cwd)) {
9168
9257
  try {
9169
- const manifest = validateManifest(JSON.parse(readFileSync23(location.manifestPath, "utf8")));
9258
+ const manifest = validateManifest(JSON.parse(readFileSync24(location.manifestPath, "utf8")));
9170
9259
  plugins.push({
9171
9260
  name: manifest.name,
9172
9261
  version: manifest.version,
@@ -9207,14 +9296,14 @@ import { resolve as resolve14 } from "path";
9207
9296
  import { Command as Command17 } from "commander";
9208
9297
 
9209
9298
  // src/lib/plugin-staleness.ts
9210
- import { existsSync as existsSync32, readFileSync as readFileSync24, readdirSync as readdirSync14, statSync as statSync8 } from "fs";
9211
- import { join as join34, relative as relative5 } from "path";
9299
+ import { existsSync as existsSync33, readFileSync as readFileSync25, readdirSync as readdirSync14, statSync as statSync8 } from "fs";
9300
+ import { join as join35, relative as relative5 } from "path";
9212
9301
  function discoverVendoredPlugins(servicesDir) {
9213
- if (!existsSync32(servicesDir)) return [];
9214
- return readdirSync14(servicesDir, { withFileTypes: true }).filter((e) => e.isDirectory() && !e.name.startsWith("_") && e.name !== "api").map((e) => e.name).filter((name) => existsSync32(join34(servicesDir, name, "biffo.plugin.json"))).sort();
9302
+ if (!existsSync33(servicesDir)) return [];
9303
+ 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();
9215
9304
  }
9216
9305
  async function checkPluginStaleness(cwd, deps) {
9217
- const servicesDir = join34(cwd, "services");
9306
+ const servicesDir = join35(cwd, "services");
9218
9307
  const names = discoverVendoredPlugins(servicesDir);
9219
9308
  let registryRepoByName = null;
9220
9309
  const resolveRegistryRepo = async (name) => {
@@ -9230,7 +9319,7 @@ async function checkPluginStaleness(cwd, deps) {
9230
9319
  };
9231
9320
  const results = [];
9232
9321
  for (const name of names) {
9233
- results.push(await checkOnePlugin(join34(servicesDir, name), name, resolveRegistryRepo, deps.git));
9322
+ results.push(await checkOnePlugin(join35(servicesDir, name), name, resolveRegistryRepo, deps.git));
9234
9323
  }
9235
9324
  return results;
9236
9325
  }
@@ -9256,7 +9345,7 @@ async function checkOnePlugin(pluginDir2, name, resolveRegistryRepo, git) {
9256
9345
  if (record?.sha && isFetchableUrl(record.origin)) {
9257
9346
  return checkViaProvenance(name, record, record.origin, git);
9258
9347
  }
9259
- const localOrigin = record && !isFetchableUrl(record.origin) && existsSync32(record.origin) ? record.origin : null;
9348
+ const localOrigin = record && !isFetchableUrl(record.origin) && existsSync33(record.origin) ? record.origin : null;
9260
9349
  if (localOrigin) {
9261
9350
  return checkViaContentDiff(name, pluginDir2, localOrigin, { isLocalDir: true }, git);
9262
9351
  }
@@ -9390,8 +9479,8 @@ async function countDifferingFiles(sourceDir, pluginDir2) {
9390
9479
  differing++;
9391
9480
  continue;
9392
9481
  }
9393
- const a = readFileSync24(join34(sourceDir, relPath));
9394
- const b = readFileSync24(join34(pluginDir2, relPath));
9482
+ const a = readFileSync25(join35(sourceDir, relPath));
9483
+ const b = readFileSync25(join35(pluginDir2, relPath));
9395
9484
  if (!a.equals(b)) differing++;
9396
9485
  }
9397
9486
  return differing;
@@ -9406,11 +9495,11 @@ function vendorFileList(dir) {
9406
9495
  return new Set(walkExcluding(dir, dir, LOCAL_COPY_EXCLUDES));
9407
9496
  }
9408
9497
  function walkExcluding(root, dir, excludes) {
9409
- if (!existsSync32(dir)) return [];
9498
+ if (!existsSync33(dir)) return [];
9410
9499
  const out = [];
9411
9500
  for (const entry of readdirSync14(dir)) {
9412
9501
  if (excludes.has(entry) || entry === ".git") continue;
9413
- const full = join34(dir, entry);
9502
+ const full = join35(dir, entry);
9414
9503
  const stat = statSync8(full);
9415
9504
  if (stat.isDirectory()) {
9416
9505
  out.push(...walkExcluding(root, full, excludes));
@@ -9462,8 +9551,8 @@ var pluginStalenessCommand = new Command17("staleness").description(
9462
9551
  });
9463
9552
 
9464
9553
  // src/commands/plugin-sync-migrations.ts
9465
- import { existsSync as existsSync33 } from "fs";
9466
- import { join as join35, relative as relative6, resolve as resolve15 } from "path";
9554
+ import { existsSync as existsSync34 } from "fs";
9555
+ import { join as join36, relative as relative6, resolve as resolve15 } from "path";
9467
9556
  import chalk17 from "chalk";
9468
9557
  import { Command as Command18 } from "commander";
9469
9558
  var pluginSyncMigrationsCommand = new Command18("sync-migrations").description(
@@ -9484,11 +9573,11 @@ var pluginSyncMigrationsCommand = new Command18("sync-migrations").description(
9484
9573
  }
9485
9574
  );
9486
9575
  async function runPluginSyncMigrations(name, options, deps) {
9487
- const servicesDir = join35(options.cwd, "services");
9488
- if (!existsSync33(servicesDir)) {
9576
+ const servicesDir = join36(options.cwd, "services");
9577
+ if (!existsSync34(servicesDir)) {
9489
9578
  throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
9490
9579
  }
9491
- if (name && !existsSync33(join35(servicesDir, name, "biffo.plugin.json"))) {
9580
+ if (name && !existsSync34(join36(servicesDir, name, "biffo.plugin.json"))) {
9492
9581
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
9493
9582
  }
9494
9583
  if (options.dryRun) {
@@ -9524,8 +9613,8 @@ async function runPluginSyncMigrations(name, options, deps) {
9524
9613
  }
9525
9614
 
9526
9615
  // src/commands/plugin-uninstall.ts
9527
- import { existsSync as existsSync34, readFileSync as readFileSync25, rmSync as rmSync9 } from "fs";
9528
- import { join as join36, resolve as resolve16 } from "path";
9616
+ import { existsSync as existsSync35, readFileSync as readFileSync26, rmSync as rmSync9 } from "fs";
9617
+ import { join as join37, resolve as resolve16 } from "path";
9529
9618
  import chalk18 from "chalk";
9530
9619
  import { Command as Command19 } from "commander";
9531
9620
  import inquirer6 from "inquirer";
@@ -9557,16 +9646,16 @@ async function runPluginUninstall(name, options, deps) {
9557
9646
  if (!NAME_PATTERN2.test(name)) {
9558
9647
  throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
9559
9648
  }
9560
- const servicesDir = join36(options.cwd, "services");
9561
- if (!existsSync34(servicesDir)) {
9649
+ const servicesDir = join37(options.cwd, "services");
9650
+ if (!existsSync35(servicesDir)) {
9562
9651
  throw new Error(
9563
9652
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
9564
9653
  );
9565
9654
  }
9566
- const targetDir = join36(servicesDir, name);
9567
- if (!existsSync34(targetDir)) {
9568
- const firstParty = join36(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
9569
- if (existsSync34(firstParty)) {
9655
+ const targetDir = join37(servicesDir, name);
9656
+ if (!existsSync35(targetDir)) {
9657
+ const firstParty = join37(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
9658
+ if (existsSync35(firstParty)) {
9570
9659
  throw new Error(
9571
9660
  `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.`
9572
9661
  );
@@ -9574,9 +9663,9 @@ async function runPluginUninstall(name, options, deps) {
9574
9663
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
9575
9664
  }
9576
9665
  const version = readInstalledVersion(targetDir);
9577
- const modulesDir = join36(options.cwd, "modules", "plugins", name);
9666
+ const modulesDir = join37(options.cwd, "modules", "plugins", name);
9578
9667
  const stagePaths = [`services/${name}`];
9579
- if (existsSync34(modulesDir)) {
9668
+ if (existsSync35(modulesDir)) {
9580
9669
  stagePaths.push(`modules/plugins/${name}`);
9581
9670
  }
9582
9671
  if (options.dryRun) {
@@ -9596,7 +9685,7 @@ async function runPluginUninstall(name, options, deps) {
9596
9685
  `${options.cwd} is not a git repository \u2014 biffo plugin uninstall must be run from a Biffo project checkout.`
9597
9686
  );
9598
9687
  }
9599
- if (existsSync34(modulesDir)) {
9688
+ if (existsSync35(modulesDir)) {
9600
9689
  const refs = findPluginModuleReferences(options.cwd, name).filter(
9601
9690
  (r) => !r.file.endsWith(`/${GENERATED_TF_FILE}`) && r.file !== GENERATED_TF_FILE
9602
9691
  );
@@ -9611,7 +9700,7 @@ Remove the reference(s) above first, then re-run uninstall.`
9611
9700
  }
9612
9701
  rmSync9(targetDir, { recursive: true, force: true });
9613
9702
  log.success(`Removed services/${name}/`);
9614
- if (existsSync34(modulesDir)) {
9703
+ if (existsSync35(modulesDir)) {
9615
9704
  rmSync9(modulesDir, { recursive: true, force: true });
9616
9705
  log.success(`Removed modules/plugins/${name}/`);
9617
9706
  const wiring = syncPluginTerraform(options.cwd);
@@ -9646,17 +9735,17 @@ Remove the reference(s) above first, then re-run uninstall.`
9646
9735
  "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."
9647
9736
  );
9648
9737
  }
9649
- if (existsSync34(join36(options.cwd, pluginSeedImportDir(name)))) {
9738
+ if (existsSync35(join37(options.cwd, pluginSeedImportDir(name)))) {
9650
9739
  log.warn(
9651
9740
  `${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.`
9652
9741
  );
9653
9742
  }
9654
9743
  }
9655
9744
  function readInstalledVersion(targetDir) {
9656
- const manifestPath = join36(targetDir, "biffo.plugin.json");
9657
- if (!existsSync34(manifestPath)) return void 0;
9745
+ const manifestPath = join37(targetDir, "biffo.plugin.json");
9746
+ if (!existsSync35(manifestPath)) return void 0;
9658
9747
  try {
9659
- return validateManifest(JSON.parse(readFileSync25(manifestPath, "utf8"))).version;
9748
+ return validateManifest(JSON.parse(readFileSync26(manifestPath, "utf8"))).version;
9660
9749
  } catch {
9661
9750
  return void 0;
9662
9751
  }
@@ -9689,8 +9778,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
9689
9778
  }
9690
9779
 
9691
9780
  // src/commands/plugin-upgrade.ts
9692
- import { cpSync as cpSync6, existsSync as existsSync35, mkdirSync as mkdirSync12, readFileSync as readFileSync26, rmSync as rmSync10 } from "fs";
9693
- import { join as join37, relative as relative7, resolve as resolve17 } from "path";
9781
+ import { cpSync as cpSync6, existsSync as existsSync36, mkdirSync as mkdirSync12, readFileSync as readFileSync27, rmSync as rmSync10 } from "fs";
9782
+ import { join as join38, relative as relative7, resolve as resolve17 } from "path";
9694
9783
  import chalk19 from "chalk";
9695
9784
  import { Command as Command20 } from "commander";
9696
9785
  import { execa as execa7 } from "execa";
@@ -9738,8 +9827,8 @@ async function runPluginUpgrade(target, options, deps) {
9738
9827
  `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\`).`
9739
9828
  );
9740
9829
  }
9741
- const servicesDir = join37(options.cwd, "services");
9742
- if (!existsSync35(servicesDir)) {
9830
+ const servicesDir = join38(options.cwd, "services");
9831
+ if (!existsSync36(servicesDir)) {
9743
9832
  throw new Error(
9744
9833
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
9745
9834
  );
@@ -9748,8 +9837,8 @@ async function runPluginUpgrade(target, options, deps) {
9748
9837
  return runLocalPluginRefresh(options.local, options, deps);
9749
9838
  }
9750
9839
  const { name, minor } = parsePluginTarget(target);
9751
- const targetDir = join37(servicesDir, name);
9752
- if (!existsSync35(targetDir)) {
9840
+ const targetDir = join38(servicesDir, name);
9841
+ if (!existsSync36(targetDir)) {
9753
9842
  throw new Error(
9754
9843
  `Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
9755
9844
  );
@@ -9763,7 +9852,7 @@ async function runPluginUpgrade(target, options, deps) {
9763
9852
  `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.`
9764
9853
  );
9765
9854
  }
9766
- const modulesDir = join37(options.cwd, "modules", "plugins", entry.name);
9855
+ const modulesDir = join38(options.cwd, "modules", "plugins", entry.name);
9767
9856
  if (options.dryRun) {
9768
9857
  printDryRun6(entry, currentVersion);
9769
9858
  return;
@@ -9791,7 +9880,7 @@ async function runPluginUpgrade(target, options, deps) {
9791
9880
  log.success(
9792
9881
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9793
9882
  );
9794
- if (!existsSync35(join37(tmpDir, "terraform"))) {
9883
+ if (!existsSync36(join38(tmpDir, "terraform"))) {
9795
9884
  refuseIfModuleStillReferenced(options.cwd, modulesDir, entry.name);
9796
9885
  }
9797
9886
  const previousProvenance = readProvenance(targetDir);
@@ -9808,11 +9897,11 @@ async function runPluginUpgrade(target, options, deps) {
9808
9897
  applyWorkspaceSources(targetDir, options.cwd, `services/${entry.name}`);
9809
9898
  const newPyproject = readPyprojectIfPresent(targetDir);
9810
9899
  const stagePaths = [`services/${entry.name}`];
9811
- if (existsSync35(modulesDir)) {
9900
+ if (existsSync36(modulesDir)) {
9812
9901
  rmSync10(modulesDir, { recursive: true, force: true });
9813
9902
  }
9814
- const tfSourceDir = join37(targetDir, "terraform");
9815
- if (existsSync35(tfSourceDir)) {
9903
+ const tfSourceDir = join38(targetDir, "terraform");
9904
+ if (existsSync36(tfSourceDir)) {
9816
9905
  mkdirSync12(modulesDir, { recursive: true });
9817
9906
  cpSync6(tfSourceDir, modulesDir, { recursive: true });
9818
9907
  stagePaths.push(`modules/plugins/${entry.name}`);
@@ -9873,16 +9962,16 @@ async function runPluginUpgrade(target, options, deps) {
9873
9962
  async function runLocalPluginRefresh(localPath, options, deps) {
9874
9963
  const source = resolveLocalPlugin(localPath);
9875
9964
  log.success(`Resolved ${source.name}@${source.version} from ${source.origin}`);
9876
- const servicesDir = join37(options.cwd, "services");
9877
- const targetDir = join37(servicesDir, source.name);
9878
- if (!existsSync35(targetDir)) {
9965
+ const servicesDir = join38(options.cwd, "services");
9966
+ const targetDir = join38(servicesDir, source.name);
9967
+ if (!existsSync36(targetDir)) {
9879
9968
  throw new Error(
9880
9969
  `Plugin '${source.name}' is not installed at services/${source.name}/. Use 'biffo plugin install --local ${localPath}' instead.`
9881
9970
  );
9882
9971
  }
9883
9972
  const inTreeSource = resolve17(source.sourceDir) === resolve17(targetDir);
9884
9973
  const currentVersion = readInstalledVersion2(targetDir);
9885
- const modulesDir = join37(options.cwd, "modules", "plugins", source.name);
9974
+ const modulesDir = join38(options.cwd, "modules", "plugins", source.name);
9886
9975
  if (options.dryRun) {
9887
9976
  printLocalDryRun(source, currentVersion, inTreeSource);
9888
9977
  return;
@@ -9905,7 +9994,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9905
9994
  log.success(
9906
9995
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9907
9996
  );
9908
- if (!existsSync35(join37(source.sourceDir, "terraform"))) {
9997
+ if (!existsSync36(join38(source.sourceDir, "terraform"))) {
9909
9998
  refuseIfModuleStillReferenced(options.cwd, modulesDir, source.name);
9910
9999
  }
9911
10000
  const previousProvenance = readProvenance(targetDir);
@@ -9925,11 +10014,11 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9925
10014
  applyWorkspaceSources(targetDir, options.cwd, `services/${source.name}`);
9926
10015
  const newPyproject = readPyprojectIfPresent(targetDir);
9927
10016
  const stagePaths = [`services/${source.name}`];
9928
- if (existsSync35(modulesDir)) {
10017
+ if (existsSync36(modulesDir)) {
9929
10018
  rmSync10(modulesDir, { recursive: true, force: true });
9930
10019
  }
9931
- const tfSourceDir = join37(targetDir, "terraform");
9932
- if (existsSync35(tfSourceDir)) {
10020
+ const tfSourceDir = join38(targetDir, "terraform");
10021
+ if (existsSync36(tfSourceDir)) {
9933
10022
  mkdirSync12(modulesDir, { recursive: true });
9934
10023
  cpSync6(tfSourceDir, modulesDir, { recursive: true });
9935
10024
  stagePaths.push(`modules/plugins/${source.name}`);
@@ -9993,7 +10082,7 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9993
10082
  }
9994
10083
  }
9995
10084
  function refuseIfModuleStillReferenced(cwd, modulesDir, name) {
9996
- if (!existsSync35(modulesDir)) return;
10085
+ if (!existsSync36(modulesDir)) return;
9997
10086
  const refs = findPluginModuleReferences(cwd, name);
9998
10087
  if (refs.length === 0) return;
9999
10088
  const refList = refs.map((r) => ` ${r.file}:${r.line} ${r.text}`).join("\n");
@@ -10031,8 +10120,8 @@ var defaultRunCommand2 = async (command, cwd) => {
10031
10120
  }
10032
10121
  };
10033
10122
  function readPyprojectIfPresent(targetDir) {
10034
- const path = join37(targetDir, "pyproject.toml");
10035
- return existsSync35(path) ? readFileSync26(path, "utf8") : null;
10123
+ const path = join38(targetDir, "pyproject.toml");
10124
+ return existsSync36(path) ? readFileSync27(path, "utf8") : null;
10036
10125
  }
10037
10126
  function dependenciesChanged(before, after) {
10038
10127
  if (before === after) return false;
@@ -10060,10 +10149,10 @@ function tomlTableBody(text, header) {
10060
10149
  return nextHeader ? rest.slice(0, nextHeader.index) : rest;
10061
10150
  }
10062
10151
  function readInstalledVersion2(targetDir) {
10063
- const manifestPath = join37(targetDir, "biffo.plugin.json");
10064
- if (!existsSync35(manifestPath)) return void 0;
10152
+ const manifestPath = join38(targetDir, "biffo.plugin.json");
10153
+ if (!existsSync36(manifestPath)) return void 0;
10065
10154
  try {
10066
- return validateManifest(JSON.parse(readFileSync26(manifestPath, "utf8"))).version;
10155
+ return validateManifest(JSON.parse(readFileSync27(manifestPath, "utf8"))).version;
10067
10156
  } catch {
10068
10157
  return void 0;
10069
10158
  }
@@ -10146,7 +10235,7 @@ pluginCommand.addCommand(pluginStalenessCommand);
10146
10235
  import { Command as Command23 } from "commander";
10147
10236
 
10148
10237
  // src/commands/sibling-check-identity.ts
10149
- import { existsSync as existsSync36, readFileSync as readFileSync27 } from "fs";
10238
+ import { existsSync as existsSync37, readFileSync as readFileSync28 } from "fs";
10150
10239
  import { resolve as resolve18 } from "path";
10151
10240
  import chalk20 from "chalk";
10152
10241
  import { Command as Command22 } from "commander";
@@ -10340,7 +10429,7 @@ async function fetchPublishedIdentity(portalUrl) {
10340
10429
  }
10341
10430
  async function resolveConfig4(options) {
10342
10431
  if (options.config) {
10343
- const raw = JSON.parse(readFileSync27(resolve18(options.config), "utf8"));
10432
+ const raw = JSON.parse(readFileSync28(resolve18(options.config), "utf8"));
10344
10433
  const result = BiffoConfigSchema.safeParse(raw);
10345
10434
  if (!result.success) {
10346
10435
  log.error(`Invalid config at ${options.config}:`);
@@ -10360,8 +10449,8 @@ async function resolveConfig4(options) {
10360
10449
  return cfg;
10361
10450
  }
10362
10451
  const localConfigPath = resolve18(process.cwd(), "biffo.config.json");
10363
- if (existsSync36(localConfigPath)) {
10364
- const raw = JSON.parse(readFileSync27(localConfigPath, "utf8"));
10452
+ if (existsSync37(localConfigPath)) {
10453
+ const raw = JSON.parse(readFileSync28(localConfigPath, "utf8"));
10365
10454
  const result = BiffoConfigSchema.safeParse(raw);
10366
10455
  if (result.success) return result.data;
10367
10456
  if (isTemplatePlaceholderConfig(raw)) {
@@ -10407,21 +10496,21 @@ siblingCommand.addCommand(siblingCheckIdentityCommand);
10407
10496
  import { Command as Command24 } from "commander";
10408
10497
 
10409
10498
  // src/scripts/check-adr-numbering.ts
10410
- import { existsSync as existsSync38 } from "fs";
10411
- import { join as join39 } from "path";
10499
+ import { existsSync as existsSync39 } from "fs";
10500
+ import { join as join40 } from "path";
10412
10501
  import { execa as execa8 } from "execa";
10413
10502
 
10414
10503
  // src/lib/adr-numbering-guard.ts
10415
- import { existsSync as existsSync37, readdirSync as readdirSync15, readFileSync as readFileSync28 } from "fs";
10416
- import { join as join38 } from "path";
10504
+ import { existsSync as existsSync38, readdirSync as readdirSync15, readFileSync as readFileSync29 } from "fs";
10505
+ import { join as join39 } from "path";
10417
10506
  var ADR_FILENAME = /^(\d{4})-.+\.md$/;
10418
10507
  var ALLOWLIST_FILENAME = ".numbering-allowlist";
10419
10508
  var TEMPLATE_ADR_RESERVED_UPTO = "0099";
10420
10509
  function readAdrNumberingAllowlist(adrDir) {
10421
- const path = join38(adrDir, ALLOWLIST_FILENAME);
10422
- if (!existsSync37(path)) return /* @__PURE__ */ new Set();
10510
+ const path = join39(adrDir, ALLOWLIST_FILENAME);
10511
+ if (!existsSync38(path)) return /* @__PURE__ */ new Set();
10423
10512
  const numbers = /* @__PURE__ */ new Set();
10424
- for (const rawLine of readFileSync28(path, "utf8").split("\n")) {
10513
+ for (const rawLine of readFileSync29(path, "utf8").split("\n")) {
10425
10514
  const line = rawLine.split("#")[0].trim();
10426
10515
  if (line) numbers.add(line);
10427
10516
  }
@@ -10429,7 +10518,7 @@ function readAdrNumberingAllowlist(adrDir) {
10429
10518
  }
10430
10519
  function adrNumbersIn(adrDir) {
10431
10520
  const claims = /* @__PURE__ */ new Map();
10432
- if (!existsSync37(adrDir)) return claims;
10521
+ if (!existsSync38(adrDir)) return claims;
10433
10522
  for (const entry of readdirSync15(adrDir).sort()) {
10434
10523
  const match = ADR_FILENAME.exec(entry);
10435
10524
  if (!match) continue;
@@ -10484,8 +10573,8 @@ function formatAdrReservedRangeViolations(violations, reservedUpTo = TEMPLATE_AD
10484
10573
  // src/scripts/check-adr-numbering.ts
10485
10574
  async function runAdrNumberingCheck() {
10486
10575
  const root = (await execa8("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
10487
- const adrDir = join39(root, "docs", "ADR");
10488
- if (!existsSync38(adrDir)) {
10576
+ const adrDir = join40(root, "docs", "ADR");
10577
+ if (!existsSync39(adrDir)) {
10489
10578
  console.log("\u2713 ADR numbering guard: no docs/ADR/ directory \u2014 nothing to compare");
10490
10579
  return;
10491
10580
  }
@@ -10786,18 +10875,18 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
10786
10875
  import { execa as execa10 } from "execa";
10787
10876
 
10788
10877
  // src/lib/claim-invocation-parity.ts
10789
- import { existsSync as existsSync39, readFileSync as readFileSync29, readdirSync as readdirSync16 } from "fs";
10790
- import { join as join40 } from "path";
10878
+ import { existsSync as existsSync40, readFileSync as readFileSync30, readdirSync as readdirSync16 } from "fs";
10879
+ import { join as join41 } from "path";
10791
10880
  function distributedAgentsDocs(root) {
10792
10881
  const docs = [];
10793
- const own = join40(root, "AGENTS.md");
10794
- if (existsSync39(own)) docs.push({ path: "AGENTS.md", text: readFileSync29(own, "utf8") });
10795
- const skeletons = join40(root, "_skeletons");
10796
- if (existsSync39(skeletons)) {
10882
+ const own = join41(root, "AGENTS.md");
10883
+ if (existsSync40(own)) docs.push({ path: "AGENTS.md", text: readFileSync30(own, "utf8") });
10884
+ const skeletons = join41(root, "_skeletons");
10885
+ if (existsSync40(skeletons)) {
10797
10886
  for (const name of readdirSync16(skeletons).sort()) {
10798
- const abs = join40(skeletons, name, "AGENTS.md");
10799
- if (!existsSync39(abs)) continue;
10800
- docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync29(abs, "utf8") });
10887
+ const abs = join41(skeletons, name, "AGENTS.md");
10888
+ if (!existsSync40(abs)) continue;
10889
+ docs.push({ path: `_skeletons/${name}/AGENTS.md`, text: readFileSync30(abs, "utf8") });
10801
10890
  }
10802
10891
  }
10803
10892
  return docs;
@@ -10929,13 +11018,13 @@ async function runClaimInvocationCheck() {
10929
11018
  }
10930
11019
 
10931
11020
  // src/scripts/check-codeql-suppression.ts
10932
- import { existsSync as existsSync40 } from "fs";
10933
- import { join as join42, relative as relative8 } from "path";
11021
+ import { existsSync as existsSync41 } from "fs";
11022
+ import { join as join43, relative as relative8 } from "path";
10934
11023
  import { execa as execa11 } from "execa";
10935
11024
 
10936
11025
  // src/lib/codeql-suppression-guard.ts
10937
- import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync9 } from "fs";
10938
- import { join as join41 } from "path";
11026
+ import { readdirSync as readdirSync17, readFileSync as readFileSync31, statSync as statSync9 } from "fs";
11027
+ import { join as join42 } from "path";
10939
11028
  var SKIP_DIRS = /* @__PURE__ */ new Set([
10940
11029
  ".git",
10941
11030
  ".worktrees",
@@ -10966,7 +11055,7 @@ function walkSourceFiles(root) {
10966
11055
  return;
10967
11056
  }
10968
11057
  for (const entry of entries) {
10969
- const p = join41(dir, entry);
11058
+ const p = join42(dir, entry);
10970
11059
  let st;
10971
11060
  try {
10972
11061
  st = statSync9(p);
@@ -10992,7 +11081,7 @@ function countSourceFiles(root) {
10992
11081
  function sweepCodeqlSuppressionComments(root) {
10993
11082
  const hits = [];
10994
11083
  for (const path of walkSourceFiles(root)) {
10995
- const text = readFileSync30(path, "utf8");
11084
+ const text = readFileSync31(path, "utf8");
10996
11085
  for (const line of findCodeqlSuppressionComments(text)) {
10997
11086
  hits.push({ path, line, text: text.split("\n")[line - 1] ?? "" });
10998
11087
  }
@@ -11003,8 +11092,8 @@ function sweepCodeqlSuppressionComments(root) {
11003
11092
  // src/scripts/check-codeql-suppression.ts
11004
11093
  async function runCodeqlSuppressionCheck() {
11005
11094
  const root = (await execa11("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11006
- const scanRoot = join42(root, "cli", "src");
11007
- if (!existsSync40(scanRoot)) {
11095
+ const scanRoot = join43(root, "cli", "src");
11096
+ if (!existsSync41(scanRoot)) {
11008
11097
  console.log(
11009
11098
  "\u2014 codeql-suppression guard: skipped \u2014 no cli/src in this repo, so there is no CLI source to scan."
11010
11099
  );
@@ -11031,8 +11120,8 @@ async function runCodeqlSuppressionCheck() {
11031
11120
  import { execa as execa12 } from "execa";
11032
11121
 
11033
11122
  // src/lib/cognito-invite-template-guard.ts
11034
- import { readdirSync as readdirSync18, readFileSync as readFileSync31, statSync as statSync10 } from "fs";
11035
- import { join as join43 } from "path";
11123
+ import { readdirSync as readdirSync18, readFileSync as readFileSync32, statSync as statSync10 } from "fs";
11124
+ import { join as join44 } from "path";
11036
11125
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
11037
11126
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
11038
11127
  var PLACEHOLDER_MEMBERS = ["email_message", "sms_message"];
@@ -11115,7 +11204,7 @@ function findModuleTerraformFiles(repoRoot) {
11115
11204
  }
11116
11205
  for (const entry of entries) {
11117
11206
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
11118
- const full = join43(dir, entry);
11207
+ const full = join44(dir, entry);
11119
11208
  const rel = `${relative11}/${entry}`;
11120
11209
  if (statSync10(full).isDirectory()) {
11121
11210
  walk2(full, rel);
@@ -11124,12 +11213,12 @@ function findModuleTerraformFiles(repoRoot) {
11124
11213
  }
11125
11214
  }
11126
11215
  };
11127
- walk2(join43(repoRoot, "modules"), "modules");
11216
+ walk2(join44(repoRoot, "modules"), "modules");
11128
11217
  return found.sort();
11129
11218
  }
11130
11219
  function checkCognitoInviteTemplates(repoRoot) {
11131
11220
  return findModuleTerraformFiles(repoRoot).flatMap(
11132
- (file) => checkInviteTemplateSource(file, readFileSync31(join43(repoRoot, file), "utf8"))
11221
+ (file) => checkInviteTemplateSource(file, readFileSync32(join44(repoRoot, file), "utf8"))
11133
11222
  );
11134
11223
  }
11135
11224
 
@@ -11157,12 +11246,12 @@ async function runCognitoInviteTemplateCheck() {
11157
11246
  }
11158
11247
 
11159
11248
  // src/scripts/check-core-direct-paths.ts
11160
- import { join as join45 } from "path";
11249
+ import { join as join46 } from "path";
11161
11250
  import { execa as execa13 } from "execa";
11162
11251
 
11163
11252
  // src/lib/core-direct-paths-audit.ts
11164
- import { existsSync as existsSync41, readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11165
- import { join as join44 } from "path";
11253
+ import { existsSync as existsSync42, readFileSync as readFileSync33, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11254
+ import { join as join45 } from "path";
11166
11255
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
11167
11256
  var API_ROUTE_PREFIX = "/api/v1";
11168
11257
  var TEST_FILE_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx"];
@@ -11326,7 +11415,7 @@ function walkFiles(root, accept, skipDir) {
11326
11415
  return;
11327
11416
  }
11328
11417
  for (const entry of entries) {
11329
- const p = join44(dir, entry);
11418
+ const p = join45(dir, entry);
11330
11419
  let st;
11331
11420
  try {
11332
11421
  st = statSync11(p);
@@ -11356,7 +11445,7 @@ function auditFrontendExtraction(frontendSrcDir, externalBases = EXTERNAL_BASE_I
11356
11445
  const extracted = [];
11357
11446
  let rawTotal = 0;
11358
11447
  for (const file of files) {
11359
- const text = readFileSync32(file, "utf8");
11448
+ const text = readFileSync33(file, "utf8");
11360
11449
  rawTotal += countRawExternalOccurrences(text, externalBases);
11361
11450
  extracted.push(...extractCoreDirectPaths(text, file, externalBases));
11362
11451
  }
@@ -11405,7 +11494,7 @@ function auditCoreRouteExtraction(apiSrcDir) {
11405
11494
  const prefixSet = /* @__PURE__ */ new Set();
11406
11495
  let rawApiRouterCount = 0;
11407
11496
  for (const file of files) {
11408
- const text = readFileSync32(file, "utf8");
11497
+ const text = readFileSync33(file, "utf8");
11409
11498
  const extraction = extractCoreRoutePrefixes(text);
11410
11499
  rawApiRouterCount += extraction.rawApiRouterCount;
11411
11500
  for (const p of extraction.prefixes) prefixSet.add(normalizePrefix(p));
@@ -11421,10 +11510,10 @@ function pathMatchesAnyCorePrefix(normalized, corePrefixes, apiRoutePrefix = API
11421
11510
  }
11422
11511
  function resolveSiblingCoreSrc(params) {
11423
11512
  const { estateDir, sibling } = params;
11424
- const configPath = join44(estateDir, sibling, "biffo.sibling.json");
11513
+ const configPath = join45(estateDir, sibling, "biffo.sibling.json");
11425
11514
  let raw;
11426
11515
  try {
11427
- raw = readFileSync32(configPath, "utf8");
11516
+ raw = readFileSync33(configPath, "utf8");
11428
11517
  } catch (err) {
11429
11518
  throw new Error(
11430
11519
  `cannot resolve ${sibling}'s core: ${configPath} does not exist or is unreadable (${err.message}) -- refusing to guess which core serves this sibling.`
@@ -11444,8 +11533,8 @@ function resolveSiblingCoreSrc(params) {
11444
11533
  `cannot resolve ${sibling}'s core: ${configPath} has no non-empty "core_project" field.`
11445
11534
  );
11446
11535
  }
11447
- const coreApiSrcDir = join44(estateDir, coreProject, "services", "api", "src");
11448
- if (!existsSync41(coreApiSrcDir)) {
11536
+ const coreApiSrcDir = join45(estateDir, coreProject, "services", "api", "src");
11537
+ if (!existsSync42(coreApiSrcDir)) {
11449
11538
  throw new Error(
11450
11539
  `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.`
11451
11540
  );
@@ -11488,7 +11577,7 @@ function auditSiblingCoreDirectPaths(params) {
11488
11577
  async function runCoreDirectPathsCheck(opts = {}) {
11489
11578
  const root = (await execa13("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
11490
11579
  const sibling = opts.sibling ?? "sibling-template (self-check)";
11491
- const frontendSrcDir = opts.frontendSrc ?? join45(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
11580
+ const frontendSrcDir = opts.frontendSrc ?? join46(root, "_skeletons", "sibling-template", "apps", "frontend", "src");
11492
11581
  let coreApiSrcDir;
11493
11582
  let coreProject = null;
11494
11583
  if (opts.coreSrc) {
@@ -11504,7 +11593,7 @@ async function runCoreDirectPathsCheck(opts = {}) {
11504
11593
  coreApiSrcDir = resolution.coreApiSrcDir;
11505
11594
  coreProject = resolution.coreProject;
11506
11595
  } else {
11507
- coreApiSrcDir = join45(root, "services", "api", "src");
11596
+ coreApiSrcDir = join46(root, "services", "api", "src");
11508
11597
  }
11509
11598
  const report = auditSiblingCoreDirectPaths({ sibling, frontendSrcDir, coreApiSrcDir });
11510
11599
  console.log(
@@ -11570,8 +11659,8 @@ async function runOwnershipCheck(argv) {
11570
11659
  const { stdout } = await execa14("git", ["diff", "--cached", "--name-status"], { cwd: root });
11571
11660
  ({ changed: changedFiles, deleted: deletedFiles } = parseNameStatus(stdout));
11572
11661
  if (messageFile) {
11573
- const { readFileSync: readFileSync44, existsSync: existsSync51 } = await import("fs");
11574
- if (existsSync51(messageFile)) commitMessage = readFileSync44(messageFile, "utf8");
11662
+ const { readFileSync: readFileSync45, existsSync: existsSync52 } = await import("fs");
11663
+ if (existsSync52(messageFile)) commitMessage = readFileSync45(messageFile, "utf8");
11575
11664
  }
11576
11665
  } else {
11577
11666
  const base = process.env["GITHUB_BASE_REF"] ?? args[0];
@@ -11675,8 +11764,8 @@ ${BOLD}If the divergence is deliberate${OFF}
11675
11764
  import { execa as execa15 } from "execa";
11676
11765
 
11677
11766
  // src/lib/eventbridge-log-permission-guard.ts
11678
- import { readFileSync as readFileSync33, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
11679
- import { join as join46 } from "path";
11767
+ import { readFileSync as readFileSync34, readdirSync as readdirSync20, statSync as statSync12 } from "fs";
11768
+ import { join as join47 } from "path";
11680
11769
  var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
11681
11770
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
11682
11771
  var LOG_RESOURCE_POLICY_TYPE = "aws_cloudwatch_log_resource_policy";
@@ -11753,7 +11842,7 @@ function walkTerraformFiles(root) {
11753
11842
  return;
11754
11843
  }
11755
11844
  for (const entry of entries) {
11756
- const p = join46(dir, entry);
11845
+ const p = join47(dir, entry);
11757
11846
  let st;
11758
11847
  try {
11759
11848
  st = statSync12(p);
@@ -11796,7 +11885,7 @@ function auditEventBridgeLogPermissions(root) {
11796
11885
  let rawEventTargetCount = 0;
11797
11886
  let rawLogPolicyCount = 0;
11798
11887
  for (const file of files) {
11799
- const text = readFileSync33(file, "utf8");
11888
+ const text = readFileSync34(file, "utf8");
11800
11889
  rawEventTargetCount += countRawResourceDeclarations(text, EVENT_TARGET_TYPE);
11801
11890
  rawLogPolicyCount += countRawResourceDeclarations(text, LOG_RESOURCE_POLICY_TYPE);
11802
11891
  eventTargetBlocks.push(...findResourceBlocks(text, file, EVENT_TARGET_TYPE));
@@ -11889,12 +11978,12 @@ async function runEventBridgeLogPermissionCheck() {
11889
11978
  import { execa as execa16 } from "execa";
11890
11979
 
11891
11980
  // src/lib/lambda-output-guard.ts
11892
- import { readFileSync as readFileSync35 } from "fs";
11893
- import { join as join48 } from "path";
11981
+ import { readFileSync as readFileSync36 } from "fs";
11982
+ import { join as join49 } from "path";
11894
11983
 
11895
11984
  // src/lib/terraform-input-guard.ts
11896
- import { existsSync as existsSync42, readdirSync as readdirSync21, readFileSync as readFileSync34, statSync as statSync13 } from "fs";
11897
- import { join as join47 } from "path";
11985
+ import { existsSync as existsSync43, readdirSync as readdirSync21, readFileSync as readFileSync35, statSync as statSync13 } from "fs";
11986
+ import { join as join48 } from "path";
11898
11987
  var GUARDED_SUBCOMMANDS = [
11899
11988
  "init",
11900
11989
  "plan",
@@ -11908,7 +11997,7 @@ function stripComments2(source) {
11908
11997
  return source.split("\n").map((line) => line.replace(/(^|\s)#.*$/, "$1")).join("\n");
11909
11998
  }
11910
11999
  function vendoredPluginServiceDirs(repoRoot) {
11911
- const servicesDir = join47(repoRoot, "services");
12000
+ const servicesDir = join48(repoRoot, "services");
11912
12001
  const result = /* @__PURE__ */ new Set();
11913
12002
  let entries;
11914
12003
  try {
@@ -11917,9 +12006,9 @@ function vendoredPluginServiceDirs(repoRoot) {
11917
12006
  return result;
11918
12007
  }
11919
12008
  for (const entry of entries) {
11920
- const full = join47(servicesDir, entry);
11921
- if (!existsSync42(full) || !statSync13(full).isDirectory()) continue;
11922
- if (existsSync42(join47(full, "biffo.plugin.json"))) {
12009
+ const full = join48(servicesDir, entry);
12010
+ if (!existsSync43(full) || !statSync13(full).isDirectory()) continue;
12011
+ if (existsSync43(join48(full, "biffo.plugin.json"))) {
11923
12012
  result.add(entry);
11924
12013
  }
11925
12014
  }
@@ -11940,7 +12029,7 @@ function findWorkflowFiles(repoRoot) {
11940
12029
  if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
11941
12030
  continue;
11942
12031
  }
11943
- const full = join47(dir, entry);
12032
+ const full = join48(dir, entry);
11944
12033
  const rel = relative11 ? `${relative11}/${entry}` : entry;
11945
12034
  if (statSync13(full).isDirectory()) {
11946
12035
  walk2(full, rel);
@@ -11984,7 +12073,7 @@ function checkWorkflowSource(file, rawSource) {
11984
12073
  }
11985
12074
  function checkTerraformInput(repoRoot) {
11986
12075
  return findWorkflowFiles(repoRoot).flatMap(
11987
- (file) => checkWorkflowSource(file, readFileSync34(join47(repoRoot, file), "utf8"))
12076
+ (file) => checkWorkflowSource(file, readFileSync35(join48(repoRoot, file), "utf8"))
11988
12077
  );
11989
12078
  }
11990
12079
 
@@ -12042,7 +12131,7 @@ function checkWorkflowSource2(file, rawSource) {
12042
12131
  }
12043
12132
  function checkLambdaOutput(repoRoot) {
12044
12133
  return findWorkflowFiles(repoRoot).flatMap(
12045
- (file) => checkWorkflowSource2(file, readFileSync35(join48(repoRoot, file), "utf8"))
12134
+ (file) => checkWorkflowSource2(file, readFileSync36(join49(repoRoot, file), "utf8"))
12046
12135
  );
12047
12136
  }
12048
12137
 
@@ -12070,8 +12159,8 @@ async function runLambdaOutputCheck() {
12070
12159
  }
12071
12160
 
12072
12161
  // src/scripts/check-pipe-trap.ts
12073
- import { readFileSync as readFileSync36, readdirSync as readdirSync22 } from "fs";
12074
- import { join as join49, relative as relative9 } from "path";
12162
+ import { readFileSync as readFileSync37, readdirSync as readdirSync22 } from "fs";
12163
+ import { join as join50, relative as relative9 } from "path";
12075
12164
  import { execa as execa17 } from "execa";
12076
12165
 
12077
12166
  // src/lib/pipe-trap-guard.ts
@@ -12168,7 +12257,7 @@ function findPipeTraps(source) {
12168
12257
  function shellFiles(root) {
12169
12258
  const out = [];
12170
12259
  for (const dir of ["scripts", ".githooks"]) {
12171
- const full = join49(root, dir);
12260
+ const full = join50(root, dir);
12172
12261
  let entries;
12173
12262
  try {
12174
12263
  entries = readdirSync22(full, { withFileTypes: true });
@@ -12178,7 +12267,7 @@ function shellFiles(root) {
12178
12267
  for (const entry of entries) {
12179
12268
  if (!entry.isFile()) continue;
12180
12269
  if (dir === "scripts" && !entry.name.endsWith(".sh")) continue;
12181
- out.push(join49(full, entry.name));
12270
+ out.push(join50(full, entry.name));
12182
12271
  }
12183
12272
  }
12184
12273
  return out;
@@ -12194,7 +12283,7 @@ async function runPipeTrapCheck() {
12194
12283
  process.exit(1);
12195
12284
  }
12196
12285
  const findings = files.flatMap(
12197
- (file) => findPipeTraps(readFileSync36(file, "utf8")).map(
12286
+ (file) => findPipeTraps(readFileSync37(file, "utf8")).map(
12198
12287
  (t) => `${relative9(root, file)}:${t.line} ${t.text}
12199
12288
  ${t.reason}`
12200
12289
  )
@@ -12214,8 +12303,8 @@ async function runPipeTrapCheck() {
12214
12303
  import { execa as execa18 } from "execa";
12215
12304
 
12216
12305
  // src/lib/plugin-allowlist-convention.ts
12217
- import { readFileSync as readFileSync37 } from "fs";
12218
- import { join as join50 } from "path";
12306
+ import { readFileSync as readFileSync38 } from "fs";
12307
+ import { join as join51 } from "path";
12219
12308
  var COMPUTE_MAIN_TF = "modules/cloud/aws/compute/main.tf";
12220
12309
  var PLUGIN_TEMPLATE_MAIN_TF = "modules/plugins/_template/main.tf";
12221
12310
  var ALLOWLIST_MAIN_TF = "modules/cloud/aws/plugin-allowlist/main.tf";
@@ -12226,7 +12315,7 @@ var PLUGIN = "<plugin>";
12226
12315
  var ACCOUNT = "<account>";
12227
12316
  function read(repoRoot, relative11) {
12228
12317
  try {
12229
- return readFileSync37(join50(repoRoot, relative11), "utf8");
12318
+ return readFileSync38(join51(repoRoot, relative11), "utf8");
12230
12319
  } catch {
12231
12320
  throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
12232
12321
  }
@@ -12347,33 +12436,33 @@ async function runPluginAllowlistConventionCheck() {
12347
12436
  }
12348
12437
 
12349
12438
  // src/scripts/check-plugin-collisions.ts
12350
- import { existsSync as existsSync44 } from "fs";
12351
- import { join as join52 } from "path";
12439
+ import { existsSync as existsSync45 } from "fs";
12440
+ import { join as join53 } from "path";
12352
12441
  import { execa as execa19 } from "execa";
12353
12442
 
12354
12443
  // src/lib/plugin-collision-guard.ts
12355
- import { existsSync as existsSync43, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
12356
- import { join as join51 } from "path";
12444
+ import { existsSync as existsSync44, readdirSync as readdirSync23, statSync as statSync14 } from "fs";
12445
+ import { join as join52 } from "path";
12357
12446
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
12358
12447
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
12359
12448
  function subdirectories(dir) {
12360
- if (!existsSync43(dir)) return [];
12449
+ if (!existsSync44(dir)) return [];
12361
12450
  return readdirSync23(dir).filter((entry) => {
12362
12451
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
12363
12452
  try {
12364
- return statSync14(join51(dir, entry)).isDirectory();
12453
+ return statSync14(join52(dir, entry)).isDirectory();
12365
12454
  } catch {
12366
12455
  return false;
12367
12456
  }
12368
12457
  });
12369
12458
  }
12370
12459
  function regularPackagesOf(pluginDir2) {
12371
- return subdirectories(pluginDir2).filter((name) => existsSync43(join51(pluginDir2, name, "__init__.py"))).sort();
12460
+ return subdirectories(pluginDir2).filter((name) => existsSync44(join52(pluginDir2, name, "__init__.py"))).sort();
12372
12461
  }
12373
12462
  function bareTestModulesOf(pluginDir2) {
12374
- const testsDir = join51(pluginDir2, "tests");
12375
- if (!existsSync43(testsDir)) return [];
12376
- if (existsSync43(join51(testsDir, "__init__.py"))) return [];
12463
+ const testsDir = join52(pluginDir2, "tests");
12464
+ if (!existsSync44(testsDir)) return [];
12465
+ if (existsSync44(join52(testsDir, "__init__.py"))) return [];
12377
12466
  return readdirSync23(testsDir).filter((f) => f.endsWith(".py") && !PYTEST_SPECIAL.has(f)).sort();
12378
12467
  }
12379
12468
  function findCollisions(servicesDir, pluginDirs) {
@@ -12382,7 +12471,7 @@ function findCollisions(servicesDir, pluginDirs) {
12382
12471
  const gather = (kind, namesOf) => {
12383
12472
  const claims = /* @__PURE__ */ new Map();
12384
12473
  for (const plugin of plugins) {
12385
- for (const name of namesOf(join51(servicesDir, plugin))) {
12474
+ for (const name of namesOf(join52(servicesDir, plugin))) {
12386
12475
  claims.set(name, [...claims.get(name) ?? [], plugin]);
12387
12476
  }
12388
12477
  }
@@ -12420,8 +12509,8 @@ function formatCollisions(collisions) {
12420
12509
  // src/scripts/check-plugin-collisions.ts
12421
12510
  async function runPluginCollisionCheck() {
12422
12511
  const root = (await execa19("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
12423
- const servicesDir = join52(root, "services");
12424
- if (!existsSync44(servicesDir)) {
12512
+ const servicesDir = join53(root, "services");
12513
+ if (!existsSync45(servicesDir)) {
12425
12514
  console.log("\u2713 plugin collision guard: no services/ directory \u2014 nothing to compare");
12426
12515
  return;
12427
12516
  }
@@ -12441,8 +12530,8 @@ async function runPluginCollisionCheck() {
12441
12530
  import { execa as execa20 } from "execa";
12442
12531
 
12443
12532
  // src/lib/plugin-terraform-guard.ts
12444
- import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24 } from "fs";
12445
- import { dirname as dirname10, join as join53, relative as relative10, sep as sep4 } from "path";
12533
+ import { existsSync as existsSync46, readFileSync as readFileSync39, readdirSync as readdirSync24 } from "fs";
12534
+ import { dirname as dirname10, join as join54, relative as relative10, sep as sep4 } from "path";
12446
12535
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
12447
12536
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
12448
12537
  function findPluginManifests(root) {
@@ -12457,9 +12546,9 @@ function findPluginManifests(root) {
12457
12546
  for (const entry of entries) {
12458
12547
  if (entry.isDirectory()) {
12459
12548
  if (SKIP_DIRS3.has(entry.name)) continue;
12460
- walk2(join53(dir, entry.name));
12549
+ walk2(join54(dir, entry.name));
12461
12550
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
12462
- found.push(relative10(root, join53(dir, entry.name)).split(sep4).join("/"));
12551
+ found.push(relative10(root, join54(dir, entry.name)).split(sep4).join("/"));
12463
12552
  }
12464
12553
  }
12465
12554
  };
@@ -12469,7 +12558,7 @@ function findPluginManifests(root) {
12469
12558
  function readSubscriptions(absManifestPath) {
12470
12559
  let parsed;
12471
12560
  try {
12472
- parsed = JSON.parse(readFileSync38(absManifestPath, "utf8"));
12561
+ parsed = JSON.parse(readFileSync39(absManifestPath, "utf8"));
12473
12562
  } catch {
12474
12563
  return null;
12475
12564
  }
@@ -12484,14 +12573,14 @@ function readSubscriptions(absManifestPath) {
12484
12573
  }
12485
12574
  function checkPluginTerraform(root) {
12486
12575
  const violations = [];
12487
- const coreManifest = existsSync45(join53(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
12576
+ const coreManifest = existsSync46(join54(root, CORE_MANIFEST_FILE)) ? readCoreManifest(root) : null;
12488
12577
  for (const manifest of findPluginManifests(root)) {
12489
12578
  if (coreManifest && !isTemplateOwned(manifest, coreManifest)) continue;
12490
- const absManifest = join53(root, manifest);
12579
+ const absManifest = join54(root, manifest);
12491
12580
  const subscriptions = readSubscriptions(absManifest);
12492
12581
  if (subscriptions === null) continue;
12493
12582
  const pluginDir2 = dirname10(absManifest);
12494
- if (existsSync45(join53(pluginDir2, "terraform"))) continue;
12583
+ if (existsSync46(join54(pluginDir2, "terraform"))) continue;
12495
12584
  const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
12496
12585
  violations.push({
12497
12586
  manifest,
@@ -12522,13 +12611,13 @@ async function runPluginTerraformCheck() {
12522
12611
  }
12523
12612
 
12524
12613
  // src/scripts/check-plugin-tool-supply.ts
12525
- import { existsSync as existsSync47 } from "fs";
12526
- import { join as join55 } from "path";
12614
+ import { existsSync as existsSync48 } from "fs";
12615
+ import { join as join56 } from "path";
12527
12616
  import { execa as execa21 } from "execa";
12528
12617
 
12529
12618
  // src/lib/plugin-tool-supply-audit.ts
12530
- import { existsSync as existsSync46, readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
12531
- import { join as join54 } from "path";
12619
+ import { existsSync as existsSync47, readFileSync as readFileSync40, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
12620
+ import { join as join55 } from "path";
12532
12621
 
12533
12622
  // src/lib/openrouter-model-snapshot.ts
12534
12623
  var OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT = "2026-08-10T06:39:01Z";
@@ -12945,7 +13034,7 @@ function listDirs(root) {
12945
13034
  }
12946
13035
  return entries.filter((e) => {
12947
13036
  try {
12948
- return statSync15(join54(root, e)).isDirectory();
13037
+ return statSync15(join55(root, e)).isDirectory();
12949
13038
  } catch {
12950
13039
  return false;
12951
13040
  }
@@ -12961,7 +13050,7 @@ function walkFiles2(root, accept, skipDir) {
12961
13050
  return;
12962
13051
  }
12963
13052
  for (const entry of entries) {
12964
- const p = join54(dir, entry);
13053
+ const p = join55(dir, entry);
12965
13054
  let st;
12966
13055
  try {
12967
13056
  st = statSync15(p);
@@ -12987,14 +13076,14 @@ function pluginPythonFiles(pluginDir2) {
12987
13076
  );
12988
13077
  }
12989
13078
  function pluginTerraformFiles(pluginDir2) {
12990
- const tfDir = join54(pluginDir2, "terraform");
13079
+ const tfDir = join55(pluginDir2, "terraform");
12991
13080
  let entries;
12992
13081
  try {
12993
13082
  entries = readdirSync25(tfDir);
12994
13083
  } catch {
12995
13084
  return [];
12996
13085
  }
12997
- return entries.filter((e) => e.endsWith(".tf")).map((e) => join54(tfDir, e)).sort();
13086
+ return entries.filter((e) => e.endsWith(".tf")).map((e) => join55(tfDir, e)).sort();
12998
13087
  }
12999
13088
  function extractManifestTools(manifestText) {
13000
13089
  let parsed;
@@ -13246,8 +13335,8 @@ function isSnapshotStale(fetchedAt, now) {
13246
13335
  function normalizeModelId(id) {
13247
13336
  return id.endsWith(":online") ? id.slice(0, -":online".length) : id;
13248
13337
  }
13249
- var CONFIG_PY_PATH = join54("services", "api", "src", "api", "config.py");
13250
- var ORCHESTRATION_SCHEMA_PATH = join54(
13338
+ var CONFIG_PY_PATH = join55("services", "api", "src", "api", "config.py");
13339
+ var ORCHESTRATION_SCHEMA_PATH = join55(
13251
13340
  "services",
13252
13341
  "api",
13253
13342
  "src",
@@ -13259,10 +13348,10 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13259
13348
  const knownModelIds = options.knownModelIds ?? OPENROUTER_MODEL_IDS;
13260
13349
  const snapshotFetchedAt = options.snapshotFetchedAt ?? OPENROUTER_MODEL_SNAPSHOT_FETCHED_AT;
13261
13350
  const now = options.now ?? /* @__PURE__ */ new Date();
13262
- const configPath = join54(repoRoot, CONFIG_PY_PATH);
13263
- const orchestrationPath = join54(repoRoot, ORCHESTRATION_SCHEMA_PATH);
13264
- const configMissing = !existsSync46(configPath);
13265
- const orchestrationSchemaMissing = !existsSync46(orchestrationPath);
13351
+ const configPath = join55(repoRoot, CONFIG_PY_PATH);
13352
+ const orchestrationPath = join55(repoRoot, ORCHESTRATION_SCHEMA_PATH);
13353
+ const configMissing = !existsSync47(configPath);
13354
+ const orchestrationSchemaMissing = !existsSync47(orchestrationPath);
13266
13355
  const knownSet = new Set(knownModelIds);
13267
13356
  const snapshotEmpty = knownModelIds.length === 0;
13268
13357
  const snapshotStale = isSnapshotStale(snapshotFetchedAt, now);
@@ -13280,13 +13369,13 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13280
13369
  };
13281
13370
  let settingsBlind = false;
13282
13371
  if (!configMissing) {
13283
- const settingsFields = extractSettingsModelFields(readFileSync39(configPath, "utf8"));
13372
+ const settingsFields = extractSettingsModelFields(readFileSync40(configPath, "utf8"));
13284
13373
  if (settingsFields.length === 0) settingsBlind = true;
13285
13374
  for (const { field, value } of settingsFields) record(`${CONFIG_PY_PATH}#${field}`, value);
13286
13375
  }
13287
13376
  let curatedFieldsBlind = false;
13288
13377
  if (!orchestrationSchemaMissing) {
13289
- const curated = extractCuratedModelFields(readFileSync39(orchestrationPath, "utf8"));
13378
+ const curated = extractCuratedModelFields(readFileSync40(orchestrationPath, "utf8"));
13290
13379
  if (curated.rawFieldCount > 0 && curated.fields.every((f) => f.defaultValue === null && f.optionValues.length === 0)) {
13291
13380
  curatedFieldsBlind = true;
13292
13381
  }
@@ -13333,7 +13422,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
13333
13422
  function discoverPluginDirs(pluginsRoot) {
13334
13423
  return listDirs(pluginsRoot).filter((name) => {
13335
13424
  try {
13336
- return statSync15(join54(pluginsRoot, name, "biffo.plugin.json")).isFile();
13425
+ return statSync15(join55(pluginsRoot, name, "biffo.plugin.json")).isFile();
13337
13426
  } catch {
13338
13427
  return false;
13339
13428
  }
@@ -13346,8 +13435,8 @@ function auditPluginToolSupply(pluginsRoot) {
13346
13435
  let terraformBlind = false;
13347
13436
  let totalDeclaredTools = 0;
13348
13437
  for (const name of pluginNames) {
13349
- const pluginDir2 = join54(pluginsRoot, name);
13350
- const manifestText = readFileSync39(join54(pluginDir2, "biffo.plugin.json"), "utf8");
13438
+ const pluginDir2 = join55(pluginsRoot, name);
13439
+ const manifestText = readFileSync40(join55(pluginDir2, "biffo.plugin.json"), "utf8");
13351
13440
  const manifest = extractManifestTools(manifestText);
13352
13441
  if (manifest.parseError) {
13353
13442
  findings.push({
@@ -13365,13 +13454,13 @@ function auditPluginToolSupply(pluginsRoot) {
13365
13454
  totalDeclaredTools += manifest.tools.length;
13366
13455
  const pySources = pluginPythonFiles(pluginDir2).map((f) => ({
13367
13456
  file: f,
13368
- text: readFileSync39(f, "utf8")
13457
+ text: readFileSync40(f, "utf8")
13369
13458
  }));
13370
13459
  const resolver = buildSymbolResolver(pySources);
13371
13460
  const registry = extractToolRegistryEntries(pySources, resolver);
13372
13461
  if (registry.rawToolDefinitionCount > 0 && registry.entries.length === 0) registryBlind = true;
13373
13462
  const tfFiles = pluginTerraformFiles(pluginDir2);
13374
- const tfText = tfFiles.map((f) => readFileSync39(f, "utf8")).join("\n");
13463
+ const tfText = tfFiles.map((f) => readFileSync40(f, "utf8")).join("\n");
13375
13464
  const terraform = extractTerraformEnvKeys(tfText);
13376
13465
  if (terraform.rawMarkerCount > 0 && terraform.resolvedBlockCount === 0) terraformBlind = true;
13377
13466
  for (const toolName of manifest.tools) {
@@ -13445,7 +13534,7 @@ function auditPluginToolSupply(pluginsRoot) {
13445
13534
  requiredEnvVars: envResult.envVars,
13446
13535
  missingEnvVars: anyWired ? [] : envResult.envVars,
13447
13536
  status: anyWired ? "ok" : "missing-env",
13448
- detail: anyWired ? `${entry.predicate}() is satisfiable: at least one of ${JSON.stringify(envResult.envVars)} is wired in Terraform` : `${entry.predicate}() reads ${JSON.stringify(envResult.envVars)} \u2014 NONE of these are wired by any environment_variables block under ${join54(pluginDir2, "terraform")}, so this deployment can never supply it`
13537
+ 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 ${join55(pluginDir2, "terraform")}, so this deployment can never supply it`
13449
13538
  });
13450
13539
  }
13451
13540
  }
@@ -13478,8 +13567,8 @@ function auditPluginToolSupply(pluginsRoot) {
13478
13567
  async function runPluginToolSupplyCheck() {
13479
13568
  const root = (await execa21("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
13480
13569
  let allOk = true;
13481
- const pluginsRoot = join55(root, "services", "_plugins");
13482
- if (!existsSync47(pluginsRoot)) {
13570
+ const pluginsRoot = join56(root, "services", "_plugins");
13571
+ if (!existsSync48(pluginsRoot)) {
13483
13572
  console.log("\u2713 plugin tool-supply guard: no services/_plugins/ \u2014 nothing to audit");
13484
13573
  } else {
13485
13574
  const report = auditPluginToolSupply(pluginsRoot);
@@ -13509,8 +13598,8 @@ async function runPluginToolSupplyCheck() {
13509
13598
  console.log(`\u2713 plugin tool-supply guard: ${report.summary}`);
13510
13599
  }
13511
13600
  }
13512
- const servicesApiRoot = join55(root, "services", "api");
13513
- if (!existsSync47(servicesApiRoot)) {
13601
+ const servicesApiRoot = join56(root, "services", "api");
13602
+ if (!existsSync48(servicesApiRoot)) {
13514
13603
  console.log("\u2713 plugin model-id guard: no services/api/ \u2014 nothing to audit");
13515
13604
  } else {
13516
13605
  const modelReport = auditDeclaredModelIds(root);
@@ -13687,10 +13776,10 @@ async function runReleaseSubjectCheck(argv) {
13687
13776
  }
13688
13777
 
13689
13778
  // src/scripts/check-shared-file-reduction.ts
13690
- import { readFileSync as readFileSync41 } from "fs";
13779
+ import { readFileSync as readFileSync42 } from "fs";
13691
13780
 
13692
13781
  // src/lib/shared-file-reduction-guard.ts
13693
- import { readFileSync as readFileSync40 } from "fs";
13782
+ import { readFileSync as readFileSync41 } from "fs";
13694
13783
  var LEAF_TEST_CALLS = /* @__PURE__ */ new Set(["it", "test"]);
13695
13784
  var SUITE_CALLS = /* @__PURE__ */ new Set(["describe", "suite"]);
13696
13785
  var TEST_FILE_PATTERN = /\.(test|spec)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/;
@@ -13817,7 +13906,7 @@ function formatReductionReport(report) {
13817
13906
  // src/scripts/check-shared-file-reduction.ts
13818
13907
  function readStdin() {
13819
13908
  try {
13820
- return readFileSync41(0, "utf8");
13909
+ return readFileSync42(0, "utf8");
13821
13910
  } catch {
13822
13911
  return "";
13823
13912
  }
@@ -13834,15 +13923,15 @@ function pairsFromTsv(tsv) {
13834
13923
  const [target, existingPath, incomingPath] = fields;
13835
13924
  pairs.push({
13836
13925
  target,
13837
- existing: readFileSync41(existingPath, "utf8"),
13838
- incoming: readFileSync41(incomingPath, "utf8")
13926
+ existing: readFileSync42(existingPath, "utf8"),
13927
+ incoming: readFileSync42(incomingPath, "utf8")
13839
13928
  });
13840
13929
  }
13841
13930
  return pairs;
13842
13931
  }
13843
13932
  function loadAccepted(manifestPath) {
13844
13933
  if (!manifestPath) return {};
13845
- const parsed = JSON.parse(readFileSync41(manifestPath, "utf8"));
13934
+ const parsed = JSON.parse(readFileSync42(manifestPath, "utf8"));
13846
13935
  return parsed.acceptedReductions ?? {};
13847
13936
  }
13848
13937
  async function runSharedFileReductionCheck(args) {
@@ -13850,13 +13939,13 @@ async function runSharedFileReductionCheck(args) {
13850
13939
  let accepted;
13851
13940
  try {
13852
13941
  if (args.pairs) {
13853
- pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync41(args.pairs, "utf8"));
13942
+ pairs = pairsFromTsv(args.pairs === "-" ? readStdin() : readFileSync42(args.pairs, "utf8"));
13854
13943
  } else if (args.target && args.existing && args.incoming) {
13855
13944
  pairs = [
13856
13945
  {
13857
13946
  target: args.target,
13858
- existing: readFileSync41(args.existing, "utf8"),
13859
- incoming: readFileSync41(args.incoming, "utf8")
13947
+ existing: readFileSync42(args.existing, "utf8"),
13948
+ incoming: readFileSync42(args.incoming, "utf8")
13860
13949
  }
13861
13950
  ];
13862
13951
  } else {
@@ -13890,13 +13979,13 @@ async function runSharedFileReductionCheck(args) {
13890
13979
  }
13891
13980
 
13892
13981
  // src/scripts/check-skeleton-drift.ts
13893
- import { existsSync as existsSync48, readdirSync as readdirSync27 } from "fs";
13894
- import { join as join57 } from "path";
13982
+ import { existsSync as existsSync49, readdirSync as readdirSync27 } from "fs";
13983
+ import { join as join58 } from "path";
13895
13984
  import { execa as execa23 } from "execa";
13896
13985
 
13897
13986
  // src/lib/skeleton-drift-guard.ts
13898
- import { readFileSync as readFileSync42, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
13899
- import { join as join56 } from "path";
13987
+ import { readFileSync as readFileSync43, readdirSync as readdirSync26, statSync as statSync16 } from "fs";
13988
+ import { join as join57 } from "path";
13900
13989
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
13901
13990
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
13902
13991
  var uncommented = (contents) => contents.split("\n").filter((line) => !/^\s*(\/\/|\/\*|\*)/.test(line)).join("\n");
@@ -13960,7 +14049,7 @@ function walk(dir, base = dir) {
13960
14049
  }
13961
14050
  for (const entry of entries) {
13962
14051
  if (entry === ".venv" || entry === "node_modules" || entry === ".git") continue;
13963
- const abs = join56(dir, entry);
14052
+ const abs = join57(dir, entry);
13964
14053
  let isDir;
13965
14054
  try {
13966
14055
  isDir = statSync16(abs).isDirectory();
@@ -13982,7 +14071,7 @@ function auditSkeleton(skeletonRoot, name, rules = SKELETON_RULES) {
13982
14071
  if (!rule.appliesTo(rel)) continue;
13983
14072
  let contents;
13984
14073
  try {
13985
- contents = readFileSync42(join56(skeletonRoot, rel), "utf8");
14074
+ contents = readFileSync43(join57(skeletonRoot, rel), "utf8");
13986
14075
  } catch {
13987
14076
  continue;
13988
14077
  }
@@ -14011,23 +14100,23 @@ function formatViolations2(violations) {
14011
14100
 
14012
14101
  // src/scripts/check-skeleton-drift.ts
14013
14102
  function discoverSkeletons(root) {
14014
- const skeletonsDir = join57(root, "_skeletons");
14103
+ const skeletonsDir = join58(root, "_skeletons");
14015
14104
  let entries;
14016
14105
  try {
14017
14106
  entries = readdirSync27(skeletonsDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
14018
14107
  } catch {
14019
14108
  return [];
14020
14109
  }
14021
- return entries.filter((name) => existsSync48(join57(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
14110
+ return entries.filter((name) => existsSync49(join58(skeletonsDir, name, ".github", "workflows", "ci.yml"))).sort();
14022
14111
  }
14023
14112
  async function runSkeletonDriftCheck() {
14024
14113
  const root = (await execa23("git", ["rev-parse", "--show-toplevel"])).stdout.trim();
14025
14114
  const skeletons = discoverSkeletons(root);
14026
14115
  let filesConsidered = 0;
14027
14116
  for (const name of skeletons) {
14028
- const skeletonRoot = join57(root, "_skeletons", name);
14117
+ const skeletonRoot = join58(root, "_skeletons", name);
14029
14118
  filesConsidered += findWorkflowFiles(skeletonRoot).length;
14030
- if (existsSync48(join57(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
14119
+ if (existsSync49(join58(skeletonRoot, "apps", "frontend", "src", "app", "layout.tsx"))) {
14031
14120
  filesConsidered += 1;
14032
14121
  }
14033
14122
  }
@@ -14041,7 +14130,7 @@ async function runSkeletonDriftCheck() {
14041
14130
  process.exit(1);
14042
14131
  }
14043
14132
  const violations = skeletons.flatMap(
14044
- (name) => auditSkeleton(join57(root, "_skeletons", name), name)
14133
+ (name) => auditSkeleton(join58(root, "_skeletons", name), name)
14045
14134
  );
14046
14135
  if (violations.length > 0) {
14047
14136
  console.error("\u2717 Skeleton-drift guard: drift found between this repo and its scaffolding\n");
@@ -14191,8 +14280,8 @@ function rawArgsAfter(subcommand) {
14191
14280
  }
14192
14281
 
14193
14282
  // src/commands/doctor.ts
14194
- import { existsSync as existsSync49, readFileSync as readFileSync43 } from "fs";
14195
- import { join as join58, resolve as resolve20 } from "path";
14283
+ import { existsSync as existsSync50, readFileSync as readFileSync44 } from "fs";
14284
+ import { join as join59, resolve as resolve20 } from "path";
14196
14285
  import chalk21 from "chalk";
14197
14286
  import { Command as Command25 } from "commander";
14198
14287
 
@@ -14371,10 +14460,10 @@ async function runDoctor(options, deps = { git: new GitAdapter() }) {
14371
14460
  return runDoctorChecks(facts);
14372
14461
  }
14373
14462
  function readLocalCoreVersion(cwd) {
14374
- const path = join58(cwd, INSTANCE_CORE_FILE);
14375
- if (!existsSync49(path)) return null;
14463
+ const path = join59(cwd, INSTANCE_CORE_FILE);
14464
+ if (!existsSync50(path)) return null;
14376
14465
  try {
14377
- return extractVersionField(readFileSync43(path, "utf8"));
14466
+ return extractVersionField(readFileSync44(path, "utf8"));
14378
14467
  } catch {
14379
14468
  return null;
14380
14469
  }
@@ -14394,10 +14483,10 @@ function extractVersionField(contents) {
14394
14483
  return match?.[1] ?? null;
14395
14484
  }
14396
14485
  function readFossil(cwd) {
14397
- const path = join58(cwd, CORE_VERSION_FILE);
14398
- if (!existsSync49(path)) return null;
14486
+ const path = join59(cwd, CORE_VERSION_FILE);
14487
+ if (!existsSync50(path)) return null;
14399
14488
  try {
14400
- const value = readFileSync43(path, "utf8").trim();
14489
+ const value = readFileSync44(path, "utf8").trim();
14401
14490
  return value === "" ? null : value;
14402
14491
  } catch {
14403
14492
  return null;
@@ -14846,13 +14935,13 @@ import { fileURLToPath as fileURLToPath6 } from "url";
14846
14935
  import { Command as Command27 } from "commander";
14847
14936
 
14848
14937
  // src/lib/packaged-scripts.ts
14849
- import { existsSync as existsSync50 } from "fs";
14850
- import { dirname as dirname11, join as join59 } from "path";
14938
+ import { existsSync as existsSync51 } from "fs";
14939
+ import { dirname as dirname11, join as join60 } from "path";
14851
14940
  function findPackagedScript(startDir, relativePath) {
14852
14941
  let dir = startDir;
14853
14942
  for (; ; ) {
14854
- const candidate = join59(dir, relativePath);
14855
- if (existsSync50(candidate)) return candidate;
14943
+ const candidate = join60(dir, relativePath);
14944
+ if (existsSync51(candidate)) return candidate;
14856
14945
  const parent = dirname11(dir);
14857
14946
  if (parent === dir) return null;
14858
14947
  dir = parent;