prisma-next 0.7.0 → 0.8.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.
@@ -1,6 +1,6 @@
1
1
  import { t as CliStructuredError } from "./cli-errors-D3_sMh2K.mjs";
2
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";
3
+ import { i as renderInitOutro, n as buildNextSteps, r as formatInitJson, t as InitOutputSchema } from "./output-BVj6a971.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { basename, dirname, extname, isAbsolute, join, normalize } from "pathe";
6
6
  import * as clack from "@clack/prompts";
@@ -9,159 +9,6 @@ import { execFile } from "node:child_process";
9
9
  import { promisify } from "node:util";
10
10
  import { detect, getUserAgent } from "package-manager-detector/detect";
11
11
  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
- }
164
- //#endregion
165
12
  //#region src/commands/init/errors.ts
166
13
  /**
167
14
  * Re-init in non-interactive mode without `--force`. Distinct from the
@@ -364,6 +211,296 @@ function errorInitEmitFailed(options) {
364
211
  }
365
212
  });
366
213
  }
214
+ /**
215
+ * The project-level agent-skill install (`npx skills add
216
+ * @prisma-next/agent-skill`) failed after a successful dependency
217
+ * install + emit. The project's scaffold remains on disk; the user
218
+ * can either fix the underlying issue (network, registry, PATH) and
219
+ * run the install command manually, or re-run `init --no-skill` to
220
+ * proceed without the skill.
221
+ *
222
+ * Non-rolling-back, matching the existing install/emit failure
223
+ * semantics. Maps to exit code `6 = SKILL_INSTALL_FAILED`.
224
+ */
225
+ function errorInitSkillInstallFailed(options) {
226
+ return new CliStructuredError("5013", "Failed to install @prisma-next/agent-skill", {
227
+ domain: "CLI",
228
+ why: `\`${options.skillInstallCommand}\` exited with an error: ${options.cause}`,
229
+ fix: `Either:
230
+ - 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}`,
231
+ docsUrl: "https://prisma-next.dev/docs/cli/init#agent-skill",
232
+ meta: {
233
+ filesWritten: options.filesWritten,
234
+ skillInstallCommand: options.skillInstallCommand,
235
+ cause: options.cause
236
+ }
237
+ });
238
+ }
239
+ //#endregion
240
+ //#region src/commands/init/agent-skill-install.ts
241
+ const exec = promisify(execFile);
242
+ /**
243
+ * The npm package the agent-skill install dispatches to. Version-locked
244
+ * to Prisma Next via the consumer project's `package.json`; the install
245
+ * subprocess picks up whatever version is resolvable at install time.
246
+ *
247
+ * The skill is **always** installed at the project level — never the
248
+ * user level — precisely so the skill version tracks the project's
249
+ * Prisma Next version. A user-level (global) install of an
250
+ * agent-skills CLI package would have to pick a single version for
251
+ * every project on the host, which breaks the version-locking invariant
252
+ * the rest of the framework relies on (skills, CLI, runtime, and
253
+ * extension packs all ship at the same version per release).
254
+ */
255
+ const AGENT_SKILL_PACKAGE = "@prisma-next/agent-skill";
256
+ /**
257
+ * The skill-install command, formatted for the project's detected
258
+ * package manager. `npx`/`pnpm dlx`/`bunx` are interchangeable to the
259
+ * user; we pick the variant that matches the rest of the install step
260
+ * so a single project consistently uses one runner.
261
+ *
262
+ * `--all` auto-selects every skill in the cluster and every detected
263
+ * agent runtime, skipping the multi-select prompts the `skills` CLI
264
+ * shows by default. A non-interactive scaffold step cannot present
265
+ * prompts, and the cluster is designed to be installed as a unit (the
266
+ * router skill routes between the workflow-scoped siblings). Users who
267
+ * want a narrower install run `npx skills add @prisma-next/agent-skill`
268
+ * themselves after `init` with the flags they want.
269
+ *
270
+ * Exported for unit tests so the per-PM dispatch can be asserted
271
+ * without a live subprocess.
272
+ */
273
+ function formatSkillInstallCommand(pm) {
274
+ const args = [
275
+ "skills",
276
+ "add",
277
+ AGENT_SKILL_PACKAGE,
278
+ "--all"
279
+ ];
280
+ switch (pm) {
281
+ case "pnpm": return `pnpm dlx ${args.join(" ")}`;
282
+ case "yarn": return `yarn dlx ${args.join(" ")}`;
283
+ case "bun": return `bunx ${args.join(" ")}`;
284
+ case "deno": return `deno run -A npm:${args.join(" ")}`;
285
+ case "npm": return `npx ${args.join(" ")}`;
286
+ }
287
+ }
288
+ /**
289
+ * Parse the project-pm-formatted command into an exec call. The
290
+ * format-then-parse split keeps the user-facing command string the same
291
+ * as the surface the structured error advertises, so a user who copies
292
+ * the error's `fix` line gets the same invocation that init just
293
+ * attempted.
294
+ */
295
+ function commandToExec(command) {
296
+ const tokens = command.split(/\s+/);
297
+ return {
298
+ file: tokens[0] ?? "npx",
299
+ args: tokens.slice(1)
300
+ };
301
+ }
302
+ /**
303
+ * Runs the project-level skill install. Returns `{ ok: true, command }`
304
+ * on success; throws a structured `errorInitSkillInstallFailed` on
305
+ * failure. The throw is intentionally fatal — project-level skill
306
+ * install is unconditional (modulo `--no-skill`) and the user opted
307
+ * into Prisma Next by running `init`. A silent skip would defeat the
308
+ * onboarding-to-zero contract.
309
+ */
310
+ async function runProjectLevelSkillInstall(ctx) {
311
+ const command = formatSkillInstallCommand(ctx.pm);
312
+ const { file, args } = commandToExec(command);
313
+ try {
314
+ await exec(file, args, { cwd: ctx.baseDir });
315
+ return {
316
+ ok: true,
317
+ command
318
+ };
319
+ } catch (err) {
320
+ throw errorInitSkillInstallFailed({
321
+ skillInstallCommand: command,
322
+ filesWritten: ctx.filesWritten,
323
+ cause: redactSecrets$1(readChildStderr$1(err)) || (err instanceof Error ? err.message : String(err))
324
+ });
325
+ }
326
+ }
327
+ function readChildStderr$1(err) {
328
+ if (err instanceof Error && "stderr" in err) return String(err.stderr ?? "");
329
+ return "";
330
+ }
331
+ /**
332
+ * Strips credentials from a `scheme://user:pass@host/...` URL anywhere
333
+ * in `stderr`. Package-manager stderr regularly contains credentialed
334
+ * registry URLs (private npm registries, GitHub Packages tokens), and
335
+ * those bubble into the structured `errorInitSkillInstallFailed`
336
+ * envelope, which ends up in logs and CI output. Redact at the
337
+ * boundary so we never re-emit a secret.
338
+ *
339
+ * Exported for unit tests.
340
+ */
341
+ function redactSecrets$1(stderr) {
342
+ if (!stderr) return stderr;
343
+ return stderr.replace(/([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s]+)@/g, "$1***@");
344
+ }
345
+ /**
346
+ * Hand-rolled skill stub path that init must not leave behind. Removed
347
+ * on every init run so a project's `.agents/skills/prisma-next/` does
348
+ * not shadow the published `@prisma-next/agent-skill` package.
349
+ */
350
+ const LEGACY_SKILL_FILE = ".agents/skills/prisma-next/SKILL.md";
351
+ //#endregion
352
+ //#region src/commands/init/detect-package-manager.ts
353
+ const KNOWN = new Set([
354
+ "pnpm",
355
+ "npm",
356
+ "yarn",
357
+ "bun",
358
+ "deno"
359
+ ]);
360
+ /**
361
+ * Resolves the package manager `init` should drive for `add` / `install`
362
+ * commands. Tries, in order:
363
+ *
364
+ * 1. **`detect()`** — walks up from `cwd` looking for a lockfile, the
365
+ * `packageManager` field, the `devEngines.packageManager` field, or
366
+ * install metadata. This is the right answer whenever the user is
367
+ * anywhere inside an existing project, including a deep workspace
368
+ * subdirectory.
369
+ *
370
+ * 2. **`getUserAgent()`** — parses `npm_config_user_agent`, the env var
371
+ * every PM sets when it spawns a script. This catches the
372
+ * bare-directory case where there's no project to walk up to but the
373
+ * user invoked us via `pnpm dlx prisma-next init` / `bunx
374
+ * prisma-next init` / `yarn dlx …`. Same signal used by every
375
+ * `create-*` tool in the ecosystem (`create-vite`, `create-next-app`,
376
+ * `create-astro`, `@antfu/ni`, …).
377
+ *
378
+ * 3. **`npm`** — final fallback. Always present alongside Node.
379
+ */
380
+ async function detectPackageManager(cwd) {
381
+ const detected = await detect({ cwd });
382
+ if (detected && KNOWN.has(detected.name)) return detected.name;
383
+ const userAgent = getUserAgent();
384
+ if (userAgent !== null && KNOWN.has(userAgent)) return userAgent;
385
+ return "npm";
386
+ }
387
+ function hasProjectManifest(cwd) {
388
+ return existsSync(join(cwd, "package.json")) || existsSync(join(cwd, "deno.json")) || existsSync(join(cwd, "deno.jsonc"));
389
+ }
390
+ function formatRunCommand(pm, bin, args) {
391
+ if (pm === "npm") return `npx ${bin} ${args}`;
392
+ if (pm === "deno") return `deno run npm:${bin} ${args}`;
393
+ return `${pm} ${bin} ${args}`;
394
+ }
395
+ function formatAddArgs(pm, packages) {
396
+ if (pm === "deno") return ["add", ...packages.map((p) => `npm:${p}`)];
397
+ return ["add", ...packages];
398
+ }
399
+ function formatAddDevArgs(pm, packages) {
400
+ if (pm === "deno") return [
401
+ "add",
402
+ "--dev",
403
+ ...packages.map((p) => `npm:${p}`)
404
+ ];
405
+ return [
406
+ "add",
407
+ "-D",
408
+ ...packages
409
+ ];
410
+ }
411
+ //#endregion
412
+ //#region src/commands/init/detect-pnpm-catalog.ts
413
+ /**
414
+ * Walks up from `baseDir` looking for `pnpm-workspace.yaml`, then scans
415
+ * its top-level `catalog:` block for entries that match any of `packages`.
416
+ *
417
+ * Implements FR7.3 / Spec Decision 8 (honour-and-warn): when `init` runs
418
+ * inside a pnpm workspace whose catalog overrides one of the packages it
419
+ * installs, surface a structured warning so the user knows the catalog
420
+ * version (not the published `latest`) is what ended up in their
421
+ * `node_modules`. pnpm itself does this silently; the warning closes the
422
+ * "looks fine, must be wrong version six months later" gap.
423
+ *
424
+ * Notes / scope:
425
+ *
426
+ * - We only inspect the unnamed top-level `catalog:` block. pnpm also
427
+ * supports `catalogs:` (plural — *named* catalogs referenced via
428
+ * `catalog:foo` specifiers); those don't apply to a vanilla
429
+ * `pnpm add prisma-next` invocation, so we skip them.
430
+ * - We don't validate YAML syntax exhaustively. The file format pnpm
431
+ * ships is line-oriented and well-known; a minimal regex is more
432
+ * robust than depending on a YAML parser for one warning.
433
+ * - We don't compare against the registry's `latest` — pnpm uses the
434
+ * catalog version regardless, so the warning fires whenever a match
435
+ * exists. The user-facing copy explains how to opt out.
436
+ */
437
+ function detectPnpmCatalogOverrides(baseDir, packages) {
438
+ const workspaceFile = findNearestPnpmWorkspaceFile(baseDir);
439
+ if (workspaceFile === null) return null;
440
+ const catalog = extractCatalogBlock(readFileSync(workspaceFile, "utf-8"));
441
+ if (catalog === null) return {
442
+ workspaceFile,
443
+ entries: []
444
+ };
445
+ const wanted = new Set(packages);
446
+ const entries = [];
447
+ for (const [name, version] of catalog) if (wanted.has(name)) entries.push({
448
+ name,
449
+ version
450
+ });
451
+ return {
452
+ workspaceFile,
453
+ entries
454
+ };
455
+ }
456
+ function findNearestPnpmWorkspaceFile(baseDir) {
457
+ let dir = baseDir;
458
+ let prev = "";
459
+ while (dir !== prev) {
460
+ const candidate = join(dir, "pnpm-workspace.yaml");
461
+ if (existsSync(candidate)) return candidate;
462
+ prev = dir;
463
+ dir = dirname(dir);
464
+ }
465
+ return null;
466
+ }
467
+ /**
468
+ * Returns the entries inside the top-level `catalog:` block as `[name, version]`
469
+ * pairs in document order, or `null` when no `catalog:` block exists.
470
+ *
471
+ * The parser is intentionally minimal: it reads line-by-line, locates the
472
+ * top-level `catalog:` line (no leading whitespace), then collects every
473
+ * subsequent indented line of the form `<key>: <value>` until the next
474
+ * top-level key (or end of file). Quotes around `<key>` and `<value>`
475
+ * are stripped; comments (`#…`) are ignored.
476
+ */
477
+ function extractCatalogBlock(contents) {
478
+ const lines = contents.split(/\r?\n/);
479
+ const startIdx = lines.findIndex((line) => /^catalog\s*:\s*$/.test(line));
480
+ if (startIdx === -1) return null;
481
+ const entries = [];
482
+ for (let i = startIdx + 1; i < lines.length; i++) {
483
+ const raw = lines[i] ?? "";
484
+ if (raw.trim() === "" || /^\s*#/.test(raw)) continue;
485
+ if (!/^\s/.test(raw)) break;
486
+ const match = raw.match(/^\s+(?:'([^']+)'|"([^"]+)"|([^:\s'"]+))\s*:\s*(.*?)\s*(?:#.*)?$/);
487
+ if (!match) continue;
488
+ const name = match[1] ?? match[2] ?? match[3];
489
+ if (name === void 0) continue;
490
+ const version = stripQuotes((match[4] ?? "").trim());
491
+ if (version === "") continue;
492
+ entries.push([name, version]);
493
+ }
494
+ return entries;
495
+ }
496
+ function stripQuotes(value) {
497
+ if (value.length >= 2) {
498
+ const first = value[0];
499
+ const last = value[value.length - 1];
500
+ if (first === "\"" && last === "\"" || first === "'" && last === "'") return value.slice(1, -1);
501
+ }
502
+ return value;
503
+ }
367
504
  //#endregion
368
505
  //#region src/commands/init/hygiene-gitattributes.ts
369
506
  /**
@@ -810,7 +947,10 @@ function dbFile(target) {
810
947
  import type { Contract } from './contract.d';
811
948
  import contractJson from './contract.json' with { type: 'json' };
812
949
 
813
- export const db = postgres<Contract>({ contractJson });
950
+ export const db = postgres<Contract>({
951
+ contractJson,
952
+ url: process.env['DATABASE_URL']!,
953
+ });
814
954
  `;
815
955
  return `import mongo from '@prisma-next/mongo/runtime';
816
956
  import type { Contract } from './contract.d';
@@ -882,6 +1022,7 @@ async function resolveInitInputs(ctx) {
882
1022
  canPrompt,
883
1023
  autoAcceptPrompts
884
1024
  });
1025
+ const installProjectSkill = options.skill !== false;
885
1026
  return {
886
1027
  target: finalTarget,
887
1028
  authoring: finalAuthoring,
@@ -891,7 +1032,8 @@ async function resolveInitInputs(ctx) {
891
1032
  probeDb: Boolean(options.probeDb),
892
1033
  strictProbe: Boolean(options.strictProbe),
893
1034
  reinit,
894
- removePreviousFacade
1035
+ removePreviousFacade,
1036
+ installProjectSkill
895
1037
  };
896
1038
  }
897
1039
  async function resolveWriteEnv(opts) {
@@ -1302,47 +1444,6 @@ function removeDependency(existing, depName) {
1302
1444
  return `${JSON.stringify(parsed, null, 2)}${trailingNewline}`;
1303
1445
  }
1304
1446
  //#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
1447
  //#region src/commands/init/templates/env.ts
1347
1448
  /**
1348
1449
  * The minimum supported server version for each target (FR8.1). The
@@ -1414,6 +1515,17 @@ function envFileContent(target) {
1414
1515
  return envPlaceholderBody(target);
1415
1516
  }
1416
1517
  //#endregion
1518
+ //#region src/commands/init/templates/render.ts
1519
+ function renderTemplate(templateFile, variableNames, vars) {
1520
+ let result = readFileSync(join(import.meta.dirname, templateFile), "utf-8");
1521
+ for (const key of variableNames) {
1522
+ const value = vars[key];
1523
+ if (value === void 0) throw new Error(`Template variable '${key}' is not defined`);
1524
+ result = result.replaceAll(`{{${key}}}`, value);
1525
+ }
1526
+ return result;
1527
+ }
1528
+ //#endregion
1417
1529
  //#region src/commands/init/templates/quick-reference.ts
1418
1530
  const variables = [
1419
1531
  "schemaPath",
@@ -1653,16 +1765,13 @@ async function runInit(baseDir, runOptions) {
1653
1765
  path: "prisma-next.md",
1654
1766
  content: quickReferenceMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun)
1655
1767
  },
1656
- {
1657
- path: ".agents/skills/prisma-next/SKILL.md",
1658
- content: agentSkillMd(inputs.target, inputs.authoring, inputs.schemaPath, pkgRun)
1659
- },
1660
1768
  {
1661
1769
  path: ".env.example",
1662
1770
  content: envExampleContent(inputs.target)
1663
1771
  }
1664
1772
  ];
1665
1773
  const filesToDelete = inputs.reinit ? [...findStaleArtefacts(baseDir, schemaDir)] : [];
1774
+ if (existsSync(join(baseDir, ".agents/skills/prisma-next/SKILL.md"))) filesToDelete.push(LEGACY_SKILL_FILE);
1666
1775
  if (inputs.writeEnv) if (!existsSync(join(baseDir, ".env"))) filesToWrite.push({
1667
1776
  path: ".env",
1668
1777
  content: envFileContent(inputs.target)
@@ -1807,6 +1916,25 @@ async function runInit(baseDir, runOptions) {
1807
1916
  filesWritten
1808
1917
  }));
1809
1918
  }
1919
+ const manualProjectSkillCommand = formatSkillInstallCommand(install.effectivePm);
1920
+ if (!inputs.installProjectSkill) warnings.push(`Skipped @prisma-next/agent-skill install (--no-skill). To install later, run \`${manualProjectSkillCommand}\` in this project.`);
1921
+ else if (install.skipped) warnings.push(`Skipped @prisma-next/agent-skill install because --no-install was passed. Once you run install manually, register the skill with \`${manualProjectSkillCommand}\`.`);
1922
+ else {
1923
+ const spinner = ui.spinner();
1924
+ spinner.start("Registering @prisma-next/agent-skill with the agent runtime...");
1925
+ try {
1926
+ const project = await runProjectLevelSkillInstall({
1927
+ baseDir,
1928
+ pm: install.effectivePm,
1929
+ filesWritten
1930
+ });
1931
+ spinner.stop(`Registered @prisma-next/agent-skill (project level) — ran \`${project.command}\``);
1932
+ } catch (error) {
1933
+ spinner.stop("Agent-skill install failed");
1934
+ if (CliStructuredError.is(error)) return emitError(ui, flags, error);
1935
+ throw error;
1936
+ }
1937
+ }
1810
1938
  const output = {
1811
1939
  ok: true,
1812
1940
  target: inputs.target === "mongo" ? "mongodb" : "postgres",
@@ -1884,6 +2012,7 @@ function exitCodeForError(error) {
1884
2012
  case "5007": return 4;
1885
2013
  case "5008": return 5;
1886
2014
  case "5009": return 1;
2015
+ case "5013": return 6;
1887
2016
  default: return 1;
1888
2017
  }
1889
2018
  }
@@ -1955,7 +2084,8 @@ async function runInstall(ctx) {
1955
2084
  skipped: true,
1956
2085
  deps: [],
1957
2086
  devDeps: [],
1958
- warnings: catalogWarnings
2087
+ warnings: catalogWarnings,
2088
+ effectivePm: pm
1959
2089
  };
1960
2090
  }
1961
2091
  const exec = promisify(execFile);
@@ -1973,7 +2103,8 @@ async function runInstall(ctx) {
1973
2103
  skipped: false,
1974
2104
  deps,
1975
2105
  devDeps,
1976
- warnings: catalogWarnings
2106
+ warnings: catalogWarnings,
2107
+ effectivePm: pm
1977
2108
  };
1978
2109
  } catch (err) {
1979
2110
  const stderrText = redactSecrets(readChildStderr(err));
@@ -1991,7 +2122,8 @@ async function runInstall(ctx) {
1991
2122
  "Falling back to `npm install` so init can complete.",
1992
2123
  stderrText ? ` pnpm error: ${stderrText.trim().split("\n")[0]}` : "",
1993
2124
  "Once the offending package republishes a clean version, re-run `pnpm install` to switch back."
1994
- ].filter(Boolean).join("\n")]
2125
+ ].filter(Boolean).join("\n")],
2126
+ effectivePm: "npm"
1995
2127
  };
1996
2128
  } catch (npmErr) {
1997
2129
  spinner.stop("Installation failed");
@@ -2150,4 +2282,4 @@ function sanitisePackageName(raw) {
2150
2282
  //#endregion
2151
2283
  export { runInit };
2152
2284
 
2153
- //# sourceMappingURL=init-BRKnARU6.mjs.map
2285
+ //# sourceMappingURL=init-B-k3a1Qw.mjs.map