create-questpie 2.0.0 → 2.0.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 (45) hide show
  1. package/README.md +10 -6
  2. package/dist/index.mjs +140 -25
  3. package/package.json +5 -3
  4. package/skills/questpie/AGENTS.md +2664 -0
  5. package/skills/questpie/SKILL.md +181 -0
  6. package/skills/questpie/references/auth.md +121 -0
  7. package/skills/questpie/references/business-logic.md +550 -0
  8. package/skills/questpie/references/codegen-plugin-api.md +382 -0
  9. package/skills/questpie/references/crud-api.md +378 -0
  10. package/skills/questpie/references/data-modeling.md +489 -0
  11. package/skills/questpie/references/extend.md +493 -0
  12. package/skills/questpie/references/field-types.md +386 -0
  13. package/skills/questpie/references/infrastructure-adapters.md +545 -0
  14. package/skills/questpie/references/multi-tenancy.md +364 -0
  15. package/skills/questpie/references/production.md +475 -0
  16. package/skills/questpie/references/query-operators.md +125 -0
  17. package/skills/questpie/references/quickstart.md +549 -0
  18. package/skills/questpie/references/rules.md +327 -0
  19. package/skills/questpie/references/tanstack-query.md +520 -0
  20. package/skills/questpie-admin/AGENTS.md +1442 -0
  21. package/skills/questpie-admin/SKILL.md +410 -0
  22. package/skills/questpie-admin/references/blocks.md +307 -0
  23. package/skills/questpie-admin/references/custom-ui.md +305 -0
  24. package/skills/questpie-admin/references/views.md +433 -0
  25. package/templates/tanstack-start/AGENTS.md +71 -62
  26. package/templates/tanstack-start/CLAUDE.md +26 -23
  27. package/templates/tanstack-start/README.md +32 -20
  28. package/templates/tanstack-start/env.example +1 -1
  29. package/templates/tanstack-start/package.json +20 -6
  30. package/templates/tanstack-start/src/lib/client.ts +2 -2
  31. package/templates/tanstack-start/src/lib/env.ts +1 -1
  32. package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
  33. package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
  34. package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
  35. package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
  36. package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
  37. package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
  38. package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
  39. package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
  40. package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
  41. package/templates/tanstack-start/src/routes/__root.tsx +0 -2
  42. package/templates/tanstack-start/src/routes/admin.tsx +8 -1
  43. package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
  44. package/templates/tanstack-start/src/vite-env.d.ts +1 -0
  45. package/templates/tanstack-start/vite.config.ts +1 -3
package/README.md CHANGED
@@ -19,8 +19,11 @@ bunx create-questpie my-app
19
19
  | Flag | Description |
20
20
  | ----------------------- | ------------------------------------------- |
21
21
  | `-t, --template <name>` | Template to use (default: `tanstack-start`) |
22
+ | `--database <name>` | Database name (default: derived from name) |
22
23
  | `--no-install` | Skip dependency installation |
23
24
  | `--no-git` | Skip git initialization |
25
+ | `--no-skills` | Skip project-local QUESTPIE agent skills |
26
+ | `--no-generate` | Skip post-install QUESTPIE codegen |
24
27
 
25
28
  ## Templates
26
29
 
@@ -69,16 +72,17 @@ my-app/
69
72
 
70
73
  ```bash
71
74
  cd my-app
72
- cp .env.example .env # Set DATABASE_URL
73
- bun questpie migrate # Run migrations
74
- bun dev # Start dev server
75
+ docker compose up -d
76
+ bun run scaffold:verify # Regenerate codegen + type-check
77
+ bun run migrate # Run migrations
78
+ bun run dev # Start dev server
75
79
 
76
80
  # Add entities (auto-runs codegen)
77
- bun questpie add collection products
78
- bun questpie add global marketing
81
+ bunx questpie add collection products
82
+ bunx questpie add global marketing
79
83
  ```
80
84
 
81
- `questpie add` runs codegen automatically. Use `bunx questpie generate` only when you create files manually.
85
+ The scaffolder creates `.env` from `.env.example`, installs project-local QUESTPIE agent skills under `.agents/skills`, and runs `questpie:generate` after dependency installation by default. `questpie add` runs codegen automatically. Use `bun run questpie:generate` only when you create files manually.
82
86
 
83
87
  ## Documentation
84
88
 
package/dist/index.mjs CHANGED
@@ -2,9 +2,9 @@
2
2
  import { Command } from "commander";
3
3
  import * as p from "@clack/prompts";
4
4
  import pc from "picocolors";
5
- import { execSync } from "node:child_process";
5
+ import { execFileSync, execSync } from "node:child_process";
6
6
  import { existsSync } from "node:fs";
7
- import { cp, readFile, readdir, rename, writeFile } from "node:fs/promises";
7
+ import { cp, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
8
8
  import { join, resolve } from "node:path";
9
9
 
10
10
  //#region src/templates.ts
@@ -62,6 +62,12 @@ function installDependencies(cwd, packageManager) {
62
62
  stdio: "inherit"
63
63
  });
64
64
  }
65
+ function runPackageScript(cwd, packageManager, script) {
66
+ execFileSync(packageManager === "npm" ? "npm" : packageManager, packageManager === "npm" ? ["run", script] : ["run", script], {
67
+ cwd,
68
+ stdio: "inherit"
69
+ });
70
+ }
65
71
  function detectPackageManager() {
66
72
  const userAgent = process.env.npm_config_user_agent;
67
73
  if (userAgent) {
@@ -81,10 +87,26 @@ const label = {
81
87
  //#endregion
82
88
  //#region src/prompts.ts
83
89
  async function runPrompts(args) {
90
+ if (!Boolean(process.stdin.isTTY && process.stdout.isTTY)) {
91
+ if (!args.projectName) throw new Error("Project name is required in non-interactive mode.");
92
+ if (!isValidPackageName(args.projectName)) throw new Error("Invalid package name (use lowercase, hyphens, no spaces).");
93
+ return {
94
+ projectName: args.projectName,
95
+ templateId: args.templateId ?? templates[0].id,
96
+ databaseName: args.databaseName ?? toDbName(args.projectName),
97
+ installDeps: args.installDeps ?? true,
98
+ initGit: args.initGit ?? true,
99
+ installSkills: args.installSkills ?? true,
100
+ runCodegen: args.runCodegen ?? true
101
+ };
102
+ }
84
103
  p.intro(pc.bgCyan(pc.black(" QUESTPIE — Create a new project ")));
85
104
  const questions = await p.group({
86
105
  projectName: () => {
87
- if (args.projectName) return Promise.resolve(args.projectName);
106
+ if (args.projectName) {
107
+ if (!isValidPackageName(args.projectName)) throw new Error("Invalid package name (use lowercase, hyphens, no spaces).");
108
+ return Promise.resolve(args.projectName);
109
+ }
88
110
  return p.text({
89
111
  message: "Project name",
90
112
  placeholder: "my-questpie-app",
@@ -128,6 +150,21 @@ async function runPrompts(args) {
128
150
  message: "Initialize git repository?",
129
151
  initialValue: true
130
152
  });
153
+ },
154
+ installSkills: () => {
155
+ if (args.installSkills !== void 0) return Promise.resolve(args.installSkills);
156
+ return p.confirm({
157
+ message: "Install QUESTPIE agent skills into the project?",
158
+ initialValue: true
159
+ });
160
+ },
161
+ runCodegen: ({ results }) => {
162
+ if (args.runCodegen !== void 0) return Promise.resolve(args.runCodegen);
163
+ if (args.installDeps === false || results.installDeps === false) return Promise.resolve(false);
164
+ return p.confirm({
165
+ message: "Run QUESTPIE codegen after installing dependencies?",
166
+ initialValue: true
167
+ });
131
168
  }
132
169
  }, { onCancel: () => {
133
170
  p.cancel("Operation cancelled.");
@@ -138,7 +175,9 @@ async function runPrompts(args) {
138
175
  templateId: questions.templateId,
139
176
  databaseName: questions.databaseName,
140
177
  installDeps: questions.installDeps,
141
- initGit: questions.initGit
178
+ initGit: questions.initGit,
179
+ installSkills: questions.installSkills,
180
+ runCodegen: questions.runCodegen
142
181
  };
143
182
  }
144
183
 
@@ -179,7 +218,7 @@ function isTextFile(filename) {
179
218
  return TEXT_EXTENSIONS.has(ext);
180
219
  }
181
220
  async function replaceInFile(filePath, vars) {
182
- const content = await readFile(filePath, "utf-8");
221
+ const content = (await readFile(filePath)).toString("utf-8");
183
222
  const replaced = content.replace(TEMPLATE_VAR_REGEX, (match, key) => {
184
223
  return key in vars ? vars[key] : match;
185
224
  });
@@ -203,6 +242,50 @@ async function renameEnvExample(targetDir) {
203
242
  const envPath = join(targetDir, "env.example");
204
243
  if (existsSync(envPath)) await rename(envPath, join(targetDir, ".env.example"));
205
244
  }
245
+ async function createLocalEnv(targetDir) {
246
+ const examplePath = join(targetDir, ".env.example");
247
+ const envPath = join(targetDir, ".env");
248
+ if (existsSync(examplePath) && !existsSync(envPath)) await cp(examplePath, envPath);
249
+ }
250
+ function getSkillSources(targetDir) {
251
+ return [{
252
+ name: "questpie",
253
+ candidates: [
254
+ resolve(import.meta.dirname, "..", "skills", "questpie"),
255
+ join(targetDir, "node_modules", "questpie", "skills", "questpie"),
256
+ resolve(import.meta.dirname, "..", "..", "questpie", "skills", "questpie"),
257
+ resolve(import.meta.dirname, "..", "..", "..", "skills", "questpie")
258
+ ]
259
+ }, {
260
+ name: "questpie-admin",
261
+ candidates: [
262
+ resolve(import.meta.dirname, "..", "skills", "questpie-admin"),
263
+ join(targetDir, "node_modules", "@questpie", "admin", "skills", "questpie-admin"),
264
+ resolve(import.meta.dirname, "..", "..", "admin", "skills", "questpie-admin"),
265
+ resolve(import.meta.dirname, "..", "..", "..", "skills", "questpie-admin")
266
+ ]
267
+ }];
268
+ }
269
+ async function installProjectSkills(targetDir) {
270
+ const installed = [];
271
+ const skillsDir = join(targetDir, ".agents", "skills");
272
+ for (const skill of getSkillSources(targetDir)) {
273
+ const source = skill.candidates.find((candidate) => existsSync(candidate));
274
+ if (!source) continue;
275
+ const destination = join(skillsDir, skill.name);
276
+ await mkdir(skillsDir, { recursive: true });
277
+ await rm(destination, {
278
+ recursive: true,
279
+ force: true
280
+ });
281
+ await cp(source, destination, {
282
+ recursive: true,
283
+ dereference: true
284
+ });
285
+ installed.push(skill.name);
286
+ }
287
+ return installed;
288
+ }
206
289
  async function scaffold(options) {
207
290
  const spinner = p.spinner();
208
291
  const targetDir = resolve(process.cwd(), options.projectName);
@@ -214,7 +297,8 @@ async function scaffold(options) {
214
297
  projectName: options.projectName,
215
298
  databaseName: options.databaseName,
216
299
  databaseUser: options.databaseName,
217
- databasePassword: generatePassword()
300
+ databasePassword: generatePassword(),
301
+ authSecret: generatePassword(48)
218
302
  };
219
303
  spinner.start("Copying template files");
220
304
  const templateDir = join(getTemplatesDir(), options.templateId);
@@ -228,17 +312,37 @@ async function scaffold(options) {
228
312
  await renameGitignore(targetDir);
229
313
  await renameEnvExample(targetDir);
230
314
  await processDirectory(targetDir, vars);
315
+ await createLocalEnv(targetDir);
231
316
  spinner.stop(label.success("Processed template variables"));
317
+ const pm = detectPackageManager();
232
318
  if (options.installDeps) {
233
- const pm$1 = detectPackageManager();
234
- spinner.start(`Installing dependencies with ${pm$1}`);
319
+ spinner.start(`Installing dependencies with ${pm}`);
235
320
  try {
236
- installDependencies(targetDir, pm$1);
321
+ installDependencies(targetDir, pm);
237
322
  spinner.stop(label.success("Installed dependencies"));
238
323
  } catch {
239
324
  spinner.stop(label.warn("Failed to install dependencies — run manually"));
240
325
  }
241
326
  }
327
+ if (options.installSkills) {
328
+ spinner.start("Installing QUESTPIE agent skills");
329
+ try {
330
+ const installedSkills = await installProjectSkills(targetDir);
331
+ if (installedSkills.length > 0) spinner.stop(label.success(`Installed skills: ${installedSkills.join(", ")}`));
332
+ else spinner.stop(label.warn("Could not find packaged skills — run `bunx skill add questpie/questpie` manually if available"));
333
+ } catch {
334
+ spinner.stop(label.warn("Failed to install skills — continuing"));
335
+ }
336
+ }
337
+ if (options.installDeps && options.runCodegen) {
338
+ spinner.start("Generating QUESTPIE app");
339
+ try {
340
+ runPackageScript(targetDir, pm, "scaffold:generate");
341
+ spinner.stop(label.success("Generated QUESTPIE app"));
342
+ } catch {
343
+ spinner.stop(label.warn("Failed to run codegen — run manually"));
344
+ }
345
+ }
242
346
  if (options.initGit && isGitInstalled()) {
243
347
  spinner.start("Initializing git repository");
244
348
  try {
@@ -248,43 +352,54 @@ async function scaffold(options) {
248
352
  spinner.stop(label.warn("Failed to initialize git — run manually"));
249
353
  }
250
354
  }
251
- const pm = detectPackageManager();
252
- const runCmd = pm === "npm" ? "npm run" : pm;
355
+ const runScript = (script) => pm === "npm" ? `npm run ${script}` : `${pm} run ${script}`;
356
+ const questpieBin = pm === "npm" ? "npx questpie" : "bunx questpie";
253
357
  p.note([
254
358
  `cd ${options.projectName}`,
255
359
  "",
360
+ "# Review the generated environment",
361
+ "# .env has already been created from .env.example",
362
+ "",
256
363
  "# Start PostgreSQL",
257
364
  "docker compose up -d",
258
365
  "",
366
+ "# Regenerate and type-check the scaffold",
367
+ runScript("scaffold:verify"),
368
+ "",
259
369
  "# Run migrations",
260
- `${runCmd} questpie migrate`,
370
+ runScript("migrate"),
261
371
  "",
262
372
  "# Start dev server",
263
- `${runCmd} dev`,
373
+ runScript("dev"),
264
374
  "",
265
375
  "# Add entities (auto-runs codegen)",
266
- `${runCmd} questpie add collection products`,
267
- `${runCmd} questpie add global marketing`,
376
+ `${questpieBin} add collection products`,
377
+ `${questpieBin} add global marketing`,
268
378
  "",
269
379
  "# If you create files manually",
270
- "bunx questpie generate"
380
+ runScript("questpie:generate")
271
381
  ].join("\n"), "Next steps");
272
382
  p.outro(`${label.success("Done!")} Happy building with QUESTPIE!`);
273
383
  }
274
384
 
275
385
  //#endregion
276
386
  //#region src/index.ts
277
- new Command().name("create-questpie").description("Create a new QUESTPIE project").version("0.1.0").argument("[project-name]", "Name of the project").option("-t, --template <name>", "Template to use (default: tanstack-start)").option("--no-install", "Skip dependency installation").option("--no-git", "Skip git initialization").action(async (projectName, opts) => {
278
- if (opts.template && !getTemplate(opts.template)) {
279
- console.error(`Unknown template: ${opts.template}`);
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) => {
388
+ try {
389
+ if (opts.template && !getTemplate(opts.template)) throw new Error(`Unknown template: ${opts.template}`);
390
+ await scaffold(await runPrompts({
391
+ projectName,
392
+ templateId: opts.template,
393
+ databaseName: opts.database,
394
+ installDeps: opts.install === false ? false : void 0,
395
+ initGit: opts.git === false ? false : void 0,
396
+ installSkills: opts.skills === false ? false : void 0,
397
+ runCodegen: opts.generate === false ? false : void 0
398
+ }));
399
+ } catch (error) {
400
+ console.error(error instanceof Error ? error.message : String(error));
280
401
  process.exit(1);
281
402
  }
282
- await scaffold(await runPrompts({
283
- projectName,
284
- templateId: opts.template,
285
- installDeps: opts.install === false ? false : void 0,
286
- initGit: opts.git === false ? false : void 0
287
- }));
288
403
  }).parse();
289
404
 
290
405
  //#endregion
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-questpie",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Create a new QUESTPIE project",
5
5
  "keywords": [
6
6
  "create",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "files": [
22
22
  "dist",
23
- "templates"
23
+ "templates",
24
+ "skills"
24
25
  ],
25
26
  "type": "module",
26
27
  "publishConfig": {
@@ -29,7 +30,8 @@
29
30
  "scripts": {
30
31
  "build": "tsdown",
31
32
  "dev": "tsdown --watch",
32
- "check-types": "tsc --noEmit"
33
+ "check-types": "tsc --noEmit",
34
+ "test": "bun test test"
33
35
  },
34
36
  "dependencies": {
35
37
  "@clack/prompts": "^0.10.0",