@prisma-next/cli 0.10.0 → 0.11.0-dev.2

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 (151) 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 +400 -13
  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-D3vL5yi8.mjs → command-helpers-DtavI0wJ.mjs} +109 -12
  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-C3STUIBg.mjs → contract-emit-uwT-Mj8-.mjs} +7 -12
  53. package/dist/contract-emit-uwT-Mj8-.mjs.map +1 -0
  54. package/dist/{contract-infer-Cnj8G1E2.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-D7cyH_zz.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 +2 -2
  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-eh2z5Tl6.mjs → init-YX6lCJpG.mjs} +528 -627
  71. package/dist/init-YX6lCJpG.mjs.map +1 -0
  72. package/dist/{inspect-live-schema-CWLK_lgs.mjs → inspect-live-schema-LeWvkZVz.mjs} +4 -4
  73. package/dist/{inspect-live-schema-CWLK_lgs.mjs.map → inspect-live-schema-LeWvkZVz.mjs.map} +1 -1
  74. package/dist/{migration-command-scaffold-CmXXC1UZ.mjs → migration-command-scaffold-BtkunvFQ.mjs} +4 -4
  75. package/dist/{migration-command-scaffold-CmXXC1UZ.mjs.map → migration-command-scaffold-BtkunvFQ.mjs.map} +1 -1
  76. package/dist/{migration-plan-CHyUlBV0.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-DyUf5lTt.mjs → migrations-CwZMa1Ck.mjs} +2 -2
  81. package/dist/{migrations-DyUf5lTt.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/quick-reference-mongo.md +1 -1
  85. package/dist/quick-reference-postgres.md +1 -1
  86. package/dist/readme-mongo.md +35 -0
  87. package/dist/readme-postgres.md +34 -0
  88. package/dist/{terminal-ui-XtOQsqe9.mjs → terminal-ui-BiB_8KNo.mjs} +131 -24
  89. package/dist/terminal-ui-BiB_8KNo.mjs.map +1 -0
  90. package/dist/{types-0aS865QN.d.mts → types--CqjMdk0.d.mts} +2 -2
  91. package/dist/{types-0aS865QN.d.mts.map → types--CqjMdk0.d.mts.map} +1 -1
  92. package/dist/{verify-D7ypCCe6.mjs → verify-Bom75OYI.mjs} +2 -2
  93. package/dist/{verify-D7ypCCe6.mjs.map → verify-Bom75OYI.mjs.map} +1 -1
  94. package/package.json +19 -17
  95. package/src/cli.ts +42 -0
  96. package/src/commands/contract-emit.ts +4 -4
  97. package/src/commands/contract-infer.ts +7 -7
  98. package/src/commands/db-init.ts +13 -5
  99. package/src/commands/db-schema.ts +4 -4
  100. package/src/commands/db-sign.ts +4 -4
  101. package/src/commands/db-update.ts +13 -5
  102. package/src/commands/db-verify.ts +5 -5
  103. package/src/commands/init/detect-package-manager.ts +15 -0
  104. package/src/commands/init/errors.ts +33 -2
  105. package/src/commands/init/hygiene-gitattributes.ts +2 -2
  106. package/src/commands/init/index.ts +15 -6
  107. package/src/commands/init/init.ts +61 -32
  108. package/src/commands/init/inputs.ts +82 -5
  109. package/src/commands/init/output.ts +1 -1
  110. package/src/commands/init/{agent-skill-install.ts → skill-install.ts} +42 -31
  111. package/src/commands/init/templates/code-templates.ts +26 -24
  112. package/src/commands/init/templates/env.ts +8 -1
  113. package/src/commands/init/templates/quick-reference-mongo.md +1 -1
  114. package/src/commands/init/templates/quick-reference-postgres.md +1 -1
  115. package/src/commands/init/templates/readme-mongo.md +35 -0
  116. package/src/commands/init/templates/readme-postgres.md +34 -0
  117. package/src/commands/init/templates/readme.ts +62 -0
  118. package/src/commands/migrate.ts +4 -7
  119. package/src/commands/migration-check.ts +4 -4
  120. package/src/commands/migration-graph.ts +4 -4
  121. package/src/commands/migration-list.ts +4 -4
  122. package/src/commands/migration-log.ts +6 -5
  123. package/src/commands/migration-new.ts +4 -4
  124. package/src/commands/migration-plan.ts +4 -4
  125. package/src/commands/migration-show.ts +4 -4
  126. package/src/commands/migration-status.ts +49 -6
  127. package/src/commands/ref.ts +8 -8
  128. package/src/control-api/operations/apply-aggregate.ts +1 -0
  129. package/src/utils/cli-errors.ts +4 -0
  130. package/src/utils/command-helpers.ts +14 -6
  131. package/src/utils/global-flags.ts +105 -16
  132. package/src/utils/is-ci.ts +18 -0
  133. package/src/utils/telemetry.ts +141 -0
  134. package/src/utils/terminal-ui.ts +44 -23
  135. package/dist/cli-errors-CF60g2cG.mjs.map +0 -1
  136. package/dist/client-Brv4qlfB.mjs.map +0 -1
  137. package/dist/command-helpers-D3vL5yi8.mjs.map +0 -1
  138. package/dist/contract-emit-C3STUIBg.mjs.map +0 -1
  139. package/dist/contract-infer-Cnj8G1E2.mjs.map +0 -1
  140. package/dist/db-verify-D7cyH_zz.mjs.map +0 -1
  141. package/dist/errors-Cw6kyTyV.mjs +0 -56
  142. package/dist/errors-Cw6kyTyV.mjs.map +0 -1
  143. package/dist/global-flags-DGmw6Kqg.d.mts.map +0 -1
  144. package/dist/helpers-eqdN8tH6.mjs +0 -25
  145. package/dist/helpers-eqdN8tH6.mjs.map +0 -1
  146. package/dist/init-eh2z5Tl6.mjs.map +0 -1
  147. package/dist/migration-plan-CHyUlBV0.mjs.map +0 -1
  148. package/dist/result-handler-Bm_6dDYg.mjs +0 -25
  149. package/dist/result-handler-Bm_6dDYg.mjs.map +0 -1
  150. package/dist/terminal-ui-XtOQsqe9.mjs.map +0 -1
  151. /package/dist/{cli-errors-DdcjVLJV.d.mts → cli-errors-Czmx92Zy.d.mts} +0 -0
@@ -20,10 +20,10 @@ import {
20
20
  } from '../utils/formatters/emit';
21
21
  import { formatStyledHeader, formatSuccessMessage } from '../utils/formatters/styled';
22
22
  import type { CommonCommandOptions } from '../utils/global-flags';
23
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
23
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
24
24
  import { createProgressAdapter } from '../utils/progress-adapter';
25
25
  import { handleResult } from '../utils/result-handler';
26
- import { TerminalUI } from '../utils/terminal-ui';
26
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
27
27
 
28
28
  interface ContractEmitOptions extends CommonCommandOptions {
29
29
  readonly config?: string;
@@ -159,8 +159,8 @@ export function createContractEmitCommand(): Command {
159
159
  addGlobalOptions(command)
160
160
  .option('--config <path>', 'Path to prisma-next.config.ts')
161
161
  .action(async (options: ContractEmitOptions) => {
162
- const flags = parseGlobalFlags(options);
163
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
162
+ const flags = parseGlobalFlagsOrExit(options);
163
+ const ui = createTerminalUI(flags);
164
164
  const startTime = Date.now();
165
165
 
166
166
  const result = await executeContractEmitCommand(options, flags, ui, startTime);
@@ -10,9 +10,9 @@ import {
10
10
  setCommandDescriptions,
11
11
  setCommandExamples,
12
12
  } from '../utils/command-helpers';
13
- import { parseGlobalFlags } from '../utils/global-flags';
13
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
14
14
  import { handleResult } from '../utils/result-handler';
15
- import { TerminalUI } from '../utils/terminal-ui';
15
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
16
16
  import { resolveContractInferOutputPath } from './contract-infer-paths';
17
17
  import {
18
18
  type InspectLiveSchemaOptions,
@@ -39,10 +39,10 @@ interface ContractInferSuccessResult {
39
39
 
40
40
  async function executeContractInferCommand(
41
41
  options: ContractInferOptions,
42
+ flags: GlobalFlags,
42
43
  ui: TerminalUI,
43
44
  startTime: number,
44
45
  ): Promise<Result<ContractInferSuccessResult, CliStructuredError>> {
45
- const flags = parseGlobalFlags(options);
46
46
  const inspectResult = await inspectLiveSchema(options, flags, ui, startTime, {
47
47
  commandName: 'contract infer',
48
48
  description: 'Infer a PSL contract from the live database schema',
@@ -104,7 +104,7 @@ export function createContractInferCommand(): Command {
104
104
  );
105
105
  setCommandExamples(command, [
106
106
  'prisma-next contract infer --db $DATABASE_URL',
107
- 'prisma-next contract infer --db $DATABASE_URL --output ./prisma/contract.prisma',
107
+ 'prisma-next contract infer --db $DATABASE_URL --output ./src/prisma/contract.prisma',
108
108
  'prisma-next contract infer --db $DATABASE_URL --json',
109
109
  ]);
110
110
  addGlobalOptions(command)
@@ -112,11 +112,11 @@ export function createContractInferCommand(): Command {
112
112
  .option('--config <path>', 'Path to prisma-next.config.ts')
113
113
  .option('--output <path>', 'Write the inferred PSL contract to the specified path')
114
114
  .action(async (options: ContractInferOptions) => {
115
- const flags = parseGlobalFlags(options);
116
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
115
+ const flags = parseGlobalFlagsOrExit(options);
116
+ const ui = createTerminalUI(flags);
117
117
  const startTime = Date.now();
118
118
 
119
- const result = await executeContractInferCommand(options, ui, startTime);
119
+ const result = await executeContractInferCommand(options, flags, ui, startTime);
120
120
  const exitCode = handleResult(result, flags, ui, (value) => {
121
121
  if (flags.json) {
122
122
  ui.output(JSON.stringify(value, null, 2));
@@ -24,13 +24,13 @@ import {
24
24
  formatMigrationPlanOutput,
25
25
  type MigrationCommandResult,
26
26
  } from '../utils/formatters/migrations';
27
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
27
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
28
28
  import {
29
29
  addMigrationCommandOptions,
30
30
  prepareMigrationContext,
31
31
  } from '../utils/migration-command-scaffold';
32
32
  import { handleResult } from '../utils/result-handler';
33
- import { TerminalUI } from '../utils/terminal-ui';
33
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
34
34
 
35
35
  type DbInitOptions = MigrationCommandOptions;
36
36
 
@@ -80,9 +80,17 @@ function mapDbInitFailure(failure: DbInitFailure): CliStructuredError {
80
80
  }
81
81
 
82
82
  if (failure.code === 'RUNNER_FAILED') {
83
+ const runnerCode =
84
+ typeof failure.meta?.['runnerErrorCode'] === 'string'
85
+ ? failure.meta['runnerErrorCode']
86
+ : undefined;
87
+ const fix =
88
+ runnerCode === 'LEGACY_MARKER_SHAPE'
89
+ ? 'Legacy marker-table shape detected. Drop `prisma_contract.marker` (Postgres) or `_prisma_marker` (SQLite) and re-run `prisma-next db init` to recreate it with the current per-space schema.'
90
+ : 'Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`';
83
91
  return errorRunnerFailed(failure.summary, {
84
92
  why: failure.why ?? 'Migration runner failed',
85
- fix: 'Fix the schema mismatch (db init is additive-only), or drop/reset the database and re-run `prisma-next db init`',
93
+ fix,
86
94
  ...(failure.meta
87
95
  ? { meta: { code: 'RUNNER_FAILED', ...failure.meta } }
88
96
  : { meta: { code: 'RUNNER_FAILED' } }),
@@ -222,10 +230,10 @@ export function createDbInitCommand(): Command {
222
230
  ]);
223
231
  addMigrationCommandOptions(command);
224
232
  command.action(async (options: DbInitOptions) => {
225
- const flags = parseGlobalFlags(options);
233
+ const flags = parseGlobalFlagsOrExit(options);
226
234
  const startTime = Date.now();
227
235
 
228
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
236
+ const ui = createTerminalUI(flags);
229
237
 
230
238
  const result = await executeDbInitCommand(options, flags, ui, startTime);
231
239
 
@@ -6,9 +6,9 @@ import {
6
6
  setCommandExamples,
7
7
  } from '../utils/command-helpers';
8
8
  import { formatIntrospectJson, formatIntrospectOutput } from '../utils/formatters/verify';
9
- import { parseGlobalFlags } from '../utils/global-flags';
9
+ import { parseGlobalFlagsOrExit } from '../utils/global-flags';
10
10
  import { handleResult } from '../utils/result-handler';
11
- import { TerminalUI } from '../utils/terminal-ui';
11
+ import { createTerminalUI } from '../utils/terminal-ui';
12
12
  import {
13
13
  type InspectLiveSchemaOptions,
14
14
  type InspectLiveSchemaResult,
@@ -46,8 +46,8 @@ export function createDbSchemaCommand(): Command {
46
46
  .option('--db <url>', 'Database connection string')
47
47
  .option('--config <path>', 'Path to prisma-next.config.ts')
48
48
  .action(async (options: InspectLiveSchemaOptions) => {
49
- const flags = parseGlobalFlags(options);
50
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
49
+ const flags = parseGlobalFlagsOrExit(options);
50
+ const ui = createTerminalUI(flags);
51
51
  const startTime = Date.now();
52
52
 
53
53
  const result = await inspectLiveSchema(options, flags, ui, startTime, {
@@ -40,10 +40,10 @@ import {
40
40
  formatSignOutput,
41
41
  } from '../utils/formatters/verify';
42
42
  import type { CommonCommandOptions } from '../utils/global-flags';
43
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
43
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
44
44
  import { createProgressAdapter } from '../utils/progress-adapter';
45
45
  import { handleResult } from '../utils/result-handler';
46
- import { TerminalUI } from '../utils/terminal-ui';
46
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
47
47
 
48
48
  interface DbSignOptions extends CommonCommandOptions {
49
49
  readonly db?: string;
@@ -270,7 +270,7 @@ export function createDbSignCommand(): Command {
270
270
  'Contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
271
271
  )
272
272
  .action(async (positionalContract: string | undefined, options: DbSignOptions) => {
273
- const flags = parseGlobalFlags(options);
273
+ const flags = parseGlobalFlagsOrExit(options);
274
274
 
275
275
  if (positionalContract && options.contract) {
276
276
  process.stderr.write(
@@ -280,7 +280,7 @@ export function createDbSignCommand(): Command {
280
280
  return;
281
281
  }
282
282
 
283
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
283
+ const ui = createTerminalUI(flags);
284
284
 
285
285
  const result = await executeDbSignCommand(positionalContract, options, flags, ui);
286
286
 
@@ -33,13 +33,13 @@ import {
33
33
  formatMigrationPlanOutput,
34
34
  type MigrationCommandResult,
35
35
  } from '../utils/formatters/migrations';
36
- import { type GlobalFlags, parseGlobalFlags } from '../utils/global-flags';
36
+ import { type GlobalFlags, parseGlobalFlagsOrExit } from '../utils/global-flags';
37
37
  import {
38
38
  addMigrationCommandOptions,
39
39
  prepareMigrationContext,
40
40
  } from '../utils/migration-command-scaffold';
41
41
  import { handleResult } from '../utils/result-handler';
42
- import { TerminalUI } from '../utils/terminal-ui';
42
+ import { createTerminalUI, type TerminalUI } from '../utils/terminal-ui';
43
43
 
44
44
  interface DbUpdateOptions extends MigrationCommandOptions {
45
45
  readonly to?: string;
@@ -54,9 +54,17 @@ function mapDbUpdateFailure(failure: DbUpdateFailure): CliStructuredError {
54
54
  }
55
55
 
56
56
  if (failure.code === 'RUNNER_FAILED') {
57
+ const runnerCode =
58
+ typeof failure.meta?.['runnerErrorCode'] === 'string'
59
+ ? failure.meta['runnerErrorCode']
60
+ : undefined;
61
+ const fix =
62
+ runnerCode === 'LEGACY_MARKER_SHAPE'
63
+ ? 'Legacy marker-table shape detected. Drop `prisma_contract.marker` (Postgres) or `_prisma_marker` (SQLite) and re-run `prisma-next db init` to recreate it with the current per-space schema.'
64
+ : 'Inspect the reported conflict, reconcile schema drift if needed, then re-run `prisma-next db update`';
57
65
  return errorRunnerFailed(failure.summary, {
58
66
  why: failure.why ?? 'Migration runner failed',
59
- fix: 'Inspect the reported conflict, reconcile schema drift if needed, then re-run `prisma-next db update`',
67
+ fix,
60
68
  ...ifDefined('meta', failure.meta),
61
69
  });
62
70
  }
@@ -238,10 +246,10 @@ export function createDbUpdateCommand(): Command {
238
246
  'Target contract reference (hash, prefix, ref name, migration dir name, <dir>^, or ./path)',
239
247
  );
240
248
  command.action(async (options: DbUpdateOptions) => {
241
- const flags = parseGlobalFlags(options);
249
+ const flags = parseGlobalFlagsOrExit(options);
242
250
  const startTime = Date.now();
243
251
 
244
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
252
+ const ui = createTerminalUI(flags);
245
253
 
246
254
  let result = await executeDbUpdateCommand(options, flags, ui, startTime);
247
255
 
@@ -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
@@ -238,7 +269,7 @@ export function errorInitEmitFailed(options: {
238
269
  }
239
270
 
240
271
  /**
241
- * The project-level agent-skill install (`npx skills add
272
+ * The project-level skills install (`npx skills add
242
273
  * prisma/prisma-next#v<version>`) failed after a successful dependency
243
274
  * install + emit. The project's scaffold remains on disk; the user
244
275
  * can either fix the underlying issue (network, registry, PATH) and
@@ -260,7 +291,7 @@ export function errorInitSkillInstallFailed(options: {
260
291
  'Either:\n' +
261
292
  ` - Re-run \`prisma-next init --no-skill${options.filesWritten.length > 0 ? ' --force' : ''}\` to skip the skill install for this run, or\n` +
262
293
  ` - Fix the underlying issue (network, npm registry, \`npx skills\` on PATH) and install manually:\n ${options.skillInstallCommand}`,
263
- docsUrl: 'https://prisma-next.dev/docs/cli/init#agent-skill',
294
+ docsUrl: 'https://prisma-next.dev/docs/cli/init#skills',
264
295
  meta: {
265
296
  filesWritten: options.filesWritten,
266
297
  skillInstallCommand: options.skillInstallCommand,
@@ -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,8 @@ 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
+ import { fireTelemetryAfterInitConsent } from '../../utils/telemetry';
8
9
  import {
9
10
  INIT_EXIT_EMIT_FAILED,
10
11
  INIT_EXIT_INSTALL_FAILED,
@@ -14,6 +15,7 @@ import {
14
15
  INIT_EXIT_SKILL_INSTALL_FAILED,
15
16
  INIT_EXIT_USER_ABORTED,
16
17
  } from './exit-codes';
18
+ import { defaultSchemaPath } from './templates/code-templates';
17
19
 
18
20
  /**
19
21
  * Commander.js parsed options for `init`. The init-specific options live
@@ -63,7 +65,7 @@ export function createInitCommand(): Command {
63
65
  'prisma-next init --yes --target mongodb --authoring typescript --json',
64
66
  'prisma-next init --yes --force --target postgres --authoring psl # overwrite an existing scaffold',
65
67
  'prisma-next init --no-install # skip pnpm/npm install + emit',
66
- 'prisma-next init --no-skill # skip the agent-skill install (air-gapped / restricted env)',
68
+ 'prisma-next init --no-skill # skip the skills install (air-gapped / restricted env)',
67
69
  ]);
68
70
 
69
71
  return addGlobalOptions(command)
@@ -71,7 +73,7 @@ export function createInitCommand(): Command {
71
73
  .option('--authoring <style>', 'Schema authoring style: psl or typescript')
72
74
  .option(
73
75
  '--schema-path <path>',
74
- 'Where to write the starter schema (default: prisma/contract.prisma)',
76
+ `Where to write the starter schema (default: ${defaultSchemaPath('psl')})`,
75
77
  )
76
78
  .option('--force', 'Overwrite an existing scaffold without prompting')
77
79
  .option(
@@ -91,15 +93,22 @@ export function createInitCommand(): Command {
91
93
  '--no-skill',
92
94
  'Skip Prisma Next skills install (air-gapped CI, restricted registries, etc.)',
93
95
  )
94
- .action(async (options: InitCommandOptions) => {
96
+ .action(async (options: InitCommandOptions, actionCommand: Command) => {
95
97
  const { runInit } = await import('./init');
96
- const flags = parseGlobalFlags(options);
98
+ const flags = parseGlobalFlagsOrExit(options);
97
99
  const canPrompt = deriveCanPrompt({
98
100
  flagsInteractive: flags.interactive,
99
101
  optionInteractive: options.interactive,
100
102
  stdinIsTTY: Boolean(process.stdin.isTTY),
101
103
  });
102
- const exitCode = await runInit(process.cwd(), { options, flags, canPrompt });
104
+ const exitCode = await runInit(process.cwd(), {
105
+ options,
106
+ flags,
107
+ canPrompt,
108
+ afterFirstTelemetryConsent: (inputs) => {
109
+ fireTelemetryAfterInitConsent(actionCommand, { databaseTarget: inputs.target });
110
+ },
111
+ });
103
112
  process.exit(exitCode);
104
113
  });
105
114
  }
@@ -6,14 +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';
10
- import {
11
- DEFAULT_AGENT_SKILL_SOURCES,
12
- formatClaudeSkillInstallCommand,
13
- formatSkillInstallCommand,
14
- LEGACY_SKILL_FILE,
15
- runProjectLevelSkillInstall,
16
- } from './agent-skill-install';
9
+ import { createTerminalUI, type TerminalUI } from '../../utils/terminal-ui';
17
10
  import {
18
11
  detectPackageManager,
19
12
  formatAddArgs,
@@ -56,9 +49,16 @@ import {
56
49
  } from './output';
57
50
  import { type ProbeOutcome, type ProbeOverrides, probeServerVersion } from './probe-db';
58
51
  import { findStaleArtefacts, removeDependency } from './reinit-cleanup';
52
+ import {
53
+ DEFAULT_SKILL_SOURCES,
54
+ formatSkillInstallCommand,
55
+ LEGACY_SKILL_FILE,
56
+ runProjectLevelSkillInstall,
57
+ } from './skill-install';
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 {
@@ -81,11 +81,11 @@ interface InstallReport {
81
81
  readonly warnings: readonly string[];
82
82
  /**
83
83
  * The package manager that actually ran. Equal to the detected `pm`
84
- * on the common path; differs when the FR7.2 pnpm → npm fallback
85
- * fired, in which case it's `'npm'`. Threaded into the agent-skill
86
- * install so the runner stays consistent with the install we just
87
- * ran — re-trying through `pnpm dlx` when `pnpm install` just failed
88
- * for workspace/catalog reasons would fail again for the same reason.
84
+ * on the common path; differs when the pnpm → npm fallback fired, in
85
+ * which case it's `'npm'`. Threaded into the skills install so the
86
+ * runner stays consistent with the install we just ran — re-trying
87
+ * through `pnpm dlx` when `pnpm install` just failed for
88
+ * workspace/catalog reasons would fail again for the same reason.
89
89
  */
90
90
  readonly effectivePm: PackageManager;
91
91
  }
@@ -95,7 +95,7 @@ interface InstallReport {
95
95
  * structured CLI errors raised at every phase (input resolution, install,
96
96
  * emit) and renders them via the same UI surface as success output
97
97
  * (`--json` to stdout, human to stderr). Exit codes follow the documented
98
- * stable set in `./exit-codes.ts` (FR1.6) and the
98
+ * stable set in `./exit-codes.ts` and the
99
99
  * [Style Guide § Exit Codes](../../../../../../../docs/CLI%20Style%20Guide.md#exit-codes).
100
100
  *
101
101
  * Layered for testability: the action handler in `./index.ts` is
@@ -113,6 +113,11 @@ export async function runInit(
113
113
  * mode) — see [Style Guide § Interactivity](../../../../../../../docs/CLI%20Style%20Guide.md#interactivity).
114
114
  */
115
115
  readonly canPrompt: boolean;
116
+ /**
117
+ * Called once after the first affirmative telemetry consent is persisted.
118
+ * Failures are swallowed by the caller; init success must not depend on telemetry.
119
+ */
120
+ readonly afterFirstTelemetryConsent?: (inputs: ResolvedInitInputs) => void | Promise<void>;
116
121
  /**
117
122
  * FR8.3 — test-only seam for the optional database version probe.
118
123
  * Production callers omit this; tests inject stub `probePostgres` /
@@ -124,8 +129,8 @@ export async function runInit(
124
129
  readonly probeOverrides?: ProbeOverrides;
125
130
  },
126
131
  ): Promise<number> {
127
- const { options, flags, canPrompt, probeOverrides } = runOptions;
128
- const ui = new TerminalUI({ color: flags.color, interactive: flags.interactive });
132
+ const { options, flags, canPrompt, probeOverrides, afterFirstTelemetryConsent } = runOptions;
133
+ const ui = createTerminalUI(flags);
129
134
  const warnings: string[] = [];
130
135
  const filesWritten: string[] = [];
131
136
  const filesDeleted: string[] = [];
@@ -144,6 +149,14 @@ export async function runInit(
144
149
  throw error;
145
150
  }
146
151
 
152
+ if (inputs.enableTelemetry === true && afterFirstTelemetryConsent !== undefined) {
153
+ try {
154
+ await afterFirstTelemetryConsent(inputs);
155
+ } catch {
156
+ // telemetry is best-effort and must never affect init
157
+ }
158
+ }
159
+
147
160
  const pm = await detectPackageManager(baseDir);
148
161
  const pkgRun = formatRunCommand(pm, 'prisma-next', '').trimEnd();
149
162
 
@@ -336,6 +349,26 @@ export async function runInit(
336
349
  }
337
350
  }
338
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
+
339
372
  // -----------------------------------------------------------------
340
373
  // Write phase — every input has been parsed and every merged file is
341
374
  // staged. From here on, failures are only possible at the
@@ -445,25 +478,20 @@ export async function runInit(
445
478
  // framework relies on. A project-level failure is fatal
446
479
  // (`INIT_EXIT_SKILL_INSTALL_FAILED`).
447
480
  //
448
- // Runs after install + emit because (1) `node_modules` is in place,
449
- // so any consumers downstream are coherent, and (2) the user has
450
- // seen the scaffolding succeed before this network-bound step
451
- // potentially fails. We skip the install when the user passed
452
- // `--no-install` for the same reason no `node_modules` means the
453
- // workspace isn't ready to consume the skill yet anyway.
454
- const manualProjectSkillCommands = DEFAULT_AGENT_SKILL_SOURCES.flatMap((source) => [
455
- formatSkillInstallCommand(install.effectivePm, source),
456
- formatClaudeSkillInstallCommand(install.effectivePm, source),
457
- ]);
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
+ );
458
490
  const manualProjectSkillSummary = manualProjectSkillCommands.map((c) => `\`${c}\``).join(' && ');
459
491
  let skillRegistered = false;
460
492
  if (!inputs.installProjectSkill) {
461
493
  warnings.push(
462
- `Skipped Prisma Next agent-skill install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`,
463
- );
464
- } else if (install.skipped) {
465
- warnings.push(
466
- `Skipped Prisma Next agent-skill install because --no-install was passed. After you run install manually, install the skills with: ${manualProjectSkillSummary}`,
494
+ `Skipped Prisma Next skills install (--no-skill). To install the skills later, run: ${manualProjectSkillSummary}`,
467
495
  );
468
496
  } else {
469
497
  const spinner = ui.spinner();
@@ -588,6 +616,7 @@ export function exitCodeForError(error: { readonly code: string }): number {
588
616
  case '5010': // invalid manifest (malformed package.json) — precondition
589
617
  case '5011': // invalid tsconfig (unparseable JSONC) — precondition
590
618
  case '5012': // probe failed under --strict-probe — precondition
619
+ case '5014': // --authoring / --schema-path extension mismatch — precondition
591
620
  return INIT_EXIT_PRECONDITION;
592
621
  case '5006': // user aborted interactive prompt
593
622
  return INIT_EXIT_USER_ABORTED;
@@ -597,7 +626,7 @@ export function exitCodeForError(error: { readonly code: string }): number {
597
626
  return INIT_EXIT_EMIT_FAILED;
598
627
  case '5009': // invalid output document — internal bug in prisma-next
599
628
  return INIT_EXIT_INTERNAL_ERROR;
600
- case '5013': // agent-skill install failed
629
+ case '5013': // skill install failed
601
630
  return INIT_EXIT_SKILL_INSTALL_FAILED;
602
631
  default:
603
632
  // Any unexpected code is treated as an internal bug rather than