create-githat-app 1.1.0 → 1.2.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.
- package/dist/cli.js +104 -123
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -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.
|
|
14
|
+
var VERSION = "1.2.0";
|
|
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"];
|
|
@@ -77,35 +77,66 @@ var DEPS = {
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
// src/utils/ascii.ts
|
|
80
|
-
var
|
|
80
|
+
var brand = gradient([...BRAND_COLORS]);
|
|
81
|
+
var violet = chalk.hex("#a78bfa");
|
|
82
|
+
var dim = chalk.dim;
|
|
83
|
+
function visibleLength(str) {
|
|
84
|
+
return str.replace(/\u001b\[.*?m/g, "").length;
|
|
85
|
+
}
|
|
86
|
+
function drawBox(lines) {
|
|
87
|
+
const W = 54;
|
|
88
|
+
const hr = "\u2500".repeat(W);
|
|
89
|
+
console.log(dim(` \u256D${hr}\u256E`));
|
|
90
|
+
console.log(dim(` \u2502${" ".repeat(W)}\u2502`));
|
|
91
|
+
for (const line of lines) {
|
|
92
|
+
const pad = W - 2 - visibleLength(line);
|
|
93
|
+
console.log(dim(" \u2502") + ` ${line}${" ".repeat(Math.max(0, pad))}` + dim("\u2502"));
|
|
94
|
+
}
|
|
95
|
+
console.log(dim(` \u2502${" ".repeat(W)}\u2502`));
|
|
96
|
+
console.log(dim(` \u2570${hr}\u256F`));
|
|
97
|
+
}
|
|
81
98
|
function displayBanner() {
|
|
82
99
|
const ascii = figlet.textSync("GitHat", {
|
|
83
|
-
font: "
|
|
100
|
+
font: "ANSI Shadow",
|
|
84
101
|
horizontalLayout: "default"
|
|
85
102
|
});
|
|
86
103
|
console.log("");
|
|
87
|
-
console.log(
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
console.log(brand(ascii));
|
|
105
|
+
drawBox([
|
|
106
|
+
`${violet("\u2726")} Create a new GitHat app`,
|
|
107
|
+
"",
|
|
108
|
+
dim("The developer platform"),
|
|
109
|
+
dim("for humans, agents & MCP servers"),
|
|
110
|
+
"",
|
|
111
|
+
dim(`v${VERSION} \xB7 githat.io`)
|
|
112
|
+
]);
|
|
113
|
+
console.log("");
|
|
114
|
+
}
|
|
115
|
+
function sectionHeader(title) {
|
|
116
|
+
const line = "\u2500".repeat(Math.max(1, 38 - title.length));
|
|
117
|
+
console.log("");
|
|
118
|
+
console.log(dim(` \u2500\u2500\u2500 ${title} ${line}`));
|
|
90
119
|
console.log("");
|
|
91
120
|
}
|
|
92
121
|
function displaySuccess(projectName, packageManager, framework) {
|
|
93
122
|
const devCmd = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
|
|
94
123
|
const port = framework === "react-vite" ? "5173" : "3000";
|
|
95
124
|
console.log("");
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
125
|
+
drawBox([
|
|
126
|
+
`${violet("\u2726")} Your GitHat app is ready!`,
|
|
127
|
+
"",
|
|
128
|
+
`${violet("$")} cd ${projectName}`,
|
|
129
|
+
`${violet("$")} ${devCmd}`,
|
|
130
|
+
"",
|
|
131
|
+
dim(`\u2192 http://localhost:${port}`),
|
|
132
|
+
"",
|
|
133
|
+
chalk.bold("Routes"),
|
|
134
|
+
`${violet("/sign-in")} Sign in`,
|
|
135
|
+
`${violet("/sign-up")} Create account`,
|
|
136
|
+
`${violet("/dashboard")} Protected dashboard`,
|
|
137
|
+
"",
|
|
138
|
+
dim("Docs \u2192 https://githat.io/docs/sdk")
|
|
139
|
+
]);
|
|
109
140
|
console.log("");
|
|
110
141
|
}
|
|
111
142
|
|
|
@@ -132,15 +163,6 @@ function validatePublishableKey(key) {
|
|
|
132
163
|
}
|
|
133
164
|
return void 0;
|
|
134
165
|
}
|
|
135
|
-
function validateApiUrl(url) {
|
|
136
|
-
if (!url) return "API URL is required";
|
|
137
|
-
try {
|
|
138
|
-
new URL(url);
|
|
139
|
-
return void 0;
|
|
140
|
-
} catch {
|
|
141
|
-
return "Must be a valid URL";
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
166
|
|
|
145
167
|
// src/prompts/project.ts
|
|
146
168
|
async function promptProject(initialName) {
|
|
@@ -153,14 +175,9 @@ async function promptProject(initialName) {
|
|
|
153
175
|
validate: validateProjectName
|
|
154
176
|
}),
|
|
155
177
|
businessName: () => p.text({
|
|
156
|
-
message: "
|
|
178
|
+
message: "Display name",
|
|
157
179
|
placeholder: "Acme Corp",
|
|
158
180
|
validate: (v) => !v ? "Display name is required" : void 0
|
|
159
|
-
}),
|
|
160
|
-
description: () => p.text({
|
|
161
|
-
message: "One-line description",
|
|
162
|
-
placeholder: "A platform for managing identity across humans and AI",
|
|
163
|
-
initialValue: ""
|
|
164
181
|
})
|
|
165
182
|
},
|
|
166
183
|
{
|
|
@@ -173,7 +190,7 @@ async function promptProject(initialName) {
|
|
|
173
190
|
return {
|
|
174
191
|
projectName: answers.projectName,
|
|
175
192
|
businessName: answers.businessName,
|
|
176
|
-
description:
|
|
193
|
+
description: `${answers.businessName} \u2014 Built with GitHat`
|
|
177
194
|
};
|
|
178
195
|
}
|
|
179
196
|
|
|
@@ -181,7 +198,6 @@ async function promptProject(initialName) {
|
|
|
181
198
|
import * as p2 from "@clack/prompts";
|
|
182
199
|
|
|
183
200
|
// src/utils/package-manager.ts
|
|
184
|
-
import { execSync } from "child_process";
|
|
185
201
|
function detectPackageManager() {
|
|
186
202
|
const userAgent = process.env.npm_config_user_agent || "";
|
|
187
203
|
if (userAgent.startsWith("pnpm")) return "pnpm";
|
|
@@ -189,14 +205,6 @@ function detectPackageManager() {
|
|
|
189
205
|
if (userAgent.startsWith("bun")) return "bun";
|
|
190
206
|
return "npm";
|
|
191
207
|
}
|
|
192
|
-
function isAvailable(pm) {
|
|
193
|
-
try {
|
|
194
|
-
execSync(`${pm} --version`, { stdio: "ignore" });
|
|
195
|
-
return true;
|
|
196
|
-
} catch {
|
|
197
|
-
return false;
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
208
|
function getInstallCommand(pm) {
|
|
201
209
|
const cmds = {
|
|
202
210
|
npm: "npm install",
|
|
@@ -209,14 +217,14 @@ function getInstallCommand(pm) {
|
|
|
209
217
|
|
|
210
218
|
// src/prompts/framework.ts
|
|
211
219
|
async function promptFramework(typescriptOverride) {
|
|
212
|
-
const
|
|
220
|
+
const packageManager = detectPackageManager();
|
|
213
221
|
const answers = await p2.group(
|
|
214
222
|
{
|
|
215
223
|
framework: () => p2.select({
|
|
216
224
|
message: "Framework",
|
|
217
225
|
options: [
|
|
218
|
-
{ value: "nextjs", label: "Next.js 16", hint: "App Router
|
|
219
|
-
{ value: "react-vite", label: "React 19 + Vite 7", hint: "SPA
|
|
226
|
+
{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" },
|
|
227
|
+
{ value: "react-vite", label: "React 19 + Vite 7", hint: "SPA \xB7 client-side routing" }
|
|
220
228
|
]
|
|
221
229
|
}),
|
|
222
230
|
typescript: () => typescriptOverride !== void 0 ? Promise.resolve(typescriptOverride) : p2.select({
|
|
@@ -225,15 +233,6 @@ async function promptFramework(typescriptOverride) {
|
|
|
225
233
|
{ value: true, label: "TypeScript", hint: "recommended" },
|
|
226
234
|
{ value: false, label: "JavaScript" }
|
|
227
235
|
]
|
|
228
|
-
}),
|
|
229
|
-
packageManager: () => p2.select({
|
|
230
|
-
message: "Package manager",
|
|
231
|
-
initialValue: detected,
|
|
232
|
-
options: ["npm", "pnpm", "yarn", "bun"].filter((pm) => pm === detected || isAvailable(pm)).map((pm) => ({
|
|
233
|
-
value: pm,
|
|
234
|
-
label: pm,
|
|
235
|
-
hint: pm === detected ? "detected" : void 0
|
|
236
|
-
}))
|
|
237
236
|
})
|
|
238
237
|
},
|
|
239
238
|
{
|
|
@@ -246,17 +245,17 @@ async function promptFramework(typescriptOverride) {
|
|
|
246
245
|
return {
|
|
247
246
|
framework: answers.framework,
|
|
248
247
|
typescript: answers.typescript,
|
|
249
|
-
packageManager
|
|
248
|
+
packageManager
|
|
250
249
|
};
|
|
251
250
|
}
|
|
252
251
|
|
|
253
252
|
// src/prompts/githat.ts
|
|
254
|
-
import { execSync
|
|
253
|
+
import { execSync } from "child_process";
|
|
255
254
|
import * as p3 from "@clack/prompts";
|
|
256
255
|
function openBrowser(url) {
|
|
257
256
|
try {
|
|
258
257
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
259
|
-
|
|
258
|
+
execSync(`${cmd} "${url}"`, { stdio: "ignore" });
|
|
260
259
|
} catch {
|
|
261
260
|
}
|
|
262
261
|
}
|
|
@@ -268,7 +267,7 @@ async function promptGitHat(existingKey) {
|
|
|
268
267
|
options: [
|
|
269
268
|
{ value: "browser", label: "Sign in with browser", hint: "opens githat.io \u2014 recommended" },
|
|
270
269
|
{ value: "paste", label: "I have a key", hint: "paste your pk_live_... key" },
|
|
271
|
-
{ value: "skip", label: "Skip for now", hint: "add key to .env
|
|
270
|
+
{ value: "skip", label: "Skip for now", hint: "add key to .env later" }
|
|
272
271
|
]
|
|
273
272
|
});
|
|
274
273
|
if (p3.isCancel(connectChoice)) {
|
|
@@ -277,8 +276,8 @@ async function promptGitHat(existingKey) {
|
|
|
277
276
|
}
|
|
278
277
|
if (connectChoice === "browser") {
|
|
279
278
|
p3.log.step("Opening githat.io in your browser...");
|
|
280
|
-
openBrowser(
|
|
281
|
-
p3.log.info("Sign up (or sign in), then go to Dashboard \u2192 Apps to copy your
|
|
279
|
+
openBrowser("https://githat.io/sign-up");
|
|
280
|
+
p3.log.info("Sign up (or sign in), then go to Dashboard \u2192 Apps to copy your key.");
|
|
282
281
|
const pastedKey = await p3.text({
|
|
283
282
|
message: "Paste your publishable key",
|
|
284
283
|
placeholder: "pk_live_...",
|
|
@@ -302,38 +301,26 @@ async function promptGitHat(existingKey) {
|
|
|
302
301
|
publishableKey = pastedKey || "";
|
|
303
302
|
}
|
|
304
303
|
}
|
|
305
|
-
const
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
],
|
|
322
|
-
initialValues: ["forgot-password", "email-verification"],
|
|
323
|
-
required: false
|
|
324
|
-
})
|
|
325
|
-
},
|
|
326
|
-
{
|
|
327
|
-
onCancel: () => {
|
|
328
|
-
p3.cancel("Setup cancelled.");
|
|
329
|
-
process.exit(0);
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
);
|
|
304
|
+
const authFeatures = await p3.multiselect({
|
|
305
|
+
message: "Auth features",
|
|
306
|
+
options: [
|
|
307
|
+
{ value: "forgot-password", label: "Forgot password", hint: "reset via email" },
|
|
308
|
+
{ value: "email-verification", label: "Email verification" },
|
|
309
|
+
{ value: "org-management", label: "Organizations", hint: "teams & roles" },
|
|
310
|
+
{ value: "mcp-servers", label: "MCP servers", hint: "Model Context Protocol" },
|
|
311
|
+
{ value: "ai-agents", label: "AI agents", hint: "wallet-based identity" }
|
|
312
|
+
],
|
|
313
|
+
initialValues: ["forgot-password", "email-verification"],
|
|
314
|
+
required: false
|
|
315
|
+
});
|
|
316
|
+
if (p3.isCancel(authFeatures)) {
|
|
317
|
+
p3.cancel("Setup cancelled.");
|
|
318
|
+
process.exit(0);
|
|
319
|
+
}
|
|
333
320
|
return {
|
|
334
321
|
publishableKey,
|
|
335
|
-
apiUrl:
|
|
336
|
-
authFeatures:
|
|
322
|
+
apiUrl: DEFAULT_API_URL,
|
|
323
|
+
authFeatures: authFeatures || []
|
|
337
324
|
};
|
|
338
325
|
}
|
|
339
326
|
|
|
@@ -345,22 +332,15 @@ async function promptFeatures() {
|
|
|
345
332
|
databaseChoice: () => p4.select({
|
|
346
333
|
message: "Database",
|
|
347
334
|
options: [
|
|
348
|
-
{ value: "none", label: "None
|
|
335
|
+
{ value: "none", label: "None", hint: "use GitHat backend directly" },
|
|
349
336
|
{ value: "prisma-postgres", label: "Prisma + PostgreSQL" },
|
|
350
337
|
{ value: "prisma-mysql", label: "Prisma + MySQL" },
|
|
351
338
|
{ value: "drizzle-postgres", label: "Drizzle + PostgreSQL" },
|
|
352
339
|
{ value: "drizzle-sqlite", label: "Drizzle + SQLite" }
|
|
353
340
|
]
|
|
354
341
|
}),
|
|
355
|
-
useTailwind: () => p4.confirm({ message: "
|
|
356
|
-
includeDashboard: () => p4.confirm({
|
|
357
|
-
message: "Include full dashboard?",
|
|
358
|
-
initialValue: true
|
|
359
|
-
}),
|
|
360
|
-
includeGithatFolder: () => p4.confirm({
|
|
361
|
-
message: "Include githat/ platform folder?",
|
|
362
|
-
initialValue: true
|
|
363
|
-
})
|
|
342
|
+
useTailwind: () => p4.confirm({ message: "Tailwind CSS?", initialValue: true }),
|
|
343
|
+
includeDashboard: () => p4.confirm({ message: "Include dashboard?", initialValue: true })
|
|
364
344
|
},
|
|
365
345
|
{
|
|
366
346
|
onCancel: () => {
|
|
@@ -373,38 +353,39 @@ async function promptFeatures() {
|
|
|
373
353
|
databaseChoice: answers.databaseChoice,
|
|
374
354
|
useTailwind: answers.useTailwind,
|
|
375
355
|
includeDashboard: answers.includeDashboard,
|
|
376
|
-
includeGithatFolder:
|
|
356
|
+
includeGithatFolder: true
|
|
377
357
|
};
|
|
378
358
|
}
|
|
379
359
|
|
|
380
360
|
// src/prompts/finalize.ts
|
|
381
361
|
import * as p5 from "@clack/prompts";
|
|
382
362
|
async function promptFinalize() {
|
|
383
|
-
const
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
process.exit(0);
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
);
|
|
363
|
+
const installDeps = await p5.confirm({
|
|
364
|
+
message: "Install dependencies?",
|
|
365
|
+
initialValue: true
|
|
366
|
+
});
|
|
367
|
+
if (p5.isCancel(installDeps)) {
|
|
368
|
+
p5.cancel("Setup cancelled.");
|
|
369
|
+
process.exit(0);
|
|
370
|
+
}
|
|
395
371
|
return {
|
|
396
|
-
initGit:
|
|
397
|
-
installDeps
|
|
372
|
+
initGit: true,
|
|
373
|
+
installDeps
|
|
398
374
|
};
|
|
399
375
|
}
|
|
400
376
|
|
|
401
377
|
// src/prompts/index.ts
|
|
402
378
|
async function runPrompts(args) {
|
|
403
379
|
p6.intro("Let\u2019s set up your GitHat app");
|
|
380
|
+
sectionHeader("Project");
|
|
404
381
|
const project = await promptProject(args.initialName);
|
|
382
|
+
sectionHeader("Stack");
|
|
405
383
|
const framework = await promptFramework(args.typescript);
|
|
384
|
+
sectionHeader("Connect");
|
|
406
385
|
const githat = await promptGitHat(args.publishableKey);
|
|
386
|
+
sectionHeader("Features");
|
|
407
387
|
const features = await promptFeatures();
|
|
388
|
+
sectionHeader("Finish");
|
|
408
389
|
const finalize = await promptFinalize();
|
|
409
390
|
return { ...project, ...framework, ...githat, ...features, ...finalize };
|
|
410
391
|
}
|
|
@@ -436,7 +417,7 @@ function answersToContext(answers) {
|
|
|
436
417
|
// src/scaffold/index.ts
|
|
437
418
|
import fs3 from "fs-extra";
|
|
438
419
|
import path3 from "path";
|
|
439
|
-
import { execSync as
|
|
420
|
+
import { execSync as execSync3 } from "child_process";
|
|
440
421
|
import * as p7 from "@clack/prompts";
|
|
441
422
|
import chalk2 from "chalk";
|
|
442
423
|
|
|
@@ -571,12 +552,12 @@ async function withSpinner(text3, fn, successText) {
|
|
|
571
552
|
}
|
|
572
553
|
|
|
573
554
|
// src/utils/git.ts
|
|
574
|
-
import { execSync as
|
|
555
|
+
import { execSync as execSync2 } from "child_process";
|
|
575
556
|
function initGit(cwd) {
|
|
576
557
|
try {
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
558
|
+
execSync2("git init", { cwd, stdio: "ignore" });
|
|
559
|
+
execSync2("git add -A", { cwd, stdio: "ignore" });
|
|
560
|
+
execSync2('git commit -m "Initial commit from create-githat-app"', {
|
|
580
561
|
cwd,
|
|
581
562
|
stdio: "ignore"
|
|
582
563
|
});
|
|
@@ -625,7 +606,7 @@ async function scaffold(context, options) {
|
|
|
625
606
|
`Installing dependencies with ${context.packageManager}...`,
|
|
626
607
|
async () => {
|
|
627
608
|
try {
|
|
628
|
-
|
|
609
|
+
execSync3(installCmd, { cwd: root, stdio: "ignore", timeout: 12e4 });
|
|
629
610
|
} catch (err) {
|
|
630
611
|
const msg = err.message || "";
|
|
631
612
|
if (msg.includes("TIMEOUT")) {
|