create-warlock 5.32.0 → 5.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/CHANGELOG.md +26 -47
  2. package/README.md +63 -39
  3. package/esm/commands/create-new-app/index.mjs +10 -9
  4. package/esm/commands/create-new-app/index.mjs.map +1 -1
  5. package/esm/commands/create-new-app/types.d.mts +1 -1
  6. package/esm/commands/create-warlock-app/index.mjs +1 -1
  7. package/esm/commands/create-warlock-app/index.mjs.map +1 -1
  8. package/esm/flags/definitions.mjs +2 -2
  9. package/esm/flags/definitions.mjs.map +1 -1
  10. package/esm/flags/seed-from-flags.mjs +1 -1
  11. package/esm/flags/seed-from-flags.mjs.map +1 -1
  12. package/esm/helpers/app.mjs +10 -25
  13. package/esm/helpers/app.mjs.map +1 -1
  14. package/esm/helpers/insert-import.mjs +2 -2
  15. package/esm/helpers/insert-import.mjs.map +1 -1
  16. package/llms-full.txt +62 -61
  17. package/package.json +2 -2
  18. package/skills/create-a-warlock-project/SKILL.md +62 -61
  19. package/templates/{warlock/.prettierignore → toolchains/oxc/.oxfmtignore} +0 -2
  20. package/templates/toolchains/oxc/.oxlintrc.json +1 -1
  21. package/templates/warlock/.oxfmtignore +2 -0
  22. package/templates/warlock/.oxfmtrc.json +15 -0
  23. package/templates/warlock/.oxlintrc.json +24 -0
  24. package/templates/warlock/.vscode/extensions.json +3 -0
  25. package/templates/warlock/.vscode/settings.json +11 -17
  26. package/templates/warlock/README.md +18 -20
  27. package/templates/warlock/package.json +7 -14
  28. package/templates/warlock/skills/code-standards/SKILL.md +53 -36
  29. package/templates/warlock/skills/git-workflow/SKILL.md +25 -20
  30. package/templates/warlock/skills/module-boundaries/SKILL.md +33 -24
  31. package/templates/warlock/skills/web-code-standards/SKILL.md +1 -1
  32. package/templates/warlock/src/web/home/components/home-footer.tsx +3 -1
  33. package/esm/prompts/ask-lint-toolchain.mjs +0 -21
  34. package/esm/prompts/ask-lint-toolchain.mjs.map +0 -1
  35. package/templates/warlock/.prettierrc.json +0 -12
  36. package/templates/warlock/eslint.config.js +0 -98
package/CHANGELOG.md CHANGED
@@ -4,6 +4,30 @@ All notable changes to `create-warlock` are documented in this file.
4
4
 
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
6
6
 
7
+ ## 5.34.0 - 2026-10-19
8
+
9
+ ### Removed
10
+
11
+ - **BREAKING** Remove the ESLint/Prettier scaffold, prompt, configs, dependencies and TypeScript 6.0.3 compatibility pin. `--lint eslint` fails before scaffolding and names 5.34.0 and the Oxlint/Oxfmt default. Remove Prettier compatibility scripts and validation paths.
12
+
13
+ ### Changed
14
+
15
+ - **BREAKING** Enable Oxlint `correctness: error` in the only app template. Move the footer's year calculation outside render to satisfy its purity check. Standalone specs use locally declared, pinned Oxlint/Oxfmt tools.
16
+
17
+ ### Upgrading
18
+
19
+ - Omit `--lint` or pass `--lint=oxc`. Existing apps use `npx warlock upgrade --from 5.33 --to 5.34.0`; review custom-rule findings and additional correctness diagnostics before committing.
20
+
21
+ ## 5.33.0 - 2026-10-19
22
+
23
+ ### Changed
24
+
25
+ - Label the explicit ESLint/Prettier prompt choice, flag help and docs as deprecated; retain TypeScript 6.0.3 compatibility and explicitly enable its dev-server checker. Package development scripts use root Oxlint/Oxfmt, and the template formatting gate defaults to Oxfmt. Oxc scripts explicitly select `.oxfmtignore`.
26
+
27
+ ### Deprecated
28
+
29
+ - `--lint=eslint` prints a clear removal warning for 5.34.0, recommends `--lint=oxc` and names the existing-app migration command. Explicit Prettier compatibility scripts also warn.
30
+
7
31
  ## 5.32.0 - 2026-10-18
8
32
 
9
33
  ### Changed
@@ -60,7 +84,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
60
84
 
61
85
  ### Changed
62
86
 
63
- - Lockstep release; package APIs are unchanged. Skills refer to other topics by name instead of file paths, so cross-references keep working after `agent-kit` copies them into an app.
87
+ - Skills refer to other topics by name instead of file paths, so cross-references keep working after `agent-kit` copies them into an app.
64
88
 
65
89
  ## 5.28.0 - 2026-10-01
66
90
 
@@ -73,7 +97,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
73
97
 
74
98
  ### Changed
75
99
 
76
- - Generated apps depend on `@mongez/supportive-is` ^3.0.0 (type-predicate checkers). Lockstep release; package APIs are otherwise unchanged.
100
+ - Generated apps depend on `@mongez/supportive-is` ^3.0.0 (type-predicate checkers).
77
101
 
78
102
  ## 5.26.0 - 2026-09-30
79
103
 
@@ -93,24 +117,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
93
117
 
94
118
  - `docs/new-module.md` in the generated app named `warlock create.module`; it is `warlock generate.module`.
95
119
 
96
- ## 5.24.0 - 2026-09-27
97
-
98
- ### Changed
99
-
100
- - Lockstep release; package APIs are unchanged.
101
-
102
- ## 5.23.2 - 2026-09-26
103
-
104
- ### Changed
105
-
106
- - Lockstep patch release; package APIs are unchanged.
107
-
108
- ## 5.23.1 - 2026-09-26
109
-
110
- ### Changed
111
-
112
- - Lockstep patch release; package APIs are unchanged.
113
-
114
120
  ## 5.23.0 - 2026-09-25
115
121
 
116
122
  ### Fixed
@@ -124,24 +130,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
124
130
 
125
131
  - Template schemas and models drop redundant `.required()` and non-load-bearing `satisfies`, matching the framework skills.
126
132
 
127
- ## 5.22.1 - 2026-09-25
128
-
129
- ### Changed
130
-
131
- - Lockstep patch release; package APIs are unchanged.
132
-
133
- ## 5.22.0 - 2026-09-25
134
-
135
- ### Changed
136
-
137
- - Lockstep release maintenance and dependency refresh.
138
-
139
- ## 5.21.0 - 2026-09-25
140
-
141
- ### Changed
142
-
143
- - Lockstep release maintenance and dependency refresh.
144
-
145
133
  ## 5.20.0 - 2026-09-24
146
134
 
147
135
  ### Changed
@@ -283,10 +271,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
283
271
 
284
272
  - A postgres scaffold shipped `src/config/database.postgres.ts` unformatted, so a new project's first dev run flagged it. The template is formatted, and the template-format check now runs as part of `create-warlock`'s own test suite.
285
273
 
286
- ## 5.9.0 - 2026-09-13
287
-
288
- _Released in lockstep with the `@warlock.js/*` family; no package-specific changes in 5.9.0._
289
-
290
274
  ## 5.7.0 - 2026-09-11
291
275
 
292
276
  ### Fixed
@@ -385,11 +369,6 @@ _Released in lockstep with the `@warlock.js/*` family; no package-specific chang
385
369
  - Replaced stale scaffold values that had been carried forward: the `wow2` project name
386
370
  and the `4.15.0` dependency version no longer appear in generated projects.
387
371
 
388
- ## 5.0.2 - 2026-08-25
389
-
390
- No changes to `create-warlock`. Released in lockstep with the `@warlock.js/web` SSR fix
391
- (`ssr.noExternal`) — see that package's changelog.
392
-
393
372
  ## 5.0.1 - 2026-08-25
394
373
 
395
374
  ### Fixed
package/README.md CHANGED
@@ -40,7 +40,7 @@ was decided prints after scaffolding, along with how to change each one.
40
40
  | `--stack=<api\|web>` | API-only or full-stack web; the structural choice | `api` |
41
41
  | `--db=<driver>` / `--no-db` | Database driver (`postgres`, `mongodb`, …), or skip one entirely | `mongodb` |
42
42
  | `--features=<list>` | Comma-separated feature keys (e.g. `test,herald`) | none (or `["web"]` when `--stack=web` and `--features` is not given) |
43
- | `--lint=<oxc\|eslint>` | Development lint/format toolchain: Oxlint/Oxfmt or ESLint/Prettier | `oxc` |
43
+ | `--lint=<oxc>` | Oxlint/Oxfmt (only supported toolchain) | `oxc` |
44
44
  | `--ai=<list>` | Comma-separated AI provider keys (e.g. `openai,anthropic`) | none |
45
45
  | `--pm=<manager>` | Package manager (`npm`, `yarn`, `pnpm`, `bun`) | inferred from the invoking agent (`npm_config_user_agent`) or the system |
46
46
  | `--agents=<list>` | Comma-separated [agent-kit](https://mongez.js.org/agent-kit) targets | `claude` |
@@ -174,43 +174,28 @@ The decisions overview shows the total selected feature/AI count and up to six
174
174
  keys, followed by `+N more` for longer selections. The final success screen lists
175
175
  all selected feature keys.
176
176
 
177
- ### Default Oxc tooling
177
+ ### Oxlint and Oxfmt
178
178
 
179
- New projects default to `--lint=oxc` (also `--lint oxc`), with Oxlint **1.88.0**
180
- and Oxfmt **0.73.0**. The full `--customize` wizard offers the same choice and
181
- preselects any supplied flag. Pass `--lint=eslint` to opt in to ESLint/Prettier.
182
- Unknown or missing `--lint` values fail before copying the project.
179
+ Oxlint **1.88.0** and Oxfmt **0.73.0** are the only supported tools. New apps
180
+ pin TypeScript **7.0.2** and include `.oxlintrc.json`, `.oxfmtrc.json`,
181
+ `.oxfmtignore`, and VS Code settings for `oxc.oxc-vscode`.
182
+ `--lint=oxc` is optional; `--lint=eslint` fails with the 5.34.0 removal message.
183
+ The wizard has no toolchain choice.
183
184
 
184
185
  ```bash
185
- # Default: Oxlint + Oxfmt, TypeScript 7.0.2
186
186
  npm create warlock@latest my-app -- --yes
187
-
188
- # Explicit opt-in: ESLint + Prettier, TypeScript 6.0.3
189
- npm create warlock@latest my-eslint-app -- --lint=eslint --yes
187
+ cd my-app
188
+ npm run lint:check
189
+ npm run format:check
190
190
  ```
191
191
 
192
- Oxc projects receive `.oxlintrc.json` and `.oxfmtrc.json`, replace the template's
193
- ESLint/Prettier dependencies and configs, and enable the Oxlint development health
194
- checker while retaining TypeScript checks. Both choices provide `lint`, `format`,
195
- `lint:check` and `format:check` scripts. The check variants do not modify files.
196
- Oxc projects also receive VS Code format-on-save settings and an extension
197
- recommendation for [Oxc](https://marketplace.visualstudio.com/items?itemName=oxc.oxc-vscode).
198
- Its formatter is `oxc.oxc-vscode` and lint fixes use `source.fixAll.oxc`;
199
- ESLint projects retain their Prettier/ESLint editor settings.
200
-
201
- The Oxlint config preserves the template's unused-variable/import checks,
202
- explicit class accessibility and type-import style. Oxfmt uses the same 100-column,
203
- double-quote, semicolon and trailing-comma settings, and sorts imports with
204
- `"sortImports": { "newlinesBetween": false, "internalPattern": ["app/", "web/", "@shared/"] }` — built-ins, packages, internal aliases, then relative
205
- paths, in one block like `@ianvs/prettier-plugin-sort-imports` in the Prettier
206
- toolchain. The Prettier config also groups internal aliases before relatives and
207
- keeps type imports separate. Its Babel parser works with TypeScript 7 without
208
- using the compiler API. Neither formatter deletes unused imports; ESLint’s
209
- `unused-imports/no-unused-imports` or Oxlint’s `no-unused-vars` reports them. Every generated script calls the local binary
210
- (`eslint`, `oxlint`, `prettier`, `oxfmt`, `tsc`) rather than `npx`. See the
211
- [Oxlint config reference](https://oxc.rs/docs/guide/usage/linter/config-file-reference)
212
- and [Oxfmt config reference](https://oxc.rs/docs/guide/usage/formatter/config-file-reference)
213
- when adding project rules.
192
+ Oxlint enables `"categories": { "correctness": "error" }`, unused-variable/import
193
+ checks, explicit class accessibility and type-import style. Oxfmt sorts imports
194
+ with `"sortImports": { "newlinesBetween": false, "internalPattern": ["app/", "web/", "@shared/"] }`.
195
+ It keeps built-ins, packages, internal aliases and relative imports in one block.
196
+ Formatting retains unused imports; lint reports them. Local package scripts use
197
+ `oxlint`, `oxfmt` and `tsc` directly. The non-mutating scripts are `lint:check`
198
+ and `format:check`. Background checks run TypeScript and configured Oxlint.
214
199
 
215
200
  ## TypeScript 7 in 5.32
216
201
 
@@ -220,10 +205,49 @@ Node types, and `paths` relative to its tsconfig, without `baseUrl`.
220
205
  Existing apps must remove `baseUrl`, rebase alias targets, and replace
221
206
  `moduleResolution: "node"` / `"node10"` before typechecking.
222
207
 
223
- Explicit `--lint=eslint` projects pin `typescript` to **6.0.3**, the newest version
224
- supported by typescript-eslint. Its parser requires the JavaScript compiler API
225
- removed in TypeScript 7, and its declared peer range is `>=4.8.4 <6.1.0`
226
- ([parser package metadata](https://registry.npmjs.org/@typescript-eslint/parser/8.71.2)).
227
- Both modes use the same tsconfig settings above, which are valid in TypeScript 6.
228
- ESLint mode prints a compatibility notice when chosen and is planned for
229
- deprecation in 5.33 and removal in 5.34. Both modes install with npm, pnpm and yarn.
208
+ ## Migrate existing apps
209
+
210
+ ESLint and Prettier support was removed in **5.34.0**. Oxlint/Oxfmt is the only
211
+ supported toolchain. `--lint eslint` and `healthCheckers.eslint` are removed.
212
+ Install the new core release and `ts-morph`, then migrate an existing app:
213
+
214
+ ```bash
215
+ npm install --save-dev ts-morph
216
+ npx warlock upgrade --from 5.33 --to 5.34.0 --dry-run
217
+ npx warlock upgrade --from 5.33 --to 5.34.0
218
+ npm install
219
+ npm run format
220
+ npm run lint:check
221
+ npx warlock check
222
+ ```
223
+
224
+ The `oxc-toolchain-removal` step reapplies the preserved 5.33 `oxc-toolchain`
225
+ migration when legacy configs, scripts or dependencies remain. Apps upgrading
226
+ from 5.32 can still use `npx warlock upgrade --from 5.32 --to 5.33.0` first.
227
+ It replaces standard scripts/dependencies, pins TypeScript 7.0.2, updates generated
228
+ editor settings, removes `healthCheckers.eslint` and selects Oxlint. A generated
229
+ `.prettierignore` becomes `.oxfmtignore`; Oxfmt scripts read it explicitly.
230
+
231
+ Literal custom rules are translated without executing the config. Per-folder
232
+ `no-restricted-imports` and `no-restricted-exports` become Oxlint overrides with
233
+ file/exclusion patterns. Supported `@typescript-eslint/*` rules become native
234
+ `typescript/*` rules (or native base rules). Options are checked against the
235
+ pinned Oxlint 1.88.0 schema. Type-aware equivalents enable `options.typeAware`
236
+ and add `oxlint-tsgolint@7.0.2004` to the app's dev dependencies.
237
+ `unused-imports/no-unused-imports` uses `no-unused-vars` with safe import fixes;
238
+ Oxlint shares the severity for imports and variables, so differing active
239
+ severities are reported for review.
240
+
241
+ Only unsupported rules/options, computed or imported settings, selectors such
242
+ as `no-restricted-syntax`, and conflicting existing Oxc rules need manual review.
243
+ Each finding names its source `file:line`, rule and affected file patterns.
244
+ Fully ported lint configs are removed; configs with findings remain for reference
245
+ until those findings are addressed. Existing Oxc rules are kept on conflict.
246
+ `--dry-run --json` reports the same plan without writes. Customized formatting,
247
+ editor settings and nonstandard scripts can also require review.
248
+
249
+ **Upgrading:** new and migrated template configs enable
250
+ `"categories": { "correctness": "error" }`. Address the additional diagnostics;
251
+ existing Oxc configs retain their categories. A real lockfile beside a workspace
252
+ marker determines the installer: `package-lock.json` plus `pnpm-workspace.yaml`
253
+ without `pnpm-lock.yaml` now produces `npm install`.
@@ -11,7 +11,6 @@ import { App } from "../../helpers/app.mjs";
11
11
  import { askAgentTargets } from "../../prompts/ask-agent-targets.mjs";
12
12
  import { askAiFeatures } from "../../prompts/ask-ai-features.mjs";
13
13
  import { askFeatures } from "../../prompts/ask-features.mjs";
14
- import { askLintToolchain } from "../../prompts/ask-lint-toolchain.mjs";
15
14
  import { askProjectName } from "../../prompts/ask-project-name.mjs";
16
15
  import { askStack } from "../../prompts/ask-stack.mjs";
17
16
  import { showIntroBanner } from "../../ui/banner.mjs";
@@ -23,6 +22,7 @@ import { colors } from "@mongez/copper";
23
22
 
24
23
  //#region ../../packages/create-warlock/src/commands/create-new-app/index.ts
25
24
  async function createNewApp(cli = {}) {
25
+ resolveLintToolchain(cli.lint);
26
26
  const pmDetectionPromise = detectPackageManagers();
27
27
  const createWarlockVersion = getJsonFile(packageRoot("package.json")).version;
28
28
  showIntroBanner(createWarlockVersion);
@@ -75,11 +75,7 @@ async function createFullWizard(cli, pmDetectionPromise) {
75
75
  process.exit(0);
76
76
  }
77
77
  setPackageManager(packageManager);
78
- const lint = await askLintToolchain(seeds.lint);
79
- if (isCancel(lint)) {
80
- cancel("Toolchain selection cancelled");
81
- process.exit(0);
82
- }
78
+ const lint = seeds.lint;
83
79
  const databaseDriver = await select({
84
80
  message: "Choose your database driver",
85
81
  options: getDatabaseDriverOptions(),
@@ -213,8 +209,7 @@ async function scaffold(appDetails) {
213
209
  * before any file is written.
214
210
  */
215
211
  function resolveNonInteractiveOptions(cli) {
216
- const lint = cli.lint ?? "oxc";
217
- if (lint !== "eslint" && lint !== "oxc") throw new Error(`Unknown lint toolchain "${lint}". Use --lint=eslint or --lint=oxc.`);
212
+ const lint = resolveLintToolchain(cli.lint);
218
213
  const databaseDriver = cli.db ?? "mongodb";
219
214
  const noDatabase = isNoDatabase(databaseDriver);
220
215
  const driver = noDatabase ? void 0 : getDatabaseDriver(databaseDriver);
@@ -263,7 +258,7 @@ async function resolveAppOptions(cli) {
263
258
  async function createNonInteractive(cli) {
264
259
  const appName = (cli.name ?? "").trim();
265
260
  if (!appName) {
266
- cancel("A project name is required and no terminal is available to ask for one. Pass it as the first argument or --name=<name>. Non-interactive flags: --yes, --pm=<npm|yarn|pnpm|bun>, --db=<driver>|--no-db, --features=<list>, --ai=<list>, --lint=<oxc|eslint>, --git|--no-git, --jwt|--no-jwt.");
261
+ cancel("A project name is required and no terminal is available to ask for one. Pass it as the first argument or --name=<name>. Non-interactive flags: --yes, --pm=<npm|yarn|pnpm|bun>, --db=<driver>|--no-db, --features=<list>, --ai=<list>, --lint=<oxc>, --git|--no-git, --jwt|--no-jwt.");
267
262
  process.exit(1);
268
263
  }
269
264
  const appPath = getAppPath(appName);
@@ -287,6 +282,12 @@ async function createNonInteractive(cli) {
287
282
  options
288
283
  });
289
284
  }
285
+ /** Reject removed modes before prompts, network discovery or filesystem writes. */
286
+ function resolveLintToolchain(lint) {
287
+ if (lint === "eslint") throw new Error("ESLint + Prettier support was removed in 5.34.0. Oxlint/Oxfmt is the default; use --lint=oxc or omit --lint.");
288
+ if (lint !== void 0 && lint !== "oxc") throw new Error(`Unknown lint toolchain "${lint}". Use --lint=oxc.`);
289
+ return "oxc";
290
+ }
290
291
 
291
292
  //#endregion
292
293
  export { createNewApp as default, resolveAppOptions, resolveNonInteractiveOptions };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../packages/create-warlock/src/commands/create-new-app/index.ts"],"sourcesContent":["import { cancel, confirm, isCancel, select } from \"@clack/prompts\";\nimport { colors } from \"@mongez/copper\";\nimport { getJsonFile } from \"@warlock.js/fs\";\nimport { resolveAgentTargets } from \"../../features/agent-kit-targets\";\nimport {\n getDatabaseDriver,\n getDatabaseDriverOptions,\n isNoDatabase,\n} from \"../../features/database-drivers\";\nimport {\n getAiPackageOptions,\n getAiProviderOptions,\n getAllFeatureKeys,\n} from \"../../features/features-map\";\nimport { assertFlagCombinations } from \"../../flags/assert-flag-combinations\";\nimport { assertPackageManagerAvailable } from \"../../flags/assert-package-manager\";\nimport { seedFromFlags } from \"../../flags/seed-from-flags\";\nimport { App } from \"../../helpers/app\";\nimport {\n detectPackageManagers,\n getPackageManager,\n getPreferredPackageManager,\n getSystemPackageManagers,\n setPackageManager,\n} from \"../../helpers/package-manager\";\nimport { packageRoot } from \"../../helpers/paths\";\nimport { hasInteractiveStdin } from \"../../helpers/tty\";\nimport { askAgentTargets } from \"../../prompts/ask-agent-targets\";\nimport { askAiFeatures } from \"../../prompts/ask-ai-features\";\nimport { askFeatures } from \"../../prompts/ask-features\";\nimport { askLintToolchain } from \"../../prompts/ask-lint-toolchain\";\nimport { askProjectName } from \"../../prompts/ask-project-name\";\nimport { askStack } from \"../../prompts/ask-stack\";\nimport { showIntroBanner } from \"../../ui/banner\";\nimport { createWarlockApp } from \"../create-warlock-app\";\nimport getAppPath from \"./get-app-path\";\nimport { AppOptions, App as AppType, CliFlags, SetupChoice, Stack } from \"./types\";\n\nexport default async function createNewApp(cli: CliFlags = {}) {\n // Start detecting package managers in the background to avoid delay later\n const pmDetectionPromise = detectPackageManagers();\n\n // Get version from package.json\n const packageJson: any = getJsonFile(packageRoot(\"package.json\"));\n const createWarlockVersion = packageJson.version;\n\n // Show the intro banner\n showIntroBanner(createWarlockVersion);\n\n console.log(colors.cyan(\"Let's create something magical! \\n\"));\n\n // Validate Node.js version (minimum 22.15: the warlock CLI uses module.registerHooks)\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < 22 || (major === 22 && minor < 15)) {\n cancel(`Node.js 22.15.0 or newer is required (found ${process.versions.node}).`);\n process.exit(0);\n }\n\n // Refuse contradictory flag combinations LOUDLY, before anything else looks\n // at them — `--customize --yes` used to drop `--customize` without a word.\n assertFlagCombinations(cli);\n\n // Non-interactive path: build everything from flags and skip the prompts.\n //\n // Triggered by --yes, OR automatically the moment stdin has no TTY — CI, a\n // script, an agent harness. This check MUST run before the first prompt\n // call: attempting `text()`/`select()` on a non-TTY stdin does not fail\n // cleanly, it crashes with a libuv internal (\"TTY initialization failed:\n // uv_tty_init returned EBADF\") before any app directory exists, no matter\n // how the run was invoked.\n //\n // Prompting is the fallback, not the precondition: a project name is the\n // only answer with no sane default, so when a name is already available\n // (positional arg or --name) the run proceeds exactly as --yes would, TTY\n // or not. When the name is ALSO missing and there is no TTY to ask for one,\n // createNonInteractive fails loudly — naming --yes and every non-interactive\n // flag — instead of ever reaching a prompt. Every other prompt already has a\n // default; see resolveNonInteractiveOptions below for the full table.\n if (cli.yes || !hasInteractiveStdin()) {\n await createNonInteractive(cli);\n return;\n }\n\n // `--interactive` (alias `--customize`) restores the full long-form wizard\n // below. Everyone else with a TTY falls through to the default path, which\n // asks the preset plus database choice — see createDefaultInteractive.\n if (cli.interactive) {\n await createFullWizard(cli, pmDetectionPromise);\n return;\n }\n\n await createDefaultInteractive(cli, pmDetectionPromise);\n}\n\n/**\n * The full long-form wizard (`--interactive` / `--customize`): every prompt,\n * one after another. This is the ORIGINAL default flow, preserved verbatim —\n * only its trigger moved from \"any TTY\" to an explicit opt-in flag, per the\n * rule that a reversible choice never prompts by default.\n */\nasync function createFullWizard(cli: CliFlags, pmDetectionPromise: Promise<void>) {\n // Step 1: Project name — but only when it is not already known. It arrives\n // answered from `--name`/the positional arg, and from the default path when\n // the user picked \"Customize\" AFTER being asked for a name. Re-asking for an\n // answer we already hold is the double-prompt this branch exists to avoid.\n const appName = cli.name ?? (await askProjectName());\n\n const appPath = getAppPath(appName);\n if (!appPath) return;\n\n // A flag passed alongside --customize is an answer given early, not a\n // contradiction: it pre-selects the matching prompt instead of being\n // dropped. Validate the ones that CAN be wrong through the same function\n // the non-interactive path uses, so an unknown driver or feature key fails\n // here rather than showing a prompt that quietly ignores it.\n try {\n resolveNonInteractiveOptions(cli);\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const seeds = seedFromFlags(cli);\n\n // Step 2: Package Manager selection\n await pmDetectionPromise; // Ensure detection is complete\n\n // Only now can availability be judged: detection is what fills\n // getSystemPackageManagers(), and the prompt below is built from it.\n assertPackageManagerAvailable(cli.pm);\n\n const packageManager = await select({\n message: \"Which package manager do you want to use?\",\n options: getSystemPackageManagers().map((pm) => ({\n value: pm,\n label: pm,\n })),\n initialValue: seeds.packageManager,\n });\n\n if (isCancel(packageManager)) {\n cancel(\"Package manager selection cancelled\");\n process.exit(0);\n }\n\n setPackageManager(packageManager as string);\n\n const lint = await askLintToolchain(seeds.lint);\n if (isCancel(lint)) {\n cancel(\"Toolchain selection cancelled\");\n process.exit(0);\n }\n\n // Step 3: Database driver selection\n const databaseDriver = await select({\n message: \"Choose your database driver\",\n options: getDatabaseDriverOptions(),\n initialValue: seeds.databaseDriver,\n });\n\n if (isCancel(databaseDriver)) {\n cancel(\"Database selection cancelled\");\n process.exit(0);\n }\n\n const selectedDriver = getDatabaseDriver(databaseDriver as string);\n\n // Step 4: Edit optional features in paged groups.\n const selectedFeatures = await askFeatures(seeds.features);\n\n if (isCancel(selectedFeatures)) {\n cancel(\"Feature selection cancelled\");\n process.exit(0);\n }\n\n // Step 4b: AI providers + capability packages — selecting any pulls\n // @warlock.js/ai automatically. The result still flows through the\n // `selectedAiProviders` → `aiProviders` pipeline, which now also carries the\n // capability packages (ai-tools / ai-panoptic / ai-workspace).\n const selectedAiProviders = await askAiFeatures(seeds.aiProviders);\n\n if (isCancel(selectedAiProviders)) {\n cancel(\"AI provider selection cancelled\");\n process.exit(0);\n }\n\n // Step 5: Git initialization\n const useGit =\n (await confirm({\n message: \"Initialize a Git repository?\",\n initialValue: seeds.useGit,\n })) === true;\n\n if (isCancel(useGit)) {\n cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // Step 6: JWT secret generation\n const useJWT =\n (await confirm({\n message: \"Generate JWT secret keys?\",\n initialValue: seeds.useJWT,\n })) === true;\n\n if (isCancel(useJWT)) {\n cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // Step 7: agent-kit targets. Asked here rather than left to --agents,\n // because Customize means every choice is offered (owner ruling, seq 1028).\n // --agents pre-ticks the selection, like every other flag.\n const agents = await askAgentTargets(cli.agents);\n\n // Build app details\n const appDetails: Required<AppType> = {\n appName: appName,\n appType: \"warlock\",\n appPath: appPath,\n pkgManager: getPackageManager(),\n options: {\n databaseDriver: databaseDriver as string,\n databasePort: selectedDriver?.defaultPort || 27017,\n features: selectedFeatures as string[],\n aiProviders: selectedAiProviders as string[],\n useGit,\n useJWT,\n agents,\n lint,\n },\n };\n\n // Create the app\n await scaffold(appDetails);\n}\n\n/**\n * The default TTY path: neither `--yes` nor `--interactive` was given, but a\n * terminal is available. Asks the project name when missing (a required\n * answer with no sane default — same treatment as the non-interactive path)\n * plus the preset's database choice when `--db`/`--no-db` was not supplied.\n * Every other choice is resolved exactly like `createNonInteractive` — flags,\n * with defaults, never a prompt.\n */\nasync function createDefaultInteractive(cli: CliFlags, pmDetectionPromise: Promise<void>) {\n const appName = cli.name ?? (await askProjectName());\n\n const appPath = getAppPath(appName);\n if (!appPath) return;\n\n const choice: SetupChoice = cli.stack ?? (await askStack());\n\n // \"Customize\" is not a third stack — it is the wizard, chosen from the menu\n // instead of from a flag nobody was told about. Hand over the name we just\n // collected so the wizard starts at its SECOND question.\n if (choice === \"customize\") {\n await createFullWizard({ ...cli, name: appName }, pmDetectionPromise);\n return;\n }\n\n const stack: Stack = choice;\n\n await pmDetectionPromise;\n\n const packageManager = cli.pm ?? getPreferredPackageManager();\n\n assertPackageManagerAvailable(cli.pm);\n\n setPackageManager(packageManager);\n\n const databaseDriver =\n cli.db ??\n (await select({\n message: \"Choose your database driver\",\n options: getDatabaseDriverOptions(),\n initialValue: \"mongodb\",\n }));\n\n if (isCancel(databaseDriver)) {\n cancel(\"Database selection cancelled\");\n process.exit(0);\n }\n\n let options: AppOptions;\n\n try {\n options = await resolveAppOptions({ ...cli, stack, db: databaseDriver });\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const appDetails: Required<AppType> = {\n appName,\n appType: \"warlock\",\n appPath,\n pkgManager: getPackageManager(),\n options,\n };\n\n await scaffold(appDetails);\n}\n\n/**\n * Run the scaffold and make the process's exit code tell the truth: anything\n * the user asked for that did not happen leaves the shell with a non-zero\n * status, so scripts and CI cannot mistake a half-built project for a finished\n * one. The problems themselves are printed by the scaffolder.\n */\nasync function scaffold(appDetails: Required<AppType>) {\n const outcome = await createWarlockApp(new App(appDetails));\n\n if (outcome && !outcome.ok) {\n process.exitCode = 1;\n }\n}\n\n/** Everything {@link resolveNonInteractiveOptions} can resolve without the network. */\nexport type SyncAppOptions = Omit<AppOptions, \"agents\">;\n\n/**\n * Resolve every flag-or-default `AppOptions` field EXCEPT `agents` from parsed\n * CLI flags — applying the non-interactive defaults, validating the database\n * driver and feature/provider keys, and handling the `none` (no database)\n * selection. `agents` is resolved separately by {@link resolveAppOptions}\n * because it can require a network fetch; this function stays synchronous so\n * the rest of the flag → app mapping remains trivially unit-testable.\n *\n * `--stack=web` (or the equivalent wizard answer) seeds the `web` feature by\n * default — the one structural choice — but an explicit `--features` always\n * wins, so a user who spells out the feature list is never second-guessed.\n *\n * Throws on an unknown driver, feature, or AI key so the caller can fail fast\n * before any file is written.\n */\nexport function resolveNonInteractiveOptions(cli: CliFlags): SyncAppOptions {\n const lint = cli.lint ?? \"oxc\";\n if (lint !== \"eslint\" && lint !== \"oxc\") {\n throw new Error(`Unknown lint toolchain \"${lint}\". Use --lint=eslint or --lint=oxc.`);\n }\n const databaseDriver = cli.db ?? \"mongodb\";\n const noDatabase = isNoDatabase(databaseDriver);\n const driver = noDatabase ? undefined : getDatabaseDriver(databaseDriver);\n\n if (!noDatabase && !driver) {\n throw new Error(`Unknown database driver \"${databaseDriver}\"`);\n }\n\n const stack: Stack = cli.stack ?? \"api\";\n const features = cli.features ?? (stack === \"web\" ? [\"web\"] : []);\n const aiProviders = cli.ai ?? [];\n\n const allowedKeys = getAllFeatureKeys();\n const invalidFeatureKeys = features.filter((key) => !allowedKeys.includes(key));\n const errors: string[] = [];\n\n if (invalidFeatureKeys.length > 0) {\n errors.push(`Unknown feature(s): ${invalidFeatureKeys.join(\",\")}`);\n }\n\n const aiFeatureKeys = [...getAiProviderOptions(), ...getAiPackageOptions()].map(\n (option) => option.value,\n );\n const invalidAiKeys = aiProviders.filter((key) => !aiFeatureKeys.includes(key));\n\n if (invalidAiKeys.length > 0) {\n errors.push(\n `Unknown AI feature(s): ${invalidAiKeys.join(\",\")}. ` +\n `Valid AI choices: ${aiFeatureKeys.join(\", \")}`,\n );\n }\n\n if (errors.length > 0) {\n throw new Error(errors.join(\"\\n\"));\n }\n\n return {\n databaseDriver,\n databasePort: driver?.defaultPort ?? 27017,\n features,\n aiProviders,\n lint,\n useGit: cli.git ?? false,\n useJWT: cli.jwt ?? false,\n };\n}\n\n/**\n * Resolve the FULL `AppOptions` — {@link resolveNonInteractiveOptions} plus\n * the validated `agents` list. Async because a non-default `--agents`\n * selection may need {@link resolveAgentTargets}'s network fetch; the default\n * (`claude`) never does.\n */\nexport async function resolveAppOptions(cli: CliFlags): Promise<AppOptions> {\n const options = resolveNonInteractiveOptions(cli);\n const agents = await resolveAgentTargets(cli.agents);\n\n return { ...options, agents };\n}\n\n/**\n * Non-interactive scaffold path for `--yes`. Builds the app from flags with\n * sensible defaults and skips every prompt — a single command scaffolds the\n * entire app. Validation lives in `resolveNonInteractiveOptions`; a typo fails\n * fast here instead of mid-install.\n */\nasync function createNonInteractive(cli: CliFlags) {\n const appName = (cli.name ?? \"\").trim();\n\n if (!appName) {\n // Reached either because --yes was passed with no name, or because stdin\n // has no TTY to prompt for one — either way, nothing is left to fall back\n // to. Name --yes AND every non-interactive flag so a CI log is enough to\n // fix the invocation without hunting for docs.\n cancel(\n \"A project name is required and no terminal is available to ask for one. \" +\n \"Pass it as the first argument or --name=<name>. Non-interactive flags: \" +\n \"--yes, --pm=<npm|yarn|pnpm|bun>, --db=<driver>|--no-db, --features=<list>, \" +\n \"--ai=<list>, --lint=<oxc|eslint>, --git|--no-git, --jwt|--no-jwt.\",\n );\n process.exit(1);\n }\n\n const appPath = getAppPath(appName);\n\n if (!appPath) return;\n\n await detectPackageManagers();\n\n const packageManager = cli.pm ?? getPreferredPackageManager();\n\n assertPackageManagerAvailable(cli.pm);\n\n setPackageManager(packageManager);\n\n let options: AppOptions;\n\n try {\n options = await resolveAppOptions(cli);\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const appDetails: Required<AppType> = {\n appName,\n appType: \"warlock\",\n appPath,\n pkgManager: getPackageManager(),\n options,\n };\n\n await scaffold(appDetails);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../packages/create-warlock/src/commands/create-new-app/index.ts"],"sourcesContent":["import { cancel, confirm, isCancel, select } from \"@clack/prompts\";\nimport { colors } from \"@mongez/copper\";\nimport { getJsonFile } from \"@warlock.js/fs\";\nimport { resolveAgentTargets } from \"../../features/agent-kit-targets\";\nimport {\n getDatabaseDriver,\n getDatabaseDriverOptions,\n isNoDatabase,\n} from \"../../features/database-drivers\";\nimport {\n getAiPackageOptions,\n getAiProviderOptions,\n getAllFeatureKeys,\n} from \"../../features/features-map\";\nimport { assertFlagCombinations } from \"../../flags/assert-flag-combinations\";\nimport { assertPackageManagerAvailable } from \"../../flags/assert-package-manager\";\nimport { seedFromFlags } from \"../../flags/seed-from-flags\";\nimport { App } from \"../../helpers/app\";\nimport {\n detectPackageManagers,\n getPackageManager,\n getPreferredPackageManager,\n getSystemPackageManagers,\n setPackageManager,\n} from \"../../helpers/package-manager\";\nimport { packageRoot } from \"../../helpers/paths\";\nimport { hasInteractiveStdin } from \"../../helpers/tty\";\nimport { askAgentTargets } from \"../../prompts/ask-agent-targets\";\nimport { askAiFeatures } from \"../../prompts/ask-ai-features\";\nimport { askFeatures } from \"../../prompts/ask-features\";\nimport { askProjectName } from \"../../prompts/ask-project-name\";\nimport { askStack } from \"../../prompts/ask-stack\";\nimport { showIntroBanner } from \"../../ui/banner\";\nimport { createWarlockApp } from \"../create-warlock-app\";\nimport getAppPath from \"./get-app-path\";\nimport { AppOptions, App as AppType, CliFlags, SetupChoice, Stack } from \"./types\";\n\nexport default async function createNewApp(cli: CliFlags = {}) {\n resolveLintToolchain(cli.lint);\n // Start detecting package managers in the background to avoid delay later\n const pmDetectionPromise = detectPackageManagers();\n\n // Get version from package.json\n const packageJson: any = getJsonFile(packageRoot(\"package.json\"));\n const createWarlockVersion = packageJson.version;\n\n // Show the intro banner\n showIntroBanner(createWarlockVersion);\n\n console.log(colors.cyan(\"Let's create something magical! \\n\"));\n\n // Validate Node.js version (minimum 22.15: the warlock CLI uses module.registerHooks)\n const [major = 0, minor = 0] = process.versions.node.split(\".\").map(Number);\n if (major < 22 || (major === 22 && minor < 15)) {\n cancel(`Node.js 22.15.0 or newer is required (found ${process.versions.node}).`);\n process.exit(0);\n }\n\n // Refuse contradictory flag combinations LOUDLY, before anything else looks\n // at them — `--customize --yes` used to drop `--customize` without a word.\n assertFlagCombinations(cli);\n\n // Non-interactive path: build everything from flags and skip the prompts.\n //\n // Triggered by --yes, OR automatically the moment stdin has no TTY — CI, a\n // script, an agent harness. This check MUST run before the first prompt\n // call: attempting `text()`/`select()` on a non-TTY stdin does not fail\n // cleanly, it crashes with a libuv internal (\"TTY initialization failed:\n // uv_tty_init returned EBADF\") before any app directory exists, no matter\n // how the run was invoked.\n //\n // Prompting is the fallback, not the precondition: a project name is the\n // only answer with no sane default, so when a name is already available\n // (positional arg or --name) the run proceeds exactly as --yes would, TTY\n // or not. When the name is ALSO missing and there is no TTY to ask for one,\n // createNonInteractive fails loudly — naming --yes and every non-interactive\n // flag — instead of ever reaching a prompt. Every other prompt already has a\n // default; see resolveNonInteractiveOptions below for the full table.\n if (cli.yes || !hasInteractiveStdin()) {\n await createNonInteractive(cli);\n return;\n }\n\n // `--interactive` (alias `--customize`) restores the full long-form wizard\n // below. Everyone else with a TTY falls through to the default path, which\n // asks the preset plus database choice — see createDefaultInteractive.\n if (cli.interactive) {\n await createFullWizard(cli, pmDetectionPromise);\n return;\n }\n\n await createDefaultInteractive(cli, pmDetectionPromise);\n}\n\n/**\n * The full long-form wizard (`--interactive` / `--customize`): every prompt,\n * one after another. This is the ORIGINAL default flow, preserved verbatim —\n * only its trigger moved from \"any TTY\" to an explicit opt-in flag, per the\n * rule that a reversible choice never prompts by default.\n */\nasync function createFullWizard(cli: CliFlags, pmDetectionPromise: Promise<void>) {\n // Step 1: Project name — but only when it is not already known. It arrives\n // answered from `--name`/the positional arg, and from the default path when\n // the user picked \"Customize\" AFTER being asked for a name. Re-asking for an\n // answer we already hold is the double-prompt this branch exists to avoid.\n const appName = cli.name ?? (await askProjectName());\n\n const appPath = getAppPath(appName);\n if (!appPath) return;\n\n // A flag passed alongside --customize is an answer given early, not a\n // contradiction: it pre-selects the matching prompt instead of being\n // dropped. Validate the ones that CAN be wrong through the same function\n // the non-interactive path uses, so an unknown driver or feature key fails\n // here rather than showing a prompt that quietly ignores it.\n try {\n resolveNonInteractiveOptions(cli);\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const seeds = seedFromFlags(cli);\n\n // Step 2: Package Manager selection\n await pmDetectionPromise; // Ensure detection is complete\n\n // Only now can availability be judged: detection is what fills\n // getSystemPackageManagers(), and the prompt below is built from it.\n assertPackageManagerAvailable(cli.pm);\n\n const packageManager = await select({\n message: \"Which package manager do you want to use?\",\n options: getSystemPackageManagers().map((pm) => ({\n value: pm,\n label: pm,\n })),\n initialValue: seeds.packageManager,\n });\n\n if (isCancel(packageManager)) {\n cancel(\"Package manager selection cancelled\");\n process.exit(0);\n }\n\n setPackageManager(packageManager as string);\n\n const lint = seeds.lint;\n\n // Step 3: Database driver selection\n const databaseDriver = await select({\n message: \"Choose your database driver\",\n options: getDatabaseDriverOptions(),\n initialValue: seeds.databaseDriver,\n });\n\n if (isCancel(databaseDriver)) {\n cancel(\"Database selection cancelled\");\n process.exit(0);\n }\n\n const selectedDriver = getDatabaseDriver(databaseDriver as string);\n\n // Step 4: Edit optional features in paged groups.\n const selectedFeatures = await askFeatures(seeds.features);\n\n if (isCancel(selectedFeatures)) {\n cancel(\"Feature selection cancelled\");\n process.exit(0);\n }\n\n // Step 4b: AI providers + capability packages — selecting any pulls\n // @warlock.js/ai automatically. The result still flows through the\n // `selectedAiProviders` → `aiProviders` pipeline, which now also carries the\n // capability packages (ai-tools / ai-panoptic / ai-workspace).\n const selectedAiProviders = await askAiFeatures(seeds.aiProviders);\n\n if (isCancel(selectedAiProviders)) {\n cancel(\"AI provider selection cancelled\");\n process.exit(0);\n }\n\n // Step 5: Git initialization\n const useGit =\n (await confirm({\n message: \"Initialize a Git repository?\",\n initialValue: seeds.useGit,\n })) === true;\n\n if (isCancel(useGit)) {\n cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // Step 6: JWT secret generation\n const useJWT =\n (await confirm({\n message: \"Generate JWT secret keys?\",\n initialValue: seeds.useJWT,\n })) === true;\n\n if (isCancel(useJWT)) {\n cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // Step 7: agent-kit targets. Asked here rather than left to --agents,\n // because Customize means every choice is offered (owner ruling, seq 1028).\n // --agents pre-ticks the selection, like every other flag.\n const agents = await askAgentTargets(cli.agents);\n\n // Build app details\n const appDetails: Required<AppType> = {\n appName: appName,\n appType: \"warlock\",\n appPath: appPath,\n pkgManager: getPackageManager(),\n options: {\n databaseDriver: databaseDriver as string,\n databasePort: selectedDriver?.defaultPort || 27017,\n features: selectedFeatures as string[],\n aiProviders: selectedAiProviders as string[],\n useGit,\n useJWT,\n agents,\n lint,\n },\n };\n\n // Create the app\n await scaffold(appDetails);\n}\n\n/**\n * The default TTY path: neither `--yes` nor `--interactive` was given, but a\n * terminal is available. Asks the project name when missing (a required\n * answer with no sane default — same treatment as the non-interactive path)\n * plus the preset's database choice when `--db`/`--no-db` was not supplied.\n * Every other choice is resolved exactly like `createNonInteractive` — flags,\n * with defaults, never a prompt.\n */\nasync function createDefaultInteractive(cli: CliFlags, pmDetectionPromise: Promise<void>) {\n const appName = cli.name ?? (await askProjectName());\n\n const appPath = getAppPath(appName);\n if (!appPath) return;\n\n const choice: SetupChoice = cli.stack ?? (await askStack());\n\n // \"Customize\" is not a third stack — it is the wizard, chosen from the menu\n // instead of from a flag nobody was told about. Hand over the name we just\n // collected so the wizard starts at its SECOND question.\n if (choice === \"customize\") {\n await createFullWizard({ ...cli, name: appName }, pmDetectionPromise);\n return;\n }\n\n const stack: Stack = choice;\n\n await pmDetectionPromise;\n\n const packageManager = cli.pm ?? getPreferredPackageManager();\n\n assertPackageManagerAvailable(cli.pm);\n\n setPackageManager(packageManager);\n\n const databaseDriver =\n cli.db ??\n (await select({\n message: \"Choose your database driver\",\n options: getDatabaseDriverOptions(),\n initialValue: \"mongodb\",\n }));\n\n if (isCancel(databaseDriver)) {\n cancel(\"Database selection cancelled\");\n process.exit(0);\n }\n\n let options: AppOptions;\n\n try {\n options = await resolveAppOptions({ ...cli, stack, db: databaseDriver });\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const appDetails: Required<AppType> = {\n appName,\n appType: \"warlock\",\n appPath,\n pkgManager: getPackageManager(),\n options,\n };\n\n await scaffold(appDetails);\n}\n\n/**\n * Run the scaffold and make the process's exit code tell the truth: anything\n * the user asked for that did not happen leaves the shell with a non-zero\n * status, so scripts and CI cannot mistake a half-built project for a finished\n * one. The problems themselves are printed by the scaffolder.\n */\nasync function scaffold(appDetails: Required<AppType>) {\n const outcome = await createWarlockApp(new App(appDetails));\n\n if (outcome && !outcome.ok) {\n process.exitCode = 1;\n }\n}\n\n/** Everything {@link resolveNonInteractiveOptions} can resolve without the network. */\nexport type SyncAppOptions = Omit<AppOptions, \"agents\">;\n\n/**\n * Resolve every flag-or-default `AppOptions` field EXCEPT `agents` from parsed\n * CLI flags — applying the non-interactive defaults, validating the database\n * driver and feature/provider keys, and handling the `none` (no database)\n * selection. `agents` is resolved separately by {@link resolveAppOptions}\n * because it can require a network fetch; this function stays synchronous so\n * the rest of the flag → app mapping remains trivially unit-testable.\n *\n * `--stack=web` (or the equivalent wizard answer) seeds the `web` feature by\n * default — the one structural choice — but an explicit `--features` always\n * wins, so a user who spells out the feature list is never second-guessed.\n *\n * Throws on an unknown driver, feature, or AI key so the caller can fail fast\n * before any file is written.\n */\nexport function resolveNonInteractiveOptions(cli: CliFlags): SyncAppOptions {\n const lint = resolveLintToolchain(cli.lint);\n const databaseDriver = cli.db ?? \"mongodb\";\n const noDatabase = isNoDatabase(databaseDriver);\n const driver = noDatabase ? undefined : getDatabaseDriver(databaseDriver);\n\n if (!noDatabase && !driver) {\n throw new Error(`Unknown database driver \"${databaseDriver}\"`);\n }\n\n const stack: Stack = cli.stack ?? \"api\";\n const features = cli.features ?? (stack === \"web\" ? [\"web\"] : []);\n const aiProviders = cli.ai ?? [];\n\n const allowedKeys = getAllFeatureKeys();\n const invalidFeatureKeys = features.filter((key) => !allowedKeys.includes(key));\n const errors: string[] = [];\n\n if (invalidFeatureKeys.length > 0) {\n errors.push(`Unknown feature(s): ${invalidFeatureKeys.join(\",\")}`);\n }\n\n const aiFeatureKeys = [...getAiProviderOptions(), ...getAiPackageOptions()].map(\n (option) => option.value,\n );\n const invalidAiKeys = aiProviders.filter((key) => !aiFeatureKeys.includes(key));\n\n if (invalidAiKeys.length > 0) {\n errors.push(\n `Unknown AI feature(s): ${invalidAiKeys.join(\",\")}. ` +\n `Valid AI choices: ${aiFeatureKeys.join(\", \")}`,\n );\n }\n\n if (errors.length > 0) {\n throw new Error(errors.join(\"\\n\"));\n }\n\n return {\n databaseDriver,\n databasePort: driver?.defaultPort ?? 27017,\n features,\n aiProviders,\n lint,\n useGit: cli.git ?? false,\n useJWT: cli.jwt ?? false,\n };\n}\n\n/**\n * Resolve the FULL `AppOptions` — {@link resolveNonInteractiveOptions} plus\n * the validated `agents` list. Async because a non-default `--agents`\n * selection may need {@link resolveAgentTargets}'s network fetch; the default\n * (`claude`) never does.\n */\nexport async function resolveAppOptions(cli: CliFlags): Promise<AppOptions> {\n const options = resolveNonInteractiveOptions(cli);\n const agents = await resolveAgentTargets(cli.agents);\n\n return { ...options, agents };\n}\n\n/**\n * Non-interactive scaffold path for `--yes`. Builds the app from flags with\n * sensible defaults and skips every prompt — a single command scaffolds the\n * entire app. Validation lives in `resolveNonInteractiveOptions`; a typo fails\n * fast here instead of mid-install.\n */\nasync function createNonInteractive(cli: CliFlags) {\n const appName = (cli.name ?? \"\").trim();\n\n if (!appName) {\n // Reached either because --yes was passed with no name, or because stdin\n // has no TTY to prompt for one — either way, nothing is left to fall back\n // to. Name --yes AND every non-interactive flag so a CI log is enough to\n // fix the invocation without hunting for docs.\n cancel(\n \"A project name is required and no terminal is available to ask for one. \" +\n \"Pass it as the first argument or --name=<name>. Non-interactive flags: \" +\n \"--yes, --pm=<npm|yarn|pnpm|bun>, --db=<driver>|--no-db, --features=<list>, \" +\n \"--ai=<list>, --lint=<oxc>, --git|--no-git, --jwt|--no-jwt.\",\n );\n process.exit(1);\n }\n\n const appPath = getAppPath(appName);\n\n if (!appPath) return;\n\n await detectPackageManagers();\n\n const packageManager = cli.pm ?? getPreferredPackageManager();\n\n assertPackageManagerAvailable(cli.pm);\n\n setPackageManager(packageManager);\n\n let options: AppOptions;\n\n try {\n options = await resolveAppOptions(cli);\n } catch (error) {\n cancel((error as Error).message);\n process.exit(1);\n }\n\n const appDetails: Required<AppType> = {\n appName,\n appType: \"warlock\",\n appPath,\n pkgManager: getPackageManager(),\n options,\n };\n\n await scaffold(appDetails);\n}\n\n/** Reject removed modes before prompts, network discovery or filesystem writes. */\nfunction resolveLintToolchain(lint: string | undefined): \"oxc\" {\n if (lint === \"eslint\") {\n throw new Error(\n \"ESLint + Prettier support was removed in 5.34.0. Oxlint/Oxfmt is the default; use --lint=oxc or omit --lint.\",\n );\n }\n if (lint !== undefined && lint !== \"oxc\") {\n throw new Error(`Unknown lint toolchain \"${lint}\". Use --lint=oxc.`);\n }\n return \"oxc\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqCA,eAA8B,aAAa,MAAgB,CAAC,GAAG;CAC7D,qBAAqB,IAAI,IAAI;CAE7B,MAAM,qBAAqB,sBAAsB;CAIjD,MAAM,uBADmB,YAAY,YAAY,cAAc,CACxB,CAAC,CAAC;CAGzC,gBAAgB,oBAAoB;CAEpC,QAAQ,IAAI,OAAO,KAAK,oCAAoC,CAAC;CAG7D,MAAM,CAAC,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,KAAK,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM;CAC1E,IAAI,QAAQ,MAAO,UAAU,MAAM,QAAQ,IAAK;EAC9C,OAAO,+CAA+C,QAAQ,SAAS,KAAK,GAAG;EAC/E,QAAQ,KAAK,CAAC;CAChB;CAIA,uBAAuB,GAAG;CAkB1B,IAAI,IAAI,OAAO,CAAC,oBAAoB,GAAG;EACrC,MAAM,qBAAqB,GAAG;EAC9B;CACF;CAKA,IAAI,IAAI,aAAa;EACnB,MAAM,iBAAiB,KAAK,kBAAkB;EAC9C;CACF;CAEA,MAAM,yBAAyB,KAAK,kBAAkB;AACxD;;;;;;;AAQA,eAAe,iBAAiB,KAAe,oBAAmC;CAKhF,MAAM,UAAU,IAAI,QAAS,MAAM,eAAe;CAElD,MAAM,UAAU,WAAW,OAAO;CAClC,IAAI,CAAC,SAAS;CAOd,IAAI;EACF,6BAA6B,GAAG;CAClC,SAAS,OAAO;EACd,OAAQ,MAAgB,OAAO;EAC/B,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,QAAQ,cAAc,GAAG;CAG/B,MAAM;CAIN,8BAA8B,IAAI,EAAE;CAEpC,MAAM,iBAAiB,MAAM,OAAO;EAClC,SAAS;EACT,SAAS,yBAAyB,CAAC,CAAC,KAAK,QAAQ;GAC/C,OAAO;GACP,OAAO;EACT,EAAE;EACF,cAAc,MAAM;CACtB,CAAC;CAED,IAAI,SAAS,cAAc,GAAG;EAC5B,OAAO,qCAAqC;EAC5C,QAAQ,KAAK,CAAC;CAChB;CAEA,kBAAkB,cAAwB;CAE1C,MAAM,OAAO,MAAM;CAGnB,MAAM,iBAAiB,MAAM,OAAO;EAClC,SAAS;EACT,SAAS,yBAAyB;EAClC,cAAc,MAAM;CACtB,CAAC;CAED,IAAI,SAAS,cAAc,GAAG;EAC5B,OAAO,8BAA8B;EACrC,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,iBAAiB,kBAAkB,cAAwB;CAGjE,MAAM,mBAAmB,MAAM,YAAY,MAAM,QAAQ;CAEzD,IAAI,SAAS,gBAAgB,GAAG;EAC9B,OAAO,6BAA6B;EACpC,QAAQ,KAAK,CAAC;CAChB;CAMA,MAAM,sBAAsB,MAAM,cAAc,MAAM,WAAW;CAEjE,IAAI,SAAS,mBAAmB,GAAG;EACjC,OAAO,iCAAiC;EACxC,QAAQ,KAAK,CAAC;CAChB;CAGA,MAAM,SACH,MAAM,QAAQ;EACb,SAAS;EACT,cAAc,MAAM;CACtB,CAAC,MAAO;CAEV,IAAI,SAAS,MAAM,GAAG;EACpB,OAAO,iBAAiB;EACxB,QAAQ,KAAK,CAAC;CAChB;CAGA,MAAM,SACH,MAAM,QAAQ;EACb,SAAS;EACT,cAAc,MAAM;CACtB,CAAC,MAAO;CAEV,IAAI,SAAS,MAAM,GAAG;EACpB,OAAO,iBAAiB;EACxB,QAAQ,KAAK,CAAC;CAChB;CAKA,MAAM,SAAS,MAAM,gBAAgB,IAAI,MAAM;CAqB/C,MAAM,SAAS;EAjBJ;EACT,SAAS;EACA;EACT,YAAY,kBAAkB;EAC9B,SAAS;GACS;GAChB,cAAc,gBAAgB,eAAe;GAC7C,UAAU;GACV,aAAa;GACb;GACA;GACA;GACA;EACF;CAIsB,CAAC;AAC3B;;;;;;;;;AAUA,eAAe,yBAAyB,KAAe,oBAAmC;CACxF,MAAM,UAAU,IAAI,QAAS,MAAM,eAAe;CAElD,MAAM,UAAU,WAAW,OAAO;CAClC,IAAI,CAAC,SAAS;CAEd,MAAM,SAAsB,IAAI,SAAU,MAAM,SAAS;CAKzD,IAAI,WAAW,aAAa;EAC1B,MAAM,iBAAiB;GAAE,GAAG;GAAK,MAAM;EAAQ,GAAG,kBAAkB;EACpE;CACF;CAEA,MAAM,QAAe;CAErB,MAAM;CAEN,MAAM,iBAAiB,IAAI,MAAM,2BAA2B;CAE5D,8BAA8B,IAAI,EAAE;CAEpC,kBAAkB,cAAc;CAEhC,MAAM,iBACJ,IAAI,MACH,MAAM,OAAO;EACZ,SAAS;EACT,SAAS,yBAAyB;EAClC,cAAc;CAChB,CAAC;CAEH,IAAI,SAAS,cAAc,GAAG;EAC5B,OAAO,8BAA8B;EACrC,QAAQ,KAAK,CAAC;CAChB;CAEA,IAAI;CAEJ,IAAI;EACF,UAAU,MAAM,kBAAkB;GAAE,GAAG;GAAK;GAAO,IAAI;EAAe,CAAC;CACzE,SAAS,OAAO;EACd,OAAQ,MAAgB,OAAO;EAC/B,QAAQ,KAAK,CAAC;CAChB;CAUA,MAAM,SAAS;EAPb;EACA,SAAS;EACT;EACA,YAAY,kBAAkB;EAC9B;CAGsB,CAAC;AAC3B;;;;;;;AAQA,eAAe,SAAS,YAA+B;CACrD,MAAM,UAAU,MAAM,iBAAiB,IAAI,IAAI,UAAU,CAAC;CAE1D,IAAI,WAAW,CAAC,QAAQ,IACtB,QAAQ,WAAW;AAEvB;;;;;;;;;;;;;;;;AAoBA,SAAgB,6BAA6B,KAA+B;CAC1E,MAAM,OAAO,qBAAqB,IAAI,IAAI;CAC1C,MAAM,iBAAiB,IAAI,MAAM;CACjC,MAAM,aAAa,aAAa,cAAc;CAC9C,MAAM,SAAS,aAAa,SAAY,kBAAkB,cAAc;CAExE,IAAI,CAAC,cAAc,CAAC,QAClB,MAAM,IAAI,MAAM,4BAA4B,eAAe,EAAE;CAG/D,MAAM,QAAe,IAAI,SAAS;CAClC,MAAM,WAAW,IAAI,aAAa,UAAU,QAAQ,CAAC,KAAK,IAAI,CAAC;CAC/D,MAAM,cAAc,IAAI,MAAM,CAAC;CAE/B,MAAM,cAAc,kBAAkB;CACtC,MAAM,qBAAqB,SAAS,QAAQ,QAAQ,CAAC,YAAY,SAAS,GAAG,CAAC;CAC9E,MAAM,SAAmB,CAAC;CAE1B,IAAI,mBAAmB,SAAS,GAC9B,OAAO,KAAK,uBAAuB,mBAAmB,KAAK,GAAG,GAAG;CAGnE,MAAM,gBAAgB,CAAC,GAAG,qBAAqB,GAAG,GAAG,oBAAoB,CAAC,CAAC,CAAC,KACzE,WAAW,OAAO,KACrB;CACA,MAAM,gBAAgB,YAAY,QAAQ,QAAQ,CAAC,cAAc,SAAS,GAAG,CAAC;CAE9E,IAAI,cAAc,SAAS,GACzB,OAAO,KACL,0BAA0B,cAAc,KAAK,GAAG,EAAE,sBAC3B,cAAc,KAAK,IAAI,GAChD;CAGF,IAAI,OAAO,SAAS,GAClB,MAAM,IAAI,MAAM,OAAO,KAAK,IAAI,CAAC;CAGnC,OAAO;EACL;EACA,cAAc,QAAQ,eAAe;EACrC;EACA;EACA;EACA,QAAQ,IAAI,OAAO;EACnB,QAAQ,IAAI,OAAO;CACrB;AACF;;;;;;;AAQA,eAAsB,kBAAkB,KAAoC;CAC1E,MAAM,UAAU,6BAA6B,GAAG;CAChD,MAAM,SAAS,MAAM,oBAAoB,IAAI,MAAM;CAEnD,OAAO;EAAE,GAAG;EAAS;CAAO;AAC9B;;;;;;;AAQA,eAAe,qBAAqB,KAAe;CACjD,MAAM,WAAW,IAAI,QAAQ,GAAE,CAAE,KAAK;CAEtC,IAAI,CAAC,SAAS;EAKZ,OACE,sRAIF;EACA,QAAQ,KAAK,CAAC;CAChB;CAEA,MAAM,UAAU,WAAW,OAAO;CAElC,IAAI,CAAC,SAAS;CAEd,MAAM,sBAAsB;CAE5B,MAAM,iBAAiB,IAAI,MAAM,2BAA2B;CAE5D,8BAA8B,IAAI,EAAE;CAEpC,kBAAkB,cAAc;CAEhC,IAAI;CAEJ,IAAI;EACF,UAAU,MAAM,kBAAkB,GAAG;CACvC,SAAS,OAAO;EACd,OAAQ,MAAgB,OAAO;EAC/B,QAAQ,KAAK,CAAC;CAChB;CAUA,MAAM,SAAS;EAPb;EACA,SAAS;EACT;EACA,YAAY,kBAAkB;EAC9B;CAGsB,CAAC;AAC3B;;AAGA,SAAS,qBAAqB,MAAiC;CAC7D,IAAI,SAAS,UACX,MAAM,IAAI,MACR,8GACF;CAEF,IAAI,SAAS,UAAa,SAAS,OACjC,MAAM,IAAI,MAAM,2BAA2B,KAAK,mBAAmB;CAErE,OAAO;AACT"}
@@ -14,7 +14,7 @@ export type CliFlags = {
14
14
  features?: string[];
15
15
  ai?: string[];
16
16
  pm?: string;
17
- /** `--lint=eslint|oxc`; validated before scaffolding. */
17
+ /** `--lint=oxc`; validated before scaffolding. */
18
18
  lint?: string;
19
19
  /** `agent-kit` targets for `--agents`. Comma-separated on the CLI. */
20
20
  agents?: string[];
@@ -44,7 +44,7 @@ async function createWarlockApp(application) {
44
44
  });
45
45
  }
46
46
  templateSpinner.stop(spinnerMessages.templateCopied);
47
- showNotes([...notes, ...options.lint === "eslint" ? ["ESLint mode uses TypeScript 6.0.3 because typescript-eslint does not support TypeScript 7 yet; ESLint mode is planned for deprecation in 5.33 and removal in 5.34."] : []]);
47
+ showNotes([...notes]);
48
48
  const installSpinner = spinner();
49
49
  installSpinner.start(spinnerMessages.installingDeps);
50
50
  const baseInstall = application.install();
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../packages/create-warlock/src/commands/create-warlock-app/index.ts"],"sourcesContent":["import { getDatabaseLabel, isNoDatabase } from \"../../features/database-drivers\";\nimport { App } from \"../../helpers/app\";\nimport { CommandResult, takeLastCommandOutput } from \"../../helpers/exec\";\nimport { getPackageManager, runPackageManagerCommand } from \"../../helpers/package-manager\";\nimport { resolveWarlockVersions } from \"../../helpers/warlock-versions\";\nimport { printDecisionsSummary } from \"../../summary\";\nimport { showSuccessScreen } from \"../../ui/banner\";\nimport {\n failFatally,\n installFailureHints,\n Problem,\n showNotes,\n showPartialScreen,\n showProblems,\n} from \"../../ui/report\";\nimport { spinner } from \"../../ui/spinner\";\nimport { spinnerMessages } from \"../../ui/spinners\";\n\n/**\n * What the scaffold actually achieved. `ok` is false when ANY step the user\n * asked for did not happen; the caller turns that into a non-zero exit code so\n * a script never mistakes a half-built project for a finished one.\n */\nexport type ScaffoldOutcome = {\n ok: boolean;\n problems: Problem[];\n};\n\n/** One-line \"why did this fail\" pulled out of a captured command. */\nfunction reasonFrom(result: CommandResult | undefined): string {\n if (!result) return \"the command reported a failure (no output captured)\";\n\n if (result.error) {\n return result.error.message || String(result.error);\n }\n\n const lastLine = `${result.stderr}\\n${result.stdout}`\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .pop();\n\n const status =\n result.code === null\n ? `killed by ${result.signal ?? \"an unknown signal\"}`\n : `exit code ${result.code}`;\n\n return lastLine ? `${status} — ${lastLine}` : status;\n}\n\nexport async function createWarlockApp(application: App): Promise<ScaffoldOutcome> {\n const options = application.options;\n const { useGit, useJWT, features, aiProviders, databaseDriver } = options;\n const noDatabase = isNoDatabase(databaseDriver);\n const problems: Problem[] = [];\n\n // Resolve the versions to pin BEFORE anything is written. The scaffolder's\n // own version is only used when the registry confirms it exists — see\n // helpers/warlock-versions.ts for why an unverified pin broke every install.\n const { versions, notes } = await resolveWarlockVersions();\n\n // Step 1: Initialize and copy template\n const templateSpinner = spinner();\n templateSpinner.start(spinnerMessages.copyingTemplate);\n\n try {\n application.init().use(\"warlock\").updatePackageJson(versions).updateDotEnv();\n\n // Wire the chosen database driver into .env — or, when the user opted out,\n // strip the database config entirely so the app boots with no database.\n if (noDatabase) {\n application.removeDatabaseConfig();\n } else {\n application.configureDatabaseEnv(databaseDriver);\n application.configureDatabaseConfig(databaseDriver);\n }\n\n application.configureWebStarter(features.includes(\"web\"));\n application.configureLintToolchain();\n application.configureAgentKitTargets(options.agents ?? [\"claude\"]);\n } catch (error) {\n templateSpinner.stop(spinnerMessages.templateFailed);\n\n failFatally({\n step: \"Template copy\",\n detail: `The project files could not be written: ${(error as Error).message}`,\n hints: [\n \"Check that the target directory is writable and that no file is locked by another process.\",\n ],\n });\n }\n\n templateSpinner.stop(spinnerMessages.templateCopied);\n\n showNotes([\n ...notes,\n ...(options.lint === \"eslint\"\n ? [\n \"ESLint mode uses TypeScript 6.0.3 because typescript-eslint does not support TypeScript 7 yet; ESLint mode is planned for deprecation in 5.33 and removal in 5.34.\",\n ]\n : []),\n ]);\n\n // Step 2: Install base dependencies (so the `warlock` binary is available).\n // Nothing downstream works without this, so a failure ends the run — loudly,\n // with the command, its exit code and its output.\n const installSpinner = spinner();\n installSpinner.start(spinnerMessages.installingDeps);\n\n const baseInstall = application.install();\n const baseInstalled = await baseInstall.install;\n const baseInstallResult = await baseInstall.result;\n\n if (!baseInstalled) {\n installSpinner.stop(spinnerMessages.depsFailed);\n\n failFatally({\n step: \"Dependency install\",\n detail: \"The project's dependencies were not installed.\",\n result: baseInstallResult,\n hints: installFailureHints(baseInstallResult),\n });\n }\n\n installSpinner.stop(spinnerMessages.depsInstalled);\n\n // Step 3: Add features via `warlock add --no-install`, then one batched install.\n // The DB driver, optional features, and AI providers all go through the single\n // source of truth (core's feature map) so versions never drift. When no\n // database was chosen, the driver is omitted (there is no `none` feature).\n const selectedFeatures = [...(noDatabase ? [] : [databaseDriver]), ...features, ...aiProviders];\n\n // Features the user asked for that are not in the project when we finish.\n const failedFeatures: { feature: string; reason: string }[] = [];\n let featureDependencyInstallFailed = false;\n\n if (selectedFeatures.length > 0) {\n const featuresSpinner = spinner();\n featuresSpinner.start(spinnerMessages.addingFeatures);\n\n let addedFeatures = selectedFeatures;\n\n if (!(await application.installFeatures(selectedFeatures))) {\n // The batch is all-or-nothing, so it cannot say WHICH feature broke it.\n // Retry them one at a time: every feature that can be added still gets\n // added, and every one that cannot gets its own reason.\n const batchFailure = takeLastCommandOutput();\n\n addedFeatures = [];\n\n for (const feature of selectedFeatures) {\n if (await application.installFeatures([feature])) {\n addedFeatures.push(feature);\n } else {\n failedFeatures.push({\n feature,\n reason: reasonFrom(takeLastCommandOutput() ?? batchFailure),\n });\n }\n }\n }\n\n let featureInstall: CommandResult | undefined;\n\n if (addedFeatures.length > 0) {\n // `warlock add` records the feature dependencies but does not reconcile\n // them with each other. The `web` feature brings `vite` while the\n // template always brings `vitest`, whose own vite range resolves to a\n // different major — two copies, and yarn 1 aborts the whole link phase\n // on them. Pin one copy BEFORE the batched install, not after.\n application.pinViteResolution();\n application.approveSelectedFeatureBuilds(addedFeatures);\n\n const install = application.install();\n const installed = await install.install;\n\n featureInstall = await install.result;\n\n if (!installed) {\n // The dependencies were recorded but never fetched — the features are\n // not usable, so they are failures, not successes.\n const reason = `their packages were not installed (${reasonFrom(featureInstall)})`;\n\n for (const feature of addedFeatures) {\n failedFeatures.push({ feature, reason });\n }\n\n addedFeatures = [];\n featureDependencyInstallFailed = true;\n }\n }\n\n if (failedFeatures.length === 0) {\n featuresSpinner.stop(spinnerMessages.featuresAdded);\n } else {\n featuresSpinner.stop(\n addedFeatures.length > 0 ? spinnerMessages.featuresPartial : spinnerMessages.featuresFailed,\n );\n\n problems.push({\n step: \"Features\",\n detail: failedFeatures\n .map(({ feature, reason }) => `${feature}: ${reason}`)\n .join(\"\\n \"),\n result: featureInstall?.ok === false ? featureInstall : undefined,\n hints: [\n `Retry inside the project with: npx warlock add ${failedFeatures\n .map(({ feature }) => feature)\n .join(\" \")}`,\n ],\n });\n }\n }\n\n // Step 4: Initialize Git repository if requested\n if (useGit) {\n const gitSpinner = spinner();\n gitSpinner.start(spinnerMessages.initializingGit);\n\n const initialized = await application.git();\n\n gitSpinner.stop(initialized ? spinnerMessages.gitInitialized : spinnerMessages.gitFailed);\n\n if (!initialized) {\n const failure = takeLastCommandOutput();\n\n problems.push({\n step: \"Git repository\",\n detail: `The repository was not initialized: ${reasonFrom(failure)}`,\n result: failure,\n hints: [\n \"Check that git is installed and that user.name / user.email are configured, then run `git init` yourself.\",\n ],\n });\n }\n }\n\n // Step 5: Generate JWT or warm cache\n if (useJWT && featureDependencyInstallFailed) {\n problems.push({\n step: \"JWT secrets\",\n detail: \"Skipped because the selected feature dependencies did not install.\",\n hints: [\n `Install the failed feature dependencies, then run \\`${runPackageManagerCommand(\"jwt\")}\\` inside the project.`,\n ],\n });\n } else if (useJWT) {\n const jwtSpinner = spinner();\n jwtSpinner.start(spinnerMessages.generatingJwt);\n\n const command = runPackageManagerCommand(\"jwt\");\n const generated = await application.exec(command);\n\n jwtSpinner.stop(generated ? spinnerMessages.jwtGenerated : spinnerMessages.jwtFailed);\n\n if (!generated) {\n const failure = takeLastCommandOutput();\n\n problems.push({\n step: \"JWT secrets\",\n detail: `No JWT secrets were written to .env: ${reasonFrom(failure)}`,\n result: failure,\n hints: [`Run \\`${command}\\` inside the project before starting it.`],\n });\n }\n } else {\n // The warm cache is a start-up optimisation, not something the user asked\n // for: report it, but it does not make the scaffold a failure.\n const warmSpinner = spinner();\n warmSpinner.start(spinnerMessages.warmingCache);\n\n const warmed = await application.exec(\"npx warlock --warm-cache\");\n\n warmSpinner.stop(warmed ? spinnerMessages.cacheWarmed : spinnerMessages.cacheWarmFailed);\n\n if (!warmed) takeLastCommandOutput();\n }\n\n // Step 6: Report what actually happened — the summary may only advertise\n // features that are really in the project.\n const requestedFeatures = [...features, ...aiProviders];\n const missingFeatures = failedFeatures\n .map(({ feature }) => feature)\n .filter((feature) => requestedFeatures.includes(feature));\n const installedFeatures = requestedFeatures.filter(\n (feature) => !missingFeatures.includes(feature),\n );\n\n if (problems.length > 0) {\n showProblems(problems);\n\n showPartialScreen({\n projectName: application.name,\n database: getDatabaseLabel(databaseDriver),\n features: installedFeatures,\n missingFeatures,\n packageManager: getPackageManager(),\n });\n\n printDecisionsSummary({ packageManager: getPackageManager(), options });\n\n return { ok: false, problems };\n }\n\n showSuccessScreen({\n projectName: application.name,\n database: getDatabaseLabel(databaseDriver),\n features: installedFeatures,\n packageManager: getPackageManager(),\n });\n\n printDecisionsSummary({ packageManager: getPackageManager(), options });\n\n return { ok: true, problems };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../../../../../../packages/create-warlock/src/commands/create-warlock-app/index.ts"],"sourcesContent":["import { getDatabaseLabel, isNoDatabase } from \"../../features/database-drivers\";\nimport { App } from \"../../helpers/app\";\nimport { CommandResult, takeLastCommandOutput } from \"../../helpers/exec\";\nimport { getPackageManager, runPackageManagerCommand } from \"../../helpers/package-manager\";\nimport { resolveWarlockVersions } from \"../../helpers/warlock-versions\";\nimport { printDecisionsSummary } from \"../../summary\";\nimport { showSuccessScreen } from \"../../ui/banner\";\nimport {\n failFatally,\n installFailureHints,\n Problem,\n showNotes,\n showPartialScreen,\n showProblems,\n} from \"../../ui/report\";\nimport { spinner } from \"../../ui/spinner\";\nimport { spinnerMessages } from \"../../ui/spinners\";\n\n/**\n * What the scaffold actually achieved. `ok` is false when ANY step the user\n * asked for did not happen; the caller turns that into a non-zero exit code so\n * a script never mistakes a half-built project for a finished one.\n */\nexport type ScaffoldOutcome = {\n ok: boolean;\n problems: Problem[];\n};\n\n/** One-line \"why did this fail\" pulled out of a captured command. */\nfunction reasonFrom(result: CommandResult | undefined): string {\n if (!result) return \"the command reported a failure (no output captured)\";\n\n if (result.error) {\n return result.error.message || String(result.error);\n }\n\n const lastLine = `${result.stderr}\\n${result.stdout}`\n .split(/\\r?\\n/)\n .map((line) => line.trim())\n .filter(Boolean)\n .pop();\n\n const status =\n result.code === null\n ? `killed by ${result.signal ?? \"an unknown signal\"}`\n : `exit code ${result.code}`;\n\n return lastLine ? `${status} — ${lastLine}` : status;\n}\n\nexport async function createWarlockApp(application: App): Promise<ScaffoldOutcome> {\n const options = application.options;\n const { useGit, useJWT, features, aiProviders, databaseDriver } = options;\n const noDatabase = isNoDatabase(databaseDriver);\n const problems: Problem[] = [];\n\n // Resolve the versions to pin BEFORE anything is written. The scaffolder's\n // own version is only used when the registry confirms it exists — see\n // helpers/warlock-versions.ts for why an unverified pin broke every install.\n const { versions, notes } = await resolveWarlockVersions();\n\n // Step 1: Initialize and copy template\n const templateSpinner = spinner();\n templateSpinner.start(spinnerMessages.copyingTemplate);\n\n try {\n application.init().use(\"warlock\").updatePackageJson(versions).updateDotEnv();\n\n // Wire the chosen database driver into .env — or, when the user opted out,\n // strip the database config entirely so the app boots with no database.\n if (noDatabase) {\n application.removeDatabaseConfig();\n } else {\n application.configureDatabaseEnv(databaseDriver);\n application.configureDatabaseConfig(databaseDriver);\n }\n\n application.configureWebStarter(features.includes(\"web\"));\n application.configureLintToolchain();\n application.configureAgentKitTargets(options.agents ?? [\"claude\"]);\n } catch (error) {\n templateSpinner.stop(spinnerMessages.templateFailed);\n\n failFatally({\n step: \"Template copy\",\n detail: `The project files could not be written: ${(error as Error).message}`,\n hints: [\n \"Check that the target directory is writable and that no file is locked by another process.\",\n ],\n });\n }\n\n templateSpinner.stop(spinnerMessages.templateCopied);\n\n showNotes([...notes]);\n\n // Step 2: Install base dependencies (so the `warlock` binary is available).\n // Nothing downstream works without this, so a failure ends the run — loudly,\n // with the command, its exit code and its output.\n const installSpinner = spinner();\n installSpinner.start(spinnerMessages.installingDeps);\n\n const baseInstall = application.install();\n const baseInstalled = await baseInstall.install;\n const baseInstallResult = await baseInstall.result;\n\n if (!baseInstalled) {\n installSpinner.stop(spinnerMessages.depsFailed);\n\n failFatally({\n step: \"Dependency install\",\n detail: \"The project's dependencies were not installed.\",\n result: baseInstallResult,\n hints: installFailureHints(baseInstallResult),\n });\n }\n\n installSpinner.stop(spinnerMessages.depsInstalled);\n\n // Step 3: Add features via `warlock add --no-install`, then one batched install.\n // The DB driver, optional features, and AI providers all go through the single\n // source of truth (core's feature map) so versions never drift. When no\n // database was chosen, the driver is omitted (there is no `none` feature).\n const selectedFeatures = [...(noDatabase ? [] : [databaseDriver]), ...features, ...aiProviders];\n\n // Features the user asked for that are not in the project when we finish.\n const failedFeatures: { feature: string; reason: string }[] = [];\n let featureDependencyInstallFailed = false;\n\n if (selectedFeatures.length > 0) {\n const featuresSpinner = spinner();\n featuresSpinner.start(spinnerMessages.addingFeatures);\n\n let addedFeatures = selectedFeatures;\n\n if (!(await application.installFeatures(selectedFeatures))) {\n // The batch is all-or-nothing, so it cannot say WHICH feature broke it.\n // Retry them one at a time: every feature that can be added still gets\n // added, and every one that cannot gets its own reason.\n const batchFailure = takeLastCommandOutput();\n\n addedFeatures = [];\n\n for (const feature of selectedFeatures) {\n if (await application.installFeatures([feature])) {\n addedFeatures.push(feature);\n } else {\n failedFeatures.push({\n feature,\n reason: reasonFrom(takeLastCommandOutput() ?? batchFailure),\n });\n }\n }\n }\n\n let featureInstall: CommandResult | undefined;\n\n if (addedFeatures.length > 0) {\n // `warlock add` records the feature dependencies but does not reconcile\n // them with each other. The `web` feature brings `vite` while the\n // template always brings `vitest`, whose own vite range resolves to a\n // different major — two copies, and yarn 1 aborts the whole link phase\n // on them. Pin one copy BEFORE the batched install, not after.\n application.pinViteResolution();\n application.approveSelectedFeatureBuilds(addedFeatures);\n\n const install = application.install();\n const installed = await install.install;\n\n featureInstall = await install.result;\n\n if (!installed) {\n // The dependencies were recorded but never fetched — the features are\n // not usable, so they are failures, not successes.\n const reason = `their packages were not installed (${reasonFrom(featureInstall)})`;\n\n for (const feature of addedFeatures) {\n failedFeatures.push({ feature, reason });\n }\n\n addedFeatures = [];\n featureDependencyInstallFailed = true;\n }\n }\n\n if (failedFeatures.length === 0) {\n featuresSpinner.stop(spinnerMessages.featuresAdded);\n } else {\n featuresSpinner.stop(\n addedFeatures.length > 0 ? spinnerMessages.featuresPartial : spinnerMessages.featuresFailed,\n );\n\n problems.push({\n step: \"Features\",\n detail: failedFeatures\n .map(({ feature, reason }) => `${feature}: ${reason}`)\n .join(\"\\n \"),\n result: featureInstall?.ok === false ? featureInstall : undefined,\n hints: [\n `Retry inside the project with: npx warlock add ${failedFeatures\n .map(({ feature }) => feature)\n .join(\" \")}`,\n ],\n });\n }\n }\n\n // Step 4: Initialize Git repository if requested\n if (useGit) {\n const gitSpinner = spinner();\n gitSpinner.start(spinnerMessages.initializingGit);\n\n const initialized = await application.git();\n\n gitSpinner.stop(initialized ? spinnerMessages.gitInitialized : spinnerMessages.gitFailed);\n\n if (!initialized) {\n const failure = takeLastCommandOutput();\n\n problems.push({\n step: \"Git repository\",\n detail: `The repository was not initialized: ${reasonFrom(failure)}`,\n result: failure,\n hints: [\n \"Check that git is installed and that user.name / user.email are configured, then run `git init` yourself.\",\n ],\n });\n }\n }\n\n // Step 5: Generate JWT or warm cache\n if (useJWT && featureDependencyInstallFailed) {\n problems.push({\n step: \"JWT secrets\",\n detail: \"Skipped because the selected feature dependencies did not install.\",\n hints: [\n `Install the failed feature dependencies, then run \\`${runPackageManagerCommand(\"jwt\")}\\` inside the project.`,\n ],\n });\n } else if (useJWT) {\n const jwtSpinner = spinner();\n jwtSpinner.start(spinnerMessages.generatingJwt);\n\n const command = runPackageManagerCommand(\"jwt\");\n const generated = await application.exec(command);\n\n jwtSpinner.stop(generated ? spinnerMessages.jwtGenerated : spinnerMessages.jwtFailed);\n\n if (!generated) {\n const failure = takeLastCommandOutput();\n\n problems.push({\n step: \"JWT secrets\",\n detail: `No JWT secrets were written to .env: ${reasonFrom(failure)}`,\n result: failure,\n hints: [`Run \\`${command}\\` inside the project before starting it.`],\n });\n }\n } else {\n // The warm cache is a start-up optimisation, not something the user asked\n // for: report it, but it does not make the scaffold a failure.\n const warmSpinner = spinner();\n warmSpinner.start(spinnerMessages.warmingCache);\n\n const warmed = await application.exec(\"npx warlock --warm-cache\");\n\n warmSpinner.stop(warmed ? spinnerMessages.cacheWarmed : spinnerMessages.cacheWarmFailed);\n\n if (!warmed) takeLastCommandOutput();\n }\n\n // Step 6: Report what actually happened — the summary may only advertise\n // features that are really in the project.\n const requestedFeatures = [...features, ...aiProviders];\n const missingFeatures = failedFeatures\n .map(({ feature }) => feature)\n .filter((feature) => requestedFeatures.includes(feature));\n const installedFeatures = requestedFeatures.filter(\n (feature) => !missingFeatures.includes(feature),\n );\n\n if (problems.length > 0) {\n showProblems(problems);\n\n showPartialScreen({\n projectName: application.name,\n database: getDatabaseLabel(databaseDriver),\n features: installedFeatures,\n missingFeatures,\n packageManager: getPackageManager(),\n });\n\n printDecisionsSummary({ packageManager: getPackageManager(), options });\n\n return { ok: false, problems };\n }\n\n showSuccessScreen({\n projectName: application.name,\n database: getDatabaseLabel(databaseDriver),\n features: installedFeatures,\n packageManager: getPackageManager(),\n });\n\n printDecisionsSummary({ packageManager: getPackageManager(), options });\n\n return { ok: true, problems };\n}\n"],"mappings":"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"}
@@ -34,8 +34,8 @@ const FLAG_DEFINITIONS = [
34
34
  defaultValue: "inferred from the invoking agent (npm_config_user_agent) or the system"
35
35
  },
36
36
  {
37
- usage: "--lint=<oxc|eslint>",
38
- description: "Linter and formatter: Oxlint/Oxfmt or ESLint/Prettier",
37
+ usage: "--lint=<oxc>",
38
+ description: "Linter and formatter: Oxlint/Oxfmt",
39
39
  defaultValue: "oxc"
40
40
  },
41
41
  {
@@ -1 +1 @@
1
- {"version":3,"file":"definitions.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/flags/definitions.ts"],"sourcesContent":["import { getAiPackageOptions, getAiProviderOptions } from \"../features/features-map\";\n\n/**\n * Single source of truth for every CLI flag's help text and default, so\n * `--help` can never drift out of sync with what the parser actually\n * understands (requirement: `--help` lists every flag together with its\n * default value).\n */\nexport type FlagDefinition = {\n /** How the flag reads on the command line, e.g. `--db=<driver>`. */\n usage: string;\n description: string;\n /** Human-readable default, or what happens when it is left unanswered. */\n defaultValue: string;\n};\n\nconst AI_FEATURE_KEYS = [...getAiProviderOptions(), ...getAiPackageOptions()].map(\n ({ value }) => value,\n);\n\nexport const FLAG_DEFINITIONS: FlagDefinition[] = [\n {\n usage: \"--name=<name>\",\n description: \"Project name (or pass it as the first positional arg)\",\n defaultValue: \"required — prompted if a TTY is available, otherwise fails naming this flag\",\n },\n {\n usage: \"--stack=<api|web>\",\n description:\n \"API-only or full-stack web — the one structural choice that changes the app's shape. Customizing everything else is a flow, not a stack: use --customize, where --stack=web arrives as a pre-ticked web feature\",\n defaultValue: \"api\",\n },\n {\n usage: \"--db=<driver> / --no-db\",\n description: \"Database driver (e.g. postgres, mongodb), or skip a database entirely\",\n defaultValue: \"mongodb\",\n },\n {\n usage: \"--features=<list>\",\n description: \"Comma-separated feature keys (e.g. test,herald)\",\n defaultValue: 'none (or [\"web\"] when --stack=web and --features is not given)',\n },\n {\n usage: \"--ai=<list>\",\n description: `Comma-separated AI provider or capability keys (${AI_FEATURE_KEYS.join(\", \")})`,\n defaultValue: \"none\",\n },\n {\n usage: \"--pm=<manager>\",\n description: \"Package manager to use (npm, yarn, pnpm, bun)\",\n defaultValue: \"inferred from the invoking agent (npm_config_user_agent) or the system\",\n },\n {\n usage: \"--lint=<oxc|eslint>\",\n description: \"Linter and formatter: Oxlint/Oxfmt or ESLint/Prettier\",\n defaultValue: \"oxc\",\n },\n {\n usage: \"--agents=<list>\",\n description: \"Comma-separated agent-kit targets to derive docs/skills for\",\n defaultValue: \"claude\",\n },\n {\n usage: \"--git / --no-git\",\n description: \"Force-enable or force-disable git initialization\",\n defaultValue: \"false\",\n },\n {\n usage: \"--jwt / --no-jwt\",\n description: \"Force-enable or force-disable JWT secret generation\",\n defaultValue: \"false\",\n },\n {\n usage: \"-y, --yes\",\n description: \"Skip every prompt and accept defaults for anything unset\",\n defaultValue: \"false\",\n },\n {\n usage: \"--customize, --interactive\",\n description:\n 'Full long-form wizard — every choice asked one by one. Also selectable as \"Customize\" in the \"What are we building?\" menu. Any other flag you pass pre-selects its prompt; --yes is refused',\n defaultValue: \"false\",\n },\n {\n usage: \"-h, --help\",\n description: \"Show this help message and exit\",\n defaultValue: \"n/a\",\n },\n {\n usage: \"-v, --version\",\n description: \"Show the installed create-warlock version and exit\",\n defaultValue: \"n/a\",\n },\n];\n\n/** Render the full `--help` text, one line per flag with its default. */\nexport function buildHelpText(): string {\n const rows = FLAG_DEFINITIONS.map(({ usage, description, defaultValue }) => {\n const label = usage.padEnd(28);\n return ` ${label}${description} (default: ${defaultValue})`;\n }).join(\"\\n\");\n\n return `\n create-warlock — scaffold a new Warlock.js project\n\n Usage\n $ create-warlock [project-name] [options]\n\n Options\n${rows}\n\n Example\n $ create-warlock my-app --db=postgres --features=test,herald --yes\n`;\n}\n"],"mappings":";;;AAgBA,MAAM,kBAAkB,CAAC,GAAG,qBAAqB,GAAG,GAAG,oBAAoB,CAAC,CAAC,CAAC,KAC3E,EAAE,YAAY,KACjB;AAEA,MAAa,mBAAqC;CAChD;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aACE;EACF,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa,mDAAmD,gBAAgB,KAAK,IAAI,EAAE;EAC3F,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aACE;EACF,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;AACF;;AAGA,SAAgB,gBAAwB;CAMtC,OAAO;;;;;;;EALM,iBAAiB,KAAK,EAAE,OAAO,aAAa,mBAAmB;EAE1E,OAAO,OADO,MAAM,OAAO,EACT,IAAI,YAAY,aAAa,aAAa;CAC9D,CAAC,CAAC,CAAC,KAAK,IASL,EAAE;;;;;AAKP"}
1
+ {"version":3,"file":"definitions.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/flags/definitions.ts"],"sourcesContent":["import { getAiPackageOptions, getAiProviderOptions } from \"../features/features-map\";\n\n/**\n * Single source of truth for every CLI flag's help text and default, so\n * `--help` can never drift out of sync with what the parser actually\n * understands (requirement: `--help` lists every flag together with its\n * default value).\n */\nexport type FlagDefinition = {\n /** How the flag reads on the command line, e.g. `--db=<driver>`. */\n usage: string;\n description: string;\n /** Human-readable default, or what happens when it is left unanswered. */\n defaultValue: string;\n};\n\nconst AI_FEATURE_KEYS = [...getAiProviderOptions(), ...getAiPackageOptions()].map(\n ({ value }) => value,\n);\n\nexport const FLAG_DEFINITIONS: FlagDefinition[] = [\n {\n usage: \"--name=<name>\",\n description: \"Project name (or pass it as the first positional arg)\",\n defaultValue: \"required — prompted if a TTY is available, otherwise fails naming this flag\",\n },\n {\n usage: \"--stack=<api|web>\",\n description:\n \"API-only or full-stack web — the one structural choice that changes the app's shape. Customizing everything else is a flow, not a stack: use --customize, where --stack=web arrives as a pre-ticked web feature\",\n defaultValue: \"api\",\n },\n {\n usage: \"--db=<driver> / --no-db\",\n description: \"Database driver (e.g. postgres, mongodb), or skip a database entirely\",\n defaultValue: \"mongodb\",\n },\n {\n usage: \"--features=<list>\",\n description: \"Comma-separated feature keys (e.g. test,herald)\",\n defaultValue: 'none (or [\"web\"] when --stack=web and --features is not given)',\n },\n {\n usage: \"--ai=<list>\",\n description: `Comma-separated AI provider or capability keys (${AI_FEATURE_KEYS.join(\", \")})`,\n defaultValue: \"none\",\n },\n {\n usage: \"--pm=<manager>\",\n description: \"Package manager to use (npm, yarn, pnpm, bun)\",\n defaultValue: \"inferred from the invoking agent (npm_config_user_agent) or the system\",\n },\n {\n usage: \"--lint=<oxc>\",\n description: \"Linter and formatter: Oxlint/Oxfmt\",\n defaultValue: \"oxc\",\n },\n {\n usage: \"--agents=<list>\",\n description: \"Comma-separated agent-kit targets to derive docs/skills for\",\n defaultValue: \"claude\",\n },\n {\n usage: \"--git / --no-git\",\n description: \"Force-enable or force-disable git initialization\",\n defaultValue: \"false\",\n },\n {\n usage: \"--jwt / --no-jwt\",\n description: \"Force-enable or force-disable JWT secret generation\",\n defaultValue: \"false\",\n },\n {\n usage: \"-y, --yes\",\n description: \"Skip every prompt and accept defaults for anything unset\",\n defaultValue: \"false\",\n },\n {\n usage: \"--customize, --interactive\",\n description:\n 'Full long-form wizard — every choice asked one by one. Also selectable as \"Customize\" in the \"What are we building?\" menu. Any other flag you pass pre-selects its prompt; --yes is refused',\n defaultValue: \"false\",\n },\n {\n usage: \"-h, --help\",\n description: \"Show this help message and exit\",\n defaultValue: \"n/a\",\n },\n {\n usage: \"-v, --version\",\n description: \"Show the installed create-warlock version and exit\",\n defaultValue: \"n/a\",\n },\n];\n\n/** Render the full `--help` text, one line per flag with its default. */\nexport function buildHelpText(): string {\n const rows = FLAG_DEFINITIONS.map(({ usage, description, defaultValue }) => {\n const label = usage.padEnd(28);\n return ` ${label}${description} (default: ${defaultValue})`;\n }).join(\"\\n\");\n\n return `\n create-warlock — scaffold a new Warlock.js project\n\n Usage\n $ create-warlock [project-name] [options]\n\n Options\n${rows}\n\n Example\n $ create-warlock my-app --db=postgres --features=test,herald --yes\n`;\n}\n"],"mappings":";;;AAgBA,MAAM,kBAAkB,CAAC,GAAG,qBAAqB,GAAG,GAAG,oBAAoB,CAAC,CAAC,CAAC,KAC3E,EAAE,YAAY,KACjB;AAEA,MAAa,mBAAqC;CAChD;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aACE;EACF,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa,mDAAmD,gBAAgB,KAAK,IAAI,EAAE;EAC3F,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aACE;EACF,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;CACA;EACE,OAAO;EACP,aAAa;EACb,cAAc;CAChB;AACF;;AAGA,SAAgB,gBAAwB;CAMtC,OAAO;;;;;;;EALM,iBAAiB,KAAK,EAAE,OAAO,aAAa,mBAAmB;EAE1E,OAAO,OADO,MAAM,OAAO,EACT,IAAI,YAAY,aAAa,aAAa;CAC9D,CAAC,CAAC,CAAC,KAAK,IASL,EAAE;;;;;AAKP"}
@@ -5,7 +5,7 @@ import { getPreferredPackageManager } from "../helpers/package-manager.mjs";
5
5
  function seedFromFlags(cli) {
6
6
  return {
7
7
  packageManager: cli.pm ?? getPreferredPackageManager(),
8
- lint: cli.lint === "eslint" ? "eslint" : "oxc",
8
+ lint: "oxc",
9
9
  databaseDriver: cli.db,
10
10
  features: cli.features ?? defaultFeatures(cli),
11
11
  aiProviders: cli.ai ?? [],
@@ -1 +1 @@
1
- {"version":3,"file":"seed-from-flags.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/flags/seed-from-flags.ts"],"sourcesContent":["import type { CliFlags, LintToolchain } from \"../commands/create-new-app/types\";\nimport { getDefaultFeatureKeys } from \"../features/features-map\";\nimport { getPreferredPackageManager } from \"../helpers/package-manager\";\n\n/**\n * The wizard's prompts, pre-selected from whatever the user already spelled\n * out on the command line.\n *\n * Before this existed, `--customize --db=postgres` silently threw the driver\n * away: the wizard asked its own question with its own default and the flag\n * never reached anything. A flag that is accepted and then ignored is the\n * worst of both worlds — the run succeeds, and the answer is wrong.\n *\n * So a flag passed alongside `--customize` is not a contradiction to refuse,\n * it is an answer given early: the prompt opens on that value and the user\n * can still change it.\n */\nexport type WizardSeeds = {\n packageManager: string;\n databaseDriver: string | undefined;\n features: string[];\n aiProviders: string[];\n useGit: boolean;\n useJWT: boolean;\n lint: LintToolchain;\n};\n\nexport function seedFromFlags(cli: CliFlags): WizardSeeds {\n return {\n packageManager: cli.pm ?? getPreferredPackageManager(),\n lint: cli.lint === \"eslint\" ? \"eslint\" : \"oxc\",\n databaseDriver: cli.db,\n features: cli.features ?? defaultFeatures(cli),\n aiProviders: cli.ai ?? [],\n // clack's confirm opens on `true` when nothing is passed, which is the\n // behaviour these two prompts already had.\n useGit: cli.git ?? true,\n useJWT: cli.jwt ?? true,\n };\n}\n\n/**\n * `--stack=web` is the one structural answer, and the wizard has no stack\n * question of its own — it asks for features instead. So the stack arrives as\n * a pre-ticked `web` feature rather than being dropped on the floor.\n */\nfunction defaultFeatures(cli: CliFlags): string[] {\n const defaults = getDefaultFeatureKeys();\n\n if (cli.stack !== \"web\") return defaults;\n\n return [...new Set([...defaults, \"web\"])];\n}\n"],"mappings":";;;;AA2BA,SAAgB,cAAc,KAA4B;CACxD,OAAO;EACL,gBAAgB,IAAI,MAAM,2BAA2B;EACrD,MAAM,IAAI,SAAS,WAAW,WAAW;EACzC,gBAAgB,IAAI;EACpB,UAAU,IAAI,YAAY,gBAAgB,GAAG;EAC7C,aAAa,IAAI,MAAM,CAAC;EAGxB,QAAQ,IAAI,OAAO;EACnB,QAAQ,IAAI,OAAO;CACrB;AACF;;;;;;AAOA,SAAS,gBAAgB,KAAyB;CAChD,MAAM,WAAW,sBAAsB;CAEvC,IAAI,IAAI,UAAU,OAAO,OAAO;CAEhC,OAAO,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC;AAC1C"}
1
+ {"version":3,"file":"seed-from-flags.mjs","names":[],"sources":["../../../../../../../packages/create-warlock/src/flags/seed-from-flags.ts"],"sourcesContent":["import type { CliFlags, LintToolchain } from \"../commands/create-new-app/types\";\nimport { getDefaultFeatureKeys } from \"../features/features-map\";\nimport { getPreferredPackageManager } from \"../helpers/package-manager\";\n\n/**\n * The wizard's prompts, pre-selected from whatever the user already spelled\n * out on the command line.\n *\n * Before this existed, `--customize --db=postgres` silently threw the driver\n * away: the wizard asked its own question with its own default and the flag\n * never reached anything. A flag that is accepted and then ignored is the\n * worst of both worlds — the run succeeds, and the answer is wrong.\n *\n * So a flag passed alongside `--customize` is not a contradiction to refuse,\n * it is an answer given early: the prompt opens on that value and the user\n * can still change it.\n */\nexport type WizardSeeds = {\n packageManager: string;\n databaseDriver: string | undefined;\n features: string[];\n aiProviders: string[];\n useGit: boolean;\n useJWT: boolean;\n lint: LintToolchain;\n};\n\nexport function seedFromFlags(cli: CliFlags): WizardSeeds {\n return {\n packageManager: cli.pm ?? getPreferredPackageManager(),\n lint: \"oxc\",\n databaseDriver: cli.db,\n features: cli.features ?? defaultFeatures(cli),\n aiProviders: cli.ai ?? [],\n // clack's confirm opens on `true` when nothing is passed, which is the\n // behaviour these two prompts already had.\n useGit: cli.git ?? true,\n useJWT: cli.jwt ?? true,\n };\n}\n\n/**\n * `--stack=web` is the one structural answer, and the wizard has no stack\n * question of its own — it asks for features instead. So the stack arrives as\n * a pre-ticked `web` feature rather than being dropped on the floor.\n */\nfunction defaultFeatures(cli: CliFlags): string[] {\n const defaults = getDefaultFeatureKeys();\n\n if (cli.stack !== \"web\") return defaults;\n\n return [...new Set([...defaults, \"web\"])];\n}\n"],"mappings":";;;;AA2BA,SAAgB,cAAc,KAA4B;CACxD,OAAO;EACL,gBAAgB,IAAI,MAAM,2BAA2B;EACrD,MAAM;EACN,gBAAgB,IAAI;EACpB,UAAU,IAAI,YAAY,gBAAgB,GAAG;EAC7C,aAAa,IAAI,MAAM,CAAC;EAGxB,QAAQ,IAAI,OAAO;EACnB,QAAQ,IAAI,OAAO;CACrB;AACF;;;;;;AAOA,SAAS,gBAAgB,KAAyB;CAChD,MAAM,WAAW,sBAAsB;CAEvC,IAAI,IAAI,UAAU,OAAO,OAAO;CAEhC,OAAO,CAAC,mBAAG,IAAI,IAAI,CAAC,GAAG,UAAU,KAAK,CAAC,CAAC;AAC1C"}