betterstart-cli 0.0.85 → 0.0.87

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.
package/dist/cli.js CHANGED
@@ -22,6 +22,7 @@ import {
22
22
 
23
23
  // cli.ts
24
24
  import { readFileSync } from "fs";
25
+ import * as p29 from "@clack/prompts";
25
26
 
26
27
  // core-engine/commands/require-init.ts
27
28
  import path2 from "path";
@@ -74,7 +75,13 @@ function createAddCommand(runtime) {
74
75
  "--skip-migration",
75
76
  "Skip running db:push after installing schema-backed presets",
76
77
  false
77
- ).option("--cwd <path>", "Project root path").action((items, options) => runtime.runAdd(items, options));
78
+ ).option("--cwd <path>", "Project root path").addHelpText(
79
+ "after",
80
+ `
81
+ Examples:
82
+ $ betterstart add blog
83
+ $ betterstart add r2 --integration`
84
+ ).action((items, options) => runtime.runAdd(items, options));
78
85
  }
79
86
  function collectRepeatedOption(value, previous = []) {
80
87
  return [...previous, value];
@@ -95,6 +102,12 @@ function createCreateCommand(runtime) {
95
102
  "--accept-generated",
96
103
  "Accept generated output for files that would otherwise get merge conflicts",
97
104
  false
105
+ ).addHelpText(
106
+ "after",
107
+ `
108
+ Examples:
109
+ $ betterstart create entity
110
+ $ betterstart create form contact`
98
111
  ).action(
99
112
  (kind, schemaName, options) => runtime.runCreate(kind, schemaName, options)
100
113
  );
@@ -104,12 +117,18 @@ function createGenerateCommand(runtime) {
104
117
  "--accept-generated",
105
118
  "Accept generated output for files that would otherwise get merge conflicts",
106
119
  false
107
- ).option("-a, --all", "Regenerate all schemas", false).option("--interactive", "Prompt during generate --all", false).option("--skip-migration", "Skip running db:push after generation", false).option("--cwd <path>", "Project root path").action(
120
+ ).option("-a, --all", "Regenerate all schemas", false).option("--interactive", "Prompt during generate --all", false).option("--skip-migration", "Skip running db:push after generation", false).option("--cwd <path>", "Project root path").addHelpText(
121
+ "after",
122
+ `
123
+ Examples:
124
+ $ betterstart generate posts
125
+ $ betterstart generate --all --skip-migration`
126
+ ).action(
108
127
  (schemaName, options) => runtime.runGenerate(schemaName, options)
109
128
  );
110
129
  }
111
130
  function createInitCommand(runtime) {
112
- return new Command("init").description("Scaffold Admin into a new or existing Next.js app").argument("[name]", "Project name (creates new directory if fresh project)").option("--presets <presets>", "Comma-separated content preset IDs (e.g. blog)").option("--integrations <integrations>", "Comma-separated integration IDs (e.g. r2,resend)").option("--namespace <name>", "Generated admin namespace (e.g. admin, dashboard)").option("-y, --yes", "Skip prompts (defaults to manual database, local storage, and no deploy)").option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
131
+ return new Command("init").description("Scaffold Admin into a new or existing Next.js app").argument("[name]", "Project name (creates new directory if fresh project)").option("--presets <presets>", "Comma-separated content preset IDs (e.g. blog)").option("--integrations <integrations>", "Comma-separated integration IDs (e.g. r2,resend)").option("--namespace <name>", "Generated admin namespace (e.g. admin, dashboard)").option("-y, --yes", "Skip prompts (defaults to manual database, local storage, and no deploy)").option("--json", "With --yes, print a machine-readable setup result on stdout", false).option("--skip-migration", "Skip running drizzle-kit push after scaffolding").option("--skip-admin-creation", "Skip prompting to create the initial admin user").option("--skip-dev-server-start", "Skip prompting to start the development server").option(
113
132
  "--database-url <url>",
114
133
  "PostgreSQL database connection string (postgres:// or postgresql://)"
115
134
  ).option("--database-provider <provider>", "Database provider: vercel, railway, or manual").option(
@@ -118,15 +137,22 @@ function createInitCommand(runtime) {
118
137
  ).option("--deploy-provider <provider>", "Deploy provider: vercel, railway, or none").option("--database-plan <id>", "Neon plan id for a Vercel-provisioned database").option("--railway-workspace <id-or-name>", "Railway workspace for a new project").option("--railway-project <id-or-name>", "Existing Railway project to intentionally reuse").option("--railway-environment <id-or-name>", "Railway environment (default: production)").option("--railway-service <id-or-name>", "Railway app service (default: web)").option(
119
138
  "--railway-bucket-region <region>",
120
139
  "Railway bucket region: sjc, iad, ams, or sin (default: iad)"
121
- ).option("--force", "Overwrite all existing Admin files (nuclear option)").action(
140
+ ).option("--force", "Overwrite all existing Admin files (nuclear option)").addHelpText(
141
+ "after",
142
+ `
143
+ Examples:
144
+ $ betterstart init
145
+ $ betterstart init my-app --yes --database-url <url>
146
+ $ betterstart init --yes --database-provider vercel --deploy-provider vercel --json`
147
+ ).action(
122
148
  (name, options) => runtime.runInit(name, options)
123
149
  );
124
150
  }
125
151
  function createListPresetsCommand(runtime) {
126
- return new Command("list-presets").description("List available BetterStart presets").option("--cwd <path>", "Project root path").action((options) => runtime.runListPresets(options));
152
+ return new Command("list-presets").description("List available BetterStart presets").option("--json", "Print presets as JSON on stdout", false).option("--cwd <path>", "Project root path").action((options) => runtime.runListPresets(options));
127
153
  }
128
154
  function createListIntegrationsCommand(runtime) {
129
- return new Command("list-integrations").description("List available BetterStart integrations").option("--cwd <path>", "Project root path").action((options) => runtime.runListIntegrations(options));
155
+ return new Command("list-integrations").description("List available BetterStart integrations").option("--json", "Print integrations as JSON on stdout", false).option("--cwd <path>", "Project root path").action((options) => runtime.runListIntegrations(options));
130
156
  }
131
157
  function createRemoveCommand(runtime) {
132
158
  return new Command("remove").description("Remove installed BetterStart presets or integrations").argument("<items...>", "Preset or integration IDs to remove (e.g. blog r2)").option("-f, --force", "Skip confirmation prompt", false).option("--integration", "Remove integration IDs instead of content preset IDs", false).option("--cwd <path>", "Project root path").action((items, options) => runtime.runRemove(items, options));
@@ -137,13 +163,29 @@ function createRemoveSchemaCommand(runtime) {
137
163
  );
138
164
  }
139
165
  function createSeedCommand(runtime) {
140
- return new Command("seed").description("Create the initial admin user").option("--cwd <path>", "Project root path").action((options) => runtime.runSeed(options));
166
+ return new Command("seed").description("Create the initial admin user").option("--email <email>", "Admin email (required off-TTY)").option(
167
+ "--name <name>",
168
+ 'Admin display name (defaults to "Admin"); set the password via BETTERSTART_ADMIN_PASSWORD off-TTY'
169
+ ).option("--cwd <path>", "Project root path").addHelpText(
170
+ "after",
171
+ `
172
+ Examples:
173
+ $ betterstart seed
174
+ $ BETTERSTART_ADMIN_PASSWORD=<password> betterstart seed --email admin@example.com`
175
+ ).action((options) => runtime.runSeed(options));
141
176
  }
142
177
  function createUninstallCommand(runtime) {
143
178
  return new Command("uninstall").description("Remove all Admin files and undo modifications made by betterstart init").option("-f, --force", "Skip all confirmation prompts", false).option("--cwd <path>", "Project root path").action((options) => runtime.runUninstall(options));
144
179
  }
145
180
  function createUpdateCommand(runtime) {
146
- return new Command("update").alias("update-component").description("Update Admin components from the latest CLI templates").argument("[components...]", "Component names to update (e.g., media-field button)").option("--list", "List all available components").option("--all", "Update all components").option("--shadcn-preset <preset>", "Apply a shadcn preset to Admin UI components").option("--only <parts>", "Apply only supported shadcn preset parts: theme").option("--cwd <path>", "Project root path").action(
181
+ return new Command("update").alias("update-component").description("Update Admin components from the latest CLI templates").argument("[components...]", "Component names to update (e.g., media-field button)").option("--list", "List all available components").option("--json", "With --list, print components as JSON on stdout", false).option("--all", "Update all components").option("--shadcn-preset <preset>", "Apply a shadcn preset to Admin UI components").option("--only <parts>", "Apply only supported shadcn preset parts: theme").option("-y, --yes", "Skip the overwrite confirmation", false).option("--cwd <path>", "Project root path").addHelpText(
182
+ "after",
183
+ `
184
+ Examples:
185
+ $ betterstart update --list
186
+ $ betterstart update media-field
187
+ $ betterstart update --all --yes`
188
+ ).action(
147
189
  (components, options) => runtime.runUpdate(components, options)
148
190
  );
149
191
  }
@@ -156,7 +198,7 @@ function createUpdateStylesCommand(runtime) {
156
198
 
157
199
  // adapters/next/commands/add.ts
158
200
  import path27 from "path";
159
- import * as p4 from "@clack/prompts";
201
+ import * as p6 from "@clack/prompts";
160
202
 
161
203
  // core-engine/config/serialize.ts
162
204
  import fs3 from "fs";
@@ -186,6 +228,7 @@ function compactShortStringArrays(json) {
186
228
  // core-engine/snapshots/store.ts
187
229
  var BARREL_SCOPE = "_barrels";
188
230
  var REMOVED_SCOPE = "_removed";
231
+ var UNSUPPORTED_PROJECT_STATE_MESSAGE = "Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout.";
189
232
  function normalizeLineEndings(content) {
190
233
  return content.replace(/\r\n/g, "\n");
191
234
  }
@@ -459,6 +502,7 @@ function createNextAppCommand() {
459
502
  // adapters/next/config.ts
460
503
  import fs6 from "fs";
461
504
  import path7 from "path";
505
+ import * as clack from "@clack/prompts";
462
506
 
463
507
  // adapters/next/config/detect.ts
464
508
  import fs5 from "fs";
@@ -691,11 +735,22 @@ async function resolveConfig(cwd) {
691
735
  };
692
736
  return config;
693
737
  }
738
+ async function resolveConfigOrExit(cwd) {
739
+ try {
740
+ return await resolveConfig(cwd);
741
+ } catch (error) {
742
+ clack.log.error(
743
+ `Error loading config: ${error instanceof Error ? error.message : String(error)}`
744
+ );
745
+ process.exit(1);
746
+ }
747
+ }
694
748
 
695
749
  // adapters/next/generators/post-generate.ts
696
750
  import { execFileSync as execFileSync2 } from "child_process";
697
751
  import fs8 from "fs";
698
752
  import path10 from "path";
753
+ import * as p3 from "@clack/prompts";
699
754
 
700
755
  // adapters/next/init/scaffolders/dependencies.ts
701
756
  import { spawn } from "child_process";
@@ -1739,6 +1794,19 @@ ${stderr}`.trim();
1739
1794
  });
1740
1795
  }
1741
1796
 
1797
+ // adapters/next/utils/next-steps.ts
1798
+ import * as p2 from "@clack/prompts";
1799
+ function printNextSteps(options) {
1800
+ const steps = [`1. Review ${options.reviewLabel}`];
1801
+ if (options.needsMigration) {
1802
+ steps.push("2. Run database migration: db:push");
1803
+ steps.push(`3. Start the dev server and visit ${options.route}`);
1804
+ } else {
1805
+ steps.push(`2. Start the dev server and visit ${options.route}`);
1806
+ }
1807
+ p2.note(steps.join("\n"), "Next steps");
1808
+ }
1809
+
1742
1810
  // adapters/next/generators/post-generate.ts
1743
1811
  var BIOME_FORMAT_TIMEOUT_MS = 3e4;
1744
1812
  function loadEnvFile(cwd) {
@@ -1838,26 +1906,25 @@ function ensureDatabasePushDependencies(cwd, pm) {
1838
1906
  if (missing.length === 0) {
1839
1907
  return true;
1840
1908
  }
1841
- console.log(
1842
- `
1843
- Installing database push dependencies (${missing.map((dependency) => dependency.name).join(", ")})...`
1909
+ p3.log.info(
1910
+ `Installing database push dependencies (${missing.map((dependency) => dependency.name).join(", ")})...`
1844
1911
  );
1845
1912
  for (const dependency of missing) {
1846
1913
  const installed2 = installDependency(cwd, pm, dependency.name, dependency.dev);
1847
1914
  if (!installed2) {
1848
- console.log(` Failed to install ${dependency.name}`);
1915
+ p3.log.warn(`Failed to install ${dependency.name}`);
1849
1916
  return false;
1850
1917
  }
1851
1918
  }
1852
1919
  const ready = hasPostgresRuntimeDependency(cwd) && hasDrizzleKitPostgresDriverDependency(cwd);
1853
1920
  if (ready) {
1854
- console.log(" Database push dependencies installed");
1921
+ p3.log.success("Database push dependencies installed");
1855
1922
  } else {
1856
1923
  const unresolved = [
1857
1924
  !hasPostgresRuntimeDependency(cwd) ? POSTGRES_RUNTIME_DEP : null,
1858
1925
  !hasDrizzleKitPostgresDriverDependency(cwd) ? DRIZZLE_KIT_POSTGRES_DRIVER_DEP : null
1859
1926
  ].filter((dependency) => Boolean(dependency));
1860
- console.log(` Installed dependencies but could not resolve ${unresolved.join(", ")}`);
1927
+ p3.log.warn(`Installed dependencies but could not resolve ${unresolved.join(", ")}`);
1861
1928
  }
1862
1929
  return ready;
1863
1930
  }
@@ -1895,16 +1962,14 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
1895
1962
  if (options.hasConflicts) {
1896
1963
  result.dbPush = "skipped";
1897
1964
  result.lintFix = "skipped";
1898
- console.log(
1899
- `
1900
- Database: skipped (resolve conflict markers in ${options.conflictPaths?.length ?? 0} file(s), then re-run)`
1901
- );
1902
- console.log(" Formatting: skipped (biome refuses files with conflict markers)");
1903
- for (const conflictPath of options.conflictPaths ?? []) {
1904
- console.log(` - ${conflictPath}`);
1905
- }
1906
- console.log(
1907
- "\n Resolve markers in the files above and re-run the BetterStart command that wrote them. Post-write tasks run automatically once every file is clean."
1965
+ const lines = [
1966
+ `Database: skipped (resolve conflict markers in ${options.conflictPaths?.length ?? 0} file(s), then re-run)`,
1967
+ "Formatting: skipped (biome refuses files with conflict markers)",
1968
+ ...(options.conflictPaths ?? []).map((conflictPath) => ` - ${conflictPath}`)
1969
+ ];
1970
+ p3.log.warn(lines.join("\n"));
1971
+ p3.log.message(
1972
+ "Resolve markers in the files above and re-run the BetterStart command that wrote them. Post-write tasks run automatically once every file is clean."
1908
1973
  );
1909
1974
  return result;
1910
1975
  }
@@ -1913,63 +1978,71 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
1913
1978
  const dbUrl = process.env.DATABASE_URL;
1914
1979
  if (!dbUrl) {
1915
1980
  result.dbPush = "no-db-url";
1916
- console.log("\n Database: skipped (no DATABASE_URL configured)");
1917
- console.log(" To sync later: run db:push after setting DATABASE_URL");
1981
+ p3.log.warn(
1982
+ [
1983
+ "Database: skipped (no DATABASE_URL configured)",
1984
+ " To sync later: run db:push after setting DATABASE_URL"
1985
+ ].join("\n")
1986
+ );
1918
1987
  } else if (!ensureDatabasePushDependencies(cwd, pm)) {
1919
1988
  result.dbPush = "failed";
1920
- console.log(
1921
- " Database push failed (install database dependencies and run drizzle-kit push manually)"
1989
+ p3.log.warn(
1990
+ "Database push failed (install database dependencies and run drizzle-kit push manually)"
1922
1991
  );
1923
1992
  } else if (hasPkgScript(cwd, "db:push")) {
1924
- console.log("\n Running db:push...");
1993
+ p3.log.info("Running db:push...");
1925
1994
  const ok = runPmScript(pm, "db:push", cwd);
1926
1995
  result.dbPush = ok ? "success" : "failed";
1927
- console.log(ok ? " Database schema synced" : " Database push failed (run db:push manually)");
1996
+ if (ok) {
1997
+ p3.log.success("Database schema synced");
1998
+ } else {
1999
+ p3.log.warn("Database push failed (run db:push manually)");
2000
+ }
1928
2001
  } else {
1929
- console.log("\n Running drizzle-kit push...");
2002
+ p3.log.info("Running drizzle-kit push...");
1930
2003
  try {
1931
2004
  const pushResult = await runDrizzlePush(cwd, { interactive: true });
1932
2005
  if (!pushResult.success) {
1933
2006
  throw new Error(pushResult.error ?? "drizzle-kit push failed");
1934
2007
  }
1935
2008
  result.dbPush = "success";
1936
- console.log(" Database schema synced");
2009
+ p3.log.success("Database schema synced");
1937
2010
  } catch {
1938
2011
  result.dbPush = "failed";
1939
- console.log(" Database push failed (run drizzle-kit push manually)");
2012
+ p3.log.warn("Database push failed (run drizzle-kit push manually)");
1940
2013
  }
1941
2014
  }
1942
2015
  } else {
1943
- console.log(`
1944
- Database: skipped (${options.skipMigrationMessage ?? "--skip-migration"})`);
2016
+ p3.log.info(`Database: skipped (${options.skipMigrationMessage ?? "--skip-migration"})`);
1945
2017
  }
1946
2018
  if (hasPkgScript(cwd, "lint:fix")) {
1947
- console.log(" Running lint:fix...");
2019
+ p3.log.info("Running lint:fix...");
1948
2020
  const ok = runPmScript(pm, "lint:fix", cwd, BIOME_FORMAT_TIMEOUT_MS);
1949
2021
  result.lintFix = ok ? "success" : "failed";
1950
- console.log(ok ? " Code formatted" : " Lint fix had issues (run lint:fix manually)");
2022
+ if (ok) {
2023
+ p3.log.success("Code formatted");
2024
+ } else {
2025
+ p3.log.warn("Lint fix had issues (run lint:fix manually)");
2026
+ }
1951
2027
  } else {
1952
2028
  try {
1953
2029
  if (!runBiomeCheckWrite(cwd)) {
1954
2030
  throw new Error("Biome binary not found");
1955
2031
  }
1956
2032
  result.lintFix = "success";
1957
- console.log(" Code formatted with Biome");
2033
+ p3.log.success("Code formatted with Biome");
1958
2034
  } catch {
1959
2035
  result.lintFix = "failed";
1960
- console.log(" Biome formatting had issues (run biome check --write manually)");
2036
+ p3.log.warn("Biome formatting had issues (run biome check --write manually)");
1961
2037
  }
1962
2038
  }
1963
2039
  if (options.showNextSteps !== false) {
1964
2040
  const schemaRoute = options.schemaRoutePath ?? `${options.adminRoutePath ?? "/admin"}/${schemaName}`;
1965
- console.log("\n Next steps:");
1966
- console.log(" 1. Review the generated files");
1967
- if (options.skipMigration || result.dbPush !== "success") {
1968
- console.log(" 2. Run database migration: db:push");
1969
- console.log(` 3. Start the dev server and visit ${schemaRoute}`);
1970
- } else {
1971
- console.log(` 2. Start the dev server and visit ${schemaRoute}`);
1972
- }
2041
+ printNextSteps({
2042
+ reviewLabel: "the generated files",
2043
+ needsMigration: Boolean(options.skipMigration) || result.dbPush !== "success",
2044
+ route: schemaRoute
2045
+ });
1973
2046
  }
1974
2047
  return result;
1975
2048
  }
@@ -1977,7 +2050,7 @@ async function runPostGenerate(cwd, schemaName, options = {}) {
1977
2050
  // adapters/next/integration-runtime.ts
1978
2051
  import fs14 from "fs";
1979
2052
  import path17 from "path";
1980
- import * as p2 from "@clack/prompts";
2053
+ import * as p4 from "@clack/prompts";
1981
2054
 
1982
2055
  // core-engine/schema/schema-reader.ts
1983
2056
  import fs9 from "fs";
@@ -3303,12 +3376,12 @@ async function resolveTextEnvValue(options) {
3303
3376
  if (existingValue) {
3304
3377
  return existingValue;
3305
3378
  }
3306
- const result = await p2.text({
3379
+ const result = await p4.text({
3307
3380
  message: options.message,
3308
3381
  defaultValue: options.defaultValue,
3309
3382
  validate: options.validate
3310
3383
  });
3311
- if (p2.isCancel(result)) {
3384
+ if (p4.isCancel(result)) {
3312
3385
  throw new Error(options.cancelMessage);
3313
3386
  }
3314
3387
  options.overwriteEnvKeys.add(options.key);
@@ -3319,11 +3392,11 @@ async function resolvePasswordEnvValue(options) {
3319
3392
  if (existingValue) {
3320
3393
  return existingValue;
3321
3394
  }
3322
- const result = await p2.password({
3395
+ const result = await p4.password({
3323
3396
  message: options.message,
3324
3397
  validate: options.validate
3325
3398
  });
3326
- if (p2.isCancel(result)) {
3399
+ if (p4.isCancel(result)) {
3327
3400
  throw new Error(options.cancelMessage);
3328
3401
  }
3329
3402
  options.overwriteEnvKeys.add(options.key);
@@ -5704,14 +5777,14 @@ function buildStepsConstant(steps) {
5704
5777
  ${entries.join(",\n")}
5705
5778
  ]`;
5706
5779
  }
5707
- function buildComponentSource(p27) {
5780
+ function buildComponentSource(p30) {
5708
5781
  const providerOpen = ` <NuqsAdapter>
5709
5782
  <React.Suspense fallback={null}>
5710
- <${p27.pascal}FormInner />
5783
+ <${p30.pascal}FormInner />
5711
5784
  </React.Suspense>
5712
5785
  </NuqsAdapter>`;
5713
5786
  const exportWrapper = `
5714
- export function ${p27.pascal}Form() {
5787
+ export function ${p30.pascal}Form() {
5715
5788
  return (
5716
5789
  ${providerOpen}
5717
5790
  )
@@ -5720,22 +5793,22 @@ ${providerOpen}
5720
5793
  return `'use client'
5721
5794
 
5722
5795
  import { standardSchemaResolver } from '@hookform/resolvers/standard-schema'
5723
- import { ChevronLeft, ChevronRight${p27.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
5796
+ import { ChevronLeft, ChevronRight${p30.hasListFields ? ", Trash2" : ""} } from 'lucide-react'
5724
5797
  import { createParser, useQueryState } from 'nuqs'
5725
5798
  import { NuqsAdapter } from 'nuqs/adapters/next/app'
5726
5799
  import * as React from 'react'
5727
- ${p27.rhfImport}
5800
+ ${p30.rhfImport}
5728
5801
  import { z } from 'zod/v3'
5729
- import { create${p27.pascal}Submission } from '@admin/actions/${p27.actionImportPath}'
5802
+ import { create${p30.pascal}Submission } from '@admin/actions/${p30.actionImportPath}'
5730
5803
 
5731
5804
  const formSchema = z.object({
5732
- ${p27.zodFields}
5805
+ ${p30.zodFields}
5733
5806
  })
5734
5807
 
5735
5808
  type FormValues = z.infer<typeof formSchema>
5736
5809
  ${buildFieldErrorHelper()}
5737
5810
 
5738
- ${p27.stepsConst}
5811
+ ${p30.stepsConst}
5739
5812
 
5740
5813
  const stepParser = createParser({
5741
5814
  parse(value) {
@@ -5753,7 +5826,7 @@ const stepParser = createParser({
5753
5826
  }
5754
5827
  }).withDefault(0)
5755
5828
 
5756
- function ${p27.pascal}FormInner() {
5829
+ function ${p30.pascal}FormInner() {
5757
5830
  const [currentStep, setCurrentStep] = useQueryState('step', stepParser)
5758
5831
  const [submitted, setSubmitted] = React.useState(false)
5759
5832
  const [submitting, startSubmitTransition] = React.useTransition()
@@ -5761,11 +5834,11 @@ function ${p27.pascal}FormInner() {
5761
5834
  const form = useForm<FormValues>({
5762
5835
  resolver: standardSchemaResolver(formSchema),
5763
5836
  defaultValues: {
5764
- ${p27.defaults}
5837
+ ${p30.defaults}
5765
5838
  },
5766
5839
  })
5767
5840
 
5768
- ${p27.fieldArraySetup}${p27.watchSetup}
5841
+ ${p30.fieldArraySetup}${p30.watchSetup}
5769
5842
  async function handleNext() {
5770
5843
  const stepFields = STEPS[currentStep].fields as (keyof FormValues)[]
5771
5844
  const isValid = await form.trigger(stepFields, { shouldFocus: true })
@@ -5781,9 +5854,9 @@ ${p27.fieldArraySetup}${p27.watchSetup}
5781
5854
  function onSubmit(values: FormValues) {
5782
5855
  startSubmitTransition(async () => {
5783
5856
  try {
5784
- const result = await create${p27.pascal}Submission(values)
5857
+ const result = await create${p30.pascal}Submission(values)
5785
5858
  if (result.success) {
5786
- ${p27.successHandler}
5859
+ ${p30.successHandler}
5787
5860
  } else {
5788
5861
  form.setError('root', { message: result.error || 'Something went wrong' })
5789
5862
  }
@@ -5797,7 +5870,7 @@ ${p27.fieldArraySetup}${p27.watchSetup}
5797
5870
  return (
5798
5871
  <div className="rounded-sm border p-6 text-center">
5799
5872
  <h3 className="text-lg font-semibold">Thank you!</h3>
5800
- <p className="mt-2 text-muted-foreground">${p27.successMessage}</p>
5873
+ <p className="mt-2 text-muted-foreground">${p30.successMessage}</p>
5801
5874
  </div>
5802
5875
  )
5803
5876
  }
@@ -5814,7 +5887,7 @@ ${p27.fieldArraySetup}${p27.watchSetup}
5814
5887
 
5815
5888
  {/* Step content */}
5816
5889
  <div key={currentStep} className="animate-in fade-in duration-300 space-y-6">
5817
- ${p27.stepContentBlocks}
5890
+ ${p30.stepContentBlocks}
5818
5891
  </div>
5819
5892
 
5820
5893
  {form.formState.errors.root && (
@@ -5848,7 +5921,7 @@ ${p27.stepContentBlocks}
5848
5921
  onClick={() => form.handleSubmit(onSubmit)()}
5849
5922
  className="inline-flex h-9 items-center justify-center rounded-md bg-primary px-4 py-2 text-sm font-medium text-primary-foreground shadow-xs transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50"
5850
5923
  >
5851
- {submitting ? 'Submitting...' : ${p27.submitText}}
5924
+ {submitting ? 'Submitting...' : ${p30.submitText}}
5852
5925
  </button>
5853
5926
  )}
5854
5927
  </div>
@@ -17672,7 +17745,7 @@ function readPresetTemplate(presetId, relativePath) {
17672
17745
  // adapters/next/snapshots/apply.ts
17673
17746
  import fs20 from "fs";
17674
17747
  import path24 from "path";
17675
- import * as p3 from "@clack/prompts";
17748
+ import * as p5 from "@clack/prompts";
17676
17749
 
17677
17750
  // core-engine/snapshots/ast-substitution.ts
17678
17751
  import { Node, Project } from "ts-morph";
@@ -18339,7 +18412,7 @@ function isInteractiveSession(interactive) {
18339
18412
  return interactive && Boolean(process.stdin.isTTY) && process.env.CI !== "true";
18340
18413
  }
18341
18414
  async function promptNoBaseDecision(filePath) {
18342
- const answer = await p3.select({
18415
+ const answer = await p5.select({
18343
18416
  message: `File already exists without snapshot base: ${filePath}`,
18344
18417
  options: [
18345
18418
  { value: "backup", label: "Backup and overwrite", hint: "recommended" },
@@ -18348,7 +18421,7 @@ async function promptNoBaseDecision(filePath) {
18348
18421
  ],
18349
18422
  initialValue: "backup"
18350
18423
  });
18351
- if (p3.isCancel(answer)) {
18424
+ if (p5.isCancel(answer)) {
18352
18425
  throw new Error("Generation cancelled.");
18353
18426
  }
18354
18427
  return answer;
@@ -19218,43 +19291,40 @@ function listAvailablePresets() {
19218
19291
 
19219
19292
  // adapters/next/commands/add.ts
19220
19293
  function printAddNextSteps(installKind, hasSchemaChanges, result, adminRoutePath) {
19221
- console.log("\n Next steps:");
19222
- console.log(` 1. Review the installed ${installKind} files`);
19223
- if (hasSchemaChanges && result.dbPush !== "success") {
19224
- console.log(" 2. Run database migration: db:push");
19225
- console.log(` 3. Start the dev server and review ${adminRoutePath}`);
19226
- return;
19227
- }
19228
- console.log(` 2. Start the dev server and review ${adminRoutePath}`);
19294
+ printNextSteps({
19295
+ reviewLabel: `the installed ${installKind} files`,
19296
+ needsMigration: hasSchemaChanges && result.dbPush !== "success",
19297
+ route: adminRoutePath
19298
+ });
19229
19299
  }
19230
19300
  async function runAddCommand(items, options) {
19231
19301
  const installIntegrationsMode = Boolean(options.integration);
19232
19302
  const presetIds = items.filter(isPresetId);
19233
19303
  const integrationIds = items.filter(isIntegrationId);
19234
19304
  if (!installIntegrationsMode && integrationIds.length > 0) {
19235
- p4.log.error(
19305
+ p6.log.error(
19236
19306
  `Integration IDs require --integration. Run \`betterstart add --integration ${integrationIds.join(" ")}\`.`
19237
19307
  );
19238
19308
  process.exit(1);
19239
19309
  }
19240
19310
  if (installIntegrationsMode && presetIds.length > 0) {
19241
- p4.log.error(`Preset IDs cannot be installed with --integration: ${presetIds.join(", ")}`);
19311
+ p6.log.error(`Preset IDs cannot be installed with --integration: ${presetIds.join(", ")}`);
19242
19312
  process.exit(1);
19243
19313
  }
19244
19314
  const invalidItems = installIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
19245
19315
  if (invalidItems.length > 0) {
19246
- p4.log.error(
19316
+ p6.log.error(
19247
19317
  installIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
19248
19318
  );
19249
19319
  process.exit(1);
19250
19320
  }
19251
19321
  const cwd = options.cwd ? path27.resolve(options.cwd) : process.cwd();
19252
- const config = await resolveConfig(cwd);
19322
+ const config = await resolveConfigOrExit(cwd);
19253
19323
  const adminRoutePath = resolveAdminNamespace(config.frameworkConfig.next.namespace).routePath;
19254
19324
  const pm = detectPackageManager(cwd);
19255
19325
  const cliSyncResult = await syncProjectCliDependency(cwd, pm);
19256
19326
  if (cliSyncResult && !cliSyncResult.success) {
19257
- p4.log.error(cliSyncResult.error ?? "Failed to sync betterstart-cli");
19327
+ p6.log.error(cliSyncResult.error ?? "Failed to sync betterstart-cli");
19258
19328
  process.exit(1);
19259
19329
  }
19260
19330
  if (installIntegrationsMode) {
@@ -19268,10 +19338,10 @@ async function runAddCommand(items, options) {
19268
19338
  });
19269
19339
  writeConfigFile(cwd, result2.config);
19270
19340
  if (result2.warnings.length > 0) {
19271
- p4.note(result2.warnings.join("\n"), "Warnings");
19341
+ p6.note(result2.warnings.join("\n"), "Warnings");
19272
19342
  }
19273
19343
  if (result2.installed.length === 0 && result2.activated.length === 0 && result2.skipped.length > 0) {
19274
- p4.outro(`No changes made. Already installed: ${result2.skipped.join(", ")}`);
19344
+ p6.outro(`No changes made. Already installed: ${result2.skipped.join(", ")}`);
19275
19345
  return;
19276
19346
  }
19277
19347
  const conflictPaths2 = scanConflictPaths(cwd);
@@ -19293,12 +19363,12 @@ async function runAddCommand(items, options) {
19293
19363
  result2.installed.length > 0 ? `Installed integration${result2.installed.length === 1 ? "" : "s"}: ${result2.installed.join(", ")}` : null,
19294
19364
  result2.activated.length > 0 ? `Activated integration${result2.activated.length === 1 ? "" : "s"}: ${result2.activated.join(", ")}` : null
19295
19365
  ].filter(Boolean);
19296
- p4.outro(messages.join("\n"));
19366
+ p6.outro(messages.join("\n"));
19297
19367
  return;
19298
19368
  }
19299
19369
  const invalidPresets = items.filter((presetId) => !isPresetId(presetId));
19300
19370
  if (invalidPresets.length > 0) {
19301
- p4.log.error(formatUnknownPresetMessage(invalidPresets));
19371
+ p6.log.error(formatUnknownPresetMessage(invalidPresets));
19302
19372
  process.exit(1);
19303
19373
  }
19304
19374
  const result = await installPresets({
@@ -19311,11 +19381,11 @@ async function runAddCommand(items, options) {
19311
19381
  });
19312
19382
  writeConfigFile(cwd, result.config);
19313
19383
  if (result.installed.length === 0 && result.skipped.length > 0) {
19314
- p4.outro(`No changes made. Already installed: ${result.skipped.join(", ")}`);
19384
+ p6.outro(`No changes made. Already installed: ${result.skipped.join(", ")}`);
19315
19385
  return;
19316
19386
  }
19317
19387
  if (result.warnings.length > 0) {
19318
- p4.note(result.warnings.join("\n"), "Warnings");
19388
+ p6.note(result.warnings.join("\n"), "Warnings");
19319
19389
  }
19320
19390
  const installedSchemaNames = result.installed.flatMap(
19321
19391
  (presetId) => getPresetDefinition(presetId).schemaFiles.map((schemaFile) => schemaFile.replace(/\.json$/, ""))
@@ -19332,14 +19402,14 @@ async function runAddCommand(items, options) {
19332
19402
  if (conflictPaths.length === 0) {
19333
19403
  printAddNextSteps("preset", hasSchemaChanges, postGenerateResult, adminRoutePath);
19334
19404
  }
19335
- p4.outro(
19405
+ p6.outro(
19336
19406
  `Installed preset${result.installed.length === 1 ? "" : "s"}: ${result.installed.join(", ")}`
19337
19407
  );
19338
19408
  }
19339
19409
 
19340
19410
  // adapters/next/commands/add-field.ts
19341
19411
  import path30 from "path";
19342
- import * as p8 from "@clack/prompts";
19412
+ import * as p10 from "@clack/prompts";
19343
19413
 
19344
19414
  // core-engine/schema/add-field.ts
19345
19415
  import fs23 from "fs";
@@ -19991,7 +20061,7 @@ function getTabFields(tab) {
19991
20061
  // adapters/next/commands/generate.ts
19992
20062
  import fs24 from "fs";
19993
20063
  import path29 from "path";
19994
- import * as p6 from "@clack/prompts";
20064
+ import * as p8 from "@clack/prompts";
19995
20065
 
19996
20066
  // core-engine/snapshots/rename-detector.ts
19997
20067
  function levenshtein(a, b) {
@@ -20085,7 +20155,7 @@ function diffSchemas(before, after) {
20085
20155
 
20086
20156
  // core-engine/utils/spinner.ts
20087
20157
  import { stripVTControlCharacters } from "util";
20088
- import * as p5 from "@clack/prompts";
20158
+ import * as p7 from "@clack/prompts";
20089
20159
  var RENDER_OVERHEAD = 7;
20090
20160
  var MIN_MESSAGE_WIDTH = 8;
20091
20161
  function fitSpinnerMessage(message) {
@@ -20132,7 +20202,7 @@ function hasActiveSpinner() {
20132
20202
  return activeSpinners > 0;
20133
20203
  }
20134
20204
  function spinner2(options) {
20135
- const inner = p5.spinner({ ...options, cancelMessage: "Setup cancelled." });
20205
+ const inner = p7.spinner({ ...options, cancelMessage: "Setup cancelled." });
20136
20206
  const guided = options?.withGuide !== false;
20137
20207
  let started = false;
20138
20208
  const setStarted = (next) => {
@@ -20251,9 +20321,7 @@ function assertSnapshotStateOrExit(cwd) {
20251
20321
  if (snapshotRootExists(cwd)) {
20252
20322
  return;
20253
20323
  }
20254
- console.error(
20255
- " Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
20256
- );
20324
+ p8.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
20257
20325
  process.exit(1);
20258
20326
  }
20259
20327
  function printSchemaDiff(scope, loaded, cwd) {
@@ -20266,35 +20334,36 @@ function printSchemaDiff(scope, loaded, cwd) {
20266
20334
  if (diff.added.length === 0 && diff.removed.length === 0 && diff.changed.length === 0 && diff.customComponents.added.length === 0 && diff.customComponents.removed.length === 0) {
20267
20335
  return;
20268
20336
  }
20269
- console.log(` Schema diff for ${scope}:`);
20337
+ const lines = [`Schema diff for ${scope}:`];
20270
20338
  if (diff.added.length > 0) {
20271
- console.log(` + fields: ${diff.added.map((field) => field.name).join(", ")}`);
20339
+ lines.push(` + fields: ${diff.added.map((field) => field.name).join(", ")}`);
20272
20340
  }
20273
20341
  if (diff.removed.length > 0) {
20274
- console.log(` - fields: ${diff.removed.map((field) => field.name).join(", ")}`);
20342
+ lines.push(` - fields: ${diff.removed.map((field) => field.name).join(", ")}`);
20275
20343
  }
20276
20344
  if (diff.changed.length > 0) {
20277
- console.log(
20278
- ` ~ fields: ${diff.changed.map((entry) => `${entry.before.name}:${entry.before.type}->${entry.after.type}`).join(", ")}`
20345
+ lines.push(
20346
+ ` ~ fields: ${diff.changed.map((entry) => `${entry.before.name}:${entry.before.type}->${entry.after.type}`).join(", ")}`
20279
20347
  );
20280
20348
  }
20281
20349
  if (renameCandidates.length > 0) {
20282
- console.log(
20283
- ` ? rename candidates: ${renameCandidates.map((entry) => `${entry.before}->${entry.after}`).join(", ")}`
20350
+ lines.push(
20351
+ ` ? rename candidates: ${renameCandidates.map((entry) => `${entry.before}->${entry.after}`).join(", ")}`
20284
20352
  );
20285
20353
  }
20286
20354
  if (diff.customComponents.added.length > 0 || diff.customComponents.removed.length > 0) {
20287
- console.log(
20288
- ` custom cells: +[${diff.customComponents.added.join(", ")}] -[${diff.customComponents.removed.join(", ")}]`
20355
+ lines.push(
20356
+ ` custom cells: +[${diff.customComponents.added.join(", ")}] -[${diff.customComponents.removed.join(", ")}]`
20289
20357
  );
20290
20358
  }
20359
+ p8.log.message(lines.join("\n"));
20291
20360
  }
20292
20361
  async function promptConfirm(message) {
20293
- const result = await p6.confirm({
20362
+ const result = await p8.confirm({
20294
20363
  message,
20295
20364
  initialValue: true
20296
20365
  });
20297
- if (p6.isCancel(result)) {
20366
+ if (p8.isCancel(result)) {
20298
20367
  throw new Error("Generation cancelled.");
20299
20368
  }
20300
20369
  return Boolean(result);
@@ -20358,9 +20427,9 @@ function collectRenamePreview(cwd, remotePaths, plan) {
20358
20427
  }
20359
20428
  for (const change of result.previewChanges) {
20360
20429
  previewLines.push(
20361
- ` ${change.path}:${change.line}
20362
- - ${change.before || "(empty)"}
20363
- + ${change.after || "(empty)"}`
20430
+ ` ${change.path}:${change.line}
20431
+ - ${change.before || "(empty)"}
20432
+ + ${change.after || "(empty)"}`
20364
20433
  );
20365
20434
  }
20366
20435
  }
@@ -20423,8 +20492,8 @@ async function maybeBuildRenamePlan(loaded, cwd, config, generatedFiles, options
20423
20492
  customCells: validatedCustomCells.renames
20424
20493
  };
20425
20494
  if (finalPlan.fields.length === 0 && finalPlan.customCells.length === 0) {
20426
- for (const warning of validatedCustomCells.warnings) {
20427
- console.warn(` ${warning}`);
20495
+ if (validatedCustomCells.warnings.length > 0) {
20496
+ p8.log.warn(validatedCustomCells.warnings.join("\n"));
20428
20497
  }
20429
20498
  return void 0;
20430
20499
  }
@@ -20433,19 +20502,19 @@ async function maybeBuildRenamePlan(loaded, cwd, config, generatedFiles, options
20433
20502
  generatedFiles.map((file) => file.path),
20434
20503
  finalPlan
20435
20504
  );
20436
- console.log(" Proposed rename substitutions:");
20437
- for (const line of formatRenameSelectionSummary(finalPlan)) {
20438
- console.log(` ${line}`);
20439
- }
20505
+ const renameLines = [
20506
+ "Proposed rename substitutions:",
20507
+ ...formatRenameSelectionSummary(finalPlan).map((line) => ` ${line}`)
20508
+ ];
20440
20509
  const previewLines = preview.previewLines.slice(0, 20);
20441
- for (const line of previewLines) {
20442
- console.log(line);
20443
- }
20510
+ renameLines.push(...previewLines);
20444
20511
  if (preview.previewLines.length > previewLines.length) {
20445
- console.log(` ... ${preview.previewLines.length - previewLines.length} more change(s)`);
20512
+ renameLines.push(` ... ${preview.previewLines.length - previewLines.length} more change(s)`);
20446
20513
  }
20447
- for (const warning of [...validatedCustomCells.warnings, ...preview.warnings]) {
20448
- console.warn(` ${warning}`);
20514
+ p8.log.message(renameLines.join("\n"));
20515
+ const warnings = [...validatedCustomCells.warnings, ...preview.warnings];
20516
+ if (warnings.length > 0) {
20517
+ p8.log.warn(warnings.join("\n"));
20449
20518
  }
20450
20519
  const confirmed = await promptConfirm("Apply these rename substitutions before merge?");
20451
20520
  return confirmed ? finalPlan : void 0;
@@ -20525,37 +20594,38 @@ async function applyBarrels(cwd, config, options) {
20525
20594
  });
20526
20595
  }
20527
20596
  function printApplySummary(schemaName, summary) {
20528
- console.log(` ${schemaName}:`);
20597
+ const lines = [`${schemaName}:`];
20529
20598
  if (summary.tombstoneCleared) {
20530
- console.log(` Tombstone cleared: ${summary.tombstoneCleared}`);
20599
+ lines.push(` Tombstone cleared: ${summary.tombstoneCleared}`);
20531
20600
  }
20532
20601
  if (summary.writtenFresh.length > 0) {
20533
- console.log(` written fresh: ${summary.writtenFresh.length}`);
20602
+ lines.push(` written fresh: ${summary.writtenFresh.length}`);
20534
20603
  }
20535
20604
  if (summary.overwritten.length > 0) {
20536
- console.log(` overwritten: ${summary.overwritten.length}`);
20605
+ lines.push(` overwritten: ${summary.overwritten.length}`);
20537
20606
  }
20538
20607
  if (summary.mergedCleanly.length > 0) {
20539
- console.log(` merged cleanly: ${summary.mergedCleanly.length}`);
20608
+ lines.push(` merged cleanly: ${summary.mergedCleanly.length}`);
20540
20609
  }
20541
20610
  if (summary.acceptedGenerated.length > 0) {
20542
- console.log(` accepted generated: ${summary.acceptedGenerated.length}`);
20611
+ lines.push(` accepted generated: ${summary.acceptedGenerated.length}`);
20543
20612
  }
20544
20613
  if (summary.conflictPaths.length > 0) {
20545
- console.log(` with conflicts: ${summary.conflictPaths.length}`);
20614
+ lines.push(` with conflicts: ${summary.conflictPaths.length}`);
20546
20615
  }
20547
20616
  if (summary.orphanedDeleted.length > 0) {
20548
- console.log(` orphaned deleted: ${summary.orphanedDeleted.length}`);
20617
+ lines.push(` orphaned deleted: ${summary.orphanedDeleted.length}`);
20549
20618
  }
20550
20619
  if (summary.orphanedKept.length > 0) {
20551
- console.log(` orphaned kept: ${summary.orphanedKept.length}`);
20620
+ lines.push(` orphaned kept: ${summary.orphanedKept.length}`);
20552
20621
  }
20553
20622
  if (summary.skipped.length > 0) {
20554
- console.log(` skipped: ${summary.skipped.join(", ")}`);
20623
+ lines.push(` skipped: ${summary.skipped.join(", ")}`);
20555
20624
  }
20556
20625
  if (summary.skippedCleared.length > 0) {
20557
- console.log(` skip entries cleared: ${summary.skippedCleared.join(", ")}`);
20626
+ lines.push(` skip entries cleared: ${summary.skippedCleared.join(", ")}`);
20558
20627
  }
20628
+ p8.log.message(lines.join("\n"));
20559
20629
  }
20560
20630
  async function runGenerateCommand(schemaName, options) {
20561
20631
  const cwd = options.cwd ? path29.resolve(options.cwd) : process.cwd();
@@ -20564,9 +20634,7 @@ async function runGenerateCommand(schemaName, options) {
20564
20634
  try {
20565
20635
  config = await resolveConfig(cwd);
20566
20636
  } catch (error) {
20567
- console.error(
20568
- ` Error loading config: ${error instanceof Error ? error.message : String(error)}`
20569
- );
20637
+ p8.log.error(`Error loading config: ${error instanceof Error ? error.message : String(error)}`);
20570
20638
  process.exit(1);
20571
20639
  }
20572
20640
  assertSnapshotStateOrExit(cwd);
@@ -20576,9 +20644,7 @@ async function runGenerateCommand(schemaName, options) {
20576
20644
  if (options.all) {
20577
20645
  const schemaNames = listSchemaNames(schemasDir);
20578
20646
  if (schemaNames.length === 0) {
20579
- console.error(`
20580
- No schemas found in ${schemasDir}
20581
- `);
20647
+ p8.log.error(`No schemas found in ${schemasDir}`);
20582
20648
  process.exit(1);
20583
20649
  }
20584
20650
  const failed = [];
@@ -20586,16 +20652,14 @@ async function runGenerateCommand(schemaName, options) {
20586
20652
  const processedRoutes = [];
20587
20653
  let markdownDepsChecked = false;
20588
20654
  const adminRoutePath2 = resolveAdminNamespace(config.frameworkConfig.next.namespace).routePath;
20589
- console.log(`
20590
- BetterStart Generator \u2014 regenerating ${schemaNames.length} schema(s)
20591
- `);
20655
+ p8.intro(`BetterStart Generator \u2014 regenerating ${schemaNames.length} schema(s)`);
20592
20656
  for (const name of schemaNames) {
20593
20657
  if (hasTombstone(cwd, name)) {
20594
20658
  if (loadManifest(cwd, name)) {
20595
20659
  clearTombstone(cwd, name);
20596
- console.warn(` - ${name}: tombstone cleared (manifest present)`);
20660
+ p8.log.warn(`${name}: tombstone cleared (manifest present)`);
20597
20661
  } else {
20598
- console.log(` - ${name}: skipped (tombstoned)`);
20662
+ p8.log.message(`${name}: skipped (tombstoned)`);
20599
20663
  continue;
20600
20664
  }
20601
20665
  }
@@ -20621,7 +20685,7 @@ async function runGenerateCommand(schemaName, options) {
20621
20685
  processed.push(name);
20622
20686
  processedRoutes.push(getGeneratedSchemaRoutePath(loaded2, adminRoutePath2));
20623
20687
  } catch (error) {
20624
- console.error(` \u2717 ${name}: ${error instanceof Error ? error.message : String(error)}`);
20688
+ p8.log.error(`${name}: ${error instanceof Error ? error.message : String(error)}`);
20625
20689
  failed.push(name);
20626
20690
  }
20627
20691
  }
@@ -20644,15 +20708,14 @@ async function runGenerateCommand(schemaName, options) {
20644
20708
  });
20645
20709
  }
20646
20710
  if (failed.length > 0) {
20647
- console.error(`
20648
- Failed: ${failed.join(", ")}`);
20711
+ p8.log.error(`Failed: ${failed.join(", ")}`);
20649
20712
  process.exit(1);
20650
20713
  }
20651
- console.log("");
20714
+ p8.outro(`Regenerated ${processed.length}/${schemaNames.length} schema(s)`);
20652
20715
  return;
20653
20716
  }
20654
20717
  if (!schemaName) {
20655
- console.error(" Error: schema name is required (or use --all)");
20718
+ p8.log.error("Error: schema name is required (or use --all)");
20656
20719
  process.exit(1);
20657
20720
  }
20658
20721
  let loaded;
@@ -20660,25 +20723,20 @@ async function runGenerateCommand(schemaName, options) {
20660
20723
  loaded = loadSchema(schemasDir, schemaName);
20661
20724
  } catch (error) {
20662
20725
  if (error instanceof SchemaNotFoundError) {
20663
- console.error(` ${error.message}`);
20726
+ p8.log.error(error.message);
20664
20727
  } else {
20665
- console.error(
20666
- ` Error loading schema: ${error instanceof Error ? error.message : String(error)}`
20667
- );
20728
+ p8.log.error(`Error loading schema: ${error instanceof Error ? error.message : String(error)}`);
20668
20729
  }
20669
20730
  process.exit(1);
20670
20731
  }
20671
20732
  const validationErrors = validateLoadedSchema(loaded);
20672
20733
  if (validationErrors.length > 0) {
20673
- console.error(" Schema validation failed:");
20674
- for (const error of validationErrors) {
20675
- console.error(` - ${error}`);
20676
- }
20734
+ p8.log.error(
20735
+ ["Schema validation failed:", ...validationErrors.map((error) => ` - ${error}`)].join("\n")
20736
+ );
20677
20737
  process.exit(1);
20678
20738
  }
20679
- console.log(`
20680
- BetterStart Generator \u2014 ${loaded.schema.name}
20681
- `);
20739
+ p8.intro(`BetterStart Generator \u2014 ${loaded.schema.name}`);
20682
20740
  const needsMarkdownRenderer = schemaNeedsMarkdownRenderer(loaded);
20683
20741
  await ensureMarkdownRendererDependencies(cwd, needsMarkdownRenderer);
20684
20742
  const result = await applySchemaGeneration(loaded, cwd, config, {
@@ -20703,38 +20761,38 @@ async function runGenerateCommand(schemaName, options) {
20703
20761
  adminRoutePath,
20704
20762
  schemaRoutePath: getGeneratedSchemaRoutePath(loaded, adminRoutePath)
20705
20763
  });
20706
- console.log("");
20764
+ p8.outro(`Generated ${loaded.schema.name}`);
20707
20765
  }
20708
20766
 
20709
20767
  // adapters/next/commands/schema-prompts.ts
20710
- import * as p7 from "@clack/prompts";
20768
+ import * as p9 from "@clack/prompts";
20711
20769
  function isInteractiveSession3() {
20712
20770
  return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
20713
20771
  }
20714
20772
  function fail(message) {
20715
- p7.log.error(message);
20773
+ p9.log.error(message);
20716
20774
  process.exit(1);
20717
20775
  }
20718
20776
  function cancel2(context) {
20719
- p7.cancel(context.cancelMessage);
20777
+ p9.cancel(context.cancelMessage);
20720
20778
  process.exit(0);
20721
20779
  }
20722
20780
  function cancelIfNeeded(context, value) {
20723
- if (p7.isCancel(value)) {
20781
+ if (p9.isCancel(value)) {
20724
20782
  cancel2(context);
20725
20783
  }
20726
20784
  return value;
20727
20785
  }
20728
20786
  async function promptConfirm2(context, message, initialValue = true) {
20729
- const confirmed = await p7.confirm({ message, initialValue });
20730
- if (p7.isCancel(confirmed)) {
20787
+ const confirmed = await p9.confirm({ message, initialValue });
20788
+ if (p9.isCancel(confirmed)) {
20731
20789
  cancel2(context);
20732
20790
  }
20733
20791
  return Boolean(confirmed);
20734
20792
  }
20735
20793
  async function promptText(context, message, optionsOrDefaultValue) {
20736
20794
  const options = typeof optionsOrDefaultValue === "string" ? { defaultValue: optionsOrDefaultValue } : optionsOrDefaultValue ?? {};
20737
- const value = await p7.text({
20795
+ const value = await p9.text({
20738
20796
  message,
20739
20797
  defaultValue: options.defaultValue,
20740
20798
  placeholder: options.placeholder,
@@ -20751,7 +20809,7 @@ async function promptText(context, message, optionsOrDefaultValue) {
20751
20809
  return String(cancelIfNeeded(context, value)).trim();
20752
20810
  }
20753
20811
  async function promptOptionalText(context, message) {
20754
- const value = await p7.text({
20812
+ const value = await p9.text({
20755
20813
  message,
20756
20814
  placeholder: "Leave blank to skip"
20757
20815
  });
@@ -20759,7 +20817,7 @@ async function promptOptionalText(context, message) {
20759
20817
  return result || void 0;
20760
20818
  }
20761
20819
  async function promptSelectValue(context, message, options, initialValue) {
20762
- const value = await p7.select({
20820
+ const value = await p9.select({
20763
20821
  message,
20764
20822
  options,
20765
20823
  initialValue
@@ -20978,7 +21036,7 @@ async function shouldPromptRequired(type) {
20978
21036
  return !["group", "section", "tabs", "separator"].includes(type);
20979
21037
  }
20980
21038
  async function promptFormFileOptions(context) {
20981
- const selected = await p7.multiselect({
21039
+ const selected = await p9.multiselect({
20982
21040
  message: "Field options",
20983
21041
  options: [
20984
21042
  { value: "required", label: "Required field?" },
@@ -21017,7 +21075,7 @@ async function promptFieldOptions(context, kind, type, creatable) {
21017
21075
  try {
21018
21076
  return parseInteractiveOptionsList(value);
21019
21077
  } catch (error) {
21020
- p7.log.error(error instanceof Error ? error.message : String(error));
21078
+ p9.log.error(error instanceof Error ? error.message : String(error));
21021
21079
  }
21022
21080
  }
21023
21081
  }
@@ -21135,7 +21193,7 @@ async function promptChildFields(context, kind, schemasDir, scope, fields, depth
21135
21193
  const addChild = fields.length === 0 ? await promptConfirm2(context, "Add a child field?", true) : await promptConfirm2(context, "Add another child field?", false);
21136
21194
  if (!addChild) {
21137
21195
  if (fields.length === 0) {
21138
- p7.log.warn("Containers need at least one child field.");
21196
+ p9.log.warn("Containers need at least one child field.");
21139
21197
  continue;
21140
21198
  }
21141
21199
  return;
@@ -21160,7 +21218,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
21160
21218
  const addTab = field.tabs.length === 0 ? await promptConfirm2(context, "Add a tab?", true) : await promptConfirm2(context, "Add another tab?", false);
21161
21219
  if (!addTab) {
21162
21220
  if (field.tabs.length === 0) {
21163
- p7.log.warn("Tabs need at least one tab.");
21221
+ p9.log.warn("Tabs need at least one tab.");
21164
21222
  continue;
21165
21223
  }
21166
21224
  return field;
@@ -21185,7 +21243,7 @@ async function promptTabsField(context, kind, schemasDir, scope, name, label, de
21185
21243
  const addChild = mainFields.length + sidebarFields.length === 0 ? await promptConfirm2(context, "Add a tab child field?", true) : await promptConfirm2(context, "Add another tab child field?", false);
21186
21244
  if (!addChild) {
21187
21245
  if (mainFields.length + sidebarFields.length === 0) {
21188
- p7.log.warn("Tabs need at least one child field.");
21246
+ p9.log.warn("Tabs need at least one child field.");
21189
21247
  continue;
21190
21248
  }
21191
21249
  break;
@@ -21256,7 +21314,7 @@ async function applyAdvancedOptions(context, field, kind, type, options) {
21256
21314
  if (Number.isInteger(parsed) && parsed > 0) {
21257
21315
  record.length = parsed;
21258
21316
  } else {
21259
- p7.log.warn("Skipped invalid length.");
21317
+ p9.log.warn("Skipped invalid length.");
21260
21318
  }
21261
21319
  }
21262
21320
  }
@@ -21414,11 +21472,11 @@ function hasNonInteractiveFieldOptions(options) {
21414
21472
  );
21415
21473
  }
21416
21474
  function fail2(message) {
21417
- p8.log.error(message);
21475
+ p10.log.error(message);
21418
21476
  process.exit(1);
21419
21477
  }
21420
21478
  function cancel4(message = "Add field cancelled.") {
21421
- p8.cancel(message);
21479
+ p10.cancel(message);
21422
21480
  process.exit(0);
21423
21481
  }
21424
21482
  function schemaNameFromLoaded(loaded) {
@@ -21523,7 +21581,7 @@ function printPreview(loaded, owner, firstTimeGeneration, insertion, options) {
21523
21581
  } else {
21524
21582
  lines.push("schema integration: none");
21525
21583
  }
21526
- p8.note(
21584
+ p10.note(
21527
21585
  `${lines.join("\n")}
21528
21586
 
21529
21587
  ${stringifyProjectJson(insertion.field).trim()}`,
@@ -21556,7 +21614,7 @@ Re-run with --yes to confirm these warning cases.`);
21556
21614
  }
21557
21615
  return;
21558
21616
  }
21559
- p8.note(warnings.join("\n"), "Warnings");
21617
+ p10.note(warnings.join("\n"), "Warnings");
21560
21618
  const confirmed = await promptConfirm3("Continue with these warnings?", true);
21561
21619
  if (!confirmed) {
21562
21620
  cancel4();
@@ -21617,11 +21675,9 @@ async function runAddFieldCommand(schemaName, options) {
21617
21675
  const cwd = options.cwd ? path30.resolve(options.cwd) : process.cwd();
21618
21676
  const nonInteractive = hasNonInteractiveFieldOptions(options);
21619
21677
  if (!snapshotRootExists(cwd)) {
21620
- fail2(
21621
- "Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
21622
- );
21678
+ fail2(UNSUPPORTED_PROJECT_STATE_MESSAGE);
21623
21679
  }
21624
- const config = await resolveConfig(cwd);
21680
+ const config = await resolveConfigOrExit(cwd);
21625
21681
  const paths = resolveProjectPaths(config);
21626
21682
  const schemasDir = path30.join(cwd, ...paths.schemasDir.split("/"));
21627
21683
  const selectedSchemaName = await resolveSchemaNameInteractively(schemasDir, schemaName);
@@ -21675,7 +21731,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
21675
21731
  }
21676
21732
  }
21677
21733
  writeAuthoredGeneratedSchema(loaded);
21678
- p8.log.success(`Updated ${path30.relative(cwd, loaded.filePath)}`);
21734
+ p10.log.success(`Updated ${path30.relative(cwd, loaded.filePath)}`);
21679
21735
  try {
21680
21736
  await runGenerateCommand(selectedSchemaName, {
21681
21737
  force: false,
@@ -21686,8 +21742,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
21686
21742
  cwd
21687
21743
  });
21688
21744
  } catch (error) {
21689
- p8.log.error(error instanceof Error ? error.message : String(error));
21690
- p8.log.info(
21745
+ p10.log.error(error instanceof Error ? error.message : String(error));
21746
+ p10.log.info(
21691
21747
  `Schema JSON was kept. Re-run \`betterstart generate ${selectedSchemaName}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
21692
21748
  );
21693
21749
  process.exit(1);
@@ -21697,7 +21753,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
21697
21753
  // adapters/next/commands/create.ts
21698
21754
  import fs25 from "fs";
21699
21755
  import path31 from "path";
21700
- import * as p9 from "@clack/prompts";
21756
+ import * as p11 from "@clack/prompts";
21701
21757
  var CREATE_PROMPT_CONTEXT = {
21702
21758
  cancelMessage: "Create schema cancelled."
21703
21759
  };
@@ -21888,7 +21944,7 @@ async function promptTabSlotFields(kind, schemasDir, titleField) {
21888
21944
  );
21889
21945
  if (!addField) {
21890
21946
  if (!hasFields) {
21891
- p9.log.warn("Tabs need at least one child field.");
21947
+ p11.log.warn("Tabs need at least one child field.");
21892
21948
  continue;
21893
21949
  }
21894
21950
  return { main, sidebar };
@@ -21936,7 +21992,7 @@ async function promptSchemaTab(kind, schemasDir, titleField, existingTabs) {
21936
21992
  while (fields.length === 0) {
21937
21993
  await promptAdditionalSchemaFields(kind, schemasDir, fields);
21938
21994
  if (fields.length === 0) {
21939
- p9.log.warn("Tabs need at least one child field.");
21995
+ p11.log.warn("Tabs need at least one child field.");
21940
21996
  }
21941
21997
  }
21942
21998
  }
@@ -22026,7 +22082,7 @@ async function promptFormSubmissionColumns(schema) {
22026
22082
  }
22027
22083
  const existingColumnNames = schema.columns ? new Set(schema.columns.map((column) => column.accessorKey)) : void 0;
22028
22084
  const initialValues = existingColumnNames ? fields.filter((field) => existingColumnNames.has(field.name)).map((field) => field.name) : fields.map((field) => field.name);
22029
- const selected = await p9.multiselect({
22085
+ const selected = await p11.multiselect({
22030
22086
  message: "Submission table columns",
22031
22087
  options: fields.map((field) => ({
22032
22088
  value: field.name,
@@ -22035,7 +22091,7 @@ async function promptFormSubmissionColumns(schema) {
22035
22091
  initialValues,
22036
22092
  required: false
22037
22093
  });
22038
- if (p9.isCancel(selected)) {
22094
+ if (p11.isCancel(selected)) {
22039
22095
  cancel2(CREATE_PROMPT_CONTEXT);
22040
22096
  }
22041
22097
  const selectedNames = new Set(selected);
@@ -22095,7 +22151,7 @@ function schemaFilePath(kind, schemasDir, schemaName) {
22095
22151
  return kind === "form" ? path31.join(schemasDir, "forms", `${schemaName}.json`) : path31.join(schemasDir, `${schemaName}.json`);
22096
22152
  }
22097
22153
  function printPreview2(loaded, cwd) {
22098
- p9.note(
22154
+ p11.note(
22099
22155
  `schema: ${loaded.name} (${loaded.kind})
22100
22156
  path: ${path31.relative(cwd, loaded.filePath)}
22101
22157
  owner: user
@@ -22108,14 +22164,12 @@ async function runCreateCommand(kindInput, schemaName, options) {
22108
22164
  const kind = normalizeKind(kindInput);
22109
22165
  const cwd = options.cwd ? path31.resolve(options.cwd) : process.cwd();
22110
22166
  if (!snapshotRootExists(cwd)) {
22111
- fail(
22112
- "Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
22113
- );
22167
+ fail(UNSUPPORTED_PROJECT_STATE_MESSAGE);
22114
22168
  }
22115
22169
  if (!isInteractiveSession3()) {
22116
22170
  fail("Interactive create requires a TTY.");
22117
22171
  }
22118
- const config = await resolveConfig(cwd);
22172
+ const config = await resolveConfigOrExit(cwd);
22119
22173
  const paths = resolveProjectPaths(config);
22120
22174
  const schemasDir = path31.join(cwd, ...paths.schemasDir.split("/"));
22121
22175
  const metadata = await promptMetadata(kind, schemaName, schemasDir);
@@ -22150,7 +22204,7 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
22150
22204
  }
22151
22205
  fs25.mkdirSync(path31.dirname(filePath), { recursive: true });
22152
22206
  writeAuthoredGeneratedSchema(loaded);
22153
- p9.log.success(`Created ${path31.relative(cwd, filePath)}`);
22207
+ p11.log.success(`Created ${path31.relative(cwd, filePath)}`);
22154
22208
  try {
22155
22209
  await runGenerateCommand(metadata.name, {
22156
22210
  force: false,
@@ -22161,8 +22215,8 @@ ${validationErrors.map((error) => ` - ${error}`).join("\n")}`
22161
22215
  cwd
22162
22216
  });
22163
22217
  } catch (error) {
22164
- p9.log.error(error instanceof Error ? error.message : String(error));
22165
- p9.log.info(
22218
+ p11.log.error(error instanceof Error ? error.message : String(error));
22219
+ p11.log.info(
22166
22220
  `Schema JSON was kept. Re-run \`betterstart generate ${metadata.name}${options.skipMigration ? " --skip-migration" : ""}\` after resolving the issue.`
22167
22221
  );
22168
22222
  process.exit(1);
@@ -22174,14 +22228,14 @@ import { execFileSync as execFileSync5, spawn as spawn6 } from "child_process";
22174
22228
  import fs41 from "fs";
22175
22229
  import path52 from "path";
22176
22230
  import { PassThrough } from "stream";
22177
- import * as p22 from "@clack/prompts";
22231
+ import * as p24 from "@clack/prompts";
22178
22232
 
22179
22233
  // core-engine/utils/cancel-guard.ts
22180
- import * as p10 from "@clack/prompts";
22234
+ import * as p12 from "@clack/prompts";
22181
22235
  function installSetupCancelGuard() {
22182
22236
  const onSignal = () => {
22183
22237
  if (!hasActiveSpinner()) {
22184
- p10.cancel("Setup cancelled.");
22238
+ p12.cancel("Setup cancelled.");
22185
22239
  }
22186
22240
  process.exit(0);
22187
22241
  };
@@ -22193,6 +22247,15 @@ function installSetupCancelGuard() {
22193
22247
  };
22194
22248
  }
22195
22249
 
22250
+ // core-engine/utils/json-mode.ts
22251
+ function redirectStdoutToStderr() {
22252
+ const originalWrite = process.stdout.write.bind(process.stdout);
22253
+ process.stdout.write = ((chunk, encodingOrCallback, callback) => typeof encodingOrCallback === "function" ? process.stderr.write(chunk, encodingOrCallback) : process.stderr.write(chunk, encodingOrCallback, callback));
22254
+ return () => {
22255
+ process.stdout.write = originalWrite;
22256
+ };
22257
+ }
22258
+
22196
22259
  // core-engine/utils/prompt-theme.ts
22197
22260
  var GREEN_STEP_SUBMIT = "\x1B[32m\u25C7\x1B[39m";
22198
22261
  var GREEN_CHECK = "\x1B[32m\u2713\x1B[39m";
@@ -22216,18 +22279,27 @@ function installPromptCheckmarks() {
22216
22279
  };
22217
22280
  }
22218
22281
 
22282
+ // core-engine/utils/redact.ts
22283
+ var REDACTED = "[redacted]";
22284
+ function redactSecrets(text7) {
22285
+ return text7.replace(/(\b[a-z][a-z0-9+.-]*:\/\/[^\s:@/]+:)[^\s@]+@/gi, `$1${REDACTED}@`).replace(/\b(bearer\s+)[a-z0-9._~+/=-]+/gi, `$1${REDACTED}`).replace(
22286
+ /(\b[\w-]*(?:token|secret|password|passwd|api[-_]?key|apikey)[\w-]*\s*[=:]\s*)(["']?)[^\s"']{4,}\2/gi,
22287
+ `$1$2${REDACTED}$2`
22288
+ );
22289
+ }
22290
+
22219
22291
  // adapters/next/init/prompts/database.ts
22220
22292
  import { execFileSync as execFileSync4 } from "child_process";
22221
- import * as p11 from "@clack/prompts";
22293
+ import * as p13 from "@clack/prompts";
22222
22294
  import pc from "picocolors";
22223
22295
  var VERCEL_NEON_URL = "https://vercel.com/dashboard/integrations/checkout/neon";
22224
22296
  async function promptServices() {
22225
- const choice = await p11.select({
22297
+ const choice = await p13.select({
22226
22298
  message: "Connect a PostgreSQL Database",
22227
22299
  options: [
22228
22300
  {
22229
22301
  value: "vercel",
22230
- label: "Vercel (Neon Postgress)",
22302
+ label: "Vercel (Neon Postgres)",
22231
22303
  hint: "Automatically provision a Neon Postgres database"
22232
22304
  },
22233
22305
  {
@@ -22243,8 +22315,8 @@ async function promptServices() {
22243
22315
  ],
22244
22316
  initialValue: "vercel"
22245
22317
  });
22246
- if (p11.isCancel(choice)) {
22247
- p11.cancel("Setup cancelled.");
22318
+ if (p13.isCancel(choice)) {
22319
+ p13.cancel("Setup cancelled.");
22248
22320
  process.exit(0);
22249
22321
  }
22250
22322
  if (choice === "vercel") {
@@ -22258,7 +22330,7 @@ async function promptServices() {
22258
22330
  }
22259
22331
  function openBrowserVercelNeon() {
22260
22332
  openBrowser(VERCEL_NEON_URL);
22261
- p11.log.info(
22333
+ p13.log.info(
22262
22334
  `Opening Vercel... Create a Neon Postgres database, then copy the ${pc.cyan("DATABASE_URL")} from the dashboard.`
22263
22335
  );
22264
22336
  }
@@ -22268,12 +22340,12 @@ function openBrowserVercelNeonResource(url) {
22268
22340
  return;
22269
22341
  }
22270
22342
  openBrowser(url);
22271
- p11.log.info(
22343
+ p13.log.info(
22272
22344
  `Opening Vercel... Copy the Neon ${pc.cyan("DATABASE_URL")} from the database dashboard, then paste it below.`
22273
22345
  );
22274
22346
  }
22275
22347
  async function promptConnectionString() {
22276
- const input = await p11.text({
22348
+ const input = await p13.text({
22277
22349
  message: "Paste your PostgreSQL connection string",
22278
22350
  placeholder: "postgres://user:pass@host/db",
22279
22351
  validate(val) {
@@ -22287,8 +22359,8 @@ async function promptConnectionString() {
22287
22359
  }
22288
22360
  }
22289
22361
  });
22290
- if (p11.isCancel(input)) {
22291
- p11.cancel("Setup cancelled.");
22362
+ if (p13.isCancel(input)) {
22363
+ p13.cancel("Setup cancelled.");
22292
22364
  process.exit(0);
22293
22365
  }
22294
22366
  return input.replace(/^['"]|['"]$/g, "").trim();
@@ -22308,8 +22380,7 @@ function openBrowser(url) {
22308
22380
  }
22309
22381
 
22310
22382
  // adapters/next/init/prompts/presets.ts
22311
- import { styleText } from "util";
22312
- import * as p12 from "@clack/prompts";
22383
+ import * as p14 from "@clack/prompts";
22313
22384
 
22314
22385
  // adapters/next/init/scaffolders/env.ts
22315
22386
  import crypto2 from "crypto";
@@ -22459,6 +22530,7 @@ function scaffoldEnv(cwd, options) {
22459
22530
  }
22460
22531
 
22461
22532
  // adapters/next/init/prompts/presets.ts
22533
+ import pc2 from "picocolors";
22462
22534
  function maskBlobToken(token) {
22463
22535
  const match = /^(vercel_blob_rw_[A-Za-z0-9]+)_/.exec(token);
22464
22536
  if (match) return `${match[1]}_***`;
@@ -22473,7 +22545,7 @@ async function promptPresets(cwd, options = {}) {
22473
22545
  overwriteKeys.add(key);
22474
22546
  }
22475
22547
  };
22476
- const storage = await p12.select({
22548
+ const storage = await p14.select({
22477
22549
  message: "Choose a file storage",
22478
22550
  options: [
22479
22551
  {
@@ -22492,13 +22564,13 @@ async function promptPresets(cwd, options = {}) {
22492
22564
  {
22493
22565
  value: "local",
22494
22566
  label: "Local filesystem (public/media)",
22495
- hint: "\u26A0 For testing only"
22567
+ hint: "\u25B2 For testing only"
22496
22568
  }
22497
22569
  ],
22498
22570
  initialValue: "vercel-blob"
22499
22571
  });
22500
- if (p12.isCancel(storage)) {
22501
- p12.cancel("Setup cancelled.");
22572
+ if (p14.isCancel(storage)) {
22573
+ p14.cancel("Setup cancelled.");
22502
22574
  process.exit(0);
22503
22575
  }
22504
22576
  if (storage === "r2") {
@@ -22508,7 +22580,7 @@ async function promptPresets(cwd, options = {}) {
22508
22580
  if (flow?.ok && flow.config) {
22509
22581
  mergeIntegrationConfig(flow.config);
22510
22582
  } else if (flow) {
22511
- p12.log.warn(
22583
+ p14.log.warn(
22512
22584
  "Continuing without Railway bucket credentials \u2014 rerun betterstart add --integration railway-bucket after fixing Railway access."
22513
22585
  );
22514
22586
  } else {
@@ -22525,7 +22597,7 @@ async function promptPresets(cwd, options = {}) {
22525
22597
  });
22526
22598
  overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
22527
22599
  } else if (flow) {
22528
- p12.log.warn(
22600
+ p14.log.warn(
22529
22601
  "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
22530
22602
  );
22531
22603
  sections.push({
@@ -22534,9 +22606,8 @@ async function promptPresets(cwd, options = {}) {
22534
22606
  });
22535
22607
  } else {
22536
22608
  if (existingToken) {
22537
- p12.log.info(
22538
- `Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${styleText(
22539
- "dim",
22609
+ p14.log.info(
22610
+ `Using the existing BLOB_READ_WRITE_TOKEN from .env.local ${pc2.dim(
22540
22611
  `(${maskBlobToken(existingToken)})`
22541
22612
  )}`
22542
22613
  );
@@ -22544,7 +22615,7 @@ async function promptPresets(cwd, options = {}) {
22544
22615
  mergeIntegrationConfig(await collectIntegrationConfig(cwd, ["vercel-blob"]));
22545
22616
  }
22546
22617
  }
22547
- const selectedPresets = await p12.multiselect({
22618
+ const selectedPresets = await p14.multiselect({
22548
22619
  message: "Select presets",
22549
22620
  options: [
22550
22621
  {
@@ -22556,8 +22627,8 @@ async function promptPresets(cwd, options = {}) {
22556
22627
  required: false,
22557
22628
  initialValues: ["blog"]
22558
22629
  });
22559
- if (p12.isCancel(selectedPresets)) {
22560
- p12.cancel("Setup cancelled.");
22630
+ if (p14.isCancel(selectedPresets)) {
22631
+ p14.cancel("Setup cancelled.");
22561
22632
  process.exit(0);
22562
22633
  }
22563
22634
  const integrations = [];
@@ -22584,9 +22655,9 @@ async function promptPresets(cwd, options = {}) {
22584
22655
  }
22585
22656
 
22586
22657
  // adapters/next/init/prompts/project.ts
22587
- import * as p13 from "@clack/prompts";
22658
+ import * as p15 from "@clack/prompts";
22588
22659
  async function promptProject(defaultName) {
22589
- const projectName = await p13.text({
22660
+ const projectName = await p15.text({
22590
22661
  message: "What is your project named?",
22591
22662
  placeholder: defaultName ?? "(Press Enter to use default)",
22592
22663
  defaultValue: defaultName ?? ".",
@@ -22599,15 +22670,15 @@ async function promptProject(defaultName) {
22599
22670
  return void 0;
22600
22671
  }
22601
22672
  });
22602
- if (p13.isCancel(projectName)) {
22603
- p13.cancel("Setup cancelled.");
22673
+ if (p15.isCancel(projectName)) {
22674
+ p15.cancel("Setup cancelled.");
22604
22675
  process.exit(0);
22605
22676
  }
22606
22677
  return { projectName: projectName.trim() || "." };
22607
22678
  }
22608
22679
 
22609
22680
  // adapters/next/init/providers.ts
22610
- import * as p14 from "@clack/prompts";
22681
+ import * as p16 from "@clack/prompts";
22611
22682
  var DATABASE_PROVIDERS = ["vercel", "railway", "manual"];
22612
22683
  var STORAGE_PROVIDERS = ["vercel-blob", "railway-bucket", "r2", "local"];
22613
22684
  var DEPLOY_PROVIDERS = ["vercel", "railway", "none"];
@@ -22664,7 +22735,7 @@ function validateResolvedDatabaseProvider(provider, options) {
22664
22735
  }
22665
22736
  }
22666
22737
  async function promptDeployProvider(initialValue = "none") {
22667
- const provider = await p14.select({
22738
+ const provider = await p16.select({
22668
22739
  message: "Deploy this project",
22669
22740
  options: [
22670
22741
  {
@@ -22682,8 +22753,8 @@ async function promptDeployProvider(initialValue = "none") {
22682
22753
  ],
22683
22754
  initialValue
22684
22755
  });
22685
- if (p14.isCancel(provider)) {
22686
- p14.cancel("Setup cancelled.");
22756
+ if (p16.isCancel(provider)) {
22757
+ p16.cancel("Setup cancelled.");
22687
22758
  process.exit(0);
22688
22759
  }
22689
22760
  return provider;
@@ -22692,7 +22763,7 @@ async function promptDeployProvider(initialValue = "none") {
22692
22763
  // adapters/next/init/railway/deploy.ts
22693
22764
  import fs28 from "fs";
22694
22765
  import path34 from "path";
22695
- import * as p16 from "@clack/prompts";
22766
+ import * as p18 from "@clack/prompts";
22696
22767
 
22697
22768
  // adapters/next/init/deploy/guard.ts
22698
22769
  import { spawn as spawn3 } from "child_process";
@@ -22789,7 +22860,7 @@ function guardBetterstartConfig(cwd, restores) {
22789
22860
  }
22790
22861
 
22791
22862
  // adapters/next/init/railway/project.ts
22792
- import * as p15 from "@clack/prompts";
22863
+ import * as p17 from "@clack/prompts";
22793
22864
 
22794
22865
  // adapters/next/init/railway/runner.ts
22795
22866
  import { spawn as spawn4 } from "child_process";
@@ -22930,7 +23001,7 @@ function railwayOutputTail(output, maxLines = 20) {
22930
23001
  }
22931
23002
 
22932
23003
  // adapters/next/init/railway/project.ts
22933
- import pc2 from "picocolors";
23004
+ import pc3 from "picocolors";
22934
23005
  function isString(value) {
22935
23006
  return typeof value === "string" && value.trim().length > 0;
22936
23007
  }
@@ -23073,22 +23144,22 @@ async function ensureRailwayProject(runner, options) {
23073
23144
  createWorkspace = options.workspaces[0]?.id;
23074
23145
  } else if (!createWorkspace && options.interactive && options.workspaces?.length) {
23075
23146
  projectSpinner.clear();
23076
- const workspace = await p15.select({
23147
+ const workspace = await p17.select({
23077
23148
  message: "Choose a Railway workspace",
23078
23149
  options: options.workspaces.map((candidate) => ({
23079
23150
  value: candidate.id,
23080
23151
  label: candidate.name
23081
23152
  }))
23082
23153
  });
23083
- if (p15.isCancel(workspace)) {
23084
- p15.cancel("Setup cancelled.");
23154
+ if (p17.isCancel(workspace)) {
23155
+ p17.cancel("Setup cancelled.");
23085
23156
  process.exit(0);
23086
23157
  }
23087
23158
  createWorkspace = workspace;
23088
23159
  projectSpinner.start("Checking Railway project names");
23089
23160
  }
23090
23161
  const projects = await listRailwayProjects(runner, options.cwd, options.env);
23091
- projectSpinner.message(`Creating Railway project ${pc2.cyan(options.projectName)}`);
23162
+ projectSpinner.message(`Creating Railway project ${pc3.cyan(options.projectName)}`);
23092
23163
  const created = await createRailwayProject(
23093
23164
  runner,
23094
23165
  options.cwd,
@@ -23368,7 +23439,7 @@ ${credentialsResult.stderr}`) ?? `Could not retrieve credentials for Railway buc
23368
23439
  }
23369
23440
 
23370
23441
  // adapters/next/init/railway/deploy.ts
23371
- import pc3 from "picocolors";
23442
+ import pc4 from "picocolors";
23372
23443
  var DEPLOY_TIMEOUT_MS = 9e5;
23373
23444
  var ENV_TIMEOUT_MS = 3e4;
23374
23445
  var ENV_SYNC_SKIP_KEYS = /* @__PURE__ */ new Set([
@@ -23607,7 +23678,7 @@ async function runRailwayDeployFlow(options) {
23607
23678
  );
23608
23679
  envSpinner.clear();
23609
23680
  for (const key of sync.failed) {
23610
- p16.log.warn(`Could not set ${pc3.cyan(key)} on Railway.`);
23681
+ p18.log.warn(`Could not set ${pc4.cyan(key)} on Railway.`);
23611
23682
  }
23612
23683
  if (sync.failed.length > 0) {
23613
23684
  return {
@@ -23622,13 +23693,13 @@ async function runRailwayDeployFlow(options) {
23622
23693
  const packageGuard = await guardProjectForDeploy(options.cwd);
23623
23694
  guardSpinner.clear();
23624
23695
  if (packageGuard.lockfile === "failed") {
23625
- p16.log.warn(
23696
+ p18.log.warn(
23626
23697
  "Could not refresh pnpm-lock.yaml after removing betterstart-cli \u2014 the remote install may fail."
23627
23698
  );
23628
23699
  }
23629
23700
  for (const dependency of packageGuard.localSpecDeps) {
23630
- p16.log.warn(
23631
- `Dependency ${pc3.cyan(dependency)} uses a local spec that cannot install on Railway.`
23701
+ p18.log.warn(
23702
+ `Dependency ${pc4.cyan(dependency)} uses a local spec that cannot install on Railway.`
23632
23703
  );
23633
23704
  }
23634
23705
  const deploySpinner = spinner2();
@@ -23662,7 +23733,7 @@ async function runRailwayDeployFlow(options) {
23662
23733
  packageGuard.restore();
23663
23734
  }
23664
23735
  if (!deploy.success) {
23665
- deploySpinner.stop(`${pc3.yellow("\u25B2")} Railway deploy did not complete`);
23736
+ deploySpinner.stop(`${pc4.yellow("\u25B2")} Railway deploy did not complete`);
23666
23737
  const logs = await readFailedDeploymentLogs(options.session, options.cwd, service);
23667
23738
  return {
23668
23739
  ok: false,
@@ -23672,7 +23743,7 @@ async function runRailwayDeployFlow(options) {
23672
23743
  ${deploy.stderr}`) ?? deploy.errorMessage
23673
23744
  };
23674
23745
  }
23675
- deploySpinner.stop(url ? `Deployed ${pc3.cyan(url)}` : "Deployed to Railway");
23746
+ deploySpinner.stop(url ? `Deployed ${pc4.cyan(url)}` : "Deployed to Railway");
23676
23747
  return { ok: true, url, service, syncedEnvKeys: sync.synced };
23677
23748
  } catch (error) {
23678
23749
  return { ok: false, detail: error instanceof Error ? error.message : String(error) };
@@ -23680,8 +23751,8 @@ ${deploy.stderr}`) ?? deploy.errorMessage
23680
23751
  }
23681
23752
 
23682
23753
  // adapters/next/init/railway/auth.ts
23683
- import * as p17 from "@clack/prompts";
23684
- import pc4 from "picocolors";
23754
+ import * as p19 from "@clack/prompts";
23755
+ import pc5 from "picocolors";
23685
23756
  var WHOAMI_TIMEOUT_MS = 3e4;
23686
23757
  var LOGIN_TIMEOUT_MS = 3e5;
23687
23758
  function isRailwayAccount(value) {
@@ -23732,7 +23803,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
23732
23803
  checkSpinner.clear();
23733
23804
  } else {
23734
23805
  checkSpinner.stop(
23735
- existing.account?.email ? `Signed in to Railway as ${pc4.cyan(existing.account.email)}` : "Signed in to Railway"
23806
+ existing.account?.email ? `Signed in to Railway as ${pc5.cyan(existing.account.email)}` : "Signed in to Railway"
23736
23807
  );
23737
23808
  }
23738
23809
  return existing;
@@ -23750,7 +23821,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
23750
23821
  return { authed: false, reason: existing.reason };
23751
23822
  }
23752
23823
  checkSpinner.clear();
23753
- p17.log.info(
23824
+ p19.log.info(
23754
23825
  "Sign in to Railway to continue. Railway will open your browser or show a device code."
23755
23826
  );
23756
23827
  const login = await runRailway(runner, ["login"], {
@@ -23767,7 +23838,7 @@ async function ensureRailwayAuth(runner, cwd, options) {
23767
23838
  const verified = await checkRailwayAuth(runner, cwd, env);
23768
23839
  if (verified.authed) {
23769
23840
  verifySpinner.stop(
23770
- verified.account?.email ? `Signed in to Railway as ${pc4.cyan(verified.account.email)}` : "Signed in to Railway"
23841
+ verified.account?.email ? `Signed in to Railway as ${pc5.cyan(verified.account.email)}` : "Signed in to Railway"
23771
23842
  );
23772
23843
  return verified;
23773
23844
  }
@@ -25637,12 +25708,12 @@ function scaffoldTsconfig(cwd, config) {
25637
25708
  }
25638
25709
 
25639
25710
  // adapters/next/init/vercel/flow.ts
25640
- import * as p21 from "@clack/prompts";
25641
- import pc8 from "picocolors";
25711
+ import * as p23 from "@clack/prompts";
25712
+ import pc9 from "picocolors";
25642
25713
 
25643
25714
  // adapters/next/init/vercel/auth.ts
25644
- import * as p18 from "@clack/prompts";
25645
- import pc5 from "picocolors";
25715
+ import * as p20 from "@clack/prompts";
25716
+ import pc6 from "picocolors";
25646
25717
 
25647
25718
  // adapters/next/init/vercel/runner.ts
25648
25719
  import { spawn as spawn5 } from "child_process";
@@ -25883,9 +25954,9 @@ async function ensureVercelAuth(runner, cwd, options) {
25883
25954
  return { authed: false, reason: existing.reason };
25884
25955
  }
25885
25956
  checkSpinner.clear();
25886
- const signInMessage = `Sign in to Vercel to continue ${pc5.dim("(or press Ctrl-C to enter a connection string manually)")}`;
25957
+ const signInMessage = `Sign in to Vercel to continue ${pc6.dim("(or press Ctrl-C to enter a connection string manually)")}`;
25887
25958
  const signInRows = clackLogRows(signInMessage);
25888
- p18.log.info(signInMessage);
25959
+ p20.log.info(signInMessage);
25889
25960
  let streamedRows = 0;
25890
25961
  const login = await runVercel(runner, ["login"], {
25891
25962
  cwd,
@@ -25913,12 +25984,12 @@ async function ensureVercelAuth(runner, cwd, options) {
25913
25984
  return { authed: false, reason: "login-failed" };
25914
25985
  }
25915
25986
  function signedInMessage(username) {
25916
- return username ? `Signed in to Vercel as ${pc5.cyan(username)}` : "Signed in to Vercel";
25987
+ return username ? `Signed in to Vercel as ${pc6.cyan(username)}` : "Signed in to Vercel";
25917
25988
  }
25918
25989
 
25919
25990
  // adapters/next/init/vercel/blob.ts
25920
- import * as p19 from "@clack/prompts";
25921
- import pc6 from "picocolors";
25991
+ import * as p21 from "@clack/prompts";
25992
+ import pc7 from "picocolors";
25922
25993
 
25923
25994
  // adapters/next/init/vercel/env-pull.ts
25924
25995
  import fs36 from "fs";
@@ -26159,8 +26230,8 @@ async function provisionBlobStoreInteractive(runner, cwd, options) {
26159
26230
  if (terminalFallbackRan) break;
26160
26231
  terminalFallbackRan = true;
26161
26232
  quietSpinner.clear();
26162
- p19.log.info(
26163
- `Create your Blob store in the Vercel prompts below ${pc6.dim(
26233
+ p21.log.info(
26234
+ `Create your Blob store in the Vercel prompts below ${pc7.dim(
26164
26235
  "(connect it to all environments)."
26165
26236
  )}`
26166
26237
  );
@@ -26358,8 +26429,8 @@ function guardEnvLocal(cwd) {
26358
26429
  }
26359
26430
 
26360
26431
  // adapters/next/init/vercel/neon.ts
26361
- import * as p20 from "@clack/prompts";
26362
- import pc7 from "picocolors";
26432
+ import * as p22 from "@clack/prompts";
26433
+ import pc8 from "picocolors";
26363
26434
  var PROVISION_TIMEOUT_MS2 = 18e4;
26364
26435
  var INTERACTIVE_PROVISION_TIMEOUT_MS2 = 6e5;
26365
26436
  var ACCEPT_TERMS_URL_PATTERN = /https:\/\/vercel\.com\/\S+\/integrations\/accept-terms\/\S+/i;
@@ -26377,7 +26448,7 @@ async function provisionNeonInteractive(runner, cwd, options) {
26377
26448
  termsResolved = true;
26378
26449
  quietSpinner.clear();
26379
26450
  eraseRows(termsNotice.rows);
26380
- p20.log.step(termsNotice.text);
26451
+ p22.log.step(termsNotice.text);
26381
26452
  };
26382
26453
  const quiet = await runVercel(runner, neonAddArgs(options), {
26383
26454
  cwd,
@@ -26392,11 +26463,11 @@ async function provisionNeonInteractive(runner, cwd, options) {
26392
26463
  const message = "Please accept the Neon terms in your browser";
26393
26464
  termsNotice = {
26394
26465
  text: `${message}
26395
- ${pc7.dim(termsUrl)}`,
26466
+ ${pc8.dim(termsUrl)}`,
26396
26467
  rows: clackLogRows(message) + clackLogRows(termsUrl) - 1
26397
26468
  };
26398
26469
  quietSpinner.clear();
26399
- p20.log.info(termsNotice.text);
26470
+ p22.log.info(termsNotice.text);
26400
26471
  quietSpinner.start("Waiting for the terms to be accepted");
26401
26472
  return;
26402
26473
  }
@@ -26412,8 +26483,8 @@ ${pc7.dim(termsUrl)}`,
26412
26483
  return readNeonProvisionInfo(quiet.stdout);
26413
26484
  }
26414
26485
  quietSpinner.clear();
26415
- p20.log.info(
26416
- `Create your Neon database in the Vercel prompts below ${pc7.dim(
26486
+ p22.log.info(
26487
+ `Create your Neon database in the Vercel prompts below ${pc8.dim(
26417
26488
  "(the Free plan is recommended)."
26418
26489
  )}`
26419
26490
  );
@@ -26489,14 +26560,14 @@ async function runVercelNeonFlow(options) {
26489
26560
  allowLogin: options.interactive
26490
26561
  });
26491
26562
  if (!auth.authed) {
26492
- p21.log.warn(authFailureMessage(auth.reason));
26563
+ p23.log.warn(authFailureMessage(auth.reason));
26493
26564
  return { ok: false };
26494
26565
  }
26495
26566
  await ensureLinkedProject(runner, options.cwd, options.projectName, env, auth.username);
26496
26567
  const neon = await provisionNeonForMode(runner, options);
26497
26568
  if (neon.failure) {
26498
- p21.log.warn(neonFailureMessage(neon.failure));
26499
- if (neon.detail) p21.log.message(pc8.dim(neon.detail));
26569
+ p23.log.warn(neonFailureMessage(neon.failure));
26570
+ if (neon.detail) p23.log.message(pc9.dim(redactSecrets(neon.detail)));
26500
26571
  return { ok: false };
26501
26572
  }
26502
26573
  const databaseUrl = await pullNeonDatabaseUrl(runner, options.cwd, env);
@@ -26509,7 +26580,7 @@ async function runVercelNeonFlow(options) {
26509
26580
  dismissSignedInNote
26510
26581
  };
26511
26582
  } catch (error) {
26512
- p21.log.warn(
26583
+ p23.log.warn(
26513
26584
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
26514
26585
  );
26515
26586
  return { ok: false };
@@ -26531,14 +26602,14 @@ async function runVercelBlobFlow(options) {
26531
26602
  allowLogin: options.interactive
26532
26603
  });
26533
26604
  if (!auth.authed) {
26534
- p21.log.warn(authFailureMessage(auth.reason));
26605
+ p23.log.warn(authFailureMessage(auth.reason));
26535
26606
  return { ok: false };
26536
26607
  }
26537
26608
  await ensureLinkedProject(runner, options.cwd, options.projectName, env, auth.username);
26538
26609
  const blob = await provisionBlobForMode(runner, options);
26539
26610
  if (blob.failure || !blob.token) {
26540
- p21.log.warn(blobFailureMessage(blob.failure));
26541
- if (blob.detail) p21.log.message(pc8.dim(blob.detail));
26611
+ p23.log.warn(blobFailureMessage(blob.failure));
26612
+ if (blob.detail) p23.log.message(pc9.dim(redactSecrets(blob.detail)));
26542
26613
  return { ok: false };
26543
26614
  }
26544
26615
  return {
@@ -26547,7 +26618,7 @@ async function runVercelBlobFlow(options) {
26547
26618
  blobStoreName: blob.storeName
26548
26619
  };
26549
26620
  } catch (error) {
26550
- p21.log.warn(
26621
+ p23.log.warn(
26551
26622
  `Vercel provisioning failed: ${error instanceof Error ? error.message : String(error)}`
26552
26623
  );
26553
26624
  return { ok: false };
@@ -26569,7 +26640,7 @@ async function runVercelDeployFlow(options) {
26569
26640
  allowLogin: options.interactive ?? true
26570
26641
  });
26571
26642
  if (!auth.authed) {
26572
- p21.log.warn(authFailureMessage(auth.reason));
26643
+ p23.log.warn(authFailureMessage(auth.reason));
26573
26644
  printManualDeployHint();
26574
26645
  return { ok: false };
26575
26646
  }
@@ -26579,8 +26650,8 @@ async function runVercelDeployFlow(options) {
26579
26650
  const sync = await syncVercelProductionEnv(runner, options.cwd, env);
26580
26651
  envSpinner.clear();
26581
26652
  for (const key of sync.failed) {
26582
- p21.log.warn(
26583
- `Could not set ${pc8.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
26653
+ p23.log.warn(
26654
+ `Could not set ${pc9.cyan(key)} on Vercel \u2014 add it in the project's environment settings.`
26584
26655
  );
26585
26656
  }
26586
26657
  ensureVercelJsonFramework(options.cwd);
@@ -26589,13 +26660,13 @@ async function runVercelDeployFlow(options) {
26589
26660
  const packageGuard = await guardProjectForDeploy(options.cwd);
26590
26661
  guardSpinner.clear();
26591
26662
  if (packageGuard.lockfile === "failed") {
26592
- p21.log.warn(
26663
+ p23.log.warn(
26593
26664
  "Could not refresh pnpm-lock.yaml after removing betterstart-cli \u2014 the remote install may fail."
26594
26665
  );
26595
26666
  }
26596
26667
  for (const dep of packageGuard.localSpecDeps) {
26597
- p21.log.warn(
26598
- `Dependency ${pc8.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
26668
+ p23.log.warn(
26669
+ `Dependency ${pc9.cyan(dep)} uses a local spec that cannot install on Vercel \u2014 the remote build may fail.`
26599
26670
  );
26600
26671
  }
26601
26672
  const deploySpinner = spinner2();
@@ -26610,16 +26681,16 @@ async function runVercelDeployFlow(options) {
26610
26681
  packageGuard.restore();
26611
26682
  }
26612
26683
  if (deploy.failure) {
26613
- deploySpinner.stop(`${pc8.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
26614
- if (deploy.detail) p21.log.message(pc8.dim(deploy.detail));
26684
+ deploySpinner.stop(`${pc9.yellow("\u25B2")} ${deployFailureMessage(deploy.failure)}`);
26685
+ if (deploy.detail) p23.log.message(pc9.dim(redactSecrets(deploy.detail)));
26615
26686
  printManualDeployHint();
26616
26687
  return { ok: false };
26617
26688
  }
26618
26689
  const url = deploy.url;
26619
- deploySpinner.stop(url ? `Deployed ${pc8.cyan(url)}` : "Deployed to Vercel");
26690
+ deploySpinner.stop(url ? `Deployed ${pc9.cyan(url)}` : "Deployed to Vercel");
26620
26691
  return { ok: true, url, syncedEnvKeys: sync.synced };
26621
26692
  } catch (error) {
26622
- p21.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
26693
+ p23.log.warn(`Vercel deploy failed: ${error instanceof Error ? error.message : String(error)}`);
26623
26694
  printManualDeployHint();
26624
26695
  return { ok: false };
26625
26696
  } finally {
@@ -26627,12 +26698,12 @@ async function runVercelDeployFlow(options) {
26627
26698
  }
26628
26699
  }
26629
26700
  function printManualDeployHint() {
26630
- p21.log.info(`You can deploy manually: ${pc8.cyan("vercel deploy --prod")}`);
26701
+ p23.log.info(`You can deploy manually: ${pc9.cyan("vercel deploy --prod")}`);
26631
26702
  }
26632
26703
  async function ensureLinkedProject(runner, cwd, projectName, env, scope) {
26633
26704
  if (readLinkedProjectId(cwd)) return;
26634
26705
  const projectSpinner = spinner2();
26635
- projectSpinner.start(`Creating a Vercel project ${pc8.cyan(projectName)}`);
26706
+ projectSpinner.start(`Creating a Vercel project ${pc9.cyan(projectName)}`);
26636
26707
  const project2 = await createVercelProject(runner, cwd, projectName, { env, scope });
26637
26708
  projectSpinner.clear();
26638
26709
  if (!project2.linked || !project2.projectId) {
@@ -26644,7 +26715,7 @@ async function pullNeonDatabaseUrl(runner, cwd, env) {
26644
26715
  neonSpinner.start("Retrieving DATABASE_URL from Vercel");
26645
26716
  const databaseUrl = await pullVercelEnvValue(runner, cwd, readDbUrlFromDotenv, env);
26646
26717
  if (!databaseUrl) {
26647
- neonSpinner.stop(`${pc8.yellow("\u25B2")} No DATABASE_URL came back from Vercel`);
26718
+ neonSpinner.stop(`${pc9.yellow("\u25B2")} No DATABASE_URL came back from Vercel`);
26648
26719
  return void 0;
26649
26720
  }
26650
26721
  neonSpinner.clear();
@@ -26704,12 +26775,19 @@ function neonFailureMessage(reason) {
26704
26775
  }
26705
26776
 
26706
26777
  // adapters/next/commands/init.ts
26707
- import pc9 from "picocolors";
26778
+ import pc10 from "picocolors";
26708
26779
 
26709
26780
  // adapters/next/commands/seed.ts
26710
26781
  import fs40 from "fs";
26711
26782
  import path51 from "path";
26712
- import * as clack from "@clack/prompts";
26783
+ import * as clack2 from "@clack/prompts";
26784
+
26785
+ // core-engine/utils/interactive.ts
26786
+ function isInteractiveSession4() {
26787
+ return Boolean(process.stdin.isTTY) && process.env.CI !== "true";
26788
+ }
26789
+
26790
+ // adapters/next/commands/seed.ts
26713
26791
  function buildSeedScript(authBasePath = "/api/admin/auth") {
26714
26792
  return `/**
26715
26793
  * BetterStart Admin \u2014 Seed Script
@@ -26882,44 +26960,80 @@ main().catch((err) => {
26882
26960
  }
26883
26961
  async function runSeedCommand(options) {
26884
26962
  const cwd = options.cwd ? path51.resolve(options.cwd) : process.cwd();
26885
- clack.intro("BetterStart Seed");
26963
+ clack2.intro("BetterStart Seed");
26886
26964
  let config;
26887
26965
  try {
26888
26966
  config = await resolveConfig(cwd);
26889
26967
  } catch (err) {
26890
- clack.cancel(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
26968
+ clack2.cancel(`Error loading config: ${err instanceof Error ? err.message : String(err)}`);
26891
26969
  process.exit(1);
26892
26970
  }
26893
26971
  const adminDir = config.paths?.admin ?? "./admin";
26894
- const email = await clack.text({
26895
- message: "Admin email",
26896
- placeholder: "admin@example.com",
26897
- validate: (v) => {
26898
- if (!v || !v.includes("@")) return "Please enter a valid email";
26972
+ const interactive = isInteractiveSession4();
26973
+ let email;
26974
+ if (options.email) {
26975
+ if (!options.email.includes("@")) {
26976
+ clack2.log.error(`Invalid email "${options.email}".`);
26977
+ process.exit(1);
26899
26978
  }
26900
- });
26901
- if (clack.isCancel(email)) {
26902
- clack.cancel("Cancelled.");
26903
- return;
26979
+ email = options.email;
26980
+ } else if (!interactive) {
26981
+ clack2.log.error("Provide --email to run seed without a terminal.");
26982
+ process.exit(1);
26983
+ } else {
26984
+ const emailInput = await clack2.text({
26985
+ message: "Admin email",
26986
+ placeholder: "admin@example.com",
26987
+ validate: (v) => {
26988
+ if (!v || !v.includes("@")) return "Please enter a valid email";
26989
+ }
26990
+ });
26991
+ if (clack2.isCancel(emailInput)) {
26992
+ clack2.cancel("Cancelled.");
26993
+ return;
26994
+ }
26995
+ email = emailInput;
26904
26996
  }
26905
- const password4 = await clack.password({
26906
- message: "Admin password",
26907
- validate: (v) => {
26908
- if (!v || v.length < 8) return "Password must be at least 8 characters";
26997
+ const envPassword = process.env.BETTERSTART_ADMIN_PASSWORD;
26998
+ let password4;
26999
+ if (envPassword) {
27000
+ if (envPassword.length < 8) {
27001
+ clack2.log.error("BETTERSTART_ADMIN_PASSWORD must be at least 8 characters.");
27002
+ process.exit(1);
26909
27003
  }
26910
- });
26911
- if (clack.isCancel(password4)) {
26912
- clack.cancel("Cancelled.");
26913
- return;
27004
+ password4 = envPassword;
27005
+ } else if (!interactive) {
27006
+ clack2.log.error("Set BETTERSTART_ADMIN_PASSWORD to run seed without a terminal.");
27007
+ process.exit(1);
27008
+ } else {
27009
+ const passwordInput = await clack2.password({
27010
+ message: "Admin password",
27011
+ validate: (v) => {
27012
+ if (!v || v.length < 8) return "Password must be at least 8 characters";
27013
+ }
27014
+ });
27015
+ if (clack2.isCancel(passwordInput)) {
27016
+ clack2.cancel("Cancelled.");
27017
+ return;
27018
+ }
27019
+ password4 = passwordInput;
26914
27020
  }
26915
- const name = await clack.text({
26916
- message: "Admin name",
26917
- placeholder: "Admin",
26918
- defaultValue: "Admin"
26919
- });
26920
- if (clack.isCancel(name)) {
26921
- clack.cancel("Cancelled.");
26922
- return;
27021
+ let name;
27022
+ if (options.name) {
27023
+ name = options.name;
27024
+ } else if (!interactive) {
27025
+ name = "Admin";
27026
+ } else {
27027
+ const nameInput = await clack2.text({
27028
+ message: "Admin name",
27029
+ placeholder: "Admin",
27030
+ defaultValue: "Admin"
27031
+ });
27032
+ if (clack2.isCancel(nameInput)) {
27033
+ clack2.cancel("Cancelled.");
27034
+ return;
27035
+ }
27036
+ name = nameInput;
26923
27037
  }
26924
27038
  const scriptsDir = path51.join(cwd, adminDir, "scripts");
26925
27039
  const seedPath = path51.join(scriptsDir, "seed.ts");
@@ -26962,11 +27076,21 @@ async function runSeedCommand(options) {
26962
27076
  if (result.code === 2) {
26963
27077
  const existingName = result.stdout.split("\n").find((l) => l.startsWith("EXISTING_USER:"))?.replace("EXISTING_USER:", "")?.trim() || "unknown";
26964
27078
  seedSpinner.stop(`Account already exists for ${email}`);
26965
- const overwrite = await clack.confirm({
27079
+ if (!interactive) {
27080
+ clack2.log.error(
27081
+ `An admin account (${existingName}) already exists for ${email}. Replacing it needs a terminal.`
27082
+ );
27083
+ try {
27084
+ fs40.unlinkSync(seedPath);
27085
+ } catch {
27086
+ }
27087
+ process.exit(1);
27088
+ }
27089
+ const overwrite = await clack2.confirm({
26966
27090
  message: `An admin account (${existingName}) already exists with this email. Replace it?`
26967
27091
  });
26968
- if (clack.isCancel(overwrite) || !overwrite) {
26969
- clack.cancel("Seed cancelled.");
27092
+ if (clack2.isCancel(overwrite) || !overwrite) {
27093
+ clack2.cancel("Seed cancelled.");
26970
27094
  try {
26971
27095
  fs40.unlinkSync(seedPath);
26972
27096
  } catch {
@@ -26982,12 +27106,12 @@ async function runSeedCommand(options) {
26982
27106
  } catch (err) {
26983
27107
  seedSpinner.stop("Failed to create admin user");
26984
27108
  const errMsg = err instanceof Error ? err.message : String(err);
26985
- clack.log.error(errMsg);
26986
- clack.log.info("You can run the seed script manually:");
26987
- clack.log.info(
27109
+ clack2.log.error(errMsg);
27110
+ clack2.log.info("You can run the seed script manually:");
27111
+ clack2.log.info(
26988
27112
  ` SEED_EMAIL="${email}" SEED_PASSWORD="..." npx tsx ${path51.relative(cwd, seedPath)}`
26989
27113
  );
26990
- clack.outro("");
27114
+ clack2.outro("");
26991
27115
  process.exit(1);
26992
27116
  }
26993
27117
  try {
@@ -26997,7 +27121,7 @@ async function runSeedCommand(options) {
26997
27121
  }
26998
27122
  } catch {
26999
27123
  }
27000
- clack.outro(`Admin user ready: ${email}`);
27124
+ clack2.outro(`Admin user ready: ${email}`);
27001
27125
  }
27002
27126
 
27003
27127
  // adapters/next/commands/init.ts
@@ -27010,16 +27134,16 @@ function addVersionToBoxBottomBorder(box2, version2) {
27010
27134
  if (!bottomLine) {
27011
27135
  return box2;
27012
27136
  }
27013
- const leftCornerIndex = bottomLine.indexOf(p22.S_CORNER_BOTTOM_LEFT);
27014
- const rightCornerIndex = bottomLine.lastIndexOf(p22.S_CORNER_BOTTOM_RIGHT);
27137
+ const leftCornerIndex = bottomLine.indexOf(p24.S_CORNER_BOTTOM_LEFT);
27138
+ const rightCornerIndex = bottomLine.lastIndexOf(p24.S_CORNER_BOTTOM_RIGHT);
27015
27139
  const label = ` v${version2} `;
27016
- const borderWidth = rightCornerIndex - leftCornerIndex - p22.S_CORNER_BOTTOM_LEFT.length;
27140
+ const borderWidth = rightCornerIndex - leftCornerIndex - p24.S_CORNER_BOTTOM_LEFT.length;
27017
27141
  if (leftCornerIndex === -1 || rightCornerIndex === -1 || borderWidth < label.length) {
27018
27142
  return box2;
27019
27143
  }
27020
27144
  const leftBorderWidth = Math.floor((borderWidth - label.length) / 2);
27021
27145
  const rightBorderWidth = borderWidth - label.length - leftBorderWidth;
27022
- lines[bottomLineIndex] = `${bottomLine.slice(0, leftCornerIndex + p22.S_CORNER_BOTTOM_LEFT.length)}${p22.S_BAR_H.repeat(leftBorderWidth)}${label}${p22.S_BAR_H.repeat(rightBorderWidth)}${bottomLine.slice(rightCornerIndex)}`;
27146
+ lines[bottomLineIndex] = `${bottomLine.slice(0, leftCornerIndex + p24.S_CORNER_BOTTOM_LEFT.length)}${p24.S_BAR_H.repeat(leftBorderWidth)}${label}${p24.S_BAR_H.repeat(rightBorderWidth)}${bottomLine.slice(rightCornerIndex)}`;
27023
27147
  return lines.join("\n");
27024
27148
  }
27025
27149
  function renderInitBanner() {
@@ -27029,7 +27153,7 @@ function renderInitBanner() {
27029
27153
  output.on("data", (chunk) => {
27030
27154
  box2 += chunk.toString();
27031
27155
  });
27032
- p22.box(
27156
+ p24.box(
27033
27157
  `
27034
27158
  \u2584 \u2597 \u2597 \u2584\u2596\u2597 \u2597
27035
27159
  \u2599\u2598\u2588\u258C\u259C\u2598\u259C\u2598\u2588\u258C\u259B\u2598\u259A \u259C\u2598\u2580\u258C\u259B\u2598\u259C\u2598
@@ -27122,6 +27246,14 @@ function removeExistingAdminPaths(cwd, namespaces) {
27122
27246
  return removed;
27123
27247
  }
27124
27248
  async function runInitCommand(name, options) {
27249
+ let restoreStdout;
27250
+ if (options.json) {
27251
+ if (!options.yes) {
27252
+ p24.log.error("--json requires --yes.");
27253
+ process.exit(1);
27254
+ }
27255
+ restoreStdout = redirectStdoutToStderr();
27256
+ }
27125
27257
  installPromptCheckmarks();
27126
27258
  const disposeCancelGuard = installSetupCancelGuard();
27127
27259
  renderInitBanner();
@@ -27138,7 +27270,7 @@ async function runInitCommand(name, options) {
27138
27270
  }
27139
27271
  } catch (error) {
27140
27272
  disposeCancelGuard();
27141
- p22.log.error(error instanceof Error ? error.message : String(error));
27273
+ p24.log.error(error instanceof Error ? error.message : String(error));
27142
27274
  process.exit(1);
27143
27275
  }
27144
27276
  let cwd = process.cwd();
@@ -27149,7 +27281,7 @@ async function runInitCommand(name, options) {
27149
27281
  try {
27150
27282
  namespace = validateAdminNamespace(options.namespace);
27151
27283
  } catch (error) {
27152
- p22.log.error(error instanceof Error ? error.message : String(error));
27284
+ p24.log.error(error instanceof Error ? error.message : String(error));
27153
27285
  process.exit(1);
27154
27286
  }
27155
27287
  }
@@ -27158,13 +27290,13 @@ async function runInitCommand(name, options) {
27158
27290
  try {
27159
27291
  projectPrompt = options.yes ? resolveNonInteractiveProject(name) : await promptProject(name);
27160
27292
  } catch (error) {
27161
- p22.log.error(error instanceof Error ? error.message : String(error));
27293
+ p24.log.error(error instanceof Error ? error.message : String(error));
27162
27294
  process.exit(1);
27163
27295
  }
27164
27296
  }
27165
27297
  if (!options.yes && !options.namespace) {
27166
27298
  const defaultDashboardPath = resolveAdminNamespace(DEFAULT_ADMIN_NAMESPACE).routePath;
27167
- const namespaceInput = await p22.text({
27299
+ const namespaceInput = await p24.text({
27168
27300
  message: "Enter the dashboard path",
27169
27301
  placeholder: `eg. ${defaultDashboardPath}`,
27170
27302
  defaultValue: defaultDashboardPath,
@@ -27177,8 +27309,8 @@ async function runInitCommand(name, options) {
27177
27309
  }
27178
27310
  }
27179
27311
  });
27180
- if (p22.isCancel(namespaceInput)) {
27181
- p22.cancel("Setup cancelled.");
27312
+ if (p24.isCancel(namespaceInput)) {
27313
+ p24.cancel("Setup cancelled.");
27182
27314
  process.exit(0);
27183
27315
  }
27184
27316
  namespace = validateAdminDashboardPath(namespaceInput);
@@ -27190,51 +27322,52 @@ async function runInitCommand(name, options) {
27190
27322
  if (project2.isExisting) {
27191
27323
  srcDir = project2.hasSrcDir;
27192
27324
  if (!project2.hasTypeScript) {
27193
- p22.log.error("TypeScript is required. Please add a tsconfig.json first.");
27325
+ p24.log.error("TypeScript is required. Please add a tsconfig.json first.");
27194
27326
  process.exit(1);
27195
27327
  }
27196
27328
  if (forceMode) {
27197
27329
  const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
27198
27330
  if (nuked > 0) {
27199
- p22.log.warn(`${pc9.yellow("Force mode:")} removed ${nuked} existing admin paths`);
27331
+ p24.log.warn(`${pc10.yellow("Force mode:")} removed ${nuked} existing admin paths`);
27200
27332
  }
27201
27333
  project2 = detectProject(cwd, namespace);
27202
27334
  } else if (project2.conflicts.length > 0) {
27203
- const conflictLines = project2.conflicts.map((c) => `${pc9.yellow("\u25B2")} ${c}`);
27335
+ const conflictLines = project2.conflicts.map((c) => `${pc10.yellow("\u25B2")} ${c}`);
27204
27336
  conflictLines.push(
27205
27337
  "",
27206
- pc9.dim(`Use ${pc9.bold("--force")} to remove existing admin files before scaffolding.`)
27338
+ pc10.dim(`Use ${pc10.bold("--force")} to remove existing admin files before scaffolding.`)
27207
27339
  );
27208
- p22.note(conflictLines.join("\n"), pc9.yellow("Conflicts"));
27209
- if (!options.yes) {
27210
- const proceed = await p22.confirm({
27211
- message: [
27212
- `Continue with ${pc9.bold(pc9.cyan("--force"))}?`,
27213
- `${pc9.cyan("\u2502")} ${pc9.dim("This will force overwrite the existing admin code.")}`,
27214
- pc9.cyan("\u2502")
27215
- ].join("\n"),
27216
- initialValue: true
27217
- });
27218
- if (p22.isCancel(proceed) || !proceed) {
27219
- p22.cancel("Setup cancelled.");
27220
- process.exit(0);
27221
- }
27222
- forceMode = true;
27223
- const nuked = removeExistingAdminPaths(
27224
- cwd,
27225
- await resolveForceInitNamespaces(cwd, namespace)
27340
+ p24.note(conflictLines.join("\n"), pc10.yellow("Conflicts"));
27341
+ if (options.yes) {
27342
+ p24.log.error(
27343
+ "Can't continue with --yes while admin files conflict. Re-run with --force to remove them first."
27226
27344
  );
27227
- if (nuked > 0) {
27228
- p22.log.warn(`${pc9.yellow("Force mode:")} removed ${nuked} existing admin paths`);
27229
- }
27230
- project2 = detectProject(cwd, namespace);
27345
+ process.exit(1);
27231
27346
  }
27347
+ const proceed = await p24.confirm({
27348
+ message: [
27349
+ `Continue with ${pc10.bold(pc10.cyan("--force"))}?`,
27350
+ `${pc10.cyan("\u2502")} ${pc10.dim("This will force overwrite the existing admin code.")}`,
27351
+ pc10.cyan("\u2502")
27352
+ ].join("\n"),
27353
+ initialValue: true
27354
+ });
27355
+ if (p24.isCancel(proceed) || !proceed) {
27356
+ p24.cancel("Setup cancelled.");
27357
+ process.exit(0);
27358
+ }
27359
+ forceMode = true;
27360
+ const nuked = removeExistingAdminPaths(cwd, await resolveForceInitNamespaces(cwd, namespace));
27361
+ if (nuked > 0) {
27362
+ p24.log.warn(`${pc10.yellow("Force mode:")} removed ${nuked} existing admin paths`);
27363
+ }
27364
+ project2 = detectProject(cwd, namespace);
27232
27365
  }
27233
27366
  } else {
27234
27367
  const freshProject = projectPrompt;
27235
27368
  srcDir = false;
27236
27369
  if (!options.yes) {
27237
- const pmChoice = await p22.select({
27370
+ const pmChoice = await p24.select({
27238
27371
  message: "Which package manager do you want to use?",
27239
27372
  options: [
27240
27373
  { value: "pnpm", label: "pnpm", hint: "recommended" },
@@ -27242,8 +27375,8 @@ async function runInitCommand(name, options) {
27242
27375
  { value: "bun", label: "bun" }
27243
27376
  ]
27244
27377
  });
27245
- if (p22.isCancel(pmChoice)) {
27246
- p22.cancel("Setup cancelled.");
27378
+ if (p24.isCancel(pmChoice)) {
27379
+ p24.cancel("Setup cancelled.");
27247
27380
  process.exit(0);
27248
27381
  }
27249
27382
  pm = pmChoice;
@@ -27278,11 +27411,11 @@ async function runInitCommand(name, options) {
27278
27411
  process.stderr.write(`${createNextAppResult.output.trimEnd()}
27279
27412
  `);
27280
27413
  }
27281
- p22.log.error(createNextAppResult.error);
27282
- p22.log.info(
27414
+ p24.log.error(createNextAppResult.error);
27415
+ p24.log.info(
27283
27416
  `You can create the project manually:
27284
- ${pc9.cyan(`npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`)}
27285
- Then run ${pc9.cyan("betterstart init")} inside it.`
27417
+ ${pc10.cyan(`npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`)}
27418
+ Then run ${pc10.cyan("betterstart init")} inside it.`
27286
27419
  );
27287
27420
  process.exit(1);
27288
27421
  }
@@ -27293,14 +27426,14 @@ async function runInitCommand(name, options) {
27293
27426
  );
27294
27427
  if (!hasPackageJson || !hasNextConfig) {
27295
27428
  createNextAppSpinner.stop(`Failed to create Next.js app: ${displayName}`);
27296
- p22.log.error(
27429
+ p24.log.error(
27297
27430
  "create-next-app completed but the project was not created. This can happen with nested npx calls."
27298
27431
  );
27299
27432
  const manualCmd = `npx create-next-app@latest ${freshProject.projectName} --typescript --tailwind --app`;
27300
- p22.log.info(
27433
+ p24.log.info(
27301
27434
  `Create the project manually:
27302
- ${pc9.cyan(manualCmd)}
27303
- Then run ${pc9.cyan("betterstart init")} inside it.`
27435
+ ${pc10.cyan(manualCmd)}
27436
+ Then run ${pc10.cyan("betterstart init")} inside it.`
27304
27437
  );
27305
27438
  process.exit(1);
27306
27439
  }
@@ -27346,22 +27479,22 @@ async function runInitCommand(name, options) {
27346
27479
  try {
27347
27480
  validateResolvedDatabaseProvider(databaseProvider, options);
27348
27481
  } catch (error) {
27349
- p22.log.error(error instanceof Error ? error.message : String(error));
27482
+ p24.log.error(error instanceof Error ? error.message : String(error));
27350
27483
  process.exit(1);
27351
27484
  }
27352
27485
  if (databaseProvider === "manual") {
27353
27486
  const candidate = options.databaseUrl ?? existingDbUrl ?? promptedManualUrl;
27354
27487
  if (candidate && !isValidDbUrl(candidate)) {
27355
- p22.log.error(
27356
- `Invalid database URL. Must start with ${pc9.cyan("postgres://")} or ${pc9.cyan("postgresql://")}`
27488
+ p24.log.error(
27489
+ `Invalid database URL. Must start with ${pc10.cyan("postgres://")} or ${pc10.cyan("postgresql://")}`
27357
27490
  );
27358
27491
  process.exit(1);
27359
27492
  }
27360
27493
  if (candidate) {
27361
27494
  databaseUrl = candidate;
27362
27495
  if (existingDbUrl === candidate && !options.databaseUrl) {
27363
- p22.log.info(
27364
- `Using the existing DATABASE_URL from .env.local ${pc9.dim(`(${maskDbUrl(candidate)})`)}`
27496
+ p24.log.info(
27497
+ `Using the existing DATABASE_URL from .env.local ${pc10.dim(`(${maskDbUrl(candidate)})`)}`
27365
27498
  );
27366
27499
  }
27367
27500
  } else if (!options.yes) {
@@ -27380,7 +27513,7 @@ async function runInitCommand(name, options) {
27380
27513
  persistDatabaseUrl(cwd, databaseUrl);
27381
27514
  dismissVercelSignedInNote = flow.dismissSignedInNote;
27382
27515
  } else if (options.yes) {
27383
- p22.log.error(
27516
+ p24.log.error(
27384
27517
  flow.ok ? "Created a Neon database, but DATABASE_URL could not be retrieved from Vercel." : "Vercel database provisioning did not complete."
27385
27518
  );
27386
27519
  process.exit(1);
@@ -27389,7 +27522,7 @@ async function runInitCommand(name, options) {
27389
27522
  databaseUrl = await promptConnectionString();
27390
27523
  persistDatabaseUrl(cwd, databaseUrl);
27391
27524
  } else {
27392
- p22.log.info("Falling back to a manual database connection string.");
27525
+ p24.log.info("Falling back to a manual database connection string.");
27393
27526
  openBrowserVercelNeon();
27394
27527
  databaseUrl = await promptConnectionString();
27395
27528
  }
@@ -27403,11 +27536,11 @@ async function runInitCommand(name, options) {
27403
27536
  } catch (error) {
27404
27537
  const message = error instanceof Error ? error.message : String(error);
27405
27538
  if (options.yes) {
27406
- p22.log.error(`Railway database provisioning failed: ${message}`);
27539
+ p24.log.error(`Railway database provisioning failed: ${message}`);
27407
27540
  process.exit(1);
27408
27541
  }
27409
- p22.log.warn(`Railway database provisioning failed: ${message}`);
27410
- p22.log.info("Falling back to a manual database connection string.");
27542
+ p24.log.warn(`Railway database provisioning failed: ${message}`);
27543
+ p24.log.info("Falling back to a manual database connection string.");
27411
27544
  databaseUrl = await promptConnectionString();
27412
27545
  }
27413
27546
  }
@@ -27437,7 +27570,7 @@ async function runInitCommand(name, options) {
27437
27570
  return { ok: true, config: config2 };
27438
27571
  } catch (error) {
27439
27572
  const message = error instanceof Error ? error.message : String(error);
27440
- p22.log.warn(`Railway bucket provisioning failed: ${message}`);
27573
+ p24.log.warn(`Railway bucket provisioning failed: ${message}`);
27441
27574
  return { ok: false };
27442
27575
  }
27443
27576
  };
@@ -27451,7 +27584,7 @@ async function runInitCommand(name, options) {
27451
27584
  if (storage === "r2") {
27452
27585
  const missingR2Keys = R2_ENV_KEYS.filter((key) => !readEnvVar(cwd, key)?.trim());
27453
27586
  if (options.yes && missingR2Keys.length > 0) {
27454
- p22.log.error(
27587
+ p24.log.error(
27455
27588
  `Cloudflare R2 is missing required environment variables: ${missingR2Keys.join(", ")}.`
27456
27589
  );
27457
27590
  process.exit(1);
@@ -27465,7 +27598,7 @@ async function runInitCommand(name, options) {
27465
27598
  if (flow.ok && flow.config) {
27466
27599
  mergeCollectedIntegrationConfig(flow.config);
27467
27600
  } else if (options.yes) {
27468
- p22.log.error("Railway bucket provisioning did not complete.");
27601
+ p24.log.error("Railway bucket provisioning did not complete.");
27469
27602
  process.exit(1);
27470
27603
  }
27471
27604
  }
@@ -27484,10 +27617,10 @@ async function runInitCommand(name, options) {
27484
27617
  });
27485
27618
  collectedIntegrationConfig.overwriteKeys.add("BLOB_READ_WRITE_TOKEN");
27486
27619
  } else if (options.yes) {
27487
- p22.log.error("Vercel Blob provisioning did not complete.");
27620
+ p24.log.error("Vercel Blob provisioning did not complete.");
27488
27621
  process.exit(1);
27489
27622
  } else {
27490
- p22.log.warn(
27623
+ p24.log.warn(
27491
27624
  "Continuing without a Blob token \u2014 add BLOB_READ_WRITE_TOKEN to .env.local from the store on vercel.com/dashboard/stores."
27492
27625
  );
27493
27626
  collectedIntegrationConfig.sections.push({
@@ -27561,7 +27694,7 @@ async function runInitCommand(name, options) {
27561
27694
  const nextConfigResult = scaffoldNextConfig({ cwd, nextMajorVersion });
27562
27695
  s.clear();
27563
27696
  if (nextConfigResult.status === "unsupported") {
27564
- p22.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
27697
+ p24.log.warn("The Next.js config could not be updated automatically \u2014 review it manually.");
27565
27698
  }
27566
27699
  const drizzleConfigPath = path52.join(cwd, "drizzle.config.ts");
27567
27700
  if (!dbFiles.includes("drizzle.config.ts") && fs41.existsSync(drizzleConfigPath)) {
@@ -27572,20 +27705,20 @@ async function runInitCommand(name, options) {
27572
27705
  readNamespacedTemplate("drizzle.config.ts", namespace),
27573
27706
  "utf-8"
27574
27707
  );
27575
- p22.log.success("Updated drizzle.config.ts");
27708
+ p24.log.success("Updated drizzle.config.ts");
27576
27709
  } else if (!options.yes) {
27577
- const overwrite = await p22.confirm({
27710
+ const overwrite = await p24.confirm({
27578
27711
  message: "drizzle.config.ts already exists. Overwrite with latest version?",
27579
27712
  initialValue: true
27580
27713
  });
27581
- if (!p22.isCancel(overwrite) && overwrite) {
27714
+ if (!p24.isCancel(overwrite) && overwrite) {
27582
27715
  const { readNamespacedTemplate } = await import("./template-reader-X6KFP7B3.js");
27583
27716
  fs41.writeFileSync(
27584
27717
  drizzleConfigPath,
27585
27718
  readNamespacedTemplate("drizzle.config.ts", namespace),
27586
27719
  "utf-8"
27587
27720
  );
27588
- p22.log.success("Updated drizzle.config.ts");
27721
+ p24.log.success("Updated drizzle.config.ts");
27589
27722
  }
27590
27723
  }
27591
27724
  }
@@ -27610,11 +27743,11 @@ async function runInitCommand(name, options) {
27610
27743
  s.stop("");
27611
27744
  } else {
27612
27745
  s.stop("Failed to install dependencies");
27613
- p22.log.warning(depsResult.error ?? "Unknown error");
27614
- p22.log.info(
27746
+ p24.log.warn(depsResult.error ?? "Unknown error");
27747
+ p24.log.info(
27615
27748
  `You can install them manually:
27616
- ${pc9.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
27617
- ${pc9.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
27749
+ ${pc10.cyan(`${pm} add ${depsResult.dependencies.join(" ")}`)}
27750
+ ${pc10.cyan(`${pm} add -D ${depsResult.devDeps.join(" ")}`)}`
27618
27751
  );
27619
27752
  disposeCancelGuard();
27620
27753
  process.exit(1);
@@ -27667,22 +27800,22 @@ async function runInitCommand(name, options) {
27667
27800
  writeConfigFile(cwd, resolvedIntegrationInstallResult.config);
27668
27801
  const usesLocalStorage = resolvedIntegrationInstallResult.config.storage.provider === "local";
27669
27802
  for (const err of coreSchemasResult.errors) {
27670
- p22.log.warn(`Core schemas: ${err}`);
27803
+ p24.log.warn(`Core schemas: ${err}`);
27671
27804
  }
27672
27805
  for (const warning of resolvedPresetInstallResult.warnings) {
27673
- p22.log.warn(warning);
27806
+ p24.log.warn(warning);
27674
27807
  }
27675
27808
  for (const warning of resolvedIntegrationInstallResult.warnings) {
27676
- p22.log.warn(warning);
27809
+ p24.log.warn(warning);
27677
27810
  }
27678
27811
  if (usesLocalStorage) {
27679
- p22.log.warn(
27812
+ p24.log.warn(
27680
27813
  "Local filesystem storage is only for development or self-hosted persistent-disk deployments. Use Vercel Blob, Railway Bucket, or Cloudflare R2 for hosted production."
27681
27814
  );
27682
27815
  }
27683
27816
  let dbPushed = false;
27684
27817
  if (depsResult.success && options.skipMigration && hasDbUrl(cwd)) {
27685
- p22.log.info(`Skipping database schema push ${pc9.dim("(--skip-migration)")}`);
27818
+ p24.log.info(`Skipping database schema push ${pc10.dim("(--skip-migration)")}`);
27686
27819
  } else if (depsResult.success && hasDbUrl(cwd)) {
27687
27820
  let driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
27688
27821
  if (!driverReady) {
@@ -27697,9 +27830,9 @@ async function runInitCommand(name, options) {
27697
27830
  });
27698
27831
  if (!driverResult.success) {
27699
27832
  s.stop("Database push failed");
27700
- p22.log.warning(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
27701
- p22.log.info(
27702
- `Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc9.cyan(drizzlePushCommand(pm))}`
27833
+ p24.log.warn(driverResult.error ?? `Failed to install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP}`);
27834
+ p24.log.info(
27835
+ `Install ${DRIZZLE_KIT_POSTGRES_DRIVER_DEP} manually, then run: ${pc10.cyan(drizzlePushCommand(pm))}`
27703
27836
  );
27704
27837
  } else {
27705
27838
  driverReady = hasDrizzleKitPostgresDriverDependency(cwd);
@@ -27726,8 +27859,8 @@ async function runInitCommand(name, options) {
27726
27859
  const verification = await verifyDatabaseReachable(cwd);
27727
27860
  clearDbSpinner();
27728
27861
  if (!verification.success) {
27729
- p22.log.warning(verification.error);
27730
- p22.log.error("Database was not reachable. Aborting setup.");
27862
+ p24.log.warn(verification.error);
27863
+ p24.log.error("Database was not reachable. Aborting setup.");
27731
27864
  process.exit(1);
27732
27865
  }
27733
27866
  } else {
@@ -27740,22 +27873,21 @@ async function runInitCommand(name, options) {
27740
27873
  s.stop("Database push failed");
27741
27874
  }
27742
27875
  const pushError = pushResult.error ?? "Unknown error";
27743
- p22.log.warning(pushError);
27876
+ p24.log.warn(pushError);
27744
27877
  if (isDatabaseReachabilityError(pushError)) {
27745
- p22.log.error("Database was not reachable. Aborting setup.");
27878
+ p24.log.error("Database was not reachable. Aborting setup.");
27746
27879
  process.exit(1);
27747
27880
  }
27748
- p22.log.info(`You can run it manually: ${pc9.cyan(drizzlePushCommand(pm))}`);
27881
+ p24.log.info(`You can run it manually: ${pc10.cyan(drizzlePushCommand(pm))}`);
27749
27882
  }
27750
27883
  }
27751
27884
  }
27752
27885
  let seedEmail;
27753
- let seedPassword;
27754
27886
  let seedSuccess = false;
27755
27887
  let adminAccountReady = false;
27756
27888
  if (dbPushed && options.skipAdminCreation) {
27757
- p22.log.info(
27758
- `Skipping admin user creation ${pc9.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
27889
+ p24.log.info(
27890
+ `Skipping admin user creation ${pc10.dim(`(use ${betterstartExecCommand(pm, "seed")} later)`)}`
27759
27891
  );
27760
27892
  }
27761
27893
  if (dbPushed && !options.yes && !options.skipAdminCreation) {
@@ -27766,14 +27898,14 @@ async function runInitCommand(name, options) {
27766
27898
  const adminCheck = await checkExistingAdmin(cwd, adminDir, authBasePath);
27767
27899
  s.clear();
27768
27900
  if (adminCheck.error) {
27769
- p22.log.warning(`Could not verify existing admin account ${pc9.dim(`(${adminCheck.error})`)}`);
27901
+ p24.log.warn(`Could not verify existing admin account ${pc10.dim(`(${adminCheck.error})`)}`);
27770
27902
  if (isDatabaseReachabilityError(adminCheck.error)) {
27771
- p22.log.error("Database was not reachable. Aborting setup.");
27903
+ p24.log.error("Database was not reachable. Aborting setup.");
27772
27904
  process.exit(1);
27773
27905
  }
27774
27906
  } else if (adminCheck.existingAdmin) {
27775
27907
  const existingAdminLabel = formatAdminIdentity(adminCheck.existingAdmin);
27776
- const adminAction = await p22.select({
27908
+ const adminAction = await p24.select({
27777
27909
  message: "Found an already existing admin account. Do you want to replace it or skip?",
27778
27910
  options: [
27779
27911
  {
@@ -27787,28 +27919,28 @@ async function runInitCommand(name, options) {
27787
27919
  }
27788
27920
  ]
27789
27921
  });
27790
- if (p22.isCancel(adminAction)) {
27791
- p22.cancel("Setup cancelled.");
27922
+ if (p24.isCancel(adminAction)) {
27923
+ p24.cancel("Setup cancelled.");
27792
27924
  process.exit(0);
27793
27925
  }
27794
27926
  if (adminAction === "skip") {
27795
27927
  adminAccountReady = true;
27796
- p22.log.info(`Keeping existing admin account ${pc9.dim(`(${existingAdminLabel})`)}`);
27928
+ p24.log.info(`Keeping existing admin account ${pc10.dim(`(${existingAdminLabel})`)}`);
27797
27929
  } else {
27798
27930
  replaceExistingAdmin = true;
27799
27931
  }
27800
27932
  }
27801
27933
  if (!adminAccountReady) {
27802
- const credentials = await p22.group(
27934
+ const credentials = await p24.group(
27803
27935
  {
27804
- email: () => p22.text({
27936
+ email: () => p24.text({
27805
27937
  message: "Admin email",
27806
27938
  placeholder: "admin@example.com",
27807
27939
  validate: (v) => {
27808
27940
  if (!v || !v.includes("@")) return "Please enter a valid email";
27809
27941
  }
27810
27942
  }),
27811
- password: () => p22.password({
27943
+ password: () => p24.password({
27812
27944
  message: "Admin password",
27813
27945
  validate: (v) => {
27814
27946
  if (!v || v.length < 8) return "Password must be at least 8 characters";
@@ -27817,14 +27949,13 @@ async function runInitCommand(name, options) {
27817
27949
  },
27818
27950
  {
27819
27951
  onCancel: () => {
27820
- p22.cancel("Setup cancelled.");
27952
+ p24.cancel("Setup cancelled.");
27821
27953
  process.exit(0);
27822
27954
  }
27823
27955
  }
27824
27956
  );
27825
27957
  seedEmail = credentials.email;
27826
- seedPassword = credentials.password;
27827
- const credentialPromptRows = clackPromptRows("Admin email", credentials.email) + clackPromptRows("Admin password", p22.S_PASSWORD_MASK.repeat(credentials.password.length));
27958
+ const credentialPromptRows = clackPromptRows("Admin email", credentials.email) + clackPromptRows("Admin password", p24.S_PASSWORD_MASK.repeat(credentials.password.length));
27828
27959
  let rowsBelowCredentialPrompts = 0;
27829
27960
  let seedOverwriteMode = replaceExistingAdmin ? "admin" : void 0;
27830
27961
  s.start(replaceExistingAdmin ? "Replacing admin user" : "Creating admin user");
@@ -27837,11 +27968,11 @@ async function runInitCommand(name, options) {
27837
27968
  seedOverwriteMode
27838
27969
  );
27839
27970
  if (seedResult.existingUser) {
27840
- const existingAccountMessage = `${pc9.yellow("\u25B2")} An account already exists for ${credentials.email}`;
27971
+ const existingAccountMessage = `${pc10.yellow("\u25B2")} An account already exists for ${credentials.email}`;
27841
27972
  s.stop(existingAccountMessage);
27842
27973
  rowsBelowCredentialPrompts += clackLogRows(existingAccountMessage);
27843
27974
  const replaceAccountMessage = "Replace the existing account with this email?";
27844
- const replace = await p22.confirm({
27975
+ const replace = await p24.confirm({
27845
27976
  message: replaceAccountMessage,
27846
27977
  initialValue: false
27847
27978
  });
@@ -27849,7 +27980,7 @@ async function runInitCommand(name, options) {
27849
27980
  replaceAccountMessage,
27850
27981
  replace === true ? "Yes" : "No"
27851
27982
  );
27852
- if (!p22.isCancel(replace) && replace) {
27983
+ if (!p24.isCancel(replace) && replace) {
27853
27984
  seedOverwriteMode = "email";
27854
27985
  s.start("Replacing admin user");
27855
27986
  seedResult = await runSeed(
@@ -27863,7 +27994,7 @@ async function runInitCommand(name, options) {
27863
27994
  }
27864
27995
  }
27865
27996
  if (seedResult.success) {
27866
- const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${pc9.green(seedEmail)} created`;
27997
+ const seedSuccessMessage = seedOverwriteMode === "admin" ? `Admin user replaced` : `Admin user ${pc10.green(seedEmail)} created`;
27867
27998
  s.stop(seedSuccessMessage);
27868
27999
  rowsBelowCredentialPrompts += clackLogRows(seedSuccessMessage);
27869
28000
  eraseRowsAbove(credentialPromptRows, rowsBelowCredentialPrompts);
@@ -27871,14 +28002,14 @@ async function runInitCommand(name, options) {
27871
28002
  adminAccountReady = true;
27872
28003
  } else if (seedResult.error) {
27873
28004
  s.stop(`Failed to create admin user`);
27874
- p22.note(
27875
- `${pc9.red(seedResult.error)}
28005
+ p24.note(
28006
+ `${pc10.red(seedResult.error)}
27876
28007
 
27877
- Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
27878
- pc9.red("Seed failed")
28008
+ Run manually: ${pc10.cyan(betterstartExecCommand(pm, "seed"))}`,
28009
+ pc10.red("Seed failed")
27879
28010
  );
27880
28011
  if (isDatabaseReachabilityError(seedResult.error)) {
27881
- p22.log.error("Database was not reachable. Aborting setup.");
28012
+ p24.log.error("Database was not reachable. Aborting setup.");
27882
28013
  process.exit(1);
27883
28014
  }
27884
28015
  }
@@ -27909,6 +28040,7 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
27909
28040
  const initialDeployProvider = railwaySessionPromise ? "railway" : readLinkedProjectId(cwd) ? "vercel" : "none";
27910
28041
  deployProvider = await promptDeployProvider(initialDeployProvider);
27911
28042
  }
28043
+ let deployedUrl;
27912
28044
  if (deployProvider === "vercel") {
27913
28045
  const deployFlow = await runVercelDeployFlow({
27914
28046
  cwd,
@@ -27916,12 +28048,13 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
27916
28048
  interactive: !options.yes,
27917
28049
  env: process.env
27918
28050
  });
28051
+ deployedUrl = deployFlow.url;
27919
28052
  if (!deployFlow.ok) {
27920
28053
  if (options.yes) {
27921
- p22.log.error("Vercel deploy did not complete.");
28054
+ p24.log.error("Vercel deploy did not complete.");
27922
28055
  process.exit(1);
27923
28056
  }
27924
- p22.log.warn("Vercel deploy did not complete; continuing.");
28057
+ p24.log.warn("Vercel deploy did not complete; continuing.");
27925
28058
  }
27926
28059
  } else if (deployProvider === "railway") {
27927
28060
  try {
@@ -27937,40 +28070,65 @@ Run manually: ${pc9.cyan(betterstartExecCommand(pm, "seed"))}`,
27937
28070
  bucketUrlStyle: railwayBucketUrlStyle
27938
28071
  })
27939
28072
  });
28073
+ deployedUrl = deployFlow.url;
27940
28074
  if (!deployFlow.ok) {
27941
- if (deployFlow.detail) p22.log.message(pc9.dim(deployFlow.detail));
28075
+ if (deployFlow.detail) p24.log.message(pc10.dim(redactSecrets(deployFlow.detail)));
27942
28076
  if (options.yes) {
27943
- p22.log.error("Railway deploy did not complete.");
28077
+ p24.log.error("Railway deploy did not complete.");
27944
28078
  process.exit(1);
27945
28079
  }
27946
- p22.log.warn("Railway deploy did not complete; continuing.");
28080
+ p24.log.warn("Railway deploy did not complete; continuing.");
27947
28081
  }
27948
28082
  } catch (error) {
27949
28083
  const message = error instanceof Error ? error.message : String(error);
27950
28084
  if (options.yes) {
27951
- p22.log.error(`Railway deploy failed: ${message}`);
28085
+ p24.log.error(`Railway deploy failed: ${message}`);
27952
28086
  process.exit(1);
27953
28087
  }
27954
- p22.log.warn(`Railway deploy failed: ${message}`);
28088
+ p24.log.warn(`Railway deploy failed: ${message}`);
27955
28089
  }
27956
28090
  }
27957
28091
  if (!options.yes && !options.skipDevServerStart) {
27958
28092
  const devCmd = runCommand(pm, "dev");
27959
- const startDev = await p22.confirm({
28093
+ const startDev = await p24.confirm({
27960
28094
  message: "Start the development server?",
27961
28095
  initialValue: true
27962
28096
  });
27963
- if (!p22.isCancel(startDev) && startDev) {
28097
+ if (!p24.isCancel(startDev) && startDev) {
27964
28098
  disposeCancelGuard();
27965
- await startManagedDevServer(cwd, devCmd, adminLoginUrl, {
27966
- email: seedSuccess && seedEmail ? seedEmail : void 0,
27967
- password: seedSuccess && seedPassword ? seedPassword : void 0
27968
- });
28099
+ await startManagedDevServer(
28100
+ cwd,
28101
+ devCmd,
28102
+ adminLoginUrl,
28103
+ seedSuccess && seedEmail ? seedEmail : void 0
28104
+ );
27969
28105
  return;
27970
28106
  }
27971
28107
  }
27972
28108
  disposeCancelGuard();
27973
- p22.outro("Done!");
28109
+ if (options.json && restoreStdout) {
28110
+ restoreStdout();
28111
+ console.log(
28112
+ JSON.stringify(
28113
+ {
28114
+ namespace,
28115
+ configPath: CONFIG_FILE_NAME,
28116
+ databaseProvider,
28117
+ storageProvider: presetSelection.storage,
28118
+ deployProvider,
28119
+ presets: presetSelection.presets,
28120
+ integrations: presetSelection.integrations,
28121
+ adminUrl: adminLoginUrl,
28122
+ adminEmail: seedEmail ?? null,
28123
+ deployedUrl: deployedUrl ?? null
28124
+ },
28125
+ null,
28126
+ 2
28127
+ )
28128
+ );
28129
+ return;
28130
+ }
28131
+ p24.outro(`Admin ready at ${adminNamespace.routePath}`);
27974
28132
  }
27975
28133
  function isValidDbUrl(url) {
27976
28134
  return url.startsWith("postgres://") || url.startsWith("postgresql://");
@@ -28356,12 +28514,11 @@ function stripAnsi2(value) {
28356
28514
  return value.replace(ANSI_ESCAPE_PATTERN2, "");
28357
28515
  }
28358
28516
  function printAdminReadyNote(state) {
28359
- const lines = [`Admin: ${pc9.cyan(state.adminLoginUrl)}`];
28360
- if (state.adminEmail && state.adminPassword) {
28361
- lines.unshift(`Password: ${pc9.cyan(state.adminPassword)}`);
28362
- lines.unshift(`Admin user: ${pc9.cyan(state.adminEmail)}`);
28517
+ const lines = [`Admin: ${pc10.cyan(state.adminLoginUrl)}`];
28518
+ if (state.adminEmail) {
28519
+ lines.unshift(`Admin user: ${pc10.cyan(state.adminEmail)}`);
28363
28520
  }
28364
- p22.note(lines.join("\n"), "Admin ready");
28521
+ p24.note(lines.join("\n"), "Admin ready");
28365
28522
  }
28366
28523
  function shouldSuppressDevServerStartupLine(line) {
28367
28524
  const plain = stripAnsi2(line).trim();
@@ -28424,7 +28581,7 @@ function pipeManagedDevServerStream(stream, target, state) {
28424
28581
  flushBuffer(true);
28425
28582
  });
28426
28583
  }
28427
- function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
28584
+ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminEmail) {
28428
28585
  return new Promise((resolve, reject) => {
28429
28586
  const [bin, ...args] = devCmd.split(" ");
28430
28587
  const child = spawn6(bin, args, {
@@ -28435,8 +28592,7 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
28435
28592
  suppressStartup: true,
28436
28593
  readyLogged: false,
28437
28594
  adminLoginUrl,
28438
- adminEmail: adminCredentials?.email,
28439
- adminPassword: adminCredentials?.password
28595
+ adminEmail
28440
28596
  };
28441
28597
  const forwardSignal = (signal) => {
28442
28598
  if (!child.killed) {
@@ -28473,74 +28629,133 @@ function startManagedDevServer(cwd, devCmd, adminLoginUrl, adminCredentials) {
28473
28629
 
28474
28630
  // adapters/next/commands/list-integrations.ts
28475
28631
  import path53 from "path";
28476
- import * as p23 from "@clack/prompts";
28632
+ import * as p25 from "@clack/prompts";
28633
+
28634
+ // core-engine/utils/table.ts
28635
+ function renderTableRows(rows) {
28636
+ const widths = [];
28637
+ for (const row of rows) {
28638
+ row.forEach((cell, index) => {
28639
+ if (index < row.length - 1) {
28640
+ widths[index] = Math.max(widths[index] ?? 0, cell.length);
28641
+ }
28642
+ });
28643
+ }
28644
+ return rows.map(
28645
+ (row) => row.map((cell, index) => index < row.length - 1 ? cell.padEnd(widths[index]) : cell).join(" ").trimEnd()
28646
+ );
28647
+ }
28648
+
28649
+ // adapters/next/commands/list-integrations.ts
28477
28650
  async function runListIntegrationsCommand(options) {
28478
28651
  const cwd = options.cwd ? path53.resolve(options.cwd) : process.cwd();
28479
- const config = await resolveConfig(cwd);
28652
+ if (options.json) {
28653
+ let installed2;
28654
+ try {
28655
+ installed2 = new Set((await resolveConfig(cwd)).integrations.installed);
28656
+ } catch (error) {
28657
+ console.error(
28658
+ `Error loading config: ${error instanceof Error ? error.message : String(error)}`
28659
+ );
28660
+ process.exit(1);
28661
+ }
28662
+ const items = listAvailableIntegrations().map((integration) => ({
28663
+ id: integration.id,
28664
+ status: installed2.has(integration.id) ? "installed" : "available",
28665
+ kind: integration.kind,
28666
+ description: integration.description
28667
+ }));
28668
+ console.log(JSON.stringify(items, null, 2));
28669
+ return;
28670
+ }
28671
+ const config = await resolveConfigOrExit(cwd);
28480
28672
  const installedIntegrations = new Set(config.integrations.installed);
28481
- const lines = listAvailableIntegrations().map((integration) => {
28482
- const status = installedIntegrations.has(integration.id) ? "installed" : "available";
28483
- return `${integration.id.padEnd(12)} ${status.padEnd(10)} ${integration.kind.padEnd(8)} ${integration.description}`;
28484
- });
28485
- p23.note(lines.join("\n"), "BetterStart integrations");
28486
- p23.outro(
28487
- `${installedIntegrations.size} installed, ${lines.length - installedIntegrations.size} available`
28673
+ const rows = listAvailableIntegrations().map((integration) => [
28674
+ integration.id,
28675
+ installedIntegrations.has(integration.id) ? "installed" : "available",
28676
+ integration.kind,
28677
+ integration.description
28678
+ ]);
28679
+ p25.note(renderTableRows(rows).join("\n"), "BetterStart integrations");
28680
+ p25.outro(
28681
+ `${installedIntegrations.size} installed, ${rows.length - installedIntegrations.size} available`
28488
28682
  );
28489
28683
  }
28490
28684
 
28491
28685
  // adapters/next/commands/list-presets.ts
28492
28686
  import path54 from "path";
28493
- import * as p24 from "@clack/prompts";
28687
+ import * as p26 from "@clack/prompts";
28494
28688
  async function runListPresetsCommand(options) {
28495
28689
  const cwd = options.cwd ? path54.resolve(options.cwd) : process.cwd();
28496
- const config = await resolveConfig(cwd);
28690
+ if (options.json) {
28691
+ let installed2;
28692
+ try {
28693
+ installed2 = new Set((await resolveConfig(cwd)).presets.installed);
28694
+ } catch (error) {
28695
+ console.error(
28696
+ `Error loading config: ${error instanceof Error ? error.message : String(error)}`
28697
+ );
28698
+ process.exit(1);
28699
+ }
28700
+ const items = listAvailablePresets().map((preset) => ({
28701
+ id: preset.id,
28702
+ status: installed2.has(preset.id) ? "installed" : "available",
28703
+ kind: preset.kind,
28704
+ description: preset.description
28705
+ }));
28706
+ console.log(JSON.stringify(items, null, 2));
28707
+ return;
28708
+ }
28709
+ const config = await resolveConfigOrExit(cwd);
28497
28710
  const installedPresets = new Set(config.presets.installed);
28498
- const lines = listAvailablePresets().map((preset) => {
28499
- const status = installedPresets.has(preset.id) ? "installed" : "available";
28500
- return `${preset.id.padEnd(10)} ${status.padEnd(10)} ${preset.kind.padEnd(12)} ${preset.description}`;
28501
- });
28502
- p24.note(lines.join("\n"), "BetterStart presets");
28503
- p24.outro(`${installedPresets.size} installed, ${lines.length - installedPresets.size} available`);
28711
+ const rows = listAvailablePresets().map((preset) => [
28712
+ preset.id,
28713
+ installedPresets.has(preset.id) ? "installed" : "available",
28714
+ preset.kind,
28715
+ preset.description
28716
+ ]);
28717
+ p26.note(renderTableRows(rows).join("\n"), "BetterStart presets");
28718
+ p26.outro(`${installedPresets.size} installed, ${rows.length - installedPresets.size} available`);
28504
28719
  }
28505
28720
 
28506
28721
  // adapters/next/commands/remove.ts
28507
28722
  import path55 from "path";
28508
- import * as p25 from "@clack/prompts";
28723
+ import * as p27 from "@clack/prompts";
28509
28724
  async function runRemoveCommand(items, options) {
28510
28725
  const removeIntegrationsMode = Boolean(options.integration);
28511
28726
  if (!removeIntegrationsMode && items.includes("core")) {
28512
- p25.log.error("The core Admin cannot be removed.");
28727
+ p27.log.error("The core Admin cannot be removed.");
28513
28728
  process.exit(1);
28514
28729
  }
28515
28730
  const presetIds = items.filter(isPresetId);
28516
28731
  const integrationIds = items.filter(isIntegrationId);
28517
28732
  if (!removeIntegrationsMode && integrationIds.length > 0) {
28518
- p25.log.error(
28733
+ p27.log.error(
28519
28734
  `Integration IDs require --integration. Run \`betterstart remove --integration ${integrationIds.join(" ")}\`.`
28520
28735
  );
28521
28736
  process.exit(1);
28522
28737
  }
28523
28738
  if (removeIntegrationsMode && presetIds.length > 0) {
28524
- p25.log.error(`Preset IDs cannot be removed with --integration: ${presetIds.join(", ")}`);
28739
+ p27.log.error(`Preset IDs cannot be removed with --integration: ${presetIds.join(", ")}`);
28525
28740
  process.exit(1);
28526
28741
  }
28527
28742
  const invalidItems = removeIntegrationsMode ? items.filter((integrationId) => !isIntegrationId(integrationId)) : items.filter((presetId) => !isPresetId(presetId));
28528
28743
  if (invalidItems.length > 0) {
28529
- p25.log.error(
28744
+ p27.log.error(
28530
28745
  removeIntegrationsMode ? formatUnknownIntegrationMessage(invalidItems) : formatUnknownPresetMessage(invalidItems)
28531
28746
  );
28532
28747
  process.exit(1);
28533
28748
  }
28534
28749
  const cwd = options.cwd ? path55.resolve(options.cwd) : process.cwd();
28535
- const config = await resolveConfig(cwd);
28750
+ const config = await resolveConfigOrExit(cwd);
28536
28751
  const pm = detectPackageManager(cwd);
28537
28752
  if (!options.force) {
28538
- const confirmed = await p25.confirm({
28753
+ const confirmed = await p27.confirm({
28539
28754
  message: `Remove ${removeIntegrationsMode ? "integration" : "preset"}${items.length === 1 ? "" : "s"} ${items.join(", ")}?`,
28540
28755
  initialValue: false
28541
28756
  });
28542
- if (p25.isCancel(confirmed) || !confirmed) {
28543
- p25.cancel(`${removeIntegrationsMode ? "Integration" : "Preset"} removal cancelled.`);
28757
+ if (p27.isCancel(confirmed) || !confirmed) {
28758
+ p27.cancel(`${removeIntegrationsMode ? "Integration" : "Preset"} removal cancelled.`);
28544
28759
  process.exit(0);
28545
28760
  }
28546
28761
  }
@@ -28553,13 +28768,13 @@ async function runRemoveCommand(items, options) {
28553
28768
  });
28554
28769
  writeConfigFile(cwd, result2.config);
28555
28770
  if (result2.removed.length === 0) {
28556
- p25.outro("No integrations were removed.");
28771
+ p27.outro("No integrations were removed.");
28557
28772
  return;
28558
28773
  }
28559
28774
  if (result2.warnings.length > 0) {
28560
- p25.note(result2.warnings.join("\n"), "Warnings");
28775
+ p27.note(result2.warnings.join("\n"), "Warnings");
28561
28776
  }
28562
- p25.outro(
28777
+ p27.outro(
28563
28778
  `Removed integration${result2.removed.length === 1 ? "" : "s"}: ${result2.removed.join(", ")}`
28564
28779
  );
28565
28780
  return;
@@ -28572,31 +28787,19 @@ async function runRemoveCommand(items, options) {
28572
28787
  });
28573
28788
  writeConfigFile(cwd, result.config);
28574
28789
  if (result.removed.length === 0) {
28575
- p25.outro("No presets were removed.");
28790
+ p27.outro("No presets were removed.");
28576
28791
  return;
28577
28792
  }
28578
28793
  if (result.warnings.length > 0) {
28579
- p25.note(result.warnings.join("\n"), "Warnings");
28794
+ p27.note(result.warnings.join("\n"), "Warnings");
28580
28795
  }
28581
- p25.outro(`Removed preset${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
28796
+ p27.outro(`Removed preset${result.removed.length === 1 ? "" : "s"}: ${result.removed.join(", ")}`);
28582
28797
  }
28583
28798
 
28584
28799
  // adapters/next/commands/remove-schema.ts
28585
28800
  import fs42 from "fs";
28586
28801
  import path56 from "path";
28587
- import readline from "readline";
28588
- async function promptConfirm4(message) {
28589
- const rl = readline.createInterface({
28590
- input: process.stdin,
28591
- output: process.stdout
28592
- });
28593
- return new Promise((resolve) => {
28594
- rl.question(`${message} (y/N) `, (answer) => {
28595
- rl.close();
28596
- resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes");
28597
- });
28598
- });
28599
- }
28802
+ import * as clack3 from "@clack/prompts";
28600
28803
  function removePath2(cwd, filePath) {
28601
28804
  const fullPath = path56.join(cwd, ...filePath.split("/"));
28602
28805
  const existed = fs42.existsSync(fullPath);
@@ -28642,35 +28845,40 @@ async function runRemoveSchemaCommand(schemaName, options) {
28642
28845
  schemaName
28643
28846
  );
28644
28847
  if (owner === "core") {
28645
- console.error(
28646
- ` "${schemaName}" is a core Admin module and cannot be removed with remove-schema.`
28848
+ clack3.log.error(
28849
+ `"${schemaName}" is a core Admin module and cannot be removed with remove-schema.`
28647
28850
  );
28648
28851
  process.exit(1);
28649
28852
  }
28650
28853
  if (owner.startsWith("preset:")) {
28651
- console.error(` "${schemaName}" is owned by ${owner}. Remove the owning preset instead.`);
28854
+ clack3.log.error(`"${schemaName}" is owned by ${owner}. Remove the owning preset instead.`);
28652
28855
  process.exit(1);
28653
28856
  }
28654
28857
  const cwd = options.cwd ? path56.resolve(options.cwd) : process.cwd();
28655
- const config = await resolveConfig(cwd);
28858
+ const config = await resolveConfigOrExit(cwd);
28656
28859
  const paths = resolveProjectPaths(config);
28657
28860
  const manifest = loadManifest(cwd, schemaName);
28658
28861
  if (!snapshotRootExists(cwd)) {
28659
- console.error(
28660
- " Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
28661
- );
28862
+ clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
28662
28863
  process.exit(1);
28663
28864
  }
28664
28865
  if (!manifest) {
28665
- console.error(
28666
- " Project state is unsupported or incomplete. Re-run `betterstart init --force` to create the current snapshot-backed layout."
28667
- );
28866
+ clack3.log.error(UNSUPPORTED_PROJECT_STATE_MESSAGE);
28668
28867
  process.exit(1);
28669
28868
  }
28670
28869
  if (!options.force) {
28671
- const confirmed = await promptConfirm4(` Remove generated files for ${schemaName}?`);
28672
- if (!confirmed) {
28673
- console.log("\n Cancelled.\n");
28870
+ if (!isInteractiveSession4()) {
28871
+ clack3.log.error(
28872
+ `Removing generated files for ${schemaName} needs confirmation. Re-run with --force.`
28873
+ );
28874
+ process.exit(1);
28875
+ }
28876
+ const confirmed = await clack3.confirm({
28877
+ message: `Remove generated files for ${schemaName}?`,
28878
+ initialValue: false
28879
+ });
28880
+ if (clack3.isCancel(confirmed) || !confirmed) {
28881
+ clack3.cancel("Cancelled.");
28674
28882
  return;
28675
28883
  }
28676
28884
  }
@@ -28718,18 +28926,18 @@ async function runRemoveSchemaCommand(schemaName, options) {
28718
28926
  force: false,
28719
28927
  interactive: false
28720
28928
  });
28721
- console.log(`
28722
- Removed generated files for ${schemaName}.`);
28723
- console.log(` Tombstone written: .betterstart/snapshots/_removed/${schemaName}`);
28724
- console.log(" Schema JSON preserved.");
28725
- console.log("");
28929
+ clack3.log.info(
28930
+ `Tombstone written: .betterstart/snapshots/_removed/${schemaName}
28931
+ Schema JSON preserved.`
28932
+ );
28933
+ clack3.outro(`Removed generated files for ${schemaName}`);
28726
28934
  }
28727
28935
 
28728
28936
  // adapters/next/commands/uninstall.ts
28729
28937
  import fs44 from "fs";
28730
28938
  import path57 from "path";
28731
- import * as p26 from "@clack/prompts";
28732
- import pc10 from "picocolors";
28939
+ import * as p28 from "@clack/prompts";
28940
+ import pc11 from "picocolors";
28733
28941
 
28734
28942
  // adapters/next/commands/uninstall-cleaners.ts
28735
28943
  import fs43 from "fs";
@@ -28950,8 +29158,8 @@ function buildUninstallPlan(cwd, namespaceValue) {
28950
29158
  count: configFiles.length,
28951
29159
  unit: configFiles.length === 1 ? "file" : "files",
28952
29160
  execute() {
28953
- for (const p27 of configPaths) {
28954
- if (fs44.existsSync(p27)) fs44.unlinkSync(p27);
29161
+ for (const p30 of configPaths) {
29162
+ if (fs44.existsSync(p30)) fs44.unlinkSync(p30);
28955
29163
  }
28956
29164
  }
28957
29165
  });
@@ -29015,7 +29223,7 @@ function buildUninstallPlan(cwd, namespaceValue) {
29015
29223
  }
29016
29224
  async function runUninstallCommand(options) {
29017
29225
  const cwd = options.cwd ? path57.resolve(options.cwd) : process.cwd();
29018
- p26.intro(pc10.bgRed(pc10.white(" BetterStart Uninstall ")));
29226
+ p28.intro(pc11.bgRed(pc11.white(" BetterStart Uninstall ")));
29019
29227
  let namespace = DEFAULT_ADMIN_NAMESPACE;
29020
29228
  try {
29021
29229
  const config = await resolveConfig(cwd);
@@ -29024,23 +29232,23 @@ async function runUninstallCommand(options) {
29024
29232
  }
29025
29233
  const steps = buildUninstallPlan(cwd, namespace);
29026
29234
  if (steps.length === 0) {
29027
- p26.log.success(`${pc10.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
29028
- p26.outro("Done");
29235
+ p28.log.success(`${pc11.green("\u2713")} Nothing to remove \u2014 project is already clean.`);
29236
+ p28.outro("Project already clean");
29029
29237
  return;
29030
29238
  }
29031
29239
  const planLines = steps.map((step) => {
29032
29240
  const names = step.items.join(" ");
29033
- const countLabel = pc10.dim(`${step.count} ${step.unit}`);
29034
- return `${pc10.red("\xD7")} ${names} ${countLabel}`;
29241
+ const countLabel = pc11.dim(`${step.count} ${step.unit}`);
29242
+ return `${pc11.red("\u2717")} ${names} ${countLabel}`;
29035
29243
  });
29036
- p26.note(planLines.join("\n"), "Uninstall plan");
29244
+ p28.note(planLines.join("\n"), "Uninstall plan");
29037
29245
  if (!options.force) {
29038
- const confirmed = await p26.confirm({
29246
+ const confirmed = await p28.confirm({
29039
29247
  message: "Proceed with uninstall?",
29040
29248
  initialValue: false
29041
29249
  });
29042
- if (p26.isCancel(confirmed) || !confirmed) {
29043
- p26.cancel("Uninstall cancelled.");
29250
+ if (p28.isCancel(confirmed) || !confirmed) {
29251
+ p28.cancel("Uninstall cancelled.");
29044
29252
  process.exit(0);
29045
29253
  }
29046
29254
  }
@@ -29052,15 +29260,15 @@ async function runUninstallCommand(options) {
29052
29260
  }
29053
29261
  const parts = steps.map((step) => `${step.count} ${step.unit}`);
29054
29262
  s.stop(`Removed ${parts.join(", ")}`);
29055
- p26.note(pc10.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
29056
- p26.outro("Uninstall complete");
29263
+ p28.note(pc11.dim("Database tables were NOT dropped \u2014 drop them manually if needed."), "Next steps");
29264
+ p28.outro("Uninstall complete");
29057
29265
  }
29058
29266
 
29059
29267
  // adapters/next/commands/update-component.ts
29060
29268
  import { execFileSync as execFileSync6 } from "child_process";
29061
29269
  import fs45 from "fs";
29062
29270
  import path58 from "path";
29063
- import * as clack2 from "@clack/prompts";
29271
+ import * as clack4 from "@clack/prompts";
29064
29272
  import fsExtra from "fs-extra";
29065
29273
  var STATIC_CUSTOM_DEPENDENCIES = {
29066
29274
  "content-editor": [
@@ -29239,6 +29447,7 @@ var TIPTAP_TEMPLATE_DEPENDENCIES = [
29239
29447
  "media-field"
29240
29448
  ];
29241
29449
  var TIPTAP_CONTENT_EDITOR_DIRECTORIES = ["tiptap-extension", "tiptap-node", "tiptap-ui"];
29450
+ var MAX_OVERWRITE_PREVIEW_ROWS = 20;
29242
29451
  var CODEMIRROR_PACKAGE_DEPS = [
29243
29452
  "@codemirror/commands",
29244
29453
  "@codemirror/lang-javascript",
@@ -30928,50 +31137,67 @@ async function runUpdateCommand(components, options) {
30928
31137
  const cwd = options.cwd ? path58.resolve(options.cwd) : process.cwd();
30929
31138
  const normalizedOnly = normalizeShadcnPresetOnly(options.only);
30930
31139
  validateShadcnPresetOptions(components, options);
31140
+ if (options.json && !options.list) {
31141
+ clack4.cancel("--json can only be used with --list.");
31142
+ process.exit(1);
31143
+ }
30931
31144
  if (options.list) {
30932
- const all = getAllComponentNames();
30933
- clack2.intro("Available components");
30934
31145
  const uiComponents = getStaticUiComponentEntries();
30935
31146
  const customComponents = getStaticCustomComponentEntries();
30936
31147
  const templateKeys = Object.keys(TEMPLATE_REGISTRY).sort();
30937
- console.log();
30938
- console.log(` Shadcn UI Components (${uiComponents.length})`);
30939
- console.log(` ${"\u2500".repeat(50)}`);
30940
- console.log(` ${"Name".padEnd(28)} ${"Path"}`);
30941
- console.log(` ${"\u2500".repeat(50)}`);
30942
- for (const component of uiComponents) {
30943
- console.log(` ${component.name.padEnd(28)} components/ui/${component.file}`);
30944
- }
30945
- console.log();
30946
- console.log(` Custom Components (${customComponents.length})`);
30947
- console.log(` ${"\u2500".repeat(56)}`);
30948
- console.log(` ${"Name".padEnd(28)} ${"Path"}`);
30949
- console.log(` ${"\u2500".repeat(56)}`);
30950
- for (const component of customComponents) {
30951
- console.log(` ${component.name.padEnd(28)} components/custom/${component.file}`);
30952
- }
30953
- console.log();
30954
- console.log(` Template Components (${templateKeys.length})`);
30955
- console.log(` ${"\u2500".repeat(56)}`);
30956
- console.log(` ${"Name".padEnd(28)} ${"Path"}`);
30957
- console.log(` ${"\u2500".repeat(56)}`);
30958
- for (const name of templateKeys) {
31148
+ const templatePath = (name) => {
30959
31149
  const entry = TEMPLATE_REGISTRY[name];
30960
- const relPath = entry.displayPath ?? entry.relPath;
30961
- console.log(` ${name.padEnd(28)} ${relPath}`);
30962
- }
30963
- console.log();
30964
- console.log(` Special`);
30965
- console.log(` ${"\u2500".repeat(56)}`);
30966
- console.log(` ${"tiptap".padEnd(28)} components/custom/content-editor/tiptap-*/ (all files)`);
30967
- console.log();
30968
- clack2.outro(`${all.length} components available`);
31150
+ return entry.displayPath ?? (typeof entry.relPath === "string" ? entry.relPath : "");
31151
+ };
31152
+ if (options.json) {
31153
+ const items = [
31154
+ ...uiComponents.map((component) => ({
31155
+ name: component.name,
31156
+ path: `components/ui/${component.file}`,
31157
+ kind: "ui"
31158
+ })),
31159
+ ...customComponents.map((component) => ({
31160
+ name: component.name,
31161
+ path: `components/custom/${component.file}`,
31162
+ kind: "custom"
31163
+ })),
31164
+ ...templateKeys.map((name) => ({ name, path: templatePath(name), kind: "template" })),
31165
+ { name: "tiptap", path: "components/custom/content-editor/tiptap-*/", kind: "special" }
31166
+ ];
31167
+ console.log(JSON.stringify(items, null, 2));
31168
+ return;
31169
+ }
31170
+ const all = getAllComponentNames();
31171
+ clack4.intro("Available components");
31172
+ clack4.note(
31173
+ renderTableRows(
31174
+ uiComponents.map((component) => [component.name, `components/ui/${component.file}`])
31175
+ ).join("\n"),
31176
+ `Shadcn UI Components (${uiComponents.length})`
31177
+ );
31178
+ clack4.note(
31179
+ renderTableRows(
31180
+ customComponents.map((component) => [component.name, `components/custom/${component.file}`])
31181
+ ).join("\n"),
31182
+ `Custom Components (${customComponents.length})`
31183
+ );
31184
+ clack4.note(
31185
+ renderTableRows(templateKeys.map((name) => [name, templatePath(name)])).join("\n"),
31186
+ `Template Components (${templateKeys.length})`
31187
+ );
31188
+ clack4.note(
31189
+ renderTableRows([["tiptap", "components/custom/content-editor/tiptap-*/ (all files)"]]).join(
31190
+ "\n"
31191
+ ),
31192
+ "Special"
31193
+ );
31194
+ clack4.outro(`${all.length} components available`);
30969
31195
  return;
30970
31196
  }
30971
- const config = await resolveConfig(cwd);
31197
+ const config = await resolveConfigOrExit(cwd);
30972
31198
  const admin = path58.resolve(cwd, config.paths.admin);
30973
31199
  if (!fs45.existsSync(admin)) {
30974
- clack2.cancel(
31200
+ clack4.cancel(
30975
31201
  `Admin directory not found at ${config.paths.admin}. Run 'betterstart init' first.`
30976
31202
  );
30977
31203
  process.exit(1);
@@ -30986,12 +31212,12 @@ async function runUpdateCommand(components, options) {
30986
31212
  return;
30987
31213
  }
30988
31214
  if (!options.all && components.length === 0) {
30989
- clack2.log.error(
31215
+ clack4.log.error(
30990
31216
  "Provide component names or use --all. Run with --list to see available components."
30991
31217
  );
30992
31218
  process.exit(1);
30993
31219
  }
30994
- clack2.intro("BetterStart Update Components");
31220
+ clack4.intro("BetterStart Update Components");
30995
31221
  const toUpdate = options.all ? getAllComponentNamesForConfig(config) : components;
30996
31222
  const uiDir = resolveCliAssetPath("shared-assets", "react-admin", "ui");
30997
31223
  const customDir = resolveCliAssetPath("shared-assets", "react-admin", "custom");
@@ -31000,6 +31226,7 @@ async function runUpdateCommand(components, options) {
31000
31226
  const updatedTemplateNames = /* @__PURE__ */ new Set();
31001
31227
  const updatedStaticNames = /* @__PURE__ */ new Set();
31002
31228
  const requiredPackageDependencies = /* @__PURE__ */ new Set();
31229
+ const pendingWrites = [];
31003
31230
  function trackPackageDependencies(name) {
31004
31231
  for (const dependency of COMPONENT_PACKAGE_DEPENDENCIES[name] ?? []) {
31005
31232
  requiredPackageDependencies.add(dependency);
@@ -31010,12 +31237,12 @@ async function runUpdateCommand(components, options) {
31010
31237
  return false;
31011
31238
  }
31012
31239
  if (entry.requiredPreset && !hasPreset(config, entry.requiredPreset)) {
31013
- clack2.log.warning(`${name} requires the ${entry.requiredPreset} preset and was skipped.`);
31240
+ clack4.log.warn(`${name} requires the ${entry.requiredPreset} preset and was skipped.`);
31014
31241
  skipped++;
31015
31242
  return false;
31016
31243
  }
31017
31244
  if (entry.requiredIntegration && !hasIntegration(config, entry.requiredIntegration)) {
31018
- clack2.log.warning(
31245
+ clack4.log.warn(
31019
31246
  `${name} requires the ${entry.requiredIntegration} integration and was skipped.`
31020
31247
  );
31021
31248
  skipped++;
@@ -31025,14 +31252,19 @@ async function runUpdateCommand(components, options) {
31025
31252
  const baseDir = entry.base === "cwd" ? cwd : admin;
31026
31253
  const destPath = path58.join(baseDir, relPath);
31027
31254
  if (entry.preserveExisting && fs45.existsSync(destPath)) {
31028
- clack2.log.info(`Preserved ${relPath}`);
31255
+ clack4.log.info(`Preserved ${relPath}`);
31029
31256
  updatedTemplateNames.add(name);
31030
31257
  skipped++;
31031
31258
  return false;
31032
31259
  }
31033
- fsExtra.ensureDirSync(path58.dirname(destPath));
31034
- fs45.writeFileSync(destPath, content, "utf-8");
31035
- clack2.log.success(`Updated ${relPath}`);
31260
+ pendingWrites.push({
31261
+ displayPath: relPath,
31262
+ write: () => {
31263
+ fsExtra.ensureDirSync(path58.dirname(destPath));
31264
+ fs45.writeFileSync(destPath, content, "utf-8");
31265
+ clack4.log.success(`Updated ${relPath}`);
31266
+ }
31267
+ });
31036
31268
  updatedTemplateNames.add(name);
31037
31269
  trackPackageDependencies(name);
31038
31270
  updated++;
@@ -31061,17 +31293,29 @@ async function runUpdateCommand(components, options) {
31061
31293
  const namespace = config.frameworkConfig.next.namespace;
31062
31294
  const namespacedAssetFile = applyAdminNamespaceToPath(assetFile, namespace);
31063
31295
  const destPath = path58.join(admin, "components", assetDirectory, namespacedAssetFile);
31064
- fsExtra.ensureDirSync(path58.dirname(destPath));
31065
- writeNamespacedFile(path58.join(assetDir, assetFile), destPath, namespace);
31066
- clack2.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
31296
+ pendingWrites.push({
31297
+ displayPath: `components/${assetDirectory}/${namespacedAssetFile}`,
31298
+ write: () => {
31299
+ fsExtra.ensureDirSync(path58.dirname(destPath));
31300
+ writeNamespacedFile(path58.join(assetDir, assetFile), destPath, namespace);
31301
+ clack4.log.success(`Updated components/${assetDirectory}/${namespacedAssetFile}`);
31302
+ }
31303
+ });
31067
31304
  if (assetDirectory === "custom") {
31068
31305
  const assetSubdir = path58.join(assetDir, name);
31069
31306
  if (fs45.existsSync(assetSubdir) && fs45.statSync(assetSubdir).isDirectory()) {
31070
31307
  const namespacedName = applyAdminNamespaceToPath(name, namespace);
31071
31308
  const destSubdir = path58.join(admin, "components", assetDirectory, namespacedName);
31072
- fsExtra.emptyDirSync(destSubdir);
31073
- copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
31074
- clack2.log.success(`Updated components/${assetDirectory}/${namespacedName}/ (all files)`);
31309
+ pendingWrites.push({
31310
+ displayPath: `components/${assetDirectory}/${namespacedName}/ (template files)`,
31311
+ write: () => {
31312
+ fsExtra.ensureDirSync(destSubdir);
31313
+ copyNamespacedDirectory(assetSubdir, destSubdir, namespace);
31314
+ clack4.log.success(
31315
+ `Updated components/${assetDirectory}/${namespacedName}/ (template files)`
31316
+ );
31317
+ }
31318
+ });
31075
31319
  }
31076
31320
  }
31077
31321
  updatedStaticNames.add(key);
@@ -31100,25 +31344,34 @@ async function runUpdateCommand(components, options) {
31100
31344
  if (!fs45.existsSync(srcBaseDir)) {
31101
31345
  return false;
31102
31346
  }
31103
- let copied = false;
31347
+ const dirsToCopy = [];
31104
31348
  for (const directory of TIPTAP_CONTENT_EDITOR_DIRECTORIES) {
31105
31349
  const srcDir = path58.join(srcBaseDir, directory);
31106
31350
  if (!fs45.existsSync(srcDir)) {
31107
31351
  continue;
31108
31352
  }
31109
- const destDir = path58.join(destBaseDir, applyAdminNamespaceToPath(directory, namespace));
31110
- fsExtra.emptyDirSync(destDir);
31111
- copyNamespacedDirectory(srcDir, destDir, namespace);
31112
- copied = true;
31353
+ dirsToCopy.push({
31354
+ srcDir,
31355
+ destDir: path58.join(destBaseDir, applyAdminNamespaceToPath(directory, namespace))
31356
+ });
31113
31357
  }
31114
- if (!copied) {
31358
+ if (dirsToCopy.length === 0) {
31115
31359
  return false;
31116
31360
  }
31117
- clack2.log.success("Updated components/custom/content-editor/tiptap-*/ (all files)");
31361
+ pendingWrites.push({
31362
+ displayPath: "components/custom/content-editor/tiptap-*/ (template files)",
31363
+ write: () => {
31364
+ for (const { srcDir, destDir } of dirsToCopy) {
31365
+ fsExtra.ensureDirSync(destDir);
31366
+ copyNamespacedDirectory(srcDir, destDir, namespace);
31367
+ }
31368
+ clack4.log.success("Updated components/custom/content-editor/tiptap-*/ (template files)");
31369
+ removeLegacyDirectory(path58.join(admin, "components", "custom", "tiptap"));
31370
+ }
31371
+ });
31118
31372
  updatedStaticNames.add(key);
31119
31373
  trackPackageDependencies("tiptap");
31120
31374
  updated++;
31121
- removeLegacyDirectory(path58.join(admin, "components", "custom", "tiptap"));
31122
31375
  for (const dependencyName of TIPTAP_TEMPLATE_DEPENDENCIES) {
31123
31376
  writeNamedDependency(dependencyName);
31124
31377
  }
@@ -31154,14 +31407,44 @@ async function runUpdateCommand(components, options) {
31154
31407
  }
31155
31408
  if (name === "tiptap") {
31156
31409
  if (!writeTiptapTemplates()) {
31157
- clack2.log.warning("tiptap templates not found");
31410
+ clack4.log.warn("tiptap templates not found");
31158
31411
  skipped++;
31159
31412
  }
31160
31413
  continue;
31161
31414
  }
31162
- clack2.log.warning(`Unknown component: ${name}`);
31415
+ clack4.log.warn(`Unknown component: ${name}`);
31163
31416
  skipped++;
31164
31417
  }
31418
+ if (pendingWrites.length > 0) {
31419
+ const displayPaths = pendingWrites.map((entry) => entry.displayPath);
31420
+ const preview = displayPaths.slice(0, MAX_OVERWRITE_PREVIEW_ROWS);
31421
+ if (displayPaths.length > preview.length) {
31422
+ preview.push(`\u2026 and ${displayPaths.length - preview.length} more`);
31423
+ }
31424
+ clack4.note(
31425
+ preview.join("\n"),
31426
+ `Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"}`
31427
+ );
31428
+ if (!options.yes) {
31429
+ if (!isInteractiveSession4()) {
31430
+ clack4.log.error(
31431
+ `Overwriting ${displayPaths.length} file${displayPaths.length === 1 ? "" : "s"} needs confirmation. Re-run with --yes.`
31432
+ );
31433
+ process.exit(1);
31434
+ }
31435
+ const proceed = await clack4.confirm({
31436
+ message: "Overwrite these files with the latest templates?",
31437
+ initialValue: true
31438
+ });
31439
+ if (clack4.isCancel(proceed) || !proceed) {
31440
+ clack4.cancel("Update cancelled.");
31441
+ process.exit(0);
31442
+ }
31443
+ }
31444
+ for (const entry of pendingWrites) {
31445
+ entry.write();
31446
+ }
31447
+ }
31165
31448
  syncInstalledPresetManifests(cwd, config);
31166
31449
  syncInstalledIntegrationManifests(cwd, config);
31167
31450
  const projectPackageJson = readProjectPackageJson2(cwd);
@@ -31169,11 +31452,11 @@ async function runUpdateCommand(components, options) {
31169
31452
  (dependency) => !hasDeclaredPackage2(projectPackageJson, dependency)
31170
31453
  );
31171
31454
  if (missingPackageDependencies.length > 0) {
31172
- clack2.log.warning(
31455
+ clack4.log.warn(
31173
31456
  `Updated components require package dependencies that are not declared: ${missingPackageDependencies.join(", ")}. Run 'pnpm betterstart update-deps' before building.`
31174
31457
  );
31175
31458
  }
31176
- clack2.outro(
31459
+ clack4.outro(
31177
31460
  `Updated ${updated} component${updated !== 1 ? "s" : ""}${skipped > 0 ? `, ${skipped} skipped` : ""}`
31178
31461
  );
31179
31462
  }
@@ -31184,22 +31467,22 @@ function removeLegacyDirectory(dirPath) {
31184
31467
  }
31185
31468
  function validateShadcnPresetOptions(components, options) {
31186
31469
  if (options.only && !options.shadcnPreset) {
31187
- clack2.cancel("--only can only be used with --shadcn-preset.");
31470
+ clack4.cancel("--only can only be used with --shadcn-preset.");
31188
31471
  process.exit(1);
31189
31472
  }
31190
31473
  if (!options.shadcnPreset) {
31191
31474
  return;
31192
31475
  }
31193
31476
  if (options.list) {
31194
- clack2.cancel("--list cannot be combined with --shadcn-preset.");
31477
+ clack4.cancel("--list cannot be combined with --shadcn-preset.");
31195
31478
  process.exit(1);
31196
31479
  }
31197
31480
  if (options.all) {
31198
- clack2.cancel("--all cannot be combined with --shadcn-preset.");
31481
+ clack4.cancel("--all cannot be combined with --shadcn-preset.");
31199
31482
  process.exit(1);
31200
31483
  }
31201
31484
  if (components.length > 0) {
31202
- clack2.cancel("Component names cannot be combined with --shadcn-preset.");
31485
+ clack4.cancel("Component names cannot be combined with --shadcn-preset.");
31203
31486
  process.exit(1);
31204
31487
  }
31205
31488
  }
@@ -31209,7 +31492,7 @@ function normalizeShadcnPresetOnly(value) {
31209
31492
  }
31210
31493
  const parts = value.split(",").map((part) => part.trim().toLowerCase()).filter(Boolean);
31211
31494
  if (parts.length !== 1 || parts[0] !== "theme") {
31212
- clack2.cancel(
31495
+ clack4.cancel(
31213
31496
  "Admin-only shadcn preset updates currently support --only theme. Omit --only to apply the full preset."
31214
31497
  );
31215
31498
  process.exit(1);
@@ -31233,11 +31516,11 @@ function runShadcnPresetUpdate({
31233
31516
  ...getHostProjectFilesToRestore(cwd)
31234
31517
  ];
31235
31518
  if (!preset) {
31236
- clack2.cancel("--shadcn-preset requires a preset code, preset name, or preset URL.");
31519
+ clack4.cancel("--shadcn-preset requires a preset code, preset name, or preset URL.");
31237
31520
  process.exit(1);
31238
31521
  }
31239
31522
  if (!fs45.existsSync(adminGlobalsPath)) {
31240
- clack2.cancel(
31523
+ clack4.cancel(
31241
31524
  `Admin globals file not found at ${path58.relative(cwd, adminGlobalsPath)}. Run 'betterstart update admin-globals' first.`
31242
31525
  );
31243
31526
  process.exit(1);
@@ -31247,8 +31530,8 @@ function runShadcnPresetUpdate({
31247
31530
  filePath,
31248
31531
  snapshot: snapshotFile(filePath)
31249
31532
  }));
31250
- clack2.intro("BetterStart Shadcn Preset");
31251
- clack2.log.info(`Applying preset to ${path58.join(config.paths.admin, "components/ui")}`);
31533
+ clack4.intro("BetterStart Shadcn Preset");
31534
+ clack4.log.info(`Applying preset to ${path58.join(config.paths.admin, "components/ui")}`);
31252
31535
  let failed = false;
31253
31536
  try {
31254
31537
  fs45.writeFileSync(
@@ -31270,10 +31553,10 @@ function runShadcnPresetUpdate({
31270
31553
  }
31271
31554
  }
31272
31555
  if (failed) {
31273
- clack2.cancel("shadcn preset application failed.");
31556
+ clack4.cancel("shadcn preset application failed.");
31274
31557
  process.exit(1);
31275
31558
  }
31276
- clack2.outro(
31559
+ clack4.outro(
31277
31560
  only ? `Applied shadcn preset parts (${only}) to the Admin.` : "Applied shadcn preset to Admin UI components and styles."
31278
31561
  );
31279
31562
  }
@@ -31309,7 +31592,7 @@ function resolveLocalShadcnBin(cwd) {
31309
31592
  const binName = process.platform === "win32" ? "shadcn.cmd" : "shadcn";
31310
31593
  const shadcnBin = path58.join(cwd, "node_modules", ".bin", binName);
31311
31594
  if (!fs45.existsSync(shadcnBin)) {
31312
- clack2.cancel(
31595
+ clack4.cancel(
31313
31596
  `shadcn is not installed in this project. Run 'betterstart update-deps' and try again.`
31314
31597
  );
31315
31598
  process.exit(1);
@@ -31349,13 +31632,13 @@ function restoreFile(filePath, snapshot) {
31349
31632
 
31350
31633
  // adapters/next/commands/update-deps.ts
31351
31634
  import path59 from "path";
31352
- import * as clack3 from "@clack/prompts";
31635
+ import * as clack5 from "@clack/prompts";
31353
31636
  async function runUpdateDepsCommand(options) {
31354
31637
  const cwd = options.cwd ? path59.resolve(options.cwd) : process.cwd();
31355
- clack3.intro("BetterStart Update Dependencies");
31638
+ clack5.intro("BetterStart Update Dependencies");
31356
31639
  const pm = detectPackageManager(cwd);
31357
- clack3.log.info(`Package manager: ${pm}`);
31358
- const config = await resolveConfig(cwd);
31640
+ clack5.log.info(`Package manager: ${pm}`);
31641
+ const config = await resolveConfigOrExit(cwd);
31359
31642
  const dependencyPlan = getDependencyPlan(
31360
31643
  getInstalledPresets(config),
31361
31644
  getInstalledIntegrations(config),
@@ -31378,25 +31661,25 @@ async function runUpdateDepsCommand(options) {
31378
31661
  s.stop(`Installed ${result.dependencies.length} deps + ${result.devDeps.length} dev deps`);
31379
31662
  } else {
31380
31663
  s.stop("Dependency install failed");
31381
- clack3.log.error(result.error ?? "Unknown error");
31664
+ clack5.log.error(result.error ?? "Unknown error");
31382
31665
  process.exit(1);
31383
31666
  }
31384
- clack3.outro("Dependencies updated");
31667
+ clack5.outro("Dependencies updated");
31385
31668
  }
31386
31669
 
31387
31670
  // adapters/next/commands/update-styles.ts
31388
31671
  import fs46 from "fs";
31389
31672
  import path60 from "path";
31390
- import * as clack4 from "@clack/prompts";
31673
+ import * as clack6 from "@clack/prompts";
31391
31674
  async function runUpdateStylesCommand(options) {
31392
31675
  const cwd = options.cwd ? path60.resolve(options.cwd) : process.cwd();
31393
- clack4.intro("BetterStart Update Styles");
31394
- const config = await resolveConfig(cwd);
31676
+ clack6.intro("BetterStart Update Styles");
31677
+ const config = await resolveConfigOrExit(cwd);
31395
31678
  const adminDir = config.paths.admin;
31396
31679
  const namespace = resolveAdminNamespace(config.frameworkConfig.next.namespace);
31397
31680
  const targetPath = path60.join(cwd, adminDir, namespace.globalsFile);
31398
31681
  if (!fs46.existsSync(targetPath)) {
31399
- clack4.cancel(`${namespace.globalsFile} not found at ${path60.relative(cwd, targetPath)}`);
31682
+ clack6.cancel(`${namespace.globalsFile} not found at ${path60.relative(cwd, targetPath)}`);
31400
31683
  process.exit(1);
31401
31684
  }
31402
31685
  fs46.writeFileSync(
@@ -31404,8 +31687,8 @@ async function runUpdateStylesCommand(options) {
31404
31687
  applyAdminNamespaceToContent(readTemplate("admin-globals.css"), namespace.segment),
31405
31688
  "utf-8"
31406
31689
  );
31407
- clack4.log.success(`Updated ${path60.relative(cwd, targetPath)}`);
31408
- clack4.outro("Styles updated");
31690
+ clack6.log.success(`Updated ${path60.relative(cwd, targetPath)}`);
31691
+ clack6.outro("Styles updated");
31409
31692
  }
31410
31693
 
31411
31694
  // adapters/next/commands-runtime.ts
@@ -31428,11 +31711,12 @@ var nextCommandRuntime = {
31428
31711
 
31429
31712
  // cli.ts
31430
31713
  import { Command as Command2 } from "commander";
31714
+ import pc12 from "picocolors";
31431
31715
  var { version } = JSON.parse(
31432
31716
  readFileSync(new URL("../package.json", import.meta.url), "utf-8")
31433
31717
  );
31434
31718
  var program = new Command2();
31435
- program.name("betterstart").description("Scaffold a preset and integration based Admin into any Next.js 16 application").version(version);
31719
+ program.name("betterstart").description("Scaffold a preset- and integration-based Admin into any Next.js 16 application").version(version);
31436
31720
  program.hook("preAction", (_command, actionCommand) => {
31437
31721
  requireInitializedProject(actionCommand);
31438
31722
  });
@@ -31450,5 +31734,16 @@ program.addCommand(createUninstallCommand(nextCommandRuntime));
31450
31734
  program.addCommand(createUpdateCommand(nextCommandRuntime));
31451
31735
  program.addCommand(createUpdateDepsCommand(nextCommandRuntime));
31452
31736
  program.addCommand(createUpdateStylesCommand(nextCommandRuntime));
31453
- program.parse();
31737
+ try {
31738
+ await program.parseAsync();
31739
+ } catch (error) {
31740
+ if (process.env.BETTERSTART_DEBUG) {
31741
+ console.error(error);
31742
+ } else {
31743
+ const message = error instanceof Error && error.message ? error.message : String(error);
31744
+ p29.log.error(message);
31745
+ p29.log.message(pc12.dim("Re-run with BETTERSTART_DEBUG=1 for the full stack trace."));
31746
+ }
31747
+ process.exit(1);
31748
+ }
31454
31749
  //# sourceMappingURL=cli.js.map