prisma-next 0.7.0 → 0.8.0-dev.10

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 (124) hide show
  1. package/README.md +1 -1
  2. package/dist/{cli-errors-D3_sMh2K.mjs → cli-errors-CF60g2cG.mjs} +40 -2
  3. package/dist/cli-errors-CF60g2cG.mjs.map +1 -0
  4. package/dist/cli.mjs +70 -21
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{client-BCnP7cHo.mjs → client-XkUw4xD0.mjs} +17 -13
  7. package/dist/client-XkUw4xD0.mjs.map +1 -0
  8. package/dist/{command-helpers-BeZHkxV8.mjs → command-helpers-D3vL5yi8.mjs} +29 -6
  9. package/dist/command-helpers-D3vL5yi8.mjs.map +1 -0
  10. package/dist/commands/contract-emit.mjs +1 -1
  11. package/dist/commands/contract-infer.mjs +1 -1
  12. package/dist/commands/db-init.mjs +7 -7
  13. package/dist/commands/db-schema.mjs +5 -5
  14. package/dist/commands/db-sign.d.mts.map +1 -1
  15. package/dist/commands/db-sign.mjs +67 -25
  16. package/dist/commands/db-sign.mjs.map +1 -1
  17. package/dist/commands/db-update.d.mts.map +1 -1
  18. package/dist/commands/db-update.mjs +37 -9
  19. package/dist/commands/db-update.mjs.map +1 -1
  20. package/dist/commands/db-verify.mjs +1 -1
  21. package/dist/commands/migrate.d.mts +28 -0
  22. package/dist/commands/migrate.d.mts.map +1 -0
  23. package/dist/commands/{migration-apply.mjs → migrate.mjs} +54 -36
  24. package/dist/commands/migrate.mjs.map +1 -0
  25. package/dist/commands/migration-check.d.mts +18 -0
  26. package/dist/commands/migration-check.d.mts.map +1 -0
  27. package/dist/commands/migration-check.mjs +284 -0
  28. package/dist/commands/migration-check.mjs.map +1 -0
  29. package/dist/commands/migration-graph.d.mts +16 -0
  30. package/dist/commands/migration-graph.d.mts.map +1 -0
  31. package/dist/commands/migration-graph.mjs +141 -0
  32. package/dist/commands/migration-graph.mjs.map +1 -0
  33. package/dist/commands/migration-list.d.mts +20 -0
  34. package/dist/commands/migration-list.d.mts.map +1 -0
  35. package/dist/commands/migration-list.mjs +107 -0
  36. package/dist/commands/migration-list.mjs.map +1 -0
  37. package/dist/commands/migration-log.d.mts +21 -0
  38. package/dist/commands/migration-log.d.mts.map +1 -0
  39. package/dist/commands/migration-log.mjs +146 -0
  40. package/dist/commands/migration-log.mjs.map +1 -0
  41. package/dist/commands/migration-new.d.mts.map +1 -1
  42. package/dist/commands/migration-new.mjs +21 -20
  43. package/dist/commands/migration-new.mjs.map +1 -1
  44. package/dist/commands/migration-plan.d.mts +2 -2
  45. package/dist/commands/migration-plan.d.mts.map +1 -1
  46. package/dist/commands/migration-plan.mjs +1 -1
  47. package/dist/commands/migration-show.d.mts +1 -1
  48. package/dist/commands/migration-show.d.mts.map +1 -1
  49. package/dist/commands/migration-show.mjs +85 -47
  50. package/dist/commands/migration-show.mjs.map +1 -1
  51. package/dist/commands/migration-status.d.mts +3 -15
  52. package/dist/commands/migration-status.d.mts.map +1 -1
  53. package/dist/commands/migration-status.mjs +732 -1
  54. package/dist/commands/migration-status.mjs.map +1 -0
  55. package/dist/commands/ref.d.mts +34 -0
  56. package/dist/commands/ref.d.mts.map +1 -0
  57. package/dist/commands/{migration-ref.mjs → ref.mjs} +28 -57
  58. package/dist/commands/ref.mjs.map +1 -0
  59. package/dist/{contract-emit-B77TsJqf.mjs → contract-emit-CgoFk9AU.mjs} +8 -4
  60. package/dist/contract-emit-CgoFk9AU.mjs.map +1 -0
  61. package/dist/{contract-emit-9DBda5Ou.mjs → contract-emit-GpxW5RLe.mjs} +6 -6
  62. package/dist/{contract-emit-9DBda5Ou.mjs.map → contract-emit-GpxW5RLe.mjs.map} +1 -1
  63. package/dist/{contract-infer-ByxhPjpW.mjs → contract-infer-D8edZOCi.mjs} +5 -5
  64. package/dist/{contract-infer-ByxhPjpW.mjs.map → contract-infer-D8edZOCi.mjs.map} +1 -1
  65. package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs → contract-space-aggregate-loader-D68YpuPR.mjs} +3 -3
  66. package/dist/{contract-space-aggregate-loader-BrwKK6Q6.mjs.map → contract-space-aggregate-loader-D68YpuPR.mjs.map} +1 -1
  67. package/dist/{db-verify-Czm5T-J4.mjs → db-verify-DtRB9iHJ.mjs} +7 -7
  68. package/dist/{db-verify-Czm5T-J4.mjs.map → db-verify-DtRB9iHJ.mjs.map} +1 -1
  69. package/dist/errors-Cw6kyTyV.mjs +56 -0
  70. package/dist/errors-Cw6kyTyV.mjs.map +1 -0
  71. package/dist/exports/control-api.d.mts +1 -1
  72. package/dist/exports/control-api.d.mts.map +1 -1
  73. package/dist/exports/control-api.mjs +2 -2
  74. package/dist/exports/index.mjs +1 -1
  75. package/dist/exports/init-output.mjs +1 -1
  76. package/dist/{framework-components-ChqVUxR-.mjs → framework-components-xFLFpZUO.mjs} +2 -2
  77. package/dist/{framework-components-ChqVUxR-.mjs.map → framework-components-xFLFpZUO.mjs.map} +1 -1
  78. package/dist/{global-flags-Icqpxk23.d.mts → global-flags-DGmw6Kqg.d.mts} +1 -1
  79. package/dist/{global-flags-Icqpxk23.d.mts.map → global-flags-DGmw6Kqg.d.mts.map} +1 -1
  80. package/dist/{migration-status-By9G5p2H.mjs → graph-render-eJDcLWny.mjs} +3 -692
  81. package/dist/graph-render-eJDcLWny.mjs.map +1 -0
  82. package/dist/{init-BRKnARU6.mjs → init-Dm0QZPUA.mjs} +412 -208
  83. package/dist/init-Dm0QZPUA.mjs.map +1 -0
  84. package/dist/{inspect-live-schema-DxdBd4Er.mjs → inspect-live-schema-CPPqCips.mjs} +4 -4
  85. package/dist/{inspect-live-schema-DxdBd4Er.mjs.map → inspect-live-schema-CPPqCips.mjs.map} +1 -1
  86. package/dist/migration-cli.mjs +1 -1
  87. package/dist/migration-cli.mjs.map +1 -1
  88. package/dist/{migration-command-scaffold-BdV8JYXV.mjs → migration-command-scaffold-B_ezTTwX.mjs} +4 -4
  89. package/dist/{migration-command-scaffold-BdV8JYXV.mjs.map → migration-command-scaffold-B_ezTTwX.mjs.map} +1 -1
  90. package/dist/{migration-plan-mRu5K81L.mjs → migration-plan-DWB-NTxH.mjs} +62 -30
  91. package/dist/migration-plan-DWB-NTxH.mjs.map +1 -0
  92. package/dist/migration-types-D2FW63pr.d.mts +15 -0
  93. package/dist/migration-types-D2FW63pr.d.mts.map +1 -0
  94. package/dist/{migrations-CTsyBXCA.mjs → migrations-DyUf5lTt.mjs} +2 -2
  95. package/dist/migrations-DyUf5lTt.mjs.map +1 -0
  96. package/dist/{output-B16Kefzx.mjs → output-B60Gw5fu.mjs} +12 -11
  97. package/dist/{output-B16Kefzx.mjs.map → output-B60Gw5fu.mjs.map} +1 -1
  98. package/dist/{result-handler-rmPVKIP2.mjs → result-handler-Bm_6dDYg.mjs} +2 -2
  99. package/dist/{result-handler-rmPVKIP2.mjs.map → result-handler-Bm_6dDYg.mjs.map} +1 -1
  100. package/dist/{terminal-ui-C_hFNbAn.mjs → terminal-ui-XtOQsqe9.mjs} +2 -54
  101. package/dist/terminal-ui-XtOQsqe9.mjs.map +1 -0
  102. package/dist/{types-LItU7E4l.d.mts → types-BS_wpjAY.d.mts} +2 -2
  103. package/dist/{types-LItU7E4l.d.mts.map → types-BS_wpjAY.d.mts.map} +1 -1
  104. package/dist/{verify-CiwNWM9N.mjs → verify-D7ypCCe6.mjs} +1 -1
  105. package/dist/{verify-CiwNWM9N.mjs.map → verify-D7ypCCe6.mjs.map} +1 -1
  106. package/package.json +11 -11
  107. package/dist/agent-skill-mongo.md +0 -138
  108. package/dist/agent-skill-postgres.md +0 -106
  109. package/dist/cli-errors-D3_sMh2K.mjs.map +0 -1
  110. package/dist/client-BCnP7cHo.mjs.map +0 -1
  111. package/dist/command-helpers-BeZHkxV8.mjs.map +0 -1
  112. package/dist/commands/migration-apply.d.mts +0 -51
  113. package/dist/commands/migration-apply.d.mts.map +0 -1
  114. package/dist/commands/migration-apply.mjs.map +0 -1
  115. package/dist/commands/migration-ref.d.mts +0 -45
  116. package/dist/commands/migration-ref.d.mts.map +0 -1
  117. package/dist/commands/migration-ref.mjs.map +0 -1
  118. package/dist/contract-emit-B77TsJqf.mjs.map +0 -1
  119. package/dist/init-BRKnARU6.mjs.map +0 -1
  120. package/dist/migration-plan-mRu5K81L.mjs.map +0 -1
  121. package/dist/migration-status-By9G5p2H.mjs.map +0 -1
  122. package/dist/migrations-CTsyBXCA.mjs.map +0 -1
  123. package/dist/terminal-ui-C_hFNbAn.mjs.map +0 -1
  124. /package/dist/{cli-errors-B9OBbled.d.mts → cli-errors-DdcjVLJV.d.mts} +0 -0
@@ -1,6 +1,7 @@
1
- import { t as CliStructuredError } from "./cli-errors-D3_sMh2K.mjs";
2
- import { i as formatErrorOutput, r as formatErrorJson, t as TerminalUI } from "./terminal-ui-C_hFNbAn.mjs";
3
- import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-B16Kefzx.mjs";
1
+ import { t as CliStructuredError } from "./cli-errors-CF60g2cG.mjs";
2
+ import { n as formatErrorOutput, t as formatErrorJson } from "./errors-Cw6kyTyV.mjs";
3
+ import { t as TerminalUI } from "./terminal-ui-XtOQsqe9.mjs";
4
+ import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-B60Gw5fu.mjs";
4
5
  import { createRequire } from "node:module";
5
6
  import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
6
7
  import * as clack from "@clack/prompts";
@@ -9,158 +10,8 @@ import { execFile } from "node:child_process";
9
10
  import { promisify } from "node:util";
10
11
  import { detect, getUserAgent } from "package-manager-detector/detect";
11
12
  import { applyEdits, modify, parse, printParseErrorCode } from "jsonc-parser";
12
- //#region src/commands/init/detect-package-manager.ts
13
- const KNOWN = new Set([
14
- "pnpm",
15
- "npm",
16
- "yarn",
17
- "bun",
18
- "deno"
19
- ]);
20
- /**
21
- * Resolves the package manager `init` should drive for `add` / `install`
22
- * commands. Tries, in order:
23
- *
24
- * 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
25
- * `packageManager` field, the `devEngines.packageManager` field, or
26
- * install metadata. This is the right answer whenever the user is
27
- * anywhere inside an existing project, including a deep workspace
28
- * subdirectory.
29
- *
30
- * 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
31
- * every PM sets when it spawns a script. This catches the
32
- * bare-directory case where there's no project to walk up to but the
33
- * user invoked us via `pnpm dlx prisma-next init` / `bunx
34
- * prisma-next init` / `yarn dlx …`. Same signal used by every
35
- * `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
36
- * `create-astro`, `@antfu/ni`, …).
37
- *
38
- * 3. **`npm`** — final fallback. Always present alongside Node.
39
- */
40
- async function detectPackageManager(cwd) {
41
- const detected = await detect({ cwd });
42
- if (detected && KNOWN.has(detected.name)) return detected.name;
43
- const userAgent = getUserAgent();
44
- if (userAgent !== null && KNOWN.has(userAgent)) return userAgent;
45
- return "npm";
46
- }
47
- function hasProjectManifest(cwd) {
48
- return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
49
- }
50
- function formatRunCommand(pm, bin, args) {
51
- if (pm === "npm") return `npx ${bin} ${args}`;
52
- if (pm === "deno") return `deno run npm:${bin} ${args}`;
53
- return `${pm} ${bin} ${args}`;
54
- }
55
- function formatAddArgs(pm, packages) {
56
- if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
57
- return ["add", ...packages];
58
- }
59
- function formatAddDevArgs(pm, packages) {
60
- if (pm === "deno") return [
61
- "add",
62
- "--dev",
63
- ...packages.map((p) => `npm:${p}`)
64
- ];
65
- return [
66
- "add",
67
- "-D",
68
- ...packages
69
- ];
70
- }
71
- //#endregion
72
- //#region src/commands/init/detect-pnpm-catalog.ts
73
- /**
74
- * Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
75
- * its top-level `catalog:` block for entries that match any of `packages`.
76
- *
77
- * Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
78
- * inside a pnpm workspace whose catalog overrides one of the packages it
79
- * installs, surface a structured warning so the user knows the catalog
80
- * version (not the published `latest`) is what ended up in their
81
- * `node_modules`. pnpm itself does this silently; the warning closes the
82
- * "looks fine, must be wrong version six months later" gap.
83
- *
84
- * Notes / scope:
85
- *
86
- * - We only inspect the unnamed top-level `catalog:` block. pnpm also
87
- * supports `catalogs:` (plural — *named* catalogs referenced via
88
- * `catalog:foo` specifiers); those don't apply to a vanilla
89
- * `pnpm add prisma-next` invocation, so we skip them.
90
- * - We don't validate YAML syntax exhaustively. The file format pnpm
91
- * ships is line-oriented and well-known; a minimal regex is more
92
- * robust than depending on a YAML parser for one warning.
93
- * - We don't compare against the registry's `latest` — pnpm uses the
94
- * catalog version regardless, so the warning fires whenever a match
95
- * exists. The user-facing copy explains how to opt out.
96
- */
97
- function detectPnpmCatalogOverrides(baseDir, packages) {
98
- const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
99
- if (workspaceFile === null) return null;
100
- const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
101
- if (catalog === null) return {
102
- workspaceFile,
103
- entries: []
104
- };
105
- const wanted = new Set(packages);
106
- const entries = [];
107
- for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
108
- name,
109
- version
110
- });
111
- return {
112
- workspaceFile,
113
- entries
114
- };
115
- }
116
- function findNearestPnpmWorkspaceFile(baseDir) {
117
- let dir = baseDir;
118
- let prev = "";
119
- while (dir !== prev) {
120
- const candidate = join(dir, "pnpm-workspace.yaml");
121
- if (existsSync(candidate)) return candidate;
122
- prev = dir;
123
- dir = dirname(dir);
124
- }
125
- return null;
126
- }
127
- /**
128
- * Returns the entries inside the top-level `catalog:` block as `[name, version]`
129
- * pairs in document order, or `null` when no `catalog:` block exists.
130
- *
131
- * The parser is intentionally minimal: it reads line-by-line, locates the
132
- * top-level `catalog:` line (no leading whitespace), then collects every
133
- * subsequent indented line of the form `<key>: <value>` until the next
134
- * top-level key (or end of file). Quotes around `<key>` and `<value>`
135
- * are stripped; comments (`#…`) are ignored.
136
- */
137
- function extractCatalogBlock(contents) {
138
- const lines = contents.split(/\r?\n/);
139
- const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
140
- if (startIdx === -1) return null;
141
- const entries = [];
142
- for (let i = startIdx + 1; i < lines.length; i++) {
143
- const raw = lines[i] ?? "";
144
- if (raw.trim() === "" || /^\s*#/.test(raw)) continue;
145
- if (!/^\s/.test(raw)) break;
146
- const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
147
- if (!match) continue;
148
- const name = match[1] ?? match[2] ?? match[3];
149
- if (name === void 0) continue;
150
- const version = stripQuotes((match[4] ?? "").trim());
151
- if (version === "") continue;
152
- entries.push([name, version]);
153
- }
154
- return entries;
155
- }
156
- function stripQuotes(value) {
157
- if (value.length >= 2) {
158
- const first = value[0];
159
- const last = value[value.length - 1];
160
- if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
161
- }
162
- return value;
163
- }
13
+ //#region package.json
14
+ var version = "0.8.0-dev.10";
164
15
  //#endregion
165
16
  //#region src/commands/init/errors.ts
166
17
  /**
@@ -364,6 +215,361 @@ function errorInitEmitFailed(options) {
364
215
  }
365
216
  });
366
217
  }
218
+ /**
219
+ * The project-level agent-skill install (`npx skills add
220
+ * prisma/prisma-next#v<version>`) failed after a successful dependency
221
+ * install + emit. The project's scaffold remains on disk; the user
222
+ * can either fix the underlying issue (network, registry, PATH) and
223
+ * run the install command manually, or re-run `init --no-skill` to
224
+ * proceed without the skill.
225
+ *
226
+ * Non-rolling-back, matching the existing install/emit failure
227
+ * semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
228
+ */
229
+ function errorInitSkillInstallFailed(options) {
230
+ return new CliStructuredError("5013", "Failed to install Prisma Next skills", {
231
+ domain: "CLI",
232
+ why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
233
+ fix: `Either:
234
+ - Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? " --force" : ""}\` to skip the skill install for this run, or\n - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
235
+ docsUrl: "https://prisma-next.dev/docs/cli/init#agent-skill",
236
+ meta: {
237
+ filesWritten: options.filesWritten,
238
+ skillInstallCommand: options.skillInstallCommand,
239
+ cause: options.cause
240
+ }
241
+ });
242
+ }
243
+ //#endregion
244
+ //#region src/commands/init/agent-skill-install.ts
245
+ const exec = promisify(execFile);
246
+ /**
247
+ * Default base for the GitHub-URL form `<owner>/<repo>` consumed by
248
+ * upstream `skills add`. Each `SkillSource` joins this base with its
249
+ * own subpath (and optional `#ref` for version-pinned clusters).
250
+ */
251
+ const DEFAULT_AGENT_SKILL_BASE = "prisma/prisma-next";
252
+ const DEFAULT_AGENT_SKILL_SOURCES = [
253
+ {
254
+ subpath: "skills",
255
+ ref: "cli",
256
+ description: "usage skills (version-locked to installed Prisma Next)"
257
+ },
258
+ {
259
+ subpath: "skills/upgrade",
260
+ ref: null,
261
+ description: "upgrade skill (always tracks `main`)"
262
+ },
263
+ {
264
+ subpath: "skills/extension-author",
265
+ ref: null,
266
+ description: "extension-author skill (always tracks `main`)"
267
+ }
268
+ ];
269
+ /**
270
+ * Test-only escape hatch for pinning the install base to a local
271
+ * checkout. Production runs leave this unset, so installs always use
272
+ * `DEFAULT_AGENT_SKILL_BASE`.
273
+ *
274
+ * When set to an absolute filesystem path (typical for tests), the
275
+ * `#ref` fragment is dropped — local-path mode in upstream's CLI does
276
+ * not accept refs, and the local clone has whatever content the test
277
+ * checked into it anyway. When set to anything else (e.g. a fork name
278
+ * `myuser/prisma-next`), the ref policy is preserved.
279
+ */
280
+ function resolveAgentSkillBase() {
281
+ const override = process.env["PRISMA_NEXT_SKILLS_BASE"]?.trim();
282
+ return override && override.length > 0 ? override : DEFAULT_AGENT_SKILL_BASE;
283
+ }
284
+ function isLocalPath(base) {
285
+ return base.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(base);
286
+ }
287
+ /**
288
+ * Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
289
+ * resolve. Exported for unit tests so the per-source format can be
290
+ * asserted without going through the full install loop.
291
+ */
292
+ function formatSkillSourceUrl(source) {
293
+ const base = resolveAgentSkillBase();
294
+ const url = `${base}/${source.subpath}`;
295
+ if (source.ref === null) return url;
296
+ if (isLocalPath(base)) return url;
297
+ if (source.ref === "cli") return `${url}#v${version}`;
298
+ return url;
299
+ }
300
+ /**
301
+ * The skill-install command for one source, formatted for the
302
+ * project's detected package manager. `npx`/`pnpm dlx`/`bunx` are
303
+ * interchangeable to the user; we pick the variant that matches the
304
+ * rest of the install step so a single project consistently uses one
305
+ * runner.
306
+ *
307
+ * `--all` auto-selects every skill in the cluster and every detected
308
+ * agent runtime, skipping the multi-select prompts the `skills` CLI
309
+ * shows by default. A non-interactive scaffold step cannot present
310
+ * prompts.
311
+ *
312
+ * Exported for unit tests so the per-PM dispatch can be asserted
313
+ * without a live subprocess.
314
+ */
315
+ function formatSkillInstallCommand(pm, source) {
316
+ return formatPackageManagerCommand(pm, [
317
+ "skills@latest",
318
+ "add",
319
+ formatSkillSourceUrl(source),
320
+ "--all"
321
+ ]);
322
+ }
323
+ /**
324
+ * `skills add --all` should cover Claude Code, but upstream currently skips
325
+ * project-local Claude symlinks when `.claude/` does not already exist. Run
326
+ * the explicit Claude Code install as well so fresh projects get
327
+ * `.claude/skills` without asking users to create that folder first.
328
+ */
329
+ function formatClaudeSkillInstallCommand(pm, source) {
330
+ return formatPackageManagerCommand(pm, [
331
+ "skills@latest",
332
+ "add",
333
+ formatSkillSourceUrl(source),
334
+ "--agent",
335
+ "claude-code",
336
+ "--skill",
337
+ "'*'",
338
+ "-y"
339
+ ]);
340
+ }
341
+ function formatPackageManagerCommand(pm, args) {
342
+ switch (pm) {
343
+ case "pnpm": return `pnpm dlx ${args.join(" ")}`;
344
+ case "yarn": return `yarn dlx ${args.join(" ")}`;
345
+ case "bun": return `bunx ${args.join(" ")}`;
346
+ case "deno": return `deno run -A npm:${args.join(" ")}`;
347
+ case "npm": return `npx ${args.join(" ")}`;
348
+ }
349
+ }
350
+ /**
351
+ * Parse the project-pm-formatted command into an exec call. The
352
+ * format-then-parse split keeps the user-facing command string the same
353
+ * as the surface the structured error advertises, so a user who copies
354
+ * the error's `fix` line gets the same invocation that init just
355
+ * attempted. Single quotes are preserved in the display form so `*` is
356
+ * safe to copy into a shell, then stripped before `execFile`.
357
+ */
358
+ function commandToExec(command) {
359
+ const tokens = (command.match(/'[^']*'|\S+/g) ?? []).map((token) => token.startsWith("'") && token.endsWith("'") ? token.slice(1, -1) : token);
360
+ return {
361
+ file: tokens[0] ?? "npx",
362
+ args: tokens.slice(1)
363
+ };
364
+ }
365
+ /**
366
+ * Runs the project-level skill install for every source in
367
+ * `DEFAULT_AGENT_SKILL_SOURCES`, in order. Returns
368
+ * `{ ok: true, commands }` on success; throws a structured
369
+ * `errorInitSkillInstallFailed` on the first failure (subsequent
370
+ * sources are not attempted — the user opted into Prisma Next by
371
+ * running `init` and a partial install would leave the project in an
372
+ * ambiguous state). The throw is intentionally fatal — project-level
373
+ * skill install is unconditional (modulo `--no-skill`).
374
+ */
375
+ async function runProjectLevelSkillInstall(ctx) {
376
+ const commands = [];
377
+ const installCommands = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [formatSkillInstallCommand(ctx.pm, source), formatClaudeSkillInstallCommand(ctx.pm, source)]);
378
+ for (const command of installCommands) {
379
+ const { file, args } = commandToExec(command);
380
+ try {
381
+ await exec(file, args, { cwd: ctx.baseDir });
382
+ commands.push(command);
383
+ } catch (err) {
384
+ throw errorInitSkillInstallFailed({
385
+ skillInstallCommand: command,
386
+ filesWritten: ctx.filesWritten,
387
+ cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
388
+ });
389
+ }
390
+ }
391
+ return {
392
+ ok: true,
393
+ commands
394
+ };
395
+ }
396
+ function readChildStderr$1(err) {
397
+ if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
398
+ return "";
399
+ }
400
+ /**
401
+ * Strips credentials from a `scheme://user:pass@host/...` URL anywhere
402
+ * in `stderr`. Package-manager stderr regularly contains credentialed
403
+ * registry URLs (private npm registries, GitHub Packages tokens), and
404
+ * those bubble into the structured `errorInitSkillInstallFailed`
405
+ * envelope, which ends up in logs and CI output. Redact at the
406
+ * boundary so we never re-emit a secret.
407
+ *
408
+ * Exported for unit tests.
409
+ */
410
+ function redactSecrets$1(stderr) {
411
+ if (!stderr) return stderr;
412
+ return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
413
+ }
414
+ /**
415
+ * Hand-rolled skill stub path that init must not leave behind. Removed
416
+ * on every init run so a project's `.agents/skills/prisma-next/` does
417
+ * not shadow the installed Prisma Next skill cluster.
418
+ */
419
+ const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
420
+ //#endregion
421
+ //#region src/commands/init/detect-package-manager.ts
422
+ const KNOWN = new Set([
423
+ "pnpm",
424
+ "npm",
425
+ "yarn",
426
+ "bun",
427
+ "deno"
428
+ ]);
429
+ /**
430
+ * Resolves the package manager `init` should drive for `add` / `install`
431
+ * commands. Tries, in order:
432
+ *
433
+ * 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
434
+ * `packageManager` field, the `devEngines.packageManager` field, or
435
+ * install metadata. This is the right answer whenever the user is
436
+ * anywhere inside an existing project, including a deep workspace
437
+ * subdirectory.
438
+ *
439
+ * 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
440
+ * every PM sets when it spawns a script. This catches the
441
+ * bare-directory case where there's no project to walk up to but the
442
+ * user invoked us via `pnpm dlx prisma-next init` / `bunx
443
+ * prisma-next init` / `yarn dlx …`. Same signal used by every
444
+ * `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
445
+ * `create-astro`, `@antfu/ni`, …).
446
+ *
447
+ * 3. **`npm`** — final fallback. Always present alongside Node.
448
+ */
449
+ async function detectPackageManager(cwd) {
450
+ const detected = await detect({ cwd });
451
+ if (detected && KNOWN.has(detected.name)) return detected.name;
452
+ const userAgent = getUserAgent();
453
+ if (userAgent !== null && KNOWN.has(userAgent)) return userAgent;
454
+ return "npm";
455
+ }
456
+ function hasProjectManifest(cwd) {
457
+ return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
458
+ }
459
+ function formatRunCommand(pm, bin, args) {
460
+ if (pm === "npm") return `npx ${bin} ${args}`;
461
+ if (pm === "deno") return `deno run npm:${bin} ${args}`;
462
+ return `${pm} ${bin} ${args}`;
463
+ }
464
+ function formatAddArgs(pm, packages) {
465
+ if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
466
+ return ["add", ...packages];
467
+ }
468
+ function formatAddDevArgs(pm, packages) {
469
+ if (pm === "deno") return [
470
+ "add",
471
+ "--dev",
472
+ ...packages.map((p) => `npm:${p}`)
473
+ ];
474
+ return [
475
+ "add",
476
+ "-D",
477
+ ...packages
478
+ ];
479
+ }
480
+ //#endregion
481
+ //#region src/commands/init/detect-pnpm-catalog.ts
482
+ /**
483
+ * Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
484
+ * its top-level `catalog:` block for entries that match any of `packages`.
485
+ *
486
+ * Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
487
+ * inside a pnpm workspace whose catalog overrides one of the packages it
488
+ * installs, surface a structured warning so the user knows the catalog
489
+ * version (not the published `latest`) is what ended up in their
490
+ * `node_modules`. pnpm itself does this silently; the warning closes the
491
+ * "looks fine, must be wrong version six months later" gap.
492
+ *
493
+ * Notes / scope:
494
+ *
495
+ * - We only inspect the unnamed top-level `catalog:` block. pnpm also
496
+ * supports `catalogs:` (plural — *named* catalogs referenced via
497
+ * `catalog:foo` specifiers); those don't apply to a vanilla
498
+ * `pnpm add prisma-next` invocation, so we skip them.
499
+ * - We don't validate YAML syntax exhaustively. The file format pnpm
500
+ * ships is line-oriented and well-known; a minimal regex is more
501
+ * robust than depending on a YAML parser for one warning.
502
+ * - We don't compare against the registry's `latest` — pnpm uses the
503
+ * catalog version regardless, so the warning fires whenever a match
504
+ * exists. The user-facing copy explains how to opt out.
505
+ */
506
+ function detectPnpmCatalogOverrides(baseDir, packages) {
507
+ const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
508
+ if (workspaceFile === null) return null;
509
+ const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
510
+ if (catalog === null) return {
511
+ workspaceFile,
512
+ entries: []
513
+ };
514
+ const wanted = new Set(packages);
515
+ const entries = [];
516
+ for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
517
+ name,
518
+ version
519
+ });
520
+ return {
521
+ workspaceFile,
522
+ entries
523
+ };
524
+ }
525
+ function findNearestPnpmWorkspaceFile(baseDir) {
526
+ let dir = baseDir;
527
+ let prev = "";
528
+ while (dir !== prev) {
529
+ const candidate = join(dir, "pnpm-workspace.yaml");
530
+ if (existsSync(candidate)) return candidate;
531
+ prev = dir;
532
+ dir = dirname(dir);
533
+ }
534
+ return null;
535
+ }
536
+ /**
537
+ * Returns the entries inside the top-level `catalog:` block as `[name, version]`
538
+ * pairs in document order, or `null` when no `catalog:` block exists.
539
+ *
540
+ * The parser is intentionally minimal: it reads line-by-line, locates the
541
+ * top-level `catalog:` line (no leading whitespace), then collects every
542
+ * subsequent indented line of the form `<key>: <value>` until the next
543
+ * top-level key (or end of file). Quotes around `<key>` and `<value>`
544
+ * are stripped; comments (`#…`) are ignored.
545
+ */
546
+ function extractCatalogBlock(contents) {
547
+ const lines = contents.split(/\r?\n/);
548
+ const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
549
+ if (startIdx === -1) return null;
550
+ const entries = [];
551
+ for (let i = startIdx + 1; i < lines.length; i++) {
552
+ const raw = lines[i] ?? "";
553
+ if (raw.trim() === "" || /^\s*#/.test(raw)) continue;
554
+ if (!/^\s/.test(raw)) break;
555
+ const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
556
+ if (!match) continue;
557
+ const name = match[1] ?? match[2] ?? match[3];
558
+ if (name === void 0) continue;
559
+ const version = stripQuotes((match[4] ?? "").trim());
560
+ if (version === "") continue;
561
+ entries.push([name, version]);
562
+ }
563
+ return entries;
564
+ }
565
+ function stripQuotes(value) {
566
+ if (value.length >= 2) {
567
+ const first = value[0];
568
+ const last = value[value.length - 1];
569
+ if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
570
+ }
571
+ return value;
572
+ }
367
573
  //#endregion
368
574
  //#region src/commands/init/hygiene-gitattributes.ts
369
575
  /**
@@ -810,7 +1016,10 @@ function dbFile(target) {
810
1016
  import type { Contract } from './contract.d';
811
1017
  import contractJson from './contract.json' with { type: 'json' };
812
1018
 
813
- export const db = postgres<Contract>({ contractJson });
1019
+ export const db = postgres<Contract>({
1020
+ contractJson,
1021
+ url: process.env['DATABASE_URL']!,
1022
+ });
814
1023
  `;
815
1024
  return `import mongo from '@prisma-next/mongo/runtime';
816
1025
  import type { Contract } from './contract.d';
@@ -882,6 +1091,7 @@ async function resolveInitInputs(ctx) {
882
1091
  canPrompt,
883
1092
  autoAcceptPrompts
884
1093
  });
1094
+ const installProjectSkill = options.skill !== false;
885
1095
  return {
886
1096
  target: finalTarget,
887
1097
  authoring: finalAuthoring,
@@ -891,7 +1101,8 @@ async function resolveInitInputs(ctx) {
891
1101
  probeDb: Boolean(options.probeDb),
892
1102
  strictProbe: Boolean(options.strictProbe),
893
1103
  reinit,
894
- removePreviousFacade
1104
+ removePreviousFacade,
1105
+ installProjectSkill
895
1106
  };
896
1107
  }
897
1108
  async function resolveWriteEnv(opts) {
@@ -1302,47 +1513,6 @@ function removeDependency(existing, depName) {
1302
1513
  return `${JSON.stringify(parsed, null, 2)}${trailingNewline}`;
1303
1514
  }
1304
1515
  //#endregion
1305
- //#region src/commands/init/templates/render.ts
1306
- function renderTemplate(templateFile, variableNames, vars) {
1307
- let result = readFileSync(join(import.meta.dirname, templateFile), "utf-8");
1308
- for (const key of variableNames) {
1309
- const value = vars[key];
1310
- if (value === void 0) throw new Error(`Template variable '${key}' is not defined`);
1311
- result = result.replaceAll(`{{${key}}}`, value);
1312
- }
1313
- return result;
1314
- }
1315
- //#endregion
1316
- //#region src/commands/init/templates/agent-skill.ts
1317
- const variables$1 = [
1318
- "schemaPath",
1319
- "schemaDir",
1320
- "dbImportPath",
1321
- "pkgRun",
1322
- "authoringLabel"
1323
- ];
1324
- /**
1325
- * Renders the per-project agent skill (FR5.2). The skill template is
1326
- * target-specific (Postgres vs Mongo query syntax differs); the authoring
1327
- * style enters via:
1328
- *
1329
- * - `schemaPath` — already routed through {@link agentSkillMd}'s caller
1330
- * (the AC says a TS-authoring scaffold must reference `prisma/contract.ts`).
1331
- * - `authoringLabel` — a short human-readable note (`PSL` / `TypeScript`)
1332
- * the skill template uses when describing the contract file.
1333
- */
1334
- function agentSkillMd(target, authoring, schemaPath, pkgRun) {
1335
- const schemaDir = dirname(schemaPath);
1336
- const vars = {
1337
- schemaPath,
1338
- schemaDir,
1339
- dbImportPath: `./${schemaDir}/db`,
1340
- pkgRun,
1341
- authoringLabel: authoring === "typescript" ? "TypeScript" : "PSL"
1342
- };
1343
- return renderTemplate(`agent-skill-${target}.md`, variables$1, vars);
1344
- }
1345
- //#endregion
1346
1516
  //#region src/commands/init/templates/env.ts
1347
1517
  /**
1348
1518
  * The minimum supported server version for each target (FR8.1). The
@@ -1414,6 +1584,17 @@ function envFileContent(target) {
1414
1584
  return envPlaceholderBody(target);
1415
1585
  }
1416
1586
  //#endregion
1587
+ //#region src/commands/init/templates/render.ts
1588
+ function renderTemplate(templateFile, variableNames, vars) {
1589
+ let result = readFileSync(join(import.meta.dirname, templateFile), "utf-8");
1590
+ for (const key of variableNames) {
1591
+ const value = vars[key];
1592
+ if (value === void 0) throw new Error(`Template variable '${key}' is not defined`);
1593
+ result = result.replaceAll(`{{${key}}}`, value);
1594
+ }
1595
+ return result;
1596
+ }
1597
+ //#endregion
1417
1598
  //#region src/commands/init/templates/quick-reference.ts
1418
1599
  const variables = [
1419
1600
  "schemaPath",
@@ -1653,16 +1834,13 @@ async function runInit(baseDir, runOptions) {
1653
1834
  path: "prisma-next.md",
1654
1835
  content: quickReferenceMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun)
1655
1836
  },
1656
- {
1657
- path: ".agents/skills/prisma-next/SKILL.md",
1658
- content: agentSkillMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun)
1659
- },
1660
1837
  {
1661
1838
  path: ".env.example",
1662
1839
  content: envExampleContent(inputs.target)
1663
1840
  }
1664
1841
  ];
1665
1842
  const filesToDelete = inputs.reinit ? [...findStaleArtefacts(baseDir, schemaDir)] : [];
1843
+ if (existsSync(join(baseDir, ".agents/skills/prisma-next/SKILL.md"))) filesToDelete.push(LEGACY_SKILL_FILE);
1666
1844
  if (inputs.writeEnv) if (!existsSync(join(baseDir, ".env"))) filesToWrite.push({
1667
1845
  path: ".env",
1668
1846
  content: envFileContent(inputs.target)
@@ -1807,6 +1985,27 @@ async function runInit(baseDir, runOptions) {
1807
1985
  filesWritten
1808
1986
  }));
1809
1987
  }
1988
+ const manualProjectSkillSummary = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [formatSkillInstallCommand(install.effectivePm, source), formatClaudeSkillInstallCommand(install.effectivePm, source)]).map((c) => `\`${c}\``).join(" && ");
1989
+ let skillRegistered = false;
1990
+ if (!inputs.installProjectSkill) warnings.push(`Skipped Prisma Next agent-skill install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`);
1991
+ else if (install.skipped) warnings.push(`Skipped Prisma Next agent-skill install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`);
1992
+ else {
1993
+ const spinner = ui.spinner();
1994
+ spinner.start("Registering Prisma Next skills with the agent runtime...");
1995
+ try {
1996
+ const project = await runProjectLevelSkillInstall({
1997
+ baseDir,
1998
+ pm: install.effectivePm,
1999
+ filesWritten
2000
+ });
2001
+ spinner.stop(`Registered Prisma Next skills (project level) — ran ${project.commands.map((c) => `\`${c}\``).join(", ")}`);
2002
+ skillRegistered = true;
2003
+ } catch (error) {
2004
+ spinner.stop("Agent-skill install failed");
2005
+ if (CliStructuredError.is(error)) return emitError(ui, flags, error);
2006
+ throw error;
2007
+ }
2008
+ }
1810
2009
  const output = {
1811
2010
  ok: true,
1812
2011
  target: inputs.target === "mongo" ? "mongodb" : "postgres",
@@ -1824,7 +2023,8 @@ async function runInit(baseDir, runOptions) {
1824
2023
  target: inputs.target === "mongo" ? "mongodb" : "postgres",
1825
2024
  contractEmitted,
1826
2025
  emitCommand,
1827
- schemaPath: inputs.schemaPath
2026
+ schemaPath: inputs.schemaPath,
2027
+ skillRegistered
1828
2028
  }),
1829
2029
  warnings
1830
2030
  };
@@ -1884,6 +2084,7 @@ function exitCodeForError(error) {
1884
2084
  case "5007": return 4;
1885
2085
  case "5008": return 5;
1886
2086
  case "5009": return 1;
2087
+ case "5013": return 6;
1887
2088
  default: return 1;
1888
2089
  }
1889
2090
  }
@@ -1955,7 +2156,8 @@ async function runInstall(ctx) {
1955
2156
  skipped: true,
1956
2157
  deps: [],
1957
2158
  devDeps: [],
1958
- warnings: catalogWarnings
2159
+ warnings: catalogWarnings,
2160
+ effectivePm: pm
1959
2161
  };
1960
2162
  }
1961
2163
  const exec = promisify(execFile);
@@ -1973,7 +2175,8 @@ async function runInstall(ctx) {
1973
2175
  skipped: false,
1974
2176
  deps,
1975
2177
  devDeps,
1976
- warnings: catalogWarnings
2178
+ warnings: catalogWarnings,
2179
+ effectivePm: pm
1977
2180
  };
1978
2181
  } catch (err) {
1979
2182
  const stderrText = redactSecrets(readChildStderr(err));
@@ -1991,7 +2194,8 @@ async function runInstall(ctx) {
1991
2194
  "Falling back to `npm install` so init can complete.",
1992
2195
  stderrText ? ` pnpm error: ${stderrText.trim().split("\n")[0]}` : "",
1993
2196
  "Once the offending package republishes a clean version, re-run `pnpm install` to switch back."
1994
- ].filter(Boolean).join("\n")]
2197
+ ].filter(Boolean).join("\n")],
2198
+ effectivePm: "npm"
1995
2199
  };
1996
2200
  } catch (npmErr) {
1997
2201
  spinner.stop("Installation failed");
@@ -2097,7 +2301,7 @@ async function runEmit(ctx) {
2097
2301
  const spinner = ctx.ui.spinner();
2098
2302
  spinner.start("Emitting contract...");
2099
2303
  try {
2100
- const { executeContractEmit } = await import("./contract-emit-B77TsJqf.mjs").then((n) => n.t);
2304
+ const { executeContractEmit } = await import("./contract-emit-CgoFk9AU.mjs").then((n) => n.t);
2101
2305
  await executeContractEmit({ configPath: join(ctx.baseDir, "prisma-next.config.ts") });
2102
2306
  spinner.stop("Contract emitted");
2103
2307
  } catch (err) {
@@ -2150,4 +2354,4 @@ function sanitisePackageName(raw) {
2150
2354
  //#endregion
2151
2355
  export { runInit };
2152
2356
 
2153
- //# sourceMappingURL=init-BRKnARU6.mjs.map
2357
+ //# sourceMappingURL=init-Dm0QZPUA.mjs.map