neon 3.0.0 → 3.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (207) hide show
  1. package/README.md +70 -5
  2. package/dist/_chunks/auth_selection-DGgq6ifc.js +83 -0
  3. package/dist/_chunks/cmd_pipeline-CUbBO9U_.js +2818 -0
  4. package/dist/_chunks/credentials-MYdHdKah.js +188 -0
  5. package/dist/_chunks/env-NbA61JR3.js +585 -0
  6. package/dist/_chunks/env_services-Tz9G4JeT.js +531 -0
  7. package/dist/_chunks/paths-DMq0Lt7a.js +151 -0
  8. package/dist/_chunks/profiles-Ir29rqns.js +217 -0
  9. package/dist/_chunks/psql-DWH-kc69.js +2169 -0
  10. package/dist/_chunks/rolldown-runtime-D7D4PA-g.js +13 -0
  11. package/dist/_chunks/secure_file-BucZj4yQ.js +39 -0
  12. package/dist/analytics.js +163 -207
  13. package/dist/api.js +815 -758
  14. package/dist/auth.js +121 -141
  15. package/dist/auth_context.js +39 -53
  16. package/dist/cli.js +4 -7
  17. package/dist/commands/api.js +220 -250
  18. package/dist/commands/api_keys.js +251 -314
  19. package/dist/commands/auth.js +283 -328
  20. package/dist/commands/bootstrap.js +372 -437
  21. package/dist/commands/branches.js +304 -455
  22. package/dist/commands/bucket.js +374 -514
  23. package/dist/commands/checkout.js +213 -298
  24. package/dist/commands/config.js +573 -690
  25. package/dist/commands/connection_string.js +137 -165
  26. package/dist/commands/data_api.js +238 -260
  27. package/dist/commands/databases.js +67 -76
  28. package/dist/commands/deploy.js +31 -25
  29. package/dist/commands/dev.js +639 -719
  30. package/dist/commands/diff.js +156 -200
  31. package/dist/commands/env.js +255 -305
  32. package/dist/commands/functions.js +275 -355
  33. package/dist/commands/index.js +70 -65
  34. package/dist/commands/init.js +84 -119
  35. package/dist/commands/inspect.js +55 -55
  36. package/dist/commands/ip_allow.js +88 -120
  37. package/dist/commands/link.js +874 -1019
  38. package/dist/commands/logs.js +291 -0
  39. package/dist/commands/neon_auth.js +725 -933
  40. package/dist/commands/operations.js +34 -25
  41. package/dist/commands/orgs.js +28 -18
  42. package/dist/commands/profile.js +615 -846
  43. package/dist/commands/projects.js +313 -373
  44. package/dist/commands/psql.js +60 -58
  45. package/dist/commands/roles.js +55 -58
  46. package/dist/commands/schema_diff.js +87 -131
  47. package/dist/commands/set_context.js +34 -26
  48. package/dist/commands/snapshots.js +288 -413
  49. package/dist/commands/status.js +41 -37
  50. package/dist/commands/user.js +21 -10
  51. package/dist/commands/vpc_endpoints.js +85 -113
  52. package/dist/config.js +7 -6
  53. package/dist/config_format.js +50 -66
  54. package/dist/config_template.js +128 -157
  55. package/dist/context.js +183 -235
  56. package/dist/current_branch_fast_path.js +40 -49
  57. package/dist/dev/env.js +2 -446
  58. package/dist/dev/functions.js +54 -68
  59. package/dist/dev/inputs.js +46 -58
  60. package/dist/dev/runtime.js +135 -164
  61. package/dist/dev/websocket.js +766 -959
  62. package/dist/env.js +27 -33
  63. package/dist/env_file.js +118 -132
  64. package/dist/env_services.js +2 -51
  65. package/dist/errors.js +57 -68
  66. package/dist/functions_api.js +45 -43
  67. package/dist/help.js +189 -140
  68. package/dist/index.js +182 -257
  69. package/dist/init/agents.js +137 -118
  70. package/dist/init/auth.js +58 -68
  71. package/dist/init/bootstrap.js +325 -396
  72. package/dist/init/build_config.js +4 -2
  73. package/dist/init/detect_agent.js +56 -101
  74. package/dist/init/editors.js +35 -52
  75. package/dist/init/enrich_output.js +51 -66
  76. package/dist/init/extension.js +134 -171
  77. package/dist/init/inspect.js +179 -266
  78. package/dist/init/interactive.js +510 -622
  79. package/dist/init/neonctl.js +117 -168
  80. package/dist/init/orchestrate.js +157 -173
  81. package/dist/init/phases/auth.js +188 -202
  82. package/dist/init/phases/cleanup.js +23 -23
  83. package/dist/init/phases/db.js +251 -277
  84. package/dist/init/phases/getting_started.js +213 -223
  85. package/dist/init/phases/mcp.js +174 -224
  86. package/dist/init/phases/migrations.js +247 -248
  87. package/dist/init/phases/neon_auth.js +114 -133
  88. package/dist/init/phases/setup.js +546 -703
  89. package/dist/init/phases/skills.js +75 -86
  90. package/dist/init/phases/status.js +72 -67
  91. package/dist/init/resolve_context.js +102 -99
  92. package/dist/init/route_command.js +91 -98
  93. package/dist/init/skills.js +174 -218
  94. package/dist/init/vsix.js +77 -99
  95. package/dist/log.js +17 -16
  96. package/dist/neon_services.js +104 -129
  97. package/dist/parameters.gen.js +481 -471
  98. package/dist/pkg.js +17 -19
  99. package/dist/profile_keys.js +44 -47
  100. package/dist/psql/cli.js +44 -47
  101. package/dist/psql/command/cmd_cond.js +231 -406
  102. package/dist/psql/command/cmd_connect.js +557 -764
  103. package/dist/psql/command/cmd_copy.js +728 -984
  104. package/dist/psql/command/cmd_describe.js +1499 -1688
  105. package/dist/psql/command/cmd_format.js +733 -905
  106. package/dist/psql/command/cmd_io.js +2 -2193
  107. package/dist/psql/command/cmd_lo.js +297 -359
  108. package/dist/psql/command/cmd_meta.js +727 -878
  109. package/dist/psql/command/cmd_misc.js +138 -172
  110. package/dist/psql/command/cmd_pipeline.js +2 -1148
  111. package/dist/psql/command/cmd_restrict.js +119 -155
  112. package/dist/psql/command/cmd_show.js +529 -688
  113. package/dist/psql/command/dispatch.js +260 -325
  114. package/dist/psql/command/inputQueue.js +35 -33
  115. package/dist/psql/command/shared.js +49 -63
  116. package/dist/psql/complete/filenames.js +90 -133
  117. package/dist/psql/complete/index.js +59 -97
  118. package/dist/psql/complete/matcher.js +236 -300
  119. package/dist/psql/complete/psqlVars.js +218 -223
  120. package/dist/psql/complete/queries.js +159 -177
  121. package/dist/psql/complete/rules.js +1493 -2299
  122. package/dist/psql/core/common.js +2 -1253
  123. package/dist/psql/core/help.js +456 -546
  124. package/dist/psql/core/mainloop.js +692 -1303
  125. package/dist/psql/core/prompt.js +391 -408
  126. package/dist/psql/core/settings.js +429 -644
  127. package/dist/psql/core/sqlHelp.js +480 -554
  128. package/dist/psql/core/startup.js +2 -846
  129. package/dist/psql/core/syncVars.js +67 -110
  130. package/dist/psql/core/variables.js +156 -278
  131. package/dist/psql/describe/formatters.js +884 -1285
  132. package/dist/psql/describe/processNamePattern.js +173 -260
  133. package/dist/psql/describe/queries.js +1368 -2403
  134. package/dist/psql/describe/versionGate.js +32 -41
  135. package/dist/psql/index.js +2 -2030
  136. package/dist/psql/io/history.js +232 -271
  137. package/dist/psql/io/input.js +103 -108
  138. package/dist/psql/io/lineEditor/buffer.js +238 -319
  139. package/dist/psql/io/lineEditor/complete.js +135 -213
  140. package/dist/psql/io/lineEditor/filename.js +139 -148
  141. package/dist/psql/io/lineEditor/index.js +653 -870
  142. package/dist/psql/io/lineEditor/keymap.js +544 -702
  143. package/dist/psql/io/lineEditor/vt100.js +294 -341
  144. package/dist/psql/io/pgpass.js +158 -187
  145. package/dist/psql/io/pgservice.js +146 -183
  146. package/dist/psql/io/psqlrc.js +328 -403
  147. package/dist/psql/print/aligned.js +1020 -1683
  148. package/dist/psql/print/asciidoc.js +180 -214
  149. package/dist/psql/print/crosstab.js +281 -442
  150. package/dist/psql/print/csv.js +48 -70
  151. package/dist/psql/print/html.js +195 -226
  152. package/dist/psql/print/json.js +75 -88
  153. package/dist/psql/print/latex.js +291 -364
  154. package/dist/psql/print/pager.js +171 -242
  155. package/dist/psql/print/troff.js +194 -226
  156. package/dist/psql/print/unaligned.js +69 -95
  157. package/dist/psql/print/units.js +167 -169
  158. package/dist/psql/scanner/slash.js +428 -483
  159. package/dist/psql/scanner/sql.js +445 -889
  160. package/dist/psql/scanner/stringutils.js +309 -379
  161. package/dist/psql/types/index.js +8 -7
  162. package/dist/psql/types/scanner.js +25 -22
  163. package/dist/psql/wire/connection.js +2042 -2803
  164. package/dist/psql/wire/copy.js +84 -100
  165. package/dist/psql/wire/notify.js +39 -59
  166. package/dist/psql/wire/pipeline.js +305 -518
  167. package/dist/psql/wire/protocol.js +349 -417
  168. package/dist/psql/wire/sasl.js +180 -265
  169. package/dist/psql/wire/tls.js +400 -561
  170. package/dist/storage_api.js +115 -129
  171. package/dist/test_utils/fixtures.js +94 -113
  172. package/dist/test_utils/oauth_server.js +10 -7
  173. package/dist/test_utils/project_dir.js +33 -0
  174. package/dist/utils/ai_gateway_notice.js +131 -162
  175. package/dist/utils/api_enums.js +21 -28
  176. package/dist/utils/auth.js +10 -4
  177. package/dist/utils/branch_notice.js +20 -19
  178. package/dist/utils/branch_picker.js +83 -89
  179. package/dist/utils/cli_name.js +15 -12
  180. package/dist/utils/compute_units.js +20 -27
  181. package/dist/utils/config_diff.js +127 -158
  182. package/dist/utils/enrichers.js +95 -148
  183. package/dist/utils/esbuild.js +130 -189
  184. package/dist/utils/flags.js +35 -47
  185. package/dist/utils/formats.js +8 -15
  186. package/dist/utils/git_diff.js +69 -80
  187. package/dist/utils/inspect_db.js +101 -143
  188. package/dist/utils/inspect_queries.js +179 -142
  189. package/dist/utils/middlewares.js +39 -45
  190. package/dist/utils/openapi.js +87 -99
  191. package/dist/utils/package_manager.js +312 -110
  192. package/dist/utils/point_in_time.js +49 -53
  193. package/dist/utils/psql.js +89 -106
  194. package/dist/utils/service_picker.js +55 -58
  195. package/dist/utils/string.js +5 -5
  196. package/dist/utils/ui.js +38 -55
  197. package/dist/utils/write_sync.js +26 -35
  198. package/dist/utils/zip.js +4 -3
  199. package/dist/writer.js +67 -87
  200. package/package.json +11 -6
  201. package/dist/_shared/auth_selection.js +0 -86
  202. package/dist/_shared/credentials.js +0 -209
  203. package/dist/_shared/env-core/env.js +0 -558
  204. package/dist/_shared/env-core/reuse-secrets.js +0 -223
  205. package/dist/_shared/paths.js +0 -148
  206. package/dist/_shared/profiles.js +0 -276
  207. package/dist/_shared/secure_file.js +0 -43
@@ -1,495 +1,430 @@
1
+ import { t as __exportAll } from "../_chunks/rolldown-runtime-D7D4PA-g.js";
2
+ import { isCi } from "../env.js";
3
+ import { log } from "../log.js";
4
+ import { getCliName } from "../utils/cli_name.js";
5
+ import { t as credentialInputs } from "../_chunks/auth_selection-DGgq6ifc.js";
6
+ import { BootstrapInputError, FALLBACK_TEMPLATES, ensureTargetUsable, fetchTemplates, findTemplate, scaffoldTemplate, templateIds } from "../init/bootstrap.js";
7
+ import { DO_NOT_SUBSTITUTE_HINT, formatInstallCommand, inferPackageManager, installArgs, installedPackageManagers, resolvePackageManager, runCommand } from "../utils/package_manager.js";
1
8
  import { existsSync } from "node:fs";
2
9
  import { join, relative, resolve } from "node:path";
3
10
  import chalk from "chalk";
4
11
  import prompts from "prompts";
5
- import { credentialInputs } from "../_shared/auth_selection.js";
6
- import { isCi } from "../env.js";
7
- import { BootstrapInputError, ensureTargetUsable, FALLBACK_TEMPLATES, fetchTemplates, findTemplate, scaffoldTemplate, templateIds, } from "../init/bootstrap.js";
8
- import { log } from "../log.js";
9
- import { getCliName } from "../utils/cli_name.js";
10
- import { detectPackageManager, installedPackageManagers, runCommand, } from "../utils/package_manager.js";
11
- // The directory positional is optional: omitting it in an interactive terminal
12
- // prompts for one. In a non-interactive context a missing directory is an error.
13
- export const command = "bootstrap [directory]";
14
- export const describe = "Scaffold a new project from a Neon starter template";
15
- export const builder = (argv) => argv
16
- .usage("$0 bootstrap [directory] [options]")
17
- .positional("directory", {
18
- describe: 'Directory to scaffold into. Use "." for the current directory. Omit to be prompted.',
19
- type: "string",
20
- })
21
- .options({
22
- template: {
23
- describe: "Template to use (skips the interactive picker). Run with --list-templates to see available templates.",
24
- type: "string",
25
- },
26
- "list-templates": {
27
- alias: ["list", "ls"],
28
- describe: "List available templates and exit.",
29
- type: "boolean",
30
- default: false,
31
- },
32
- force: {
33
- describe: "Scaffold into the target directory even if it is not empty (colliding files are overwritten).",
34
- type: "boolean",
35
- default: false,
36
- },
37
- agent: {
38
- describe: "Emit a JSON state-machine response designed for AI agents instead of prompting. The output is a single JSON object with a discriminated `status` field describing the next step.",
39
- type: "boolean",
40
- default: false,
41
- },
42
- default: {
43
- alias: "y",
44
- describe: "Quick start: scaffold the default template (or --template) and run the usual setup (install dependencies, git init) without prompting. Linking is left to you since it needs a project choice.",
45
- type: "boolean",
46
- default: false,
47
- },
48
- install: {
49
- describe: "Install dependencies after scaffolding. In interactive mode this is offered as a prompt; use --no-install to skip without being asked.",
50
- type: "boolean",
51
- default: true,
52
- },
53
- git: {
54
- describe: "Initialize a git repository after scaffolding. In interactive mode this is offered as a prompt; use --no-git to skip without being asked.",
55
- type: "boolean",
56
- default: true,
57
- },
58
- link: {
59
- describe: `Run \`${getCliName()} link\` in the scaffolded directory after installing. In interactive mode this is offered as a prompt; use --no-link to skip without being asked.`,
60
- type: "boolean",
61
- default: true,
62
- },
63
- })
64
- .example("$0 bootstrap my-app", "Create ./my-app from an interactively chosen template")
65
- .example("$0 bootstrap . --template hono", "Scaffold the Hono template into the current directory")
66
- .example("$0 bootstrap my-app --default", "Quick start: scaffold the default template and run setup without prompting")
67
- .example("$0 bootstrap my-app --template hono --agent", "Scaffold without prompting and emit the JSON state machine for AI agents")
68
- .strict();
69
- export const handler = async (props) => {
70
- if (props.listTemplates) {
71
- const templates = await fetchTemplates();
72
- for (const t of templates) {
73
- const services = t.services && t.services.length > 0
74
- ? ` [${t.services.join(" · ")}]`
75
- : "";
76
- process.stdout.write(`${t.id} — ${t.description}${services}\n`);
77
- }
78
- return;
79
- }
80
- if (props.agent) {
81
- await runAgentSafely(props);
82
- return;
83
- }
84
- const templates = await resolveTemplateList(props);
85
- // --default is a non-interactive quick start: it fills in the template and
86
- // directory and runs setup without asking, so it must not fall into the
87
- // prompt path even on a TTY.
88
- const interactive = !props.default && Boolean(process.stdout.isTTY) && !isCi();
89
- const template = await resolveSelectedTemplate(props, interactive, templates);
90
- const targetDir = await resolveTargetDir(props, interactive, template);
91
- ensureTargetUsable(targetDir, props.force);
92
- await scaffold(template, targetDir);
93
- printScaffolded(template, targetDir);
94
- await runPostScaffoldSteps(props, targetDir, interactive);
12
+ //#region src/commands/bootstrap.ts
13
+ var bootstrap_exports = /* @__PURE__ */ __exportAll({
14
+ builder: () => builder,
15
+ command: () => command,
16
+ describe: () => describe,
17
+ handler: () => handler
18
+ });
19
+ const command = "bootstrap [directory]";
20
+ const describe = "Scaffold a new project from a Neon starter template";
21
+ const builder = (argv) => argv.usage("$0 bootstrap [directory] [options]").positional("directory", {
22
+ describe: "Directory to scaffold into. Use \".\" for the current directory. Omit to be prompted.",
23
+ type: "string"
24
+ }).options({
25
+ template: {
26
+ describe: "Template to use (skips the interactive picker). Run with --list-templates to see available templates.",
27
+ type: "string"
28
+ },
29
+ "list-templates": {
30
+ alias: ["list", "ls"],
31
+ describe: "List available templates and exit.",
32
+ type: "boolean",
33
+ default: false
34
+ },
35
+ force: {
36
+ describe: "Scaffold into the target directory even if it is not empty (colliding files are overwritten).",
37
+ type: "boolean",
38
+ default: false
39
+ },
40
+ agent: {
41
+ describe: "Emit a JSON state-machine response designed for AI agents instead of prompting. The output is a single JSON object with a discriminated `status` field describing the next step.",
42
+ type: "boolean",
43
+ default: false
44
+ },
45
+ default: {
46
+ alias: "y",
47
+ describe: "Quick start: scaffold the default template (or --template) and run the usual setup (install dependencies, git init) without prompting. Linking is left to you since it needs a project choice.",
48
+ type: "boolean",
49
+ default: false
50
+ },
51
+ install: {
52
+ describe: "Install dependencies after scaffolding. In interactive mode this is offered as a prompt; use --no-install to skip without being asked.",
53
+ type: "boolean",
54
+ default: true
55
+ },
56
+ git: {
57
+ describe: "Initialize a git repository after scaffolding. In interactive mode this is offered as a prompt; use --no-git to skip without being asked.",
58
+ type: "boolean",
59
+ default: true
60
+ },
61
+ link: {
62
+ describe: `Run \`${getCliName()} link\` in the scaffolded directory after installing. In interactive mode this is offered as a prompt; use --no-link to skip without being asked.`,
63
+ type: "boolean",
64
+ default: true
65
+ }
66
+ }).example("$0 bootstrap my-app", "Create ./my-app from an interactively chosen template").example("$0 bootstrap . --template hono", "Scaffold the Hono template into the current directory").example("$0 bootstrap my-app --default", "Quick start: scaffold the default template and run setup without prompting").example("$0 bootstrap my-app --template hono --agent", "Scaffold without prompting and emit the JSON state machine for AI agents").strict();
67
+ const handler = async (props) => {
68
+ if (props.listTemplates) {
69
+ const templates = await fetchTemplates();
70
+ for (const t of templates) {
71
+ const services = t.services && t.services.length > 0 ? ` [${t.services.join(" · ")}]` : "";
72
+ process.stdout.write(`${t.id} — ${t.description}${services}\n`);
73
+ }
74
+ return;
75
+ }
76
+ if (props.agent) {
77
+ await runAgentSafely(props);
78
+ return;
79
+ }
80
+ const templates = await resolveTemplateList(props);
81
+ const interactive = !props.default && Boolean(process.stdout.isTTY) && !isCi();
82
+ const template = await resolveSelectedTemplate(props, interactive, templates);
83
+ const targetDir = await resolveTargetDir(props, interactive, template);
84
+ ensureTargetUsable(targetDir, props.force);
85
+ await scaffold(template, targetDir);
86
+ printScaffolded(template, targetDir);
87
+ await runPostScaffoldSteps(props, targetDir, interactive);
95
88
  };
96
89
  /**
97
- * The template list to choose from. When --template is given we try the
98
- * built-in fallback list first to avoid a network round-trip, only fetching the
99
- * remote manifest if the id isn't one of the defaults.
100
- */
101
- const resolveTemplateList = async (props) => props.template && findTemplate(FALLBACK_TEMPLATES, props.template)
102
- ? FALLBACK_TEMPLATES
103
- : fetchTemplates();
90
+ * The template list to choose from. When --template is given we try the
91
+ * built-in fallback list first to avoid a network round-trip, only fetching the
92
+ * remote manifest if the id isn't one of the defaults.
93
+ */
94
+ const resolveTemplateList = async (props) => props.template && findTemplate(FALLBACK_TEMPLATES, props.template) ? FALLBACK_TEMPLATES : fetchTemplates();
104
95
  /**
105
- * The picker label for a template: the title first, then the Neon services it
106
- * uses as a dim, italic suffix, e.g. "Hono API … Postgres · Functions". The
107
- * suffix is styled with chalk.dim (and italic) only — never a foreground color —
108
- * so it survives the cyan/underline `prompts` paints over the focused row: dim
109
- * and italic reset with their own SGRs, leaving the row's color and underline
110
- * intact. Descriptions are intentionally omitted to keep the picker uncluttered.
111
- */
96
+ * The picker label for a template: the title first, then the Neon services it
97
+ * uses as a dim, italic suffix, e.g. "Hono API … Postgres · Functions". The
98
+ * suffix is styled with chalk.dim (and italic) only — never a foreground color —
99
+ * so it survives the cyan/underline `prompts` paints over the focused row: dim
100
+ * and italic reset with their own SGRs, leaving the row's color and underline
101
+ * intact. Descriptions are intentionally omitted to keep the picker uncluttered.
102
+ */
112
103
  const formatTemplateTitle = (template) => {
113
- if (!template.services || template.services.length === 0) {
114
- return template.title;
115
- }
116
- return `${template.title} ${chalk.dim.italic(template.services.join(" · "))}`;
104
+ if (!template.services || template.services.length === 0) return template.title;
105
+ return `${template.title} ${chalk.dim.italic(template.services.join(" · "))}`;
117
106
  };
118
107
  const resolveSelectedTemplate = async (props, interactive, templates) => {
119
- if (props.template) {
120
- const template = findTemplate(templates, props.template);
121
- if (!template) {
122
- throw new Error(`Unknown template "${props.template}". Available templates: ${templateIds(templates)}.`);
123
- }
124
- return template;
125
- }
126
- // --default with no --template falls back to the first (default) template so
127
- // a bare `neon bootstrap my-app --default` works end to end.
128
- if (props.default) {
129
- const fallback = templates[0];
130
- if (!fallback) {
131
- throw new Error("No templates available to scaffold from.");
132
- }
133
- return fallback;
134
- }
135
- if (!interactive) {
136
- throw new Error(`No template selected. Re-run in an interactive terminal to pick one, or pass --template <id>. Available templates: ${templateIds(templates)}.`);
137
- }
138
- const { id } = await prompts({
139
- onState: onPromptState,
140
- type: "select",
141
- name: "id",
142
- message: "Which template would you like to use?",
143
- choices: templates.map((template) => ({
144
- title: formatTemplateTitle(template),
145
- value: template.id,
146
- })),
147
- initial: 0,
148
- });
149
- const template = findTemplate(templates, id);
150
- if (!template) {
151
- throw new Error("No template selected.");
152
- }
153
- return template;
108
+ if (props.template) {
109
+ const template = findTemplate(templates, props.template);
110
+ if (!template) throw new Error(`Unknown template "${props.template}". Available templates: ${templateIds(templates)}.`);
111
+ return template;
112
+ }
113
+ if (props.default) {
114
+ const fallback = templates[0];
115
+ if (!fallback) throw new Error("No templates available to scaffold from.");
116
+ return fallback;
117
+ }
118
+ if (!interactive) throw new Error(`No template selected. Re-run in an interactive terminal to pick one, or pass --template <id>. Available templates: ${templateIds(templates)}.`);
119
+ const { id } = await prompts({
120
+ onState: onPromptState,
121
+ type: "select",
122
+ name: "id",
123
+ message: "Which template would you like to use?",
124
+ choices: templates.map((template) => ({
125
+ title: formatTemplateTitle(template),
126
+ value: template.id
127
+ })),
128
+ initial: 0
129
+ });
130
+ const template = findTemplate(templates, id);
131
+ if (!template) throw new Error("No template selected.");
132
+ return template;
154
133
  };
155
134
  const resolveTargetDir = async (props, interactive, template) => {
156
- let dir = props.directory;
157
- if (dir === undefined) {
158
- // --default supplies a directory (the template's name) so the quick start
159
- // needs nothing but a template.
160
- if (props.default) {
161
- return resolve(process.cwd(), defaultDirName(template));
162
- }
163
- if (!interactive) {
164
- throw new Error(`No target directory given. Pass one, e.g. \`${getCliName()} bootstrap my-app\` (or "." for the current directory).`);
165
- }
166
- const { value } = await prompts({
167
- onState: onPromptState,
168
- type: "text",
169
- name: "value",
170
- message: "Where should we scaffold your project?",
171
- initial: defaultDirName(template),
172
- validate: (input) => input && input.trim().length > 0
173
- ? true
174
- : 'Enter a directory (use "." for the current directory).',
175
- });
176
- dir = String(value).trim();
177
- }
178
- return resolve(process.cwd(), dir === "." ? "" : dir);
135
+ let dir = props.directory;
136
+ if (dir === void 0) {
137
+ if (props.default) return resolve(process.cwd(), defaultDirName(template));
138
+ if (!interactive) throw new Error(`No target directory given. Pass one, e.g. \`${getCliName()} bootstrap my-app\` (or "." for the current directory).`);
139
+ const { value } = await prompts({
140
+ onState: onPromptState,
141
+ type: "text",
142
+ name: "value",
143
+ message: "Where should we scaffold your project?",
144
+ initial: defaultDirName(template),
145
+ validate: (input) => input && input.trim().length > 0 ? true : "Enter a directory (use \".\" for the current directory)."
146
+ });
147
+ dir = String(value).trim();
148
+ }
149
+ return resolve(process.cwd(), dir === "." ? "" : dir);
179
150
  };
180
151
  const defaultDirName = (template) => template.source.subdir.split("/").pop() || template.id;
181
152
  /**
182
- * Download and materialize the template into `targetDir`. The actual
183
- * download/extract/write lives in `src/init/bootstrap.ts`, shared with `neon init`
184
- * (exec-bit and symlink fidelity, graceful symlink fallback); here we just
185
- * frame it with progress logging. Returns the number of files written.
186
- */
153
+ * Download and materialize the template into `targetDir`. The actual
154
+ * download/extract/write lives in `src/init/bootstrap.ts`, shared with `neon init`
155
+ * (exec-bit and symlink fidelity, graceful symlink fallback); here we just
156
+ * frame it with progress logging. Returns the number of files written.
157
+ */
187
158
  const scaffold = async (template, targetDir) => {
188
- log.info('Fetching template "%s" from GitHub…', template.id);
189
- const filesWritten = await scaffoldTemplate(template, targetDir, {
190
- onWarn: (message) => {
191
- log.warning(message);
192
- },
193
- });
194
- log.info("Scaffolded %d files into %s.", filesWritten, targetDir);
195
- return filesWritten;
159
+ log.info("Fetching template \"%s\" from GitHub…", template.id);
160
+ const filesWritten = await scaffoldTemplate(template, targetDir, { onWarn: (message) => {
161
+ log.warning(message);
162
+ } });
163
+ log.info("Scaffolded %d files into %s.", filesWritten, targetDir);
164
+ return filesWritten;
196
165
  };
197
- // ----------------------------------------------------------------------------
198
- // Post-scaffold steps (install dependencies, git init, link to a Neon project)
199
- // ----------------------------------------------------------------------------
200
166
  /**
201
- * After a human scaffold, offer the things you almost always do next: install
202
- * dependencies, initialize a git repo, and link the directory to a Neon
203
- * project. In an interactive terminal each is a y/n prompt (skippable up front
204
- * with --no-install / --no-git / --no-link); `--default` runs install + git
205
- * without asking; otherwise we just print the manual steps so nothing runs
206
- * behind the user's back. Agent mode never reaches here — it returns these as
207
- * structured `next_steps` instead (see {@link runAgent}).
208
- */
167
+ * After a human scaffold, offer the things you almost always do next: install
168
+ * dependencies, initialize a git repo, and link the directory to a Neon
169
+ * project. In an interactive terminal each is a y/n prompt (skippable up front
170
+ * with --no-install / --no-git / --no-link); `--default` runs install + git
171
+ * without asking; otherwise we just print the manual steps so nothing runs
172
+ * behind the user's back. Agent mode never reaches here — it returns these as
173
+ * structured `next_steps` instead (see {@link runAgent}).
174
+ */
209
175
  const runPostScaffoldSteps = async (props, targetDir, interactive) => {
210
- const detected = detectPackageManager();
211
- if (props.default) {
212
- await runDefaultSteps(props, targetDir, detected ?? "npm");
213
- return;
214
- }
215
- if (!interactive) {
216
- printNextSteps(targetDir, detected ?? "npm", {
217
- installed: false,
218
- suggestLink: true,
219
- });
220
- return;
221
- }
222
- // The package manager used for the install (and shown in the closing hint).
223
- // When we couldn't infer it from the invocation we ask, so a globally
224
- // installed `neon` doesn't silently force npm on a bun/pnpm user.
225
- let pm = detected ?? "npm";
226
- let installed = false;
227
- if (props.install && (await confirm(installPrompt(detected)))) {
228
- pm = detected ?? (await selectPackageManager());
229
- installed = await runCommand(pm, ["install"], targetDir);
230
- }
231
- if (props.git &&
232
- !isGitRepo(targetDir) &&
233
- (await confirm("Initialize a git repository?"))) {
234
- await initGitRepo(targetDir);
235
- }
236
- // `neon link` pulls env vars, which loads this project's neon.ts — and that
237
- // evaluation needs the dependencies installed. So when deps weren't installed
238
- // and the scaffold ships a neon.ts, skip the link prompt (it would just fail)
239
- // and tell the user how to finish by hand.
240
- if (props.link) {
241
- if (!installed && hasNeonConfig(targetDir)) {
242
- log.info(`Skipping the Neon link step: \`${getCliName()} link\` reads this project's neon.ts ` +
243
- `to pull env vars, which needs its dependencies. Run \`${pm} install\`, ` +
244
- `then \`${getCliName()} link\`.`);
245
- }
246
- else if (await confirm(`Link this project to a Neon project now? (runs ${getCliName()} link)`)) {
247
- await runNeonLink(props, targetDir);
248
- // link prints its own summary (and pulls env), so end with just the run hint.
249
- printNextSteps(targetDir, pm, { installed, suggestLink: false });
250
- return;
251
- }
252
- }
253
- printNextSteps(targetDir, pm, { installed, suggestLink: true });
176
+ const inferred = inferPackageManager(targetDir);
177
+ const defaultPm = resolvePackageManager(targetDir);
178
+ if (props.default) {
179
+ await runDefaultSteps(props, targetDir, defaultPm);
180
+ return;
181
+ }
182
+ if (!interactive) {
183
+ printNextSteps(targetDir, defaultPm, {
184
+ installed: false,
185
+ suggestLink: true
186
+ });
187
+ return;
188
+ }
189
+ let pm = defaultPm;
190
+ let installed = false;
191
+ if (props.install && await confirm(installPrompt(inferred))) {
192
+ pm = inferred ?? await selectPackageManager();
193
+ installed = await runCommand(pm, installArgs(pm), targetDir);
194
+ }
195
+ if (props.git && !isGitRepo(targetDir) && await confirm("Initialize a git repository?")) await initGitRepo(targetDir);
196
+ if (props.link) {
197
+ if (!installed && hasNeonConfig(targetDir)) log.info(`Skipping the Neon link step: \`${getCliName()} link\` reads this project's neon.ts to pull env vars, which needs its dependencies. Run \`${formatInstallCommand(pm)}\`, then \`${getCliName()} link\`.`);
198
+ else if (await confirm(`Link this project to a Neon project now? (runs ${getCliName()} link)`)) {
199
+ await runNeonLink(props, targetDir);
200
+ printNextSteps(targetDir, pm, {
201
+ installed,
202
+ suggestLink: false
203
+ });
204
+ return;
205
+ }
206
+ }
207
+ printNextSteps(targetDir, pm, {
208
+ installed,
209
+ suggestLink: true
210
+ });
254
211
  };
255
- const installPrompt = (detected) => detected
256
- ? `Install dependencies with ${detected}?`
257
- : "Install dependencies?";
212
+ const installPrompt = (inferred) => inferred ? `Install dependencies with ${inferred}?` : "Install dependencies?";
258
213
  /**
259
- * `--default` quick start: run install + git init without prompting, honoring
260
- * --no-install / --no-git. Linking is intentionally skipped — it needs an
261
- * org/project choice we can't make non-interactively — so we point at it in the
262
- * closing hint instead.
263
- */
214
+ * `--default` quick start: run install + git init without prompting, honoring
215
+ * --no-install / --no-git. Linking is intentionally skipped — it needs an
216
+ * org/project choice we can't make non-interactively — so we point at it in the
217
+ * closing hint instead.
218
+ */
264
219
  const runDefaultSteps = async (props, targetDir, pm) => {
265
- log.info("Quick start (--default): running setup without prompting.");
266
- let installed = false;
267
- if (props.install) {
268
- installed = await runCommand(pm, ["install"], targetDir);
269
- }
270
- if (props.git && !isGitRepo(targetDir)) {
271
- await initGitRepo(targetDir);
272
- }
273
- printNextSteps(targetDir, pm, { installed, suggestLink: true });
220
+ log.info("Quick start (--default): running setup without prompting.");
221
+ let installed = false;
222
+ if (props.install) installed = await runCommand(pm, installArgs(pm), targetDir);
223
+ if (props.git && !isGitRepo(targetDir)) await initGitRepo(targetDir);
224
+ printNextSteps(targetDir, pm, {
225
+ installed,
226
+ suggestLink: true
227
+ });
274
228
  };
275
229
  const isGitRepo = (dir) => existsSync(join(dir, ".git"));
276
- // Config filenames the runtime loads (mirrors @neon/config). A scaffold
277
- // that ships one makes `neon link`'s env pull evaluate it — which needs deps.
278
- const NEON_CONFIG_FILENAMES = ["neon.ts", "neon.mts", "neon.js", "neon.mjs"];
230
+ const NEON_CONFIG_FILENAMES = [
231
+ "neon.ts",
232
+ "neon.mts",
233
+ "neon.js",
234
+ "neon.mjs"
235
+ ];
279
236
  const hasNeonConfig = (dir) => NEON_CONFIG_FILENAMES.some((name) => existsSync(join(dir, name)));
280
237
  /**
281
- * Initialize a git repository in the scaffolded directory. Just `git init` — we
282
- * deliberately don't auto-commit, both to avoid failing on a machine with no
283
- * git identity configured and to leave the first commit to the user.
284
- */
238
+ * Initialize a git repository in the scaffolded directory. Just `git init` — we
239
+ * deliberately don't auto-commit, both to avoid failing on a machine with no
240
+ * git identity configured and to leave the first commit to the user.
241
+ */
285
242
  const initGitRepo = async (dir) => {
286
- await runCommand("git", ["init"], dir);
243
+ await runCommand("git", ["init"], dir);
287
244
  };
288
245
  const confirm = async (message) => {
289
- const { value } = await prompts({
290
- onState: onPromptState,
291
- type: "confirm",
292
- name: "value",
293
- message,
294
- initial: true,
295
- });
296
- return value === true;
246
+ const { value } = await prompts({
247
+ onState: onPromptState,
248
+ type: "confirm",
249
+ name: "value",
250
+ message,
251
+ initial: true
252
+ });
253
+ return value === true;
297
254
  };
298
255
  /**
299
- * Ask which package manager to install with when we couldn't infer one from the
300
- * invocation. Offers the managers actually installed (npm preselected); with
301
- * one or none installed there's nothing to choose, so it returns that one (or
302
- * npm) without prompting. A cancelled prompt falls back to npm.
303
- */
256
+ * Ask which package manager to install with when we couldn't infer one from the
257
+ * invocation. Offers the managers actually installed (npm preselected); with
258
+ * one or none installed there's nothing to choose, so it returns that one (or
259
+ * npm) without prompting. A cancelled prompt falls back to npm.
260
+ */
304
261
  const selectPackageManager = async () => {
305
- const installed = installedPackageManagers();
306
- if (installed.length <= 1) {
307
- return installed[0] ?? "npm";
308
- }
309
- const { pm } = await prompts({
310
- onState: onPromptState,
311
- type: "select",
312
- name: "pm",
313
- message: "Which package manager should we use?",
314
- choices: installed.map((manager) => ({
315
- title: manager,
316
- value: manager,
317
- })),
318
- initial: Math.max(0, installed.indexOf("npm")),
319
- });
320
- return pm ?? "npm";
262
+ const installed = installedPackageManagers();
263
+ if (installed.length <= 1) return installed[0] ?? "npm";
264
+ const { pm } = await prompts({
265
+ onState: onPromptState,
266
+ type: "select",
267
+ name: "pm",
268
+ message: "Which package manager should we use?",
269
+ choices: installed.map((manager) => ({
270
+ title: manager,
271
+ value: manager
272
+ })),
273
+ initial: Math.max(0, installed.indexOf("npm"))
274
+ });
275
+ return pm ?? "npm";
321
276
  };
322
277
  /**
323
- * Re-invoke this same CLI as `neon link` inside the scaffolded directory, so the
324
- * new project's `.neon` context (and pulled `.env`) land in the right place and
325
- * link's own interactive picker drives org/project/branch selection. Re-execing
326
- * (rather than calling the handler in-process) keeps link running with `cwd` set
327
- * to the target dir, which is where its env pull writes.
328
- */
278
+ * Re-invoke this same CLI as `neon link` inside the scaffolded directory, so the
279
+ * new project's `.neon` context (and pulled `.env`) land in the right place and
280
+ * link's own interactive picker drives org/project/branch selection. Re-execing
281
+ * (rather than calling the handler in-process) keeps link running with `cwd` set
282
+ * to the target dir, which is where its env pull writes.
283
+ */
329
284
  const runNeonLink = async (props, targetDir) => {
330
- const args = [process.argv[1], "link"];
331
- // Forward how to authenticate, never the credential itself. A key on the child's argv is
332
- // visible to anything that can list processes, and `runCommand` prints the whole argument
333
- // list when the command fails — so a failed link would put the key in the log too.
334
- // Forwarding the selection also keeps the child on the same account: without `--profile` it
335
- // would resolve its own, and `--config-dir` was never forwarded at all.
336
- if (props.configDir) {
337
- args.push("--config-dir", props.configDir);
338
- }
339
- if (props.profile) {
340
- args.push("--profile", props.profile);
341
- }
342
- args.push("--api-host", props.apiHost, "--output", props.output);
343
- // An explicit `--api-key` has nowhere else to go — the child cannot re-resolve a flag we
344
- // were given — so it travels in the environment, which neither `ps` nor our own logging
345
- // exposes. A profile needs none of this; the child reads the credential itself.
346
- const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
347
- await runCommand(process.execPath, args, targetDir, explicitKey ? { NEON_API_KEY: explicitKey } : undefined);
285
+ const args = [process.argv[1], "link"];
286
+ if (props.configDir) args.push("--config-dir", props.configDir);
287
+ if (props.profile) args.push("--profile", props.profile);
288
+ args.push("--api-host", props.apiHost, "--output", props.output);
289
+ const explicitKey = props.profile ? "" : credentialInputs().apiKeyFlag;
290
+ await runCommand(process.execPath, args, targetDir, explicitKey ? { NEON_API_KEY: explicitKey } : void 0);
348
291
  };
349
292
  const printScaffolded = (template, targetDir) => {
350
- log.info("");
351
- log.info('Done. Scaffolded "%s" into %s.', template.title, isCurrentDir(targetDir)
352
- ? "the current directory"
353
- : displayDir(targetDir));
293
+ log.info("");
294
+ log.info("Done. Scaffolded \"%s\" into %s.", template.title, isCurrentDir(targetDir) ? "the current directory" : displayDir(targetDir));
354
295
  };
355
296
  /**
356
- * The closing "Next steps" hint. Skips `cd` for the current directory, omits
357
- * the install line once deps are in, and only nudges `neon link` when linking
358
- * wasn't already offered/run — so the user never sees a step they just did.
359
- */
297
+ * The closing "Next steps" hint. Skips `cd` for the current directory, omits
298
+ * the install line once deps are in, and only nudges `neon link` when linking
299
+ * wasn't already offered/run — so the user never sees a step they just did.
300
+ */
360
301
  const printNextSteps = (targetDir, pm, opts) => {
361
- log.info("");
362
- log.info("Next steps:");
363
- if (!isCurrentDir(targetDir)) {
364
- log.info(" cd %s", displayDir(targetDir));
365
- }
366
- if (!opts.installed) {
367
- log.info(" %s install", pm);
368
- }
369
- if (opts.suggestLink) {
370
- log.info(` ${getCliName()} link`);
371
- }
372
- log.info(" See the README to run it.");
373
- log.info("");
302
+ log.info("");
303
+ log.info("Next steps:");
304
+ if (!isCurrentDir(targetDir)) log.info(" cd %s", displayDir(targetDir));
305
+ if (!opts.installed) log.info(" %s", formatInstallCommand(pm));
306
+ if (opts.suggestLink) log.info(` ${getCliName()} link`);
307
+ log.info(" See the README to run it.");
308
+ log.info("");
374
309
  };
375
310
  const runAgentSafely = async (props) => {
376
- try {
377
- await runAgent(props);
378
- }
379
- catch (err) {
380
- emitAgent(toAgentError(err));
381
- process.exit(1);
382
- }
311
+ try {
312
+ await runAgent(props);
313
+ } catch (err) {
314
+ emitAgent(toAgentError(err));
315
+ process.exit(1);
316
+ }
383
317
  };
384
318
  /**
385
- * The `--agent` flow: resolve what the flags determine and emit one JSON object
386
- * describing either the next input needed (`needs_template` / `needs_directory`)
387
- * or the terminal result (`scaffolded`). Unlike interactive mode it never
388
- * prompts and never runs install/git/link itself — those come back as structured
389
- * `next_steps` so the agent can confirm with the user and run them (the link
390
- * step chains into `neon link --agent`).
391
- */
319
+ * The `--agent` flow: resolve what the flags determine and emit one JSON object
320
+ * describing either the next input needed (`needs_template` / `needs_directory`)
321
+ * or the terminal result (`scaffolded`). Unlike interactive mode it never
322
+ * prompts and never runs install/git/link itself — those come back as structured
323
+ * `next_steps` so the agent can confirm with the user and run them (the link
324
+ * step chains into `neon link --agent`).
325
+ */
392
326
  const runAgent = async (props) => {
393
- if (!props.template) {
394
- const templates = await fetchTemplates();
395
- emitAgent({
396
- status: "needs_template",
397
- instruction: `Ask the user which template to scaffold, then re-run the next_command_template with the chosen --template value${props.directory ? "" : " and a target directory"}.`,
398
- options: templates.map((template) => ({
399
- id: template.id,
400
- title: template.title,
401
- description: template.description,
402
- ...(template.services ? { services: template.services } : {}),
403
- })),
404
- next_command_template: `${getCliName()} bootstrap --agent ${props.directory ? shellArg(props.directory) : "<directory>"} --template <template_id>`,
405
- });
406
- return;
407
- }
408
- const templates = await resolveTemplateList(props);
409
- const template = findTemplate(templates, props.template);
410
- if (!template) {
411
- throw new BootstrapInputError(`Unknown template "${props.template}". Available templates: ${templateIds(templates)}.`, "UNKNOWN_TEMPLATE");
412
- }
413
- if (props.directory === undefined) {
414
- emitAgent({
415
- status: "needs_directory",
416
- instruction: 'Ask the user which directory to scaffold into (use "." for the current directory), then re-run the next_command_template with it.',
417
- next_command_template: `${getCliName()} bootstrap --agent <directory> --template ${shellArg(template.id)}`,
418
- });
419
- return;
420
- }
421
- const targetDir = resolve(process.cwd(), props.directory === "." ? "" : props.directory);
422
- ensureTargetUsable(targetDir, props.force);
423
- const filesWritten = await scaffold(template, targetDir);
424
- const dir = displayDir(targetDir);
425
- const runIn = isCurrentDir(targetDir) ? "" : `cd ${shellArg(dir)} && `;
426
- emitAgent({
427
- status: "scaffolded",
428
- directory: targetDir,
429
- template: { id: template.id, title: template.title },
430
- files_written: filesWritten,
431
- next_steps: [
432
- {
433
- action: "install_dependencies",
434
- instruction: "Ask the user whether to install dependencies, then run this in the project directory.",
435
- command: `${runIn}npm install`,
436
- },
437
- {
438
- action: "initialize_git",
439
- instruction: "Ask the user whether to initialize a git repository in the project directory.",
440
- command: `${runIn}git init`,
441
- },
442
- {
443
- action: "link_neon_project",
444
- instruction: "Ask the user whether to link the project to a Neon project now. This runs the link state machine — follow its JSON output for the next step.",
445
- command: `${runIn}${getCliName()} link --agent`,
446
- },
447
- ],
448
- message: `Scaffolded "${template.title}" (${filesWritten} files) into ${dir}. Offer the next_steps to the user: install dependencies, initialize git, then link a Neon project.`,
449
- });
327
+ if (!props.template) {
328
+ const templates = await fetchTemplates();
329
+ emitAgent({
330
+ status: "needs_template",
331
+ instruction: `Ask the user which template to scaffold, then re-run the next_command_template with the chosen --template value${props.directory ? "" : " and a target directory"}.`,
332
+ options: templates.map((template) => ({
333
+ id: template.id,
334
+ title: template.title,
335
+ description: template.description,
336
+ ...template.services ? { services: template.services } : {}
337
+ })),
338
+ next_command_template: `${getCliName()} bootstrap --agent ${props.directory ? shellArg(props.directory) : "<directory>"} --template <template_id>`
339
+ });
340
+ return;
341
+ }
342
+ const templates = await resolveTemplateList(props);
343
+ const template = findTemplate(templates, props.template);
344
+ if (!template) throw new BootstrapInputError(`Unknown template "${props.template}". Available templates: ${templateIds(templates)}.`, "UNKNOWN_TEMPLATE");
345
+ if (props.directory === void 0) {
346
+ emitAgent({
347
+ status: "needs_directory",
348
+ instruction: "Ask the user which directory to scaffold into (use \".\" for the current directory), then re-run the next_command_template with it.",
349
+ next_command_template: `${getCliName()} bootstrap --agent <directory> --template ${shellArg(template.id)}`
350
+ });
351
+ return;
352
+ }
353
+ const targetDir = resolve(process.cwd(), props.directory === "." ? "" : props.directory);
354
+ ensureTargetUsable(targetDir, props.force);
355
+ const filesWritten = await scaffold(template, targetDir);
356
+ const dir = displayDir(targetDir);
357
+ const runIn = isCurrentDir(targetDir) ? "" : `cd ${shellArg(dir)} && `;
358
+ const installPm = resolvePackageManager(targetDir);
359
+ emitAgent({
360
+ status: "scaffolded",
361
+ directory: targetDir,
362
+ template: {
363
+ id: template.id,
364
+ title: template.title
365
+ },
366
+ files_written: filesWritten,
367
+ next_steps: [
368
+ {
369
+ action: "install_dependencies",
370
+ instruction: `Ask the user whether to install dependencies, then run this in the project directory. ${DO_NOT_SUBSTITUTE_HINT}`,
371
+ command: `${runIn}${formatInstallCommand(installPm)}`
372
+ },
373
+ {
374
+ action: "initialize_git",
375
+ instruction: "Ask the user whether to initialize a git repository in the project directory.",
376
+ command: `${runIn}git init`
377
+ },
378
+ {
379
+ action: "link_neon_project",
380
+ instruction: "Ask the user whether to link the project to a Neon project now. This runs the link state machine — follow its JSON output for the next step.",
381
+ command: `${runIn}${getCliName()} link --agent`
382
+ }
383
+ ],
384
+ message: `Scaffolded "${template.title}" (${filesWritten} files) into ${dir}. Offer the next_steps to the user: install dependencies, initialize git, then link a Neon project.`
385
+ });
450
386
  };
451
387
  const emitAgent = (response) => {
452
- process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
388
+ process.stdout.write(`${JSON.stringify(response, null, 2)}\n`);
453
389
  };
454
390
  const toAgentError = (err) => {
455
- if (err instanceof BootstrapInputError) {
456
- return { status: "error", code: err.agentCode, message: err.message };
457
- }
458
- if (err instanceof Error) {
459
- return {
460
- status: "error",
461
- code: "INTERNAL_ERROR",
462
- message: err.message,
463
- };
464
- }
465
- return { status: "error", code: "INTERNAL_ERROR", message: String(err) };
391
+ if (err instanceof BootstrapInputError) return {
392
+ status: "error",
393
+ code: err.agentCode,
394
+ message: err.message
395
+ };
396
+ if (err instanceof Error) return {
397
+ status: "error",
398
+ code: "INTERNAL_ERROR",
399
+ message: err.message
400
+ };
401
+ return {
402
+ status: "error",
403
+ code: "INTERNAL_ERROR",
404
+ message: String(err)
405
+ };
466
406
  };
467
- // ----------------------------------------------------------------------------
468
- // Path display helpers
469
- // ----------------------------------------------------------------------------
470
407
  const isCurrentDir = (targetDir) => relative(process.cwd(), targetDir) === "";
471
408
  /**
472
- * The path to show the user: the bare relative path for the common
473
- * `bootstrap my-app` case, the absolute path when the target sits outside the
474
- * cwd (a deep `../../..` is noise), and "." for the current directory.
475
- */
409
+ * The path to show the user: the bare relative path for the common
410
+ * `bootstrap my-app` case, the absolute path when the target sits outside the
411
+ * cwd (a deep `../../..` is noise), and "." for the current directory.
412
+ */
476
413
  const displayDir = (targetDir) => {
477
- const rel = relative(process.cwd(), targetDir);
478
- if (rel === "") {
479
- return ".";
480
- }
481
- return rel.startsWith("..") ? targetDir : rel;
414
+ const rel = relative(process.cwd(), targetDir);
415
+ if (rel === "") return ".";
416
+ return rel.startsWith("..") ? targetDir : rel;
482
417
  };
483
418
  const shellArg = (value) => {
484
- if (/^[A-Za-z0-9._:/-]+$/.test(value)) {
485
- return value;
486
- }
487
- return `'${value.replace(/'/g, `'\\''`)}'`;
419
+ if (/^[A-Za-z0-9._:/-]+$/.test(value)) return value;
420
+ return `'${value.replace(/'/g, `'\\''`)}'`;
488
421
  };
489
422
  const onPromptState = (state) => {
490
- if (state.aborted) {
491
- process.stdout.write("\x1B[?25h");
492
- process.stdout.write("\n");
493
- process.exit(1);
494
- }
423
+ if (state.aborted) {
424
+ process.stdout.write("\x1B[?25h");
425
+ process.stdout.write("\n");
426
+ process.exit(1);
427
+ }
495
428
  };
429
+ //#endregion
430
+ export { builder, command, describe, handler, bootstrap_exports as t };