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.
Files changed (51) hide show
  1. package/README.md +2 -1
  2. package/bundle/meta/scaffold-manifest.json +7 -7
  3. package/bundle/scaffold/VERSION +1 -1
  4. package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +7 -0
  5. package/bundle/scaffold/src/app/page.tsx +43 -6
  6. package/bundle/scaffold/src/components/portal-shell.tsx +23 -0
  7. package/bundle/scaffold/src/lib/portal-contracts.ts +33 -0
  8. package/bundle/toolchain/CLI_VERSION +1 -0
  9. package/bundle/toolchain/TELEMETRY_CONFIG.json +4 -0
  10. package/bundle/toolchain/VERSION +1 -1
  11. package/bundle/toolchain/bin/ensure-cloud-project-link.ts +34 -1
  12. package/bundle/toolchain/bin/showpane-config +108 -29
  13. package/bundle/toolchain/bin/showpane-telemetry-log +84 -0
  14. package/bundle/toolchain/bin/showpane-telemetry-sync +212 -0
  15. package/bundle/toolchain/bin/showpane-update-check +130 -0
  16. package/bundle/toolchain/skills/SKILL.md.tmpl +13 -0
  17. package/bundle/toolchain/skills/VERSION +1 -1
  18. package/bundle/toolchain/skills/portal-analytics/SKILL.md +59 -34
  19. package/bundle/toolchain/skills/portal-analytics/SKILL.md.tmpl +192 -0
  20. package/bundle/toolchain/skills/portal-create/SKILL.md +59 -58
  21. package/bundle/toolchain/skills/portal-create/SKILL.md.tmpl +264 -0
  22. package/bundle/toolchain/skills/portal-credentials/SKILL.md +62 -42
  23. package/bundle/toolchain/skills/portal-credentials/SKILL.md.tmpl +198 -0
  24. package/bundle/toolchain/skills/portal-delete/SKILL.md +56 -31
  25. package/bundle/toolchain/skills/portal-delete/SKILL.md.tmpl +194 -0
  26. package/bundle/toolchain/skills/portal-deploy/SKILL.md +59 -46
  27. package/bundle/toolchain/skills/portal-deploy/SKILL.md.tmpl +452 -0
  28. package/bundle/toolchain/skills/portal-dev/SKILL.md +62 -41
  29. package/bundle/toolchain/skills/portal-dev/SKILL.md.tmpl +228 -0
  30. package/bundle/toolchain/skills/portal-list/SKILL.md +56 -32
  31. package/bundle/toolchain/skills/portal-list/SKILL.md.tmpl +181 -0
  32. package/bundle/toolchain/skills/portal-onboard/SKILL.md +310 -161
  33. package/bundle/toolchain/skills/portal-onboard/SKILL.md.tmpl +317 -0
  34. package/bundle/toolchain/skills/portal-preview/SKILL.md +58 -34
  35. package/bundle/toolchain/skills/portal-preview/SKILL.md.tmpl +171 -0
  36. package/bundle/toolchain/skills/portal-setup/SKILL.md +73 -56
  37. package/bundle/toolchain/skills/portal-setup/SKILL.md.tmpl +222 -0
  38. package/bundle/toolchain/skills/portal-share/SKILL.md +62 -37
  39. package/bundle/toolchain/skills/portal-share/SKILL.md.tmpl +162 -0
  40. package/bundle/toolchain/skills/portal-status/SKILL.md +57 -33
  41. package/bundle/toolchain/skills/portal-status/SKILL.md.tmpl +196 -0
  42. package/bundle/toolchain/skills/portal-update/SKILL.md +58 -41
  43. package/bundle/toolchain/skills/portal-update/SKILL.md.tmpl +269 -0
  44. package/bundle/toolchain/skills/portal-upgrade/SKILL.md +56 -31
  45. package/bundle/toolchain/skills/portal-upgrade/SKILL.md.tmpl +164 -0
  46. package/bundle/toolchain/skills/portal-verify/SKILL.md +72 -14
  47. package/bundle/toolchain/skills/portal-verify/SKILL.md.tmpl +224 -0
  48. package/bundle/toolchain/skills/shared/preamble.md +30 -126
  49. package/bundle/toolchain/skills/shared/runtime-principles.md +25 -0
  50. package/dist/index.js +51 -9
  51. package/package.json +5 -2
@@ -18,16 +18,20 @@ hooks:
18
18
 
19
19
  ## Preamble (run first)
20
20
 
21
+ Before doing anything else, execute this block in a Bash tool call:
22
+
21
23
  ```bash
22
- # Read config
23
- CONFIG="$HOME/.showpane/config.json"
24
+ SHOWPANE_HOME="$HOME/.showpane"
25
+ SHOWPANE_BIN="$SHOWPANE_HOME/bin"
26
+ CONFIG="$SHOWPANE_HOME/config.json"
24
27
  if [ ! -f "$CONFIG" ]; then
25
28
  echo "Showpane not configured. Run /portal setup first."
26
29
  exit 1
27
30
  fi
28
- APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
29
- DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null)
30
- ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
31
+
32
+ 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)
33
+ 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")
34
+ 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)
31
35
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
32
36
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
33
37
  DATABASE_URL="${DATABASE_URL:-}"
@@ -35,20 +39,26 @@ if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
35
39
  echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
36
40
  exit 1
37
41
  fi
38
- SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$HOME/.showpane/current}"
42
+
43
+ SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
39
44
  SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
40
- echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
41
- LEARN_FILE="$HOME/.showpane/learnings.jsonl"
45
+ _UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
46
+ [ -n "$_UPD" ] && echo "$_UPD" || true
47
+ mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
48
+ touch "$SHOWPANE_HOME/sessions/$PPID"
49
+ find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
50
+ TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
51
+ TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
52
+ _TEL_START=$(date +%s)
53
+ _SESSION_ID="${PPID:-0}-$(date +%s)"
54
+
55
+ LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
42
56
  [ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
43
-
44
- # Predictive next-skill suggestion
45
- if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
46
- _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)
57
+ if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
58
+ _RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
59
+ ' ',' | sed 's/,$//' || true)
47
60
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
48
61
  fi
49
-
50
- # Search relevant learnings
51
- LEARN_FILE="$HOME/.showpane/learnings.jsonl"
52
62
  if [ -f "$LEARN_FILE" ]; then
53
63
  _LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
54
64
  echo "LEARNINGS: $_LEARN_COUNT entries"
@@ -58,23 +68,42 @@ if [ -f "$LEARN_FILE" ]; then
58
68
  fi
59
69
  fi
60
70
 
61
- # Track skill execution
62
- SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
71
+ SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
63
72
  mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
64
73
  echo '{"skill":"portal-credentials","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
74
+ echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
75
+ echo "TELEMETRY: $TEL"
76
+ echo "TEL_PROMPTED: $TEL_PROMPTED"
65
77
  ```
66
78
 
67
- If RECENT_SKILLS is shown, suggest the likely next skill:
79
+ If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
80
+
81
+ If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal upgrade`.
82
+
83
+ If `TEL_PROMPTED` is `no`, ask the user once about telemetry and then record the decision:
84
+
85
+ - anonymous — local analytics plus anonymous remote sync, with no stable device id
86
+ - off — local analytics only, no remote sync
87
+
88
+ After the user chooses, run:
89
+ ```bash
90
+ "$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
91
+ touch "$SHOWPANE_HOME/.telemetry-prompted"
92
+ ```
93
+
94
+ If `RECENT_SKILLS` is shown, suggest the likely next skill:
68
95
  - After portal-create → suggest /portal-preview
69
- - After portal-preview → suggest /portal-deploy or /portal-share
96
+ - After portal-preview → suggest /portal-deploy
70
97
  - After portal-deploy → suggest /portal-status or /portal-verify
71
- - After portal-setup → suggest /portal-create
72
- - After portal-credentials → suggest /portal-share
73
- - After portal-update → suggest /portal-deploy
98
+ - After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
99
+ - After portal-credentials → suggest /portal-deploy before external sharing
100
+ - After portal-update → suggest /portal-preview or /portal-deploy
101
+
102
+ 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.
74
103
 
75
- If RECENT_LEARNINGS is shown, review them before proceeding. Past learnings may contain
76
- relevant warnings or tips for this operation. Apply them where relevant but don't
77
- mention them unless they directly affect the current task.
104
+ Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
105
+
106
+ If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
78
107
 
79
108
  ## Steps
80
109
 
@@ -167,7 +196,8 @@ Provide guidance on how to share the credentials with the client:
167
196
  > **How to share with your client:**
168
197
  > - Send the username and password via a secure channel (encrypted email, Signal, etc.)
169
198
  > - The login page is at `/client` — the client enters the portal slug as the "company" field, then the username and password
170
- > - For a direct link without credentials, use `/portal share <slug>` to generate a reusable share URL that bypasses the login entirely
199
+ > - For external access, publish the portal first with `/portal deploy`
200
+ > - After publish, you can use `/portal share <slug>` if you want a direct hosted link instead of asking the client to log in
171
201
 
172
202
  Recommend against sharing credentials via:
173
203
  - Unencrypted email (can be intercepted)
@@ -206,8 +236,8 @@ Credentials created for: acme-health
206
236
 
207
237
  Next steps:
208
238
  1. Send credentials to the client via a secure channel
209
- 2. Generate a share link: /portal share <slug>
210
- 3. Deploy if not already live: /portal deploy
239
+ 2. Deploy if not already live: /portal deploy
240
+ 3. Optional hosted direct link after publish: /portal share <slug>
211
241
  ```
212
242
 
213
243
  ### Step 9: Record credential event
@@ -254,21 +284,11 @@ Even in bulk mode, show the security warning once after all credentials are disp
254
284
 
255
285
  ## Completion
256
286
 
257
- As a final step, log skill completion:
287
+ As a final step, log skill completion and telemetry:
258
288
 
259
289
  ```bash
260
290
  echo '{"skill":"portal-credentials","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
291
+ _TEL_END=$(date +%s)
292
+ _TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
293
+ "$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-credentials" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
261
294
  ```
262
-
263
- ## Conventions
264
-
265
- - Always run the bin script rather than manipulating the database directly — never generate passwords, hash them, or write to the database from the skill
266
- - Show credentials in an ASCII box for clear visibility in the terminal
267
- - Warn about credential impermanence immediately after displaying them
268
- - If the portal has no DB record, direct the user to `/portal create` first
269
- - Never store, log, or record the plaintext password anywhere — not in learnings, not in telemetry, not in config
270
- - Confirm before rotating existing credentials (default to "no") since rotation invalidates sessions
271
- - The username is always the same as the portal slug — this is by design for simplicity
272
- - If the user asks "what is the password for X portal", explain that passwords are one-way hashed and cannot be retrieved — offer to rotate to a new one instead
273
- - After creating credentials, always suggest the next step: either share with the client or deploy if not already live
274
- - If the app is not deployed yet, credentials still work — they are stored in the database regardless of whether the app is running in production
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: portal-credentials
3
+ description: |
4
+ Create or rotate login credentials for a client portal. Generates username and password, hashes and stores in DB.
5
+ Trigger phrases: "portal credentials", "create login", "rotate password", "reset credentials", "portal password". (showpane)
6
+ allowed-tools: [Bash, Read, Write, Edit]
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
+ - matcher: "Edit"
14
+ hooks:
15
+ - type: command
16
+ command: "bash ${CLAUDE_SKILL_DIR}/../showpane-shared/bin/check-portal-guard.sh"
17
+ ---
18
+
19
+ {{PREAMBLE}}
20
+
21
+ ## Steps
22
+
23
+ ### Step 1: Identify the portal
24
+
25
+ If the user provided a slug (e.g., `/portal credentials acme-health`), use it. Otherwise, list available portals to help the user choose:
26
+
27
+ ```bash
28
+ cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/list-portals.ts" --org-id <org_id>
29
+ ```
30
+
31
+ Present the list and ask which portal needs credentials. If there is only one portal, confirm it rather than asking.
32
+
33
+ Verify the portal exists by checking the database. The `create-portal.ts` script should have been run during `/portal create` to register the portal. If no DB record exists, inform the user:
34
+
35
+ > "No portal record found for '<slug>'. Run `/portal create <slug>` first to register it."
36
+
37
+ Also check the portal's current credential status from the list output. If credentials already exist, inform the user before proceeding:
38
+
39
+ > "Portal '<slug>' already has credentials (username: <username>). Running this will rotate to a new password and invalidate all existing sessions. Continue? (y/N)"
40
+
41
+ This confirmation is important because credential rotation has an immediate impact on any clients currently logged in. Default to "no" to prevent accidental rotation.
42
+
43
+ ### Step 2: Run the rotate-credentials script
44
+
45
+ This script handles both initial credential creation and rotation:
46
+
47
+ ```bash
48
+ cd "$APP_PATH" && NODE_PATH="$APP_PATH/node_modules" npx tsx --tsconfig "$APP_PATH/tsconfig.json" "$SKILL_DIR/bin/rotate-credentials.ts" --slug <slug> --org-id <org_id>
49
+ ```
50
+
51
+ The script returns JSON on stdout:
52
+
53
+ **For initial creation:**
54
+ ```json
55
+ {"ok": true, "username": "acme-health", "password": "xK9mP2vL8nQr", "rotated": false}
56
+ ```
57
+
58
+ **For rotation (credentials already existed):**
59
+ ```json
60
+ {"ok": true, "username": "acme-health", "password": "bT4wN7jF3hYs", "rotated": true}
61
+ ```
62
+
63
+ **On error:**
64
+ ```json
65
+ {"error": "portal_not_found", "message": "No portal with slug 'acme-health' in org 'demo'"}
66
+ ```
67
+
68
+ ### Step 3: Handle script errors
69
+
70
+ If the script returns an error, handle it based on the error type:
71
+
72
+ - `portal_not_found` — The portal slug doesn't exist in the database for this org. Suggest running `/portal create <slug>` first.
73
+ - `database_error` — Connection or query failure. Check DATABASE_URL and re-apply the local schema with `cd $APP_PATH && npm run prisma:db-push`.
74
+ - `auth_secret_missing` — The AUTH_SECRET environment variable is not set in `$APP_PATH/.env`. The rotate-credentials script needs this to function. Suggest adding `AUTH_SECRET=<random-string>` to the .env file.
75
+
76
+ If the script exits with a non-zero code but produces no JSON output, check stderr for Prisma or Node.js errors and relay them to the user.
77
+
78
+ ### Step 4: Display credentials
79
+
80
+ Present the credentials clearly and prominently. Use an ASCII box for visibility:
81
+
82
+ ```
83
+ ┌─────────────────────────────────────────┐
84
+ │ Portal credentials for: acme-health │
85
+ │ │
86
+ │ Username: acme-health │
87
+ │ Password: xK9mP2vL8nQr │
88
+ │ │
89
+ │ Login URL: /client │
90
+ └─────────────────────────────────────────┘
91
+ ```
92
+
93
+ The ASCII box ensures the credentials stand out clearly in terminal output. Do not use markdown formatting (bold, code blocks) as these may not render in all terminal environments.
94
+
95
+ ### Step 5: Security warning
96
+
97
+ Immediately after displaying credentials, show this warning:
98
+
99
+ > **Save these credentials now.** They will not be shown again. The password is hashed in the database and cannot be retrieved. If lost, run `/portal credentials <slug>` again to rotate to a new password.
100
+
101
+ If this was a rotation (`"rotated": true`), add:
102
+
103
+ > **Credential rotation complete.** All existing sessions for this portal have been invalidated immediately. Anyone currently logged in will need to re-authenticate with the new password.
104
+
105
+ ### Step 6: Suggest sharing method
106
+
107
+ Provide guidance on how to share the credentials with the client:
108
+
109
+ > **How to share with your client:**
110
+ > - Send the username and password via a secure channel (encrypted email, Signal, etc.)
111
+ > - The login page is at `/client` — the client enters the portal slug as the "company" field, then the username and password
112
+ > - For external access, publish the portal first with `/portal deploy`
113
+ > - After publish, you can use `/portal share <slug>` if you want a direct hosted link instead of asking the client to log in
114
+
115
+ Recommend against sharing credentials via:
116
+ - Unencrypted email (can be intercepted)
117
+ - Slack or Teams messages (persistent and searchable by admins)
118
+ - Shared documents or spreadsheets
119
+
120
+ Good sharing channels:
121
+ - Signal or WhatsApp (encrypted, ephemeral)
122
+ - A phone call (verbal)
123
+ - An encrypted email service
124
+ - A password manager's secure sharing feature
125
+
126
+ ### Step 7: Verify login works (if dev server running)
127
+
128
+ If the dev server is running, suggest testing the credentials:
129
+
130
+ ```bash
131
+ lsof -i :3000 -sTCP:LISTEN -t 2>/dev/null
132
+ ```
133
+
134
+ If running, inform the user:
135
+
136
+ > "Test the login at http://localhost:3000/client — enter slug '<slug>', username '<username>', and the password shown above."
137
+
138
+ This step is optional but helps catch configuration issues (like AUTH_SECRET not being set) before sharing credentials with a client.
139
+
140
+ ### Step 8: Summary
141
+
142
+ Print a brief summary:
143
+
144
+ ```
145
+ Credentials created for: acme-health
146
+ Action: new credentials (or: rotated — old sessions invalidated)
147
+ Username: acme-health
148
+ Login: /client
149
+
150
+ Next steps:
151
+ 1. Send credentials to the client via a secure channel
152
+ 2. Deploy if not already live: /portal deploy
153
+ 3. Optional hosted direct link after publish: /portal share <slug>
154
+ ```
155
+
156
+ ### Step 9: Record credential event
157
+
158
+ Record that credentials were created or rotated (but NEVER record the actual credentials):
159
+
160
+ ```bash
161
+ echo '{"skill":"portal-credentials","key":"credentials-created","insight":"Created credentials for <slug>. Rotated: <true|false>.","confidence":10,"ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/learnings.jsonl"
162
+ ```
163
+
164
+ ## Security Conventions
165
+
166
+ - **Credentials are shown exactly once.** Never log them, never write them to a file, never include them in learnings or telemetry.
167
+ - **Rotation is immediate.** When credentials are rotated, the `credentialVersion` is bumped in the database. All existing session tokens for that portal are invalidated instantly because they reference the old version.
168
+ - **Passwords are generated server-side** by the `rotate-credentials.ts` script using cryptographically secure random bytes. Do not generate passwords in the skill — always use the script.
169
+ - **Usernames are derived from the slug.** The username is typically the same as the portal slug (e.g., slug `acme-health` gets username `acme-health`).
170
+ - **No password in config or learnings.** The `~/.showpane/config.json` and `~/.showpane/learnings.jsonl` files must never contain passwords or password hashes.
171
+ - **AUTH_SECRET stays in .env.** The secret used for token signing lives in `$APP_PATH/.env` and is read at runtime by the app. It is never copied to config.json.
172
+
173
+ ## Error Handling
174
+
175
+ - **Script not found**: If `$SKILL_DIR/bin/rotate-credentials.ts` does not exist, the skill pack may not be fully installed. Suggest running `/portal upgrade` or checking the Showpane installation.
176
+ - **Prisma connection error**: DATABASE_URL may be wrong or the database may be down. Check the .env file and database status.
177
+ - **Permission denied on .env**: The preamble sources `$APP_PATH/.env`. If this file is not readable, the DATABASE_URL will not be set. Check file permissions.
178
+ - **Script timeout**: If the script takes more than 30 seconds, something is wrong. The most common cause is a database connection timeout — check network connectivity to the database host.
179
+
180
+ ## Bulk credential creation
181
+
182
+ If the user asks to create credentials for multiple portals at once, run the rotate-credentials script for each portal sequentially. Display all credential sets together in a single output block:
183
+
184
+ ```
185
+ ┌─────────────────────────────────────────┐
186
+ │ 1. acme-health │
187
+ │ Username: acme-health │
188
+ │ Password: xK9mP2vL8nQr │
189
+ │ │
190
+ │ 2. beta-corp │
191
+ │ Username: beta-corp │
192
+ │ Password: mT7kR4wQ9nFs │
193
+ └─────────────────────────────────────────┘
194
+ ```
195
+
196
+ Even in bulk mode, show the security warning once after all credentials are displayed.
197
+
198
+ {{COMPLETION}}
@@ -21,14 +21,17 @@ hooks:
21
21
  Before doing anything else, execute this block in a Bash tool call:
22
22
 
23
23
  ```bash
24
- CONFIG="$HOME/.showpane/config.json"
24
+ SHOWPANE_HOME="$HOME/.showpane"
25
+ SHOWPANE_BIN="$SHOWPANE_HOME/bin"
26
+ CONFIG="$SHOWPANE_HOME/config.json"
25
27
  if [ ! -f "$CONFIG" ]; then
26
28
  echo "Showpane not configured. Run /portal setup first."
27
29
  exit 1
28
30
  fi
29
- APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
30
- DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
31
- 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)
31
+
32
+ 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)
33
+ 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")
34
+ 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)
32
35
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
33
36
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
34
37
  DATABASE_URL="${DATABASE_URL:-}"
@@ -36,20 +39,26 @@ if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
36
39
  echo "App dependencies not installed. Run: cd $APP_PATH && npm install"
37
40
  exit 1
38
41
  fi
39
- SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$HOME/.showpane/current}"
42
+
43
+ SKILL_DIR="${SHOWPANE_TOOLCHAIN_DIR:-$SHOWPANE_HOME/current}"
40
44
  SKILL_VERSION=$(cat "$SKILL_DIR/VERSION" 2>/dev/null || echo "unknown")
41
- echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
42
- LEARN_FILE="$HOME/.showpane/learnings.jsonl"
45
+ _UPD=$("$SHOWPANE_BIN/showpane-update-check" 2>/dev/null || true)
46
+ [ -n "$_UPD" ] && echo "$_UPD" || true
47
+ mkdir -p "$SHOWPANE_HOME/sessions" "$SHOWPANE_HOME/analytics" "$SHOWPANE_HOME/checkpoints"
48
+ touch "$SHOWPANE_HOME/sessions/$PPID"
49
+ find "$SHOWPANE_HOME/sessions" -mmin +120 -type f -delete 2>/dev/null || true
50
+ TEL=$("$SHOWPANE_BIN/showpane-config" get telemetry 2>/dev/null || echo "anonymous")
51
+ TEL_PROMPTED=$([ -f "$SHOWPANE_HOME/.telemetry-prompted" ] && echo "yes" || echo "no")
52
+ _TEL_START=$(date +%s)
53
+ _SESSION_ID="${PPID:-0}-$(date +%s)"
54
+
55
+ LEARN_FILE="$SHOWPANE_HOME/learnings.jsonl"
43
56
  [ -f "$LEARN_FILE" ] && echo "LEARNINGS: $(wc -l < "$LEARN_FILE" | tr -d ' ') loaded" || echo "LEARNINGS: 0"
44
-
45
- # Predictive next-skill suggestion
46
- if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
47
- _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)
57
+ if [ -f "$SHOWPANE_HOME/timeline.jsonl" ]; then
58
+ _RECENT=$(grep '"event":"completed"' "$SHOWPANE_HOME/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '
59
+ ' ',' | sed 's/,$//' || true)
48
60
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
49
61
  fi
50
-
51
- # Search relevant learnings
52
- LEARN_FILE="$HOME/.showpane/learnings.jsonl"
53
62
  if [ -f "$LEARN_FILE" ]; then
54
63
  _LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
55
64
  echo "LEARNINGS: $_LEARN_COUNT entries"
@@ -59,23 +68,42 @@ if [ -f "$LEARN_FILE" ]; then
59
68
  fi
60
69
  fi
61
70
 
62
- # Track skill execution
63
- SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
71
+ SHOWPANE_TIMELINE="$SHOWPANE_HOME/timeline.jsonl"
64
72
  mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
65
73
  echo '{"skill":"portal-delete","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
74
+ echo "SHOWPANE: v$SKILL_VERSION | MODE: $DEPLOY_MODE | APP: $APP_PATH"
75
+ echo "TELEMETRY: $TEL"
76
+ echo "TEL_PROMPTED: $TEL_PROMPTED"
66
77
  ```
67
78
 
68
- If RECENT_SKILLS is shown, suggest the likely next skill:
79
+ If output shows `JUST_UPGRADED <from> <to>`, tell the user Showpane was just upgraded and continue.
80
+
81
+ If output shows `UPGRADE_AVAILABLE <old> <new>`, tell the user a newer Showpane toolchain is available and recommend `/portal upgrade`.
82
+
83
+ If `TEL_PROMPTED` is `no`, ask the user once about telemetry and then record the decision:
84
+
85
+ - anonymous — local analytics plus anonymous remote sync, with no stable device id
86
+ - off — local analytics only, no remote sync
87
+
88
+ After the user chooses, run:
89
+ ```bash
90
+ "$SHOWPANE_BIN/showpane-config" set telemetry <anonymous|off>
91
+ touch "$SHOWPANE_HOME/.telemetry-prompted"
92
+ ```
93
+
94
+ If `RECENT_SKILLS` is shown, suggest the likely next skill:
69
95
  - After portal-create → suggest /portal-preview
70
- - After portal-preview → suggest /portal-deploy or /portal-share
96
+ - After portal-preview → suggest /portal-deploy
71
97
  - After portal-deploy → suggest /portal-status or /portal-verify
72
- - After portal-setup → suggest /portal-create
73
- - After portal-credentials → suggest /portal-share
74
- - After portal-update → suggest /portal-deploy
98
+ - After portal-setup → suggest /portal-onboard for a first run, or /portal-create for the fast path
99
+ - After portal-credentials → suggest /portal-deploy before external sharing
100
+ - After portal-update → suggest /portal-preview or /portal-deploy
75
101
 
76
- If RECENT_LEARNINGS is shown, review them before proceeding. Past learnings may contain
77
- relevant warnings or tips for this operation. Apply them where relevant but don't
78
- mention them unless they directly affect the current task.
102
+ 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.
103
+
104
+ Read `skills/shared/runtime-principles.md` once near the start of the skill and apply the relevant product defaults.
105
+
106
+ If `skills/shared/platform-constraints.md` exists, read it once near the start of the skill and apply only the relevant limits.
79
107
 
80
108
  ## Safety Guard
81
109
 
@@ -252,14 +280,11 @@ If the user wants to immediately revoke all access AND deactivate, the sequence
252
280
 
253
281
  ## Completion
254
282
 
255
- As a final step, log skill completion:
283
+ As a final step, log skill completion and telemetry:
256
284
 
257
285
  ```bash
258
286
  echo '{"skill":"portal-delete","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
287
+ _TEL_END=$(date +%s)
288
+ _TEL_DUR=$(( _TEL_END - ${_TEL_START:-_TEL_END} ))
289
+ "$HOME/.showpane/bin/showpane-telemetry-log" --skill "portal-delete" --duration "$_TEL_DUR" --outcome success --session-id "${_SESSION_ID:-}" 2>/dev/null || true
259
290
  ```
260
-
261
- ## Related Skills
262
-
263
- - `/portal list` -- see all portals before deciding which to deactivate
264
- - `/portal credentials` -- rotate credentials to immediately revoke all access (stronger than deactivate)
265
- - `/portal status` -- check portal health to identify candidates for deactivation