@rawsql-ts/ztd-cli 0.16.0 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +33 -20
  2. package/dist/commands/init.d.ts +13 -0
  3. package/dist/commands/init.js +372 -127
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/commands/lint.d.ts +4 -4
  6. package/dist/commands/lint.js +60 -40
  7. package/dist/commands/lint.js.map +1 -1
  8. package/dist/commands/ztdConfig.d.ts +2 -2
  9. package/dist/commands/ztdConfig.js +26 -12
  10. package/dist/commands/ztdConfig.js.map +1 -1
  11. package/dist/utils/optionalDependencies.d.ts +35 -0
  12. package/dist/utils/optionalDependencies.js +96 -0
  13. package/dist/utils/optionalDependencies.js.map +1 -0
  14. package/package.json +16 -9
  15. package/templates/AGENTS.md +36 -309
  16. package/templates/README.md +12 -215
  17. package/templates/dist/ztd-cli/templates/src/db/sql-client.ts +24 -0
  18. package/templates/src/AGENTS.md +26 -0
  19. package/templates/src/catalog/AGENTS.md +37 -0
  20. package/templates/src/catalog/runtime/AGENTS.md +75 -0
  21. package/templates/src/catalog/runtime/_coercions.ts +1 -0
  22. package/templates/src/catalog/runtime/_smoke.runtime.ts +21 -0
  23. package/templates/src/catalog/specs/AGENTS.md +48 -0
  24. package/templates/src/catalog/specs/_smoke.spec.arktype.ts +21 -0
  25. package/templates/src/catalog/specs/_smoke.spec.zod.ts +20 -0
  26. package/templates/src/db/sql-client.ts +5 -5
  27. package/templates/src/jobs/AGENTS.md +26 -0
  28. package/templates/src/jobs/README.md +3 -0
  29. package/templates/src/repositories/AGENTS.md +118 -0
  30. package/templates/src/repositories/tables/AGENTS.md +94 -0
  31. package/templates/src/repositories/tables/README.md +3 -0
  32. package/templates/src/repositories/views/AGENTS.md +25 -0
  33. package/templates/src/repositories/views/README.md +3 -0
  34. package/templates/src/sql/AGENTS.md +77 -0
  35. package/templates/src/sql/README.md +6 -0
  36. package/templates/tests/AGENTS.md +43 -150
  37. package/templates/tests/generated/AGENTS.md +16 -0
  38. package/templates/tests/smoke.test.ts +5 -0
  39. package/templates/tests/smoke.validation.test.ts +34 -0
  40. package/templates/tests/support/AGENTS.md +26 -0
  41. package/templates/tests/support/global-setup.ts +8 -23
  42. package/templates/tests/support/testkit-client.ts +13 -741
  43. package/templates/tsconfig.json +8 -1
  44. package/templates/ztd/AGENTS.md +11 -67
  45. package/templates/ztd/README.md +4 -13
  46. package/templates/ztd/ddl/AGENTS.md +34 -0
  47. package/templates/ztd/ddl/demo.sql +74 -0
  48. package/templates/src/repositories/user-accounts.ts +0 -179
  49. package/templates/tests/user-profiles.test.ts +0 -161
  50. package/templates/tests/writer-constraints.test.ts +0 -32
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createConsolePrompter = createConsolePrompter;
7
7
  exports.runInitCommand = runInitCommand;
8
+ exports.normalizeSchemaName = normalizeSchemaName;
9
+ exports.sanitizeSchemaFileName = sanitizeSchemaFileName;
8
10
  exports.registerInitCommand = registerInitCommand;
9
11
  const node_child_process_1 = require("node:child_process");
10
12
  const node_fs_1 = require("node:fs");
@@ -15,7 +17,6 @@ const agents_1 = require("../utils/agents");
15
17
  const ztdProjectConfig_1 = require("../utils/ztdProjectConfig");
16
18
  const ztdConfig_1 = require("./ztdConfig");
17
19
  const pull_1 = require("./pull");
18
- const options_1 = require("./options");
19
20
  /**
20
21
  * Create a readline-backed prompter that reads from stdin/stdout.
21
22
  */
@@ -28,6 +29,11 @@ function createConsolePrompter() {
28
29
  async function requestLine(question) {
29
30
  return (await rl.question(question)).trim();
30
31
  }
32
+ async function requestLineWithDefault(question, defaultValue, example) {
33
+ const prompt = `${question}${example ? ` (${example})` : ''} [default: ${defaultValue}]: `;
34
+ const answer = await requestLine(prompt);
35
+ return answer.length > 0 ? answer : defaultValue;
36
+ }
31
37
  return {
32
38
  async selectChoice(question, choices) {
33
39
  while (true) {
@@ -52,6 +58,9 @@ function createConsolePrompter() {
52
58
  console.log('This value cannot be empty.');
53
59
  }
54
60
  },
61
+ async promptInputWithDefault(question, defaultValue, example) {
62
+ return requestLineWithDefault(question, defaultValue, example);
63
+ },
55
64
  async confirm(question) {
56
65
  while (true) {
57
66
  const answer = (await requestLine(`${question} (y/N): `)).toLowerCase();
@@ -72,37 +81,64 @@ function createConsolePrompter() {
72
81
  }
73
82
  };
74
83
  }
75
- const SAMPLE_SCHEMA = `
76
- CREATE TABLE public.user_account (
77
- user_account_id bigserial PRIMARY KEY,
78
- username text NOT NULL,
79
- email text NOT NULL UNIQUE,
80
- display_name text NOT NULL,
81
- created_at timestamptz NOT NULL DEFAULT now(),
82
- updated_at timestamptz NOT NULL DEFAULT now()
83
- );
84
-
85
- CREATE TABLE public.user_profile (
86
- profile_id bigserial PRIMARY KEY,
87
- user_account_id bigint NOT NULL REFERENCES public.user_account(user_account_id),
88
- bio text,
89
- website text,
90
- verified boolean NOT NULL DEFAULT false
91
- );
92
- `;
84
+ const MANDATORY_TESTKIT_DEPENDENCIES = {
85
+ '@rawsql-ts/adapter-node-pg': '^0.15.1',
86
+ '@rawsql-ts/testkit-postgres': '^0.15.1'
87
+ };
88
+ const SQL_CONTRACT_DEPENDENCY = {
89
+ '@rawsql-ts/sql-contract': '^0.1.0'
90
+ };
91
+ const ZOD_DEPENDENCY = {
92
+ zod: '^4.3.6'
93
+ };
94
+ const ARKTYPE_DEPENDENCY = {
95
+ arktype: '^2.1.29'
96
+ };
97
+ async function gatherOptionalFeatures(prompter, _dependencies) {
98
+ const validatorChoice = await prompter.selectChoice('Runtime DTO validation is required for ZTD tests. Which validator backend should we install?', ['Zod (zod, recommended)', 'ArkType (arktype)']);
99
+ const validator = validatorChoice === 0 ? 'zod' : 'arktype';
100
+ return { validator };
101
+ }
93
102
  const README_TEMPLATE = 'README.md';
94
- const TESTS_CONFIG_TEMPLATE = 'tests/ztd-layout.generated.ts';
95
103
  const TESTS_AGENTS_TEMPLATE = 'tests/AGENTS.md';
104
+ const TESTS_SUPPORT_AGENTS_TEMPLATE = 'tests/support/AGENTS.md';
105
+ const TESTS_GENERATED_AGENTS_TEMPLATE = 'tests/generated/AGENTS.md';
106
+ const SMOKE_SPEC_ZOD_TEMPLATE = 'src/catalog/specs/_smoke.spec.zod.ts';
107
+ const SMOKE_SPEC_ARKTYPE_TEMPLATE = 'src/catalog/specs/_smoke.spec.arktype.ts';
108
+ const SMOKE_COERCIONS_TEMPLATE = 'src/catalog/runtime/_coercions.ts';
109
+ const SMOKE_RUNTIME_TEMPLATE = 'src/catalog/runtime/_smoke.runtime.ts';
110
+ const SMOKE_VALIDATION_TEST_TEMPLATE = 'tests/smoke.validation.test.ts';
111
+ const TESTS_SMOKE_TEMPLATE = 'tests/smoke.test.ts';
96
112
  const TESTKIT_CLIENT_TEMPLATE = 'tests/support/testkit-client.ts';
97
113
  const GLOBAL_SETUP_TEMPLATE = 'tests/support/global-setup.ts';
98
114
  const VITEST_CONFIG_TEMPLATE = 'vitest.config.ts';
115
+ const TSCONFIG_TEMPLATE = 'tsconfig.json';
99
116
  const SQL_CLIENT_TEMPLATE = 'src/db/sql-client.ts';
100
- const NEXT_STEPS = [
101
- ' 1. Review the schema files under ztd/ddl/<schema>.sql',
102
- ' 2. Inspect tests/generated/ztd-layout.generated.ts for the SQL layout',
103
- ' 3. Run npx ztd ztd-config',
104
- ' 4. Run ZTD tests with the testkit-postgres stack'
105
- ];
117
+ const SQL_README_TEMPLATE = 'src/sql/README.md';
118
+ const VIEWS_REPO_README_TEMPLATE = 'src/repositories/views/README.md';
119
+ const TABLES_REPO_README_TEMPLATE = 'src/repositories/tables/README.md';
120
+ const JOBS_README_TEMPLATE = 'src/jobs/README.md';
121
+ const SRC_AGENTS_TEMPLATE = 'src/AGENTS.md';
122
+ const SRC_CATALOG_AGENTS_TEMPLATE = 'src/catalog/AGENTS.md';
123
+ const SRC_CATALOG_RUNTIME_AGENTS_TEMPLATE = 'src/catalog/runtime/AGENTS.md';
124
+ const SRC_CATALOG_SPECS_AGENTS_TEMPLATE = 'src/catalog/specs/AGENTS.md';
125
+ const SRC_SQL_AGENTS_TEMPLATE = 'src/sql/AGENTS.md';
126
+ const SRC_REPOS_AGENTS_TEMPLATE = 'src/repositories/AGENTS.md';
127
+ const VIEWS_REPO_AGENTS_TEMPLATE = 'src/repositories/views/AGENTS.md';
128
+ const TABLES_REPO_AGENTS_TEMPLATE = 'src/repositories/tables/AGENTS.md';
129
+ const JOBS_AGENTS_TEMPLATE = 'src/jobs/AGENTS.md';
130
+ const ZTD_AGENTS_TEMPLATE = 'ztd/AGENTS.md';
131
+ const ZTD_README_TEMPLATE = 'ztd/README.md';
132
+ const ZTD_DDL_AGENTS_TEMPLATE = 'ztd/ddl/AGENTS.md';
133
+ const ZTD_DDL_DEMO_TEMPLATE = 'ztd/ddl/demo.sql';
134
+ const EMPTY_SCHEMA_COMMENT = (schemaName) => [
135
+ `-- DDL for schema "${schemaName}".`,
136
+ '-- Add CREATE TABLE statements here.',
137
+ ''
138
+ ].join('\n');
139
+ const DEMO_SCHEMA_TEMPLATE = (_schemaName) => {
140
+ return loadTemplate(ZTD_DDL_DEMO_TEMPLATE);
141
+ };
106
142
  const AGENTS_FILE_CANDIDATES = ['AGENTS.md', 'AGENTS_ztd.md'];
107
143
  const APP_INTERFACE_SECTION_MARKER = '## Application Interface Guidance';
108
144
  const APP_INTERFACE_SECTION = `---
@@ -197,26 +233,67 @@ const DEFAULT_DEPENDENCIES = {
197
233
  * Run the interactive `ztd init` workflow and return the resulting summary.
198
234
  */
199
235
  async function runInitCommand(prompter, options) {
200
- var _a, _b;
236
+ var _a, _b, _c, _d;
201
237
  const rootDir = (_a = options === null || options === void 0 ? void 0 : options.rootDir) !== null && _a !== void 0 ? _a : process.cwd();
202
238
  const dependencies = {
203
239
  ...DEFAULT_DEPENDENCIES,
204
240
  ...((_b = options === null || options === void 0 ? void 0 : options.dependencies) !== null && _b !== void 0 ? _b : {})
205
241
  };
206
- const schemaFileName = `${ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddl.defaultSchema}.sql`;
242
+ const overwritePolicy = {
243
+ force: (_c = options === null || options === void 0 ? void 0 : options.forceOverwrite) !== null && _c !== void 0 ? _c : false,
244
+ nonInteractive: (_d = options === null || options === void 0 ? void 0 : options.nonInteractive) !== null && _d !== void 0 ? _d : false
245
+ };
246
+ if (options === null || options === void 0 ? void 0 : options.withAppInterface) {
247
+ // Provide the documentation-only path before triggering any scaffolding work.
248
+ const summary = await appendAppInterfaceGuidance(rootDir, dependencies);
249
+ dependencies.log(`Appended application interface guidance to ${summary.relativePath}.`);
250
+ return {
251
+ summary: `App interface guidance appended to ${summary.relativePath}.`,
252
+ files: [summary]
253
+ };
254
+ }
255
+ // Ask how the user prefers to populate the initial schema.
256
+ const workflowChoice = await prompter.selectChoice('How do you want to start your database workflow?', [
257
+ 'Pull schema from Postgres (pg_dump)',
258
+ 'Create empty scaffold (I will write DDL)',
259
+ 'Create scaffold with demo DDL (no app code)'
260
+ ]);
261
+ const workflow = workflowChoice === 0 ? 'pg_dump' : workflowChoice === 1 ? 'empty' : 'demo';
262
+ const schemaName = normalizeSchemaName(ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddl.defaultSchema);
263
+ const schemaFileName = `${sanitizeSchemaFileName(schemaName)}.sql`;
207
264
  const absolutePaths = {
208
265
  schema: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddlDir, schemaFileName),
209
266
  config: node_path_1.default.join(rootDir, 'ztd.config.json'),
210
- ztdConfig: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'generated', 'ztd-row-map.generated.ts'),
211
- testsConfig: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'generated', 'ztd-layout.generated.ts'),
267
+ smokeSpec: node_path_1.default.join(rootDir, 'src', 'catalog', 'specs', '_smoke.spec.ts'),
268
+ smokeCoercions: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', '_coercions.ts'),
269
+ smokeRuntime: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', '_smoke.runtime.ts'),
270
+ smokeValidationTest: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'smoke.validation.test.ts'),
212
271
  testsAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'AGENTS.md'),
272
+ testsSupportAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'AGENTS.md'),
273
+ testsGeneratedAgents: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'generated', 'AGENTS.md'),
274
+ testsSmoke: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'smoke.test.ts'),
213
275
  readme: node_path_1.default.join(rootDir, 'README.md'),
276
+ sqlReadme: node_path_1.default.join(rootDir, 'src', 'sql', 'README.md'),
277
+ viewsRepoReadme: node_path_1.default.join(rootDir, 'src', 'repositories', 'views', 'README.md'),
278
+ tablesRepoReadme: node_path_1.default.join(rootDir, 'src', 'repositories', 'tables', 'README.md'),
279
+ jobsReadme: node_path_1.default.join(rootDir, 'src', 'jobs', 'README.md'),
280
+ srcAgents: node_path_1.default.join(rootDir, 'src', 'AGENTS.md'),
281
+ srcCatalogAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'AGENTS.md'),
282
+ srcCatalogRuntimeAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'runtime', 'AGENTS.md'),
283
+ srcCatalogSpecsAgents: node_path_1.default.join(rootDir, 'src', 'catalog', 'specs', 'AGENTS.md'),
284
+ srcSqlAgents: node_path_1.default.join(rootDir, 'src', 'sql', 'AGENTS.md'),
285
+ srcReposAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'AGENTS.md'),
286
+ viewsRepoAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'views', 'AGENTS.md'),
287
+ tablesRepoAgents: node_path_1.default.join(rootDir, 'src', 'repositories', 'tables', 'AGENTS.md'),
288
+ jobsAgents: node_path_1.default.join(rootDir, 'src', 'jobs', 'AGENTS.md'),
214
289
  sqlClient: node_path_1.default.join(rootDir, 'src', 'db', 'sql-client.ts'),
215
290
  testkitClient: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'testkit-client.ts'),
216
291
  globalSetup: node_path_1.default.join(rootDir, ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.testsDir, 'support', 'global-setup.ts'),
217
292
  vitestConfig: node_path_1.default.join(rootDir, 'vitest.config.ts'),
293
+ tsconfig: node_path_1.default.join(rootDir, 'tsconfig.json'),
218
294
  ztdDocsAgent: node_path_1.default.join(rootDir, 'ztd', 'AGENTS.md'),
219
295
  ztdDocsReadme: node_path_1.default.join(rootDir, 'ztd', 'README.md'),
296
+ ztdDdlAgents: node_path_1.default.join(rootDir, 'ztd', 'ddl', 'AGENTS.md'),
220
297
  agents: node_path_1.default.join(rootDir, 'AGENTS.md'),
221
298
  gitignore: node_path_1.default.join(rootDir, '.gitignore'),
222
299
  editorconfig: node_path_1.default.join(rootDir, '.editorconfig'),
@@ -226,111 +303,170 @@ async function runInitCommand(prompter, options) {
226
303
  };
227
304
  const relativePath = (key) => node_path_1.default.relative(rootDir, absolutePaths[key]).replace(/\\/g, '/') || absolutePaths[key];
228
305
  const summaries = {};
229
- if (options === null || options === void 0 ? void 0 : options.withAppInterface) {
230
- // Provide the documentation-only path before triggering any scaffolding work.
231
- const summary = await appendAppInterfaceGuidance(rootDir, dependencies);
232
- dependencies.log(`Appended application interface guidance to ${summary.relativePath}.`);
233
- return {
234
- summary: `App interface guidance appended to ${summary.relativePath}.`,
235
- files: [summary]
236
- };
237
- }
238
306
  // Ask how the user prefers to populate the initial schema.
239
- const workflow = await prompter.selectChoice('How do you want to start your database workflow?', ['Pull schema from Postgres (DDL-first)', 'Write DDL manually']);
240
- if (workflow === 0) {
307
+ if (workflow === 'pg_dump') {
241
308
  // Database-first path: pull the schema before writing any DDL files.
242
309
  if (!dependencies.checkPgDump()) {
243
310
  throw new Error('Unable to find pg_dump. Install Postgres or set PG_DUMP_PATH before running ztd init.');
244
311
  }
245
312
  const connectionString = await prompter.promptInput('Enter the Postgres connection string for your database', 'postgres://user:pass@host:5432/db');
246
- const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, async () => {
313
+ const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
247
314
  dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
248
315
  await dependencies.runPullSchema({
249
316
  url: connectionString,
250
- out: node_path_1.default.dirname(absolutePaths.schema)
317
+ out: node_path_1.default.dirname(absolutePaths.schema),
318
+ schemas: [schemaName]
251
319
  });
252
320
  });
253
321
  summaries.schema = schemaSummary;
254
322
  }
255
- else {
323
+ else if (workflow === 'empty') {
256
324
  // Manual path: seed the DDL directory with a starter schema so ztd-config can run.
257
- const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, async () => {
325
+ const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
326
+ dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
327
+ dependencies.writeFile(absolutePaths.schema, EMPTY_SCHEMA_COMMENT(schemaName));
328
+ });
329
+ summaries.schema = schemaSummary;
330
+ }
331
+ else {
332
+ const schemaSummary = await writeFileWithConsent(absolutePaths.schema, relativePath('schema'), dependencies, prompter, overwritePolicy, async () => {
258
333
  dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.schema));
259
- dependencies.writeFile(absolutePaths.schema, SAMPLE_SCHEMA);
334
+ dependencies.writeFile(absolutePaths.schema, DEMO_SCHEMA_TEMPLATE(schemaName));
260
335
  });
261
336
  summaries.schema = schemaSummary;
262
337
  }
263
338
  // Seed the ztd.config.json defaults so downstream tooling knows where ddl/tests live.
264
- const configSummary = await writeFileWithConsent(absolutePaths.config, relativePath('config'), dependencies, prompter, () => {
265
- (0, ztdProjectConfig_1.writeZtdProjectConfig)(rootDir);
339
+ const configSummary = await writeFileWithConsent(absolutePaths.config, relativePath('config'), dependencies, prompter, overwritePolicy, () => {
340
+ (0, ztdProjectConfig_1.writeZtdProjectConfig)(rootDir, {
341
+ ddl: {
342
+ defaultSchema: schemaName,
343
+ searchPath: [schemaName]
344
+ }
345
+ });
266
346
  });
267
347
  summaries.config = configSummary;
268
- const projectConfig = (0, ztdProjectConfig_1.loadZtdProjectConfig)(rootDir);
269
- const ztdConfigTarget = await confirmOverwriteIfExists(absolutePaths.ztdConfig, relativePath('ztdConfig'), dependencies, prompter);
270
- if (ztdConfigTarget.write) {
271
- // Regenerate tests/generated/ztd-row-map.generated.ts so TestRowMap reflects the DDL snapshot.
272
- dependencies.ensureDirectory(node_path_1.default.dirname(absolutePaths.ztdConfig));
273
- await dependencies.runGenerateZtdConfig({
274
- directories: [node_path_1.default.resolve(node_path_1.default.dirname(absolutePaths.schema))],
275
- extensions: options_1.DEFAULT_EXTENSIONS,
276
- out: absolutePaths.ztdConfig,
277
- defaultSchema: projectConfig.ddl.defaultSchema,
278
- searchPath: projectConfig.ddl.searchPath,
279
- ddlLint: projectConfig.ddlLint
280
- });
281
- }
282
- else {
283
- dependencies.log('Skipping ZTD config generation; existing tests/generated/ztd-row-map.generated.ts preserved.');
284
- }
285
- summaries.ztdConfig = {
286
- relativePath: relativePath('ztdConfig'),
287
- outcome: ztdConfigTarget.existed
288
- ? ztdConfigTarget.write
289
- ? 'overwritten'
290
- : 'unchanged'
291
- : 'created'
292
- };
348
+ const optionalFeatures = await gatherOptionalFeatures(prompter, dependencies);
293
349
  // Emit supporting documentation that describes the workflow for contributors.
294
- const readmeSummary = await writeTemplateFile(rootDir, absolutePaths.readme, relativePath('readme'), README_TEMPLATE, dependencies, prompter, true);
350
+ const readmeSummary = await writeTemplateFile(rootDir, absolutePaths.readme, relativePath('readme'), README_TEMPLATE, dependencies, prompter, overwritePolicy, true);
295
351
  if (readmeSummary) {
296
352
  summaries.readme = readmeSummary;
297
353
  }
298
- if (options === null || options === void 0 ? void 0 : options.withSqlClient) {
299
- const sqlClientSummary = writeOptionalTemplateFile(absolutePaths.sqlClient, relativePath('sqlClient'), SQL_CLIENT_TEMPLATE, dependencies);
300
- if (sqlClientSummary) {
301
- summaries.sqlClient = sqlClientSummary;
302
- }
354
+ const ztdDocsAgentSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsAgent, relativePath('ztdDocsAgent'), ZTD_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
355
+ if (ztdDocsAgentSummary) {
356
+ summaries.ztdDocsAgent = ztdDocsAgentSummary;
357
+ }
358
+ const ztdDocsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsReadme, relativePath('ztdDocsReadme'), ZTD_README_TEMPLATE, dependencies, prompter, overwritePolicy);
359
+ if (ztdDocsReadmeSummary) {
360
+ summaries.ztdDocsReadme = ztdDocsReadmeSummary;
361
+ }
362
+ const ztdDdlAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDdlAgents, relativePath('ztdDdlAgents'), ZTD_DDL_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
363
+ if (ztdDdlAgentsSummary) {
364
+ summaries.ztdDdlAgents = ztdDdlAgentsSummary;
365
+ }
366
+ const srcAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcAgents, relativePath('srcAgents'), SRC_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
367
+ if (srcAgentsSummary) {
368
+ summaries.srcAgents = srcAgentsSummary;
369
+ }
370
+ const srcCatalogAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogAgents, relativePath('srcCatalogAgents'), SRC_CATALOG_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
371
+ if (srcCatalogAgentsSummary) {
372
+ summaries.srcCatalogAgents = srcCatalogAgentsSummary;
373
+ }
374
+ const srcCatalogRuntimeAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogRuntimeAgents, relativePath('srcCatalogRuntimeAgents'), SRC_CATALOG_RUNTIME_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
375
+ if (srcCatalogRuntimeAgentsSummary) {
376
+ summaries.srcCatalogRuntimeAgents = srcCatalogRuntimeAgentsSummary;
377
+ }
378
+ const srcCatalogSpecsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcCatalogSpecsAgents, relativePath('srcCatalogSpecsAgents'), SRC_CATALOG_SPECS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
379
+ if (srcCatalogSpecsAgentsSummary) {
380
+ summaries.srcCatalogSpecsAgents = srcCatalogSpecsAgentsSummary;
381
+ }
382
+ const srcSqlAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcSqlAgents, relativePath('srcSqlAgents'), SRC_SQL_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
383
+ if (srcSqlAgentsSummary) {
384
+ summaries.srcSqlAgents = srcSqlAgentsSummary;
385
+ }
386
+ const srcReposAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.srcReposAgents, relativePath('srcReposAgents'), SRC_REPOS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
387
+ if (srcReposAgentsSummary) {
388
+ summaries.srcReposAgents = srcReposAgentsSummary;
389
+ }
390
+ const viewsRepoAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.viewsRepoAgents, relativePath('viewsRepoAgents'), VIEWS_REPO_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
391
+ if (viewsRepoAgentsSummary) {
392
+ summaries.viewsRepoAgents = viewsRepoAgentsSummary;
393
+ }
394
+ const tablesRepoAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.tablesRepoAgents, relativePath('tablesRepoAgents'), TABLES_REPO_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
395
+ if (tablesRepoAgentsSummary) {
396
+ summaries.tablesRepoAgents = tablesRepoAgentsSummary;
397
+ }
398
+ const jobsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.jobsAgents, relativePath('jobsAgents'), JOBS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
399
+ if (jobsAgentsSummary) {
400
+ summaries.jobsAgents = jobsAgentsSummary;
401
+ }
402
+ const sqlReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.sqlReadme, relativePath('sqlReadme'), SQL_README_TEMPLATE, dependencies, prompter, overwritePolicy);
403
+ if (sqlReadmeSummary) {
404
+ summaries.sqlReadme = sqlReadmeSummary;
405
+ }
406
+ const viewsRepoReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.viewsRepoReadme, relativePath('viewsRepoReadme'), VIEWS_REPO_README_TEMPLATE, dependencies, prompter, overwritePolicy);
407
+ if (viewsRepoReadmeSummary) {
408
+ summaries.viewsRepoReadme = viewsRepoReadmeSummary;
409
+ }
410
+ const tablesRepoReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.tablesRepoReadme, relativePath('tablesRepoReadme'), TABLES_REPO_README_TEMPLATE, dependencies, prompter, overwritePolicy);
411
+ if (tablesRepoReadmeSummary) {
412
+ summaries.tablesRepoReadme = tablesRepoReadmeSummary;
303
413
  }
304
- const testkitSummary = await writeTemplateFile(rootDir, absolutePaths.testkitClient, relativePath('testkitClient'), TESTKIT_CLIENT_TEMPLATE, dependencies, prompter);
414
+ const jobsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.jobsReadme, relativePath('jobsReadme'), JOBS_README_TEMPLATE, dependencies, prompter, overwritePolicy);
415
+ if (jobsReadmeSummary) {
416
+ summaries.jobsReadme = jobsReadmeSummary;
417
+ }
418
+ const smokeSpecTemplate = optionalFeatures.validator === 'zod' ? SMOKE_SPEC_ZOD_TEMPLATE : SMOKE_SPEC_ARKTYPE_TEMPLATE;
419
+ const smokeSpecSummary = await writeTemplateFile(rootDir, absolutePaths.smokeSpec, relativePath('smokeSpec'), smokeSpecTemplate, dependencies, prompter, overwritePolicy);
420
+ if (smokeSpecSummary) {
421
+ summaries.smokeSpec = smokeSpecSummary;
422
+ }
423
+ const smokeCoercionsSummary = await writeTemplateFile(rootDir, absolutePaths.smokeCoercions, relativePath('smokeCoercions'), SMOKE_COERCIONS_TEMPLATE, dependencies, prompter, overwritePolicy);
424
+ if (smokeCoercionsSummary) {
425
+ summaries.smokeCoercions = smokeCoercionsSummary;
426
+ }
427
+ const smokeRuntimeSummary = await writeTemplateFile(rootDir, absolutePaths.smokeRuntime, relativePath('smokeRuntime'), SMOKE_RUNTIME_TEMPLATE, dependencies, prompter, overwritePolicy);
428
+ if (smokeRuntimeSummary) {
429
+ summaries.smokeRuntime = smokeRuntimeSummary;
430
+ }
431
+ const smokeValidationTestSummary = await writeTemplateFile(rootDir, absolutePaths.smokeValidationTest, relativePath('smokeValidationTest'), SMOKE_VALIDATION_TEST_TEMPLATE, dependencies, prompter, overwritePolicy);
432
+ if (smokeValidationTestSummary) {
433
+ summaries.smokeValidationTest = smokeValidationTestSummary;
434
+ }
435
+ const sqlClientSummary = writeOptionalTemplateFile(absolutePaths.sqlClient, relativePath('sqlClient'), SQL_CLIENT_TEMPLATE, dependencies);
436
+ if (sqlClientSummary) {
437
+ summaries.sqlClient = sqlClientSummary;
438
+ }
439
+ const testsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsAgents, relativePath('testsAgents'), TESTS_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
440
+ if (testsAgentsSummary) {
441
+ summaries.testsAgents = testsAgentsSummary;
442
+ }
443
+ const testsSupportAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsSupportAgents, relativePath('testsSupportAgents'), TESTS_SUPPORT_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
444
+ if (testsSupportAgentsSummary) {
445
+ summaries.testsSupportAgents = testsSupportAgentsSummary;
446
+ }
447
+ const testsGeneratedAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsGeneratedAgents, relativePath('testsGeneratedAgents'), TESTS_GENERATED_AGENTS_TEMPLATE, dependencies, prompter, overwritePolicy);
448
+ if (testsGeneratedAgentsSummary) {
449
+ summaries.testsGeneratedAgents = testsGeneratedAgentsSummary;
450
+ }
451
+ const testsSmokeSummary = await writeTemplateFile(rootDir, absolutePaths.testsSmoke, relativePath('testsSmoke'), TESTS_SMOKE_TEMPLATE, dependencies, prompter, overwritePolicy);
452
+ if (testsSmokeSummary) {
453
+ summaries.testsSmoke = testsSmokeSummary;
454
+ }
455
+ const testkitSummary = await writeTemplateFile(rootDir, absolutePaths.testkitClient, relativePath('testkitClient'), TESTKIT_CLIENT_TEMPLATE, dependencies, prompter, overwritePolicy);
305
456
  if (testkitSummary) {
306
457
  summaries.testkitClient = testkitSummary;
307
458
  }
308
- const globalSetupSummary = await writeTemplateFile(rootDir, absolutePaths.globalSetup, relativePath('globalSetup'), GLOBAL_SETUP_TEMPLATE, dependencies, prompter);
459
+ const globalSetupSummary = await writeTemplateFile(rootDir, absolutePaths.globalSetup, relativePath('globalSetup'), GLOBAL_SETUP_TEMPLATE, dependencies, prompter, overwritePolicy);
309
460
  if (globalSetupSummary) {
310
461
  summaries.globalSetup = globalSetupSummary;
311
462
  }
312
- const vitestConfigSummary = await writeTemplateFile(rootDir, absolutePaths.vitestConfig, relativePath('vitestConfig'), VITEST_CONFIG_TEMPLATE, dependencies, prompter);
463
+ const vitestConfigSummary = await writeTemplateFile(rootDir, absolutePaths.vitestConfig, relativePath('vitestConfig'), VITEST_CONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
313
464
  if (vitestConfigSummary) {
314
465
  summaries.vitestConfig = vitestConfigSummary;
315
466
  }
316
- const testsConfigSummary = await writeTemplateFile(rootDir, absolutePaths.testsConfig, relativePath('testsConfig'), TESTS_CONFIG_TEMPLATE, dependencies, prompter);
317
- if (testsConfigSummary) {
318
- summaries.testsConfig = testsConfigSummary;
319
- }
320
- // Ensure the generated tests guidance lands beside the generated layout config.
321
- const testsAgentsSummary = await writeTemplateFile(rootDir, absolutePaths.testsAgents, relativePath('testsAgents'), TESTS_AGENTS_TEMPLATE, dependencies, prompter);
322
- if (testsAgentsSummary) {
323
- summaries.testsAgents = testsAgentsSummary;
324
- }
325
- // Seed the shared guidance that lives inside the ztd/ directory so contributors see the new instructions.
326
- const ztdDocsAgentSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsAgent, relativePath('ztdDocsAgent'), 'ztd/AGENTS.md', dependencies, prompter);
327
- if (ztdDocsAgentSummary) {
328
- summaries.ztdDocsAgent = ztdDocsAgentSummary;
329
- }
330
- // Provide the companion README inside ztd/ so maintainers understand the schema expectations and doc layout.
331
- const ztdDocsReadmeSummary = await writeTemplateFile(rootDir, absolutePaths.ztdDocsReadme, relativePath('ztdDocsReadme'), 'ztd/README.md', dependencies, prompter);
332
- if (ztdDocsReadmeSummary) {
333
- summaries.ztdDocsReadme = ztdDocsReadmeSummary;
467
+ const tsconfigSummary = await writeTemplateFile(rootDir, absolutePaths.tsconfig, relativePath('tsconfig'), TSCONFIG_TEMPLATE, dependencies, prompter, overwritePolicy);
468
+ if (tsconfigSummary) {
469
+ summaries.tsconfig = tsconfigSummary;
334
470
  }
335
471
  const editorconfigSummary = copyTemplateFileIfMissing(rootDir, relativePath('editorconfig'), '.editorconfig', dependencies);
336
472
  if (editorconfigSummary) {
@@ -348,7 +484,7 @@ async function runInitCommand(prompter, options) {
348
484
  if (prettierignoreSummary) {
349
485
  summaries.prettierignore = prettierignoreSummary;
350
486
  }
351
- const packageSummary = ensurePackageJsonFormatting(rootDir, relativePath('package'), dependencies);
487
+ const packageSummary = ensurePackageJsonFormatting(rootDir, relativePath('package'), dependencies, optionalFeatures);
352
488
  if (packageSummary) {
353
489
  summaries.package = packageSummary;
354
490
  }
@@ -358,7 +494,8 @@ async function runInitCommand(prompter, options) {
358
494
  summaries.agents = agentsRelative;
359
495
  }
360
496
  await ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summaries, dependencies);
361
- const summaryLines = buildSummaryLines(summaries);
497
+ const nextSteps = buildNextSteps(normalizeRelative(rootDir, absolutePaths.schema), workflow);
498
+ const summaryLines = buildSummaryLines(summaries, optionalFeatures, nextSteps);
362
499
  summaryLines.forEach(dependencies.log);
363
500
  return {
364
501
  summary: summaryLines.join('\n'),
@@ -534,7 +671,7 @@ function listDeclaredPackages(rootDir) {
534
671
  return declared;
535
672
  }
536
673
  function listTemplateReferencedPackages(absolutePaths, summaries) {
537
- const packages = new Set(['@rawsql-ts/testkit-postgres', '@rawsql-ts/adapter-node-pg']);
674
+ const packages = new Set();
538
675
  const touchedKeys = Object.entries(summaries)
539
676
  .filter((entry) => Boolean(entry[1]))
540
677
  .filter(([, summary]) => summary.outcome === 'created' || summary.outcome === 'overwritten')
@@ -557,7 +694,7 @@ async function ensureTemplateDependenciesInstalled(rootDir, absolutePaths, summa
557
694
  var _a;
558
695
  const packageJsonPath = node_path_1.default.join(rootDir, 'package.json');
559
696
  if (!dependencies.fileExists(packageJsonPath)) {
560
- dependencies.log('Skipping dependency installation because package.json is missing.');
697
+ dependencies.log('Skipping dependency installation because package.json is missing. Next: run pnpm init (or npm init), install dependencies, then run npx ztd ztd-config.');
561
698
  return;
562
699
  }
563
700
  const packageManager = detectPackageManager(rootDir);
@@ -612,18 +749,22 @@ function writeOptionalTemplateFile(absolutePath, relative, templateName, depende
612
749
  dependencies.writeFile(absolutePath, (0, node_fs_1.readFileSync)(templatePath, 'utf8'));
613
750
  return { relativePath: relative, outcome: 'created' };
614
751
  }
615
- function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
752
+ function ensurePackageJsonFormatting(rootDir, relative, dependencies, optionalFeatures) {
616
753
  var _a, _b;
617
754
  const packagePath = node_path_1.default.join(rootDir, 'package.json');
618
- // Skip wiring defaults when the project does not yet have a package manifest.
619
- if (!dependencies.fileExists(packagePath)) {
620
- return null;
621
- }
622
- const document = (0, node_fs_1.readFileSync)(packagePath, 'utf8');
623
- const parsed = JSON.parse(document);
755
+ const packageExists = dependencies.fileExists(packagePath);
756
+ const parsed = packageExists
757
+ ? JSON.parse((0, node_fs_1.readFileSync)(packagePath, 'utf8'))
758
+ : {
759
+ name: inferPackageName(rootDir),
760
+ version: '0.0.0',
761
+ private: true
762
+ };
624
763
  let changed = false;
625
764
  const scripts = (_a = parsed.scripts) !== null && _a !== void 0 ? _a : {};
626
765
  const requiredScripts = {
766
+ test: 'vitest run',
767
+ typecheck: 'tsc --noEmit',
627
768
  format: 'prettier . --write',
628
769
  lint: 'eslint .',
629
770
  'lint:fix': 'eslint . --fix'
@@ -661,6 +802,11 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
661
802
  'prettier-plugin-sql': '^0.19.2',
662
803
  'simple-git-hooks': '^2.13.1'
663
804
  };
805
+ const testingDeps = {
806
+ vitest: '^4.0.7',
807
+ typescript: '^5.8.2',
808
+ '@types/node': '^22.13.10'
809
+ };
664
810
  // Add the formatting toolchain dependencies that back the scripts and hooks.
665
811
  for (const [dep, version] of Object.entries(formattingDeps)) {
666
812
  if (dep in devDependencies) {
@@ -669,6 +815,31 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
669
815
  devDependencies[dep] = version;
670
816
  changed = true;
671
817
  }
818
+ const stackDependencies = {
819
+ ...MANDATORY_TESTKIT_DEPENDENCIES,
820
+ ...SQL_CONTRACT_DEPENDENCY
821
+ };
822
+ if (optionalFeatures.validator === 'zod') {
823
+ Object.assign(stackDependencies, ZOD_DEPENDENCY);
824
+ }
825
+ else {
826
+ Object.assign(stackDependencies, ARKTYPE_DEPENDENCY);
827
+ }
828
+ // Ensure test and typecheck toolchain dependencies are present for a runnable scaffold.
829
+ for (const [dep, version] of Object.entries(testingDeps)) {
830
+ if (dep in devDependencies) {
831
+ continue;
832
+ }
833
+ devDependencies[dep] = version;
834
+ changed = true;
835
+ }
836
+ for (const [dep, version] of Object.entries(stackDependencies)) {
837
+ if (dep in devDependencies) {
838
+ continue;
839
+ }
840
+ devDependencies[dep] = version;
841
+ changed = true;
842
+ }
672
843
  if (!changed) {
673
844
  return null;
674
845
  }
@@ -676,42 +847,56 @@ function ensurePackageJsonFormatting(rootDir, relative, dependencies) {
676
847
  dependencies.ensureDirectory(node_path_1.default.dirname(packagePath));
677
848
  // Persist the updated manifest so the new scripts and tools are available immediately.
678
849
  dependencies.writeFile(packagePath, `${JSON.stringify(parsed, null, 2)}\n`);
679
- return { relativePath: relative, outcome: 'overwritten' };
850
+ return { relativePath: relative, outcome: packageExists ? 'overwritten' : 'created' };
851
+ }
852
+ function inferPackageName(rootDir) {
853
+ const baseName = node_path_1.default.basename(rootDir).toLowerCase();
854
+ const normalized = baseName.replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '');
855
+ if (normalized.length > 0) {
856
+ return normalized;
857
+ }
858
+ return 'ztd-project';
680
859
  }
681
- async function writeFileWithConsent(absolutePath, relative, dependencies, prompter, writer) {
682
- const { existed, write } = await confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter);
860
+ async function writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, writer) {
861
+ const { existed, write } = await confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy);
683
862
  if (!write) {
684
863
  return { relativePath: relative, outcome: 'unchanged' };
685
864
  }
686
865
  await writer();
687
866
  return { relativePath: relative, outcome: existed ? 'overwritten' : 'created' };
688
867
  }
689
- async function confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter) {
868
+ async function confirmOverwriteIfExists(absolutePath, relative, dependencies, prompter, overwritePolicy) {
690
869
  const existed = dependencies.fileExists(absolutePath);
691
870
  if (!existed) {
692
871
  return { existed: false, write: true };
693
872
  }
873
+ if (overwritePolicy.force) {
874
+ return { existed: true, write: true };
875
+ }
876
+ if (overwritePolicy.nonInteractive) {
877
+ throw new Error(`File ${relative} already exists. Re-run with --yes to overwrite or remove the file before running ztd init.`);
878
+ }
694
879
  const overwrite = await prompter.confirm(`File ${relative} already exists. Overwrite?`);
695
880
  if (!overwrite) {
696
881
  return { existed: true, write: false };
697
882
  }
698
883
  return { existed: true, write: true };
699
884
  }
700
- async function writeDocFile(absolutePath, relative, contents, dependencies, prompter) {
701
- const summary = await writeFileWithConsent(absolutePath, relative, dependencies, prompter, () => {
885
+ async function writeDocFile(absolutePath, relative, contents, dependencies, prompter, overwritePolicy) {
886
+ const summary = await writeFileWithConsent(absolutePath, relative, dependencies, prompter, overwritePolicy, () => {
702
887
  dependencies.ensureDirectory(node_path_1.default.dirname(absolutePath));
703
888
  dependencies.writeFile(absolutePath, contents);
704
889
  });
705
890
  return summary;
706
891
  }
707
- async function writeTemplateFile(rootDir, absolutePath, relative, templateName, dependencies, prompter, allowFallback) {
892
+ async function writeTemplateFile(rootDir, absolutePath, relative, templateName, dependencies, prompter, overwritePolicy, allowFallback) {
708
893
  const templateTarget = resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback);
709
894
  if (!templateTarget) {
710
895
  return null;
711
896
  }
712
897
  // Load shared documentation templates so every new project gets the same guidance.
713
898
  const contents = loadTemplate(templateName);
714
- return writeDocFile(templateTarget.absolutePath, templateTarget.relativePath, contents, dependencies, prompter);
899
+ return writeDocFile(templateTarget.absolutePath, templateTarget.relativePath, contents, dependencies, prompter, overwritePolicy);
715
900
  }
716
901
  function resolveTemplateTarget(rootDir, absolutePath, relative, dependencies, allowFallback) {
717
902
  if (!dependencies.fileExists(absolutePath)) {
@@ -738,6 +923,25 @@ function normalizeRelative(rootDir, absolutePath) {
738
923
  const relative = node_path_1.default.relative(rootDir, absolutePath).replace(/\\/g, '/');
739
924
  return relative || absolutePath;
740
925
  }
926
+ /**
927
+ * Normalizes a schema identifier into the canonical lowercase form used by ztd-cli file naming.
928
+ * Empty input falls back to the configured default schema.
929
+ */
930
+ function normalizeSchemaName(value) {
931
+ const trimmed = value.trim();
932
+ if (!trimmed) {
933
+ return ztdProjectConfig_1.DEFAULT_ZTD_CONFIG.ddl.defaultSchema;
934
+ }
935
+ return trimmed.replace(/^"|"$/g, '').toLowerCase();
936
+ }
937
+ /**
938
+ * Sanitizes a normalized schema identifier so it can be used as a filesystem-safe file stem.
939
+ * Returns `schema` when all characters are stripped by sanitization.
940
+ */
941
+ function sanitizeSchemaFileName(schemaName) {
942
+ const sanitized = schemaName.replace(/[^a-z0-9_-]/g, '_').replace(/^_+|_+$/g, '');
943
+ return sanitized || 'schema';
944
+ }
741
945
  function resolveOrCreateAgentsFile(rootDir, dependencies) {
742
946
  // Prefer materializing the bundled template before looking for existing attention files.
743
947
  const templateTarget = dependencies.copyAgentsTemplate(rootDir);
@@ -780,20 +984,51 @@ function loadTemplate(templateName) {
780
984
  }
781
985
  return (0, node_fs_1.readFileSync)(templatePath, 'utf8');
782
986
  }
783
- function buildSummaryLines(summaries) {
987
+ function buildNextSteps(schemaRelativePath, workflow) {
988
+ const stepOne = workflow === 'pg_dump'
989
+ ? ` 1. Review the dumped DDL in ${schemaRelativePath}`
990
+ : ` 1. If the schema file is empty, edit ${schemaRelativePath}`;
991
+ return [
992
+ stepOne,
993
+ ' 2. Run npx ztd ztd-config',
994
+ ' 3. Provide a SqlClient implementation (adapter or mock)',
995
+ ' 4. Run tests (pnpm test or npx vitest run)'
996
+ ];
997
+ }
998
+ function buildSummaryLines(summaries, optionalFeatures, nextSteps) {
784
999
  const orderedKeys = [
785
1000
  'schema',
786
1001
  'config',
787
- 'testsConfig',
788
- 'testsAgents',
789
- 'ztdConfig',
790
1002
  'readme',
1003
+ 'ztdDocsAgent',
1004
+ 'ztdDocsReadme',
1005
+ 'ztdDdlAgents',
1006
+ 'srcAgents',
1007
+ 'srcCatalogAgents',
1008
+ 'srcCatalogRuntimeAgents',
1009
+ 'srcCatalogSpecsAgents',
1010
+ 'srcSqlAgents',
1011
+ 'srcReposAgents',
1012
+ 'viewsRepoAgents',
1013
+ 'tablesRepoAgents',
1014
+ 'jobsAgents',
1015
+ 'sqlReadme',
1016
+ 'viewsRepoReadme',
1017
+ 'tablesRepoReadme',
1018
+ 'jobsReadme',
1019
+ 'smokeSpec',
1020
+ 'smokeCoercions',
1021
+ 'smokeRuntime',
1022
+ 'smokeValidationTest',
1023
+ 'testsAgents',
1024
+ 'testsSupportAgents',
1025
+ 'testsGeneratedAgents',
1026
+ 'testsSmoke',
791
1027
  'sqlClient',
792
1028
  'testkitClient',
793
1029
  'globalSetup',
794
1030
  'vitestConfig',
795
- 'ztdDocsAgent',
796
- 'ztdDocsReadme',
1031
+ 'tsconfig',
797
1032
  'agents',
798
1033
  'gitignore',
799
1034
  'editorconfig',
@@ -813,7 +1048,13 @@ function buildSummaryLines(summaries) {
813
1048
  lines.push(` - ${summary.relativePath}${note}`);
814
1049
  }
815
1050
  }
816
- lines.push('', 'Next steps:', ...NEXT_STEPS);
1051
+ lines.push('', 'Validation configuration:');
1052
+ lines.push(' - SQL catalog/mapping support via @rawsql-ts/sql-contract (see docs/recipes/sql-contract.md)');
1053
+ const validatorLabel = optionalFeatures.validator === 'zod'
1054
+ ? 'Zod (zod, docs/recipes/validation-zod.md)'
1055
+ : 'ArkType (arktype, docs/recipes/validation-arktype.md)';
1056
+ lines.push(` - Validator backend: ${validatorLabel}`);
1057
+ lines.push('', 'Next steps:', ...nextSteps);
817
1058
  return lines;
818
1059
  }
819
1060
  function registerInitCommand(program) {
@@ -822,12 +1063,16 @@ function registerInitCommand(program) {
822
1063
  .description('Automate project setup for Zero Table Dependency workflows')
823
1064
  .option('--with-sqlclient', 'Generate a minimal SqlClient interface for repositories')
824
1065
  .option('--with-app-interface', 'Append application interface guidance to AGENTS.md only')
1066
+ .option('--yes', 'Overwrite existing scaffold files without prompting')
825
1067
  .action(async (options) => {
1068
+ var _a;
826
1069
  const prompter = createConsolePrompter();
827
1070
  try {
828
1071
  await runInitCommand(prompter, {
829
1072
  withSqlClient: options.withSqlclient,
830
- withAppInterface: options.withAppInterface
1073
+ withAppInterface: options.withAppInterface,
1074
+ forceOverwrite: (_a = options.yes) !== null && _a !== void 0 ? _a : false,
1075
+ nonInteractive: !process.stdin.isTTY
831
1076
  });
832
1077
  }
833
1078
  finally {