@biffo/cli 0.283.1 → 0.283.3

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 +160 -66
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3007,8 +3007,16 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
3007
3007
  ];
3008
3008
 
3009
3009
  // src/lib/plugin-terraform-wiring.ts
3010
- import { existsSync as existsSync11, mkdirSync as mkdirSync3, readFileSync as readFileSync9, readdirSync as readdirSync3, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "fs";
3011
- import { join as join12 } from "path";
3010
+ import {
3011
+ existsSync as existsSync11,
3012
+ mkdirSync as mkdirSync3,
3013
+ readFileSync as readFileSync9,
3014
+ readdirSync as readdirSync3,
3015
+ rmSync as rmSync4,
3016
+ statSync as statSync2,
3017
+ writeFileSync as writeFileSync4
3018
+ } from "fs";
3019
+ import { join as join12, relative as relative2, sep as sep2 } from "path";
3012
3020
  var TEMPLATE_MODULE_DIR = "_template";
3013
3021
  var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
3014
3022
  var GENERATED_TF_FILE = "plugins.generated.tf";
@@ -3096,6 +3104,62 @@ function staleFirstPartyCopies(cwd) {
3096
3104
  const copied = new Set(listPluginModules(cwd));
3097
3105
  return firstPartyPluginNames(cwd).filter((name) => copied.has(name));
3098
3106
  }
3107
+ function walkTfFiles(root) {
3108
+ const out = [];
3109
+ const walk2 = (dir) => {
3110
+ let entries;
3111
+ try {
3112
+ entries = readdirSync3(dir);
3113
+ } catch {
3114
+ return;
3115
+ }
3116
+ for (const entry of entries) {
3117
+ const p = join12(dir, entry);
3118
+ let st;
3119
+ try {
3120
+ st = statSync2(p);
3121
+ } catch {
3122
+ continue;
3123
+ }
3124
+ if (st.isDirectory()) {
3125
+ walk2(p);
3126
+ continue;
3127
+ }
3128
+ if (entry.endsWith(".tf")) out.push(p);
3129
+ }
3130
+ };
3131
+ walk2(root);
3132
+ return out.sort();
3133
+ }
3134
+ function escapeRegExp(value) {
3135
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3136
+ }
3137
+ function findPluginModuleReferences(cwd, name) {
3138
+ const infraDir = join12(cwd, "infra");
3139
+ const sourceSegments = ["modules", "plugins", name];
3140
+ const sourceLinePattern = /^\s*source\s*=\s*"([^"]+)"/;
3141
+ const moduleRefPattern = new RegExp(`module\\.plugin_${escapeRegExp(name)}(?![A-Za-z0-9_-])`);
3142
+ const refs = [];
3143
+ for (const absPath of walkTfFiles(infraDir)) {
3144
+ let contents;
3145
+ try {
3146
+ contents = readFileSync9(absPath, "utf8");
3147
+ } catch {
3148
+ continue;
3149
+ }
3150
+ const relPath = relative2(cwd, absPath).split(sep2).join("/");
3151
+ contents.split("\n").forEach((rawLine, idx) => {
3152
+ const sourceValue = sourceLinePattern.exec(rawLine)?.[1];
3153
+ const sourceValueSegments = sourceValue?.split("/").filter((s) => s.length > 0 && s !== ".");
3154
+ const isSourceRef = sourceValueSegments !== void 0 && sourceValueSegments.slice(-sourceSegments.length).join("/") === sourceSegments.join("/");
3155
+ const isExprRef = moduleRefPattern.test(rawLine);
3156
+ if (isSourceRef || isExprRef) {
3157
+ refs.push({ file: relPath, line: idx + 1, text: rawLine.trim() });
3158
+ }
3159
+ });
3160
+ }
3161
+ return refs;
3162
+ }
3099
3163
  function listEnvironments(cwd) {
3100
3164
  const dir = join12(cwd, "infra", "environments");
3101
3165
  let entries;
@@ -4643,7 +4707,7 @@ import {
4643
4707
  readdirSync as readdirSync4,
4644
4708
  readFileSync as readFileSync10,
4645
4709
  rmSync as rmSync6,
4646
- statSync as statSync2,
4710
+ statSync as statSync3,
4647
4711
  writeFileSync as writeFileSync5
4648
4712
  } from "fs";
4649
4713
  import { homedir } from "os";
@@ -4677,7 +4741,7 @@ function findLatestSession() {
4677
4741
  if (files.length === 0) return null;
4678
4742
  const sorted = files.map((f) => {
4679
4743
  const fullPath = join16(dir, f);
4680
- const mtime = existsSync15(fullPath) ? statSync2(fullPath).mtimeMs : -1;
4744
+ const mtime = existsSync15(fullPath) ? statSync3(fullPath).mtimeMs : -1;
4681
4745
  return { f, mtime };
4682
4746
  }).sort((a, b) => b.mtime - a.mtime);
4683
4747
  try {
@@ -4875,7 +4939,7 @@ async function resolveConfig(options) {
4875
4939
 
4876
4940
  // src/commands/data-import.ts
4877
4941
  import { execSync as execSync3 } from "child_process";
4878
- import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync3 } from "fs";
4942
+ import { cpSync, existsSync as existsSync17, mkdirSync as mkdirSync5, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
4879
4943
  import { join as join17, resolve as resolve5 } from "path";
4880
4944
  import chalk6 from "chalk";
4881
4945
  import { Command as Command6 } from "commander";
@@ -4928,7 +4992,7 @@ async function runDataImport(name, options, deps) {
4928
4992
  `DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
4929
4993
  );
4930
4994
  }
4931
- const isLocalDir = existsSync17(options.source) && statSync3(options.source).isDirectory();
4995
+ const isLocalDir = existsSync17(options.source) && statSync4(options.source).isDirectory();
4932
4996
  let sourceDir;
4933
4997
  let cleanupClone = null;
4934
4998
  if (isLocalDir) {
@@ -6013,8 +6077,8 @@ import { Command as Command12 } from "commander";
6013
6077
  import inquirer5 from "inquirer";
6014
6078
 
6015
6079
  // src/lib/build-freshness.ts
6016
- import { existsSync as existsSync20, readdirSync as readdirSync7, statSync as statSync4 } from "fs";
6017
- import { dirname as dirname5, join as join19, relative as relative2, sep as sep2 } from "path";
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";
6018
6082
  import { fileURLToPath as fileURLToPath3 } from "url";
6019
6083
  var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
6020
6084
  function checkBuildFreshness(options = {}) {
@@ -6048,8 +6112,8 @@ function checkBuildFreshness(options = {}) {
6048
6112
  if (!existsSync20(entry)) {
6049
6113
  return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
6050
6114
  }
6051
- const builtAt = statSync4(entry).mtimeMs;
6052
- const newer = collectSourceFiles(srcDir).map((file) => ({ file, mtimeMs: statSync4(file).mtimeMs })).filter((f) => f.mtimeMs > builtAt).sort((a, b) => b.mtimeMs - a.mtimeMs).map((f) => relative2(packageRoot, f.file));
6115
+ const builtAt = statSync5(entry).mtimeMs;
6116
+ const newer = collectSourceFiles(srcDir).map((file) => ({ file, mtimeMs: statSync5(file).mtimeMs })).filter((f) => f.mtimeMs > builtAt).sort((a, b) => b.mtimeMs - a.mtimeMs).map((f) => relative3(packageRoot, f.file));
6053
6117
  if (newer.length === 0) {
6054
6118
  return { status: "fresh", reason: "dist is newer than src", newerSources: [] };
6055
6119
  }
@@ -6116,8 +6180,8 @@ function findPackageRoot(from) {
6116
6180
  }
6117
6181
  function isInside(parent, child) {
6118
6182
  if (child === parent) return true;
6119
- const rel = relative2(parent, child);
6120
- return rel !== "" && !rel.startsWith("..") && !rel.startsWith(`..${sep2}`);
6183
+ const rel = relative3(parent, child);
6184
+ return rel !== "" && !rel.startsWith("..") && !rel.startsWith(`..${sep3}`);
6121
6185
  }
6122
6186
 
6123
6187
  // src/lib/credentials.ts
@@ -6402,7 +6466,7 @@ import {
6402
6466
  readdirSync as readdirSync8,
6403
6467
  readFileSync as readFileSync15,
6404
6468
  rmSync as rmSync7,
6405
- statSync as statSync5,
6469
+ statSync as statSync6,
6406
6470
  writeFileSync as writeFileSync6
6407
6471
  } from "fs";
6408
6472
  import { homedir as homedir3 } from "os";
@@ -8479,8 +8543,8 @@ function printEntry(entry) {
8479
8543
  }
8480
8544
 
8481
8545
  // src/commands/plugin-install.ts
8482
- import { cpSync as cpSync5, existsSync as existsSync30, mkdirSync as mkdirSync11, readFileSync as readFileSync22, statSync as statSync6 } from "fs";
8483
- import { join as join32, relative as relative3, resolve as resolve12 } from "path";
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";
8484
8548
  import chalk15 from "chalk";
8485
8549
  import { Command as Command15 } from "commander";
8486
8550
 
@@ -8794,8 +8858,8 @@ function ensureWorkspaceSources(pluginPyprojectPath, memberNames) {
8794
8858
  updated = `${text.slice(0, insertAt)}
8795
8859
  ${lines.join("\n")}${text.slice(insertAt)}`;
8796
8860
  } else {
8797
- const sep4 = text.endsWith("\n") ? "" : "\n";
8798
- updated = `${text}${sep4}
8861
+ const sep5 = text.endsWith("\n") ? "" : "\n";
8862
+ updated = `${text}${sep5}
8799
8863
  # Vendored into this instance by \`biffo plugin install\`: resolve dependencies the
8800
8864
  # instance's uv workspace provides as members from the workspace, not PyPI.
8801
8865
  [tool.uv.sources]
@@ -8850,7 +8914,7 @@ function resolveLocalPlugin(localPath) {
8850
8914
  if (!existsSync30(localPath)) {
8851
8915
  throw new Error(`--local path does not exist: ${localPath}`);
8852
8916
  }
8853
- if (!statSync6(localPath).isDirectory()) {
8917
+ if (!statSync7(localPath).isDirectory()) {
8854
8918
  throw new Error(`--local path is not a directory: ${localPath}`);
8855
8919
  }
8856
8920
  const manifestPath = join32(localPath, "biffo.plugin.json");
@@ -9009,10 +9073,10 @@ async function runPluginInstall(target, options, deps) {
9009
9073
  log.info(`Generating migration for ${relTargetDir}/'s ${manifest.tables.length} table(s)...`);
9010
9074
  const generatedPaths = await deps.migrations.generate(options.cwd, [pluginName]);
9011
9075
  for (const absPath of generatedPaths) {
9012
- stagePaths.push(relative3(options.cwd, absPath));
9076
+ stagePaths.push(relative4(options.cwd, absPath));
9013
9077
  }
9014
9078
  if (generatedPaths.length > 0) {
9015
- log.success(`Generated migration: ${relative3(options.cwd, generatedPaths[0])}`);
9079
+ log.success(`Generated migration: ${relative4(options.cwd, generatedPaths[0])}`);
9016
9080
  }
9017
9081
  } else {
9018
9082
  log.info(`${pluginName} declares no tables \u2014 nothing to migrate.`);
@@ -9143,8 +9207,8 @@ import { resolve as resolve14 } from "path";
9143
9207
  import { Command as Command17 } from "commander";
9144
9208
 
9145
9209
  // src/lib/plugin-staleness.ts
9146
- import { existsSync as existsSync32, readFileSync as readFileSync24, readdirSync as readdirSync14, statSync as statSync7 } from "fs";
9147
- import { join as join34, relative as relative4 } from "path";
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";
9148
9212
  function discoverVendoredPlugins(servicesDir) {
9149
9213
  if (!existsSync32(servicesDir)) return [];
9150
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();
@@ -9347,11 +9411,11 @@ function walkExcluding(root, dir, excludes) {
9347
9411
  for (const entry of readdirSync14(dir)) {
9348
9412
  if (excludes.has(entry) || entry === ".git") continue;
9349
9413
  const full = join34(dir, entry);
9350
- const stat = statSync7(full);
9414
+ const stat = statSync8(full);
9351
9415
  if (stat.isDirectory()) {
9352
9416
  out.push(...walkExcluding(root, full, excludes));
9353
9417
  } else {
9354
- out.push(relative4(root, full));
9418
+ out.push(relative5(root, full));
9355
9419
  }
9356
9420
  }
9357
9421
  return out;
@@ -9399,7 +9463,7 @@ var pluginStalenessCommand = new Command17("staleness").description(
9399
9463
 
9400
9464
  // src/commands/plugin-sync-migrations.ts
9401
9465
  import { existsSync as existsSync33 } from "fs";
9402
- import { join as join35, relative as relative5, resolve as resolve15 } from "path";
9466
+ import { join as join35, relative as relative6, resolve as resolve15 } from "path";
9403
9467
  import chalk17 from "chalk";
9404
9468
  import { Command as Command18 } from "commander";
9405
9469
  var pluginSyncMigrationsCommand = new Command18("sync-migrations").description(
@@ -9440,7 +9504,7 @@ async function runPluginSyncMigrations(name, options, deps) {
9440
9504
  );
9441
9505
  return;
9442
9506
  }
9443
- const relativePaths = generated.map((p) => relative5(options.cwd, p));
9507
+ const relativePaths = generated.map((p) => relative6(options.cwd, p));
9444
9508
  for (const p of relativePaths) {
9445
9509
  log.success(`Generated ${p}`);
9446
9510
  }
@@ -9532,6 +9596,19 @@ async function runPluginUninstall(name, options, deps) {
9532
9596
  `${options.cwd} is not a git repository \u2014 biffo plugin uninstall must be run from a Biffo project checkout.`
9533
9597
  );
9534
9598
  }
9599
+ if (existsSync34(modulesDir)) {
9600
+ const refs = findPluginModuleReferences(options.cwd, name).filter(
9601
+ (r) => !r.file.endsWith(`/${GENERATED_TF_FILE}`) && r.file !== GENERATED_TF_FILE
9602
+ );
9603
+ if (refs.length > 0) {
9604
+ const refList = refs.map((r) => ` ${r.file}:${r.line} ${r.text}`).join("\n");
9605
+ throw new Error(
9606
+ `Refusing to uninstall '${name}' \u2014 its Terraform module is referenced outside the generated wiring, so removing it would break these hand-authored file(s):
9607
+ ${refList}
9608
+ Remove the reference(s) above first, then re-run uninstall.`
9609
+ );
9610
+ }
9611
+ }
9535
9612
  rmSync9(targetDir, { recursive: true, force: true });
9536
9613
  log.success(`Removed services/${name}/`);
9537
9614
  if (existsSync34(modulesDir)) {
@@ -9613,7 +9690,7 @@ function printDryRun5(name, version, stagePaths, keepData) {
9613
9690
 
9614
9691
  // src/commands/plugin-upgrade.ts
9615
9692
  import { cpSync as cpSync6, existsSync as existsSync35, mkdirSync as mkdirSync12, readFileSync as readFileSync26, rmSync as rmSync10 } from "fs";
9616
- import { join as join37, relative as relative6, resolve as resolve17 } from "path";
9693
+ import { join as join37, relative as relative7, resolve as resolve17 } from "path";
9617
9694
  import chalk19 from "chalk";
9618
9695
  import { Command as Command20 } from "commander";
9619
9696
  import inquirer7 from "inquirer";
@@ -9713,6 +9790,9 @@ async function runPluginUpgrade(target, options, deps) {
9713
9790
  log.success(
9714
9791
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9715
9792
  );
9793
+ if (!existsSync35(join37(tmpDir, "terraform"))) {
9794
+ refuseIfModuleStillReferenced(options.cwd, modulesDir, entry.name);
9795
+ }
9716
9796
  const previousProvenance = readProvenance(targetDir);
9717
9797
  rmSync10(targetDir, { recursive: true, force: true });
9718
9798
  mkdirSync12(targetDir, { recursive: true });
@@ -9741,10 +9821,10 @@ async function runPluginUpgrade(target, options, deps) {
9741
9821
  );
9742
9822
  const generatedPaths = await deps.migrations.generate(options.cwd, [entry.name]);
9743
9823
  for (const absPath of generatedPaths) {
9744
- stagePaths.push(relative6(options.cwd, absPath));
9824
+ stagePaths.push(relative7(options.cwd, absPath));
9745
9825
  }
9746
9826
  if (generatedPaths.length > 0) {
9747
- log.success(`Generated migration: ${relative6(options.cwd, generatedPaths[0])}`);
9827
+ log.success(`Generated migration: ${relative7(options.cwd, generatedPaths[0])}`);
9748
9828
  } else {
9749
9829
  log.info(
9750
9830
  `${entry.name}'s tables and columns already match the manifest \u2014 no migration needed.`
@@ -9805,6 +9885,9 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9805
9885
  log.success(
9806
9886
  `Manifest valid \u2014 ${manifest.tables.length} table(s), ${manifest.api_routes.length} route(s)`
9807
9887
  );
9888
+ if (!existsSync35(join37(source.sourceDir, "terraform"))) {
9889
+ refuseIfModuleStillReferenced(options.cwd, modulesDir, source.name);
9890
+ }
9808
9891
  const previousProvenance = readProvenance(targetDir);
9809
9892
  if (inTreeSource) {
9810
9893
  log.info(
@@ -9836,10 +9919,10 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9836
9919
  );
9837
9920
  const generatedPaths = await deps.migrations.generate(options.cwd, [source.name]);
9838
9921
  for (const absPath of generatedPaths) {
9839
- stagePaths.push(relative6(options.cwd, absPath));
9922
+ stagePaths.push(relative7(options.cwd, absPath));
9840
9923
  }
9841
9924
  if (generatedPaths.length > 0) {
9842
- log.success(`Generated migration: ${relative6(options.cwd, generatedPaths[0])}`);
9925
+ log.success(`Generated migration: ${relative7(options.cwd, generatedPaths[0])}`);
9843
9926
  } else {
9844
9927
  log.info(
9845
9928
  `${source.name}'s tables and columns already match the manifest \u2014 no migration needed.`
@@ -9870,6 +9953,17 @@ async function runLocalPluginRefresh(localPath, options, deps) {
9870
9953
  source.cleanup();
9871
9954
  }
9872
9955
  }
9956
+ function refuseIfModuleStillReferenced(cwd, modulesDir, name) {
9957
+ if (!existsSync35(modulesDir)) return;
9958
+ const refs = findPluginModuleReferences(cwd, name);
9959
+ if (refs.length === 0) return;
9960
+ const refList = refs.map((r) => ` ${r.file}:${r.line} ${r.text}`).join("\n");
9961
+ throw new Error(
9962
+ `Refusing to remove modules/plugins/${name}/ \u2014 '${name}' ships no terraform/ directory in this version, but the module is still referenced:
9963
+ ${refList}
9964
+ A plugin repo without terraform/ is missing the directory, not declaring that the module should go, and a wrong destructive action here is worse than no action. Either remove the reference(s) above yourself, or run 'biffo plugin uninstall ${name}', which removes the module and unwires the reference together.`
9965
+ );
9966
+ }
9873
9967
  function readInstalledVersion2(targetDir) {
9874
9968
  const manifestPath = join37(targetDir, "biffo.plugin.json");
9875
9969
  if (!existsSync35(manifestPath)) return void 0;
@@ -10595,11 +10689,11 @@ async function runBranchProtectionCheck(explicitRepo, options = {}) {
10595
10689
 
10596
10690
  // src/scripts/check-codeql-suppression.ts
10597
10691
  import { existsSync as existsSync39 } from "fs";
10598
- import { join as join41, relative as relative7 } from "path";
10692
+ import { join as join41, relative as relative8 } from "path";
10599
10693
  import { execa as execa9 } from "execa";
10600
10694
 
10601
10695
  // src/lib/codeql-suppression-guard.ts
10602
- import { readdirSync as readdirSync16, readFileSync as readFileSync29, statSync as statSync8 } from "fs";
10696
+ import { readdirSync as readdirSync16, readFileSync as readFileSync29, statSync as statSync9 } from "fs";
10603
10697
  import { join as join40 } from "path";
10604
10698
  var SKIP_DIRS = /* @__PURE__ */ new Set([
10605
10699
  ".git",
@@ -10634,7 +10728,7 @@ function walkSourceFiles(root) {
10634
10728
  const p = join40(dir, entry);
10635
10729
  let st;
10636
10730
  try {
10637
- st = statSync8(p);
10731
+ st = statSync9(p);
10638
10732
  } catch {
10639
10733
  continue;
10640
10734
  }
@@ -10681,7 +10775,7 @@ async function runCodeqlSuppressionCheck() {
10681
10775
  "\u2717 codeql-suppression guard: found a `codeql[...]`-shaped comment, which does not suppress anything in this repo (#1491) \u2014 dismiss the real alert instead (UI, or `PATCH .../code-scanning/alerts/<n>` with a recorded reason)\n"
10682
10776
  );
10683
10777
  for (const hit of hits) {
10684
- console.error(` ${relative7(root, hit.path)}:${hit.line} ${hit.text.trim()}`);
10778
+ console.error(` ${relative8(root, hit.path)}:${hit.line} ${hit.text.trim()}`);
10685
10779
  }
10686
10780
  process.exit(1);
10687
10781
  }
@@ -10696,7 +10790,7 @@ async function runCodeqlSuppressionCheck() {
10696
10790
  import { execa as execa10 } from "execa";
10697
10791
 
10698
10792
  // src/lib/cognito-invite-template-guard.ts
10699
- import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync9 } from "fs";
10793
+ import { readdirSync as readdirSync17, readFileSync as readFileSync30, statSync as statSync10 } from "fs";
10700
10794
  import { join as join42 } from "path";
10701
10795
  var REQUIRED_INVITE_MEMBERS = ["email_subject", "email_message", "sms_message"];
10702
10796
  var REQUIRED_INVITE_PLACEHOLDERS = ["{username}", "{####}"];
@@ -10771,7 +10865,7 @@ function memberBody(blockBody, member) {
10771
10865
  }
10772
10866
  function findModuleTerraformFiles(repoRoot) {
10773
10867
  const found = [];
10774
- const walk2 = (dir, relative10) => {
10868
+ const walk2 = (dir, relative11) => {
10775
10869
  let entries;
10776
10870
  try {
10777
10871
  entries = readdirSync17(dir);
@@ -10781,8 +10875,8 @@ function findModuleTerraformFiles(repoRoot) {
10781
10875
  for (const entry of entries) {
10782
10876
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
10783
10877
  const full = join42(dir, entry);
10784
- const rel = `${relative10}/${entry}`;
10785
- if (statSync9(full).isDirectory()) {
10878
+ const rel = `${relative11}/${entry}`;
10879
+ if (statSync10(full).isDirectory()) {
10786
10880
  walk2(full, rel);
10787
10881
  } else if (entry.endsWith(".tf")) {
10788
10882
  found.push(rel);
@@ -10826,7 +10920,7 @@ import { join as join44 } from "path";
10826
10920
  import { execa as execa11 } from "execa";
10827
10921
 
10828
10922
  // src/lib/core-direct-paths-audit.ts
10829
- import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync18, statSync as statSync10 } from "fs";
10923
+ import { existsSync as existsSync40, readFileSync as readFileSync31, readdirSync as readdirSync18, statSync as statSync11 } from "fs";
10830
10924
  import { join as join43 } from "path";
10831
10925
  var EXTERNAL_BASE_IDENTIFIERS = ["CORE_API_URL"];
10832
10926
  var API_ROUTE_PREFIX = "/api/v1";
@@ -10994,7 +11088,7 @@ function walkFiles(root, accept, skipDir) {
10994
11088
  const p = join43(dir, entry);
10995
11089
  let st;
10996
11090
  try {
10997
- st = statSync10(p);
11091
+ st = statSync11(p);
10998
11092
  } catch {
10999
11093
  continue;
11000
11094
  }
@@ -11340,7 +11434,7 @@ ${BOLD}If the divergence is deliberate${OFF}
11340
11434
  import { execa as execa13 } from "execa";
11341
11435
 
11342
11436
  // src/lib/eventbridge-log-permission-guard.ts
11343
- import { readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync11 } from "fs";
11437
+ import { readFileSync as readFileSync32, readdirSync as readdirSync19, statSync as statSync12 } from "fs";
11344
11438
  import { join as join45 } from "path";
11345
11439
  var SKIP_DIRS2 = /* @__PURE__ */ new Set(["node_modules", ".git", ".terraform", ".worktrees", "dist"]);
11346
11440
  var EVENT_TARGET_TYPE = "aws_cloudwatch_event_target";
@@ -11421,7 +11515,7 @@ function walkTerraformFiles(root) {
11421
11515
  const p = join45(dir, entry);
11422
11516
  let st;
11423
11517
  try {
11424
- st = statSync11(p);
11518
+ st = statSync12(p);
11425
11519
  } catch {
11426
11520
  continue;
11427
11521
  }
@@ -11558,7 +11652,7 @@ import { readFileSync as readFileSync34 } from "fs";
11558
11652
  import { join as join47 } from "path";
11559
11653
 
11560
11654
  // src/lib/terraform-input-guard.ts
11561
- import { existsSync as existsSync41, readdirSync as readdirSync20, readFileSync as readFileSync33, statSync as statSync12 } from "fs";
11655
+ import { existsSync as existsSync41, readdirSync as readdirSync20, readFileSync as readFileSync33, statSync as statSync13 } from "fs";
11562
11656
  import { join as join46 } from "path";
11563
11657
  var GUARDED_SUBCOMMANDS = [
11564
11658
  "init",
@@ -11583,7 +11677,7 @@ function vendoredPluginServiceDirs(repoRoot) {
11583
11677
  }
11584
11678
  for (const entry of entries) {
11585
11679
  const full = join46(servicesDir, entry);
11586
- if (!existsSync41(full) || !statSync12(full).isDirectory()) continue;
11680
+ if (!existsSync41(full) || !statSync13(full).isDirectory()) continue;
11587
11681
  if (existsSync41(join46(full, "biffo.plugin.json"))) {
11588
11682
  result.add(entry);
11589
11683
  }
@@ -11593,7 +11687,7 @@ function vendoredPluginServiceDirs(repoRoot) {
11593
11687
  function findWorkflowFiles(repoRoot) {
11594
11688
  const found = [];
11595
11689
  const vendoredPluginDirs = vendoredPluginServiceDirs(repoRoot);
11596
- const walk2 = (dir, relative10) => {
11690
+ const walk2 = (dir, relative11) => {
11597
11691
  let entries;
11598
11692
  try {
11599
11693
  entries = readdirSync20(dir);
@@ -11602,14 +11696,14 @@ function findWorkflowFiles(repoRoot) {
11602
11696
  }
11603
11697
  for (const entry of entries) {
11604
11698
  if (entry === "node_modules" || entry === ".git" || entry === ".worktrees") continue;
11605
- if (entry === ".github" && relative10.startsWith("services/") && vendoredPluginDirs.has(relative10.slice("services/".length))) {
11699
+ if (entry === ".github" && relative11.startsWith("services/") && vendoredPluginDirs.has(relative11.slice("services/".length))) {
11606
11700
  continue;
11607
11701
  }
11608
11702
  const full = join46(dir, entry);
11609
- const rel = relative10 ? `${relative10}/${entry}` : entry;
11610
- if (statSync12(full).isDirectory()) {
11703
+ const rel = relative11 ? `${relative11}/${entry}` : entry;
11704
+ if (statSync13(full).isDirectory()) {
11611
11705
  walk2(full, rel);
11612
- } else if (/\.ya?ml$/.test(entry) && relative10.endsWith(".github/workflows")) {
11706
+ } else if (/\.ya?ml$/.test(entry) && relative11.endsWith(".github/workflows")) {
11613
11707
  found.push(rel);
11614
11708
  }
11615
11709
  }
@@ -11736,7 +11830,7 @@ async function runLambdaOutputCheck() {
11736
11830
 
11737
11831
  // src/scripts/check-pipe-trap.ts
11738
11832
  import { readFileSync as readFileSync35, readdirSync as readdirSync21 } from "fs";
11739
- import { join as join48, relative as relative8 } from "path";
11833
+ import { join as join48, relative as relative9 } from "path";
11740
11834
  import { execa as execa15 } from "execa";
11741
11835
 
11742
11836
  // src/lib/pipe-trap-guard.ts
@@ -11860,7 +11954,7 @@ async function runPipeTrapCheck() {
11860
11954
  }
11861
11955
  const findings = files.flatMap(
11862
11956
  (file) => findPipeTraps(readFileSync35(file, "utf8")).map(
11863
- (t) => `${relative8(root, file)}:${t.line} ${t.text}
11957
+ (t) => `${relative9(root, file)}:${t.line} ${t.text}
11864
11958
  ${t.reason}`
11865
11959
  )
11866
11960
  );
@@ -11889,11 +11983,11 @@ var PROJECT = "<project>";
11889
11983
  var ENV = "<env>";
11890
11984
  var PLUGIN = "<plugin>";
11891
11985
  var ACCOUNT = "<account>";
11892
- function read(repoRoot, relative10) {
11986
+ function read(repoRoot, relative11) {
11893
11987
  try {
11894
- return readFileSync36(join49(repoRoot, relative10), "utf8");
11988
+ return readFileSync36(join49(repoRoot, relative11), "utf8");
11895
11989
  } catch {
11896
- throw new Error(`plugin-allowlist drift guard: cannot read ${relative10}`);
11990
+ throw new Error(`plugin-allowlist drift guard: cannot read ${relative11}`);
11897
11991
  }
11898
11992
  }
11899
11993
  function assignedString(source, name) {
@@ -12017,7 +12111,7 @@ import { join as join51 } from "path";
12017
12111
  import { execa as execa17 } from "execa";
12018
12112
 
12019
12113
  // src/lib/plugin-collision-guard.ts
12020
- import { existsSync as existsSync42, readdirSync as readdirSync22, statSync as statSync13 } from "fs";
12114
+ import { existsSync as existsSync42, readdirSync as readdirSync22, statSync as statSync14 } from "fs";
12021
12115
  import { join as join50 } from "path";
12022
12116
  var PYTEST_SPECIAL = /* @__PURE__ */ new Set(["conftest.py"]);
12023
12117
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".venv", "node_modules", "__pycache__", ".git", "dist", "build"]);
@@ -12026,7 +12120,7 @@ function subdirectories(dir) {
12026
12120
  return readdirSync22(dir).filter((entry) => {
12027
12121
  if (IGNORED_DIRS.has(entry) || entry.startsWith(".")) return false;
12028
12122
  try {
12029
- return statSync13(join50(dir, entry)).isDirectory();
12123
+ return statSync14(join50(dir, entry)).isDirectory();
12030
12124
  } catch {
12031
12125
  return false;
12032
12126
  }
@@ -12107,7 +12201,7 @@ import { execa as execa18 } from "execa";
12107
12201
 
12108
12202
  // src/lib/plugin-terraform-guard.ts
12109
12203
  import { existsSync as existsSync44, readFileSync as readFileSync37, readdirSync as readdirSync23 } from "fs";
12110
- import { dirname as dirname10, join as join52, relative as relative9, sep as sep3 } from "path";
12204
+ import { dirname as dirname10, join as join52, relative as relative10, sep as sep4 } from "path";
12111
12205
  var SKIP_DIRS3 = /* @__PURE__ */ new Set(["node_modules", ".git", ".worktrees", "dist", ".venv", "__pycache__"]);
12112
12206
  var PLUGIN_MANIFEST_FILE2 = "biffo.plugin.json";
12113
12207
  function findPluginManifests(root) {
@@ -12124,7 +12218,7 @@ function findPluginManifests(root) {
12124
12218
  if (SKIP_DIRS3.has(entry.name)) continue;
12125
12219
  walk2(join52(dir, entry.name));
12126
12220
  } else if (entry.isFile() && entry.name === PLUGIN_MANIFEST_FILE2) {
12127
- found.push(relative9(root, join52(dir, entry.name)).split(sep3).join("/"));
12221
+ found.push(relative10(root, join52(dir, entry.name)).split(sep4).join("/"));
12128
12222
  }
12129
12223
  }
12130
12224
  };
@@ -12157,7 +12251,7 @@ function checkPluginTerraform(root) {
12157
12251
  if (subscriptions === null) continue;
12158
12252
  const pluginDir2 = dirname10(absManifest);
12159
12253
  if (existsSync44(join52(pluginDir2, "terraform"))) continue;
12160
- const relPluginDir = relative9(root, pluginDir2).split(sep3).join("/");
12254
+ const relPluginDir = relative10(root, pluginDir2).split(sep4).join("/");
12161
12255
  violations.push({
12162
12256
  manifest,
12163
12257
  expectedTerraformDir: relPluginDir ? `${relPluginDir}/terraform` : "terraform",
@@ -12192,7 +12286,7 @@ import { join as join54 } from "path";
12192
12286
  import { execa as execa19 } from "execa";
12193
12287
 
12194
12288
  // src/lib/plugin-tool-supply-audit.ts
12195
- import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24, statSync as statSync14 } from "fs";
12289
+ import { existsSync as existsSync45, readFileSync as readFileSync38, readdirSync as readdirSync24, statSync as statSync15 } from "fs";
12196
12290
  import { join as join53 } from "path";
12197
12291
 
12198
12292
  // src/lib/openrouter-model-snapshot.ts
@@ -12610,7 +12704,7 @@ function listDirs(root) {
12610
12704
  }
12611
12705
  return entries.filter((e) => {
12612
12706
  try {
12613
- return statSync14(join53(root, e)).isDirectory();
12707
+ return statSync15(join53(root, e)).isDirectory();
12614
12708
  } catch {
12615
12709
  return false;
12616
12710
  }
@@ -12629,7 +12723,7 @@ function walkFiles2(root, accept, skipDir) {
12629
12723
  const p = join53(dir, entry);
12630
12724
  let st;
12631
12725
  try {
12632
- st = statSync14(p);
12726
+ st = statSync15(p);
12633
12727
  } catch {
12634
12728
  continue;
12635
12729
  }
@@ -12998,7 +13092,7 @@ function auditDeclaredModelIds(repoRoot, options = {}) {
12998
13092
  function discoverPluginDirs(pluginsRoot) {
12999
13093
  return listDirs(pluginsRoot).filter((name) => {
13000
13094
  try {
13001
- return statSync14(join53(pluginsRoot, name, "biffo.plugin.json")).isFile();
13095
+ return statSync15(join53(pluginsRoot, name, "biffo.plugin.json")).isFile();
13002
13096
  } catch {
13003
13097
  return false;
13004
13098
  }
@@ -13357,7 +13451,7 @@ import { join as join56 } from "path";
13357
13451
  import { execa as execa21 } from "execa";
13358
13452
 
13359
13453
  // src/lib/skeleton-drift-guard.ts
13360
- import { readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync15 } from "fs";
13454
+ import { readFileSync as readFileSync39, readdirSync as readdirSync25, statSync as statSync16 } from "fs";
13361
13455
  import { join as join55 } from "path";
13362
13456
  var isWorkflow = (rel) => rel.startsWith(".github/workflows/") && (rel.endsWith(".yml") || rel.endsWith(".yaml"));
13363
13457
  var isRootLayout = (rel) => rel.endsWith("src/app/layout.tsx");
@@ -13425,7 +13519,7 @@ function walk(dir, base = dir) {
13425
13519
  const abs = join55(dir, entry);
13426
13520
  let isDir;
13427
13521
  try {
13428
- isDir = statSync15(abs).isDirectory();
13522
+ isDir = statSync16(abs).isDirectory();
13429
13523
  } catch {
13430
13524
  continue;
13431
13525
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.283.1",
3
+ "version": "0.283.3",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",