showpane 0.4.1 → 0.4.2
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 +14 -1
- package/bundle/meta/scaffold-manifest.json +73 -0
- package/bundle/scaffold/VERSION +1 -0
- package/bundle/scaffold/__dot__env.example +24 -0
- package/bundle/scaffold/__dot__gitignore +41 -0
- package/bundle/scaffold/docker/Caddyfile +3 -0
- package/bundle/scaffold/docker/Dockerfile +30 -0
- package/bundle/scaffold/docker-compose.yml +53 -0
- package/bundle/scaffold/next.config.ts +20 -0
- package/bundle/scaffold/package-lock.json +5843 -0
- package/bundle/scaffold/package.json +42 -0
- package/bundle/scaffold/postcss.config.js +6 -0
- package/bundle/scaffold/prisma/migrations/20260408000000_init/migration.sql +143 -0
- package/bundle/scaffold/prisma/migrations/20260408010000_add_visitor_tracking/migration.sql +6 -0
- package/bundle/scaffold/prisma/migrations/20260409040000_add_portal_file_checksum/migration.sql +2 -0
- package/bundle/scaffold/prisma/migrations/migration_lock.toml +3 -0
- package/bundle/scaffold/prisma/schema.local.prisma +131 -0
- package/bundle/scaffold/prisma/schema.prisma +128 -0
- package/bundle/scaffold/prisma/seed.ts +49 -0
- package/bundle/scaffold/public/example-avatar.svg +4 -0
- package/bundle/scaffold/public/example-logo.svg +4 -0
- package/bundle/scaffold/public/robots.txt +2 -0
- package/bundle/scaffold/scripts/backup.sh +19 -0
- package/bundle/scaffold/scripts/e2e-verify.sh +487 -0
- package/bundle/scaffold/scripts/prisma-db-push.mjs +7 -0
- package/bundle/scaffold/scripts/prisma-generate.mjs +3 -0
- package/bundle/scaffold/scripts/prisma-schema.mjs +74 -0
- package/bundle/scaffold/scripts/restore.sh +31 -0
- package/bundle/scaffold/src/__tests__/client-portals.test.ts +80 -0
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +32 -0
- package/bundle/scaffold/src/app/(portal)/client/[slug]/page.tsx +79 -0
- package/bundle/scaffold/src/app/(portal)/client/[slug]/s/[token]/route.ts +22 -0
- package/bundle/scaffold/src/app/(portal)/client/example/example-client.tsx +372 -0
- package/bundle/scaffold/src/app/(portal)/client/example/page.tsx +5 -0
- package/bundle/scaffold/src/app/(portal)/client/layout.tsx +7 -0
- package/bundle/scaffold/src/app/(portal)/client/page.tsx +18 -0
- package/bundle/scaffold/src/app/api/client-auth/route.ts +82 -0
- package/bundle/scaffold/src/app/api/client-auth/share/route.ts +30 -0
- package/bundle/scaffold/src/app/api/client-events/route.ts +87 -0
- package/bundle/scaffold/src/app/api/client-files/[...path]/route.ts +80 -0
- package/bundle/scaffold/src/app/api/client-files/client-upload/route.ts +118 -0
- package/bundle/scaffold/src/app/api/client-files/route.ts +37 -0
- package/bundle/scaffold/src/app/api/client-files/upload/route.ts +131 -0
- package/bundle/scaffold/src/app/api/health/route.ts +19 -0
- package/bundle/scaffold/src/app/globals.css +7 -0
- package/bundle/scaffold/src/app/layout.tsx +25 -0
- package/bundle/scaffold/src/app/page.tsx +171 -0
- package/bundle/scaffold/src/components/portal-login.tsx +169 -0
- package/bundle/scaffold/src/components/portal-shell.tsx +373 -0
- package/bundle/scaffold/src/lib/abuse-controls.ts +43 -0
- package/bundle/scaffold/src/lib/branding.ts +50 -0
- package/bundle/scaffold/src/lib/client-auth.ts +98 -0
- package/bundle/scaffold/src/lib/client-portals.ts +134 -0
- package/bundle/scaffold/src/lib/control-plane.ts +100 -0
- package/bundle/scaffold/src/lib/db.ts +7 -0
- package/bundle/scaffold/src/lib/files.ts +124 -0
- package/bundle/scaffold/src/lib/load-app-env.ts +42 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +69 -0
- package/bundle/scaffold/src/lib/prisma-client.ts +5 -0
- package/bundle/scaffold/src/lib/runtime-state.ts +69 -0
- package/bundle/scaffold/src/lib/storage.ts +204 -0
- package/bundle/scaffold/src/lib/token.ts +186 -0
- package/bundle/scaffold/src/lib/utils.ts +6 -0
- package/bundle/scaffold/src/middleware.ts +61 -0
- package/bundle/scaffold/tailwind.config.ts +15 -0
- package/bundle/scaffold/tests/__dot__gitkeep +0 -0
- package/bundle/scaffold/tsconfig.json +23 -0
- package/bundle/scaffold/vitest.config.ts +13 -0
- package/bundle/toolchain/VERSION +1 -0
- package/bundle/toolchain/bin/check-slug.ts +59 -0
- package/bundle/toolchain/bin/create-deploy-bundle.ts +93 -0
- package/bundle/toolchain/bin/create-portal.ts +71 -0
- package/bundle/toolchain/bin/delete-portal.ts +48 -0
- package/bundle/toolchain/bin/export-file-manifest.ts +84 -0
- package/bundle/toolchain/bin/export-runtime-state.ts +90 -0
- package/bundle/toolchain/bin/generate-share-link.ts +68 -0
- package/bundle/toolchain/bin/list-portals.ts +53 -0
- package/bundle/toolchain/bin/materialize-file.ts +35 -0
- package/bundle/toolchain/bin/query-analytics.ts +88 -0
- package/bundle/toolchain/bin/rotate-credentials.ts +57 -0
- package/bundle/toolchain/bin/showpane-config +63 -0
- package/bundle/toolchain/bin/tsconfig.json +13 -0
- package/bundle/toolchain/skills/VERSION +1 -0
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +263 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +341 -0
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +274 -0
- package/bundle/toolchain/skills/portal-delete/SKILL.md +265 -0
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +721 -0
- package/bundle/toolchain/skills/portal-dev/SKILL.md +301 -0
- package/bundle/toolchain/skills/portal-list/SKILL.md +253 -0
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +277 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +257 -0
- package/bundle/toolchain/skills/portal-setup/SKILL.md +309 -0
- package/bundle/toolchain/skills/portal-share/SKILL.md +234 -0
- package/bundle/toolchain/skills/portal-status/SKILL.md +268 -0
- package/bundle/toolchain/skills/portal-update/SKILL.md +348 -0
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +235 -0
- package/bundle/toolchain/skills/portal-verify/SKILL.md +265 -0
- package/bundle/toolchain/skills/shared/bin/check-portal-guard.sh +49 -0
- package/bundle/toolchain/skills/shared/platform-constraints.md +33 -0
- package/bundle/toolchain/skills/shared/preamble.md +137 -0
- package/bundle/toolchain/templates/consulting/consulting-client.tsx +205 -0
- package/bundle/toolchain/templates/onboarding/onboarding-client.tsx +237 -0
- package/bundle/toolchain/templates/sales-followup/sales-followup-client.tsx +283 -0
- package/dist/index.js +873 -166
- package/package.json +3 -2
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: portal-onboard
|
|
3
|
+
description: |
|
|
4
|
+
Guided first-run experience that chains setup, create, credentials, and preview.
|
|
5
|
+
Use when asked to "get started", "onboard", "first time setup", "walk me through",
|
|
6
|
+
or "set up showpane from scratch". (showpane)
|
|
7
|
+
allowed-tools: [Bash, Read, Write, Edit, Glob, Grep]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Preamble (run first)
|
|
11
|
+
|
|
12
|
+
Before doing anything else, execute this block in a Bash tool call:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
CONFIG="$HOME/.showpane/config.json"
|
|
16
|
+
if [ ! -f "$CONFIG" ]; then
|
|
17
|
+
echo "Showpane not configured. Run /portal setup first."
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
21
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','docker'))" 2>/dev/null)
|
|
22
|
+
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
23
|
+
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
24
|
+
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
25
|
+
DATABASE_URL="${DATABASE_URL:-}"
|
|
26
|
+
if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
27
|
+
echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
|
|
28
|
+
exit 1
|
|
29
|
+
fi
|
|
30
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$HOME/.showpane/current}"
|
|
31
|
+
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
32
|
+
echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
|
|
33
|
+
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
34
|
+
[ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
|
|
35
|
+
|
|
36
|
+
# Predictive next-skill suggestion
|
|
37
|
+
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
38
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
39
|
+
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Search relevant learnings
|
|
43
|
+
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
44
|
+
if [ -f "$LEARN_FILE" ]; then
|
|
45
|
+
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
46
|
+
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
47
|
+
if [ "$_LEARN_COUNT" -gt 0 ] 2>/dev/null; then
|
|
48
|
+
echo "RECENT_LEARNINGS:"
|
|
49
|
+
tail -5 "$LEARN_FILE" 2>/dev/null
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Track skill execution
|
|
54
|
+
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
55
|
+
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
56
|
+
echo '{"skill":"portal-onboard","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
If RECENT_SKILLS is shown, suggest the likely next skill:
|
|
60
|
+
- After portal-create → suggest /portal-preview
|
|
61
|
+
- After portal-preview → suggest /portal-deploy or /portal-share
|
|
62
|
+
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
63
|
+
- After portal-setup → suggest /portal-create
|
|
64
|
+
- After portal-credentials → suggest /portal-share
|
|
65
|
+
- After portal-update → suggest /portal-deploy
|
|
66
|
+
|
|
67
|
+
If RECENT_LEARNINGS is shown, review them before proceeding. Past learnings may contain
|
|
68
|
+
relevant warnings or tips for this operation. Apply them where relevant but don't
|
|
69
|
+
mention them unless they directly affect the current task.
|
|
70
|
+
|
|
71
|
+
## Overview
|
|
72
|
+
|
|
73
|
+
This skill is the guided first-run experience for Showpane. It chains four other skills together in sequence -- setup, create, credentials, and preview -- walking the user through each step with clear transitions. The goal is to go from zero to a working portal in the browser in under 5 minutes.
|
|
74
|
+
|
|
75
|
+
This is specifically designed for users who have just installed Showpane and want to see it working end-to-end. It is opinionated about the order of operations and provides more context and encouragement than the individual skills would on their own.
|
|
76
|
+
|
|
77
|
+
The onboard flow is linear. Each step depends on the previous one completing successfully. If any step fails, the flow stops and provides recovery instructions rather than skipping ahead.
|
|
78
|
+
|
|
79
|
+
## Steps
|
|
80
|
+
|
|
81
|
+
### Step 1: Welcome message
|
|
82
|
+
|
|
83
|
+
Start with a welcome that sets expectations. First, display the ASCII welcome banner:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
╔══════════════════════════════════════════╗
|
|
87
|
+
║ SHOWPANE — Client Portal Generator ║
|
|
88
|
+
╚══════════════════════════════════════════╝
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Then continue with:
|
|
92
|
+
|
|
93
|
+
"Welcome to Showpane! Let's set up your first client portal. This will take about 5 minutes and we'll go through four steps:
|
|
94
|
+
|
|
95
|
+
1. Configure Showpane (app path, database, organization)
|
|
96
|
+
2. Create your first portal
|
|
97
|
+
3. Set up login credentials
|
|
98
|
+
4. Open it in your browser
|
|
99
|
+
|
|
100
|
+
Let's get started."
|
|
101
|
+
|
|
102
|
+
This framing is important. It tells the user what to expect and roughly how long it will take. Uncertainty causes drop-off.
|
|
103
|
+
|
|
104
|
+
### Step 2: Run the setup flow
|
|
105
|
+
|
|
106
|
+
Read the instructions from `skills/portal-setup/SKILL.md` and execute the setup flow inline. This means you follow the setup skill's instructions as if the user had run `/portal setup` directly.
|
|
107
|
+
|
|
108
|
+
The setup flow will:
|
|
109
|
+
- Detect the app path (or ask the user)
|
|
110
|
+
- Verify the database connection
|
|
111
|
+
- Create or select an organization
|
|
112
|
+
- Write the config to `~/.showpane/config.json`
|
|
113
|
+
- Set file permissions
|
|
114
|
+
|
|
115
|
+
During setup, also ask for the company's website URL (e.g., "acme.com"). This is used for auto-branding:
|
|
116
|
+
- Fetch the company logo via `getLogoUrl(domain)` from `app/src/lib/branding.ts`
|
|
117
|
+
- The logo URL will be stored in the Organization record's `logoUrl` field
|
|
118
|
+
- If the user doesn't have a website, skip — the initial-based fallback works fine
|
|
119
|
+
|
|
120
|
+
Also ask for the contact's email address and use `getAvatarUrl(email, contactName)` to auto-populate the contact avatar.
|
|
121
|
+
|
|
122
|
+
**Important**: If setup detects that Showpane is already configured (config file exists, database is connected, org exists), acknowledge it and skip to the next step: "Showpane is already configured. Skipping setup."
|
|
123
|
+
|
|
124
|
+
If setup fails at any point (database not reachable, app not found), stop the onboard flow and provide clear instructions: "Setup needs to be completed before we can continue. Fix the issue above and run /portal onboard again."
|
|
125
|
+
|
|
126
|
+
After successful setup, transition:
|
|
127
|
+
|
|
128
|
+
"Setup complete! Your Showpane instance is configured. Let's create your first portal."
|
|
129
|
+
|
|
130
|
+
### Granola MCP Integration
|
|
131
|
+
|
|
132
|
+
After setup, check if Granola MCP tools are available by attempting to call `list_meetings`.
|
|
133
|
+
|
|
134
|
+
If available:
|
|
135
|
+
- Show: "I found your Granola meetings. Want to use a recent call as the source for your first portal?"
|
|
136
|
+
- List recent meetings with date + title
|
|
137
|
+
- If selected, use the transcript to pre-populate portal content
|
|
138
|
+
|
|
139
|
+
If not available:
|
|
140
|
+
- Skip silently. Do not mention Granola or show an error.
|
|
141
|
+
|
|
142
|
+
### Step 3: Run the create flow
|
|
143
|
+
|
|
144
|
+
Read the instructions from `skills/portal-create/SKILL.md` and execute the create flow inline. This creates the portal page files and database record.
|
|
145
|
+
|
|
146
|
+
During onboarding, provide a bit more guidance than the standalone create skill would:
|
|
147
|
+
|
|
148
|
+
- Suggest a slug if the user is unsure: "Pick a short name for the portal URL, like your client's company name in lowercase. For example: 'acme' or 'whzan'."
|
|
149
|
+
- If the user has Granola MCP connected, offer to pull a recent meeting as the source material. If not, prompt for a brief description of the client and what the portal should contain.
|
|
150
|
+
- Use the sales-followup template as the default suggestion for first-time users: "We'll use the sales follow-up template as a starting point. You can customize everything after."
|
|
151
|
+
|
|
152
|
+
After successful creation, transition:
|
|
153
|
+
|
|
154
|
+
"Great! Portal '<slug>' created for <company>. Now let's set up credentials so your client can log in."
|
|
155
|
+
|
|
156
|
+
If creation fails (e.g., slug already taken), help the user pick a different slug and retry. Do not abort the entire onboard flow for a recoverable error.
|
|
157
|
+
|
|
158
|
+
### Step 4: Run the credentials flow
|
|
159
|
+
|
|
160
|
+
Read the instructions from `skills/portal-credentials/SKILL.md` and execute the credentials flow inline. This creates a username and password for the portal.
|
|
161
|
+
|
|
162
|
+
During onboarding, the credentials flow is straightforward -- generate initial credentials. There is no rotation to handle on first run.
|
|
163
|
+
|
|
164
|
+
After credentials are generated, display them clearly:
|
|
165
|
+
|
|
166
|
+
"Credentials created. Save these -- they are shown once:
|
|
167
|
+
|
|
168
|
+
Username: <username>
|
|
169
|
+
Password: <password>
|
|
170
|
+
|
|
171
|
+
You'll share these with your client (or use /portal share to generate a link instead)."
|
|
172
|
+
|
|
173
|
+
Transition to the final step:
|
|
174
|
+
|
|
175
|
+
"Almost done! Let's open your portal in the browser."
|
|
176
|
+
|
|
177
|
+
### Step 5: Run the preview flow
|
|
178
|
+
|
|
179
|
+
Read the instructions from `skills/portal-preview/SKILL.md` and execute the preview flow inline. This opens the portal in the user's default browser.
|
|
180
|
+
|
|
181
|
+
During onboarding, add a note about what the user will see:
|
|
182
|
+
|
|
183
|
+
"Opening your portal now. You'll see a login page. Use the credentials above to log in, or just review the page to make sure everything looks right."
|
|
184
|
+
|
|
185
|
+
If no dev server is running, start one: "The dev server isn't running. Let me start it for you." Then run the dev server startup (from `/portal dev` instructions) and wait for it to be ready before opening the browser.
|
|
186
|
+
|
|
187
|
+
After the browser opens, transition to the final summary.
|
|
188
|
+
|
|
189
|
+
### Step 6: Final summary
|
|
190
|
+
|
|
191
|
+
Display the completion summary in an ASCII box:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
════════════════════════════════════════
|
|
195
|
+
Showpane is ready!
|
|
196
|
+
|
|
197
|
+
Portal: <slug> (<company>)
|
|
198
|
+
URL: http://localhost:3000/client/<slug>
|
|
199
|
+
Username: <username>
|
|
200
|
+
|
|
201
|
+
Next: /portal deploy to go live
|
|
202
|
+
Help: /portal list, /portal status
|
|
203
|
+
════════════════════════════════════════
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Note: Do NOT include the password in the final summary. It was shown once during Step 4. The summary is a reference card the user might screenshot or scroll back to, so it should not contain the password.
|
|
207
|
+
|
|
208
|
+
After the summary, provide a brief "what's next" orientation:
|
|
209
|
+
|
|
210
|
+
"Your portal is live locally. Here's what you can do next:
|
|
211
|
+
|
|
212
|
+
- `/portal deploy` -- push to production so your client can access it
|
|
213
|
+
- `/portal share <slug>` -- generate a share link (no login needed)
|
|
214
|
+
- `/portal update <slug>` -- edit the portal content
|
|
215
|
+
- `/portal analytics <slug>` -- track client engagement
|
|
216
|
+
- `/portal status` -- see a dashboard of all your portals"
|
|
217
|
+
|
|
218
|
+
## Flow Control
|
|
219
|
+
|
|
220
|
+
This skill is a sequential orchestrator. Each step depends on the previous one:
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
setup -> create -> credentials -> preview -> summary
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Rules:
|
|
227
|
+
- **Never skip a step.** Even if the user says "I already have credentials", verify by checking the database. If credentials exist, acknowledge and move on. But do not skip the check.
|
|
228
|
+
- **Stop on hard failures.** If setup cannot connect to the database, or create fails due to a missing dependency, stop and explain. Do not try to continue with a broken foundation.
|
|
229
|
+
- **Retry on soft failures.** If the user picks a slug that is taken, help them pick another. If the dev server takes a moment to start, wait. These are not reasons to abort.
|
|
230
|
+
- **Respect existing state.** If the user already has a configured Showpane, an existing portal, and credentials, acknowledge each one and skip to the next incomplete step. The onboard flow should work for both brand-new and partially-set-up installations.
|
|
231
|
+
|
|
232
|
+
## Detecting Existing State
|
|
233
|
+
|
|
234
|
+
Before each step, check whether it has already been completed:
|
|
235
|
+
|
|
236
|
+
1. **Setup**: Check if `~/.showpane/config.json` exists and is valid. If yes, skip setup.
|
|
237
|
+
2. **Create**: Check if the org has at least one portal (via `list-portals.ts`). If yes, ask: "You already have portals. Want to create another, or skip to credentials?"
|
|
238
|
+
3. **Credentials**: Check if the selected portal has credentials (from list response `hasCredentials` field). If yes, skip credentials.
|
|
239
|
+
4. **Preview**: Always run. Opening the browser is always useful.
|
|
240
|
+
|
|
241
|
+
## Conventions
|
|
242
|
+
|
|
243
|
+
- Use encouraging but not patronizing language. "Great!" and "Let's move on" are fine. "Wow, amazing job!" is too much.
|
|
244
|
+
- Keep transitions between steps to one sentence. Do not recap what just happened -- the user can see it.
|
|
245
|
+
- Use double-line box drawing (`═`) for the final summary box only. Keep step transitions as plain text.
|
|
246
|
+
- If the total onboard time exceeds 10 minutes (e.g., due to long create flow with Granola), do not comment on it. The user is working, not racing.
|
|
247
|
+
- If learnings exist from a previous session, load them silently. The onboard flow benefits from prior context without mentioning it.
|
|
248
|
+
|
|
249
|
+
## Error Handling
|
|
250
|
+
|
|
251
|
+
- If the preamble itself fails (no config), this is expected for first-run. In this specific case, do NOT abort. Instead, proceed directly to the setup step: "Looks like Showpane isn't configured yet. Let's fix that."
|
|
252
|
+
- If any bin/ script returns a non-zero exit, read the error from stderr, display it, and suggest a fix.
|
|
253
|
+
- If the user wants to bail out mid-flow, respect it: "No problem. You can pick up where you left off by running the individual skills: /portal setup, /portal create, /portal credentials, /portal preview."
|
|
254
|
+
|
|
255
|
+
## Special Case: Preamble Failure
|
|
256
|
+
|
|
257
|
+
Unlike other skills, `/portal onboard` should NOT hard-fail if the preamble reports "Showpane not configured." The entire point of this skill is to handle first-run. If the config file does not exist, catch the preamble error and proceed to Step 2 (setup) which will create the config.
|
|
258
|
+
|
|
259
|
+
To handle this: Run the preamble, capture the output. If it includes "not configured", note that setup is needed and continue. Do not exit.
|
|
260
|
+
|
|
261
|
+
## Completion
|
|
262
|
+
|
|
263
|
+
As a final step, log skill completion:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
echo '{"skill":"portal-onboard","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Related Skills
|
|
270
|
+
|
|
271
|
+
This skill chains the following skills inline (read their SKILL.md files for detailed instructions):
|
|
272
|
+
- `/portal setup` -- Step 2
|
|
273
|
+
- `/portal create` -- Step 3
|
|
274
|
+
- `/portal credentials` -- Step 4
|
|
275
|
+
- `/portal preview` -- Step 5
|
|
276
|
+
- `/portal dev` -- called within Step 5 if needed
|
|
277
|
+
- `/portal deploy` -- suggested as next step in the summary
|
|
@@ -0,0 +1,257 @@
|
|
|
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 (run first)
|
|
10
|
+
|
|
11
|
+
Before doing anything else, execute this block in a Bash tool call:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
CONFIG="$HOME/.showpane/config.json"
|
|
15
|
+
if [ ! -f "$CONFIG" ]; then
|
|
16
|
+
echo "Showpane not configured. Run /portal setup first."
|
|
17
|
+
exit 1
|
|
18
|
+
fi
|
|
19
|
+
APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
|
|
20
|
+
DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','docker'))" 2>/dev/null)
|
|
21
|
+
ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
|
|
22
|
+
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
23
|
+
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
24
|
+
DATABASE_URL="${DATABASE_URL:-}"
|
|
25
|
+
if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
26
|
+
echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
|
|
27
|
+
exit 1
|
|
28
|
+
fi
|
|
29
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$HOME/.showpane/current}"
|
|
30
|
+
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
31
|
+
echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
|
|
32
|
+
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
33
|
+
[ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
|
|
34
|
+
|
|
35
|
+
# Predictive next-skill suggestion
|
|
36
|
+
if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
|
|
37
|
+
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
|
|
38
|
+
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# Search relevant learnings
|
|
42
|
+
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
43
|
+
if [ -f "$LEARN_FILE" ]; then
|
|
44
|
+
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
45
|
+
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
46
|
+
if [ "$_LEARN_COUNT" -gt 0 ] 2>/dev/null; then
|
|
47
|
+
echo "RECENT_LEARNINGS:"
|
|
48
|
+
tail -5 "$LEARN_FILE" 2>/dev/null
|
|
49
|
+
fi
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Track skill execution
|
|
53
|
+
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
54
|
+
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
55
|
+
echo '{"skill":"portal-preview","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If RECENT_SKILLS is shown, suggest the likely next skill:
|
|
59
|
+
- After portal-create → suggest /portal-preview
|
|
60
|
+
- After portal-preview → suggest /portal-deploy or /portal-share
|
|
61
|
+
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
62
|
+
- After portal-setup → suggest /portal-create
|
|
63
|
+
- After portal-credentials → suggest /portal-share
|
|
64
|
+
- After portal-update → suggest /portal-deploy
|
|
65
|
+
|
|
66
|
+
If RECENT_LEARNINGS is shown, review them before proceeding. Past learnings may contain
|
|
67
|
+
relevant warnings or tips for this operation. Apply them where relevant but don't
|
|
68
|
+
mention them unless they directly affect the current task.
|
|
69
|
+
|
|
70
|
+
## Overview
|
|
71
|
+
|
|
72
|
+
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 the deployment mode and whether a local dev server is running, then opens it using the platform's native command.
|
|
73
|
+
|
|
74
|
+
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.
|
|
75
|
+
|
|
76
|
+
## Steps
|
|
77
|
+
|
|
78
|
+
### Step 1: Identify the target portal
|
|
79
|
+
|
|
80
|
+
If the user specified a slug (e.g., "preview whzan"), use that slug directly.
|
|
81
|
+
|
|
82
|
+
If no slug is provided, check how many portals exist:
|
|
83
|
+
- If there is exactly one portal, use that one and mention it: "Opening your only portal: <slug>"
|
|
84
|
+
- If there are multiple portals, ask: "Which portal do you want to preview? Run /portal list to see your portals."
|
|
85
|
+
- If there are zero portals, say: "No portals found. Create one first with /portal create."
|
|
86
|
+
|
|
87
|
+
For single-portal organizations, auto-selecting saves a round trip.
|
|
88
|
+
|
|
89
|
+
### Step 2: Determine the correct URL
|
|
90
|
+
|
|
91
|
+
The URL depends on the environment. Check in this order of priority:
|
|
92
|
+
|
|
93
|
+
**Option A: Local dev server running (check port 3000)**
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
lsof -i :3000 -sTCP:LISTEN -t >/dev/null 2>&1 && echo "DEV_RUNNING=true" || echo "DEV_RUNNING=false"
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
If a process is listening on port 3000, the URL is:
|
|
100
|
+
```
|
|
101
|
+
http://localhost:3000/client/<slug>
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Option B: Docker deploy mode (check port 8080)**
|
|
105
|
+
|
|
106
|
+
If `DEPLOY_MODE` is `docker`, check if the container is running:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
lsof -i :8080 -sTCP:LISTEN -t >/dev/null 2>&1 && echo "DOCKER_RUNNING=true" || echo "DOCKER_RUNNING=false"
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
If running, the URL is:
|
|
113
|
+
```
|
|
114
|
+
http://localhost:8080/client/<slug>
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Option C: NEXT_PUBLIC_APP_URL is set**
|
|
118
|
+
|
|
119
|
+
If the environment variable `NEXT_PUBLIC_APP_URL` is set (sourced from `.env` by the preamble), use it:
|
|
120
|
+
```
|
|
121
|
+
${NEXT_PUBLIC_APP_URL}/client/<slug>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
This handles production/staging URLs, custom domains, and any other deployment target.
|
|
125
|
+
|
|
126
|
+
**Fallback: Nothing running**
|
|
127
|
+
|
|
128
|
+
If no server is detected and no app URL is configured, do not open the browser. Instead, inform the user:
|
|
129
|
+
|
|
130
|
+
"No running server detected. Start the dev server with /portal dev, or set NEXT_PUBLIC_APP_URL in your .env file."
|
|
131
|
+
|
|
132
|
+
### Step 3: Open the URL in the browser
|
|
133
|
+
|
|
134
|
+
Use the platform-appropriate command:
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# macOS
|
|
138
|
+
open "<url>"
|
|
139
|
+
|
|
140
|
+
# Linux
|
|
141
|
+
xdg-open "<url>"
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Detect the platform:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
if [[ "$OSTYPE" == "darwin"* ]]; then
|
|
148
|
+
open "<url>"
|
|
149
|
+
elif command -v xdg-open >/dev/null 2>&1; then
|
|
150
|
+
xdg-open "<url>"
|
|
151
|
+
else
|
|
152
|
+
echo "Cannot detect browser opener. Visit: <url>"
|
|
153
|
+
fi
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Run this in a single Bash tool call so the browser opens immediately.
|
|
157
|
+
|
|
158
|
+
### Step 4: Print confirmation
|
|
159
|
+
|
|
160
|
+
After opening, display:
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
Opened portal for <slug> in browser
|
|
164
|
+
URL: http://localhost:3000/client/<slug>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
If the portal has credentials set up, remind the user:
|
|
168
|
+
"Login with the credentials from /portal credentials <slug>. Or generate a share link with /portal share <slug> to bypass login."
|
|
169
|
+
|
|
170
|
+
If the portal is the example portal (slug is "example"), no credentials are needed -- it is publicly accessible by design.
|
|
171
|
+
|
|
172
|
+
## URL Priority Order
|
|
173
|
+
|
|
174
|
+
To be explicit about resolution order:
|
|
175
|
+
|
|
176
|
+
1. **Dev server on port 3000** -- always preferred for local development, regardless of other settings.
|
|
177
|
+
2. **Docker on port 8080** -- used when deploy_mode is docker and the container is running.
|
|
178
|
+
3. **NEXT_PUBLIC_APP_URL** -- used for production or staging environments, or when no local server is detected.
|
|
179
|
+
4. **Fallback** -- no URL available, prompt the user to start a server.
|
|
180
|
+
|
|
181
|
+
This order ensures that during development, the user always sees the latest local version, even if NEXT_PUBLIC_APP_URL points to production.
|
|
182
|
+
|
|
183
|
+
## Conventions
|
|
184
|
+
|
|
185
|
+
- 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.
|
|
186
|
+
- 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).
|
|
187
|
+
- Do not open multiple browser tabs. One call to `open` per invocation.
|
|
188
|
+
- If the slug is "example", mention that this is the built-in example portal and does not require authentication.
|
|
189
|
+
- If learnings indicate the user prefers a specific browser or has a custom port, adapt the URL accordingly.
|
|
190
|
+
|
|
191
|
+
## Edge Cases
|
|
192
|
+
|
|
193
|
+
- **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.
|
|
194
|
+
- **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`.
|
|
195
|
+
- **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>"
|
|
196
|
+
- **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."
|
|
197
|
+
|
|
198
|
+
## Error Handling
|
|
199
|
+
|
|
200
|
+
- If the preamble fails, stop and display the error.
|
|
201
|
+
- If no server is running and no URL is configured, provide clear instructions rather than opening a dead URL.
|
|
202
|
+
- If the `open` or `xdg-open` command fails, print the URL as a fallback: "Could not open browser automatically. Visit: <url>"
|
|
203
|
+
|
|
204
|
+
## Login Context
|
|
205
|
+
|
|
206
|
+
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:
|
|
207
|
+
|
|
208
|
+
- **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.
|
|
209
|
+
- **No credentials**: Warn that the portal will show a login page but there are no credentials to enter. Suggest running `/portal credentials <slug>` first.
|
|
210
|
+
- **Example portal**: The built-in example at `/client/example` is publicly accessible and does not require login. No credentials needed.
|
|
211
|
+
|
|
212
|
+
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."
|
|
213
|
+
|
|
214
|
+
## Previewing After Changes
|
|
215
|
+
|
|
216
|
+
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. If the user is running a production build (docker mode), they may need to rebuild first.
|
|
217
|
+
|
|
218
|
+
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 (hot reload) or rebuild with /portal deploy for docker mode."
|
|
219
|
+
|
|
220
|
+
## Multiple Portals Preview
|
|
221
|
+
|
|
222
|
+
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."
|
|
223
|
+
|
|
224
|
+
To open multiple tabs, run the open command for each URL sequentially:
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
open "http://localhost:3000/client/whzan"
|
|
228
|
+
open "http://localhost:3000/client/acme"
|
|
229
|
+
open "http://localhost:3000/client/example"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
Each `open` call creates a new tab in the default browser.
|
|
233
|
+
|
|
234
|
+
## Telemetry
|
|
235
|
+
|
|
236
|
+
If telemetry is enabled, the preview skill records a minimal event:
|
|
237
|
+
|
|
238
|
+
```json
|
|
239
|
+
{"skill":"portal-preview","ts":"2026-04-07T12:00:00Z","duration_s":1,"outcome":"success"}
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
No portal slug or URL is included in telemetry. The event only records that a preview was triggered.
|
|
243
|
+
|
|
244
|
+
## Completion
|
|
245
|
+
|
|
246
|
+
As a final step, log skill completion:
|
|
247
|
+
|
|
248
|
+
```bash
|
|
249
|
+
echo '{"skill":"portal-preview","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Related Skills
|
|
253
|
+
|
|
254
|
+
- `/portal dev` -- start the local development server
|
|
255
|
+
- `/portal share` -- generate a link that works for anyone, not just the local machine
|
|
256
|
+
- `/portal deploy` -- deploy to production so the portal is accessible externally
|
|
257
|
+
- `/portal credentials` -- get or rotate login credentials for the portal
|