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.
- package/README.md +10 -6
- package/dist/index.mjs +140 -25
- package/package.json +5 -3
- package/skills/questpie/AGENTS.md +2664 -0
- package/skills/questpie/SKILL.md +181 -0
- package/skills/questpie/references/auth.md +121 -0
- package/skills/questpie/references/business-logic.md +550 -0
- package/skills/questpie/references/codegen-plugin-api.md +382 -0
- package/skills/questpie/references/crud-api.md +378 -0
- package/skills/questpie/references/data-modeling.md +489 -0
- package/skills/questpie/references/extend.md +493 -0
- package/skills/questpie/references/field-types.md +386 -0
- package/skills/questpie/references/infrastructure-adapters.md +545 -0
- package/skills/questpie/references/multi-tenancy.md +364 -0
- package/skills/questpie/references/production.md +475 -0
- package/skills/questpie/references/query-operators.md +125 -0
- package/skills/questpie/references/quickstart.md +549 -0
- package/skills/questpie/references/rules.md +327 -0
- package/skills/questpie/references/tanstack-query.md +520 -0
- package/skills/questpie-admin/AGENTS.md +1442 -0
- package/skills/questpie-admin/SKILL.md +410 -0
- package/skills/questpie-admin/references/blocks.md +307 -0
- package/skills/questpie-admin/references/custom-ui.md +305 -0
- package/skills/questpie-admin/references/views.md +433 -0
- package/templates/tanstack-start/AGENTS.md +71 -62
- package/templates/tanstack-start/CLAUDE.md +26 -23
- package/templates/tanstack-start/README.md +32 -20
- package/templates/tanstack-start/env.example +1 -1
- package/templates/tanstack-start/package.json +20 -6
- package/templates/tanstack-start/src/lib/client.ts +2 -2
- package/templates/tanstack-start/src/lib/env.ts +1 -1
- package/templates/tanstack-start/src/questpie/admin/.generated/client.ts +13 -0
- package/templates/tanstack-start/src/questpie/admin/modules.ts +1 -0
- package/templates/tanstack-start/src/questpie/server/.generated/factories.ts +117 -241
- package/templates/tanstack-start/src/questpie/server/.generated/index.ts +129 -81
- package/templates/tanstack-start/src/questpie/server/app.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/config/admin.ts +27 -30
- package/templates/tanstack-start/src/questpie/server/globals/site-settings.global.ts +1 -1
- package/templates/tanstack-start/src/questpie/server/questpie.config.ts +1 -1
- package/templates/tanstack-start/src/routeTree.gen.ts +138 -0
- package/templates/tanstack-start/src/routes/__root.tsx +0 -2
- package/templates/tanstack-start/src/routes/admin.tsx +8 -1
- package/templates/tanstack-start/src/tanstack-start.d.ts +1 -0
- package/templates/tanstack-start/src/vite-env.d.ts +1 -0
- 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
|
-
|
|
73
|
-
bun
|
|
74
|
-
bun
|
|
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
|
-
|
|
78
|
-
|
|
81
|
+
bunx questpie add collection products
|
|
82
|
+
bunx questpie add global marketing
|
|
79
83
|
```
|
|
80
84
|
|
|
81
|
-
`questpie add` runs codegen automatically. Use `
|
|
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)
|
|
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
|
|
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
|
-
|
|
234
|
-
spinner.start(`Installing dependencies with ${pm$1}`);
|
|
319
|
+
spinner.start(`Installing dependencies with ${pm}`);
|
|
235
320
|
try {
|
|
236
|
-
installDependencies(targetDir, pm
|
|
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
|
|
252
|
-
const
|
|
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
|
-
|
|
370
|
+
runScript("migrate"),
|
|
261
371
|
"",
|
|
262
372
|
"# Start dev server",
|
|
263
|
-
|
|
373
|
+
runScript("dev"),
|
|
264
374
|
"",
|
|
265
375
|
"# Add entities (auto-runs codegen)",
|
|
266
|
-
`${
|
|
267
|
-
`${
|
|
376
|
+
`${questpieBin} add collection products`,
|
|
377
|
+
`${questpieBin} add global marketing`,
|
|
268
378
|
"",
|
|
269
379
|
"# If you create files manually",
|
|
270
|
-
"
|
|
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
|
|
278
|
-
|
|
279
|
-
|
|
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.
|
|
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",
|