@biffo/cli 0.50.1 → 0.50.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 (3) hide show
  1. package/core.version +1 -1
  2. package/dist/index.js +271 -178
  3. package/package.json +1 -1
package/core.version CHANGED
@@ -1 +1 @@
1
- 0.50.1
1
+ 0.50.3
package/dist/index.js CHANGED
@@ -337,8 +337,9 @@ async function runCoreStatus(options) {
337
337
 
338
338
  // src/commands/core-upgrade.ts
339
339
  import { execSync as execSync2 } from "child_process";
340
- import { join as join8, resolve as resolve3 } from "path";
340
+ import { join as join9, resolve as resolve3 } from "path";
341
341
  import chalk4 from "chalk";
342
+ import { execa as execa3 } from "execa";
342
343
  import { Command as Command3 } from "commander";
343
344
 
344
345
  // src/adapters/git/index.ts
@@ -1467,7 +1468,10 @@ var gitMergeFile = async (base, ours, theirs) => {
1467
1468
  writeFileSync3(b, base);
1468
1469
  writeFileSync3(o, ours);
1469
1470
  writeFileSync3(t, theirs);
1470
- const result = await execa2("git", ["merge-file", "-p", o, b, t], { reject: false });
1471
+ const result = await execa2("git", ["merge-file", "-p", o, b, t], {
1472
+ reject: false,
1473
+ stripFinalNewline: false
1474
+ });
1471
1475
  if (typeof result.exitCode !== "number" || result.exitCode < 0) {
1472
1476
  throw new Error(`git merge-file failed: ${result.stderr}`);
1473
1477
  }
@@ -1630,6 +1634,45 @@ var GLOBAL_DISPATCH_WORKFLOW_PATHS = [
1630
1634
  ".github/workflows/deploy-global.yml"
1631
1635
  ];
1632
1636
 
1637
+ // src/lib/lockfile-refresh.ts
1638
+ import { existsSync as existsSync6 } from "fs";
1639
+ import { join as join8 } from "path";
1640
+ var LOCKFILE_TRIGGERS = [
1641
+ {
1642
+ manifest: "package.json",
1643
+ lockfile: "pnpm-lock.yaml",
1644
+ command: ["pnpm", "install", "--lockfile-only"],
1645
+ ecosystem: "pnpm"
1646
+ },
1647
+ {
1648
+ manifest: "pyproject.toml",
1649
+ lockfile: "uv.lock",
1650
+ command: ["uv", "lock"],
1651
+ ecosystem: "uv"
1652
+ }
1653
+ ];
1654
+ function lockfilesNeedingRefresh(changedPaths, instanceDir, triggers = LOCKFILE_TRIGGERS) {
1655
+ return triggers.filter((t) => {
1656
+ const touched = changedPaths.some((p) => p === t.manifest || p.endsWith(`/${t.manifest}`));
1657
+ return touched && existsSync6(join8(instanceDir, t.lockfile));
1658
+ });
1659
+ }
1660
+ async function refreshLockfiles(instanceDir, triggers, run) {
1661
+ const outcomes = [];
1662
+ for (const trigger of triggers) {
1663
+ const result = await run(trigger.command, instanceDir);
1664
+ const outcome = { trigger, ok: result.ok };
1665
+ if (result.error !== void 0) outcome.error = result.error;
1666
+ outcomes.push(outcome);
1667
+ }
1668
+ return outcomes;
1669
+ }
1670
+ function describeFailures(outcomes) {
1671
+ return outcomes.filter((o) => !o.ok).map(
1672
+ (o) => `${o.trigger.lockfile} could not be refreshed (${o.trigger.ecosystem}): ${o.error ?? "unknown error"}. Run \`${o.trigger.command.join(" ")}\` in the instance and commit the result, or CI will fail on a lockfile that disagrees with ${o.trigger.manifest}.`
1673
+ );
1674
+ }
1675
+
1633
1676
  // src/commands/core-upgrade.ts
1634
1677
  var MISSING_TEMPLATE_ROOT_GUIDANCE2 = "Pass --template-repo <path> to a biffo-template git checkout, e.g. `biffo core upgrade --template-repo /path/to/biffo-template`.";
1635
1678
  var coreUpgradeCommand = new Command3("upgrade").description("Three-way-merge template-owned files for a core upgrade; preview it or open a PR").option("--cwd <path>", "Instance repo root to upgrade (defaults to the current directory)").option(
@@ -1700,9 +1743,9 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
1700
1743
  let toVersion;
1701
1744
  if (options.theirsDir) {
1702
1745
  theirsDir = options.theirsDir;
1703
- toVersion = readCoreVersionFile(join8(theirsDir, "core.version"));
1746
+ toVersion = readCoreVersionFile(join9(theirsDir, "core.version"));
1704
1747
  } else {
1705
- const workingVersion = readCoreVersionFile(join8(templateRepo, "core.version"));
1748
+ const workingVersion = readCoreVersionFile(join9(templateRepo, "core.version"));
1706
1749
  toVersion = options.toVersion ?? workingVersion;
1707
1750
  if (toVersion === workingVersion) {
1708
1751
  theirsDir = templateRepo;
@@ -1716,7 +1759,7 @@ async function runCoreUpgradeResolved(options, deps, cleanups) {
1716
1759
  let fromVersion;
1717
1760
  if (options.baseDir) {
1718
1761
  baseDir = options.baseDir;
1719
- fromVersion = readCoreVersionFile(join8(baseDir, "core.version"));
1762
+ fromVersion = readCoreVersionFile(join9(baseDir, "core.version"));
1720
1763
  } else {
1721
1764
  if (instanceVersion === null) {
1722
1765
  throw new Error(
@@ -1791,6 +1834,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
1791
1834
  4,
1792
1835
  `Applied ${applied.written.length} change(s), ${applied.deleted.length} deletion(s), ${carried.length} new migration(s)`
1793
1836
  );
1837
+ const lockfiles = await refreshInstanceLockfiles(options.cwd, plan, deps);
1794
1838
  await git.add(options.cwd, ["-A"]);
1795
1839
  await git.commit(options.cwd, `chore(core): upgrade template core ${fromVersion} -> ${toVersion}`);
1796
1840
  log.step(3, 4, `Pushing ${branch}`);
@@ -1806,7 +1850,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
1806
1850
  head: branch,
1807
1851
  base,
1808
1852
  title: `Upgrade Biffo core ${fromVersion} \u2192 ${toVersion}`,
1809
- body: buildPrBody(fromVersion, toVersion, plan, migrations, base)
1853
+ body: buildPrBody(fromVersion, toVersion, plan, migrations, base, lockfiles)
1810
1854
  });
1811
1855
  if (plan.conflicts.length > 0) {
1812
1856
  log.warn(`PR opened with ${plan.conflicts.length} conflict(s) to resolve: ${pr.url}`);
@@ -1814,7 +1858,7 @@ async function applyAndOpenPr(options, deps, plan, migrations, fromVersion, toVe
1814
1858
  log.success(`Opened PR #${pr.number}: ${pr.url}`);
1815
1859
  }
1816
1860
  }
1817
- function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF) {
1861
+ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF, lockfiles = []) {
1818
1862
  const lines = [
1819
1863
  "Automated core upgrade generated by `biffo core upgrade` (ADR-0006).",
1820
1864
  "",
@@ -1856,6 +1900,26 @@ function buildPrBody(from, to, plan, migrations, base = GLOBAL_DISPATCH_REF) {
1856
1900
  `Merging here lands the fix on \`${base}\` \u2014 **but the deploy runs it from \`${GLOBAL_DISPATCH_REF}\`.** Until you promote \`${base}\` \u2192 \`${GLOBAL_DISPATCH_REF}\` (open a PR from \`${base}\` into \`${GLOBAL_DISPATCH_REF}\` and merge it), the old workflow keeps running and this fix will not take effect.`
1857
1901
  );
1858
1902
  }
1903
+ const lockfileFailures = describeFailures(lockfiles);
1904
+ if (lockfileFailures.length > 0) {
1905
+ lines.push(
1906
+ "",
1907
+ "## \u26A0 Lockfiles could not be refreshed",
1908
+ "",
1909
+ "This upgrade changed a dependency manifest, but the matching lockfile could not be regenerated on the machine that ran the upgrade. **CI will fail on the mismatch** until this is done by hand:",
1910
+ "",
1911
+ ...lockfileFailures.map((f) => `- ${f}`)
1912
+ );
1913
+ } else if (lockfiles.length > 0) {
1914
+ lines.push(
1915
+ "",
1916
+ `## Lockfiles refreshed (${lockfiles.length})`,
1917
+ "",
1918
+ "This upgrade changed a dependency manifest. The manifests are template-owned and the lockfiles are not, so the lockfiles were regenerated **in this repo** \u2014 against its own registry config \u2014 and committed alongside, rather than left disagreeing:",
1919
+ "",
1920
+ ...lockfiles.map((o) => `- \`${o.trigger.lockfile}\` (\`${o.trigger.command.join(" ")}\`)`)
1921
+ );
1922
+ }
1859
1923
  if (plan.conflicts.length > 0) {
1860
1924
  lines.push(
1861
1925
  "",
@@ -1898,6 +1962,35 @@ function printPlan(plan) {
1898
1962
  console.log(` ${color(e.status.padEnd(15))} ${e.path}`);
1899
1963
  }
1900
1964
  }
1965
+ async function refreshInstanceLockfiles(cwd, plan, deps) {
1966
+ const triggers = lockfilesNeedingRefresh(
1967
+ plan.changes.map((c) => c.path),
1968
+ cwd
1969
+ );
1970
+ if (triggers.length === 0) return [];
1971
+ const run = deps.runCommand ?? defaultRunCommand;
1972
+ const outcomes = await refreshLockfiles(cwd, triggers, run);
1973
+ const refreshed = outcomes.filter((o) => o.ok);
1974
+ if (refreshed.length > 0) {
1975
+ log.info(
1976
+ `Refreshed ${refreshed.map((o) => o.trigger.lockfile).join(", ")} \u2014 the upgrade changed a dependency manifest they lock.`
1977
+ );
1978
+ }
1979
+ for (const message of describeFailures(outcomes)) log.warn(message);
1980
+ return outcomes;
1981
+ }
1982
+ var defaultRunCommand = async (command, cwd) => {
1983
+ const [bin, ...args] = command;
1984
+ if (!bin) return { ok: false, error: "empty command" };
1985
+ try {
1986
+ await execa3(bin, args, { cwd });
1987
+ return { ok: true };
1988
+ } catch (err) {
1989
+ const cause = err;
1990
+ const detail = cause.stderr?.trim() || cause.shortMessage || cause.message || "failed";
1991
+ return { ok: false, error: detail.split("\n")[0] ?? "failed" };
1992
+ }
1993
+ };
1901
1994
 
1902
1995
  // src/commands/core.ts
1903
1996
  var coreCommand = new Command4("core").description(
@@ -1911,7 +2004,7 @@ coreCommand.addCommand(coreUpgradeCommand);
1911
2004
  import { Command as Command8 } from "commander";
1912
2005
 
1913
2006
  // src/commands/data-apply.ts
1914
- import { existsSync as existsSync7, readFileSync as readFileSync6 } from "fs";
2007
+ import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
1915
2008
  import { resolve as resolve4 } from "path";
1916
2009
  import chalk5 from "chalk";
1917
2010
  import { Command as Command5 } from "commander";
@@ -2362,7 +2455,7 @@ function isTemplatePlaceholderConfig(raw) {
2362
2455
 
2363
2456
  // src/lib/session.ts
2364
2457
  import {
2365
- existsSync as existsSync6,
2458
+ existsSync as existsSync7,
2366
2459
  mkdirSync as mkdirSync3,
2367
2460
  readdirSync as readdirSync3,
2368
2461
  readFileSync as readFileSync5,
@@ -2371,7 +2464,7 @@ import {
2371
2464
  writeFileSync as writeFileSync4
2372
2465
  } from "fs";
2373
2466
  import { homedir } from "os";
2374
- import { join as join9 } from "path";
2467
+ import { join as join10 } from "path";
2375
2468
  var LEGACY_STEP_ALIASES = {
2376
2469
  github_config: ["github_branches", "github_instance_files", "github_settings"]
2377
2470
  };
@@ -2380,14 +2473,14 @@ function hasCompleted(session, step) {
2380
2473
  return session.completedSteps.some((done) => LEGACY_STEP_ALIASES[done]?.includes(step) ?? false);
2381
2474
  }
2382
2475
  function sessionsDir() {
2383
- return process.env["BIFFO_SESSIONS_DIR"] ?? join9(homedir(), ".biffo", "sessions");
2476
+ return process.env["BIFFO_SESSIONS_DIR"] ?? join10(homedir(), ".biffo", "sessions");
2384
2477
  }
2385
2478
  function sessionPath(projectName) {
2386
- return join9(sessionsDir(), `${projectName}.json`);
2479
+ return join10(sessionsDir(), `${projectName}.json`);
2387
2480
  }
2388
2481
  function loadSession(projectName) {
2389
2482
  const path = sessionPath(projectName);
2390
- if (!existsSync6(path)) return null;
2483
+ if (!existsSync7(path)) return null;
2391
2484
  try {
2392
2485
  return JSON.parse(readFileSync5(path, "utf8"));
2393
2486
  } catch {
@@ -2396,23 +2489,23 @@ function loadSession(projectName) {
2396
2489
  }
2397
2490
  function findLatestSession() {
2398
2491
  const dir = sessionsDir();
2399
- if (!existsSync6(dir)) return null;
2492
+ if (!existsSync7(dir)) return null;
2400
2493
  const files = readdirSync3(dir).filter((f) => f.endsWith(".json"));
2401
2494
  if (files.length === 0) return null;
2402
2495
  const sorted = files.map((f) => {
2403
- const fullPath = join9(dir, f);
2404
- const mtime = existsSync6(fullPath) ? statSync(fullPath).mtimeMs : -1;
2496
+ const fullPath = join10(dir, f);
2497
+ const mtime = existsSync7(fullPath) ? statSync(fullPath).mtimeMs : -1;
2405
2498
  return { f, mtime };
2406
2499
  }).sort((a, b) => b.mtime - a.mtime);
2407
2500
  try {
2408
- return JSON.parse(readFileSync5(join9(dir, sorted[0].f), "utf8"));
2501
+ return JSON.parse(readFileSync5(join10(dir, sorted[0].f), "utf8"));
2409
2502
  } catch {
2410
2503
  return null;
2411
2504
  }
2412
2505
  }
2413
2506
  function saveSession(session) {
2414
2507
  const dir = sessionsDir();
2415
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2508
+ if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2416
2509
  const name = session.config.project?.name ?? "unknown";
2417
2510
  const prior = loadSession(name);
2418
2511
  if (prior) {
@@ -2434,19 +2527,19 @@ function markStepComplete(session, step) {
2434
2527
  }
2435
2528
  function deleteSession(projectName) {
2436
2529
  const path = sessionPath(projectName);
2437
- if (existsSync6(path)) rmSync4(path);
2530
+ if (existsSync7(path)) rmSync4(path);
2438
2531
  }
2439
2532
  function projectsDir() {
2440
- return process.env["BIFFO_PROJECTS_DIR"] ?? join9(homedir(), ".biffo", "projects");
2533
+ return process.env["BIFFO_PROJECTS_DIR"] ?? join10(homedir(), ".biffo", "projects");
2441
2534
  }
2442
2535
  function saveProjectConfig(config) {
2443
2536
  const dir = projectsDir();
2444
- if (!existsSync6(dir)) mkdirSync3(dir, { recursive: true });
2445
- writeFileSync4(join9(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
2537
+ if (!existsSync7(dir)) mkdirSync3(dir, { recursive: true });
2538
+ writeFileSync4(join10(dir, `${config.project.name}.json`), JSON.stringify(config, null, 2));
2446
2539
  }
2447
2540
  function loadProjectConfig(name) {
2448
- const path = join9(projectsDir(), `${name}.json`);
2449
- if (!existsSync6(path)) return null;
2541
+ const path = join10(projectsDir(), `${name}.json`);
2542
+ if (!existsSync7(path)) return null;
2450
2543
  try {
2451
2544
  const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(path, "utf8")));
2452
2545
  return result.success ? result.data : null;
@@ -2455,15 +2548,15 @@ function loadProjectConfig(name) {
2455
2548
  }
2456
2549
  }
2457
2550
  function deleteProjectConfig(name) {
2458
- const path = join9(projectsDir(), `${name}.json`);
2459
- if (existsSync6(path)) rmSync4(path);
2551
+ const path = join10(projectsDir(), `${name}.json`);
2552
+ if (existsSync7(path)) rmSync4(path);
2460
2553
  }
2461
2554
  function listProjectConfigs() {
2462
2555
  const dir = projectsDir();
2463
- if (!existsSync6(dir)) return [];
2556
+ if (!existsSync7(dir)) return [];
2464
2557
  return readdirSync3(dir).filter((f) => f.endsWith(".json")).flatMap((f) => {
2465
2558
  try {
2466
- const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(join9(dir, f), "utf8")));
2559
+ const result = BiffoConfigSchema.safeParse(JSON.parse(readFileSync5(join10(dir, f), "utf8")));
2467
2560
  return result.success ? [result.data] : [];
2468
2561
  } catch {
2469
2562
  return [];
@@ -2552,7 +2645,7 @@ async function resolveConfig(options) {
2552
2645
  return cfg;
2553
2646
  }
2554
2647
  const localConfigPath = resolve4(process.cwd(), "biffo.config.json");
2555
- if (existsSync7(localConfigPath)) {
2648
+ if (existsSync8(localConfigPath)) {
2556
2649
  const raw = JSON.parse(readFileSync6(localConfigPath, "utf8"));
2557
2650
  const result = BiffoConfigSchema.safeParse(raw);
2558
2651
  if (result.success) return result.data;
@@ -2599,8 +2692,8 @@ async function resolveConfig(options) {
2599
2692
 
2600
2693
  // src/commands/data-import.ts
2601
2694
  import { execSync as execSync3 } from "child_process";
2602
- import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
2603
- import { join as join10, resolve as resolve5 } from "path";
2695
+ import { cpSync, existsSync as existsSync9, mkdirSync as mkdirSync4, readdirSync as readdirSync4, statSync as statSync2 } from "fs";
2696
+ import { join as join11, resolve as resolve5 } from "path";
2604
2697
  import chalk6 from "chalk";
2605
2698
  import { Command as Command6 } from "commander";
2606
2699
  import inquirer2 from "inquirer";
@@ -2640,23 +2733,23 @@ async function runDataImport(name, options, deps) {
2640
2733
  `Invalid import name '${name}'. Use lowercase letters, numbers, and hyphens, starting with a letter.`
2641
2734
  );
2642
2735
  }
2643
- const servicesDir = join10(options.cwd, "services");
2644
- if (!existsSync8(servicesDir)) {
2736
+ const servicesDir = join11(options.cwd, "services");
2737
+ if (!existsSync9(servicesDir)) {
2645
2738
  throw new Error(
2646
2739
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
2647
2740
  );
2648
2741
  }
2649
- const targetDir = join10(options.cwd, "db", "imports", name);
2650
- if (existsSync8(targetDir)) {
2742
+ const targetDir = join11(options.cwd, "db", "imports", name);
2743
+ if (existsSync9(targetDir)) {
2651
2744
  throw new Error(
2652
2745
  `DDL import '${name}' is already present at db/imports/${name}/. Remove it first to re-import.`
2653
2746
  );
2654
2747
  }
2655
- const isLocalDir = existsSync8(options.source) && statSync2(options.source).isDirectory();
2748
+ const isLocalDir = existsSync9(options.source) && statSync2(options.source).isDirectory();
2656
2749
  let sourceDir;
2657
2750
  let cleanupClone = null;
2658
2751
  if (isLocalDir) {
2659
- sourceDir = options.path ? join10(options.source, options.path) : options.source;
2752
+ sourceDir = options.path ? join11(options.source, options.path) : options.source;
2660
2753
  } else {
2661
2754
  const token = options.token ?? await resolveDdlImportToken();
2662
2755
  log.info(`Cloning ${options.source}...`);
@@ -2664,10 +2757,10 @@ async function runDataImport(name, options, deps) {
2664
2757
  cleanupClone = () => {
2665
2758
  deps.git.cleanup(tmpDir);
2666
2759
  };
2667
- sourceDir = options.path ? join10(tmpDir, options.path) : tmpDir;
2760
+ sourceDir = options.path ? join11(tmpDir, options.path) : tmpDir;
2668
2761
  }
2669
2762
  try {
2670
- if (!existsSync8(sourceDir)) {
2763
+ if (!existsSync9(sourceDir)) {
2671
2764
  throw new Error(`Source directory does not exist: ${sourceDir}`);
2672
2765
  }
2673
2766
  const sqlFiles = readdirSync4(sourceDir, { withFileTypes: true }).filter((entry) => entry.isFile() && entry.name.endsWith(".sql")).map((entry) => entry.name).sort();
@@ -2692,7 +2785,7 @@ async function runDataImport(name, options, deps) {
2692
2785
  }
2693
2786
  mkdirSync4(targetDir, { recursive: true });
2694
2787
  for (const file of sqlFiles) {
2695
- cpSync(join10(sourceDir, file), join10(targetDir, file));
2788
+ cpSync(join11(sourceDir, file), join11(targetDir, file));
2696
2789
  }
2697
2790
  log.success(`Imported ${String(sqlFiles.length)} .sql file(s) to db/imports/${name}/`);
2698
2791
  const commitMessage = `feat(data): import ${name} (${String(sqlFiles.length)} SQL file(s))`;
@@ -2744,8 +2837,8 @@ function printDryRun(name, sqlFiles) {
2744
2837
  }
2745
2838
 
2746
2839
  // src/commands/data-list.ts
2747
- import { existsSync as existsSync9, readdirSync as readdirSync5 } from "fs";
2748
- import { join as join11, resolve as resolve6 } from "path";
2840
+ import { existsSync as existsSync10, readdirSync as readdirSync5 } from "fs";
2841
+ import { join as join12, resolve as resolve6 } from "path";
2749
2842
  import chalk7 from "chalk";
2750
2843
  import { Command as Command7 } from "commander";
2751
2844
  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) => {
@@ -2758,15 +2851,15 @@ var dataListCommand = new Command7("list").description("List DDL imports vendore
2758
2851
  }
2759
2852
  });
2760
2853
  async function runDataList(options) {
2761
- const importsDir = join11(options.cwd, "db", "imports");
2762
- if (!existsSync9(importsDir)) {
2854
+ const importsDir = join12(options.cwd, "db", "imports");
2855
+ if (!existsSync10(importsDir)) {
2763
2856
  console.log(chalk7.dim("\n No DDL imports in this checkout.\n"));
2764
2857
  return;
2765
2858
  }
2766
2859
  const candidates = readdirSync5(importsDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name).sort();
2767
2860
  const imports = [];
2768
2861
  for (const name of candidates) {
2769
- const fileCount = readdirSync5(join11(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
2862
+ const fileCount = readdirSync5(join12(importsDir, name)).filter((f) => f.endsWith(".sql")).length;
2770
2863
  if (fileCount > 0) imports.push({ name, fileCount });
2771
2864
  }
2772
2865
  if (imports.length === 0) {
@@ -2796,7 +2889,7 @@ dataCommand.addCommand(dataListCommand);
2796
2889
 
2797
2890
  // src/commands/deploy.ts
2798
2891
  import { execSync as execSync4 } from "child_process";
2799
- import { existsSync as existsSync10, readFileSync as readFileSync7 } from "fs";
2892
+ import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
2800
2893
  import { resolve as resolve7 } from "path";
2801
2894
  import chalk8 from "chalk";
2802
2895
  import { Command as Command9 } from "commander";
@@ -3170,7 +3263,7 @@ async function resolveConfig2(options) {
3170
3263
  return cfg;
3171
3264
  }
3172
3265
  const localConfigPath = resolve7(process.cwd(), "biffo.config.json");
3173
- if (existsSync10(localConfigPath)) {
3266
+ if (existsSync11(localConfigPath)) {
3174
3267
  const raw = JSON.parse(readFileSync7(localConfigPath, "utf8"));
3175
3268
  const result = BiffoConfigSchema.safeParse(raw);
3176
3269
  if (result.success) return result.data;
@@ -3722,8 +3815,8 @@ import { Command as Command12 } from "commander";
3722
3815
  import inquirer5 from "inquirer";
3723
3816
 
3724
3817
  // src/lib/build-freshness.ts
3725
- import { existsSync as existsSync11, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
3726
- import { dirname as dirname5, join as join12, relative as relative2, sep as sep2 } from "path";
3818
+ import { existsSync as existsSync12, readdirSync as readdirSync6, statSync as statSync3 } from "fs";
3819
+ import { dirname as dirname5, join as join13, relative as relative2, sep as sep2 } from "path";
3727
3820
  import { fileURLToPath as fileURLToPath3 } from "url";
3728
3821
  var SKIP_ENV_VAR = "BIFFO_SKIP_BUILD_FRESHNESS_CHECK";
3729
3822
  function checkBuildFreshness(options = {}) {
@@ -3737,7 +3830,7 @@ function checkBuildFreshness(options = {}) {
3737
3830
  if (!packageRoot) {
3738
3831
  return { status: "skipped", reason: `no package.json above ${moduleDir}`, newerSources: [] };
3739
3832
  }
3740
- const distDir = join12(packageRoot, "dist");
3833
+ const distDir = join13(packageRoot, "dist");
3741
3834
  if (!isInside(distDir, moduleDir)) {
3742
3835
  return {
3743
3836
  status: "skipped",
@@ -3745,16 +3838,16 @@ function checkBuildFreshness(options = {}) {
3745
3838
  newerSources: []
3746
3839
  };
3747
3840
  }
3748
- const srcDir = join12(packageRoot, "src");
3749
- if (!existsSync11(srcDir)) {
3841
+ const srcDir = join13(packageRoot, "src");
3842
+ if (!existsSync12(srcDir)) {
3750
3843
  return {
3751
3844
  status: "skipped",
3752
3845
  reason: "no src/ alongside dist/ \u2014 this is a shipped package",
3753
3846
  newerSources: []
3754
3847
  };
3755
3848
  }
3756
- const entry = join12(distDir, "index.js");
3757
- if (!existsSync11(entry)) {
3849
+ const entry = join13(distDir, "index.js");
3850
+ if (!existsSync12(entry)) {
3758
3851
  return { status: "skipped", reason: `${entry} not found`, newerSources: [] };
3759
3852
  }
3760
3853
  const builtAt = statSync3(entry).mtimeMs;
@@ -3798,7 +3891,7 @@ function collectSourceFiles(srcDir) {
3798
3891
  const found = [];
3799
3892
  const walk = (dir) => {
3800
3893
  for (const entry of readdirSync6(dir, { withFileTypes: true })) {
3801
- const full = join12(dir, entry.name);
3894
+ const full = join13(dir, entry.name);
3802
3895
  if (entry.isDirectory()) {
3803
3896
  if (entry.name === "node_modules") continue;
3804
3897
  walk(full);
@@ -3817,7 +3910,7 @@ function collectSourceFiles(srcDir) {
3817
3910
  function findPackageRoot(from) {
3818
3911
  let dir = from;
3819
3912
  for (; ; ) {
3820
- if (existsSync11(join12(dir, "package.json"))) return dir;
3913
+ if (existsSync12(join13(dir, "package.json"))) return dir;
3821
3914
  const parent = dirname5(dir);
3822
3915
  if (parent === dir) return null;
3823
3916
  dir = parent;
@@ -3831,9 +3924,9 @@ function isInside(parent, child) {
3831
3924
 
3832
3925
  // src/lib/credentials.ts
3833
3926
  import { execSync as execSync6 } from "child_process";
3834
- import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
3927
+ import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
3835
3928
  import { homedir as homedir2 } from "os";
3836
- import { join as join13 } from "path";
3929
+ import { join as join14 } from "path";
3837
3930
  import { GetCallerIdentityCommand as GetCallerIdentityCommand2, STSClient as STSClient2 } from "@aws-sdk/client-sts";
3838
3931
  import chalk10 from "chalk";
3839
3932
  import inquirer4 from "inquirer";
@@ -4012,10 +4105,10 @@ async function verifySelectedAwsCredentials(profile, region) {
4012
4105
  return sts.send(new GetCallerIdentityCommand2({}));
4013
4106
  }
4014
4107
  function discoverAwsProfiles() {
4015
- const files = [join13(homedir2(), ".aws", "credentials"), join13(homedir2(), ".aws", "config")];
4108
+ const files = [join14(homedir2(), ".aws", "credentials"), join14(homedir2(), ".aws", "config")];
4016
4109
  const profiles = /* @__PURE__ */ new Set();
4017
4110
  for (const file of files) {
4018
- if (!existsSync12(file)) continue;
4111
+ if (!existsSync13(file)) continue;
4019
4112
  const content = readFileSync9(file, "utf8");
4020
4113
  for (const match of content.matchAll(/^\s*\[([^\]]+)\]\s*$/gm)) {
4021
4114
  const section = match[1]?.trim();
@@ -4106,7 +4199,7 @@ var SiblingConfigSchema = z4.object({
4106
4199
 
4107
4200
  // src/lib/sibling-session.ts
4108
4201
  import {
4109
- existsSync as existsSync13,
4202
+ existsSync as existsSync14,
4110
4203
  mkdirSync as mkdirSync5,
4111
4204
  readdirSync as readdirSync7,
4112
4205
  readFileSync as readFileSync10,
@@ -4115,16 +4208,16 @@ import {
4115
4208
  writeFileSync as writeFileSync5
4116
4209
  } from "fs";
4117
4210
  import { homedir as homedir3 } from "os";
4118
- import { join as join14 } from "path";
4211
+ import { join as join15 } from "path";
4119
4212
  function sessionsDir2() {
4120
- return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join14(homedir3(), ".biffo", "sibling-sessions");
4213
+ return process.env["BIFFO_SIBLING_SESSIONS_DIR"] ?? join15(homedir3(), ".biffo", "sibling-sessions");
4121
4214
  }
4122
4215
  function sessionPath2(projectName) {
4123
- return join14(sessionsDir2(), `${projectName}.json`);
4216
+ return join15(sessionsDir2(), `${projectName}.json`);
4124
4217
  }
4125
4218
  function loadSiblingSession(projectName) {
4126
4219
  const path = sessionPath2(projectName);
4127
- if (!existsSync13(path)) return null;
4220
+ if (!existsSync14(path)) return null;
4128
4221
  try {
4129
4222
  return JSON.parse(readFileSync10(path, "utf8"));
4130
4223
  } catch {
@@ -4133,7 +4226,7 @@ function loadSiblingSession(projectName) {
4133
4226
  }
4134
4227
  function saveSiblingSession(session) {
4135
4228
  const dir = sessionsDir2();
4136
- if (!existsSync13(dir)) mkdirSync5(dir, { recursive: true });
4229
+ if (!existsSync14(dir)) mkdirSync5(dir, { recursive: true });
4137
4230
  const name = session.config.project?.name ?? "unknown";
4138
4231
  const prior = loadSiblingSession(name);
4139
4232
  if (prior) {
@@ -4155,30 +4248,30 @@ function markSiblingStepComplete(session, step) {
4155
4248
  }
4156
4249
  function deleteSiblingSession(projectName) {
4157
4250
  const path = sessionPath2(projectName);
4158
- if (existsSync13(path)) rmSync5(path);
4251
+ if (existsSync14(path)) rmSync5(path);
4159
4252
  }
4160
4253
 
4161
4254
  // src/commands/sibling-create.ts
4162
- import { cpSync as cpSync2, existsSync as existsSync14, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
4255
+ import { cpSync as cpSync2, existsSync as existsSync15, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "fs";
4163
4256
  import { tmpdir as tmpdir4 } from "os";
4164
- import { dirname as dirname6, join as join16, resolve as resolve9 } from "path";
4257
+ import { dirname as dirname6, join as join17, resolve as resolve9 } from "path";
4165
4258
  import { fileURLToPath as fileURLToPath4 } from "url";
4166
4259
  import chalk11 from "chalk";
4167
4260
  import { Command as Command11 } from "commander";
4168
4261
 
4169
4262
  // src/lib/skeleton-dotfiles.ts
4170
4263
  import { readdirSync as readdirSync8, renameSync } from "fs";
4171
- import { join as join15 } from "path";
4264
+ import { join as join16 } from "path";
4172
4265
  var PACKAGED_GITIGNORE = "_gitignore";
4173
4266
  var REAL_GITIGNORE = ".gitignore";
4174
4267
  function restorePackagedDotfiles(dir) {
4175
4268
  const restored = [];
4176
4269
  for (const entry of readdirSync8(dir, { withFileTypes: true })) {
4177
- const full = join15(dir, entry.name);
4270
+ const full = join16(dir, entry.name);
4178
4271
  if (entry.isDirectory()) {
4179
4272
  restored.push(...restorePackagedDotfiles(full));
4180
4273
  } else if (entry.name === PACKAGED_GITIGNORE) {
4181
- const target = join15(dir, REAL_GITIGNORE);
4274
+ const target = join16(dir, REAL_GITIGNORE);
4182
4275
  renameSync(full, target);
4183
4276
  restored.push(target);
4184
4277
  }
@@ -4223,7 +4316,7 @@ async function runSiblingCreateCommand(name, options) {
4223
4316
  printDryRun2(config, coreConfig, options.templateRoot);
4224
4317
  return;
4225
4318
  }
4226
- if (!existsSync14(options.templateRoot)) {
4319
+ if (!existsSync15(options.templateRoot)) {
4227
4320
  throw new Error(`Sibling template not found at ${options.templateRoot}`);
4228
4321
  }
4229
4322
  let session = null;
@@ -4481,7 +4574,7 @@ async function resolveCoreIdentity(coreAws, coreConfig, environments) {
4481
4574
  return coreIdentity;
4482
4575
  }
4483
4576
  async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, githubToken) {
4484
- const workDir = mkdtempSync4(join16(tmpdir4(), `biffo-sibling-${config.project.name}-`));
4577
+ const workDir = mkdtempSync4(join17(tmpdir4(), `biffo-sibling-${config.project.name}-`));
4485
4578
  try {
4486
4579
  writeSiblingTemplate(skeletonRoot, workDir, config, {
4487
4580
  coreProjectName: coreConfig.project.name,
@@ -4497,13 +4590,13 @@ async function pushSkeleton(git, skeletonRoot, cloneUrl, config, coreConfig, git
4497
4590
  }
4498
4591
  }
4499
4592
  function writeSiblingTemplate(templateRoot, targetDir, config, context) {
4500
- if (!existsSync14(templateRoot)) {
4593
+ if (!existsSync15(templateRoot)) {
4501
4594
  throw new Error(`Sibling template not found at ${templateRoot}`);
4502
4595
  }
4503
4596
  cpSync2(templateRoot, targetDir, { recursive: true });
4504
4597
  restorePackagedDotfiles(targetDir);
4505
4598
  writeFileSync6(
4506
- join16(targetDir, "biffo.sibling.json"),
4599
+ join17(targetDir, "biffo.sibling.json"),
4507
4600
  JSON.stringify(
4508
4601
  {
4509
4602
  name: config.project.name,
@@ -4519,7 +4612,7 @@ function writeSiblingTemplate(templateRoot, targetDir, config, context) {
4519
4612
  2
4520
4613
  ) + "\n"
4521
4614
  );
4522
- const envPath = join16(targetDir, "apps", "frontend", ".env.example");
4615
+ const envPath = join17(targetDir, "apps", "frontend", ".env.example");
4523
4616
  try {
4524
4617
  const path = basePathFor(context.pathPrefix);
4525
4618
  const content = readFileSync11(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}`);
@@ -4575,7 +4668,7 @@ function readExistingSiblingOrigins(filePath) {
4575
4668
  }
4576
4669
  }
4577
4670
  function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x") {
4578
- const cdnVarsPath = join16(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
4671
+ const cdnVarsPath = join17(cloneDir, "modules", "cloud", "aws", "cdn", "variables.tf");
4579
4672
  let declaresSiblingOrigins = false;
4580
4673
  try {
4581
4674
  declaresSiblingOrigins = /variable\s+"sibling_origins"/.test(readFileSync11(cdnVarsPath, "utf8"));
@@ -4588,7 +4681,7 @@ function assertCoreSupportsSiblingRouting(cloneDir, coreRepo, pathPrefix = "x")
4588
4681
  );
4589
4682
  }
4590
4683
  if (!isRootPathPrefix(pathPrefix)) return;
4591
- const cdnMainPath = join16(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
4684
+ const cdnMainPath = join17(cloneDir, "modules", "cloud", "aws", "cdn", "main.tf");
4592
4685
  let supportsRoot = false;
4593
4686
  try {
4594
4687
  supportsRoot = /root_sibling_registered/.test(readFileSync11(cdnMainPath, "utf8"));
@@ -4621,8 +4714,8 @@ async function registerWithCore(git, github, config, coreConfig, pathPrefix, git
4621
4714
  for (const env of config.environments) {
4622
4715
  const bucketName = siteBucketName(config.project.name, env, siblingAccountId);
4623
4716
  const domain = bucketRegionalDomain(bucketName, coreAwsRegion);
4624
- const relativePath = join16("infra", "environments", env, "siblings.auto.tfvars.json");
4625
- const filePath = join16(cloneDir, relativePath);
4717
+ const relativePath = join17("infra", "environments", env, "siblings.auto.tfvars.json");
4718
+ const filePath = join17(cloneDir, relativePath);
4626
4719
  const existing = readExistingSiblingOrigins(filePath);
4627
4720
  const siblings = upsertSiblingOrigin(existing.sibling_origins ?? [], {
4628
4721
  name,
@@ -4702,8 +4795,8 @@ function defaultSiblingTemplateRoot() {
4702
4795
  const start = dirname6(fileURLToPath4(import.meta.url));
4703
4796
  let dir = start;
4704
4797
  for (; ; ) {
4705
- const candidate = join16(dir, "_skeletons", "sibling-template");
4706
- if (existsSync14(candidate)) return candidate;
4798
+ const candidate = join17(dir, "_skeletons", "sibling-template");
4799
+ if (existsSync15(candidate)) return candidate;
4707
4800
  const parent = dirname6(dir);
4708
4801
  if (parent === dir) break;
4709
4802
  dir = parent;
@@ -5160,28 +5253,28 @@ async function promptForConfig(awsAccountId, awsRegion, awsProfile) {
5160
5253
  import { Command as Command20 } from "commander";
5161
5254
 
5162
5255
  // src/commands/plugin-create.ts
5163
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
5164
- import { dirname as dirname8, join as join19, resolve as resolve11 } from "path";
5256
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
5257
+ import { dirname as dirname8, join as join20, resolve as resolve11 } from "path";
5165
5258
  import { fileURLToPath as fileURLToPath5 } from "url";
5166
5259
  import chalk13 from "chalk";
5167
5260
  import { Command as Command13 } from "commander";
5168
5261
 
5169
5262
  // src/lib/plugin-locations.ts
5170
- import { existsSync as existsSync15, readdirSync as readdirSync9 } from "fs";
5171
- import { join as join17 } from "path";
5263
+ import { existsSync as existsSync16, readdirSync as readdirSync9 } from "fs";
5264
+ import { join as join18 } from "path";
5172
5265
  var FIRST_PARTY_PLUGINS_DIR = "_plugins";
5173
5266
  var PLUGIN_MANIFEST_FILE = "biffo.plugin.json";
5174
5267
  function pluginDir(name, channel) {
5175
5268
  return channel === "first-party" ? `services/${FIRST_PARTY_PLUGINS_DIR}/${name}` : `services/${name}`;
5176
5269
  }
5177
5270
  function scanDir(absDir, relDir, channel) {
5178
- if (!existsSync15(absDir)) return [];
5271
+ if (!existsSync16(absDir)) return [];
5179
5272
  const found = [];
5180
5273
  for (const entry of readdirSync9(absDir, { withFileTypes: true })) {
5181
5274
  if (!entry.isDirectory()) continue;
5182
5275
  if (channel === "third-party" && entry.name === FIRST_PARTY_PLUGINS_DIR) continue;
5183
- const manifestPath = join17(absDir, entry.name, PLUGIN_MANIFEST_FILE);
5184
- if (!existsSync15(manifestPath)) continue;
5276
+ const manifestPath = join18(absDir, entry.name, PLUGIN_MANIFEST_FILE);
5277
+ if (!existsSync16(manifestPath)) continue;
5185
5278
  found.push({
5186
5279
  dirName: entry.name,
5187
5280
  relDir: `${relDir}/${entry.name}`,
@@ -5192,11 +5285,11 @@ function scanDir(absDir, relDir, channel) {
5192
5285
  return found;
5193
5286
  }
5194
5287
  function findInstalledPlugins(cwd) {
5195
- const servicesDir = join17(cwd, "services");
5288
+ const servicesDir = join18(cwd, "services");
5196
5289
  return [
5197
5290
  ...scanDir(servicesDir, "services", "third-party"),
5198
5291
  ...scanDir(
5199
- join17(servicesDir, FIRST_PARTY_PLUGINS_DIR),
5292
+ join18(servicesDir, FIRST_PARTY_PLUGINS_DIR),
5200
5293
  `services/${FIRST_PARTY_PLUGINS_DIR}`,
5201
5294
  "first-party"
5202
5295
  )
@@ -5356,13 +5449,13 @@ function validateManifest(raw) {
5356
5449
  // src/lib/plugin-scaffold.ts
5357
5450
  import {
5358
5451
  copyFileSync,
5359
- existsSync as existsSync16,
5452
+ existsSync as existsSync17,
5360
5453
  mkdirSync as mkdirSync7,
5361
5454
  readFileSync as readFileSync13,
5362
5455
  readdirSync as readdirSync10,
5363
5456
  writeFileSync as writeFileSync7
5364
5457
  } from "fs";
5365
- import { dirname as dirname7, join as join18 } from "path";
5458
+ import { dirname as dirname7, join as join19 } from "path";
5366
5459
  var STANDALONE_ONLY_ENTRIES = {
5367
5460
  ".github": "standalone-repo CI/release workflows \u2014 the host monorepo already runs lint/type/test/security over services/",
5368
5461
  "registry-schema.json": "the plugin-registry publishing schema, used when submitting a *published* plugin to the registry repo, not by an in-tree plugin"
@@ -5415,10 +5508,10 @@ function applySubstitutions(text, names) {
5415
5508
  }
5416
5509
  var BINARY_EXTENSIONS = /\.(png|jpe?g|gif|ico|woff2?|ttf|zip|gz)$/i;
5417
5510
  function scaffoldPlugin(skeletonRoot, destDir, names) {
5418
- if (!existsSync16(skeletonRoot)) {
5511
+ if (!existsSync17(skeletonRoot)) {
5419
5512
  throw new Error(`Plugin skeleton not found at ${skeletonRoot}`);
5420
5513
  }
5421
- if (!existsSync16(join18(skeletonRoot, "terraform"))) {
5514
+ if (!existsSync17(join19(skeletonRoot, "terraform"))) {
5422
5515
  throw new Error(
5423
5516
  `Plugin skeleton at ${skeletonRoot} has no terraform/ directory. Refusing to scaffold a plugin that cannot receive events (issue #194) \u2014 the skeleton is broken.`
5424
5517
  );
@@ -5426,7 +5519,7 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
5426
5519
  const skipped = [];
5427
5520
  const files = [];
5428
5521
  const walk = (relDir) => {
5429
- const absDir = join18(skeletonRoot, relDir);
5522
+ const absDir = join19(skeletonRoot, relDir);
5430
5523
  for (const entry of readdirSync10(absDir, { withFileTypes: true }).sort(
5431
5524
  (a, b) => a.name.localeCompare(b.name)
5432
5525
  )) {
@@ -5441,14 +5534,14 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
5441
5534
  continue;
5442
5535
  }
5443
5536
  const destRel = applySubstitutions(relPath, names);
5444
- const destPath = join18(destDir, destRel);
5537
+ const destPath = join19(destDir, destRel);
5445
5538
  mkdirSync7(dirname7(destPath), { recursive: true });
5446
5539
  if (BINARY_EXTENSIONS.test(entry.name)) {
5447
- copyFileSync(join18(skeletonRoot, relPath), destPath);
5540
+ copyFileSync(join19(skeletonRoot, relPath), destPath);
5448
5541
  } else {
5449
5542
  writeFileSync7(
5450
5543
  destPath,
5451
- applySubstitutions(readFileSync13(join18(skeletonRoot, relPath), "utf8"), names)
5544
+ applySubstitutions(readFileSync13(join19(skeletonRoot, relPath), "utf8"), names)
5452
5545
  );
5453
5546
  }
5454
5547
  files.push(destRel);
@@ -5465,8 +5558,8 @@ function scaffoldPlugin(skeletonRoot, destDir, names) {
5465
5558
  function findSkeletonRoot(startDir, skeleton) {
5466
5559
  let dir = startDir;
5467
5560
  for (; ; ) {
5468
- const candidate = join18(dir, "_skeletons", skeleton);
5469
- if (existsSync16(candidate)) return candidate;
5561
+ const candidate = join19(dir, "_skeletons", skeleton);
5562
+ if (existsSync17(candidate)) return candidate;
5470
5563
  const parent = dirname7(dir);
5471
5564
  if (parent === dir) return null;
5472
5565
  dir = parent;
@@ -5503,7 +5596,7 @@ var pluginCreateCommand = new Command13("create").description("Scaffold a new pl
5503
5596
  );
5504
5597
  async function runPluginCreate(name, options, deps) {
5505
5598
  const names = deriveNames(name);
5506
- const isInstance = existsSync17(join19(options.cwd, INSTANCE_CORE_FILE));
5599
+ const isInstance = existsSync18(join20(options.cwd, INSTANCE_CORE_FILE));
5507
5600
  if (options.firstParty && isInstance) {
5508
5601
  throw new Error(
5509
5602
  `--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.`
@@ -5511,19 +5604,19 @@ async function runPluginCreate(name, options, deps) {
5511
5604
  }
5512
5605
  const channel = options.firstParty ? "first-party" : "third-party";
5513
5606
  const relDir = pluginDir(names.slug, channel);
5514
- const destDir = join19(options.cwd, relDir);
5515
- const servicesDir = join19(options.cwd, "services");
5516
- if (!existsSync17(servicesDir)) {
5607
+ const destDir = join20(options.cwd, relDir);
5608
+ const servicesDir = join20(options.cwd, "services");
5609
+ if (!existsSync18(servicesDir)) {
5517
5610
  throw new Error(
5518
5611
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
5519
5612
  );
5520
5613
  }
5521
- if (existsSync17(destDir)) {
5614
+ if (existsSync18(destDir)) {
5522
5615
  throw new Error(`${relDir}/ already exists. Choose a different name, or remove it first.`);
5523
5616
  }
5524
5617
  const here = dirname8(fileURLToPath5(import.meta.url));
5525
- const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join19(options.cwd, "_skeletons", "plugin-template");
5526
- if (!existsSync17(skeletonRoot)) {
5618
+ const skeletonRoot = options.skeletonRoot ?? findSkeletonRoot(here, "plugin-template") ?? join20(options.cwd, "_skeletons", "plugin-template");
5619
+ if (!existsSync18(skeletonRoot)) {
5527
5620
  throw new Error(
5528
5621
  `Could not find the plugin skeleton (_skeletons/plugin-template/). Pass --skeleton <path> to point at it explicitly.`
5529
5622
  );
@@ -5538,7 +5631,7 @@ async function runPluginCreate(name, options, deps) {
5538
5631
  for (const { entry, reason } of skipped) {
5539
5632
  log.info(`Skipped ${entry} \u2014 ${reason}`);
5540
5633
  }
5541
- const manifestPath = join19(destDir, "biffo.plugin.json");
5634
+ const manifestPath = join20(destDir, "biffo.plugin.json");
5542
5635
  const manifest = validateManifest(JSON.parse(readFileSync14(manifestPath, "utf8")));
5543
5636
  if (manifest.name !== names.slug) {
5544
5637
  throw new Error(
@@ -5729,14 +5822,14 @@ function printEntry(entry) {
5729
5822
  }
5730
5823
 
5731
5824
  // src/commands/plugin-install.ts
5732
- import { cpSync as cpSync3, existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
5733
- import { basename, join as join22, relative as relative3, resolve as resolve12 } from "path";
5825
+ import { cpSync as cpSync3, existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync16, statSync as statSync5 } from "fs";
5826
+ import { basename, join as join23, relative as relative3, resolve as resolve12 } from "path";
5734
5827
  import chalk15 from "chalk";
5735
5828
  import { Command as Command15 } from "commander";
5736
5829
 
5737
5830
  // src/adapters/plugin-migrations/index.ts
5738
- import { execa as execa3 } from "execa";
5739
- import { join as join20 } from "path";
5831
+ import { execa as execa4 } from "execa";
5832
+ import { join as join21 } from "path";
5740
5833
  var PluginMigrationsAdapter = class {
5741
5834
  /**
5742
5835
  * Generates migration file(s) for `pluginNames` (every discovered
@@ -5745,22 +5838,22 @@ var PluginMigrationsAdapter = class {
5745
5838
  * or declared no tables.
5746
5839
  */
5747
5840
  async generate(cwd, pluginNames) {
5748
- const scriptPath = join20(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
5841
+ const scriptPath = join21(cwd, "services", "api", "scripts", "generate_plugin_migrations.py");
5749
5842
  const args = [
5750
5843
  "run",
5751
5844
  "python",
5752
5845
  scriptPath,
5753
5846
  "--services-root",
5754
- join20(cwd, "services"),
5847
+ join21(cwd, "services"),
5755
5848
  "--versions-dir",
5756
- join20(cwd, "services", "api", "migrations", "versions")
5849
+ join21(cwd, "services", "api", "migrations", "versions")
5757
5850
  ];
5758
5851
  for (const name of pluginNames ?? []) {
5759
5852
  args.push("--plugin", name);
5760
5853
  }
5761
5854
  let result;
5762
5855
  try {
5763
- result = await execa3("uv", args, { cwd: join20(cwd, "services", "api") });
5856
+ result = await execa4("uv", args, { cwd: join21(cwd, "services", "api") });
5764
5857
  } catch (err) {
5765
5858
  const cause = err;
5766
5859
  if (cause.code === "ENOENT") {
@@ -5777,8 +5870,8 @@ var PluginMigrationsAdapter = class {
5777
5870
  };
5778
5871
 
5779
5872
  // src/lib/plugin-terraform-wiring.ts
5780
- import { existsSync as existsSync18, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync11, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
5781
- import { join as join21 } from "path";
5873
+ import { existsSync as existsSync19, mkdirSync as mkdirSync8, readFileSync as readFileSync15, readdirSync as readdirSync11, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "fs";
5874
+ import { join as join22 } from "path";
5782
5875
  var TEMPLATE_MODULE_DIR = "_template";
5783
5876
  var DEFAULT_PLUGIN_HANDLER = "src.lambda.main.handler";
5784
5877
  var GENERATED_TF_FILE = "plugins.generated.tf";
@@ -5796,7 +5889,7 @@ function standardArguments(pluginName, handler) {
5796
5889
  ];
5797
5890
  }
5798
5891
  function listPluginModules(cwd) {
5799
- const dir = join21(cwd, "modules", "plugins");
5892
+ const dir = join22(cwd, "modules", "plugins");
5800
5893
  let entries;
5801
5894
  try {
5802
5895
  entries = readdirSync11(dir, { withFileTypes: true });
@@ -5806,7 +5899,7 @@ function listPluginModules(cwd) {
5806
5899
  return entries.filter((e) => e.isDirectory() && e.name !== TEMPLATE_MODULE_DIR && !e.name.startsWith(".")).map((e) => e.name).sort();
5807
5900
  }
5808
5901
  function listEnvironments(cwd) {
5809
- const dir = join21(cwd, "infra", "environments");
5902
+ const dir = join22(cwd, "infra", "environments");
5810
5903
  let entries;
5811
5904
  try {
5812
5905
  entries = readdirSync11(dir, { withFileTypes: true });
@@ -5814,12 +5907,12 @@ function listEnvironments(cwd) {
5814
5907
  return [];
5815
5908
  }
5816
5909
  return entries.filter((e) => {
5817
- if (!e.isDirectory() || !existsSync18(join21(dir, e.name, "main.tf"))) return false;
5818
- return declaredVariables(join21(dir, e.name)).has("enabled_plugins");
5910
+ if (!e.isDirectory() || !existsSync19(join22(dir, e.name, "main.tf"))) return false;
5911
+ return declaredVariables(join22(dir, e.name)).has("enabled_plugins");
5819
5912
  }).map((e) => e.name).sort();
5820
5913
  }
5821
5914
  function listUnwirableEnvironments(cwd) {
5822
- const dir = join21(cwd, "infra", "environments");
5915
+ const dir = join22(cwd, "infra", "environments");
5823
5916
  let entries;
5824
5917
  try {
5825
5918
  entries = readdirSync11(dir, { withFileTypes: true });
@@ -5827,7 +5920,7 @@ function listUnwirableEnvironments(cwd) {
5827
5920
  return [];
5828
5921
  }
5829
5922
  return entries.filter(
5830
- (e) => e.isDirectory() && existsSync18(join21(dir, e.name, "main.tf")) && !declaredVariables(join21(dir, e.name)).has("enabled_plugins")
5923
+ (e) => e.isDirectory() && existsSync19(join22(dir, e.name, "main.tf")) && !declaredVariables(join22(dir, e.name)).has("enabled_plugins")
5831
5924
  ).map((e) => e.name).sort();
5832
5925
  }
5833
5926
  function declaredVariables(moduleDir) {
@@ -5842,7 +5935,7 @@ function declaredVariables(moduleDir) {
5842
5935
  if (!entry.isFile() || !entry.name.endsWith(".tf")) continue;
5843
5936
  let contents;
5844
5937
  try {
5845
- contents = readFileSync15(join21(moduleDir, entry.name), "utf8");
5938
+ contents = readFileSync15(join22(moduleDir, entry.name), "utf8");
5846
5939
  } catch {
5847
5940
  continue;
5848
5941
  }
@@ -5914,19 +6007,19 @@ function syncPluginTerraform(cwd) {
5914
6007
  const changedPaths = [];
5915
6008
  const rendered = plugins.map((name) => ({
5916
6009
  name,
5917
- declaredVariables: declaredVariables(join21(cwd, "modules", "plugins", name))
6010
+ declaredVariables: declaredVariables(join22(cwd, "modules", "plugins", name))
5918
6011
  }));
5919
6012
  for (const env of environments) {
5920
- const envDir = join21(cwd, "infra", "environments", env);
5921
- const tfPath = join21(envDir, GENERATED_TF_FILE);
5922
- const tfvarsPath = join21(envDir, GENERATED_TFVARS_FILE);
6013
+ const envDir = join22(cwd, "infra", "environments", env);
6014
+ const tfPath = join22(envDir, GENERATED_TF_FILE);
6015
+ const tfvarsPath = join22(envDir, GENERATED_TFVARS_FILE);
5923
6016
  const relBase = `infra/environments/${env}`;
5924
6017
  if (plugins.length === 0) {
5925
6018
  for (const [abs, rel] of [
5926
6019
  [tfPath, `${relBase}/${GENERATED_TF_FILE}`],
5927
6020
  [tfvarsPath, `${relBase}/${GENERATED_TFVARS_FILE}`]
5928
6021
  ]) {
5929
- if (existsSync18(abs)) {
6022
+ if (existsSync19(abs)) {
5930
6023
  rmSync6(abs);
5931
6024
  changedPaths.push(rel);
5932
6025
  }
@@ -5983,14 +6076,14 @@ var LOCAL_COPY_EXCLUDES = /* @__PURE__ */ new Set([
5983
6076
  ".terraform"
5984
6077
  ]);
5985
6078
  function resolveLocalPlugin(localPath) {
5986
- if (!existsSync19(localPath)) {
6079
+ if (!existsSync20(localPath)) {
5987
6080
  throw new Error(`--local path does not exist: ${localPath}`);
5988
6081
  }
5989
6082
  if (!statSync5(localPath).isDirectory()) {
5990
6083
  throw new Error(`--local path is not a directory: ${localPath}`);
5991
6084
  }
5992
- const manifestPath = join22(localPath, "biffo.plugin.json");
5993
- if (!existsSync19(manifestPath)) {
6085
+ const manifestPath = join23(localPath, "biffo.plugin.json");
6086
+ if (!existsSync20(manifestPath)) {
5994
6087
  throw new Error(
5995
6088
  `${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>\`.)`
5996
6089
  );
@@ -6016,8 +6109,8 @@ function parsePluginTarget(target) {
6016
6109
  async function cloneAndValidatePlugin(entry, git) {
6017
6110
  const tmpDir = await git.cloneToTemp(entry.repo, `biffo-plugin-${entry.name}`);
6018
6111
  try {
6019
- const manifestPath = join22(tmpDir, "biffo.plugin.json");
6020
- if (!existsSync19(manifestPath)) {
6112
+ const manifestPath = join23(tmpDir, "biffo.plugin.json");
6113
+ if (!existsSync20(manifestPath)) {
6021
6114
  throw new Error(
6022
6115
  `Plugin repo ${entry.repo} does not contain a biffo.plugin.json manifest at its root.`
6023
6116
  );
@@ -6045,8 +6138,8 @@ async function runPluginInstall(target, options, deps) {
6045
6138
  `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\`).`
6046
6139
  );
6047
6140
  }
6048
- const servicesDir = join22(options.cwd, "services");
6049
- if (!existsSync19(servicesDir)) {
6141
+ const servicesDir = join23(options.cwd, "services");
6142
+ if (!existsSync20(servicesDir)) {
6050
6143
  throw new Error(
6051
6144
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6052
6145
  );
@@ -6064,10 +6157,10 @@ async function runPluginInstall(target, options, deps) {
6064
6157
  }
6065
6158
  const pluginName = entry ? entry.name : source.name;
6066
6159
  const relTargetDir = pluginDir(pluginName, "third-party");
6067
- const targetDir = join22(options.cwd, relTargetDir);
6068
- const modulesDir = join22(options.cwd, "modules", "plugins", pluginName);
6160
+ const targetDir = join23(options.cwd, relTargetDir);
6161
+ const modulesDir = join23(options.cwd, "modules", "plugins", pluginName);
6069
6162
  const inTreeSource = options.local !== void 0 && resolve12(options.local) === resolve12(targetDir);
6070
- if (existsSync19(targetDir) && !inTreeSource) {
6163
+ if (existsSync20(targetDir) && !inTreeSource) {
6071
6164
  throw new Error(
6072
6165
  `Plugin '${pluginName}' is already installed at ${relTargetDir}/. Remove it first, or wait for a future 'biffo plugin upgrade' command.`
6073
6166
  );
@@ -6110,8 +6203,8 @@ async function runPluginInstall(target, options, deps) {
6110
6203
  log.success(`Installed plugin source at ${relTargetDir}/`);
6111
6204
  }
6112
6205
  const stagePaths = [relTargetDir];
6113
- const tfSourceDir = join22(targetDir, "terraform");
6114
- if (existsSync19(tfSourceDir)) {
6206
+ const tfSourceDir = join23(targetDir, "terraform");
6207
+ if (existsSync20(tfSourceDir)) {
6115
6208
  mkdirSync9(modulesDir, { recursive: true });
6116
6209
  cpSync3(tfSourceDir, modulesDir, { recursive: true });
6117
6210
  stagePaths.push(`modules/plugins/${pluginName}`);
@@ -6205,8 +6298,8 @@ function printDryRun4(entry, source, relTargetDir, inTreeSource) {
6205
6298
  }
6206
6299
 
6207
6300
  // src/commands/plugin-list.ts
6208
- import { existsSync as existsSync20, readFileSync as readFileSync17 } from "fs";
6209
- import { join as join23, resolve as resolve13 } from "path";
6301
+ import { existsSync as existsSync21, readFileSync as readFileSync17 } from "fs";
6302
+ import { join as join24, resolve as resolve13 } from "path";
6210
6303
  import chalk16 from "chalk";
6211
6304
  import { Command as Command16 } from "commander";
6212
6305
  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) => {
@@ -6219,8 +6312,8 @@ var pluginListCommand = new Command16("list").description("List plugins installe
6219
6312
  }
6220
6313
  });
6221
6314
  async function runPluginList(options) {
6222
- const servicesDir = join23(options.cwd, "services");
6223
- if (!existsSync20(servicesDir)) {
6315
+ const servicesDir = join24(options.cwd, "services");
6316
+ if (!existsSync21(servicesDir)) {
6224
6317
  throw new Error(
6225
6318
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6226
6319
  );
@@ -6265,8 +6358,8 @@ async function runPluginList(options) {
6265
6358
  }
6266
6359
 
6267
6360
  // src/commands/plugin-sync-migrations.ts
6268
- import { existsSync as existsSync21 } from "fs";
6269
- import { join as join24, relative as relative4, resolve as resolve14 } from "path";
6361
+ import { existsSync as existsSync22 } from "fs";
6362
+ import { join as join25, relative as relative4, resolve as resolve14 } from "path";
6270
6363
  import chalk17 from "chalk";
6271
6364
  import { Command as Command17 } from "commander";
6272
6365
  var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
@@ -6287,11 +6380,11 @@ var pluginSyncMigrationsCommand = new Command17("sync-migrations").description(
6287
6380
  }
6288
6381
  );
6289
6382
  async function runPluginSyncMigrations(name, options, deps) {
6290
- const servicesDir = join24(options.cwd, "services");
6291
- if (!existsSync21(servicesDir)) {
6383
+ const servicesDir = join25(options.cwd, "services");
6384
+ if (!existsSync22(servicesDir)) {
6292
6385
  throw new Error(`${servicesDir} does not exist \u2014 is ${options.cwd} a Biffo project checkout?`);
6293
6386
  }
6294
- if (name && !existsSync21(join24(servicesDir, name, "biffo.plugin.json"))) {
6387
+ if (name && !existsSync22(join25(servicesDir, name, "biffo.plugin.json"))) {
6295
6388
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
6296
6389
  }
6297
6390
  if (options.dryRun) {
@@ -6327,8 +6420,8 @@ async function runPluginSyncMigrations(name, options, deps) {
6327
6420
  }
6328
6421
 
6329
6422
  // src/commands/plugin-uninstall.ts
6330
- import { existsSync as existsSync22, readFileSync as readFileSync18, rmSync as rmSync7 } from "fs";
6331
- import { join as join25, resolve as resolve15 } from "path";
6423
+ import { existsSync as existsSync23, readFileSync as readFileSync18, rmSync as rmSync7 } from "fs";
6424
+ import { join as join26, resolve as resolve15 } from "path";
6332
6425
  import chalk18 from "chalk";
6333
6426
  import { Command as Command18 } from "commander";
6334
6427
  import inquirer6 from "inquirer";
@@ -6360,16 +6453,16 @@ async function runPluginUninstall(name, options, deps) {
6360
6453
  if (!NAME_PATTERN2.test(name)) {
6361
6454
  throw new Error(`Invalid plugin name '${name}'. Expected a lowercase kebab-case slug.`);
6362
6455
  }
6363
- const servicesDir = join25(options.cwd, "services");
6364
- if (!existsSync22(servicesDir)) {
6456
+ const servicesDir = join26(options.cwd, "services");
6457
+ if (!existsSync23(servicesDir)) {
6365
6458
  throw new Error(
6366
6459
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6367
6460
  );
6368
6461
  }
6369
- const targetDir = join25(servicesDir, name);
6370
- if (!existsSync22(targetDir)) {
6371
- const firstParty = join25(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
6372
- if (existsSync22(firstParty)) {
6462
+ const targetDir = join26(servicesDir, name);
6463
+ if (!existsSync23(targetDir)) {
6464
+ const firstParty = join26(servicesDir, FIRST_PARTY_PLUGINS_DIR, name);
6465
+ if (existsSync23(firstParty)) {
6373
6466
  throw new Error(
6374
6467
  `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.`
6375
6468
  );
@@ -6377,9 +6470,9 @@ async function runPluginUninstall(name, options, deps) {
6377
6470
  throw new Error(`Plugin '${name}' is not installed at services/${name}/.`);
6378
6471
  }
6379
6472
  const version = readInstalledVersion(targetDir);
6380
- const modulesDir = join25(options.cwd, "modules", "plugins", name);
6473
+ const modulesDir = join26(options.cwd, "modules", "plugins", name);
6381
6474
  const stagePaths = [`services/${name}`];
6382
- if (existsSync22(modulesDir)) {
6475
+ if (existsSync23(modulesDir)) {
6383
6476
  stagePaths.push(`modules/plugins/${name}`);
6384
6477
  }
6385
6478
  if (options.dryRun) {
@@ -6401,7 +6494,7 @@ async function runPluginUninstall(name, options, deps) {
6401
6494
  }
6402
6495
  rmSync7(targetDir, { recursive: true, force: true });
6403
6496
  log.success(`Removed services/${name}/`);
6404
- if (existsSync22(modulesDir)) {
6497
+ if (existsSync23(modulesDir)) {
6405
6498
  rmSync7(modulesDir, { recursive: true, force: true });
6406
6499
  log.success(`Removed modules/plugins/${name}/`);
6407
6500
  const wiring = syncPluginTerraform(options.cwd);
@@ -6438,8 +6531,8 @@ async function runPluginUninstall(name, options, deps) {
6438
6531
  }
6439
6532
  }
6440
6533
  function readInstalledVersion(targetDir) {
6441
- const manifestPath = join25(targetDir, "biffo.plugin.json");
6442
- if (!existsSync22(manifestPath)) return void 0;
6534
+ const manifestPath = join26(targetDir, "biffo.plugin.json");
6535
+ if (!existsSync23(manifestPath)) return void 0;
6443
6536
  try {
6444
6537
  return validateManifest(JSON.parse(readFileSync18(manifestPath, "utf8"))).version;
6445
6538
  } catch {
@@ -6474,8 +6567,8 @@ function printDryRun5(name, version, stagePaths, keepData) {
6474
6567
  }
6475
6568
 
6476
6569
  // src/commands/plugin-upgrade.ts
6477
- import { cpSync as cpSync4, existsSync as existsSync23, mkdirSync as mkdirSync10, readFileSync as readFileSync19, rmSync as rmSync8 } from "fs";
6478
- import { join as join26, relative as relative5, resolve as resolve16 } from "path";
6570
+ import { cpSync as cpSync4, existsSync as existsSync24, mkdirSync as mkdirSync10, readFileSync as readFileSync19, rmSync as rmSync8 } from "fs";
6571
+ import { join as join27, relative as relative5, resolve as resolve16 } from "path";
6479
6572
  import chalk19 from "chalk";
6480
6573
  import { Command as Command19 } from "commander";
6481
6574
  import inquirer7 from "inquirer";
@@ -6500,14 +6593,14 @@ var pluginUpgradeCommand = new Command19("upgrade").description(
6500
6593
  });
6501
6594
  async function runPluginUpgrade(target, options, deps) {
6502
6595
  const { name, minor } = parsePluginTarget(target);
6503
- const servicesDir = join26(options.cwd, "services");
6504
- if (!existsSync23(servicesDir)) {
6596
+ const servicesDir = join27(options.cwd, "services");
6597
+ if (!existsSync24(servicesDir)) {
6505
6598
  throw new Error(
6506
6599
  `${servicesDir} does not exist \u2014 is ${options.cwd} the root of a Biffo project checkout?`
6507
6600
  );
6508
6601
  }
6509
- const targetDir = join26(servicesDir, name);
6510
- if (!existsSync23(targetDir)) {
6602
+ const targetDir = join27(servicesDir, name);
6603
+ if (!existsSync24(targetDir)) {
6511
6604
  throw new Error(
6512
6605
  `Plugin '${name}' is not installed at services/${name}/. Use 'biffo plugin install ${name}@${minor}' instead.`
6513
6606
  );
@@ -6521,7 +6614,7 @@ async function runPluginUpgrade(target, options, deps) {
6521
6614
  `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.`
6522
6615
  );
6523
6616
  }
6524
- const modulesDir = join26(options.cwd, "modules", "plugins", entry.name);
6617
+ const modulesDir = join27(options.cwd, "modules", "plugins", entry.name);
6525
6618
  if (options.dryRun) {
6526
6619
  printDryRun6(entry, currentVersion);
6527
6620
  return;
@@ -6554,11 +6647,11 @@ async function runPluginUpgrade(target, options, deps) {
6554
6647
  cpSync4(tmpDir, targetDir, { recursive: true });
6555
6648
  log.success(`Upgraded plugin source at services/${entry.name}/`);
6556
6649
  const stagePaths = [`services/${entry.name}`];
6557
- if (existsSync23(modulesDir)) {
6650
+ if (existsSync24(modulesDir)) {
6558
6651
  rmSync8(modulesDir, { recursive: true, force: true });
6559
6652
  }
6560
- const tfSourceDir = join26(targetDir, "terraform");
6561
- if (existsSync23(tfSourceDir)) {
6653
+ const tfSourceDir = join27(targetDir, "terraform");
6654
+ if (existsSync24(tfSourceDir)) {
6562
6655
  mkdirSync10(modulesDir, { recursive: true });
6563
6656
  cpSync4(tfSourceDir, modulesDir, { recursive: true });
6564
6657
  stagePaths.push(`modules/plugins/${entry.name}`);
@@ -6592,8 +6685,8 @@ async function runPluginUpgrade(target, options, deps) {
6592
6685
  }
6593
6686
  }
6594
6687
  function readInstalledVersion2(targetDir) {
6595
- const manifestPath = join26(targetDir, "biffo.plugin.json");
6596
- if (!existsSync23(manifestPath)) return void 0;
6688
+ const manifestPath = join27(targetDir, "biffo.plugin.json");
6689
+ if (!existsSync24(manifestPath)) return void 0;
6597
6690
  try {
6598
6691
  return validateManifest(JSON.parse(readFileSync19(manifestPath, "utf8"))).version;
6599
6692
  } catch {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@biffo/cli",
3
- "version": "0.50.1",
3
+ "version": "0.50.3",
4
4
  "description": "Biffo project scaffolding CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",