create-githat-app 1.0.3 → 1.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.
- package/dist/cli.js +271 -89
- package/package.json +1 -1
- package/templates/fullstack/apps-api-express/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-express/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-express/package.json.hbs +24 -0
- package/templates/fullstack/apps-api-express/src/index.ts.hbs +41 -0
- package/templates/fullstack/apps-api-express/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-express/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-express/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-api-fastify/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-fastify/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-fastify/package.json.hbs +22 -0
- package/templates/fullstack/apps-api-fastify/src/index.ts.hbs +28 -0
- package/templates/fullstack/apps-api-fastify/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-fastify/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-fastify/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-api-hono/.env.example.hbs +6 -0
- package/templates/fullstack/apps-api-hono/.env.local.hbs +6 -0
- package/templates/fullstack/apps-api-hono/package.json.hbs +22 -0
- package/templates/fullstack/apps-api-hono/src/index.ts.hbs +35 -0
- package/templates/fullstack/apps-api-hono/src/routes/health.ts.hbs +11 -0
- package/templates/fullstack/apps-api-hono/src/routes/users.ts.hbs +43 -0
- package/templates/fullstack/apps-api-hono/tsconfig.json.hbs +16 -0
- package/templates/fullstack/apps-web-nextjs/.env.example.hbs +5 -0
- package/templates/fullstack/apps-web-nextjs/.env.local.hbs +5 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/forgot-password/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/reset-password/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-in/page.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/sign-up/page.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/(auth)/verify-email/page.tsx.hbs +11 -0
- package/templates/fullstack/apps-web-nextjs/app/dashboard/layout.tsx.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/app/dashboard/page.tsx.hbs +27 -0
- package/templates/fullstack/apps-web-nextjs/app/globals.css.hbs +20 -0
- package/templates/fullstack/apps-web-nextjs/app/layout.tsx.hbs +26 -0
- package/templates/fullstack/apps-web-nextjs/app/page.tsx.hbs +18 -0
- package/templates/fullstack/apps-web-nextjs/next.config.ts.hbs +15 -0
- package/templates/fullstack/apps-web-nextjs/package.json.hbs +33 -0
- package/templates/fullstack/apps-web-nextjs/postcss.config.mjs.hbs +9 -0
- package/templates/fullstack/apps-web-nextjs/tsconfig.json.hbs +21 -0
- package/templates/fullstack/root/.gitignore.hbs +42 -0
- package/templates/fullstack/root/githat.yaml.hbs +17 -0
- package/templates/fullstack/root/package.json.hbs +15 -0
- package/templates/fullstack/root/turbo.json.hbs +20 -0
package/dist/cli.js
CHANGED
|
@@ -2,7 +2,7 @@ import { createRequire } from 'module'; const require = createRequire(import.met
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import { Command as Command7 } from "commander";
|
|
5
|
-
import * as
|
|
5
|
+
import * as p11 from "@clack/prompts";
|
|
6
6
|
import chalk9 from "chalk";
|
|
7
7
|
|
|
8
8
|
// src/utils/ascii.ts
|
|
@@ -11,7 +11,7 @@ import gradient from "gradient-string";
|
|
|
11
11
|
import chalk from "chalk";
|
|
12
12
|
|
|
13
13
|
// src/constants.ts
|
|
14
|
-
var VERSION = "1.0.
|
|
14
|
+
var VERSION = "1.0.4";
|
|
15
15
|
var DEFAULT_API_URL = "https://api.githat.io";
|
|
16
16
|
var DASHBOARD_URL = "https://githat.io/dashboard/apps";
|
|
17
17
|
var BRAND_COLORS = ["#7c3aed", "#6366f1", "#8b5cf6"];
|
|
@@ -73,6 +73,48 @@ var DEPS = {
|
|
|
73
73
|
"drizzle-sqlite": {
|
|
74
74
|
dependencies: { "drizzle-orm": "^0.39.0", "better-sqlite3": "^11.0.0" },
|
|
75
75
|
devDependencies: { "drizzle-kit": "^0.30.0", "@types/better-sqlite3": "^7.6.0" }
|
|
76
|
+
},
|
|
77
|
+
// Backend frameworks for fullstack
|
|
78
|
+
hono: {
|
|
79
|
+
dependencies: {
|
|
80
|
+
hono: "^4.6.0",
|
|
81
|
+
"@hono/node-server": "^1.13.0"
|
|
82
|
+
},
|
|
83
|
+
devDependencies: {
|
|
84
|
+
typescript: "^5.9.0",
|
|
85
|
+
"@types/node": "^22.0.0",
|
|
86
|
+
tsx: "^4.19.0"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
express: {
|
|
90
|
+
dependencies: {
|
|
91
|
+
express: "^5.0.0",
|
|
92
|
+
cors: "^2.8.5"
|
|
93
|
+
},
|
|
94
|
+
devDependencies: {
|
|
95
|
+
typescript: "^5.9.0",
|
|
96
|
+
"@types/node": "^22.0.0",
|
|
97
|
+
"@types/express": "^5.0.0",
|
|
98
|
+
"@types/cors": "^2.8.17",
|
|
99
|
+
tsx: "^4.19.0"
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
fastify: {
|
|
103
|
+
dependencies: {
|
|
104
|
+
fastify: "^5.2.0",
|
|
105
|
+
"@fastify/cors": "^10.0.0"
|
|
106
|
+
},
|
|
107
|
+
devDependencies: {
|
|
108
|
+
typescript: "^5.9.0",
|
|
109
|
+
"@types/node": "^22.0.0",
|
|
110
|
+
tsx: "^4.19.0"
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
// Turborepo for fullstack monorepo
|
|
114
|
+
turbo: {
|
|
115
|
+
devDependencies: {
|
|
116
|
+
turbo: "^2.3.0"
|
|
117
|
+
}
|
|
76
118
|
}
|
|
77
119
|
};
|
|
78
120
|
|
|
@@ -123,35 +165,59 @@ function sectionHeader(title) {
|
|
|
123
165
|
console.log(dim(` \u2500\u2500\u2500 ${title} ${"\u2500".repeat(lineLen)}`));
|
|
124
166
|
console.log("");
|
|
125
167
|
}
|
|
126
|
-
function displaySuccess(projectName, packageManager, framework, hasPublishableKey = true) {
|
|
168
|
+
function displaySuccess(projectName, packageManager, framework, hasPublishableKey = true, isFullstack = false) {
|
|
127
169
|
const devCmd = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
|
|
128
170
|
const port = framework === "react-vite" ? "5173" : "3000";
|
|
129
171
|
console.log("");
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
172
|
+
if (isFullstack) {
|
|
173
|
+
drawBox([
|
|
174
|
+
`${violet("\u2726")} Your GitHat fullstack app is ready!`,
|
|
175
|
+
"",
|
|
176
|
+
`${violet("$")} cd ${projectName}`,
|
|
177
|
+
`${violet("$")} ${devCmd}`,
|
|
178
|
+
"",
|
|
179
|
+
dim(`\u2192 Web: http://localhost:3000`),
|
|
180
|
+
dim(`\u2192 API: http://localhost:3001`),
|
|
181
|
+
"",
|
|
182
|
+
chalk.bold("Structure"),
|
|
183
|
+
`${violet("apps/web")} Next.js frontend`,
|
|
184
|
+
`${violet("apps/api")} API backend`,
|
|
185
|
+
`${violet("packages/")} Shared code`,
|
|
186
|
+
"",
|
|
187
|
+
...hasPublishableKey ? [] : [
|
|
188
|
+
chalk.yellow("No key configured \u2014 auth works on localhost."),
|
|
189
|
+
`For production: ${violet("githat.io/dashboard/apps")}`,
|
|
190
|
+
""
|
|
191
|
+
],
|
|
192
|
+
dim("Docs \u2192 https://githat.io/docs/sdk")
|
|
193
|
+
]);
|
|
194
|
+
} else {
|
|
195
|
+
drawBox([
|
|
196
|
+
`${violet("\u2726")} Your GitHat app is ready!`,
|
|
197
|
+
"",
|
|
198
|
+
`${violet("$")} cd ${projectName}`,
|
|
199
|
+
`${violet("$")} ${devCmd}`,
|
|
200
|
+
"",
|
|
201
|
+
dim(`\u2192 http://localhost:${port}`),
|
|
202
|
+
"",
|
|
203
|
+
chalk.bold("Routes"),
|
|
204
|
+
`${violet("/sign-in")} Sign in`,
|
|
205
|
+
`${violet("/sign-up")} Create account`,
|
|
206
|
+
`${violet("/dashboard")} Protected dashboard`,
|
|
207
|
+
"",
|
|
208
|
+
...hasPublishableKey ? [] : [
|
|
209
|
+
chalk.yellow("No key configured \u2014 auth works on localhost."),
|
|
210
|
+
`For production: ${violet("githat.io/dashboard/apps")}`,
|
|
211
|
+
""
|
|
212
|
+
],
|
|
213
|
+
dim("Docs \u2192 https://githat.io/docs/sdk")
|
|
214
|
+
]);
|
|
215
|
+
}
|
|
150
216
|
console.log("");
|
|
151
217
|
}
|
|
152
218
|
|
|
153
219
|
// src/prompts/index.ts
|
|
154
|
-
import * as
|
|
220
|
+
import * as p8 from "@clack/prompts";
|
|
155
221
|
|
|
156
222
|
// src/prompts/project.ts
|
|
157
223
|
import * as p from "@clack/prompts";
|
|
@@ -204,8 +270,41 @@ async function promptProject(initialName) {
|
|
|
204
270
|
};
|
|
205
271
|
}
|
|
206
272
|
|
|
207
|
-
// src/prompts/
|
|
273
|
+
// src/prompts/project-type.ts
|
|
208
274
|
import * as p2 from "@clack/prompts";
|
|
275
|
+
async function promptProjectType() {
|
|
276
|
+
const answers = await p2.group(
|
|
277
|
+
{
|
|
278
|
+
projectType: () => p2.select({
|
|
279
|
+
message: "Project type",
|
|
280
|
+
options: [
|
|
281
|
+
{
|
|
282
|
+
value: "frontend",
|
|
283
|
+
label: "Frontend only",
|
|
284
|
+
hint: "Next.js with API routes"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
value: "fullstack",
|
|
288
|
+
label: "Fullstack",
|
|
289
|
+
hint: "Next.js + separate API (Turborepo)"
|
|
290
|
+
}
|
|
291
|
+
]
|
|
292
|
+
})
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
onCancel: () => {
|
|
296
|
+
p2.cancel("Setup cancelled.");
|
|
297
|
+
process.exit(0);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
);
|
|
301
|
+
return {
|
|
302
|
+
projectType: answers.projectType
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/prompts/framework.ts
|
|
307
|
+
import * as p3 from "@clack/prompts";
|
|
209
308
|
|
|
210
309
|
// src/utils/package-manager.ts
|
|
211
310
|
function detectPackageManager() {
|
|
@@ -228,16 +327,16 @@ function getInstallCommand(pm) {
|
|
|
228
327
|
// src/prompts/framework.ts
|
|
229
328
|
async function promptFramework(typescriptOverride) {
|
|
230
329
|
const packageManager = detectPackageManager();
|
|
231
|
-
const answers = await
|
|
330
|
+
const answers = await p3.group(
|
|
232
331
|
{
|
|
233
|
-
framework: () =>
|
|
332
|
+
framework: () => p3.select({
|
|
234
333
|
message: "Framework",
|
|
235
334
|
options: [
|
|
236
335
|
{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" },
|
|
237
336
|
{ value: "react-vite", label: "React 19 + Vite 7", hint: "SPA \xB7 client-side routing" }
|
|
238
337
|
]
|
|
239
338
|
}),
|
|
240
|
-
typescript: () => typescriptOverride !== void 0 ? Promise.resolve(typescriptOverride) :
|
|
339
|
+
typescript: () => typescriptOverride !== void 0 ? Promise.resolve(typescriptOverride) : p3.select({
|
|
241
340
|
message: "Language",
|
|
242
341
|
options: [
|
|
243
342
|
{ value: true, label: "TypeScript", hint: "recommended" },
|
|
@@ -247,7 +346,7 @@ async function promptFramework(typescriptOverride) {
|
|
|
247
346
|
},
|
|
248
347
|
{
|
|
249
348
|
onCancel: () => {
|
|
250
|
-
|
|
349
|
+
p3.cancel("Setup cancelled.");
|
|
251
350
|
process.exit(0);
|
|
252
351
|
}
|
|
253
352
|
}
|
|
@@ -259,9 +358,47 @@ async function promptFramework(typescriptOverride) {
|
|
|
259
358
|
};
|
|
260
359
|
}
|
|
261
360
|
|
|
361
|
+
// src/prompts/backend.ts
|
|
362
|
+
import * as p4 from "@clack/prompts";
|
|
363
|
+
async function promptBackend() {
|
|
364
|
+
const answers = await p4.group(
|
|
365
|
+
{
|
|
366
|
+
backendFramework: () => p4.select({
|
|
367
|
+
message: "Backend framework",
|
|
368
|
+
options: [
|
|
369
|
+
{
|
|
370
|
+
value: "hono",
|
|
371
|
+
label: "Hono",
|
|
372
|
+
hint: "recommended \xB7 serverless-native \xB7 14KB"
|
|
373
|
+
},
|
|
374
|
+
{
|
|
375
|
+
value: "express",
|
|
376
|
+
label: "Express",
|
|
377
|
+
hint: "classic Node.js \xB7 large ecosystem"
|
|
378
|
+
},
|
|
379
|
+
{
|
|
380
|
+
value: "fastify",
|
|
381
|
+
label: "Fastify",
|
|
382
|
+
hint: "high performance \xB7 schema validation"
|
|
383
|
+
}
|
|
384
|
+
]
|
|
385
|
+
})
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
onCancel: () => {
|
|
389
|
+
p4.cancel("Setup cancelled.");
|
|
390
|
+
process.exit(0);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
return {
|
|
395
|
+
backendFramework: answers.backendFramework
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
262
399
|
// src/prompts/githat.ts
|
|
263
400
|
import { execSync } from "child_process";
|
|
264
|
-
import * as
|
|
401
|
+
import * as p5 from "@clack/prompts";
|
|
265
402
|
function openBrowser(url) {
|
|
266
403
|
try {
|
|
267
404
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
@@ -272,7 +409,7 @@ function openBrowser(url) {
|
|
|
272
409
|
async function promptGitHat(existingKey) {
|
|
273
410
|
let publishableKey = existingKey || "";
|
|
274
411
|
if (!publishableKey) {
|
|
275
|
-
const connectChoice = await
|
|
412
|
+
const connectChoice = await p5.select({
|
|
276
413
|
message: "Connect to GitHat",
|
|
277
414
|
options: [
|
|
278
415
|
{ value: "browser", label: "Sign in with browser", hint: "opens githat.io \u2014 recommended" },
|
|
@@ -280,41 +417,41 @@ async function promptGitHat(existingKey) {
|
|
|
280
417
|
{ value: "skip", label: "Skip for now", hint: "add key to .env later" }
|
|
281
418
|
]
|
|
282
419
|
});
|
|
283
|
-
if (
|
|
284
|
-
|
|
420
|
+
if (p5.isCancel(connectChoice)) {
|
|
421
|
+
p5.cancel("Setup cancelled.");
|
|
285
422
|
process.exit(0);
|
|
286
423
|
}
|
|
287
424
|
if (connectChoice === "browser") {
|
|
288
|
-
|
|
425
|
+
p5.log.step("Opening githat.io in your browser...");
|
|
289
426
|
openBrowser("https://githat.io/sign-up");
|
|
290
|
-
|
|
291
|
-
const pastedKey = await
|
|
427
|
+
p5.log.info("Sign up (or sign in), then go to Dashboard \u2192 Apps to copy your key.");
|
|
428
|
+
const pastedKey = await p5.text({
|
|
292
429
|
message: "Paste your publishable key",
|
|
293
430
|
placeholder: "pk_live_...",
|
|
294
431
|
validate: validatePublishableKey
|
|
295
432
|
});
|
|
296
|
-
if (
|
|
297
|
-
|
|
433
|
+
if (p5.isCancel(pastedKey)) {
|
|
434
|
+
p5.cancel("Setup cancelled.");
|
|
298
435
|
process.exit(0);
|
|
299
436
|
}
|
|
300
437
|
publishableKey = pastedKey || "";
|
|
301
438
|
} else if (connectChoice === "paste") {
|
|
302
|
-
const pastedKey = await
|
|
439
|
+
const pastedKey = await p5.text({
|
|
303
440
|
message: "Publishable key",
|
|
304
441
|
placeholder: `pk_live_... (get one at ${DASHBOARD_URL})`,
|
|
305
442
|
validate: validatePublishableKey
|
|
306
443
|
});
|
|
307
|
-
if (
|
|
308
|
-
|
|
444
|
+
if (p5.isCancel(pastedKey)) {
|
|
445
|
+
p5.cancel("Setup cancelled.");
|
|
309
446
|
process.exit(0);
|
|
310
447
|
}
|
|
311
448
|
publishableKey = pastedKey || "";
|
|
312
449
|
} else if (connectChoice === "skip") {
|
|
313
|
-
|
|
314
|
-
|
|
450
|
+
p5.log.info("Auth works on localhost without a key (CORS bypass for development).");
|
|
451
|
+
p5.log.info("Sign up at githat.io \u2014 a publishable key is auto-created for you.");
|
|
315
452
|
}
|
|
316
453
|
}
|
|
317
|
-
const authFeatures = await
|
|
454
|
+
const authFeatures = await p5.multiselect({
|
|
318
455
|
message: "Auth features",
|
|
319
456
|
options: [
|
|
320
457
|
{ value: "forgot-password", label: "Forgot password", hint: "reset via email" },
|
|
@@ -326,8 +463,8 @@ async function promptGitHat(existingKey) {
|
|
|
326
463
|
initialValues: ["forgot-password"],
|
|
327
464
|
required: false
|
|
328
465
|
});
|
|
329
|
-
if (
|
|
330
|
-
|
|
466
|
+
if (p5.isCancel(authFeatures)) {
|
|
467
|
+
p5.cancel("Setup cancelled.");
|
|
331
468
|
process.exit(0);
|
|
332
469
|
}
|
|
333
470
|
return {
|
|
@@ -338,11 +475,11 @@ async function promptGitHat(existingKey) {
|
|
|
338
475
|
}
|
|
339
476
|
|
|
340
477
|
// src/prompts/features.ts
|
|
341
|
-
import * as
|
|
478
|
+
import * as p6 from "@clack/prompts";
|
|
342
479
|
async function promptFeatures() {
|
|
343
|
-
const answers = await
|
|
480
|
+
const answers = await p6.group(
|
|
344
481
|
{
|
|
345
|
-
databaseChoice: () =>
|
|
482
|
+
databaseChoice: () => p6.select({
|
|
346
483
|
message: "Database",
|
|
347
484
|
options: [
|
|
348
485
|
{ value: "none", label: "None", hint: "use GitHat backend directly" },
|
|
@@ -352,12 +489,12 @@ async function promptFeatures() {
|
|
|
352
489
|
{ value: "drizzle-sqlite", label: "Drizzle + SQLite" }
|
|
353
490
|
]
|
|
354
491
|
}),
|
|
355
|
-
useTailwind: () =>
|
|
356
|
-
includeDashboard: () =>
|
|
492
|
+
useTailwind: () => p6.confirm({ message: "Tailwind CSS?", initialValue: true }),
|
|
493
|
+
includeDashboard: () => p6.confirm({ message: "Include dashboard?", initialValue: true })
|
|
357
494
|
},
|
|
358
495
|
{
|
|
359
496
|
onCancel: () => {
|
|
360
|
-
|
|
497
|
+
p6.cancel("Setup cancelled.");
|
|
361
498
|
process.exit(0);
|
|
362
499
|
}
|
|
363
500
|
}
|
|
@@ -371,14 +508,14 @@ async function promptFeatures() {
|
|
|
371
508
|
}
|
|
372
509
|
|
|
373
510
|
// src/prompts/finalize.ts
|
|
374
|
-
import * as
|
|
511
|
+
import * as p7 from "@clack/prompts";
|
|
375
512
|
async function promptFinalize() {
|
|
376
|
-
const installDeps = await
|
|
513
|
+
const installDeps = await p7.confirm({
|
|
377
514
|
message: "Install dependencies?",
|
|
378
515
|
initialValue: true
|
|
379
516
|
});
|
|
380
|
-
if (
|
|
381
|
-
|
|
517
|
+
if (p7.isCancel(installDeps)) {
|
|
518
|
+
p7.cancel("Setup cancelled.");
|
|
382
519
|
process.exit(0);
|
|
383
520
|
}
|
|
384
521
|
return {
|
|
@@ -397,6 +534,7 @@ function getDefaults(projectName, publishableKey, typescript) {
|
|
|
397
534
|
projectName,
|
|
398
535
|
businessName: displayName,
|
|
399
536
|
description: `${displayName} \u2014 Built with GitHat`,
|
|
537
|
+
projectType: "frontend",
|
|
400
538
|
framework: "nextjs",
|
|
401
539
|
typescript: typescript ?? true,
|
|
402
540
|
packageManager: detectPackageManager(),
|
|
@@ -413,21 +551,26 @@ function getDefaults(projectName, publishableKey, typescript) {
|
|
|
413
551
|
}
|
|
414
552
|
async function runPrompts(args) {
|
|
415
553
|
if (args.yes && args.initialName) {
|
|
416
|
-
|
|
554
|
+
p8.log.info("Using defaults (--yes flag)");
|
|
417
555
|
return getDefaults(args.initialName, args.publishableKey, args.typescript);
|
|
418
556
|
}
|
|
419
|
-
|
|
557
|
+
p8.intro("Let's set up your GitHat app");
|
|
420
558
|
sectionHeader("Project");
|
|
421
559
|
const project = await promptProject(args.initialName);
|
|
560
|
+
const projectType = await promptProjectType();
|
|
422
561
|
sectionHeader("Stack");
|
|
423
562
|
const framework = await promptFramework(args.typescript);
|
|
563
|
+
let backend = {};
|
|
564
|
+
if (projectType.projectType === "fullstack") {
|
|
565
|
+
backend = await promptBackend();
|
|
566
|
+
}
|
|
424
567
|
sectionHeader("Connect");
|
|
425
568
|
const githat = await promptGitHat(args.publishableKey);
|
|
426
569
|
sectionHeader("Features");
|
|
427
570
|
const features = await promptFeatures();
|
|
428
571
|
sectionHeader("Finish");
|
|
429
572
|
const finalize = await promptFinalize();
|
|
430
|
-
return { ...project, ...framework, ...githat, ...features, ...finalize };
|
|
573
|
+
return { ...project, ...projectType, ...framework, ...backend, ...githat, ...features, ...finalize };
|
|
431
574
|
}
|
|
432
575
|
function answersToContext(answers) {
|
|
433
576
|
return {
|
|
@@ -439,6 +582,9 @@ function answersToContext(answers) {
|
|
|
439
582
|
packageManager: answers.packageManager,
|
|
440
583
|
publishableKey: answers.publishableKey,
|
|
441
584
|
apiUrl: answers.apiUrl,
|
|
585
|
+
// Project type
|
|
586
|
+
projectType: answers.projectType,
|
|
587
|
+
backendFramework: answers.backendFramework,
|
|
442
588
|
useDatabase: answers.databaseChoice !== "none",
|
|
443
589
|
databaseChoice: answers.databaseChoice,
|
|
444
590
|
useTailwind: answers.useTailwind,
|
|
@@ -458,7 +604,7 @@ function answersToContext(answers) {
|
|
|
458
604
|
import fs3 from "fs-extra";
|
|
459
605
|
import path3 from "path";
|
|
460
606
|
import { execSync as execSync3 } from "child_process";
|
|
461
|
-
import * as
|
|
607
|
+
import * as p9 from "@clack/prompts";
|
|
462
608
|
import chalk2 from "chalk";
|
|
463
609
|
|
|
464
610
|
// src/utils/template-engine.ts
|
|
@@ -611,26 +757,33 @@ function initGit(cwd) {
|
|
|
611
757
|
async function scaffold(context, options) {
|
|
612
758
|
const root = path3.resolve(process.cwd(), context.projectName);
|
|
613
759
|
if (fs3.existsSync(root)) {
|
|
614
|
-
|
|
760
|
+
p9.cancel(`Directory "${context.projectName}" already exists.`);
|
|
615
761
|
process.exit(1);
|
|
616
762
|
}
|
|
763
|
+
const isFullstack = context.projectType === "fullstack";
|
|
617
764
|
await withSpinner("Creating project structure...", async () => {
|
|
618
765
|
fs3.ensureDirSync(root);
|
|
619
766
|
const templatesRoot = getTemplatesRoot();
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
renderTemplateDirectory(
|
|
767
|
+
if (isFullstack) {
|
|
768
|
+
scaffoldFullstack(templatesRoot, root, context);
|
|
769
|
+
} else {
|
|
770
|
+
const frameworkDir = path3.join(templatesRoot, context.framework);
|
|
771
|
+
if (!fs3.existsSync(frameworkDir)) {
|
|
772
|
+
throw new Error(`Templates not found at ${frameworkDir}. This is a bug \u2014 please report it.`);
|
|
773
|
+
}
|
|
774
|
+
renderTemplateDirectory(frameworkDir, root, context);
|
|
775
|
+
const baseDir = path3.join(templatesRoot, "base");
|
|
776
|
+
if (fs3.existsSync(baseDir)) {
|
|
777
|
+
renderTemplateDirectory(baseDir, root, context);
|
|
778
|
+
}
|
|
628
779
|
}
|
|
629
780
|
}, "Project structure created");
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
781
|
+
if (!isFullstack) {
|
|
782
|
+
await withSpinner("Generating package.json...", async () => {
|
|
783
|
+
const pkg = buildPackageJson(context);
|
|
784
|
+
writeJson(root, "package.json", pkg);
|
|
785
|
+
}, "package.json generated");
|
|
786
|
+
}
|
|
634
787
|
if (options.initGit) {
|
|
635
788
|
const gitSpinner = createSpinner("Initializing git repository...");
|
|
636
789
|
gitSpinner.start();
|
|
@@ -651,32 +804,61 @@ async function scaffold(context, options) {
|
|
|
651
804
|
} catch (err) {
|
|
652
805
|
const msg = err.message || "";
|
|
653
806
|
if (msg.includes("TIMEOUT")) {
|
|
654
|
-
|
|
807
|
+
p9.log.warn(`Install timed out. Run ${chalk2.cyan(installCmd)} manually.`);
|
|
655
808
|
} else {
|
|
656
|
-
|
|
809
|
+
p9.log.warn(`Could not auto-install. Run ${chalk2.cyan(installCmd)} manually.`);
|
|
657
810
|
}
|
|
658
811
|
}
|
|
659
812
|
},
|
|
660
813
|
"Dependencies installed"
|
|
661
814
|
);
|
|
662
815
|
}
|
|
663
|
-
|
|
664
|
-
displaySuccess(context.projectName, context.packageManager, context.framework, !!context.publishableKey);
|
|
816
|
+
p9.outro("Setup complete!");
|
|
817
|
+
displaySuccess(context.projectName, context.packageManager, context.framework, !!context.publishableKey, isFullstack);
|
|
665
818
|
if (!options.skipPrompts) {
|
|
666
|
-
const starPrompt = await
|
|
819
|
+
const starPrompt = await p9.confirm({
|
|
667
820
|
message: "Star GitHat on GitHub? (helps us grow!)",
|
|
668
821
|
initialValue: false
|
|
669
822
|
});
|
|
670
|
-
if (!
|
|
823
|
+
if (!p9.isCancel(starPrompt) && starPrompt) {
|
|
671
824
|
try {
|
|
672
825
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
673
826
|
execSync3(`${cmd} "https://github.com/GitHat-IO/githat"`, { stdio: "ignore" });
|
|
674
827
|
} catch {
|
|
675
|
-
|
|
828
|
+
p9.log.info("Visit https://github.com/GitHat-IO/githat to star us!");
|
|
676
829
|
}
|
|
677
830
|
}
|
|
678
831
|
}
|
|
679
832
|
}
|
|
833
|
+
function scaffoldFullstack(templatesRoot, root, context) {
|
|
834
|
+
const fullstackDir = path3.join(templatesRoot, "fullstack");
|
|
835
|
+
const rootDir = path3.join(fullstackDir, "root");
|
|
836
|
+
if (fs3.existsSync(rootDir)) {
|
|
837
|
+
renderTemplateDirectory(rootDir, root, context);
|
|
838
|
+
}
|
|
839
|
+
const appsDir = path3.join(root, "apps");
|
|
840
|
+
fs3.ensureDirSync(appsDir);
|
|
841
|
+
const webDir = path3.join(appsDir, "web");
|
|
842
|
+
fs3.ensureDirSync(webDir);
|
|
843
|
+
const webTemplateDir = path3.join(fullstackDir, `apps-web-${context.framework}`);
|
|
844
|
+
if (fs3.existsSync(webTemplateDir)) {
|
|
845
|
+
renderTemplateDirectory(webTemplateDir, webDir, context);
|
|
846
|
+
} else {
|
|
847
|
+
throw new Error(`Web app templates not found at ${webTemplateDir}. This is a bug \u2014 please report it.`);
|
|
848
|
+
}
|
|
849
|
+
const apiDir = path3.join(appsDir, "api");
|
|
850
|
+
fs3.ensureDirSync(apiDir);
|
|
851
|
+
const backendFramework = context.backendFramework || "hono";
|
|
852
|
+
const apiTemplateDir = path3.join(fullstackDir, `apps-api-${backendFramework}`);
|
|
853
|
+
if (fs3.existsSync(apiTemplateDir)) {
|
|
854
|
+
renderTemplateDirectory(apiTemplateDir, apiDir, context);
|
|
855
|
+
} else {
|
|
856
|
+
throw new Error(`API templates not found at ${apiTemplateDir}. This is a bug \u2014 please report it.`);
|
|
857
|
+
}
|
|
858
|
+
const packagesDir = path3.join(root, "packages");
|
|
859
|
+
fs3.ensureDirSync(packagesDir);
|
|
860
|
+
fs3.writeFileSync(path3.join(packagesDir, ".gitkeep"), "");
|
|
861
|
+
}
|
|
680
862
|
|
|
681
863
|
// src/commands/skills/index.ts
|
|
682
864
|
import { Command as Command6 } from "commander";
|
|
@@ -996,7 +1178,7 @@ import { Command as Command5 } from "commander";
|
|
|
996
1178
|
import chalk7 from "chalk";
|
|
997
1179
|
import * as fs6 from "fs";
|
|
998
1180
|
import * as path6 from "path";
|
|
999
|
-
import * as
|
|
1181
|
+
import * as p10 from "@clack/prompts";
|
|
1000
1182
|
var SKILL_TYPES = ["template", "integration", "ui", "ai", "workflow"];
|
|
1001
1183
|
function generateReadme(manifest) {
|
|
1002
1184
|
return `# ${manifest.name}
|
|
@@ -1144,7 +1326,7 @@ var initCommand = new Command5("init").description("Initialize a new skill packa
|
|
|
1144
1326
|
if (options.type && SKILL_TYPES.includes(options.type)) {
|
|
1145
1327
|
type = options.type;
|
|
1146
1328
|
} else {
|
|
1147
|
-
const result = await
|
|
1329
|
+
const result = await p10.select({
|
|
1148
1330
|
message: "What type of skill are you creating?",
|
|
1149
1331
|
options: [
|
|
1150
1332
|
{ value: "integration", label: "Integration", hint: "Connect to external services (Stripe, SendGrid, etc.)" },
|
|
@@ -1154,19 +1336,19 @@ var initCommand = new Command5("init").description("Initialize a new skill packa
|
|
|
1154
1336
|
{ value: "workflow", label: "Workflow", hint: "Automation recipes" }
|
|
1155
1337
|
]
|
|
1156
1338
|
});
|
|
1157
|
-
if (
|
|
1158
|
-
|
|
1339
|
+
if (p10.isCancel(result)) {
|
|
1340
|
+
p10.cancel("Operation cancelled");
|
|
1159
1341
|
process.exit(0);
|
|
1160
1342
|
}
|
|
1161
1343
|
type = result;
|
|
1162
1344
|
}
|
|
1163
|
-
const description = await
|
|
1345
|
+
const description = await p10.text({
|
|
1164
1346
|
message: "Short description:",
|
|
1165
1347
|
placeholder: `A ${type} skill for...`,
|
|
1166
1348
|
validate: (v) => v.length < 10 ? "Description must be at least 10 characters" : void 0
|
|
1167
1349
|
});
|
|
1168
|
-
if (
|
|
1169
|
-
|
|
1350
|
+
if (p10.isCancel(description)) {
|
|
1351
|
+
p10.cancel("Operation cancelled");
|
|
1170
1352
|
process.exit(0);
|
|
1171
1353
|
}
|
|
1172
1354
|
const manifest = {
|
|
@@ -1259,7 +1441,7 @@ program.command("create [project-name]", { isDefault: true }).description("Scaff
|
|
|
1259
1441
|
displayBanner();
|
|
1260
1442
|
const typescript = opts.js ? false : opts.ts ? true : void 0;
|
|
1261
1443
|
if (opts.yes && !projectName) {
|
|
1262
|
-
|
|
1444
|
+
p11.cancel(chalk9.red("Project name is required when using --yes flag"));
|
|
1263
1445
|
process.exit(1);
|
|
1264
1446
|
}
|
|
1265
1447
|
const answers = await runPrompts({
|
|
@@ -1275,7 +1457,7 @@ program.command("create [project-name]", { isDefault: true }).description("Scaff
|
|
|
1275
1457
|
skipPrompts: opts.yes
|
|
1276
1458
|
});
|
|
1277
1459
|
} catch (err) {
|
|
1278
|
-
|
|
1460
|
+
p11.cancel(chalk9.red(err.message || "Something went wrong."));
|
|
1279
1461
|
process.exit(1);
|
|
1280
1462
|
}
|
|
1281
1463
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@{{projectName}}/api",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "tsx watch src/index.ts",
|
|
8
|
+
"build": "tsc",
|
|
9
|
+
"start": "node dist/index.js",
|
|
10
|
+
"typecheck": "tsc --noEmit"
|
|
11
|
+
},
|
|
12
|
+
"dependencies": {
|
|
13
|
+
"@githat/nextjs": "^0.2.2",
|
|
14
|
+
"cors": "^2.8.5",
|
|
15
|
+
"express": "^5.0.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/cors": "^2.8.17",
|
|
19
|
+
"@types/express": "^5.0.0",
|
|
20
|
+
"@types/node": "^22.0.0",
|
|
21
|
+
"tsx": "^4.19.0",
|
|
22
|
+
"typescript": "^5.9.0"
|
|
23
|
+
}
|
|
24
|
+
}
|