@prisma-next/cli 0.10.0-dev.9 → 0.11.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/README.md +1 -1
  2. package/dist/{cli-errors-CF60g2cG.mjs → cli-errors-Djtz98Vm.mjs} +3 -3
  3. package/dist/cli-errors-Djtz98Vm.mjs.map +1 -0
  4. package/dist/cli.mjs +296 -53
  5. package/dist/cli.mjs.map +1 -1
  6. package/dist/{client-Brv4qlfB.mjs → client-oXO2WCPD.mjs} +6 -5
  7. package/dist/client-oXO2WCPD.mjs.map +1 -0
  8. package/dist/{command-helpers-Dvgul7UA.mjs → command-helpers-DtavI0wJ.mjs} +108 -11
  9. package/dist/command-helpers-DtavI0wJ.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.d.mts.map +1 -1
  13. package/dist/commands/db-init.mjs +19 -20
  14. package/dist/commands/db-init.mjs.map +1 -1
  15. package/dist/commands/db-schema.mjs +6 -10
  16. package/dist/commands/db-schema.mjs.map +1 -1
  17. package/dist/commands/db-sign.mjs +7 -11
  18. package/dist/commands/db-sign.mjs.map +1 -1
  19. package/dist/commands/db-update.d.mts.map +1 -1
  20. package/dist/commands/db-update.mjs +16 -17
  21. package/dist/commands/db-update.mjs.map +1 -1
  22. package/dist/commands/db-verify.mjs +1 -1
  23. package/dist/commands/migrate.d.mts +1 -1
  24. package/dist/commands/migrate.mjs +7 -11
  25. package/dist/commands/migrate.mjs.map +1 -1
  26. package/dist/commands/migration-check.mjs +4 -7
  27. package/dist/commands/migration-check.mjs.map +1 -1
  28. package/dist/commands/migration-graph.d.mts +1 -1
  29. package/dist/commands/migration-graph.mjs +6 -10
  30. package/dist/commands/migration-graph.mjs.map +1 -1
  31. package/dist/commands/migration-list.mjs +5 -9
  32. package/dist/commands/migration-list.mjs.map +1 -1
  33. package/dist/commands/migration-log.d.mts.map +1 -1
  34. package/dist/commands/migration-log.mjs +7 -10
  35. package/dist/commands/migration-log.mjs.map +1 -1
  36. package/dist/commands/migration-new.mjs +6 -10
  37. package/dist/commands/migration-new.mjs.map +1 -1
  38. package/dist/commands/migration-plan.d.mts +1 -1
  39. package/dist/commands/migration-plan.mjs +1 -1
  40. package/dist/commands/migration-show.d.mts +1 -1
  41. package/dist/commands/migration-show.mjs +8 -12
  42. package/dist/commands/migration-show.mjs.map +1 -1
  43. package/dist/commands/migration-status.d.mts +1 -1
  44. package/dist/commands/migration-status.d.mts.map +1 -1
  45. package/dist/commands/migration-status.mjs +36 -14
  46. package/dist/commands/migration-status.mjs.map +1 -1
  47. package/dist/commands/ref.d.mts +1 -1
  48. package/dist/commands/ref.mjs +9 -19
  49. package/dist/commands/ref.mjs.map +1 -1
  50. package/dist/{contract-emit-iynA3BCA.mjs → contract-emit-bcrpT-wD.mjs} +3 -3
  51. package/dist/{contract-emit-iynA3BCA.mjs.map → contract-emit-bcrpT-wD.mjs.map} +1 -1
  52. package/dist/{contract-emit-BDBzHlaC.mjs → contract-emit-uwT-Mj8-.mjs} +7 -12
  53. package/dist/contract-emit-uwT-Mj8-.mjs.map +1 -0
  54. package/dist/{contract-infer-Dm8pBZMR.mjs → contract-infer-pKkiCt7C.mjs} +9 -14
  55. package/dist/contract-infer-pKkiCt7C.mjs.map +1 -0
  56. package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs → contract-space-aggregate-loader-BmNQwlws.mjs} +2 -2
  57. package/dist/{contract-space-aggregate-loader-pAc8CDfY.mjs.map → contract-space-aggregate-loader-BmNQwlws.mjs.map} +1 -1
  58. package/dist/{db-verify-CW8DR5Ei.mjs → db-verify-AoIUriL4.mjs} +9 -13
  59. package/dist/db-verify-AoIUriL4.mjs.map +1 -0
  60. package/dist/exports/control-api.d.mts +1 -1
  61. package/dist/exports/control-api.mjs +2 -2
  62. package/dist/exports/index.mjs +1 -1
  63. package/dist/exports/init-output.mjs +1 -1
  64. package/dist/{framework-components-xFLFpZUO.mjs → framework-components-65gOHkHB.mjs} +2 -2
  65. package/dist/{framework-components-xFLFpZUO.mjs.map → framework-components-65gOHkHB.mjs.map} +1 -1
  66. package/dist/{global-flags-DGmw6Kqg.d.mts → global-flags-CdE7M0d9.d.mts} +4 -1
  67. package/dist/global-flags-CdE7M0d9.d.mts.map +1 -0
  68. package/dist/{graph-render-eJDcLWny.mjs → graph-render-DJVv0_uf.mjs} +1 -1
  69. package/dist/{graph-render-eJDcLWny.mjs.map → graph-render-DJVv0_uf.mjs.map} +1 -1
  70. package/dist/{init-CxS9eqbQ.mjs → init-YX6lCJpG.mjs} +140 -301
  71. package/dist/init-YX6lCJpG.mjs.map +1 -0
  72. package/dist/{inspect-live-schema-iETRZ_59.mjs → inspect-live-schema-LeWvkZVz.mjs} +4 -4
  73. package/dist/{inspect-live-schema-iETRZ_59.mjs.map → inspect-live-schema-LeWvkZVz.mjs.map} +1 -1
  74. package/dist/{migration-command-scaffold-BlgVj_Pn.mjs → migration-command-scaffold-BtkunvFQ.mjs} +4 -4
  75. package/dist/{migration-command-scaffold-BlgVj_Pn.mjs.map → migration-command-scaffold-BtkunvFQ.mjs.map} +1 -1
  76. package/dist/{migration-plan-BSzcWsvm.mjs → migration-plan-C2jeH1J5.mjs} +8 -12
  77. package/dist/migration-plan-C2jeH1J5.mjs.map +1 -0
  78. package/dist/{migration-types-D2FW63pr.d.mts → migration-types-BXWvz12q.d.mts} +1 -1
  79. package/dist/{migration-types-D2FW63pr.d.mts.map → migration-types-BXWvz12q.d.mts.map} +1 -1
  80. package/dist/{migrations-CgANWI0w.mjs → migrations-CwZMa1Ck.mjs} +2 -2
  81. package/dist/{migrations-CgANWI0w.mjs.map → migrations-CwZMa1Ck.mjs.map} +1 -1
  82. package/dist/{output-B60Gw5fu.mjs → output-BlsrGMEF.mjs} +1 -1
  83. package/dist/{output-B60Gw5fu.mjs.map → output-BlsrGMEF.mjs.map} +1 -1
  84. package/dist/readme-mongo.md +35 -0
  85. package/dist/readme-postgres.md +34 -0
  86. package/dist/{terminal-ui-XtOQsqe9.mjs → terminal-ui-BiB_8KNo.mjs} +131 -24
  87. package/dist/terminal-ui-BiB_8KNo.mjs.map +1 -0
  88. package/dist/{types-0aS865QN.d.mts → types--CqjMdk0.d.mts} +2 -2
  89. package/dist/{types-0aS865QN.d.mts.map → types--CqjMdk0.d.mts.map} +1 -1
  90. package/dist/{verify-nlzO0uIY.mjs → verify-Bom75OYI.mjs} +2 -2
  91. package/dist/{verify-nlzO0uIY.mjs.map → verify-Bom75OYI.mjs.map} +1 -1
  92. package/package.json +18 -18
  93. package/src/cli.ts +36 -9
  94. package/src/commands/contract-emit.ts +4 -4
  95. package/src/commands/contract-infer.ts +7 -7
  96. package/src/commands/db-init.ts +13 -5
  97. package/src/commands/db-schema.ts +4 -4
  98. package/src/commands/db-sign.ts +4 -4
  99. package/src/commands/db-update.ts +13 -5
  100. package/src/commands/db-verify.ts +5 -5
  101. package/src/commands/init/detect-package-manager.ts +15 -0
  102. package/src/commands/init/errors.ts +31 -0
  103. package/src/commands/init/hygiene-gitattributes.ts +2 -2
  104. package/src/commands/init/index.ts +4 -3
  105. package/src/commands/init/init.ts +33 -17
  106. package/src/commands/init/inputs.ts +6 -4
  107. package/src/commands/init/output.ts +1 -1
  108. package/src/commands/init/skill-install.ts +37 -26
  109. package/src/commands/init/templates/code-templates.ts +8 -14
  110. package/src/commands/init/templates/env.ts +8 -1
  111. package/src/commands/init/templates/readme-mongo.md +35 -0
  112. package/src/commands/init/templates/readme-postgres.md +34 -0
  113. package/src/commands/init/templates/readme.ts +62 -0
  114. package/src/commands/migrate.ts +4 -7
  115. package/src/commands/migration-check.ts +4 -4
  116. package/src/commands/migration-graph.ts +4 -4
  117. package/src/commands/migration-list.ts +4 -4
  118. package/src/commands/migration-log.ts +6 -5
  119. package/src/commands/migration-new.ts +4 -4
  120. package/src/commands/migration-plan.ts +4 -4
  121. package/src/commands/migration-show.ts +4 -4
  122. package/src/commands/migration-status.ts +49 -6
  123. package/src/commands/ref.ts +8 -8
  124. package/src/control-api/operations/apply-aggregate.ts +1 -0
  125. package/src/utils/cli-errors.ts +4 -0
  126. package/src/utils/command-helpers.ts +14 -6
  127. package/src/utils/global-flags.ts +102 -17
  128. package/src/utils/telemetry.ts +27 -52
  129. package/src/utils/terminal-ui.ts +44 -23
  130. package/dist/cli-errors-CF60g2cG.mjs.map +0 -1
  131. package/dist/client-Brv4qlfB.mjs.map +0 -1
  132. package/dist/command-helpers-Dvgul7UA.mjs.map +0 -1
  133. package/dist/contract-emit-BDBzHlaC.mjs.map +0 -1
  134. package/dist/contract-infer-Dm8pBZMR.mjs.map +0 -1
  135. package/dist/db-verify-CW8DR5Ei.mjs.map +0 -1
  136. package/dist/errors-BYAXmyRJ.mjs +0 -56
  137. package/dist/errors-BYAXmyRJ.mjs.map +0 -1
  138. package/dist/global-flags-DGmw6Kqg.d.mts.map +0 -1
  139. package/dist/init-CxS9eqbQ.mjs.map +0 -1
  140. package/dist/is-ci-YyvQBBke.mjs +0 -44
  141. package/dist/is-ci-YyvQBBke.mjs.map +0 -1
  142. package/dist/migration-plan-BSzcWsvm.mjs.map +0 -1
  143. package/dist/result-handler-CG3vVoKf.mjs +0 -25
  144. package/dist/result-handler-CG3vVoKf.mjs.map +0 -1
  145. package/dist/terminal-ui-XtOQsqe9.mjs.map +0 -1
  146. /package/dist/{cli-errors-DdcjVLJV.d.mts → cli-errors-Czmx92Zy.d.mts} +0 -0
@@ -47,10 +47,10 @@ import {
47
47
  formatVerifyOutput,
48
48
  } from '../utils/formatters/verify';
49
49
  import type { CommonCommandOptions } from '../utils/global-flags';
50
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
50
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
51
51
  import { createProgressAdapter } from '../utils/progress-adapter';
52
52
  import { handleResult } from '../utils/result-handler';
53
- import { TerminalUI } from '../utils/terminal-ui';
53
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
54
54
 
55
55
  interface DbVerifyOptions extends CommonCommandOptions {
56
56
  readonly db?: string;
@@ -331,7 +331,7 @@ function wrapVerifyError(
331
331
  contractPathAbsolute: string,
332
332
  modeLabel: string,
333
333
  ): Result<never, CliStructuredError> {
334
- if (error instanceof CliStructuredError) {
334
+ if (CliStructuredError.is(error)) {
335
335
  return notOk(error);
336
336
  }
337
337
  if (error instanceof ContractValidationError) {
@@ -529,8 +529,8 @@ export function createDbVerifyCommand(): Command {
529
529
  false,
530
530
  )
531
531
  .action(async (options: DbVerifyOptions) => {
532
- const flags = parseGlobalFlags(options);
533
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
532
+ const flags = parseGlobalFlagsOrExit(options);
533
+ const ui = createTerminalUI(flags);
534
534
 
535
535
  const modeResult = resolveDbVerifyMode(options);
536
536
  if (!modeResult.ok) {
@@ -56,6 +56,21 @@ export function formatRunCommand(pm: PackageManager, bin: string, args: string):
56
56
  return `${pm} ${bin} ${args}`;
57
57
  }
58
58
 
59
+ export function formatRunScriptCommand(pm: PackageManager, scriptName: string): string {
60
+ switch (pm) {
61
+ case 'deno':
62
+ return `deno task ${scriptName}`;
63
+ case 'bun':
64
+ return `bun run ${scriptName}`;
65
+ case 'pnpm':
66
+ return `pnpm run ${scriptName}`;
67
+ case 'yarn':
68
+ return `yarn run ${scriptName}`;
69
+ default:
70
+ return `npm run ${scriptName}`;
71
+ }
72
+ }
73
+
59
74
  export function formatAddArgs(pm: PackageManager, packages: string[]): string[] {
60
75
  if (pm === 'deno') {
61
76
  return ['add', ...packages.map((p) => `npm:${p}`)];
@@ -69,6 +69,37 @@ export function errorInitInvalidFlagValue(options: {
69
69
  });
70
70
  }
71
71
 
72
+ /**
73
+ * `--authoring` and `--schema-path` disagree on file extension (e.g. PSL
74
+ * authoring with a `.ts` path). Surfaces before any scaffold files are
75
+ * written so the project tree stays untouched.
76
+ */
77
+ export function errorInitAuthoringSchemaPathMismatch(options: {
78
+ readonly authoring: 'psl' | 'typescript';
79
+ readonly schemaPath: string;
80
+ readonly actualExtension: string;
81
+ readonly expectedExtension: string;
82
+ }): CliStructuredError {
83
+ const expectedAuthoring = options.expectedExtension === '.ts' ? 'typescript' : 'psl';
84
+ return new CliStructuredError('5014', 'Authoring and schema path do not match', {
85
+ domain: 'CLI',
86
+ why:
87
+ `\`--authoring ${options.authoring}\` requires a schema file ending in ${options.expectedExtension}, ` +
88
+ `but \`--schema-path ${options.schemaPath}\` ends in ${options.actualExtension}.`,
89
+ fix:
90
+ `Use a matching pair, for example \`--authoring ${expectedAuthoring} --schema-path <path>${options.expectedExtension}\`, ` +
91
+ 'or change `--authoring` to match the path you supplied. ' +
92
+ 'You can also omit `--schema-path` to use the default for the chosen authoring.',
93
+ docsUrl: 'https://prisma-next.dev/docs/cli/init',
94
+ meta: {
95
+ authoring: options.authoring,
96
+ schemaPath: options.schemaPath,
97
+ actualExtension: options.actualExtension,
98
+ expectedExtension: options.expectedExtension,
99
+ },
100
+ });
101
+ }
102
+
72
103
  /**
73
104
  * The user cancelled an interactive prompt (Ctrl-C, escape, declined a
74
105
  * selection). Distinct from `errorInitReinitNeedsForce` because that path
@@ -20,7 +20,7 @@ import type { TargetId } from './templates/code-templates';
20
20
  * `db/contract.json linguist-generated` — not the workspace-glob form
21
21
  * `<glob>/contract.json` (which would over-match any unrelated
22
22
  * `contract.json` the user has elsewhere) and not the absolute
23
- * `prisma/contract.json` (which would silently break for a non-default
23
+ * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json` (which would silently break for a non-default
24
24
  * schema path).
25
25
  */
26
26
  const ARTEFACT_FILENAMES: readonly string[] = [
@@ -59,7 +59,7 @@ export function requiredGitattributesLines(
59
59
  *
60
60
  * Equivalence is exact-line: a user-customised line like
61
61
  * `prisma/*.json linguist-generated` is *not* recognised as covering
62
- * `prisma/contract.json linguist-generated`. We accept that
62
+ * `DEFAULT_CONTRACT_SOURCE_DIR/contract.json linguist-generated`. We accept that
63
63
  * over-specification — preserving the user's broad pattern *and*
64
64
  * appending the narrow one — because the narrow lines are what the
65
65
  * acceptance criteria pin (FR3.4 AC).
@@ -4,7 +4,7 @@ import {
4
4
  setCommandDescriptions,
5
5
  setCommandExamples,
6
6
  } from '../../utils/command-helpers';
7
- import { type CommonCommandOptions, parseGlobalFlags } from '../../utils/global-flags';
7
+ import { type CommonCommandOptions, parseGlobalFlagsOrExit } from '../../utils/global-flags';
8
8
  import { fireTelemetryAfterInitConsent } from '../../utils/telemetry';
9
9
  import {
10
10
  INIT_EXIT_EMIT_FAILED,
@@ -15,6 +15,7 @@ import {
15
15
  INIT_EXIT_SKILL_INSTALL_FAILED,
16
16
  INIT_EXIT_USER_ABORTED,
17
17
  } from './exit-codes';
18
+ import { defaultSchemaPath } from './templates/code-templates';
18
19
 
19
20
  /**
20
21
  * Commander.js parsed options for `init`. The init-specific options live
@@ -72,7 +73,7 @@ export function createInitCommand(): Command {
72
73
  .option('--authoring <style>', 'Schema authoring style: psl or typescript')
73
74
  .option(
74
75
  '--schema-path <path>',
75
- 'Where to write the starter schema (default: prisma/contract.prisma)',
76
+ `Where to write the starter schema (default: ${defaultSchemaPath('psl')})`,
76
77
  )
77
78
  .option('--force', 'Overwrite an existing scaffold without prompting')
78
79
  .option(
@@ -94,7 +95,7 @@ export function createInitCommand(): Command {
94
95
  )
95
96
  .action(async (options: InitCommandOptions, actionCommand: Command) => {
96
97
  const { runInit } = await import('./init');
97
- const flags = parseGlobalFlags(options);
98
+ const flags = parseGlobalFlagsOrExit(options);
98
99
  const canPrompt = deriveCanPrompt({
99
100
  flagsInteractive: flags.interactive,
100
101
  optionInteractive: options.interactive,
@@ -6,7 +6,7 @@ import { basename, dirname, isAbsolute, join } from 'pathe';
6
6
  import { CliStructuredError } from '../../utils/cli-errors';
7
7
  import { formatErrorJson, formatErrorOutput } from '../../utils/formatters/errors';
8
8
  import type { GlobalFlags } from '../../utils/global-flags';
9
- import { TerminalUI } from '../../utils/terminal-ui';
9
+ import { createTerminalUI, type TerminalUI } from '../../utils/terminal-ui';
10
10
  import {
11
11
  detectPackageManager,
12
12
  formatAddArgs,
@@ -51,7 +51,6 @@ import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './pr
51
51
  import { findStaleArtefacts, removeDependency } from './reinit-cleanup';
52
52
  import {
53
53
  DEFAULT_SKILL_SOURCES,
54
- formatClaudeSkillInstallCommand,
55
54
  formatSkillInstallCommand,
56
55
  LEGACY_SKILL_FILE,
57
56
  runProjectLevelSkillInstall,
@@ -59,6 +58,7 @@ import {
59
58
  import { configFile, dbFile, starterSchema, targetPackageName } from './templates/code-templates';
60
59
  import { envExampleContent, envFileContent, MIN_SERVER_VERSION } from './templates/env';
61
60
  import { quickReferenceMd } from './templates/quick-reference';
61
+ import { minimalProjectReadmeMd } from './templates/readme';
62
62
  import { defaultTsConfig, mergeTsConfig, TsConfigParseError } from './templates/tsconfig';
63
63
 
64
64
  interface FileEntry {
@@ -130,7 +130,7 @@ export async function runInit(
130
130
  },
131
131
  ): Promise<number> {
132
132
  const { options, flags, canPrompt, probeOverrides, afterFirstTelemetryConsent } = runOptions;
133
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
133
+ const ui = createTerminalUI(flags);
134
134
  const warnings: string[] = [];
135
135
  const filesWritten: string[] = [];
136
136
  const filesDeleted: string[] = [];
@@ -349,6 +349,26 @@ export async function runInit(
349
349
  }
350
350
  }
351
351
 
352
+ if (existsSync(join(baseDir, 'src/index.ts'))) {
353
+ if (!existsSync(join(baseDir, 'README.md'))) {
354
+ const rawName =
355
+ parsedPackageJson !== null && typeof parsedPackageJson['name'] === 'string'
356
+ ? parsedPackageJson['name']
357
+ : basename(baseDir);
358
+ filesToWrite.push({
359
+ path: 'README.md',
360
+ content: minimalProjectReadmeMd(
361
+ inputs.target,
362
+ inputs.schemaPath,
363
+ sanitisePackageName(rawName),
364
+ pm,
365
+ ),
366
+ });
367
+ } else {
368
+ warnings.push('README.md already exists; leaving it untouched.');
369
+ }
370
+ }
371
+
352
372
  // -----------------------------------------------------------------
353
373
  // Write phase — every input has been parsed and every merged file is
354
374
  // staged. From here on, failures are only possible at the
@@ -458,26 +478,21 @@ export async function runInit(
458
478
  // framework relies on. A project-level failure is fatal
459
479
  // (`INIT_EXIT_SKILL_INSTALL_FAILED`).
460
480
  //
461
- // Runs after install + emit because (1) `node_modules` is in place,
462
- // so any consumers downstream are coherent, and (2) the user has
463
- // seen the scaffolding succeed before this network-bound step
464
- // potentially fails. We skip the install when the user passed
465
- // `--no-install` for the same reason no `node_modules` means the
466
- // workspace isn't ready to consume the skill yet anyway.
467
- const manualProjectSkillCommands = DEFAULT_SKILL_SOURCES.flatMap((source) => [
468
- formatSkillInstallCommand(install.effectivePm, source),
469
- formatClaudeSkillInstallCommand(install.effectivePm, source),
470
- ]);
481
+ // Runs after the install + emit phase when those steps are enabled,
482
+ // but is not coupled to them: the skills are pulled directly from
483
+ // the Prisma Next GitHub repo and do not require `node_modules`.
484
+ // `--no-install` therefore skips only dependency installation and
485
+ // contract emission; `--no-skill` is the explicit escape hatch for
486
+ // skipping skills.
487
+ const manualProjectSkillCommands = DEFAULT_SKILL_SOURCES.map((source) =>
488
+ formatSkillInstallCommand({ pm: install.effectivePm, source }),
489
+ );
471
490
  const manualProjectSkillSummary = manualProjectSkillCommands.map((c) => `\`${c}\``).join(' && ');
472
491
  let skillRegistered = false;
473
492
  if (!inputs.installProjectSkill) {
474
493
  warnings.push(
475
494
  `Skipped Prisma Next skills install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`,
476
495
  );
477
- } else if (install.skipped) {
478
- warnings.push(
479
- `Skipped Prisma Next skills install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`,
480
- );
481
496
  } else {
482
497
  const spinner = ui.spinner();
483
498
  spinner.start('Registering Prisma Next skills with the agent runtime...');
@@ -601,6 +616,7 @@ export function exitCodeForError(error: { readonly code: string }): number {
601
616
  case '5010': // invalid manifest (malformed package.json) — precondition
602
617
  case '5011': // invalid tsconfig (unparseable JSONC) — precondition
603
618
  case '5012': // probe failed under --strict-probe — precondition
619
+ case '5014': // --authoring / --schema-path extension mismatch — precondition
604
620
  return INIT_EXIT_PRECONDITION;
605
621
  case '5006': // user aborted interactive prompt
606
622
  return INIT_EXIT_USER_ABORTED;
@@ -5,6 +5,7 @@ import { extname, join, normalize } from 'pathe';
5
5
  import type { GlobalFlags } from '../../utils/global-flags';
6
6
  import { isCI } from '../../utils/is-ci';
7
7
  import {
8
+ errorInitAuthoringSchemaPathMismatch,
8
9
  errorInitInvalidFlagValue,
9
10
  errorInitMissingFlags,
10
11
  errorInitReinitNeedsForce,
@@ -451,10 +452,11 @@ function validateSchemaPath(value: string, authoring: AuthoringId): string {
451
452
  const ext = extname(trimmed).toLowerCase();
452
453
  const expected = authoring === 'typescript' ? '.ts' : '.prisma';
453
454
  if (ext !== expected) {
454
- throw errorInitInvalidFlagValue({
455
- flag: 'schema-path',
456
- value,
457
- allowed: [`<file path ending in ${expected} for --authoring ${authoring}>`],
455
+ throw errorInitAuthoringSchemaPathMismatch({
456
+ authoring,
457
+ schemaPath: trimmed,
458
+ actualExtension: ext.length > 0 ? ext : '(none)',
459
+ expectedExtension: expected,
458
460
  });
459
461
  }
460
462
  return normalize(trimmed);
@@ -123,7 +123,7 @@ export function buildNextSteps(options: {
123
123
  /**
124
124
  * Whether the project-level Prisma Next skills install actually ran
125
125
  * and succeeded during this `init`. When false (the user passed
126
- * `--no-skill` or `--no-install`, so the install was skipped), the
126
+ * `--no-skill`, so the install was skipped), the
127
127
  * "registered with your agent runtime" step is omitted — the skip is
128
128
  * already surfaced in the warnings array with a manual-install hint.
129
129
  */
@@ -15,8 +15,8 @@ export const DEFAULT_SKILL_BASE = 'prisma/prisma-next';
15
15
 
16
16
  /**
17
17
  * One discovery scope inside the Prisma Next monorepo. The CLI emits
18
- * one `skills add <base>/<subpath>[#ref] --all` invocation per source
19
- * during `init`.
18
+ * one `skills add <base>/<subpath>[#ref] --agent ... --skill '*' -y`
19
+ * invocation per source during `init`.
20
20
  *
21
21
  * `ref` semantics:
22
22
  * - `cli`: pin to the CLI's own package version (lockstep with the
@@ -73,6 +73,21 @@ function isLocalPath(base: string): boolean {
73
73
  return base.startsWith('/') || /^[a-zA-Z]:[\\/]/.test(base);
74
74
  }
75
75
 
76
+ /** Agent slugs accepted by the upstream `skills add --agent` flag. */
77
+ export type SkillAgent = 'cursor' | 'claude-code' | 'codex' | 'windsurf';
78
+
79
+ /**
80
+ * Agents passed to every project-level init install. Upstream `skills add`
81
+ * is the source of truth for per-agent install behaviour; the CLI lists
82
+ * every supported runtime on one invocation and delegates the rest.
83
+ */
84
+ export const DEFAULT_SKILL_AGENTS: readonly SkillAgent[] = [
85
+ 'cursor',
86
+ 'claude-code',
87
+ 'codex',
88
+ 'windsurf',
89
+ ];
90
+
76
91
  /**
77
92
  * Build the `<base>/<subpath>[#ref]` URL the `skills` CLI will
78
93
  * resolve. Exported for unit tests so the per-source format can be
@@ -94,37 +109,36 @@ export function formatSkillSourceUrl(source: SkillSource): string {
94
109
  * rest of the install step so a single project consistently uses one
95
110
  * runner.
96
111
  *
97
- * `--all` auto-selects every skill in the cluster and every detected
98
- * agent runtime, skipping the multi-select prompts the `skills` CLI
99
- * shows by default. A non-interactive scaffold step cannot present
100
- * prompts.
112
+ * `--agent` takes space-separated slugs on one flag; `--skill '*'` and `-y`
113
+ * skip the multi-select prompts a non-interactive scaffold step cannot show.
101
114
  *
102
115
  * Exported for unit tests so the per-PM dispatch can be asserted
103
116
  * without a live subprocess.
104
117
  */
105
- export function formatSkillInstallCommand(pm: PackageManager, source: SkillSource): string {
106
- const args = ['skills@latest', 'add', formatSkillSourceUrl(source), '--all'];
107
- return formatPackageManagerCommand(pm, args);
108
- }
109
-
110
- /**
111
- * `skills add --all` should cover Claude Code, but upstream currently skips
112
- * project-local Claude symlinks when `.claude/` does not already exist. Run
113
- * the explicit Claude Code install as well so fresh projects get
114
- * `.claude/skills` without asking users to create that folder first.
115
- */
116
- export function formatClaudeSkillInstallCommand(pm: PackageManager, source: SkillSource): string {
117
- const args = [
118
+ export function formatSkillInstallCommand(args: {
119
+ readonly pm: PackageManager;
120
+ readonly source: SkillSource;
121
+ readonly agents?: readonly SkillAgent[];
122
+ }): string {
123
+ const agents = args.agents ?? DEFAULT_SKILL_AGENTS;
124
+ const cliArgs = [
118
125
  'skills@latest',
119
126
  'add',
120
- formatSkillSourceUrl(source),
127
+ formatSkillSourceUrl(args.source),
121
128
  '--agent',
122
- 'claude-code',
129
+ ...agents,
123
130
  '--skill',
124
131
  "'*'",
125
132
  '-y',
126
133
  ];
127
- return formatPackageManagerCommand(pm, args);
134
+ return formatPackageManagerCommand(args.pm, cliArgs);
135
+ }
136
+
137
+ /**
138
+ * Ordered skill-install commands for one init run. Exported for unit tests.
139
+ */
140
+ export function resolveProjectSkillInstallCommands(pm: PackageManager): readonly string[] {
141
+ return DEFAULT_SKILL_SOURCES.map((source) => formatSkillInstallCommand({ pm, source }));
128
142
  }
129
143
 
130
144
  function formatPackageManagerCommand(pm: PackageManager, args: readonly string[]): string {
@@ -176,10 +190,7 @@ export async function runProjectLevelSkillInstall(ctx: {
176
190
  readonly filesWritten: readonly string[];
177
191
  }): Promise<{ readonly ok: true; readonly commands: readonly string[] }> {
178
192
  const commands: string[] = [];
179
- const installCommands = DEFAULT_SKILL_SOURCES.flatMap((source) => [
180
- formatSkillInstallCommand(ctx.pm, source),
181
- formatClaudeSkillInstallCommand(ctx.pm, source),
182
- ]);
193
+ const installCommands = resolveProjectSkillInstallCommands(ctx.pm);
183
194
 
184
195
  for (const command of installCommands) {
185
196
  const { file, args } = commandToExec(command);
@@ -1,3 +1,5 @@
1
+ import { DEFAULT_CONTRACT_SOURCE_DIR } from '@prisma-next/config/config-types';
2
+
1
3
  export type TargetId = 'postgres' | 'mongo';
2
4
  export type AuthoringId = 'psl' | 'typescript';
3
5
 
@@ -11,9 +13,9 @@ export function targetLabel(target: TargetId): string {
11
13
 
12
14
  export function defaultSchemaPath(authoring: AuthoringId): string {
13
15
  if (authoring === 'typescript') {
14
- return 'prisma/contract.ts';
16
+ return `${DEFAULT_CONTRACT_SOURCE_DIR}/contract.ts`;
15
17
  }
16
- return 'prisma/contract.prisma';
18
+ return `${DEFAULT_CONTRACT_SOURCE_DIR}/contract.prisma`;
17
19
  }
18
20
 
19
21
  export function starterSchema(target: TargetId, authoring: AuthoringId): string {
@@ -65,11 +67,9 @@ model User {
65
67
  function schemaSampleTsPostgres(): string {
66
68
  return `\`\`\`typescript
67
69
  import { defineContract } from '@prisma-next/postgres/contract-builder';
68
- import sqlFamily from '@prisma-next/postgres/family';
69
- import postgresTarget from '@prisma-next/postgres/target';
70
70
 
71
71
  export const contract = defineContract(
72
- { family: sqlFamily, target: postgresTarget },
72
+ {},
73
73
  ({ field, model }) => ({
74
74
  models: {
75
75
  User: model('User', {
@@ -89,11 +89,9 @@ export const contract = defineContract(
89
89
  function schemaSampleTsMongo(): string {
90
90
  return `\`\`\`typescript
91
91
  import { defineContract } from '@prisma-next/mongo/contract-builder';
92
- import mongoFamily from '@prisma-next/mongo/family';
93
- import mongoTarget from '@prisma-next/mongo/target';
94
92
 
95
93
  export const contract = defineContract(
96
- { family: mongoFamily, target: mongoTarget },
94
+ {},
97
95
  ({ field, model }) => ({
98
96
  models: {
99
97
  User: model('User', {
@@ -161,11 +159,9 @@ model Post {
161
159
 
162
160
  function starterSchemaTsPostgres(): string {
163
161
  return `import { defineContract } from '@prisma-next/postgres/contract-builder';
164
- import sqlFamily from '@prisma-next/postgres/family';
165
- import postgresTarget from '@prisma-next/postgres/target';
166
162
 
167
163
  export const contract = defineContract(
168
- { family: sqlFamily, target: postgresTarget },
164
+ {},
169
165
  ({ field, model, rel }) => ({
170
166
  models: {
171
167
  User: model('User', {
@@ -203,11 +199,9 @@ export const contract = defineContract(
203
199
 
204
200
  function starterSchemaTsMongo(): string {
205
201
  return `import { defineContract } from '@prisma-next/mongo/contract-builder';
206
- import mongoFamily from '@prisma-next/mongo/family';
207
- import mongoTarget from '@prisma-next/mongo/target';
208
202
 
209
203
  export const contract = defineContract(
210
- { family: mongoFamily, target: mongoTarget },
204
+ {},
211
205
  ({ field, model, rel }) => ({
212
206
  models: {
213
207
  User: model('User', {
@@ -40,7 +40,14 @@ function envPlaceholderBody(target: TargetId): string {
40
40
  if (target === 'postgres') {
41
41
  lines.push('DATABASE_URL="postgresql://user:password@localhost:5432/mydb"');
42
42
  } else {
43
- lines.push('DATABASE_URL="mongodb://localhost:27017/mydb"');
43
+ lines.push(
44
+ '# Standalone local mongod / `docker run mongo:7` — no replica set required for first-run queries.',
45
+ );
46
+ lines.push(
47
+ '# Transactions and change streams need a replica set; add ?replicaSet=... only after initiating one.',
48
+ );
49
+ lines.push('');
50
+ lines.push('DATABASE_URL="mongodb://user:password@localhost:27017/mydb"');
44
51
  }
45
52
  lines.push('');
46
53
  return lines.join('\n');
@@ -0,0 +1,35 @@
1
+ # {{projectName}}
2
+
3
+ Generated by `prisma-next init` with the Minimal template.
4
+
5
+ ## First run
6
+
7
+ 1. `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
8
+ 2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
9
+
10
+ ## Available scripts
11
+
12
+ - `{{runDev}}` — run the Prisma Next sample script
13
+
14
+ ### Database and migrations
15
+
16
+ - `{{runContractEmit}}` — emit contract artifacts after contract changes
17
+ - `{{runDbUp}}` — start the local MongoDB replica set with `mongodb-memory-server`. Data persists in `.mongo-data/` across restarts.
18
+ - `{{runDbDown}}` — stop the local MongoDB process (data preserved)
19
+ - `{{runDbReset}}` — stop and wipe `.mongo-data/` for a clean slate
20
+ - `{{runMigrationPlan}}` — create a MongoDB migration plan
21
+ - `{{runMigrate}}` — apply the planned MongoDB migration
22
+ - `{{runDbSeed}}` — insert sample users manually
23
+
24
+ ## Prisma Next
25
+
26
+ Prisma Next setup is scaffolded in:
27
+
28
+ - `{{contractPath}}`
29
+ - `prisma-next.config.ts`
30
+ - `prisma/db.ts`
31
+ - `src/lib/prisma.ts`
32
+
33
+ For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
34
+
35
+ Node-based Prisma Next projects expect Node.js 24 LTS or newer.
@@ -0,0 +1,34 @@
1
+ # {{projectName}}
2
+
3
+ Generated by `prisma-next init` with the Minimal template.
4
+
5
+ ## First run
6
+
7
+ 1. `{{runDbInit}}` — applies your contract to the database (creates tables, signs the marker).
8
+ 2. `{{runDev}}` — runs `src/index.ts`, which writes one row and reads it back.
9
+
10
+ ## Available scripts
11
+
12
+ - `{{runDev}}` — run the Prisma Next sample script
13
+
14
+ ### Database and migrations
15
+
16
+ - `{{runContractEmit}}` — emit contract artifacts after contract changes
17
+ - `{{runDbInit}}` — initialize database state manually
18
+ - `{{runDbUpdate}}` — update database state manually
19
+ - `{{runMigrationPlan}}` — create a migration plan
20
+ - `{{runMigrate}}` — apply a planned migration
21
+ - `{{runDbSeed}}` — insert sample users manually (run after `db:init`)
22
+
23
+ ## Prisma Next
24
+
25
+ Prisma Next setup is scaffolded in:
26
+
27
+ - `{{contractPath}}`
28
+ - `prisma-next.config.ts`
29
+ - `prisma/db.ts`
30
+ - `src/lib/prisma.ts`
31
+
32
+ For provider-specific Prisma Next reference docs, see `prisma-next.md`. Prisma Next skills live in the upstream `skills/` directory: https://github.com/prisma/prisma-next/tree/main/skills.
33
+
34
+ Node-based Prisma Next projects expect Node.js 24 LTS or newer.
@@ -0,0 +1,62 @@
1
+ import { formatRunScriptCommand, type PackageManager } from '../detect-package-manager';
2
+ import type { TargetId } from './code-templates';
3
+ import { renderTemplate } from './render';
4
+
5
+ const sharedVariables = ['projectName', 'contractPath', 'runDev', 'runContractEmit'] as const;
6
+
7
+ export const postgresVariables = [
8
+ ...sharedVariables,
9
+ 'runDbInit',
10
+ 'runDbUpdate',
11
+ 'runMigrationPlan',
12
+ 'runMigrate',
13
+ 'runDbSeed',
14
+ ] as const;
15
+
16
+ export const mongoVariables = [
17
+ ...sharedVariables,
18
+ 'runDbUp',
19
+ 'runDbDown',
20
+ 'runDbReset',
21
+ 'runMigrationPlan',
22
+ 'runMigrate',
23
+ 'runDbSeed',
24
+ ] as const;
25
+
26
+ type PostgresVars = Record<(typeof postgresVariables)[number], string>;
27
+ type MongoVars = Record<(typeof mongoVariables)[number], string>;
28
+
29
+ export function minimalProjectReadmeMd(
30
+ target: TargetId,
31
+ schemaPath: string,
32
+ projectName: string,
33
+ pm: PackageManager,
34
+ ): string {
35
+ const run = (script: string): string => formatRunScriptCommand(pm, script);
36
+ const shared = {
37
+ projectName,
38
+ contractPath: schemaPath,
39
+ runDev: run('dev'),
40
+ runContractEmit: run('contract:emit'),
41
+ runMigrationPlan: run('migration:plan'),
42
+ runMigrate: run('migrate'),
43
+ runDbSeed: run('db:seed'),
44
+ };
45
+
46
+ if (target === 'mongo') {
47
+ const vars: MongoVars = {
48
+ ...shared,
49
+ runDbUp: run('db:up'),
50
+ runDbDown: run('db:down'),
51
+ runDbReset: run('db:reset'),
52
+ };
53
+ return renderTemplate('readme-mongo.md', mongoVariables, vars);
54
+ }
55
+
56
+ const vars: PostgresVars = {
57
+ ...shared,
58
+ runDbInit: run('db:init'),
59
+ runDbUpdate: run('db:update'),
60
+ };
61
+ return renderTemplate('readme-postgres.md', postgresVariables, vars);
62
+ }
@@ -43,9 +43,9 @@ import {
43
43
  import { formatMigrationApplyCommandOutput } from '../utils/formatters/migrations';
44
44
  import { formatStyledHeader } from '../utils/formatters/styled';
45
45
  import type { CommonCommandOptions } from '../utils/global-flags';
46
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
46
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
47
47
  import { handleResult } from '../utils/result-handler';
48
- import { TerminalUI } from '../utils/terminal-ui';
48
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
49
49
 
50
50
  interface MigrateCommandOptions extends CommonCommandOptions {
51
51
  readonly db?: string;
@@ -319,13 +319,10 @@ export function createMigrateCommand(): Command {
319
319
  'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
320
320
  )
321
321
  .action(async (options: MigrateCommandOptions) => {
322
- const flags = parseGlobalFlags(options);
322
+ const flags = parseGlobalFlagsOrExit(options);
323
323
  const startTime = Date.now();
324
324
 
325
- const ui = new TerminalUI({
326
- color: flags.color,
327
- interactive: flags.interactive,
328
- });
325
+ const ui = createTerminalUI(flags);
329
326
 
330
327
  const result = await executeMigrateCommand(options, flags, ui, startTime);
331
328
 
@@ -17,8 +17,8 @@ import {
17
17
  } from '../utils/command-helpers';
18
18
  import { formatStyledHeader } from '../utils/formatters/styled';
19
19
  import type { CommonCommandOptions } from '../utils/global-flags';
20
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
21
- import { TerminalUI } from '../utils/terminal-ui';
20
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
21
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
22
22
  import { INTEGRITY_FAILED, OK, PRECONDITION } from './migration-check/exit-codes';
23
23
 
24
24
  interface MigrationCheckOptions extends CommonCommandOptions {
@@ -335,8 +335,8 @@ export function createMigrationCheckCommand(): Command {
335
335
  .argument('[migration]', 'Migration reference (directory name or hash) to check')
336
336
  .option('--config <path>', 'Path to prisma-next.config.ts')
337
337
  .action(async (target: string | undefined, options: MigrationCheckOptions) => {
338
- const flags = parseGlobalFlags(options);
339
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
338
+ const flags = parseGlobalFlagsOrExit(options);
339
+ const ui = createTerminalUI(flags);
340
340
 
341
341
  let result: MigrationCheckResult;
342
342
  let exitCode: number;