create-githat-app 1.0.15 → 1.0.17

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 (42) hide show
  1. package/dist/cli.js +42 -28
  2. package/package.json +1 -1
  3. package/templates/agent/app/admin/agent/page.tsx.hbs +127 -0
  4. package/templates/agent/app/page.tsx.hbs +85 -108
  5. package/templates/base/.env.local.hbs +11 -0
  6. package/templates/base/.gitignore.hbs +17 -2
  7. package/templates/classroom/app/(auth)/sign-in/page.tsx.hbs +9 -0
  8. package/templates/classroom/app/(auth)/sign-up/page.tsx.hbs +9 -0
  9. package/templates/classroom/app/globals.css.hbs +87 -0
  10. package/templates/classroom/app/layout.tsx.hbs +41 -0
  11. package/templates/classroom/app/page.tsx.hbs +103 -0
  12. package/templates/classroom/app/projects/[id]/feedback/page.tsx.hbs +159 -0
  13. package/templates/classroom/app/projects/[id]/present/page.tsx.hbs +113 -0
  14. package/templates/classroom/next.config.ts.hbs +7 -0
  15. package/templates/classroom/postcss.config.mjs.hbs +14 -0
  16. package/templates/classroom/proxy.ts.hbs +10 -0
  17. package/templates/classroom/tsconfig.json.hbs +21 -0
  18. package/templates/content/app/newsletter/page.tsx.hbs +90 -0
  19. package/templates/content/app/page.tsx.hbs +93 -111
  20. package/templates/content/app/posts/[slug]/page.tsx.hbs +119 -0
  21. package/templates/dashboard/app/admin/data/[entity]/page.tsx.hbs +68 -0
  22. package/templates/dashboard/app/admin/page.tsx.hbs +59 -0
  23. package/templates/dashboard/app/page.tsx.hbs +42 -108
  24. package/templates/dashboard/src/lib/db.ts.hbs +39 -0
  25. package/templates/portfolio/app/(auth)/sign-in/page.tsx.hbs +9 -0
  26. package/templates/portfolio/app/(auth)/sign-up/page.tsx.hbs +9 -0
  27. package/templates/portfolio/app/globals.css.hbs +87 -0
  28. package/templates/portfolio/app/layout.tsx.hbs +41 -0
  29. package/templates/portfolio/app/page.tsx.hbs +86 -0
  30. package/templates/portfolio/next.config.ts.hbs +7 -0
  31. package/templates/portfolio/postcss.config.mjs.hbs +14 -0
  32. package/templates/portfolio/proxy.ts.hbs +10 -0
  33. package/templates/portfolio/tsconfig.json.hbs +21 -0
  34. package/templates/saas/app/admin/billing/page.tsx.hbs +145 -0
  35. package/templates/saas/app/admin/page.tsx.hbs +106 -0
  36. package/templates/saas/app/admin/team/page.tsx.hbs +134 -0
  37. package/templates/saas/app/page.tsx.hbs +95 -110
  38. package/templates/saas/app/pricing/page.tsx.hbs +131 -0
  39. package/templates/agent/TODO.md +0 -9
  40. package/templates/content/TODO.md +0 -9
  41. package/templates/dashboard/TODO.md +0 -9
  42. package/templates/saas/TODO.md +0 -9
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.0.15";
14
+ var VERSION = "1.0.17";
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"];
@@ -167,6 +167,16 @@ function sectionHeader(title) {
167
167
  console.log(dim(` \u2500\u2500\u2500 ${title} ${"\u2500".repeat(lineLen)}`));
168
168
  console.log("");
169
169
  }
170
+ function keyNextSteps() {
171
+ return [
172
+ chalk.bold("Next:") + " add your GitHat key",
173
+ ` 1. Open ${violet(".env.local")}`,
174
+ ` 2. Paste your key from ${violet("https://githat.io/dashboard/apps")}`,
175
+ ` 3. Run ${violet("npm run dev")} again`,
176
+ dim(" (.env.local is gitignored \u2014 your key never gets committed.)"),
177
+ ""
178
+ ];
179
+ }
170
180
  function displaySuccess(projectName, packageManager, framework, hasPublishableKey = true, isFullstack = false) {
171
181
  const devCmd = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
172
182
  const port = framework === "react-vite" ? "5173" : "3000";
@@ -186,11 +196,7 @@ function displaySuccess(projectName, packageManager, framework, hasPublishableKe
186
196
  `${violet("apps/api")} API backend`,
187
197
  `${violet("packages/")} Shared code`,
188
198
  "",
189
- ...hasPublishableKey ? [] : [
190
- chalk.yellow("No key configured \u2014 auth works on localhost."),
191
- `For production: ${violet("githat.io/dashboard/apps")}`,
192
- ""
193
- ],
199
+ ...hasPublishableKey ? [] : keyNextSteps(),
194
200
  dim("Docs \u2192 https://githat.io/docs/sdk")
195
201
  ]);
196
202
  } else {
@@ -207,11 +213,7 @@ function displaySuccess(projectName, packageManager, framework, hasPublishableKe
207
213
  `${violet("/sign-up")} Create account`,
208
214
  `${violet("/dashboard")} Protected dashboard`,
209
215
  "",
210
- ...hasPublishableKey ? [] : [
211
- chalk.yellow("No key configured \u2014 auth works on localhost."),
212
- `For production: ${violet("githat.io/dashboard/apps")}`,
213
- ""
214
- ],
216
+ ...hasPublishableKey ? [] : keyNextSteps(),
215
217
  dim("Docs \u2192 https://githat.io/docs/sdk")
216
218
  ]);
217
219
  }
@@ -332,10 +334,12 @@ async function promptFramework(typescriptOverride, isFullstack) {
332
334
  const frameworkOptions = isFullstack ? [{ value: "nextjs", label: "Next.js 16", hint: "App Router \xB7 SSR \xB7 middleware auth" }] : [
333
335
  { value: "plain", label: "Plain", hint: "Auth + a homepage. Smallest possible GitHat app." },
334
336
  { value: "saas", label: "SaaS", hint: "Orgs, teams, RBAC, subscription billing. Replaces Clerk + Stripe." },
335
- { value: "marketplace", label: "Marketplace", hint: "Multi-vendor commerce. Anonymous-first browsing, Sebastn Connect." },
336
- { value: "agent", label: "AI Agent", hint: "Web4 wallet-bound agent + MCP server. Public verification." },
337
- { value: "content", label: "Content", hint: "Paywalled posts, newsletter, one-time purchases. Replaces Substack." },
337
+ { value: "marketplace", label: "Marketplace", hint: "Multi-vendor commerce. Anonymous-first, Sebastn Connect." },
338
+ { value: "content", label: "Content", hint: "Paywalled posts, newsletter. Replaces Substack." },
338
339
  { value: "dashboard", label: "Dashboard", hint: "Auth-gated admin UI over your existing database." },
340
+ { value: "portfolio", label: "Portfolio", hint: "Personal site: public projects, auth-gated editor." },
341
+ { value: "classroom", label: "Classroom", hint: "Live student presentations + real-time audience feedback." },
342
+ { value: "agent", label: "AI Agent", hint: "Web4 wallet-bound agent + MCP server. Public verification." },
339
343
  { value: "nextjs", label: "Next.js (full kit)", hint: "Legacy: dashboard + orgs + agents + MCP scaffolding." },
340
344
  { value: "react-vite", label: "React + Vite", hint: "SPA \xB7 client-side routing." }
341
345
  ];
@@ -693,7 +697,9 @@ var NEXT_LIKE = /* @__PURE__ */ new Set([
693
697
  "marketplace",
694
698
  "agent",
695
699
  "content",
696
- "dashboard"
700
+ "dashboard",
701
+ "portfolio",
702
+ "classroom"
697
703
  ]);
698
704
  Handlebars.registerHelper("ifNext", function(framework, options) {
699
705
  return NEXT_LIKE.has(framework) ? options.fn(this) : options.inverse(this);
@@ -751,7 +757,9 @@ var NEXT_LIKE2 = /* @__PURE__ */ new Set([
751
757
  "marketplace",
752
758
  "agent",
753
759
  "content",
754
- "dashboard"
760
+ "dashboard",
761
+ "portfolio",
762
+ "classroom"
755
763
  ]);
756
764
  var MINIMAL = /* @__PURE__ */ new Set([
757
765
  "plain",
@@ -759,7 +767,9 @@ var MINIMAL = /* @__PURE__ */ new Set([
759
767
  "marketplace",
760
768
  "agent",
761
769
  "content",
762
- "dashboard"
770
+ "dashboard",
771
+ "portfolio",
772
+ "classroom"
763
773
  ]);
764
774
  function buildPackageJson(ctx) {
765
775
  const isNext = NEXT_LIKE2.has(ctx.framework);
@@ -973,7 +983,9 @@ async function scaffold(context, options) {
973
983
  "marketplace",
974
984
  "agent",
975
985
  "content",
976
- "dashboard"
986
+ "dashboard",
987
+ "portfolio",
988
+ "classroom"
977
989
  ]);
978
990
  if (!isFullstack && NEXT_LIKE3.has(context.framework)) {
979
991
  await registerApp(context.projectName, root);
@@ -1630,23 +1642,25 @@ skillsCommand.action(() => {
1630
1642
  // src/cli.ts
1631
1643
  var program = new Command7();
1632
1644
  program.name("githat").description("GitHat CLI - Scaffold apps and manage skills").version(VERSION);
1633
- program.command("create [project-name]", { isDefault: true }).description("Scaffold a new GitHat app").option("--key <key>", "GitHat publishable key (pk_live_...)").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").option("--plain", "Smallest scaffold: auth + a homepage. No dashboard.").option("--saas", "B2B starter: orgs, teams, RBAC, subscription billing.").option("--marketplace", "Multi-vendor commerce: anonymous-first browsing, Sebastn Connect.").option("--agent", "Web4 wallet-bound autonomous agent + MCP server registration.").option("--content", "Paywalled posts, newsletter, one-time purchases via Sebastn.").option("--dashboard", "Admin UI over your existing database, auth-gated.").option("--fullstack", "Create fullstack project (Turborepo)").option("--backend <framework>", "Backend framework (hono, express, fastify)").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, opts) => {
1645
+ program.command("create [project-name]", { isDefault: true }).description("Scaffold a new GitHat app").option("--key <key>", "CI only: bake key into .env.local. Default flow is paste into .env.local after scaffold.").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").option("--plain", "Smallest scaffold: auth + a homepage. No dashboard.").option("--saas", "B2B starter: orgs, teams, RBAC, subscription billing.").option("--marketplace", "Multi-vendor commerce: anonymous-first browsing, Sebastn Connect.").option("--agent", "Web4 wallet-bound autonomous agent + MCP server registration.").option("--content", "Paywalled posts, newsletter, one-time purchases via Sebastn.").option("--dashboard", "Admin UI over your existing database, auth-gated.").option("--portfolio", "Personal portfolio: public projects, auth-gated editor.").option("--classroom", "Live student presentations with real-time audience feedback.").option("--fullstack", "Create fullstack project (Turborepo)").option("--backend <framework>", "Backend framework (hono, express, fastify)").option("-y, --yes", "Skip prompts and use defaults").action(async (projectName, opts) => {
1634
1646
  try {
1635
1647
  displayBanner();
1636
1648
  if (!opts.yes && !opts.key) {
1637
1649
  p12.note(
1638
1650
  [
1639
- chalk10.bold("Two ways to connect this app to GitHat:"),
1651
+ chalk10.bold("How the GitHat key flow works:"),
1652
+ "",
1653
+ ` ${chalk10.cyan("1.")} We'll scaffold your project with a placeholder`,
1654
+ ` in ${chalk10.bold(".env.local")} (gitignored \u2014 safe to keep secrets here)`,
1640
1655
  "",
1641
- ` ${chalk10.cyan("1.")} ${chalk10.bold("Already signed up?")} Pass your key:`,
1642
- ` ${chalk10.dim("npx create-githat-app my-app --key pk_live_...")}`,
1643
- ` Get the key at ${chalk10.cyan("https://githat.io/dashboard/apps")}`,
1656
+ ` ${chalk10.cyan("2.")} You open ${chalk10.cyan("https://githat.io/dashboard/apps")}`,
1657
+ ` and copy your publishable key`,
1644
1658
  "",
1645
- ` ${chalk10.cyan("2.")} ${chalk10.bold("Don't have an account yet?")} Press Enter`,
1646
- ` below \u2014 we'll open a browser, you sign up in 30s,`,
1647
- ` and the key is stitched in for you.`,
1659
+ ` ${chalk10.cyan("3.")} You paste it into ${chalk10.bold(".env.local")} and run`,
1660
+ ` ${chalk10.dim("npm run dev")}`,
1648
1661
  "",
1649
- chalk10.dim("Either path lands you in the same place.")
1662
+ chalk10.dim("Why not pass the key on the command line? Shell"),
1663
+ chalk10.dim("history is forever. .env.local is more secure.")
1650
1664
  ].join("\n"),
1651
1665
  "First time with GitHat?"
1652
1666
  );
@@ -1656,7 +1670,7 @@ program.command("create [project-name]", { isDefault: true }).description("Scaff
1656
1670
  p12.cancel(chalk10.red("Project name is required when using --yes flag"));
1657
1671
  process.exit(1);
1658
1672
  }
1659
- const template = opts.marketplace ? "marketplace" : opts.agent ? "agent" : opts.saas ? "saas" : opts.content ? "content" : opts.dashboard ? "dashboard" : opts.plain ? "plain" : void 0;
1673
+ const template = opts.marketplace ? "marketplace" : opts.classroom ? "classroom" : opts.portfolio ? "portfolio" : opts.agent ? "agent" : opts.saas ? "saas" : opts.content ? "content" : opts.dashboard ? "dashboard" : opts.plain ? "plain" : void 0;
1660
1674
  const answers = await runPrompts({
1661
1675
  initialName: projectName,
1662
1676
  publishableKey: opts.key,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-githat-app",
3
- "version": "1.0.15",
3
+ "version": "1.0.17",
4
4
  "description": "GitHat CLI — scaffold apps and manage the skills marketplace",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,127 @@
1
+ 'use client';
2
+
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
5
+
6
+ /**
7
+ * Agent operator dashboard — control the agent you registered.
8
+ *
9
+ * Three core actions an operator does:
10
+ * 1. See live status (active / paused / revoked)
11
+ * 2. Adjust capabilities (which tools the agent can call)
12
+ * 3. Hit the kill switch
13
+ *
14
+ * Real apps fetch the agent record from GitHat's
15
+ * GET /agent/{walletAddress}. Stub below shows the data shape.
16
+ */
17
+ export default function AgentAdminPage() {
18
+ const { isSignedIn, isLoading } = useAuth();
19
+
20
+ if (isLoading) return <div style=\{{ padding: 'var(--space-8)', color: 'var(--fg-muted)' }}>Loading…</div>;
21
+ if (!isSignedIn) {
22
+ return (
23
+ <div style=\{{ padding: 'var(--space-8)', textAlign: 'center' }}>
24
+ <Link href="/sign-in" style=\{{ color: 'var(--primary)' }}>Sign in →</Link>
25
+ </div>
26
+ );
27
+ }
28
+
29
+ const agent = {
30
+ name: 'My research agent',
31
+ wallet: '0xAB12…CDEf',
32
+ status: 'active' as 'active' | 'paused' | 'revoked',
33
+ capabilities: ['web.search', 'web.fetch', 'arxiv.read'],
34
+ actionsLast24h: 47,
35
+ verifyUrl: 'https://githat.io/verify/agent/0xAB12...CDEf',
36
+ };
37
+
38
+ return (
39
+ <div style=\{{ padding: 'var(--space-8) var(--space-4)', maxWidth: '48rem', margin: '0 auto' }}>
40
+ <h1 style=\{{ fontFamily: 'var(--font-wordmark)', fontSize: '2rem', marginBottom: 'var(--space-2)' }}>
41
+ {agent.name}
42
+ </h1>
43
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '0.875rem', marginBottom: 'var(--space-6)' }}>
44
+ Wallet: <code>{agent.wallet}</code>
45
+ </p>
46
+
47
+ <section style=\{{
48
+ padding: 'var(--space-5)',
49
+ borderRadius: 'var(--radius-md, 0.5rem)',
50
+ border: '1px solid var(--border)',
51
+ background: 'var(--surface)',
52
+ marginBottom: 'var(--space-6)',
53
+ }}>
54
+ <div style=\{{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 'var(--space-3)' }}>
55
+ <span style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)' }}>Status</span>
56
+ <span style=\{{
57
+ padding: 'var(--space-1) var(--space-3)',
58
+ borderRadius: 'var(--radius-full, 9999px)',
59
+ background: agent.status === 'active' ? 'var(--success)' : agent.status === 'paused' ? 'var(--warn)' : 'var(--danger)',
60
+ color: 'var(--bg)',
61
+ fontSize: '0.75rem',
62
+ fontWeight: 700,
63
+ }}>
64
+ {agent.status.toUpperCase()}
65
+ </span>
66
+ </div>
67
+ <div style=\{{ fontSize: '2rem', fontWeight: 600, marginBottom: 'var(--space-1)' }}>
68
+ {agent.actionsLast24h}
69
+ </div>
70
+ <div style=\{{ fontSize: '0.75rem', color: 'var(--fg-subtle)' }}>actions in the last 24 hours</div>
71
+ </section>
72
+
73
+ <section style=\{{ marginBottom: 'var(--space-6)' }}>
74
+ <h2 style=\{{ fontSize: '1.125rem', marginBottom: 'var(--space-3)' }}>Capabilities</h2>
75
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexWrap: 'wrap', gap: 'var(--space-2)' }}>
76
+ {agent.capabilities.map((cap) => (
77
+ <li key={cap} style=\{{
78
+ padding: 'var(--space-2) var(--space-3)',
79
+ borderRadius: 'var(--radius-md, 0.5rem)',
80
+ border: '1px solid var(--border)',
81
+ background: 'var(--surface)',
82
+ fontSize: '0.875rem',
83
+ fontFamily: 'var(--font-mono, monospace)',
84
+ }}>
85
+ {cap}
86
+ </li>
87
+ ))}
88
+ </ul>
89
+ </section>
90
+
91
+ <section style=\{{ display: 'flex', gap: 'var(--space-3)', flexWrap: 'wrap' }}>
92
+ <a href={agent.verifyUrl} target="_blank" rel="noopener noreferrer" style=\{{
93
+ padding: 'var(--space-3) var(--space-4)',
94
+ borderRadius: 'var(--radius-md, 0.5rem)',
95
+ border: '1px solid var(--border)',
96
+ color: 'var(--fg)',
97
+ textDecoration: 'none',
98
+ }}>
99
+ Public verify URL ↗
100
+ </a>
101
+ <Link href="/admin/mcp" style=\{{
102
+ padding: 'var(--space-3) var(--space-4)',
103
+ borderRadius: 'var(--radius-md, 0.5rem)',
104
+ border: '1px solid var(--border)',
105
+ color: 'var(--fg)',
106
+ textDecoration: 'none',
107
+ }}>
108
+ MCP servers
109
+ </Link>
110
+ <button
111
+ onClick={() => { /* TODO: POST /api/agent/kill */ }}
112
+ style=\{{
113
+ padding: 'var(--space-3) var(--space-4)',
114
+ borderRadius: 'var(--radius-md, 0.5rem)',
115
+ border: '1px solid var(--danger)',
116
+ background: 'transparent',
117
+ color: 'var(--danger)',
118
+ fontWeight: 600,
119
+ cursor: 'pointer',
120
+ }}
121
+ >
122
+ Kill switch
123
+ </button>
124
+ </section>
125
+ </div>
126
+ );
127
+ }
@@ -1,123 +1,100 @@
1
1
  'use client';
2
2
 
3
- import { SignInButton, SignUpButton, UserButton, useAuth } from '@githat/nextjs';
3
+ import Link from 'next/link';
4
+ import { useAuth } from '@githat/nextjs';
4
5
 
5
6
  /**
6
- * Plain GitHat homepage.
7
+ * AI Agent template — Web4 identity, MCP, public verification.
7
8
  *
8
- * No dashboard, no orgs, no agents just one page that flips between
9
- * "signed out" and "signed in." The smallest possible example of "I
10
- * have GitHat wired up." Replace this file with whatever your app
11
- * actually does auth keeps working.
9
+ * The pitch: every other auth platform stops at humans. GitHat
10
+ * extends identity to autonomous agents wallet-bound (Ethereum
11
+ * sigs), capability-scoped, kill-switchable, audit-logged. Anyone
12
+ * can verify an agent at /verify/agent/<wallet>.
13
+ *
14
+ * This template is the agent operator's dashboard, plus the public
15
+ * /verify endpoint anyone can hit to check an agent's status.
12
16
  */
13
17
  export default function Home() {
14
- const { isSignedIn, isLoading, user } = useAuth();
15
-
16
- if (isLoading) {
17
- return (
18
- <div
19
- style=\{{
20
- display: 'flex',
21
- minHeight: 'calc(100vh - 64px)',
22
- alignItems: 'center',
23
- justifyContent: 'center',
24
- color: 'var(--fg-muted)',
25
- }}
26
- >
27
- Loading…
28
- </div>
29
- );
30
- }
18
+ const { isSignedIn } = useAuth();
31
19
 
32
20
  return (
33
- <div
34
- style=\{{
35
- display: 'flex',
36
- minHeight: 'calc(100vh - 64px)',
37
- alignItems: 'center',
38
- justifyContent: 'center',
39
- padding: 'var(--space-8) var(--space-4)',
40
- background: 'var(--bg)',
41
- }}
42
- >
43
- <main
44
- style=\{{
45
- width: '100%',
46
- maxWidth: '32rem',
47
- textAlign: 'center',
48
- color: 'var(--fg)',
49
- }}
50
- >
21
+ <div style=\{{ background: 'var(--bg)', color: 'var(--fg)', minHeight: 'calc(100vh - 64px)' }}>
22
+ <section style=\{{
23
+ padding: 'var(--space-12) var(--space-4)',
24
+ textAlign: 'center',
25
+ maxWidth: '40rem',
26
+ margin: '0 auto',
27
+ }}>
28
+ <h1 style=\{{
29
+ fontFamily: 'var(--font-wordmark, Georgia, serif)',
30
+ fontSize: 'clamp(2rem, 5vw, 3rem)',
31
+ lineHeight: 1.1,
32
+ marginBottom: 'var(--space-3)',
33
+ }}>
34
+ {{businessName}}
35
+ </h1>
36
+ <p style=\{{ color: 'var(--fg-muted)', fontSize: '1.125rem', marginBottom: 'var(--space-6)' }}>
37
+ Wallet-bound identity for autonomous AI agents. Capability
38
+ scoping, kill switch, audit trail — and a public /verify
39
+ endpoint anyone can hit.
40
+ </p>
51
41
  {!isSignedIn ? (
52
- <>
53
- <h1
54
- style=\{{
55
- fontFamily: 'var(--font-wordmark)',
56
- fontSize: '2.5rem',
57
- lineHeight: 1.1,
58
- marginBottom: 'var(--space-3)',
59
- }}
60
- >
61
- Welcome to {{businessName}}
62
- </h1>
63
- <p
64
- style=\{{
65
- color: 'var(--fg-muted)',
66
- marginBottom: 'var(--space-6)',
67
- lineHeight: 1.6,
68
- }}
69
- >
70
- Sign in to continue, or create an account in 30 seconds.
71
- We use GitHat for identity — your password never touches
72
- this app.
73
- </p>
74
- <div
75
- style=\{{
76
- display: 'flex',
77
- gap: 'var(--space-3)',
78
- justifyContent: 'center',
79
- flexWrap: 'wrap',
80
- }}
81
- >
82
- <SignInButton />
83
- <SignUpButton />
84
- </div>
85
- </>
42
+ <Link href="/sign-up" style=\{{
43
+ display: 'inline-block',
44
+ padding: 'var(--space-3) var(--space-6)',
45
+ borderRadius: 'var(--radius-md, 0.5rem)',
46
+ background: 'var(--primary)',
47
+ color: 'var(--bg)',
48
+ fontWeight: 600,
49
+ textDecoration: 'none',
50
+ }}>
51
+ Register an agent →
52
+ </Link>
86
53
  ) : (
87
- <>
88
- <div
89
- style=\{{
90
- display: 'flex',
91
- alignItems: 'center',
92
- justifyContent: 'center',
93
- gap: 'var(--space-4)',
94
- marginBottom: 'var(--space-6)',
95
- }}
96
- >
97
- <UserButton />
98
- <h1
99
- style=\{{
100
- fontFamily: 'var(--font-wordmark)',
101
- fontSize: '2rem',
102
- margin: 0,
103
- }}
104
- >
105
- Hello{user?.name ? `, ${user.name}` : ''}.
106
- </h1>
107
- </div>
108
- <p
109
- style=\{{
110
- color: 'var(--fg-muted)',
111
- lineHeight: 1.6,
112
- }}
113
- >
114
- That's it. The plain template doesn't ship a dashboard or
115
- any other route — replace this page with whatever your
116
- app actually does. Authentication will keep working.
117
- </p>
118
- </>
54
+ <Link href="/admin/agent" style=\{{
55
+ display: 'inline-block',
56
+ padding: 'var(--space-3) var(--space-6)',
57
+ borderRadius: 'var(--radius-md, 0.5rem)',
58
+ background: 'var(--primary)',
59
+ color: 'var(--bg)',
60
+ fontWeight: 600,
61
+ textDecoration: 'none',
62
+ }}>
63
+ Open agent dashboard →
64
+ </Link>
119
65
  )}
120
- </main>
66
+ </section>
67
+
68
+ <section style=\{{ padding: 'var(--space-8) var(--space-4)', background: 'var(--surface-sub)' }}>
69
+ <div style=\{{ maxWidth: '48rem', margin: '0 auto' }}>
70
+ <h2 style=\{{ fontSize: '1.5rem', marginBottom: 'var(--space-4)' }}>What you get</h2>
71
+ <ul style=\{{ listStyle: 'none', padding: 0, margin: 0, display: 'flex', flexDirection: 'column', gap: 'var(--space-3)' }}>
72
+ <Item emoji="🪪" title="Wallet-bound identity" body="Ethereum-style key pair. The agent signs requests; clients verify the signature. No shared secrets to leak." />
73
+ <Item emoji="🎚" title="Capability scopes" body="Each agent can only call the tools you explicitly granted. Add or revoke at any time." />
74
+ <Item emoji="🛑" title="Kill switch" body="One click and the agent is revoked everywhere. Existing tokens stop working immediately." />
75
+ <Item emoji="📜" title="Audit log" body="Every action is logged with timestamp, capability used, and signature. Prove what happened." />
76
+ <Item emoji="🔍" title="Public verification" body="Anyone can hit githat.io/verify/agent/<wallet> and see the agent's status, capabilities, and recent actions." />
77
+ </ul>
78
+ </div>
79
+ </section>
121
80
  </div>
122
81
  );
123
82
  }
83
+
84
+ function Item({ emoji, title, body }: { emoji: string; title: string; body: string }) {
85
+ return (
86
+ <li style=\{{
87
+ display: 'flex',
88
+ gap: 'var(--space-4)',
89
+ padding: 'var(--space-4)',
90
+ borderRadius: 'var(--radius-md, 0.5rem)',
91
+ background: 'var(--surface)',
92
+ }}>
93
+ <span style=\{{ fontSize: '1.5rem' }} aria-hidden>{emoji}</span>
94
+ <div>
95
+ <div style=\{{ fontWeight: 600, marginBottom: 'var(--space-1)' }}>{title}</div>
96
+ <div style=\{{ fontSize: '0.875rem', color: 'var(--fg-muted)' }}>{body}</div>
97
+ </div>
98
+ </li>
99
+ );
100
+ }
@@ -1,10 +1,21 @@
1
1
  {{#ifNext framework}}
2
+ # ─── GitHat publishable key ──────────────────────────────────────────────
3
+ # Paste your key from https://githat.io/dashboard/apps below.
4
+ # This file is gitignored — your key never gets committed by accident.
5
+ # (Don't put real keys on the command line. Shell history is forever.)
2
6
  NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY={{publishableKey}}
3
7
  NEXT_PUBLIC_GITHAT_API_URL={{apiUrl}}
4
8
  {{else}}
9
+ # ─── GitHat publishable key ──────────────────────────────────────────────
10
+ # Paste your key from https://githat.io/dashboard/apps below.
11
+ # This file is gitignored — your key never gets committed by accident.
12
+ # (Don't put real keys on the command line. Shell history is forever.)
5
13
  VITE_GITHAT_PUBLISHABLE_KEY={{publishableKey}}
6
14
  VITE_GITHAT_API_URL={{apiUrl}}
7
15
  {{/ifNext}}
8
16
  {{#if useDatabase}}
17
+
18
+ # ─── Database ────────────────────────────────────────────────────────────
19
+ # Replace with your real connection string.
9
20
  DATABASE_URL="postgresql://user:password@localhost:5432/{{projectName}}"
10
21
  {{/if}}
@@ -1,11 +1,26 @@
1
1
  node_modules
2
- {{#ifEquals framework "nextjs"}}
2
+ {{#ifNext framework}}
3
3
  .next
4
4
  out
5
5
  {{else}}
6
6
  dist
7
- {{/ifEquals}}
7
+ {{/ifNext}}
8
+
9
+ # Environment files — NEVER commit these. Real keys live here.
10
+ # `.env.local`, `.env.development.local`, `.env.production.local`,
11
+ # and any per-package `.env*.local` are matched by the patterns below.
8
12
  .env
13
+ .env.local
9
14
  .env*.local
15
+ .env.*.local
16
+
17
+ # OS / editor
10
18
  .DS_Store
11
19
  *.tgz
20
+ .idea/
21
+ .vscode/
22
+
23
+ # Logs / coverage
24
+ *.log
25
+ .turbo/
26
+ coverage/
@@ -0,0 +1,9 @@
1
+ import { SignInForm } from '@githat/nextjs';
2
+
3
+ export default function SignInPage() {
4
+ return (
5
+ <main {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b]"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b' }}{{/if}}>
6
+ <SignInForm signUpUrl="/sign-up" {{#if includeForgotPassword}}forgotPasswordUrl="/forgot-password"{{/if}} />
7
+ </main>
8
+ );
9
+ }
@@ -0,0 +1,9 @@
1
+ import { SignUpForm } from '@githat/nextjs';
2
+
3
+ export default function SignUpPage() {
4
+ return (
5
+ <main {{#if useTailwind}}className="flex items-center justify-center min-h-screen bg-[#09090b]"{{else}}style=\{{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh', background: '#09090b' }}{{/if}}>
6
+ <SignUpForm signInUrl="/sign-in" />
7
+ </main>
8
+ );
9
+ }
@@ -0,0 +1,87 @@
1
+ /*
2
+ * Tailwind v4 — required because @githat/nextjs/styles is processed
3
+ * through @tailwindcss/postcss. Plain doesn't ship utility classes,
4
+ * but the import is needed for the auth pages to render styled.
5
+ */
6
+ @import "tailwindcss";
7
+
8
+ /*
9
+ * Plain template — self-contained globals.
10
+ *
11
+ * Defines the minimum CSS variables a GitHat app uses for layout and
12
+ * the auth-page styling that ships with @githat/nextjs/styles.
13
+ * Override these in your own files when you want a real theme.
14
+ *
15
+ * Light theme by default; flip --bg/--fg for dark.
16
+ */
17
+
18
+ :root {
19
+ /* Surface */
20
+ --bg: #ffffff;
21
+ --surface: #fafafa;
22
+ --surface-sub: #f4f4f5;
23
+
24
+ /* Borders */
25
+ --border: #e5e7eb;
26
+
27
+ /* Foreground */
28
+ --fg: #0a0a0a;
29
+ --fg-muted: #525252;
30
+ --fg-subtle: #737373;
31
+
32
+ /* Brand — change these two to re-skin the whole auth flow */
33
+ --primary: #6366f1;
34
+ --accent: #f59e0b;
35
+
36
+ /* Semantic */
37
+ --success: #16a34a;
38
+ --warn: #d97706;
39
+ --danger: #dc2626;
40
+
41
+ /* Spacing — used by @githat/nextjs/styles */
42
+ --space-1: 0.25rem;
43
+ --space-2: 0.5rem;
44
+ --space-3: 0.75rem;
45
+ --space-4: 1rem;
46
+ --space-6: 1.5rem;
47
+ --space-8: 2rem;
48
+
49
+ /* Radius */
50
+ --radius: 0.5rem;
51
+ --radius-md: 0.5rem;
52
+ --radius-lg: 0.75rem;
53
+
54
+ /* Fonts */
55
+ --font-sans: 'Inter', system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
56
+ --font-wordmark: 'Instrument Serif', Georgia, serif;
57
+ }
58
+
59
+ @media (prefers-color-scheme: dark) {
60
+ :root {
61
+ --bg: #0a0a0a;
62
+ --surface: #18181b;
63
+ --surface-sub: #27272a;
64
+ --border: #3f3f46;
65
+ --fg: #fafafa;
66
+ --fg-muted: #a1a1aa;
67
+ --fg-subtle: #71717a;
68
+ }
69
+ }
70
+
71
+ * {
72
+ box-sizing: border-box;
73
+ margin: 0;
74
+ padding: 0;
75
+ }
76
+
77
+ body {
78
+ font-family: var(--font-sans);
79
+ background: var(--bg);
80
+ color: var(--fg);
81
+ line-height: 1.5;
82
+ }
83
+
84
+ a {
85
+ color: inherit;
86
+ text-decoration: none;
87
+ }