showpane 0.4.13 → 0.4.14
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 +7 -7
- package/bundle/scaffold/VERSION +1 -1
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +7 -0
- 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 +59 -34
- package/bundle/toolchain/skills/portal-analytics/SKILL.md.tmpl +192 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +59 -58
- package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +264 -0
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +62 -42
- package/bundle/toolchain/skills/portal-credentials/SKILL.md.tmpl +198 -0
- package/bundle/toolchain/skills/portal-delete/SKILL.md +56 -31
- package/bundle/toolchain/skills/portal-delete/SKILL.md.tmpl +194 -0
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +59 -46
- package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +452 -0
- package/bundle/toolchain/skills/portal-dev/SKILL.md +62 -41
- package/bundle/toolchain/skills/portal-dev/SKILL.md.tmpl +228 -0
- package/bundle/toolchain/skills/portal-list/SKILL.md +56 -32
- package/bundle/toolchain/skills/portal-list/SKILL.md.tmpl +181 -0
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +310 -161
- package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +317 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +58 -34
- package/bundle/toolchain/skills/portal-preview/SKILL.md.tmpl +171 -0
- package/bundle/toolchain/skills/portal-setup/SKILL.md +73 -56
- package/bundle/toolchain/skills/portal-setup/SKILL.md.tmpl +222 -0
- package/bundle/toolchain/skills/portal-share/SKILL.md +62 -37
- package/bundle/toolchain/skills/portal-share/SKILL.md.tmpl +162 -0
- package/bundle/toolchain/skills/portal-status/SKILL.md +57 -33
- package/bundle/toolchain/skills/portal-status/SKILL.md.tmpl +196 -0
- package/bundle/toolchain/skills/portal-update/SKILL.md +58 -41
- package/bundle/toolchain/skills/portal-update/SKILL.md.tmpl +269 -0
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +56 -31
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md.tmpl +164 -0
- package/bundle/toolchain/skills/portal-verify/SKILL.md +72 -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/dist/index.js +51 -9
- package/package.json +5 -2
|
@@ -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,46 @@ 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"
|
|
72
|
+
```
|
|
73
|
+
|
|
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`, ask the user once about telemetry and then record the decision:
|
|
79
|
+
|
|
80
|
+
- anonymous — local analytics plus anonymous remote sync, with no stable device id
|
|
81
|
+
- off — local analytics only, no remote sync
|
|
82
|
+
|
|
83
|
+
After the user chooses, run:
|
|
84
|
+
```bash
|
|
85
|
+
"$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
|
|
86
|
+
touch "$SHOWPANE_HOME/.telemetry-prompted"
|
|
63
87
|
```
|
|
64
88
|
|
|
65
|
-
If RECENT_SKILLS is shown, suggest the likely next skill:
|
|
89
|
+
If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
66
90
|
- After portal-create → suggest /portal-preview
|
|
67
|
-
- After portal-preview → suggest /portal-deploy
|
|
91
|
+
- After portal-preview → suggest /portal-deploy
|
|
68
92
|
- 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
|
|
93
|
+
- After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
|
|
94
|
+
- After portal-credentials → suggest /portal-deploy before external sharing
|
|
95
|
+
- After portal-update → suggest /portal-preview or /portal-deploy
|
|
72
96
|
|
|
73
|
-
If RECENT_LEARNINGS is shown, review them before proceeding.
|
|
74
|
-
|
|
75
|
-
|
|
97
|
+
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.
|
|
98
|
+
|
|
99
|
+
Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
|
|
100
|
+
|
|
101
|
+
If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
|
|
76
102
|
|
|
77
103
|
## Steps
|
|
78
104
|
|
|
@@ -203,18 +229,17 @@ Update the Organization record with `primaryColor` set to the validated hex valu
|
|
|
203
229
|
|
|
204
230
|
If the user says "skip" or "default", use `#1a1a1a`. Brand color can always be changed later by running `/portal setup` again.
|
|
205
231
|
|
|
206
|
-
### Step 8: Telemetry
|
|
232
|
+
### Step 8: Telemetry reminder
|
|
207
233
|
|
|
208
|
-
|
|
234
|
+
Do not re-run the global telemetry interview here.
|
|
209
235
|
|
|
210
|
-
|
|
236
|
+
If the shared runtime preamble already set telemetry, keep that value. If it has
|
|
237
|
+
not been set yet for some reason, default to:
|
|
211
238
|
|
|
212
|
-
|
|
213
|
-
- **
|
|
214
|
-
- **anonymous** — anonymous usage stats with no device ID
|
|
215
|
-
- **off** — no telemetry (default)
|
|
239
|
+
- **anonymous** — local analytics plus anonymous remote sync, no stable device ID
|
|
240
|
+
- **off** — local analytics only, no remote sync
|
|
216
241
|
|
|
217
|
-
|
|
242
|
+
If you must set it manually, write the chosen value into config.
|
|
218
243
|
|
|
219
244
|
### Step 9: Save configuration
|
|
220
245
|
|
|
@@ -232,7 +257,7 @@ cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
|
|
|
232
257
|
"app_path": "<resolved_absolute_path>",
|
|
233
258
|
"deploy_mode": "local",
|
|
234
259
|
"orgSlug": "<org_slug>",
|
|
235
|
-
"telemetry": "<
|
|
260
|
+
"telemetry": "<anonymous|off>"
|
|
236
261
|
}
|
|
237
262
|
CONFIGEOF
|
|
238
263
|
chmod 600 "$HOME/.showpane/config.json"
|
|
@@ -251,11 +276,11 @@ Showpane setup complete!
|
|
|
251
276
|
Deploy mode: local
|
|
252
277
|
Organization: Acme Consulting (acme-consulting)
|
|
253
278
|
Brand color: #2563eb
|
|
254
|
-
Telemetry:
|
|
279
|
+
Telemetry: anonymous
|
|
255
280
|
|
|
256
281
|
Next steps:
|
|
257
|
-
1.
|
|
258
|
-
2.
|
|
282
|
+
1. Recommended first run: /portal onboard
|
|
283
|
+
2. Fast repeat-user path: /portal create <slug>
|
|
259
284
|
3. View the example portal: open http://localhost:3000/client/example
|
|
260
285
|
```
|
|
261
286
|
|
|
@@ -282,19 +307,11 @@ Never silently continue past a failure. Each step depends on the previous step s
|
|
|
282
307
|
|
|
283
308
|
## Completion
|
|
284
309
|
|
|
285
|
-
As a final step, log skill completion:
|
|
310
|
+
As a final step, log skill completion and telemetry:
|
|
286
311
|
|
|
287
312
|
```bash
|
|
288
313
|
echo '{"skill":"portal-setup","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
314
|
+
_TEL_END=$(date +%s)
|
|
315
|
+
_TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
|
|
316
|
+
"$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-setup" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
|
|
289
317
|
```
|
|
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,222 @@
|
|
|
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
|
+
- Still ask for org name, contact details, and website URL
|
|
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
|
+
1. **Organization name** (required) — e.g., "Acme Consulting". This is the name that appears in portal headers alongside the client name.
|
|
108
|
+
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.
|
|
109
|
+
3. **Contact email** (required) — e.g., "jane@acme.com". Displayed in the portal footer as a mailto link.
|
|
110
|
+
4. **Contact title** (optional, default: "Account Manager") — e.g., "Director", "Partner", "Client Success Lead". Shown next to the contact name in the portal footer.
|
|
111
|
+
5. **Contact phone** (optional) — e.g., "+44 7700 900000". If provided, displayed alongside email in the portal footer.
|
|
112
|
+
6. **Company website URL** (optional) — e.g., "acme.com". Used to auto-fetch the company logo via Clearbit.
|
|
113
|
+
- If provided, fetch logo URL: `https://logo.clearbit.com/{domain}` and store in `Organization.logoUrl`
|
|
114
|
+
- Also store the URL in `Organization.websiteUrl`
|
|
115
|
+
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`
|
|
116
|
+
|
|
117
|
+
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.
|
|
118
|
+
|
|
119
|
+
Create the organization record in the database using the app's Prisma client. The Organization model stores:
|
|
120
|
+
- `name`, `slug`, `contactName`, `contactEmail`, `contactTitle`, `contactPhone`
|
|
121
|
+
|
|
122
|
+
If an Organization with that slug already exists in the database, present two options:
|
|
123
|
+
1. Use the existing organization (show its current details so the user can verify)
|
|
124
|
+
2. Create a new organization with a different slug
|
|
125
|
+
|
|
126
|
+
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.
|
|
127
|
+
|
|
128
|
+
### Step 7: Theme configuration
|
|
129
|
+
|
|
130
|
+
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:
|
|
131
|
+
|
|
132
|
+
- Default: `#1a1a1a` (near-black, professional — works well for most brands)
|
|
133
|
+
- Common choices: `#2563eb` (blue), `#059669` (green), `#dc2626` (red), `#7c3aed` (purple)
|
|
134
|
+
- Tip: if the user has a brand guidelines document or website, suggest pulling the primary color from there
|
|
135
|
+
|
|
136
|
+
Validate the input is a valid hex color:
|
|
137
|
+
- Accept with or without `#` prefix (add `#` if missing)
|
|
138
|
+
- Accept 3-digit shorthand (`#abc` expands to `#aabbcc`)
|
|
139
|
+
- Accept 6-digit full form (`#2563eb`)
|
|
140
|
+
- Reject anything else with a clear message: "Please enter a valid hex color like #2563eb"
|
|
141
|
+
|
|
142
|
+
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).
|
|
143
|
+
|
|
144
|
+
If the user says "skip" or "default", use `#1a1a1a`. Brand color can always be changed later by running `/portal setup` again.
|
|
145
|
+
|
|
146
|
+
### Step 8: Telemetry reminder
|
|
147
|
+
|
|
148
|
+
Do not re-run the global telemetry interview here.
|
|
149
|
+
|
|
150
|
+
If the shared runtime preamble already set telemetry, keep that value. If it has
|
|
151
|
+
not been set yet for some reason, default to:
|
|
152
|
+
|
|
153
|
+
- **anonymous** — local analytics plus anonymous remote sync, no stable device ID
|
|
154
|
+
- **off** — local analytics only, no remote sync
|
|
155
|
+
|
|
156
|
+
If you must set it manually, write the chosen value into config.
|
|
157
|
+
|
|
158
|
+
### Step 9: Save configuration
|
|
159
|
+
|
|
160
|
+
Create the `~/.showpane/` directory if it doesn't exist:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
mkdir -p "$HOME/.showpane"
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Write the config file:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
|
|
170
|
+
{
|
|
171
|
+
"app_path": "<resolved_absolute_path>",
|
|
172
|
+
"deploy_mode": "local",
|
|
173
|
+
"orgSlug": "<org_slug>",
|
|
174
|
+
"telemetry": "<anonymous|off>"
|
|
175
|
+
}
|
|
176
|
+
CONFIGEOF
|
|
177
|
+
chmod 600 "$HOME/.showpane/config.json"
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
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).
|
|
181
|
+
|
|
182
|
+
### Step 10: Print success summary
|
|
183
|
+
|
|
184
|
+
Display a clear summary:
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
Showpane setup complete!
|
|
188
|
+
|
|
189
|
+
App path: /path/to/showpane-project
|
|
190
|
+
Deploy mode: local
|
|
191
|
+
Organization: Acme Consulting (acme-consulting)
|
|
192
|
+
Brand color: #2563eb
|
|
193
|
+
Telemetry: anonymous
|
|
194
|
+
|
|
195
|
+
Next steps:
|
|
196
|
+
1. Recommended first run: /portal onboard
|
|
197
|
+
2. Fast repeat-user path: /portal create <slug>
|
|
198
|
+
3. View the example portal: open http://localhost:3000/client/example
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Step 11: Record learning (optional)
|
|
202
|
+
|
|
203
|
+
If this is the first setup, create an initial learning entry:
|
|
204
|
+
|
|
205
|
+
```bash
|
|
206
|
+
mkdir -p "$HOME/.showpane"
|
|
207
|
+
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"
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Error Handling
|
|
211
|
+
|
|
212
|
+
Each step can fail independently. Handle failures gracefully:
|
|
213
|
+
|
|
214
|
+
- **App path not found**: Ask the user to run `npx showpane` first or point setup at an existing generated Showpane project
|
|
215
|
+
- **npm install fails**: Check Node.js version (requires 18+), check internet connectivity, suggest clearing `node_modules` and retrying
|
|
216
|
+
- **Prisma db push fails**: Check that DATABASE_URL is set to `file:./dev.db` (or another valid SQLite `file:` URL) in `.env`
|
|
217
|
+
- **Organization creation fails**: Check database connectivity. If the Prisma client throws a connection error, verify DATABASE_URL in `.env`
|
|
218
|
+
- **Config write fails**: Check that `$HOME/.showpane/` is writable. On some systems, home directory permissions may block directory creation
|
|
219
|
+
|
|
220
|
+
Never silently continue past a failure. Each step depends on the previous step succeeding.
|
|
221
|
+
|
|
222
|
+
{{COMPLETION}}
|
|
@@ -14,14 +14,17 @@ If the user asks for broader share-link capabilities than Showpane supports, rea
|
|
|
14
14
|
Before doing anything else, execute this block in a Bash tool call:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
|
-
|
|
17
|
+
SHOWPANE_HOME="$HOME/.showpane"
|
|
18
|
+
SHOWPANE_BIN="$SHOWPANE_HOME/bin"
|
|
19
|
+
CONFIG="$SHOWPANE_HOME/config.json"
|
|
18
20
|
if [ ! -f "$CONFIG" ]; then
|
|
19
21
|
echo "Showpane not configured. Run /portal setup first."
|
|
20
22
|
exit 1
|
|
21
23
|
fi
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
|
|
25
|
+
APP_PATH=$("$SHOWPANE_BIN/showpane-config" get app_path 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
|
|
26
|
+
DEPLOY_MODE=$("$SHOWPANE_BIN/showpane-config" get deploy_mode 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null || echo "local")
|
|
27
|
+
ORG_SLUG=$("$SHOWPANE_BIN/showpane-config" get orgSlug 2>/dev/null || python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null || true)
|
|
25
28
|
APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
|
|
26
29
|
if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
|
|
27
30
|
DATABASE_URL="${DATABASE_URL:-}"
|
|
@@ -29,20 +32,26 @@ if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
|
|
|
29
32
|
echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
|
|
30
33
|
exit 1
|
|
31
34
|
fi
|
|
32
|
-
|
|
35
|
+
|
|
36
|
+
SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
|
|
33
37
|
SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
|
|
34
|
-
|
|
35
|
-
|
|
38
|
+
_UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
|
|
39
|
+
[ -n "$_UPD" ] && echo "$_UPD" || true
|
|
40
|
+
mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
|
|
41
|
+
touch "$SHOWPANE_HOME/sessions/$PPID"
|
|
42
|
+
find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
|
|
43
|
+
TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
|
|
44
|
+
TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
|
|
45
|
+
_TEL_START=$(date +%s)
|
|
46
|
+
_SESSION_ID="${PPID:-0}-$(date +%s)"
|
|
47
|
+
|
|
48
|
+
LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
|
|
36
49
|
[ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
_RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
|
|
50
|
+
if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
|
|
51
|
+
_RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
|
|
52
|
+
' ',' | sed 's/,$//' || true)
|
|
41
53
|
[ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
|
|
42
54
|
fi
|
|
43
|
-
|
|
44
|
-
# Search relevant learnings
|
|
45
|
-
LEARN_FILE="$HOME/.showpane/learnings.jsonl"
|
|
46
55
|
if [ -f "$LEARN_FILE" ]; then
|
|
47
56
|
_LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
|
|
48
57
|
echo "LEARNINGS: $_LEARN_COUNT entries"
|
|
@@ -52,27 +61,46 @@ if [ -f "$LEARN_FILE" ]; then
|
|
|
52
61
|
fi
|
|
53
62
|
fi
|
|
54
63
|
|
|
55
|
-
|
|
56
|
-
SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
|
|
64
|
+
SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
|
|
57
65
|
mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
|
|
58
66
|
echo '{"skill":"portal-share","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
|
|
67
|
+
echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
|
|
68
|
+
echo "TELEMETRY: $TEL"
|
|
69
|
+
echo "TEL_PROMPTED: $TEL_PROMPTED"
|
|
59
70
|
```
|
|
60
71
|
|
|
61
|
-
If
|
|
72
|
+
If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
|
|
73
|
+
|
|
74
|
+
If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal upgrade`.
|
|
75
|
+
|
|
76
|
+
If `TEL_PROMPTED` is `no`, ask the user once about telemetry and then record the decision:
|
|
77
|
+
|
|
78
|
+
- anonymous — local analytics plus anonymous remote sync, with no stable device id
|
|
79
|
+
- off — local analytics only, no remote sync
|
|
80
|
+
|
|
81
|
+
After the user chooses, run:
|
|
82
|
+
```bash
|
|
83
|
+
"$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
|
|
84
|
+
touch "$SHOWPANE_HOME/.telemetry-prompted"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
If `RECENT_SKILLS` is shown, suggest the likely next skill:
|
|
62
88
|
- After portal-create → suggest /portal-preview
|
|
63
|
-
- After portal-preview → suggest /portal-deploy
|
|
89
|
+
- After portal-preview → suggest /portal-deploy
|
|
64
90
|
- After portal-deploy → suggest /portal-status or /portal-verify
|
|
65
|
-
- After portal-setup → suggest /portal-create
|
|
66
|
-
- After portal-credentials → suggest /portal-
|
|
67
|
-
- After portal-update → suggest /portal-deploy
|
|
91
|
+
- After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
|
|
92
|
+
- After portal-credentials → suggest /portal-deploy before external sharing
|
|
93
|
+
- After portal-update → suggest /portal-preview or /portal-deploy
|
|
68
94
|
|
|
69
|
-
If RECENT_LEARNINGS is shown, review them before proceeding.
|
|
70
|
-
|
|
71
|
-
|
|
95
|
+
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.
|
|
96
|
+
|
|
97
|
+
Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
|
|
98
|
+
|
|
99
|
+
If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
|
|
72
100
|
|
|
73
101
|
## Overview
|
|
74
102
|
|
|
75
|
-
This skill generates a signed share link that allows
|
|
103
|
+
This skill generates a signed share link that allows someone to access a hosted portal without entering a username and password. Treat it as a hosted convenience after publish, not as the default answer to first-run sharing.
|
|
76
104
|
|
|
77
105
|
Share links use HMAC-SHA256 signed tokens. The token encodes the portal slug, a share scope, and the current credential version. Share links do not expire automatically. If credentials are rotated after the link is generated, the link becomes invalid automatically -- this is by design.
|
|
78
106
|
|
|
@@ -92,7 +120,7 @@ Run the share link generator:
|
|
|
92
120
|
cd $APP_PATH && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig $APP_PATH/tsconfig.json $SKILL_DIR/bin/generate-share-link.ts --slug <slug> --org-id <org_id>
|
|
93
121
|
```
|
|
94
122
|
|
|
95
|
-
The script reads AUTH_SECRET from the app's `.env` file (sourced by the preamble) and uses the `signShareToken` function from the app's `client-auth.ts` module. It constructs a full URL using NEXT_PUBLIC_APP_URL (also from `.env`).
|
|
123
|
+
The script reads AUTH_SECRET from the app's `.env` file (sourced by the preamble) and uses the `signShareToken` function from the app's `client-auth.ts` module. It constructs a full URL using NEXT_PUBLIC_APP_URL (also from `.env`), so for external sharing the recommended path is: deploy first, then generate the share link.
|
|
96
124
|
|
|
97
125
|
Expected success response:
|
|
98
126
|
|
|
@@ -127,7 +155,7 @@ Present the link in a clear, copy-friendly ASCII box:
|
|
|
127
155
|
|
|
128
156
|
After displaying the link, add this note:
|
|
129
157
|
|
|
130
|
-
"
|
|
158
|
+
"This link is meant for the hosted portal. The link stays valid until the portal credentials are rotated or the portal is deactivated."
|
|
131
159
|
|
|
132
160
|
If the user has previously generated share links (check learnings for patterns), you can skip the explanation and just show the link.
|
|
133
161
|
|
|
@@ -147,7 +175,7 @@ Do NOT log the share URL to learnings or telemetry. The URL contains the signed
|
|
|
147
175
|
- Always display the full URL, never truncate or abbreviate it. The user needs to copy-paste it.
|
|
148
176
|
- Make it clear that the link does not expire automatically and is revoked by credential rotation or portal deactivation.
|
|
149
177
|
- Use double-line box drawing (`═`) for the border around the link.
|
|
150
|
-
- If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user: "This is a local development URL.
|
|
178
|
+
- If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user immediately: "This is a local development URL. Do not send this to a client. Publish with /portal deploy first, then generate the share link again."
|
|
151
179
|
|
|
152
180
|
## Error Handling
|
|
153
181
|
|
|
@@ -186,11 +214,11 @@ The signature is computed using AUTH_SECRET from the app's `.env`. If AUTH_SECRE
|
|
|
186
214
|
|
|
187
215
|
Common patterns for using share links in practice:
|
|
188
216
|
|
|
189
|
-
**After
|
|
217
|
+
**After publish**: Deploy the portal with `/portal deploy`, then generate a share link if you want a direct hosted access URL instead of asking the client to log in with credentials.
|
|
190
218
|
|
|
191
219
|
**For quick reviews**: If a colleague or stakeholder needs to see the portal but should not have permanent credentials, a share link is ideal. It can be reused and does not create a full operator login.
|
|
192
220
|
|
|
193
|
-
**Re-sharing after content update**: If you update portal content with `/portal update
|
|
221
|
+
**Re-sharing after content update**: If you update portal content with `/portal update`, publish the latest version if needed, then generate a fresh share link and send it.
|
|
194
222
|
|
|
195
223
|
## Learnings Integration
|
|
196
224
|
|
|
@@ -220,14 +248,11 @@ If the user wants to revoke all outstanding share links immediately, the mechani
|
|
|
220
248
|
|
|
221
249
|
## Completion
|
|
222
250
|
|
|
223
|
-
As a final step, log skill completion:
|
|
251
|
+
As a final step, log skill completion and telemetry:
|
|
224
252
|
|
|
225
253
|
```bash
|
|
226
254
|
echo '{"skill":"portal-share","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
|
|
255
|
+
_TEL_END=$(date +%s)
|
|
256
|
+
_TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
|
|
257
|
+
"$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-share" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
|
|
227
258
|
```
|
|
228
|
-
|
|
229
|
-
## Related Skills
|
|
230
|
-
|
|
231
|
-
- `/portal credentials` -- set up or rotate credentials (required before sharing)
|
|
232
|
-
- `/portal preview` -- open the portal locally to verify content before sharing
|
|
233
|
-
- `/portal analytics` -- check engagement after sharing to see if the client accessed the portal
|