showpane 0.4.13 → 0.4.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/bundle/meta/scaffold-manifest.json +10 -10
- package/bundle/scaffold/VERSION +1 -1
- package/bundle/scaffold/prisma/seed.ts +40 -35
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +7 -0
- package/bundle/scaffold/src/app/(portal)/client/example/example-client.tsx +1 -2
- package/bundle/scaffold/src/app/(portal)/client/page.tsx +5 -4
- package/bundle/scaffold/src/app/page.tsx +43 -6
- package/bundle/scaffold/src/components/portal-shell.tsx +23 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +33 -0
- package/bundle/toolchain/CLI_VERSION +1 -0
- package/bundle/toolchain/TELEMETRY_CONFIG.json +4 -0
- package/bundle/toolchain/VERSION +1 -1
- package/bundle/toolchain/bin/ensure-cloud-project-link.ts +34 -1
- package/bundle/toolchain/bin/showpane-config +108 -29
- package/bundle/toolchain/bin/showpane-telemetry-log +84 -0
- package/bundle/toolchain/bin/showpane-telemetry-sync +212 -0
- package/bundle/toolchain/bin/showpane-update-check +130 -0
- package/bundle/toolchain/skills/SKILL.md.tmpl +13 -0
- package/bundle/toolchain/skills/VERSION +1 -1
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +60 -38
- package/bundle/toolchain/skills/portal-analytics/SKILL.md.tmpl +192 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +65 -67
- package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +264 -0
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +66 -49
- package/bundle/toolchain/skills/portal-credentials/SKILL.md.tmpl +198 -0
- package/bundle/toolchain/skills/portal-delete/SKILL.md +63 -41
- package/bundle/toolchain/skills/portal-delete/SKILL.md.tmpl +194 -0
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +57 -47
- package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +452 -0
- package/bundle/toolchain/skills/portal-dev/SKILL.md +65 -47
- package/bundle/toolchain/skills/portal-dev/SKILL.md.tmpl +228 -0
- package/bundle/toolchain/skills/portal-list/SKILL.md +64 -43
- package/bundle/toolchain/skills/portal-list/SKILL.md.tmpl +181 -0
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +331 -162
- package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +340 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +65 -44
- package/bundle/toolchain/skills/portal-preview/SKILL.md.tmpl +171 -0
- package/bundle/toolchain/skills/portal-setup/SKILL.md +79 -60
- package/bundle/toolchain/skills/portal-setup/SKILL.md.tmpl +227 -0
- package/bundle/toolchain/skills/portal-share/SKILL.md +69 -47
- package/bundle/toolchain/skills/portal-share/SKILL.md.tmpl +162 -0
- package/bundle/toolchain/skills/portal-status/SKILL.md +58 -37
- package/bundle/toolchain/skills/portal-status/SKILL.md.tmpl +196 -0
- package/bundle/toolchain/skills/portal-update/SKILL.md +60 -46
- package/bundle/toolchain/skills/portal-update/SKILL.md.tmpl +269 -0
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +55 -33
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md.tmpl +164 -0
- package/bundle/toolchain/skills/portal-verify/SKILL.md +69 -14
- package/bundle/toolchain/skills/portal-verify/SKILL.md.tmpl +224 -0
- package/bundle/toolchain/skills/shared/preamble.md +30 -126
- package/bundle/toolchain/skills/shared/runtime-principles.md +25 -0
- package/bundle/toolchain/templates/sales-followup/sales-followup-client.tsx +1 -1
- package/dist/index.js +79 -14
- package/package.json +5 -2
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: portal-preview
|
|
3
|
+
description: |
|
|
4
|
+
Open a portal in the browser for preview. Use when asked to "preview portal",
|
|
5
|
+
"open portal", "view portal", "show me the portal", or "open in browser". (showpane)
|
|
6
|
+
allowed-tools: [Bash, Read]
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
{{PREAMBLE}}
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
This skill opens a portal in the user's default web browser. It is the fastest way to see what a portal looks like after creating or updating it. The skill determines the correct URL based on whether a local dev server is running and whether a public app URL is configured, then opens it using the platform's native command.
|
|
14
|
+
|
|
15
|
+
This is a lightweight skill -- it does not start a dev server, build the app, or modify anything. It just opens a URL. If the dev server is not running, it tells the user how to start it.
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### Step 1: Identify the target portal
|
|
20
|
+
|
|
21
|
+
If the user specified a slug (e.g., "preview whzan"), use that slug directly.
|
|
22
|
+
|
|
23
|
+
If no slug is provided, check how many portals exist:
|
|
24
|
+
- If there is exactly one portal, use that one and mention it: "Opening your only portal: <slug>"
|
|
25
|
+
- If there are multiple portals, ask: "Which portal do you want to preview? Run /portal-list to see your portals."
|
|
26
|
+
- If there are zero portals, say: "No portals found. Create one first with /portal-create."
|
|
27
|
+
|
|
28
|
+
For single-portal organizations, auto-selecting saves a round trip.
|
|
29
|
+
|
|
30
|
+
### Step 2: Determine the correct URL
|
|
31
|
+
|
|
32
|
+
The URL depends on the environment. Check in this order of priority:
|
|
33
|
+
|
|
34
|
+
**Option A: Local dev server running (check port 3000)**
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
lsof -i :3000 -sTCP:LISTEN -t >/dev/null 2>&1 && echo "DEV_RUNNING=true" || echo "DEV_RUNNING=false"
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
If a process is listening on port 3000, the URL is:
|
|
41
|
+
```
|
|
42
|
+
http://localhost:3000/client/<slug>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Option B: NEXT_PUBLIC_APP_URL is set**
|
|
46
|
+
|
|
47
|
+
If the environment variable `NEXT_PUBLIC_APP_URL` is set (sourced from `.env` by the preamble), use it:
|
|
48
|
+
```
|
|
49
|
+
${NEXT_PUBLIC_APP_URL}/client/<slug>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This handles deployed URLs, custom domains, and any other public preview target.
|
|
53
|
+
|
|
54
|
+
**Fallback: Nothing running**
|
|
55
|
+
|
|
56
|
+
If no server is detected and no app URL is configured, do not open the browser. Instead, inform the user:
|
|
57
|
+
|
|
58
|
+
"No running server detected. Start the dev server with /portal-dev, or set NEXT_PUBLIC_APP_URL in your .env file."
|
|
59
|
+
|
|
60
|
+
### Step 3: Open the URL in the browser
|
|
61
|
+
|
|
62
|
+
Use the platform-appropriate command:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# macOS
|
|
66
|
+
open "<url>"
|
|
67
|
+
|
|
68
|
+
# Linux
|
|
69
|
+
xdg-open "<url>"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Detect the platform:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
76
|
+
open "<url>"
|
|
77
|
+
elif command -v xdg-open >/dev/null 2>&1; then
|
|
78
|
+
xdg-open "<url>"
|
|
79
|
+
else
|
|
80
|
+
echo "Cannot detect browser opener. Visit: <url>"
|
|
81
|
+
fi
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Run this in a single Bash tool call so the browser opens immediately.
|
|
85
|
+
|
|
86
|
+
### Step 4: Print confirmation
|
|
87
|
+
|
|
88
|
+
After opening, display:
|
|
89
|
+
|
|
90
|
+
```
|
|
91
|
+
Opened portal for <slug> in browser
|
|
92
|
+
URL: http://localhost:3000/client/<slug>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
If the portal has credentials set up, remind the user:
|
|
96
|
+
"Login with the credentials from /portal-credentials <slug>. For external access, publish first with /portal-deploy."
|
|
97
|
+
|
|
98
|
+
If the portal is the example portal (slug is "example"), no credentials are needed -- it is publicly accessible by design.
|
|
99
|
+
|
|
100
|
+
## URL Priority Order
|
|
101
|
+
|
|
102
|
+
To be explicit about resolution order:
|
|
103
|
+
|
|
104
|
+
1. **Dev server on port 3000** -- always preferred for local development, regardless of other settings.
|
|
105
|
+
2. **NEXT_PUBLIC_APP_URL** -- used for deployed environments or when no local server is detected.
|
|
106
|
+
3. **Fallback** -- no URL available, prompt the user to start a server.
|
|
107
|
+
|
|
108
|
+
This order ensures that during development, the user always sees the latest local version, even if NEXT_PUBLIC_APP_URL points to production.
|
|
109
|
+
|
|
110
|
+
## Conventions
|
|
111
|
+
|
|
112
|
+
- Always check for a running server before attempting to open. Opening a URL with no server just shows a browser error page -- a poor experience.
|
|
113
|
+
- Print the full URL so the user can copy-paste it if needed (e.g., to share with a colleague looking at the same screen).
|
|
114
|
+
- Do not open multiple browser tabs. One call to `open` per invocation.
|
|
115
|
+
- If the slug is "example", mention that this is the built-in example portal and does not require authentication.
|
|
116
|
+
- If learnings indicate the user prefers a specific browser or has a custom port, adapt the URL accordingly.
|
|
117
|
+
|
|
118
|
+
## Edge Cases
|
|
119
|
+
|
|
120
|
+
- **Port conflict**: If port 3000 has a non-Showpane process running, `lsof` will still detect it and the URL will be wrong. This is unlikely in practice but worth noting. The user will see a different app in the browser and can correct by specifying the URL manually.
|
|
121
|
+
- **WSL (Windows Subsystem for Linux)**: `xdg-open` may not work. On WSL, use `wslview` or `explorer.exe` instead. Detect WSL via `grep -qi microsoft /proc/version`.
|
|
122
|
+
- **SSH/remote session**: If the user is connected via SSH, opening a browser on the remote machine does nothing useful. Detect this via the `SSH_CONNECTION` environment variable and print the URL instead: "You appear to be in an SSH session. Visit this URL on your local machine: <url>"
|
|
123
|
+
- **Inactive portal**: If the portal is deactivated (`isActive: false`), the preview will show a "not found" page. Warn the user: "Portal '<slug>' is inactive. You'll see a not-found page. Reactivate it first or use /portal-list to check status."
|
|
124
|
+
|
|
125
|
+
## Error Handling
|
|
126
|
+
|
|
127
|
+
- If the preamble fails, stop and display the error.
|
|
128
|
+
- If no server is running and no URL is configured, provide clear instructions rather than opening a dead URL.
|
|
129
|
+
- If the `open` or `xdg-open` command fails, print the URL as a fallback: "Could not open browser automatically. Visit: <url>"
|
|
130
|
+
|
|
131
|
+
## Login Context
|
|
132
|
+
|
|
133
|
+
When the portal opens in the browser, the client will see a login page (unless they have an active session or use a share link). Provide context about how to access the portal:
|
|
134
|
+
|
|
135
|
+
- **Has credentials**: Remind the user of the username (derived from the slug). Do not display the password -- they can get it from `/portal-credentials <slug>` if they need it.
|
|
136
|
+
- **No credentials**: Warn that the portal will show a login page but there are no credentials to enter. Suggest running `/portal-credentials <slug>` first.
|
|
137
|
+
- **Example portal**: The built-in example at `/client/example` is publicly accessible and does not require login. No credentials needed.
|
|
138
|
+
|
|
139
|
+
If the user is previewing to check content before sharing with a client, suggest: "To see exactly what the client will see, open an incognito/private window. Your existing session cookies may affect the view. When the portal looks right, publish it with /portal-deploy before sending anything externally."
|
|
140
|
+
|
|
141
|
+
## Previewing After Changes
|
|
142
|
+
|
|
143
|
+
A common workflow is: edit portal content with `/portal-update`, then preview to verify. If the dev server is running with Next.js hot reload, changes to the client component files will appear immediately without a page refresh.
|
|
144
|
+
|
|
145
|
+
If the user just ran `/portal-update` and then `/portal-preview`, mention: "If you don't see your changes, make sure the dev server is running so hot reload can pick them up."
|
|
146
|
+
|
|
147
|
+
## Multiple Portals Preview
|
|
148
|
+
|
|
149
|
+
If the user asks to "preview all portals" or "open all my portals", open each one in a separate browser tab. However, limit to 5 tabs maximum to avoid browser overload. If there are more than 5 portals, open the 5 most recently updated and note: "Opened the 5 most recently updated portals. Use /portal-preview <slug> for specific portals."
|
|
150
|
+
|
|
151
|
+
To open multiple tabs, run the open command for each URL sequentially:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
open "http://localhost:3000/client/whzan"
|
|
155
|
+
open "http://localhost:3000/client/acme"
|
|
156
|
+
open "http://localhost:3000/client/example"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Each `open` call creates a new tab in the default browser.
|
|
160
|
+
|
|
161
|
+
## Telemetry
|
|
162
|
+
|
|
163
|
+
If telemetry is enabled, the preview skill records a minimal event:
|
|
164
|
+
|
|
165
|
+
```json
|
|
166
|
+
{"skill":"portal-preview","ts":"2026-04-07T12:00:00Z","duration_s":1,"outcome":"success"}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
No portal slug or URL is included in telemetry. The event only records that a preview was triggered.
|
|
170
|
+
|
|
171
|
+
{{COMPLETION}}
|
|
@@ -14,38 +14,42 @@ hooks:
|
|
|
14
14
|
|
|
15
15
|
## Preamble (run first)
|
|
16
16
|
|
|
17
|
-
This skill's preamble is
|
|
17
|
+
This skill's preamble is tolerant of first-run state because setup may create the config.
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
SHOWPANE_HOME="$HOME/.showpane"
|
|
21
|
+
SHOWPANE_BIN="$SHOWPANE_HOME/bin"
|
|
22
|
+
CONFIG="$SHOWPANE_HOME/config.json"
|
|
23
|
+
APP_PATH=""
|
|
24
|
+
DEPLOY_MODE="local"
|
|
25
|
+
ORG_SLUG=""
|
|
22
26
|
EXISTING_CONFIG=false
|
|
27
|
+
|
|
23
28
|
if [ -f "$CONFIG" ]; then
|
|
24
29
|
EXISTING_CONFIG=true
|
|
25
30
|
APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null || true)
|
|
26
|
-
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode',''))" 2>/dev/null ||
|
|
31
|
+
DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null || echo "local")
|
|
27
32
|
ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null || true)
|
|
28
|
-
echo "EXISTING_CONFIG: true"
|
|
29
|
-
echo "APP_PATH: $APP_PATH"
|
|
30
|
-
echo "DEPLOY_MODE: $DEPLOY_MODE"
|
|
31
|
-
echo "ORG_SLUG: $ORG_SLUG"
|
|
32
|
-
else
|
|
33
|
-
echo "EXISTING_CONFIG: false"
|
|
34
|
-
echo "No config found. Starting fresh setup."
|
|
35
33
|
fi
|
|
36
34
|
|
|
37
|
-
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$
|
|
38
|
-
SKILL_VERSION=$(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
|
|
36
|
+
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
37
|
+
_UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
|
|
38
|
+
[ -n "$_UPD" ] && echo "$_UPD" || true
|
|
39
|
+
mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
|
|
40
|
+
touch "$SHOWPANE_HOME/sessions/$PPID"
|
|
41
|
+
find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
|
|
42
|
+
TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
|
|
43
|
+
TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
|
|
44
|
+
_TEL_START=$(date +%s)
|
|
45
|
+
_SESSION_ID="${PPID:-0}-$(date +%s)"
|
|
46
|
+
|
|
47
|
+
LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
|
|
48
|
+
if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
|
|
49
|
+
_RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
|
|
50
|
+
' ',' | sed 's/,$//' || true)
|
|
44
51
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
45
52
|
fi
|
|
46
|
-
|
|
47
|
-
# Search relevant learnings
|
|
48
|
-
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
49
53
|
if [ -f "$LEARN_FILE" ]; then
|
|
50
54
|
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
51
55
|
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
@@ -55,24 +59,43 @@ if [ -f "$LEARN_FILE" ]; then
|
|
|
55
59
|
fi
|
|
56
60
|
fi
|
|
57
61
|
|
|
58
|
-
|
|
59
|
-
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
62
|
+
SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
|
|
60
63
|
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
61
64
|
echo '{"skill":"portal-setup","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
62
|
-
|
|
65
|
+
echo "SHOWPANE: v$SKILL_VERSION | SETUP MODE"
|
|
66
|
+
echo "EXISTING_CONFIG: $EXISTING_CONFIG"
|
|
67
|
+
echo "APP_PATH: ${APP_PATH:-missing}"
|
|
68
|
+
echo "DEPLOY_MODE: $DEPLOY_MODE"
|
|
69
|
+
echo "ORG_SLUG: ${ORG_SLUG:-missing}"
|
|
70
|
+
echo "TELEMETRY: $TEL"
|
|
71
|
+
echo "TEL_PROMPTED: $TEL_PROMPTED"
|
|
63
72
|
```
|
|
64
73
|
|
|
65
|
-
If
|
|
74
|
+
If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
|
|
75
|
+
|
|
76
|
+
If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal-upgrade`.
|
|
77
|
+
|
|
78
|
+
If `TEL_PROMPTED` is `no`, default telemetry to `anonymous` without interrupting the flow. Do not mention telemetry unless the user asks.
|
|
79
|
+
|
|
80
|
+
Run:
|
|
81
|
+
```bash
|
|
82
|
+
"$SHOWPANE_BIN/showpane-config" set telemetry anonymous
|
|
83
|
+
touch "$SHOWPANE_HOME/.telemetry-prompted"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
66
87
|
- After portal-create → suggest /portal-preview
|
|
67
|
-
- After portal-preview → suggest /portal-deploy
|
|
88
|
+
- After portal-preview → suggest /portal-deploy
|
|
68
89
|
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
69
|
-
- After portal-setup → suggest /portal-create
|
|
70
|
-
- After portal-credentials → suggest /portal-
|
|
71
|
-
- After portal-update → suggest /portal-deploy
|
|
90
|
+
- After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
|
|
91
|
+
- After portal-credentials → suggest /portal-deploy before external sharing
|
|
92
|
+
- After portal-update → suggest /portal-preview or /portal-deploy
|
|
93
|
+
|
|
94
|
+
If `RECENT_LEARNINGS` is shown, review them before proceeding. Apply them where relevant but do not mention them unless they materially affect the current task.
|
|
95
|
+
|
|
96
|
+
Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
|
|
72
97
|
|
|
73
|
-
If
|
|
74
|
-
relevant warnings or tips for this operation. Apply them where relevant but don't
|
|
75
|
-
mention them unless they directly affect the current task.
|
|
98
|
+
If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
|
|
76
99
|
|
|
77
100
|
## Steps
|
|
78
101
|
|
|
@@ -114,7 +137,7 @@ When the user is inside a freshly generated Showpane project, the setup should:
|
|
|
114
137
|
- Prefer the current working directory as `APP_PATH`
|
|
115
138
|
- Skip any suggestion to clone the upstream Showpane repository
|
|
116
139
|
- Auto-detect SQLite from DATABASE_URL (if it starts with "file:" it's SQLite)
|
|
117
|
-
-
|
|
140
|
+
- Reuse the existing workspace org name if it is already obvious, then ask for the next missing contact detail one at a time
|
|
118
141
|
- Be concise — the user just ran `npx showpane` and wants to get going fast
|
|
119
142
|
|
|
120
143
|
### Step 3: Set the workspace mode
|
|
@@ -164,12 +187,17 @@ Do NOT proceed until the local schema is applied successfully.
|
|
|
164
187
|
|
|
165
188
|
Ask the user for their organization details one at a time. Do not present all questions at once — guide them through the process conversationally.
|
|
166
189
|
|
|
167
|
-
|
|
190
|
+
Before asking anything, check the local database. If there is already exactly one organization and it clearly matches the workspace the user just created, keep it as the default and do not ask for the organization name again unless the user wants to change it.
|
|
191
|
+
|
|
192
|
+
If the org already exists with a real name, ask only for the next missing field.
|
|
193
|
+
Do not restart the whole org questionnaire from the top.
|
|
194
|
+
|
|
195
|
+
1. **Organization name** (required only if not already known) — e.g., "Acme Consulting". This is the name that appears in portal headers alongside the client name.
|
|
168
196
|
2. **Contact name** (required) — the person who appears on portal footers as the point of contact, e.g., "Jane Smith". This is typically the account manager or sales rep.
|
|
169
197
|
3. **Contact email** (required) — e.g., "jane@acme.com". Displayed in the portal footer as a mailto link.
|
|
170
198
|
4. **Contact title** (optional, default: "Account Manager") — e.g., "Director", "Partner", "Client Success Lead". Shown next to the contact name in the portal footer.
|
|
171
199
|
5. **Contact phone** (optional) — e.g., "+44 7700 900000". If provided, displayed alongside email in the portal footer.
|
|
172
|
-
6. **Company website URL**
|
|
200
|
+
6. **Company website URL** — e.g., "acme.com". Ask for it plainly instead of framing it as optional. Used to auto-fetch the company logo via Clearbit.
|
|
173
201
|
- If provided, fetch logo URL: `https://logo.clearbit.com/{domain}` and store in `Organization.logoUrl`
|
|
174
202
|
- Also store the URL in `Organization.websiteUrl`
|
|
175
203
|
7. **Contact avatar**: Auto-populated from the contact email via Gravatar. No need to ask — just use `getAvatarUrl(email, contactName)` from `app/src/lib/branding.ts` and store in `Organization.contactAvatar`
|
|
@@ -201,20 +229,19 @@ Validate the input is a valid hex color:
|
|
|
201
229
|
|
|
202
230
|
Update the Organization record with `primaryColor` set to the validated hex value. The portal app reads this value and applies it via CSS custom properties (the `--primary` variable in the Tailwind theme).
|
|
203
231
|
|
|
204
|
-
If the user says "skip" or "default", use `#1a1a1a`. Brand color can always be changed later by running `/portal
|
|
232
|
+
If the user says "skip" or "default", use `#1a1a1a`. Brand color can always be changed later by running `/portal-setup` again.
|
|
205
233
|
|
|
206
|
-
### Step 8: Telemetry
|
|
234
|
+
### Step 8: Telemetry reminder
|
|
207
235
|
|
|
208
|
-
|
|
236
|
+
Do not re-run the global telemetry interview here.
|
|
209
237
|
|
|
210
|
-
|
|
238
|
+
If the shared runtime preamble already set telemetry, keep that value. If it has
|
|
239
|
+
not been set yet for some reason, default to:
|
|
211
240
|
|
|
212
|
-
|
|
213
|
-
- **
|
|
214
|
-
- **anonymous** — anonymous usage stats with no device ID
|
|
215
|
-
- **off** — no telemetry (default)
|
|
241
|
+
- **anonymous** — local analytics plus anonymous remote sync, no stable device ID
|
|
242
|
+
- **off** — local analytics only, no remote sync
|
|
216
243
|
|
|
217
|
-
|
|
244
|
+
If you must set it manually, write the chosen value into config.
|
|
218
245
|
|
|
219
246
|
### Step 9: Save configuration
|
|
220
247
|
|
|
@@ -232,7 +259,7 @@ cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
|
|
|
232
259
|
"app_path": "<resolved_absolute_path>",
|
|
233
260
|
"deploy_mode": "local",
|
|
234
261
|
"orgSlug": "<org_slug>",
|
|
235
|
-
"telemetry": "<
|
|
262
|
+
"telemetry": "<anonymous|off>"
|
|
236
263
|
}
|
|
237
264
|
CONFIGEOF
|
|
238
265
|
chmod 600 "$HOME/.showpane/config.json"
|
|
@@ -251,11 +278,11 @@ Showpane setup complete!
|
|
|
251
278
|
Deploy mode: local
|
|
252
279
|
Organization: Acme Consulting (acme-consulting)
|
|
253
280
|
Brand color: #2563eb
|
|
254
|
-
Telemetry:
|
|
281
|
+
Telemetry: anonymous
|
|
255
282
|
|
|
256
283
|
Next steps:
|
|
257
|
-
1.
|
|
258
|
-
2.
|
|
284
|
+
1. Recommended first run: /portal-onboard
|
|
285
|
+
2. Fast repeat-user path: /portal-create <slug>
|
|
259
286
|
3. View the example portal: open http://localhost:3000/client/example
|
|
260
287
|
```
|
|
261
288
|
|
|
@@ -282,19 +309,11 @@ Never silently continue past a failure. Each step depends on the previous step s
|
|
|
282
309
|
|
|
283
310
|
## Completion
|
|
284
311
|
|
|
285
|
-
As a final step, log skill completion:
|
|
312
|
+
As a final step, log skill completion and telemetry:
|
|
286
313
|
|
|
287
314
|
```bash
|
|
288
315
|
echo '{"skill":"portal-setup","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
316
|
+
_TEL_END=$(date +%s)
|
|
317
|
+
_TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
|
|
318
|
+
"$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-setup" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
|
|
289
319
|
```
|
|
290
|
-
|
|
291
|
-
## Conventions
|
|
292
|
-
|
|
293
|
-
- Never store DATABASE_URL or AUTH_SECRET in config.json — these live in the app's `.env` file
|
|
294
|
-
- Config file permissions must be 600 (owner read/write only)
|
|
295
|
-
- The orgSlug in config.json determines which Organization record is used by all other skills
|
|
296
|
-
- All user-facing output should be concise and scannable — use indented key-value pairs, not paragraphs
|
|
297
|
-
- If any step fails, provide a clear error message and suggest how to fix it, but do not silently continue
|
|
298
|
-
- The setup wizard is interactive — ask one question at a time, don't dump all questions at once
|
|
299
|
-
- When reconfiguring, show current values as defaults so the user can press enter to keep them
|
|
300
|
-
- The setup wizard should complete in under 5 minutes for a user with Node.js, internet access, and a generated Showpane project
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: portal-setup
|
|
3
|
+
description: |
|
|
4
|
+
Interactive setup wizard for Showpane. Creates config, installs deps, applies the local SQLite schema, and creates the organization.
|
|
5
|
+
Trigger phrases: "portal setup", "configure showpane", "set up showpane", "initialize showpane". (showpane)
|
|
6
|
+
allowed-tools: [Bash, Read, Write, Edit, Glob, Grep]
|
|
7
|
+
hooks:
|
|
8
|
+
PreToolUse:
|
|
9
|
+
- matcher: "Bash"
|
|
10
|
+
hooks:
|
|
11
|
+
- type: command
|
|
12
|
+
command: "bash ${CLAUDE_SKILL_DIR}/../showpane-shared/bin/check-portal-guard.sh"
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{{PREAMBLE}}
|
|
16
|
+
|
|
17
|
+
## Steps
|
|
18
|
+
|
|
19
|
+
### Step 1: Check for existing configuration
|
|
20
|
+
|
|
21
|
+
If the preamble output shows `EXISTING_CONFIG: true`, inform the user that Showpane is already configured and show the current settings. Ask if they want to reconfigure. If they say no, exit gracefully. If they say yes, continue with the setup — existing values become defaults the user can accept or change.
|
|
22
|
+
|
|
23
|
+
### Step 2: Detect or ask for app_path
|
|
24
|
+
|
|
25
|
+
Try to find the Showpane app automatically by checking these locations in order:
|
|
26
|
+
|
|
27
|
+
1. Current working directory — look for `package.json` containing `"name"` with "showpane" in it
|
|
28
|
+
2. Parent directory — check `../app/` for the same
|
|
29
|
+
3. Common locations: `~/git/showpane/`, `~/showpane/`
|
|
30
|
+
4. The `SHOWPANE_APP_PATH` environment variable
|
|
31
|
+
|
|
32
|
+
Run the detection:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
for candidate in "$(pwd)" "$(pwd)/../app" "$HOME/git/showpane/app" "$HOME/showpane/app"; do
|
|
36
|
+
if [ -f "$candidate/package.json" ] && { [ -f "$candidate/prisma/schema.local.prisma" ] || [ -f "$candidate/prisma.config.ts" ]; }; then
|
|
37
|
+
echo "FOUND: $(cd "$candidate" && pwd)"
|
|
38
|
+
break
|
|
39
|
+
fi
|
|
40
|
+
done
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
If found, confirm with the user: "Found Showpane app at /path/to/app. Use this? (Y/n)"
|
|
44
|
+
|
|
45
|
+
If not found, ask the user to provide the path. Validate that the path exists and contains `package.json` plus either `prisma/schema.local.prisma` or `prisma.config.ts`. If those markers are missing, the path is not a valid Showpane app directory — explain which file is missing and ask again.
|
|
46
|
+
|
|
47
|
+
Resolve the path to an absolute path (no `~` or relative components) before storing it. Use `cd "$path" && pwd` to resolve.
|
|
48
|
+
|
|
49
|
+
Store the resolved absolute path as `APP_PATH`.
|
|
50
|
+
|
|
51
|
+
#### Packaged Installer Mode
|
|
52
|
+
|
|
53
|
+
When the user is inside a freshly generated Showpane project, the setup should:
|
|
54
|
+
- Prefer the current working directory as `APP_PATH`
|
|
55
|
+
- Skip any suggestion to clone the upstream Showpane repository
|
|
56
|
+
- Auto-detect SQLite from DATABASE_URL (if it starts with "file:" it's SQLite)
|
|
57
|
+
- Reuse the existing workspace org name if it is already obvious, then ask for the next missing contact detail one at a time
|
|
58
|
+
- Be concise — the user just ran `npx showpane` and wants to get going fast
|
|
59
|
+
|
|
60
|
+
### Step 3: Set the workspace mode
|
|
61
|
+
|
|
62
|
+
Use `local` as the setup mode.
|
|
63
|
+
|
|
64
|
+
Store `DEPLOY_MODE=local` in the config written later in this flow.
|
|
65
|
+
|
|
66
|
+
### Step 4: Install dependencies
|
|
67
|
+
|
|
68
|
+
Check if `$APP_PATH/node_modules` exists. If not, run:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
cd "$APP_PATH" && npm install
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
If node_modules exists but `.prisma` client is missing, run:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
cd "$APP_PATH" && npm run prisma:generate
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Wait for installation to complete before proceeding.
|
|
81
|
+
|
|
82
|
+
### Step 5: Apply the local database schema
|
|
83
|
+
|
|
84
|
+
Source the app's `.env` file to get `DATABASE_URL`:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The supported setup path is SQLite. Apply the local schema:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
cd "$APP_PATH" && npm run prisma:db-push
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If `DATABASE_URL` is not set in `.env`, inform the user they need to configure it. Provide guidance:
|
|
97
|
+
- For local development: `file:./dev.db`
|
|
98
|
+
|
|
99
|
+
If `DATABASE_URL` is set to anything other than a `file:` URL, explain that the supported setup flow is SQLite-first and suggest resetting `.env` back to `file:./dev.db`.
|
|
100
|
+
|
|
101
|
+
Do NOT proceed until the local schema is applied successfully.
|
|
102
|
+
|
|
103
|
+
### Step 6: Create or find Organization record
|
|
104
|
+
|
|
105
|
+
Ask the user for their organization details one at a time. Do not present all questions at once — guide them through the process conversationally.
|
|
106
|
+
|
|
107
|
+
Before asking anything, check the local database. If there is already exactly one organization and it clearly matches the workspace the user just created, keep it as the default and do not ask for the organization name again unless the user wants to change it.
|
|
108
|
+
|
|
109
|
+
If the org already exists with a real name, ask only for the next missing field.
|
|
110
|
+
Do not restart the whole org questionnaire from the top.
|
|
111
|
+
|
|
112
|
+
1. **Organization name** (required only if not already known) — e.g., "Acme Consulting". This is the name that appears in portal headers alongside the client name.
|
|
113
|
+
2. **Contact name** (required) — the person who appears on portal footers as the point of contact, e.g., "Jane Smith". This is typically the account manager or sales rep.
|
|
114
|
+
3. **Contact email** (required) — e.g., "jane@acme.com". Displayed in the portal footer as a mailto link.
|
|
115
|
+
4. **Contact title** (optional, default: "Account Manager") — e.g., "Director", "Partner", "Client Success Lead". Shown next to the contact name in the portal footer.
|
|
116
|
+
5. **Contact phone** (optional) — e.g., "+44 7700 900000". If provided, displayed alongside email in the portal footer.
|
|
117
|
+
6. **Company website URL** — e.g., "acme.com". Ask for it plainly instead of framing it as optional. Used to auto-fetch the company logo via Clearbit.
|
|
118
|
+
- If provided, fetch logo URL: `https://logo.clearbit.com/{domain}` and store in `Organization.logoUrl`
|
|
119
|
+
- Also store the URL in `Organization.websiteUrl`
|
|
120
|
+
7. **Contact avatar**: Auto-populated from the contact email via Gravatar. No need to ask — just use `getAvatarUrl(email, contactName)` from `app/src/lib/branding.ts` and store in `Organization.contactAvatar`
|
|
121
|
+
|
|
122
|
+
Generate an org slug from the organization name: lowercase, replace spaces with hyphens, strip non-alphanumeric characters except hyphens, remove consecutive hyphens. For example, "Acme Consulting Ltd." becomes `acme-consulting-ltd`. Confirm the generated slug with the user and allow them to override it.
|
|
123
|
+
|
|
124
|
+
Create the organization record in the database using the app's Prisma client. The Organization model stores:
|
|
125
|
+
- `name`, `slug`, `contactName`, `contactEmail`, `contactTitle`, `contactPhone`
|
|
126
|
+
|
|
127
|
+
If an Organization with that slug already exists in the database, present two options:
|
|
128
|
+
1. Use the existing organization (show its current details so the user can verify)
|
|
129
|
+
2. Create a new organization with a different slug
|
|
130
|
+
|
|
131
|
+
If the user chooses to use the existing org, skip creation and proceed with that org's slug. This is common when reconfiguring or when multiple people set up the same Showpane instance.
|
|
132
|
+
|
|
133
|
+
### Step 7: Theme configuration
|
|
134
|
+
|
|
135
|
+
Ask the user for their primary brand color as a hex value. This color is used for active tab indicators, bullet point accents, and interactive elements throughout the portal. Provide suggestions:
|
|
136
|
+
|
|
137
|
+
- Default: `#1a1a1a` (near-black, professional — works well for most brands)
|
|
138
|
+
- Common choices: `#2563eb` (blue), `#059669` (green), `#dc2626` (red), `#7c3aed` (purple)
|
|
139
|
+
- Tip: if the user has a brand guidelines document or website, suggest pulling the primary color from there
|
|
140
|
+
|
|
141
|
+
Validate the input is a valid hex color:
|
|
142
|
+
- Accept with or without `#` prefix (add `#` if missing)
|
|
143
|
+
- Accept 3-digit shorthand (`#abc` expands to `#aabbcc`)
|
|
144
|
+
- Accept 6-digit full form (`#2563eb`)
|
|
145
|
+
- Reject anything else with a clear message: "Please enter a valid hex color like #2563eb"
|
|
146
|
+
|
|
147
|
+
Update the Organization record with `primaryColor` set to the validated hex value. The portal app reads this value and applies it via CSS custom properties (the `--primary` variable in the Tailwind theme).
|
|
148
|
+
|
|
149
|
+
If the user says "skip" or "default", use `#1a1a1a`. Brand color can always be changed later by running `/portal-setup` again.
|
|
150
|
+
|
|
151
|
+
### Step 8: Telemetry reminder
|
|
152
|
+
|
|
153
|
+
Do not re-run the global telemetry interview here.
|
|
154
|
+
|
|
155
|
+
If the shared runtime preamble already set telemetry, keep that value. If it has
|
|
156
|
+
not been set yet for some reason, default to:
|
|
157
|
+
|
|
158
|
+
- **anonymous** — local analytics plus anonymous remote sync, no stable device ID
|
|
159
|
+
- **off** — local analytics only, no remote sync
|
|
160
|
+
|
|
161
|
+
If you must set it manually, write the chosen value into config.
|
|
162
|
+
|
|
163
|
+
### Step 9: Save configuration
|
|
164
|
+
|
|
165
|
+
Create the `~/.showpane/` directory if it doesn't exist:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
mkdir -p "$HOME/.showpane"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Write the config file:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
|
|
175
|
+
{
|
|
176
|
+
"app_path": "<resolved_absolute_path>",
|
|
177
|
+
"deploy_mode": "local",
|
|
178
|
+
"orgSlug": "<org_slug>",
|
|
179
|
+
"telemetry": "<anonymous|off>"
|
|
180
|
+
}
|
|
181
|
+
CONFIGEOF
|
|
182
|
+
chmod 600 "$HOME/.showpane/config.json"
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Replace the placeholder values with the actual values collected. Use `chmod 600` so only the owner can read/write the config (it may contain sensitive paths).
|
|
186
|
+
|
|
187
|
+
### Step 10: Print success summary
|
|
188
|
+
|
|
189
|
+
Display a clear summary:
|
|
190
|
+
|
|
191
|
+
```
|
|
192
|
+
Showpane setup complete!
|
|
193
|
+
|
|
194
|
+
App path: /path/to/showpane-project
|
|
195
|
+
Deploy mode: local
|
|
196
|
+
Organization: Acme Consulting (acme-consulting)
|
|
197
|
+
Brand color: #2563eb
|
|
198
|
+
Telemetry: anonymous
|
|
199
|
+
|
|
200
|
+
Next steps:
|
|
201
|
+
1. Recommended first run: /portal-onboard
|
|
202
|
+
2. Fast repeat-user path: /portal-create <slug>
|
|
203
|
+
3. View the example portal: open http://localhost:3000/client/example
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Step 11: Record learning (optional)
|
|
207
|
+
|
|
208
|
+
If this is the first setup, create an initial learning entry:
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
mkdir -p "$HOME/.showpane"
|
|
212
|
+
echo '{"skill":"portal-setup","key":"initial-setup","insight":"Setup completed. Deploy mode: <mode>. Org: <name>.","confidence":10,"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/learnings.jsonl"
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
## Error Handling
|
|
216
|
+
|
|
217
|
+
Each step can fail independently. Handle failures gracefully:
|
|
218
|
+
|
|
219
|
+
- **App path not found**: Ask the user to run `npx showpane` first or point setup at an existing generated Showpane project
|
|
220
|
+
- **npm install fails**: Check Node.js version (requires 18+), check internet connectivity, suggest clearing `node_modules` and retrying
|
|
221
|
+
- **Prisma db push fails**: Check that DATABASE_URL is set to `file:./dev.db` (or another valid SQLite `file:` URL) in `.env`
|
|
222
|
+
- **Organization creation fails**: Check database connectivity. If the Prisma client throws a connection error, verify DATABASE_URL in `.env`
|
|
223
|
+
- **Config write fails**: Check that `$HOME/.showpane/` is writable. On some systems, home directory permissions may block directory creation
|
|
224
|
+
|
|
225
|
+
Never silently continue past a failure. Each step depends on the previous step succeeding.
|
|
226
|
+
|
|
227
|
+
{{COMPLETION}}
|