create-questpie 2.0.3 → 2.0.4

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 (35) hide show
  1. package/dist/index.mjs +244 -30
  2. package/package.json +1 -1
  3. package/skills/questpie/AGENTS.md +299 -98
  4. package/skills/questpie/SKILL.md +50 -17
  5. package/skills/questpie/coverage.json +213 -0
  6. package/skills/questpie/references/auth.md +119 -4
  7. package/skills/questpie/references/business-logic.md +126 -56
  8. package/skills/questpie/references/crud-api.md +231 -29
  9. package/skills/questpie/references/data-modeling.md +22 -6
  10. package/skills/questpie/references/extend.md +34 -7
  11. package/skills/questpie/references/field-types.md +14 -2
  12. package/skills/questpie/references/infrastructure-adapters.md +207 -32
  13. package/skills/questpie/references/mcp.md +147 -0
  14. package/skills/questpie/references/multi-tenancy.md +1 -2
  15. package/skills/questpie/references/production.md +218 -53
  16. package/skills/questpie/references/quickstart.md +6 -8
  17. package/skills/questpie/references/rules.md +86 -21
  18. package/skills/questpie/references/sandbox.md +110 -0
  19. package/skills/questpie/references/tanstack-query.md +34 -11
  20. package/skills/questpie/references/type-inference.md +167 -0
  21. package/skills/questpie/references/workflows.md +155 -0
  22. package/skills/questpie-admin/AGENTS.md +47 -40
  23. package/skills/questpie-admin/SKILL.md +46 -39
  24. package/skills/questpie-admin/references/custom-ui.md +1 -1
  25. package/templates/tanstack-start/AGENTS.md +15 -8
  26. package/templates/tanstack-start/CLAUDE.md +12 -5
  27. package/templates/tanstack-start/README.md +7 -6
  28. package/templates/tanstack-start/package.json +1 -0
  29. package/templates/tanstack-start/src/questpie/admin/modules.ts +3 -1
  30. package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +10 -9
  31. package/templates/tanstack-start/src/questpie/server/config/auth.ts +1 -1
  32. package/templates/tanstack-start/src/questpie/server/modules.ts +4 -5
  33. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +2 -1
  34. package/templates/tanstack-start/src/routes/api/$.ts +1 -2
  35. package/templates/tanstack-start/vite.config.ts +2 -2
package/dist/index.mjs CHANGED
@@ -1,11 +1,11 @@
1
1
  #!/usr/bin/env node
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join, resolve } from "node:path";
2
4
  import { Command } from "commander";
3
5
  import * as p from "@clack/prompts";
4
6
  import pc from "picocolors";
5
7
  import { execFileSync, execSync } from "node:child_process";
6
- import { existsSync } from "node:fs";
7
8
  import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
8
- import { join, resolve } from "node:path";
9
9
 
10
10
  //#region src/templates.ts
11
11
  const templates = [{
@@ -86,19 +86,62 @@ const label = {
86
86
 
87
87
  //#endregion
88
88
  //#region src/prompts.ts
89
+ const queueAdapters = [
90
+ "pg-boss",
91
+ "bullmq",
92
+ "none"
93
+ ];
94
+ const emailAdapters = [
95
+ "console",
96
+ "smtp",
97
+ "resend",
98
+ "plunk"
99
+ ];
100
+ const realtimeAdapters = [
101
+ "none",
102
+ "pg-notify",
103
+ "redis-streams"
104
+ ];
105
+ const kvAdapters = ["memory", "redis"];
106
+ function assertChoice(name, value, choices) {
107
+ if (value === void 0) return void 0;
108
+ if (choices.includes(value)) return value;
109
+ throw new Error(`Invalid ${name}: ${value}. Expected one of: ${choices.join(", ")}.`);
110
+ }
111
+ function withOptionDefaults(options) {
112
+ return {
113
+ ...options,
114
+ queueAdapter: options.queueAdapter ?? "pg-boss",
115
+ emailAdapter: options.emailAdapter ?? "console",
116
+ realtimeAdapter: options.realtimeAdapter ?? "none",
117
+ kvAdapter: options.kvAdapter ?? "memory",
118
+ includeWorkflows: options.includeWorkflows ?? false
119
+ };
120
+ }
89
121
  async function runPrompts(args) {
90
- if (!Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
122
+ const isInteractive = Boolean(process.stdin.isTTY && process.stdout.isTTY);
123
+ const queueAdapter = assertChoice("queue adapter", args.queueAdapter, queueAdapters);
124
+ const emailAdapter = assertChoice("email adapter", args.emailAdapter, emailAdapters);
125
+ const realtimeAdapter = assertChoice("realtime adapter", args.realtimeAdapter, realtimeAdapters);
126
+ const kvAdapter = assertChoice("KV adapter", args.kvAdapter, kvAdapters);
127
+ if (!isInteractive) {
91
128
  if (!args.projectName) throw new Error("Project name is required in non-interactive mode.");
92
129
  if (!isValidPackageName(args.projectName)) throw new Error("Invalid package name (use lowercase, hyphens, no spaces).");
93
- return {
130
+ return withOptionDefaults({
94
131
  projectName: args.projectName,
95
132
  templateId: args.templateId ?? templates[0].id,
96
133
  databaseName: args.databaseName ?? toDbName(args.projectName),
97
134
  installDeps: args.installDeps ?? true,
98
135
  initGit: args.initGit ?? true,
99
136
  installSkills: args.installSkills ?? true,
100
- runCodegen: args.runCodegen ?? true
101
- };
137
+ runCodegen: args.runCodegen ?? true,
138
+ continueOnError: args.continueOnError ?? false,
139
+ queueAdapter,
140
+ emailAdapter,
141
+ realtimeAdapter,
142
+ kvAdapter,
143
+ includeWorkflows: args.includeWorkflows ?? false
144
+ });
102
145
  }
103
146
  p.intro(pc.bgCyan(pc.black(" QUESTPIE — Create a new project ")));
104
147
  const questions = await p.group({
@@ -170,15 +213,21 @@ async function runPrompts(args) {
170
213
  p.cancel("Operation cancelled.");
171
214
  process.exit(0);
172
215
  } });
173
- return {
216
+ return withOptionDefaults({
174
217
  projectName: questions.projectName,
175
218
  templateId: questions.templateId,
176
219
  databaseName: questions.databaseName,
177
220
  installDeps: questions.installDeps,
178
221
  initGit: questions.initGit,
179
222
  installSkills: questions.installSkills,
180
- runCodegen: questions.runCodegen
181
- };
223
+ runCodegen: questions.runCodegen,
224
+ continueOnError: args.continueOnError ?? false,
225
+ queueAdapter,
226
+ emailAdapter,
227
+ realtimeAdapter,
228
+ kvAdapter,
229
+ includeWorkflows: args.includeWorkflows ?? false
230
+ });
182
231
  }
183
232
 
184
233
  //#endregion
@@ -286,24 +335,168 @@ async function installProjectSkills(targetDir) {
286
335
  }
287
336
  return installed;
288
337
  }
338
+ function handleFatalStepFailure(message, error, continueOnError) {
339
+ if (continueOnError) return;
340
+ const cause = error instanceof Error ? error.message : typeof error === "string" ? error : String(error);
341
+ throw new Error(`${message}: ${cause}`);
342
+ }
343
+ async function applyProjectOptions(targetDir, options) {
344
+ await updatePackageJson(targetDir, options);
345
+ await writeFile(join(targetDir, "src", "lib", "env.ts"), buildEnvFile(options), "utf-8");
346
+ await writeFile(join(targetDir, "src", "questpie", "server", "questpie.config.ts"), buildRuntimeConfig(options), "utf-8");
347
+ await writeFile(join(targetDir, "src", "questpie", "server", "modules.ts"), buildServerModules(options), "utf-8");
348
+ await writeFile(join(targetDir, "src", "questpie", "admin", "modules.ts"), buildAdminModules(options), "utf-8");
349
+ }
350
+ async function updatePackageJson(targetDir, options) {
351
+ const packageJsonPath = join(targetDir, "package.json");
352
+ const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8"));
353
+ if (options.queueAdapter === "bullmq") packageJson.dependencies.bullmq = "^5.0.0";
354
+ if (options.queueAdapter === "bullmq" || options.realtimeAdapter === "redis-streams" || options.kvAdapter === "redis") packageJson.dependencies.redis = "^5.0.0";
355
+ if (options.includeWorkflows) packageJson.dependencies["@questpie/workflows"] = "latest";
356
+ await writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, " ")}\n`);
357
+ }
358
+ function buildEnvFile(options) {
359
+ const mailAdapters = Array.from(new Set(["console", options.emailAdapter ?? "console"]));
360
+ const lines = [
361
+ `import { createEnv } from "@t3-oss/env-core";`,
362
+ `import { z } from "zod";`,
363
+ ``,
364
+ `export const env = createEnv({`,
365
+ `\tserver: {`,
366
+ `\t\tDATABASE_URL: z.string().url(),`,
367
+ `\t\tAPP_URL: z.string().url().default("http://localhost:3000"),`,
368
+ `\t\tPORT: z`,
369
+ `\t\t\t.string()`,
370
+ `\t\t\t.transform(Number)`,
371
+ `\t\t\t.pipe(z.number().int().positive())`,
372
+ `\t\t\t.default(3000),`,
373
+ `\t\tBETTER_AUTH_SECRET: z.string().min(1).default("change-me-in-production"),`,
374
+ `\t\tMAIL_ADAPTER: z.enum(${JSON.stringify(mailAdapters)}).default("console"),`
375
+ ];
376
+ if (options.emailAdapter === "smtp") lines.push(`\t\tSMTP_HOST: z.string().optional(),`, `\t\tSMTP_PORT: z`, `\t\t\t.string()`, `\t\t\t.transform(Number)`, `\t\t\t.pipe(z.number().int().positive())`, `\t\t\t.optional(),`);
377
+ if (options.emailAdapter === "resend") lines.push(`\t\tRESEND_API_KEY: z.string().optional(),`);
378
+ if (options.emailAdapter === "plunk") lines.push(`\t\tPLUNK_SECRET_KEY: z.string().optional(),`);
379
+ if (options.queueAdapter === "bullmq" || options.realtimeAdapter === "redis-streams" || options.kvAdapter === "redis") lines.push(`\t\tREDIS_URL: z.string().url().default("redis://localhost:6379"),`);
380
+ lines.push(`\t},`, `\truntimeEnv: process.env,`, `\temptyStringAsUndefined: true,`, `});`, ``);
381
+ return lines.join("\n");
382
+ }
383
+ function buildRuntimeConfig(options) {
384
+ const imports = [`import { runtimeConfig } from "questpie/app";`, `import { ConsoleAdapter } from "questpie/adapters/console";`];
385
+ if (options.queueAdapter === "pg-boss") imports.push(`import { pgBossAdapter } from "questpie/adapters/pg-boss";`);
386
+ if (options.queueAdapter === "bullmq") imports.push(`import { bullMQAdapter } from "questpie/adapters/bullmq";`);
387
+ if (options.emailAdapter === "smtp") imports.push(`import { SmtpAdapter } from "questpie/adapters/smtp";`);
388
+ if (options.emailAdapter === "resend") imports.push(`import { ResendAdapter } from "questpie/adapters/resend";`);
389
+ if (options.emailAdapter === "plunk") imports.push(`import { PlunkAdapter } from "questpie/adapters/plunk";`);
390
+ if (options.realtimeAdapter === "pg-notify") imports.push(`import { pgNotifyAdapter } from "questpie/adapters/pg-notify";`);
391
+ if (options.realtimeAdapter === "redis-streams") imports.push(`import { redisStreamsAdapter } from "questpie/adapters/redis-streams";`);
392
+ if (options.kvAdapter === "redis") {
393
+ imports.push(`import { redisKVAdapter } from "questpie/adapters/redis-kv";`);
394
+ imports.push(`import { createClient } from "redis";`);
395
+ }
396
+ imports.push(``, `import { env } from "@/lib/env.js";`, ``);
397
+ const helpers = [];
398
+ if (options.emailAdapter === "resend" || options.emailAdapter === "plunk") helpers.push(`function requiredEnv(value: string | undefined, name: string): string {`, `\tif (!value) throw new Error(\`Missing required environment variable: \${name}\`);`, `\treturn value;`, `}`, ``);
399
+ if (options.kvAdapter === "redis") helpers.push(`async function getRedis() {`, `\tconst redis = createClient({ url: env.REDIS_URL });`, `\tawait redis.connect();`, `\treturn redis;`, `}`, ``);
400
+ const configEntries = [
401
+ `\tapp: { url: env.APP_URL },`,
402
+ `\tdb: { url: env.DATABASE_URL },`,
403
+ `\tstorage: { basePath: "/api" },`,
404
+ `\temail: {`,
405
+ `\t\tadapter: ${buildEmailAdapterExpression(options)},`,
406
+ `\t},`
407
+ ];
408
+ if (options.queueAdapter === "pg-boss") configEntries.push(`\tqueue: {`, `\t\tadapter: pgBossAdapter({ connectionString: env.DATABASE_URL }),`, `\t},`);
409
+ if (options.queueAdapter === "bullmq") configEntries.push(`\tqueue: {`, `\t\tadapter: bullMQAdapter({ connection: { url: env.REDIS_URL } }),`, `\t},`);
410
+ if (options.realtimeAdapter === "pg-notify") configEntries.push(`\trealtime: {`, `\t\tadapter: pgNotifyAdapter({ connectionString: env.DATABASE_URL }),`, `\t},`);
411
+ if (options.realtimeAdapter === "redis-streams") configEntries.push(`\trealtime: {`, `\t\tadapter: redisStreamsAdapter({ url: env.REDIS_URL }),`, `\t},`);
412
+ if (options.kvAdapter === "redis") configEntries.push(`\tkv: {`, `\t\tadapter: redisKVAdapter({ client: getRedis, keyPrefix: "${options.projectName}:" }),`, `\t},`);
413
+ return [
414
+ `/**`,
415
+ ` * QUESTPIE Runtime Configuration`,
416
+ ` *`,
417
+ ` * Runtime-only configuration: database, adapters, secrets.`,
418
+ ` * Entity definitions are codegen-generated.`,
419
+ ` */`,
420
+ ``,
421
+ ...imports,
422
+ ...helpers,
423
+ `export default runtimeConfig({`,
424
+ ...configEntries,
425
+ `});`,
426
+ ``
427
+ ].join("\n");
428
+ }
429
+ function buildEmailAdapterExpression(options) {
430
+ if (options.emailAdapter === "smtp") return `env.MAIL_ADAPTER === "smtp"\n\t\t\t? new SmtpAdapter({\n\t\t\t\t\ttransport: {\n\t\t\t\t\t\thost: env.SMTP_HOST || "localhost",\n\t\t\t\t\t\tport: env.SMTP_PORT || 1025,\n\t\t\t\t\t\tsecure: false,\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t: new ConsoleAdapter({ logHtml: false })`;
431
+ if (options.emailAdapter === "resend") return `env.MAIL_ADAPTER === "resend"\n\t\t\t? new ResendAdapter({ apiKey: requiredEnv(env.RESEND_API_KEY, "RESEND_API_KEY") })\n\t\t\t: new ConsoleAdapter({ logHtml: false })`;
432
+ if (options.emailAdapter === "plunk") return `env.MAIL_ADAPTER === "plunk"\n\t\t\t? new PlunkAdapter({ apiKey: requiredEnv(env.PLUNK_SECRET_KEY, "PLUNK_SECRET_KEY") })\n\t\t\t: new ConsoleAdapter({ logHtml: false })`;
433
+ return `new ConsoleAdapter({ logHtml: false })`;
434
+ }
435
+ function buildServerModules(options) {
436
+ const imports = [
437
+ `/**`,
438
+ ` * Modules — static module dependencies for this project.`,
439
+ ` */`,
440
+ `import { adminModule } from "@questpie/admin/modules/admin";`,
441
+ `import { openApiModule } from "@questpie/openapi";`
442
+ ];
443
+ const modules = ["adminModule", "openApiModule"];
444
+ if (options.includeWorkflows) {
445
+ imports.push(`import { workflowsModule } from "@questpie/workflows/modules/workflows";`);
446
+ modules.push("workflowsModule");
447
+ }
448
+ return [
449
+ ...imports,
450
+ ``,
451
+ `const modules = [`,
452
+ ...modules.map((mod) => `\t${mod},`),
453
+ `] as const;`,
454
+ ``,
455
+ `export default modules;`,
456
+ ``
457
+ ].join("\n");
458
+ }
459
+ function buildAdminModules(options) {
460
+ const imports = [`import { adminClientModule } from "@questpie/admin/client/modules/admin";`];
461
+ const modules = ["adminClientModule"];
462
+ if (options.includeWorkflows) {
463
+ imports.push(`import { workflowsClientModule } from "@questpie/workflows/client/modules/workflows";`);
464
+ modules.push("workflowsClientModule");
465
+ }
466
+ return [
467
+ ...imports,
468
+ ``,
469
+ `export default [${modules.join(", ")}] as const;`,
470
+ ``
471
+ ].join("\n");
472
+ }
289
473
  async function scaffold(options) {
474
+ const resolvedOptions = {
475
+ ...options,
476
+ queueAdapter: options.queueAdapter ?? "pg-boss",
477
+ emailAdapter: options.emailAdapter ?? "console",
478
+ realtimeAdapter: options.realtimeAdapter ?? "none",
479
+ kvAdapter: options.kvAdapter ?? "memory",
480
+ includeWorkflows: options.includeWorkflows ?? false
481
+ };
290
482
  const spinner = p.spinner();
291
- const targetDir = resolve(process.cwd(), options.projectName);
483
+ const targetDir = resolve(process.cwd(), resolvedOptions.projectName);
484
+ const continueOnError = resolvedOptions.continueOnError === true;
292
485
  if (existsSync(targetDir)) {
293
- p.log.error(`Directory ${options.projectName} already exists.`);
486
+ p.log.error(`Directory ${resolvedOptions.projectName} already exists.`);
294
487
  process.exit(1);
295
488
  }
296
489
  const vars = {
297
- projectName: options.projectName,
298
- databaseName: options.databaseName,
299
- databaseUser: options.databaseName,
490
+ projectName: resolvedOptions.projectName,
491
+ databaseName: resolvedOptions.databaseName,
492
+ databaseUser: resolvedOptions.databaseName,
300
493
  databasePassword: generatePassword(),
301
494
  authSecret: generatePassword(48)
302
495
  };
303
496
  spinner.start("Copying template files");
304
- const templateDir = join(getTemplatesDir(), options.templateId);
497
+ const templateDir = join(getTemplatesDir(), resolvedOptions.templateId);
305
498
  if (!existsSync(templateDir)) {
306
- spinner.stop(label.error(`Template "${options.templateId}" not found`));
499
+ spinner.stop(label.error(`Template "${resolvedOptions.templateId}" not found`));
307
500
  process.exit(1);
308
501
  }
309
502
  await cp(templateDir, targetDir, { recursive: true });
@@ -313,18 +506,20 @@ async function scaffold(options) {
313
506
  await renameEnvExample(targetDir);
314
507
  await processDirectory(targetDir, vars);
315
508
  await createLocalEnv(targetDir);
509
+ await applyProjectOptions(targetDir, resolvedOptions);
316
510
  spinner.stop(label.success("Processed template variables"));
317
511
  const pm = detectPackageManager();
318
- if (options.installDeps) {
512
+ if (resolvedOptions.installDeps) {
319
513
  spinner.start(`Installing dependencies with ${pm}`);
320
514
  try {
321
515
  installDependencies(targetDir, pm);
322
516
  spinner.stop(label.success("Installed dependencies"));
323
- } catch {
324
- spinner.stop(label.warn("Failed to install dependencies — run manually"));
517
+ } catch (error) {
518
+ spinner.stop(label.warn("Failed to install dependencies"));
519
+ handleFatalStepFailure("Dependency installation failed", error, continueOnError);
325
520
  }
326
521
  }
327
- if (options.installSkills) {
522
+ if (resolvedOptions.installSkills) {
328
523
  spinner.start("Installing QUESTPIE agent skills");
329
524
  try {
330
525
  const installedSkills = await installProjectSkills(targetDir);
@@ -334,16 +529,17 @@ async function scaffold(options) {
334
529
  spinner.stop(label.warn("Failed to install skills — continuing"));
335
530
  }
336
531
  }
337
- if (options.installDeps && options.runCodegen) {
532
+ if (resolvedOptions.installDeps && resolvedOptions.runCodegen) {
338
533
  spinner.start("Generating QUESTPIE app");
339
534
  try {
340
535
  runPackageScript(targetDir, pm, "scaffold:generate");
341
536
  spinner.stop(label.success("Generated QUESTPIE app"));
342
- } catch {
343
- spinner.stop(label.warn("Failed to run codegen — run manually"));
537
+ } catch (error) {
538
+ spinner.stop(label.warn("Failed to run codegen"));
539
+ handleFatalStepFailure("QUESTPIE codegen failed", error, continueOnError);
344
540
  }
345
541
  }
346
- if (options.initGit && isGitInstalled()) {
542
+ if (resolvedOptions.initGit && isGitInstalled()) {
347
543
  spinner.start("Initializing git repository");
348
544
  try {
349
545
  gitInit(targetDir);
@@ -355,7 +551,7 @@ async function scaffold(options) {
355
551
  const runScript = (script) => pm === "npm" ? `npm run ${script}` : `${pm} run ${script}`;
356
552
  const questpieBin = pm === "npm" ? "npx questpie" : "bunx questpie";
357
553
  p.note([
358
- `cd ${options.projectName}`,
554
+ `cd ${resolvedOptions.projectName}`,
359
555
  "",
360
556
  "# Review the generated environment",
361
557
  "# .env has already been created from .env.example",
@@ -366,8 +562,8 @@ async function scaffold(options) {
366
562
  "# Regenerate and type-check the scaffold",
367
563
  runScript("scaffold:verify"),
368
564
  "",
369
- "# Run migrations",
370
- runScript("migrate"),
565
+ "# Create local database tables",
566
+ runScript("db:push"),
371
567
  "",
372
568
  "# Start dev server",
373
569
  runScript("dev"),
@@ -377,14 +573,26 @@ async function scaffold(options) {
377
573
  `${questpieBin} add global marketing`,
378
574
  "",
379
575
  "# If you create files manually",
380
- runScript("questpie:generate")
576
+ runScript("questpie:generate"),
577
+ "",
578
+ "# For production migrations",
579
+ runScript("migrate:create"),
580
+ runScript("migrate")
381
581
  ].join("\n"), "Next steps");
382
582
  p.outro(`${label.success("Done!")} Happy building with QUESTPIE!`);
383
583
  }
384
584
 
385
585
  //#endregion
386
586
  //#region src/index.ts
387
- new Command().name("create-questpie").description("Create a new QUESTPIE project").version("2.0.1").argument("[project-name]", "Name of the project").option("-t, --template <name>", "Template to use (default: tanstack-start)").option("--database <name>", "Database name (default: derived from project name)").option("--no-install", "Skip dependency installation").option("--no-git", "Skip git initialization").option("--no-skills", "Skip installing project-local QUESTPIE agent skills").option("--no-generate", "Skip running QUESTPIE codegen after install").action(async (projectName, opts) => {
587
+ function readPackageVersion() {
588
+ for (const candidate of [resolve(import.meta.dirname, "..", "package.json"), resolve(import.meta.dirname, "..", "..", "package.json")]) {
589
+ if (!existsSync(candidate)) continue;
590
+ const packageJson = JSON.parse(readFileSync(candidate, "utf-8"));
591
+ if (packageJson.version) return packageJson.version;
592
+ }
593
+ return "0.0.0";
594
+ }
595
+ new Command().name("create-questpie").description("Create a new QUESTPIE project").version(readPackageVersion()).argument("[project-name]", "Name of the project").option("-t, --template <name>", "Template to use (default: tanstack-start)").option("--database <name>", "Database name (default: derived from project name)").option("--no-install", "Skip dependency installation").option("--no-git", "Skip git initialization").option("--no-skills", "Skip installing project-local QUESTPIE agent skills").option("--no-generate", "Skip running QUESTPIE codegen after install").option("--queue <adapter>", "Queue adapter: pg-boss, bullmq, none (default: pg-boss)").option("--email <adapter>", "Email adapters to scaffold: console, smtp, resend, plunk (default: console)").option("--realtime <adapter>", "Realtime adapter: none, pg-notify, redis-streams (default: none)").option("--kv <adapter>", "KV adapter: memory, redis (default: memory)").option("--workflows", "Install and register @questpie/workflows").option("--continue-on-error", "Keep scaffold files when dependency install or codegen fails").action(async (projectName, opts) => {
388
596
  try {
389
597
  if (opts.template && !getTemplate(opts.template)) throw new Error(`Unknown template: ${opts.template}`);
390
598
  await scaffold(await runPrompts({
@@ -394,7 +602,13 @@ new Command().name("create-questpie").description("Create a new QUESTPIE project
394
602
  installDeps: opts.install === false ? false : void 0,
395
603
  initGit: opts.git === false ? false : void 0,
396
604
  installSkills: opts.skills === false ? false : void 0,
397
- runCodegen: opts.generate === false ? false : void 0
605
+ runCodegen: opts.generate === false ? false : void 0,
606
+ continueOnError: opts.continueOnError === true,
607
+ queueAdapter: opts.queue,
608
+ emailAdapter: opts.email,
609
+ realtimeAdapter: opts.realtime,
610
+ kvAdapter: opts.kv,
611
+ includeWorkflows: opts.workflows === true
398
612
  }));
399
613
  } catch (error) {
400
614
  console.error(error instanceof Error ? error.message : String(error));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-questpie",
3
- "version": "2.0.3",
3
+ "version": "2.0.4",
4
4
  "description": "Create a new QUESTPIE project",
5
5
  "keywords": [
6
6
  "create",