showpane 0.4.3 → 0.4.5

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.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-04-09T11:52:07.132Z",
4
- "scaffoldVersion": "0.2.0",
3
+ "generatedAt": "2026-04-09T14:11:15.147Z",
4
+ "scaffoldVersion": "0.2.2",
5
5
  "files": {
6
6
  ".env.example": "0dd692f1c7e6bcabdf5dbdfe9abb73797d79d8e90da150d6098b63ddc695dc29",
7
7
  ".gitignore": "998e5f43865ea56ac79a05acfd5d4b0d696f310bd5325a1ed458c3d40154d437",
8
- "VERSION": "1f930dd1f133c1f97a94fe3acb8db34372cf4c01ffdb2b3ff4ca72f9494121e9",
8
+ "VERSION": "998f6b887ed7a6ef44e84210d0237b715bed42ea7254f48dc418afec0a484103",
9
9
  "docker-compose.yml": "420fd123da019c22f03662933537e24779b4c2c91f90c23abfec5965cd0f35ce",
10
10
  "docker/Caddyfile": "d9c58086986795f5b3e42ff9b5942e60b8df946a1a0c40351381616c0b4d2bed",
11
11
  "docker/Dockerfile": "340470e3735ea53b2c03003a13a91361652291add33c40a2bf13e6af2a8cb73a",
@@ -47,7 +47,8 @@
47
47
  "src/app/api/health/route.ts": "78fff55707372ce0cd6e9e49ef4f049622bc43cc42916d3f83e0162409d678b1",
48
48
  "src/app/globals.css": "28dcda76006d0e6af01b6dcf1a315dc5b5b6931c880fc53fd6565ff09d5dd13a",
49
49
  "src/app/layout.tsx": "c17aabeb2b486f023e777230343ace6cc06840f641a10b9dd9f65e092018f82f",
50
- "src/app/page.tsx": "a0778e98016957ce4ccdb58fae9be0a4a71e1961fd39a2dfca1612914179b1ae",
50
+ "src/app/page.tsx": "df6abc817e751782df1f59ed770d6e0b47d3a24ea4ad3682485680bdf71845f8",
51
+ "src/components/copy-button.tsx": "2f3d1d8a6a0a570c8d78e19c3c15519c44af17b5d8893ae5a5f57db5ecce7077",
51
52
  "src/components/portal-login.tsx": "8b0d91bb28674e1102fd2e5b5ddcc3a93755dd806fbd3d1b2dbea2646cffca5e",
52
53
  "src/components/portal-shell.tsx": "a4e16e118ef93f79e71fb69e80f1fac6e6fff90f0fbdacdf8deb821a57656877",
53
54
  "src/lib/abuse-controls.ts": "d79d58d93267aca48ad0b7b9b91f753c9a3c27263e4e98daf768a950c44a6fc6",
@@ -1 +1 @@
1
- 0.2.0
1
+ 0.2.2
@@ -1,177 +1,161 @@
1
+ import { CopyButton } from "@/components/copy-button";
2
+ import { resolveDefaultOrganizationId } from "@/lib/client-portals";
1
3
  import { prisma } from "@/lib/db";
2
4
  import { getRuntimeState, isRuntimeSnapshotMode } from "@/lib/runtime-state";
5
+ import { ArrowUpRight, BookOpen, Command, MessageSquareQuote } from "lucide-react";
3
6
  import Link from "next/link";
4
- import { Presentation, Briefcase, UserPlus } from "lucide-react";
5
7
  import os from "node:os";
6
8
  import path from "node:path";
7
9
 
8
- const templates = [
9
- {
10
- name: "Sales Follow-up",
11
- description: "Meeting notes, next steps, documents",
12
- icon: Presentation,
13
- },
14
- {
15
- name: "Consulting",
16
- description: "Project overview, deliverables, timeline",
17
- icon: Briefcase,
18
- },
19
- {
20
- name: "Onboarding",
21
- description: "Welcome, setup steps, resources",
22
- icon: UserPlus,
23
- },
10
+ const GUIDE_URL = "https://app.showpane.com/docs/first-portal";
11
+ const PROMPT_EXAMPLES = [
12
+ "Create a portal for my client Acme based on my call transcript, which is here: [paste it]",
13
+ "Create a portal for my client Acme based on my call from earlier today. Use the Granola MCP to grab the transcript.",
24
14
  ];
25
15
 
26
16
  export default async function Home() {
27
17
  let portalCount = 0;
28
18
  const showpaneBinDir = path.join(os.homedir(), ".showpane", "bin");
29
- const resumeCommand = (process.env.PATH ?? "").split(path.delimiter).includes(showpaneBinDir)
30
- ? "showpane claude"
31
- : "npx showpane claude";
19
+ const prefersCanonicalCommand = (process.env.PATH ?? "").split(path.delimiter).includes(showpaneBinDir);
20
+ const primaryCommand = "showpane claude";
21
+ const fallbackCommand = "npx showpane claude";
32
22
  try {
33
23
  if (isRuntimeSnapshotMode()) {
34
24
  const state = await getRuntimeState();
35
25
  portalCount = state?.portals.length ?? 0;
36
26
  } else {
37
- portalCount = await prisma.clientPortal.count();
27
+ const organizationId = await resolveDefaultOrganizationId();
28
+ if (organizationId) {
29
+ portalCount = await prisma.clientPortal.count({
30
+ where: { organizationId },
31
+ });
32
+ }
38
33
  }
39
34
  } catch {
40
35
  // DB not ready yet — show welcome page with 0 portals
41
36
  }
42
37
 
43
38
  return (
44
- <main className="min-h-screen flex flex-col">
45
- {/* Hero zone */}
46
- <div className="bg-gradient-to-b from-[#2C5278] to-[#5A8BB5] px-4 py-16 md:py-24 text-center relative overflow-hidden">
47
- <div
48
- className="absolute inset-0 opacity-[0.07]"
49
- style={{
50
- backgroundImage: "radial-gradient(circle, white 1px, transparent 1px)",
51
- backgroundSize: "24px 24px",
52
- }}
53
- />
54
- <div className="relative">
55
- <h1 className="sr-only">SHOWPANE</h1>
56
- <div
57
- role="img"
58
- aria-label="SHOWPANE"
59
- className="font-mono text-white text-[0.45rem] leading-[1.1] sm:text-[0.55rem] md:text-xs whitespace-pre select-none mx-auto w-fit"
60
- >
61
- {`███████╗██╗ ██╗ ██████╗ ██╗ ██╗██████╗ █████╗ ███╗ ██╗███████╗
62
- ██╔════╝██║ ██║██╔═══██╗██║ ██║██╔══██╗██╔══██╗████╗ ██║██╔════╝
63
- ███████╗███████║██║ ██║██║ █╗ ██║██████╔╝███████║██╔██╗ ██║█████╗
64
- ╚════██║██╔══██║██║ ██║██║███╗██║██╔═══╝ ██╔══██║██║╚██╗██║██╔══╝
65
- ███████║██║ ██║╚██████╔╝╚███╔███╔╝██║ ██║ ██║██║ ╚████║███████╗
66
- ╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══╝╚══╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝`}
39
+ <main className="min-h-screen bg-[#f6f1e8] text-slate-900">
40
+ <div className="bg-[radial-gradient(circle_at_top,_rgba(255,255,255,0.24),_transparent_48%),linear-gradient(180deg,_#214668_0%,_#3d6f9c_100%)] px-4 py-16 text-white sm:py-20">
41
+ <div className="mx-auto max-w-3xl">
42
+ <div className="inline-flex items-center rounded-full border border-white/20 bg-white/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.2em] text-white/80">
43
+ Local app running
67
44
  </div>
68
- <p className="mt-6 text-white/90 text-lg">
69
- Create professional client portals with Claude Code.
45
+ <h1 className="mt-6 text-4xl font-semibold tracking-tight sm:text-5xl">
46
+ Your Showpane workspace is ready
47
+ </h1>
48
+ <p className="mt-4 max-w-2xl text-base leading-7 text-white/82 sm:text-lg">
49
+ Open a new terminal window, run Showpane with Claude, and create your first client portal.
70
50
  </p>
71
51
  </div>
72
52
  </div>
73
53
 
74
- {/* Action zone */}
75
- <div className="flex-1 bg-[#FDFBF7] px-4 py-12 md:py-16">
76
- <div className="max-w-lg mx-auto">
77
- {/* Steps */}
78
- <ol className="space-y-4">
79
- <li className="border border-gray-200 rounded-lg p-5 bg-white">
80
- <div className="flex items-start gap-4">
81
- <span className="flex-shrink-0 w-7 h-7 rounded-full bg-gray-900 text-white text-sm font-medium flex items-center justify-center">
82
- 1
83
- </span>
84
- <div className="min-w-0">
85
- <p className="text-gray-900 font-medium mb-2">
86
- In a terminal, reopen your Showpane workspace
87
- </p>
88
- <code className="block text-sm text-gray-300 font-mono bg-[#111827] px-3 py-2 rounded overflow-x-auto">
89
- {resumeCommand}
90
- </code>
54
+ <div className="px-4 py-10 sm:py-12">
55
+ <div className="mx-auto max-w-3xl space-y-5">
56
+ <section className="rounded-[28px] border border-slate-200 bg-slate-950 p-6 text-white shadow-[0_24px_80px_rgba(15,23,42,0.16)] sm:p-7">
57
+ <div className="flex items-start justify-between gap-4">
58
+ <div>
59
+ <div className="flex items-center gap-2 text-sm font-semibold text-white/80">
60
+ <Command className="h-4 w-4" />
61
+ Start with Claude
91
62
  </div>
92
- </div>
93
- </li>
94
-
95
- <li className="border border-gray-200 rounded-lg p-5 bg-white">
96
- <div className="flex items-start gap-4">
97
- <span className="flex-shrink-0 w-7 h-7 rounded-full bg-gray-900 text-white text-sm font-medium flex items-center justify-center">
98
- 2
99
- </span>
100
- <div className="min-w-0">
101
- <p className="text-gray-900 font-medium mb-2">
102
- Use the fast path slash command
63
+ <p className="mt-3 text-sm leading-6 text-white/72">
64
+ Open a new terminal window and run this command there. Your current terminal is running the local app, so this command belongs in a fresh one.
65
+ </p>
66
+ {!prefersCanonicalCommand && (
67
+ <p className="mt-2 text-xs leading-5 text-white/60">
68
+ If <code className="font-mono text-white">{primaryCommand}</code> isn&apos;t available in your shell yet, use{" "}
69
+ <code className="font-mono text-white">{fallbackCommand}</code>.
103
70
  </p>
104
- <code className="block text-sm text-gray-300 font-mono bg-[#111827] px-3 py-2 rounded">
105
- /portal create acme-health
106
- </code>
107
- </div>
71
+ )}
108
72
  </div>
109
- </li>
73
+ <CopyButton text={primaryCommand} invert />
74
+ </div>
110
75
 
111
- <li className="border border-gray-200 rounded-lg p-5 bg-white">
112
- <div className="flex items-start gap-4">
113
- <span className="flex-shrink-0 w-7 h-7 rounded-full bg-gray-900 text-white text-sm font-medium flex items-center justify-center">
114
- 3
115
- </span>
116
- <div className="min-w-0">
117
- <p className="text-gray-900 font-medium mb-2">
118
- Or tell it what to create
119
- </p>
120
- <code className="block text-sm text-gray-300 font-mono bg-[#111827] px-3 py-2 rounded overflow-x-auto">
121
- Create a portal for my call with [client name]
122
- </code>
123
- </div>
124
- </div>
125
- </li>
126
- </ol>
76
+ <div className="mt-5 rounded-2xl border border-white/10 bg-white/5 p-4">
77
+ <code className="block overflow-x-auto font-mono text-sm text-white sm:text-[15px]">
78
+ {primaryCommand}
79
+ </code>
80
+ </div>
81
+ </section>
127
82
 
128
- <p className="mt-4 text-xs text-gray-400 text-center">
129
- Don&apos;t have Claude Code?{" "}
130
- <a
131
- href="https://claude.ai/code"
132
- className="text-blue-600 hover:underline"
133
- target="_blank"
134
- rel="noopener noreferrer"
135
- >
136
- Install it here
137
- </a>
138
- </p>
83
+ <section className="rounded-[28px] border border-slate-200 bg-white p-6 shadow-[0_20px_70px_rgba(15,23,42,0.07)] sm:p-7">
84
+ <div className="flex items-center gap-2 text-sm font-semibold text-slate-700">
85
+ <MessageSquareQuote className="h-4 w-4" />
86
+ What to say to Claude
87
+ </div>
139
88
 
140
- {/* Template previews */}
141
- <div className="mt-12">
142
- <p className="text-sm font-medium text-gray-500 text-center mb-4">
143
- Claude Code generates portals from templates
144
- </p>
145
- <div className="grid grid-cols-1 sm:grid-cols-3 gap-3">
146
- {templates.map((t) => (
89
+ <div className="mt-5 space-y-3">
90
+ {PROMPT_EXAMPLES.map((example, index) => (
147
91
  <div
148
- key={t.name}
149
- className="border border-gray-200 rounded-lg p-4 bg-white text-center"
92
+ key={example}
93
+ className="rounded-2xl border border-slate-200 bg-slate-50 p-4"
150
94
  >
151
- <t.icon className="h-5 w-5 text-gray-400 mx-auto mb-2" />
152
- <p className="text-sm font-medium text-gray-900">{t.name}</p>
153
- <p className="text-xs text-gray-500 mt-1">{t.description}</p>
95
+ <div className="flex items-start justify-between gap-3">
96
+ <p className="text-[11px] font-semibold uppercase tracking-[0.18em] text-slate-400">
97
+ Example {index + 1}
98
+ </p>
99
+ <CopyButton text={example} />
100
+ </div>
101
+ <p className="mt-3 whitespace-pre-wrap pr-2 font-mono text-sm leading-6 text-slate-700">
102
+ {example}
103
+ </p>
154
104
  </div>
155
105
  ))}
156
106
  </div>
107
+ </section>
108
+
109
+ <section className="rounded-[32px] border border-[#b8d2ee] bg-[linear-gradient(135deg,_#f8fcff_0%,_#d8ebfb_55%,_#bed8f1_100%)] p-6 shadow-[0_24px_90px_rgba(61,111,156,0.18)] sm:p-8">
110
+ <div className="flex flex-col gap-6 sm:flex-row sm:items-start sm:justify-between">
111
+ <div className="max-w-xl">
112
+ <div className="flex items-center gap-2 text-sm font-semibold text-[#214668]">
113
+ <BookOpen className="h-4 w-4" />
114
+ Need help creating your first portal?
115
+ </div>
116
+ <p className="mt-3 text-base leading-7 text-[#284f74]">
117
+ Follow the step-by-step guide with examples, best practices, and a walkthrough video.
118
+ </p>
119
+ </div>
120
+ <a
121
+ href={GUIDE_URL}
122
+ className="inline-flex items-center justify-center gap-2 rounded-full bg-[#214668] px-4 py-2 text-sm font-semibold text-white transition hover:bg-[#18344d]"
123
+ target="_blank"
124
+ rel="noopener noreferrer"
125
+ >
126
+ Open Creating Your First Portal
127
+ <ArrowUpRight className="h-4 w-4" />
128
+ </a>
129
+ </div>
130
+ </section>
131
+
132
+ <div className="space-y-3 pt-2 text-center">
133
+ <p className="text-xs text-slate-500">
134
+ Need Claude Code first?{" "}
135
+ <a
136
+ href="https://claude.ai/code"
137
+ className="font-medium text-[#214668] underline decoration-slate-300 underline-offset-4 hover:decoration-[#214668]"
138
+ target="_blank"
139
+ rel="noopener noreferrer"
140
+ >
141
+ Install it here
142
+ </a>
143
+ </p>
144
+
145
+ <div className="space-y-1 text-xs text-slate-400">
146
+ {portalCount > 0 && (
147
+ <p>
148
+ You have {portalCount} portal{portalCount !== 1 ? "s" : ""}.{" "}
149
+ <Link href="/client" className="text-[#214668] hover:underline">
150
+ Go to login
151
+ </Link>
152
+ </p>
153
+ )}
154
+ <p>Powered by Claude Code</p>
155
+ </div>
157
156
  </div>
158
157
  </div>
159
158
  </div>
160
-
161
- {/* Footer zone */}
162
- <footer className="bg-[#FDFBF7] px-4 pb-8 text-center space-y-2">
163
- {portalCount > 0 && (
164
- <p className="text-sm text-gray-500">
165
- You have {portalCount} portal{portalCount !== 1 ? "s" : ""}.{" "}
166
- <Link href="/client" className="text-blue-600 hover:underline">
167
- Go to login
168
- </Link>
169
- </p>
170
- )}
171
- <p className="text-xs text-gray-400">
172
- Powered by Claude Code
173
- </p>
174
- </footer>
175
159
  </main>
176
160
  );
177
161
  }
@@ -0,0 +1,49 @@
1
+ "use client";
2
+
3
+ import { Check, Copy } from "lucide-react";
4
+ import { useEffect, useState } from "react";
5
+
6
+ type CopyButtonProps = {
7
+ text: string;
8
+ invert?: boolean;
9
+ };
10
+
11
+ export function CopyButton({ text, invert = false }: CopyButtonProps) {
12
+ const [copied, setCopied] = useState(false);
13
+ const [copyError, setCopyError] = useState(false);
14
+
15
+ useEffect(() => {
16
+ if (!copied && !copyError) return;
17
+
18
+ const timeout = window.setTimeout(() => {
19
+ setCopied(false);
20
+ setCopyError(false);
21
+ }, 2000);
22
+
23
+ return () => window.clearTimeout(timeout);
24
+ }, [copied, copyError]);
25
+
26
+ async function handleCopy() {
27
+ try {
28
+ await navigator.clipboard.writeText(text);
29
+ setCopied(true);
30
+ } catch {
31
+ setCopyError(true);
32
+ }
33
+ }
34
+
35
+ return (
36
+ <button
37
+ type="button"
38
+ onClick={handleCopy}
39
+ className={
40
+ invert
41
+ ? "inline-flex items-center gap-1.5 rounded-full border border-white/20 bg-white/10 px-3 py-1.5 text-xs font-semibold text-white transition hover:bg-white/20"
42
+ : "inline-flex items-center gap-1.5 rounded-full border border-slate-200 bg-white px-3 py-1.5 text-xs font-semibold text-slate-700 transition hover:border-slate-300 hover:bg-slate-50"
43
+ }
44
+ >
45
+ {copied ? <Check className="h-3.5 w-3.5" /> : <Copy className="h-3.5 w-3.5" />}
46
+ {copied ? "Copied!" : copyError ? "Failed" : "Copy"}
47
+ </button>
48
+ );
49
+ }
@@ -1 +1 @@
1
- 1.1.0 (requires app >= 0.2.0)
1
+ 1.1.2 (requires app >= 0.2.2)
@@ -9,30 +9,46 @@ async function main() {
9
9
  const args = process.argv.slice(2);
10
10
 
11
11
  if (args.includes("--help")) {
12
- console.log("Usage: list-portals [--org-id <orgId>]");
13
- console.log("Lists all client portals. Defaults to first organization if --org-id omitted.");
12
+ console.log("Usage: list-portals [--org-id <orgId>] [--org-slug <slug>]");
13
+ console.log("Lists all client portals. Defaults to first organization if no org filter is provided.");
14
14
  process.exit(0);
15
15
  }
16
16
 
17
17
  const prisma = new PrismaClient();
18
18
  try {
19
19
  const getArg = (flag: string) => { const i = args.indexOf(flag); return i !== -1 ? args[i + 1] : undefined; };
20
- let orgId = getArg("--org-id");
20
+ const orgId = getArg("--org-id");
21
+ const orgSlug = getArg("--org-slug");
21
22
 
22
- if (!orgId) {
23
- const firstOrg = await prisma.organization.findFirst({ select: { id: true } });
24
- if (!firstOrg) fail("No organizations found");
25
- orgId = firstOrg.id;
26
- }
23
+ const organization = orgId
24
+ ? await prisma.organization.findUnique({
25
+ where: { id: orgId },
26
+ select: { id: true, name: true, slug: true },
27
+ })
28
+ : orgSlug
29
+ ? await prisma.organization.findUnique({
30
+ where: { slug: orgSlug },
31
+ select: { id: true, name: true, slug: true },
32
+ })
33
+ : await prisma.organization.findFirst({
34
+ orderBy: { createdAt: "asc" },
35
+ select: { id: true, name: true, slug: true },
36
+ });
37
+
38
+ if (!organization) fail("No organizations found");
27
39
 
28
40
  const portals = await prisma.clientPortal.findMany({
29
- where: { organizationId: orgId },
41
+ where: { organizationId: organization.id },
30
42
  include: { organization: { select: { name: true } } },
31
43
  orderBy: { createdAt: "desc" },
32
44
  });
33
45
 
34
46
  console.log(JSON.stringify({
35
47
  ok: true,
48
+ orgId: organization.id,
49
+ orgSlug: organization.slug,
50
+ orgName: organization.name,
51
+ total: portals.length,
36
52
  portals: portals.map((p) => ({
37
53
  slug: p.slug,
38
54
  companyName: p.companyName,
@@ -71,6 +71,24 @@ mention them unless they directly affect the current task.
71
71
 
72
72
  ## Steps
73
73
 
74
+ ### Step 0: Check first-portal guide eligibility
75
+
76
+ Before asking for the slug or generating anything, determine the existing portal count
77
+ for the active organization:
78
+
79
+ ```bash
80
+ cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/list-portals.ts" --org-slug "$ORG_SLUG"
81
+ ```
82
+
83
+ The script returns JSON with `total` and `orgId` for the active org. Reuse that
84
+ `orgId` for the slug validation and portal creation commands below.
85
+
86
+ - If `total` is `0`, `1`, or `2`, say this line exactly once:
87
+ `If helpful, the first-portal guide has examples and best practices: https://app.showpane.com/docs/first-portal`
88
+ - If `total` is `3` or higher, do not mention the guide.
89
+ - Mention it once per invocation only. Do not repeat it later in the same flow.
90
+ - Use the org-scoped portal count directly. Do not use learnings or timeline heuristics.
91
+
74
92
  ### Step 1: Determine the portal slug
75
93
 
76
94
  If the user provided a slug (e.g., `/portal create acme-health`), use it. Otherwise, infer from context — the company name mentioned in conversation, a meeting transcript, or ask the user directly.
package/dist/index.js CHANGED
@@ -331,6 +331,18 @@ function stepFailure(label, errorLike, hint) {
331
331
  }
332
332
  process.exit(1);
333
333
  }
334
+ function shouldUseSpinner(verbose) {
335
+ return process.stdout.isTTY && !verbose;
336
+ }
337
+ function stepStartForCreate(label, options) {
338
+ stepStart(label, shouldUseSpinner(options.verbose));
339
+ }
340
+ function stepSuccessForCreate(label) {
341
+ stepSuccess(label);
342
+ }
343
+ function stepFailureForCreate(label, errorLike, hint) {
344
+ stepFailure(label, errorLike, hint);
345
+ }
334
346
  function attachSpinnerCleanup() {
335
347
  const cleanup = () => stopSpinner();
336
348
  process.on("exit", cleanup);
@@ -792,9 +804,11 @@ function printCreateSuccessCard(projectRoot, url) {
792
804
  console.log(` ${BOLD}App:${RESET} ${url}`);
793
805
  console.log(` ${BOLD}Demo:${RESET} example / demo-only-password`);
794
806
  console.log();
795
- console.log(` ${BOLD}Next:${RESET}`);
807
+ console.log(` ${BOLD}Next (in a new terminal window):${RESET}`);
796
808
  console.log(` ${DIM}${resumeCommand}${RESET}`);
797
809
  console.log();
810
+ console.log(` ${DIM}Your current terminal is running the local app logs, so open a fresh terminal before you run that command.${RESET}`);
811
+ console.log();
798
812
  console.log(` ${BOLD}Try:${RESET}`);
799
813
  console.log(` ${DIM}Create a portal for my call with Acme Health${RESET}`);
800
814
  if (resumeHint) {
@@ -1046,19 +1060,19 @@ async function createProject(args) {
1046
1060
  console.log();
1047
1061
  blue(`Setting up ${BOLD}${companyName}${RESET} portal as ${DIM}${dirName}/${RESET}`);
1048
1062
  console.log();
1049
- stepStart("Create project");
1063
+ stepStartForCreate("Create project", options);
1050
1064
  try {
1051
1065
  copyScaffoldFiles(join(bundleRoot, "scaffold"), projectRoot, scaffoldManifest);
1052
- stepSuccess("Project created");
1066
+ stepSuccessForCreate("Project created");
1053
1067
  } catch (errorLike) {
1054
- stepFailure("Create project", errorLike);
1068
+ stepFailureForCreate("Create project", errorLike);
1055
1069
  }
1056
- stepStart("Install dependencies");
1070
+ stepStartForCreate("Install dependencies", options);
1057
1071
  try {
1058
1072
  installDependencies(projectRoot, options.verbose);
1059
- stepSuccess("Dependencies installed");
1073
+ stepSuccessForCreate("Dependencies installed");
1060
1074
  } catch (errorLike) {
1061
- stepFailure(
1075
+ stepFailureForCreate(
1062
1076
  "Install dependencies",
1063
1077
  errorLike,
1064
1078
  "Check your Node.js version and network connection, then try again."
@@ -1072,19 +1086,19 @@ async function createProject(args) {
1072
1086
  AUTH_SECRET="${authSecret}"
1073
1087
  `
1074
1088
  );
1075
- stepStart("Configure database");
1089
+ stepStartForCreate("Configure database", options);
1076
1090
  try {
1077
1091
  generateLocalDatabase(projectRoot, databaseUrl, options.verbose);
1078
1092
  seedProject(projectRoot, databaseUrl, options.verbose);
1079
- stepSuccess("Database configured");
1093
+ stepSuccessForCreate("Database configured");
1080
1094
  } catch (errorLike) {
1081
- stepFailure(
1095
+ stepFailureForCreate(
1082
1096
  "Configure database",
1083
1097
  errorLike,
1084
1098
  "Check Prisma setup and the generated .env file, then retry the install."
1085
1099
  );
1086
1100
  }
1087
- stepStart("Install Claude skills");
1101
+ stepStartForCreate("Install Claude skills", options);
1088
1102
  let toolchainInfo;
1089
1103
  try {
1090
1104
  toolchainInfo = syncToolchain(bundleRoot, showpaneVersion, false);
@@ -1102,15 +1116,15 @@ AUTH_SECRET="${authSecret}"
1102
1116
  toolchainInfo.toolchainVersion
1103
1117
  );
1104
1118
  tryInitializeGitRepo(projectRoot, false);
1105
- stepSuccess("Claude skills installed");
1119
+ stepSuccessForCreate("Claude skills installed");
1106
1120
  } catch (errorLike) {
1107
- stepFailure(
1121
+ stepFailureForCreate(
1108
1122
  "Install Claude skills",
1109
1123
  errorLike,
1110
1124
  "Check permissions for ~/.showpane and ~/.claude/skills, then try again."
1111
1125
  );
1112
1126
  }
1113
- stepStart("Start app");
1127
+ stepStartForCreate("Start app", options);
1114
1128
  let serverStart;
1115
1129
  try {
1116
1130
  serverStart = await startDevServer(
@@ -1120,7 +1134,7 @@ AUTH_SECRET="${authSecret}"
1120
1134
  options.verbose
1121
1135
  );
1122
1136
  } catch (errorLike) {
1123
- stepFailure(
1137
+ stepFailureForCreate(
1124
1138
  "Start app",
1125
1139
  errorLike,
1126
1140
  `Run ${BOLD}cd ${dirName} && npm run dev${RESET} for more detail.`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "showpane",
3
- "version": "0.4.3",
3
+ "version": "0.4.5",
4
4
  "description": "CLI for Showpane — AI-generated client portals",
5
5
  "type": "module",
6
6
  "bin": {