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.
Files changed (106) hide show
  1. package/README.md +14 -1
  2. package/bundle/meta/scaffold-manifest.json +73 -0
  3. package/bundle/scaffold/VERSION +1 -0
  4. package/bundle/scaffold/__dot__env.example +24 -0
  5. package/bundle/scaffold/__dot__gitignore +41 -0
  6. package/bundle/scaffold/docker/Caddyfile +3 -0
  7. package/bundle/scaffold/docker/Dockerfile +30 -0
  8. package/bundle/scaffold/docker-compose.yml +53 -0
  9. package/bundle/scaffold/next.config.ts +20 -0
  10. package/bundle/scaffold/package-lock.json +5843 -0
  11. package/bundle/scaffold/package.json +42 -0
  12. package/bundle/scaffold/postcss.config.js +6 -0
  13. package/bundle/scaffold/prisma/migrations/20260408000000_init/migration.sql +143 -0
  14. package/bundle/scaffold/prisma/migrations/20260408010000_add_visitor_tracking/migration.sql +6 -0
  15. package/bundle/scaffold/prisma/migrations/20260409040000_add_portal_file_checksum/migration.sql +2 -0
  16. package/bundle/scaffold/prisma/migrations/migration_lock.toml +3 -0
  17. package/bundle/scaffold/prisma/schema.local.prisma +131 -0
  18. package/bundle/scaffold/prisma/schema.prisma +128 -0
  19. package/bundle/scaffold/prisma/seed.ts +49 -0
  20. package/bundle/scaffold/public/example-avatar.svg +4 -0
  21. package/bundle/scaffold/public/example-logo.svg +4 -0
  22. package/bundle/scaffold/public/robots.txt +2 -0
  23. package/bundle/scaffold/scripts/backup.sh +19 -0
  24. package/bundle/scaffold/scripts/e2e-verify.sh +487 -0
  25. package/bundle/scaffold/scripts/prisma-db-push.mjs +7 -0
  26. package/bundle/scaffold/scripts/prisma-generate.mjs +3 -0
  27. package/bundle/scaffold/scripts/prisma-schema.mjs +74 -0
  28. package/bundle/scaffold/scripts/restore.sh +31 -0
  29. package/bundle/scaffold/src/__tests__/client-portals.test.ts +80 -0
  30. package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +32 -0
  31. package/bundle/scaffold/src/app/(portal)/client/[slug]/page.tsx +79 -0
  32. package/bundle/scaffold/src/app/(portal)/client/[slug]/s/[token]/route.ts +22 -0
  33. package/bundle/scaffold/src/app/(portal)/client/example/example-client.tsx +372 -0
  34. package/bundle/scaffold/src/app/(portal)/client/example/page.tsx +5 -0
  35. package/bundle/scaffold/src/app/(portal)/client/layout.tsx +7 -0
  36. package/bundle/scaffold/src/app/(portal)/client/page.tsx +18 -0
  37. package/bundle/scaffold/src/app/api/client-auth/route.ts +82 -0
  38. package/bundle/scaffold/src/app/api/client-auth/share/route.ts +30 -0
  39. package/bundle/scaffold/src/app/api/client-events/route.ts +87 -0
  40. package/bundle/scaffold/src/app/api/client-files/[...path]/route.ts +80 -0
  41. package/bundle/scaffold/src/app/api/client-files/client-upload/route.ts +118 -0
  42. package/bundle/scaffold/src/app/api/client-files/route.ts +37 -0
  43. package/bundle/scaffold/src/app/api/client-files/upload/route.ts +131 -0
  44. package/bundle/scaffold/src/app/api/health/route.ts +19 -0
  45. package/bundle/scaffold/src/app/globals.css +7 -0
  46. package/bundle/scaffold/src/app/layout.tsx +25 -0
  47. package/bundle/scaffold/src/app/page.tsx +171 -0
  48. package/bundle/scaffold/src/components/portal-login.tsx +169 -0
  49. package/bundle/scaffold/src/components/portal-shell.tsx +373 -0
  50. package/bundle/scaffold/src/lib/abuse-controls.ts +43 -0
  51. package/bundle/scaffold/src/lib/branding.ts +50 -0
  52. package/bundle/scaffold/src/lib/client-auth.ts +98 -0
  53. package/bundle/scaffold/src/lib/client-portals.ts +134 -0
  54. package/bundle/scaffold/src/lib/control-plane.ts +100 -0
  55. package/bundle/scaffold/src/lib/db.ts +7 -0
  56. package/bundle/scaffold/src/lib/files.ts +124 -0
  57. package/bundle/scaffold/src/lib/load-app-env.ts +42 -0
  58. package/bundle/scaffold/src/lib/portal-contracts.ts +69 -0
  59. package/bundle/scaffold/src/lib/prisma-client.ts +5 -0
  60. package/bundle/scaffold/src/lib/runtime-state.ts +69 -0
  61. package/bundle/scaffold/src/lib/storage.ts +204 -0
  62. package/bundle/scaffold/src/lib/token.ts +186 -0
  63. package/bundle/scaffold/src/lib/utils.ts +6 -0
  64. package/bundle/scaffold/src/middleware.ts +61 -0
  65. package/bundle/scaffold/tailwind.config.ts +15 -0
  66. package/bundle/scaffold/tests/__dot__gitkeep +0 -0
  67. package/bundle/scaffold/tsconfig.json +23 -0
  68. package/bundle/scaffold/vitest.config.ts +13 -0
  69. package/bundle/toolchain/VERSION +1 -0
  70. package/bundle/toolchain/bin/check-slug.ts +59 -0
  71. package/bundle/toolchain/bin/create-deploy-bundle.ts +93 -0
  72. package/bundle/toolchain/bin/create-portal.ts +71 -0
  73. package/bundle/toolchain/bin/delete-portal.ts +48 -0
  74. package/bundle/toolchain/bin/export-file-manifest.ts +84 -0
  75. package/bundle/toolchain/bin/export-runtime-state.ts +90 -0
  76. package/bundle/toolchain/bin/generate-share-link.ts +68 -0
  77. package/bundle/toolchain/bin/list-portals.ts +53 -0
  78. package/bundle/toolchain/bin/materialize-file.ts +35 -0
  79. package/bundle/toolchain/bin/query-analytics.ts +88 -0
  80. package/bundle/toolchain/bin/rotate-credentials.ts +57 -0
  81. package/bundle/toolchain/bin/showpane-config +63 -0
  82. package/bundle/toolchain/bin/tsconfig.json +13 -0
  83. package/bundle/toolchain/skills/VERSION +1 -0
  84. package/bundle/toolchain/skills/portal-analytics/SKILL.md +263 -0
  85. package/bundle/toolchain/skills/portal-create/SKILL.md +341 -0
  86. package/bundle/toolchain/skills/portal-credentials/SKILL.md +274 -0
  87. package/bundle/toolchain/skills/portal-delete/SKILL.md +265 -0
  88. package/bundle/toolchain/skills/portal-deploy/SKILL.md +721 -0
  89. package/bundle/toolchain/skills/portal-dev/SKILL.md +301 -0
  90. package/bundle/toolchain/skills/portal-list/SKILL.md +253 -0
  91. package/bundle/toolchain/skills/portal-onboard/SKILL.md +277 -0
  92. package/bundle/toolchain/skills/portal-preview/SKILL.md +257 -0
  93. package/bundle/toolchain/skills/portal-setup/SKILL.md +309 -0
  94. package/bundle/toolchain/skills/portal-share/SKILL.md +234 -0
  95. package/bundle/toolchain/skills/portal-status/SKILL.md +268 -0
  96. package/bundle/toolchain/skills/portal-update/SKILL.md +348 -0
  97. package/bundle/toolchain/skills/portal-upgrade/SKILL.md +235 -0
  98. package/bundle/toolchain/skills/portal-verify/SKILL.md +265 -0
  99. package/bundle/toolchain/skills/shared/bin/check-portal-guard.sh +49 -0
  100. package/bundle/toolchain/skills/shared/platform-constraints.md +33 -0
  101. package/bundle/toolchain/skills/shared/preamble.md +137 -0
  102. package/bundle/toolchain/templates/consulting/consulting-client.tsx +205 -0
  103. package/bundle/toolchain/templates/onboarding/onboarding-client.tsx +237 -0
  104. package/bundle/toolchain/templates/sales-followup/sales-followup-client.tsx +283 -0
  105. package/dist/index.js +873 -166
  106. package/package.json +3 -2
@@ -0,0 +1,301 @@
1
+ ---
2
+ name: portal-dev
3
+ description: |
4
+ Start the local development server for Showpane. Quick way to preview portals during development.
5
+ Trigger phrases: "portal dev", "start dev server", "run showpane locally", "start the server". (showpane)
6
+ allowed-tools: [Bash, Read]
7
+ ---
8
+
9
+ ## Preamble (run first)
10
+
11
+ This skill uses a simplified preamble — no version check or learnings loading needed for starting a dev server.
12
+
13
+ ```bash
14
+ # Read config
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=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
21
+ APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
22
+ if [ -z "$APP_PATH" ] || [ ! -d "$APP_PATH" ]; then
23
+ echo "App path not found: $APP_PATH"
24
+ echo "Run /portal setup to reconfigure."
25
+ exit 1
26
+ fi
27
+ echo "APP_PATH: $APP_PATH"
28
+
29
+ # Predictive next-skill suggestion
30
+ if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
31
+ _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
32
+ [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
33
+ fi
34
+
35
+ # Search relevant learnings
36
+ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
37
+ if [ -f "$LEARN_FILE" ]; then
38
+ _LEARN_COUNT=$(wc -l < "$LEARN_FILE" 2>/dev/null | tr -d ' ')
39
+ echo "LEARNINGS: $_LEARN_COUNT entries"
40
+ if [ "$_LEARN_COUNT" -gt 0 ] 2>/dev/null; then
41
+ echo "RECENT_LEARNINGS:"
42
+ tail -5 "$LEARN_FILE" 2>/dev/null
43
+ fi
44
+ fi
45
+
46
+ # Track skill execution
47
+ SHOWPANE_TIMELINE="$HOME/.showpane/timeline.jsonl"
48
+ mkdir -p "$(dirname "$SHOWPANE_TIMELINE")"
49
+ echo '{"skill":"portal-dev","event":"started","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$SHOWPANE_TIMELINE" 2>/dev/null
50
+ ```
51
+
52
+ If RECENT_SKILLS is shown, suggest the likely next skill:
53
+ - After portal-create → suggest /portal-preview
54
+ - After portal-preview → suggest /portal-deploy or /portal-share
55
+ - After portal-deploy → suggest /portal-status or /portal-verify
56
+ - After portal-setup → suggest /portal-create
57
+ - After portal-credentials → suggest /portal-share
58
+ - After portal-update → suggest /portal-deploy
59
+
60
+ If RECENT_LEARNINGS is shown, review them before proceeding. Past learnings may contain
61
+ relevant warnings or tips for this operation. Apply them where relevant but don't
62
+ mention them unless they directly affect the current task.
63
+
64
+ ## Steps
65
+
66
+ ### Step 1: Check if port 3000 is already in use
67
+
68
+ ```bash
69
+ lsof -i :3000 -sTCP:LISTEN -t 2>/dev/null
70
+ ```
71
+
72
+ If a process is listening on port 3000:
73
+
74
+ 1. Check if it's already a Showpane/Next.js dev server:
75
+ ```bash
76
+ lsof -i :3000 -sTCP:LISTEN 2>/dev/null | head -5
77
+ ```
78
+
79
+ 2. If it's already the Showpane dev server, inform the user:
80
+ > "Dev server is already running at http://localhost:3000. No action needed."
81
+ >
82
+ > Useful links:
83
+ > - Login page: http://localhost:3000/client
84
+ > - Example portal: http://localhost:3000/client/example
85
+
86
+ 3. If it's a different process, warn the user:
87
+ > "Port 3000 is in use by another process (PID: <pid>). Options:"
88
+ > - Kill it: `kill <pid>` then re-run `/portal dev`
89
+ > - Use a different port: `cd $APP_PATH && PORT=3001 npm run dev`
90
+
91
+ Ask the user how to proceed. Do not kill processes without explicit permission.
92
+
93
+ ### Step 2: Verify dependencies
94
+
95
+ Check that node_modules exists and the Prisma client is generated:
96
+
97
+ ```bash
98
+ if [ ! -d "$APP_PATH/node_modules" ]; then
99
+ echo "Installing dependencies..."
100
+ cd "$APP_PATH" && npm install
101
+ elif [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
102
+ echo "Generating Prisma client..."
103
+ cd "$APP_PATH" && npx prisma generate
104
+ fi
105
+ ```
106
+
107
+ If `npm install` is needed, wait for it to complete before starting the dev server.
108
+
109
+ ### Step 3: Source environment variables
110
+
111
+ The dev server needs DATABASE_URL and other env vars:
112
+
113
+ ```bash
114
+ if [ -f "$APP_PATH/.env" ]; then
115
+ echo "Loading .env from $APP_PATH"
116
+ else
117
+ echo "WARNING: No .env file found at $APP_PATH/.env"
118
+ echo "The dev server may fail without DATABASE_URL and AUTH_SECRET."
119
+ echo "Create $APP_PATH/.env with at minimum:"
120
+ echo " DATABASE_URL=postgresql://postgres:postgres@localhost:5432/showpane"
121
+ echo " AUTH_SECRET=<any-random-string>"
122
+ fi
123
+ ```
124
+
125
+ ### Step 4: Check database connectivity
126
+
127
+ Before starting the dev server, verify the database is reachable. The app will fail to load pages that query the database if the connection is broken.
128
+
129
+ ```bash
130
+ if [ -f "$APP_PATH/.env" ]; then
131
+ set -a && source "$APP_PATH/.env" && set +a
132
+ fi
133
+ if [ -n "$DATABASE_URL" ]; then
134
+ cd "$APP_PATH" && npx prisma db execute --stdin <<< "SELECT 1" 2>/dev/null
135
+ if [ $? -ne 0 ]; then
136
+ echo "WARNING: Database connection failed. Check DATABASE_URL in $APP_PATH/.env"
137
+ echo "The dev server will start but portal pages may not load correctly."
138
+ fi
139
+ else
140
+ echo "WARNING: DATABASE_URL not set in $APP_PATH/.env"
141
+ fi
142
+ ```
143
+
144
+ This is a non-blocking check. If the database is down, the dev server still starts — it just won't be able to serve authenticated portal pages. Static pages and the example portal (which uses hardcoded data) will still work.
145
+
146
+ ### Step 5: Check for pending migrations
147
+
148
+ ```bash
149
+ cd "$APP_PATH" && npx prisma migrate status 2>&1 | grep -q "pending"
150
+ ```
151
+
152
+ If there are pending migrations, inform the user:
153
+
154
+ > "There are pending database migrations. Run `cd $APP_PATH && npx prisma migrate dev` to apply them, or they will be applied automatically when you next run `/portal deploy`."
155
+
156
+ This is informational only — do not block the dev server start.
157
+
158
+ ### Step 6: Start the dev server
159
+
160
+ Run the dev server in the background so it doesn't block the terminal:
161
+
162
+ ```bash
163
+ cd "$APP_PATH" && npm run dev
164
+ ```
165
+
166
+ Use the Bash tool's `run_in_background` parameter to start it without blocking. The user will see the output when Next.js finishes compiling.
167
+
168
+ If the `npm run dev` script is not defined in package.json, fall back to:
169
+
170
+ ```bash
171
+ cd "$APP_PATH" && npx next dev
172
+ ```
173
+
174
+ The dev server typically takes 3-10 seconds to start depending on the project size and machine speed.
175
+
176
+ ### Step 7: List existing portals
177
+
178
+ After starting, discover which portals exist so the user has useful links:
179
+
180
+ ```bash
181
+ ls -d "$APP_PATH/src/app/(portal)/client"/*/ 2>/dev/null | while read dir; do
182
+ slug=$(basename "$dir")
183
+ echo "$slug"
184
+ done
185
+ ```
186
+
187
+ ### Step 8: Confirm startup and show links
188
+
189
+ Present a clean summary:
190
+
191
+ ```
192
+ Dev server starting at http://localhost:3000
193
+
194
+ Login page: http://localhost:3000/client
195
+ Example portal: http://localhost:3000/client/example
196
+ ```
197
+
198
+ If the user has created portals, also list them:
199
+
200
+ ```
201
+ Your portals:
202
+ - http://localhost:3000/client/acme-health
203
+ - http://localhost:3000/client/beta-corp
204
+ ```
205
+
206
+ Then show tips:
207
+
208
+ ```
209
+ Tips:
210
+ - Create a portal: /portal create <slug>
211
+ - Edit a portal: /portal update <slug>
212
+ - View all portals: /portal list
213
+ - Hot reload is ON: edits to portal files appear instantly
214
+ - Stop the server: Ctrl+C in the terminal, or kill the process
215
+ ```
216
+
217
+ ## Error Handling
218
+
219
+ ### Port conflict resolution
220
+ If port 3000 is occupied by a non-Showpane process:
221
+ - Show the PID and process name
222
+ - Offer two options: kill the process (with explicit permission) or use PORT=3001
223
+ - If using an alternate port, update all the links shown to the user
224
+
225
+ ### npm run dev fails immediately
226
+ Common causes:
227
+ - **Missing .env**: Next.js may require certain env vars at build time. Check for required vars.
228
+ - **Port already in use**: Covered in Step 1 but can race if something starts between the check and the dev server launch.
229
+ - **Node version mismatch**: Showpane requires Node.js 18+. Check with `node --version`.
230
+ - **Corrupted node_modules**: Suggest `rm -rf $APP_PATH/node_modules && cd $APP_PATH && npm install`.
231
+
232
+ ### Database-related page errors
233
+ If the dev server starts but portal pages show errors:
234
+ - Check the terminal output for Prisma errors
235
+ - Verify DATABASE_URL is correct and the database server is running
236
+ - Run `cd $APP_PATH && npx prisma migrate dev` to ensure the schema is up to date
237
+ - Run `cd $APP_PATH && npx prisma generate` to regenerate the Prisma client if the schema changed
238
+ - If using Docker for the database, ensure the database container is running: `docker compose ps`
239
+ - Check that the database user has the correct permissions for the showpane database
240
+
241
+ ## Working with the Dev Server
242
+
243
+ ### Hot reload behavior
244
+
245
+ The Next.js dev server watches all files under `src/` for changes. When you edit a portal's client component file, the browser automatically updates without a full page reload. This means:
246
+
247
+ - Editing tab content: reflected instantly in the browser
248
+ - Adding a new tab: reflected instantly
249
+ - Changing PortalShell props: reflected instantly
250
+ - Adding a new portal directory: requires navigating to the new URL manually (hot reload only works on already-loaded pages)
251
+ - Changing `prisma/schema.prisma`: requires restarting the dev server after running `npx prisma generate`
252
+
253
+ ### Accessing portals during development
254
+
255
+ Portals in development can be accessed in two ways:
256
+
257
+ 1. **Direct URL**: Navigate to `http://localhost:3000/client/<slug>` — this bypasses authentication during local development if the portal doesn't have credentials set yet
258
+ 2. **Login page**: Navigate to `http://localhost:3000/client` — use this to test the full authentication flow with credentials created via `/portal credentials`
259
+
260
+ The example portal at `/client/example` always works without authentication and is a useful reference while building new portals.
261
+
262
+ ### Stopping the dev server
263
+
264
+ The dev server can be stopped in several ways:
265
+ - `Ctrl+C` in the terminal where it's running
266
+ - `kill <pid>` using the PID shown during startup
267
+ - Running `lsof -i :3000 -sTCP:LISTEN -t | xargs kill` to find and kill whatever is on port 3000
268
+
269
+ ### Using a different port
270
+
271
+ If port 3000 is reserved for another project, set the PORT environment variable:
272
+
273
+ ```bash
274
+ cd "$APP_PATH" && PORT=3001 npm run dev
275
+ ```
276
+
277
+ When using a non-standard port, all portal URLs change accordingly (e.g., `http://localhost:3001/client/<slug>`).
278
+
279
+ ## Completion
280
+
281
+ As a final step, log skill completion:
282
+
283
+ ```bash
284
+ echo '{"skill":"portal-dev","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
285
+ ```
286
+
287
+ ## Conventions
288
+
289
+ - Never kill processes on port 3000 without explicit user permission
290
+ - Use the simplified preamble — dev server startup should be fast, not a health check
291
+ - If dependencies are missing, install them automatically (no need to ask)
292
+ - Show useful links after startup (login page, example portal, user's portals)
293
+ - The dev server uses Next.js hot reload — edits to portal files are reflected immediately without restarting
294
+ - If .env is missing, warn but still try to start (the server will show its own error)
295
+ - Keep the output minimal and scannable — the user wants to get to work, not read documentation
296
+ - Database and migration checks are informational, not blocking — let the dev server start regardless
297
+ - This is the most commonly run skill — optimize for speed and minimal friction
298
+ - When the dev server is already running, do not restart it — just confirm it is running and show the links
299
+ - If the user is starting their work session, suggest `/portal dev` followed by `/portal create` or `/portal update` as the natural workflow
300
+ - The dev server process belongs to the user's terminal session — do not try to daemonize it or manage it as a background service
301
+ - If the user closes their terminal, the dev server stops — this is expected behavior for a development workflow
@@ -0,0 +1,253 @@
1
+ ---
2
+ name: portal-list
3
+ description: |
4
+ List all client portals in the organization. Use when asked to "list portals",
5
+ "show portals", "what portals do I have", "my portals", or "all portals". (showpane)
6
+ allowed-tools: [Bash, Read, Glob, Grep]
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-list","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 retrieves all portals belonging to the current organization and displays them as a formatted table in the terminal. It is the quickest way to see what portals exist, their status, and when they were last updated. It is also a good starting point before running other portal-specific skills -- users often run `/portal list` to find a slug before using `/portal analytics`, `/portal share`, or `/portal preview`.
73
+
74
+ ## Steps
75
+
76
+ ### Step 1: Query all portals
77
+
78
+ Run the list script:
79
+
80
+ ```bash
81
+ 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>
82
+ ```
83
+
84
+ Use the `ORG_SLUG` from the preamble output to determine the `org_id`. The script resolves the org slug to an ID internally if needed.
85
+
86
+ Expected success response:
87
+
88
+ ```json
89
+ {
90
+ "ok": true,
91
+ "orgName": "Demo Company",
92
+ "portals": [
93
+ {
94
+ "slug": "whzan",
95
+ "companyName": "Whzan Digital Health",
96
+ "isActive": true,
97
+ "lastUpdated": "2026-04-02T10:00:00Z",
98
+ "hasCredentials": true,
99
+ "tabCount": 4
100
+ },
101
+ {
102
+ "slug": "acme",
103
+ "companyName": "Acme Corp",
104
+ "isActive": true,
105
+ "lastUpdated": "2026-04-05T14:22:00Z",
106
+ "hasCredentials": true,
107
+ "tabCount": 3
108
+ }
109
+ ],
110
+ "total": 2
111
+ }
112
+ ```
113
+
114
+ If the script exits with a non-zero code, read stderr. Common errors:
115
+
116
+ - `org_not_found`: The organization slug from config does not exist in the database. The user may need to re-run `/portal setup`.
117
+ - `database_error`: Connection issue. Check that DATABASE_URL is correct and the database is accessible.
118
+
119
+ ### Step 2: Format as an ASCII table
120
+
121
+ Present the portal list in a clean, scannable format:
122
+
123
+ ```
124
+ PORTALS (Demo Company)
125
+ ════════════════════════════════════════
126
+ Slug Company Status Last Updated
127
+ ─────────── ──────────────────── ───────── ────────────
128
+ whzan Whzan Digital Health Active 2 Apr 2026
129
+ acme Acme Corp Active 5 Apr 2026
130
+ example Example Portal Active 6 Apr 2026
131
+ ════════════════════════════════════════
132
+ 3 portals total
133
+ ```
134
+
135
+ Column definitions:
136
+
137
+ - **Slug**: The URL-safe identifier used in the portal path (`/client/<slug>`).
138
+ - **Company**: The client company name displayed in the portal header.
139
+ - **Status**: "Active" for portals with `isActive: true`. "Inactive" for deactivated portals. Inactive portals should be displayed in the table but noted as such.
140
+ - **Last Updated**: The date the portal content or configuration was last modified. Format as `DD Mon YYYY`.
141
+
142
+ ### Step 3: Handle empty state
143
+
144
+ If the organization has zero portals, do not show an empty table. Instead, display:
145
+
146
+ ```
147
+ PORTALS (Demo Company)
148
+ ════════════════════════════════════════
149
+ No portals yet.
150
+
151
+ Create your first portal: /portal create <slug>
152
+ ════════════════════════════════════════
153
+ ```
154
+
155
+ This guides new users toward the next action. If the user ran `/portal onboard`, they would have gone through this already, but many users will discover the CLI skill-by-skill.
156
+
157
+ ### Step 4: Add contextual hints
158
+
159
+ After the table, provide brief contextual guidance based on what the list reveals:
160
+
161
+ - **All portals active, credentials set**: No additional notes needed. The list is self-explanatory.
162
+ - **Portal without credentials**: Append a note: "Portal 'example' has no credentials. Run /portal credentials example to set up access."
163
+ - **Inactive portals present**: "1 inactive portal not shown. Use --all to include inactive portals." (The `--all` flag can be passed to `list-portals.ts`.)
164
+ - **Large number of portals (10+)**: Consider grouping or paginating. For now, just show all of them -- the terminal handles scrolling.
165
+
166
+ Keep hints to one line each. Do not over-explain.
167
+
168
+ ## Filtering and Sorting
169
+
170
+ The default sort order is by `lastUpdated` descending (most recently updated first). This puts the portals the user is actively working on at the top.
171
+
172
+ If the user asks to filter (e.g., "show only active portals" or "portals updated this week"), apply the filter before formatting. Common filters:
173
+
174
+ - `--active-only` (default): Only show portals with `isActive: true`.
175
+ - `--all`: Include inactive/deactivated portals.
176
+ - `--updated-since <date>`: Only portals updated after the given date.
177
+
178
+ Pass these flags through to the `list-portals.ts` script if the user requests them.
179
+
180
+ ## Conventions
181
+
182
+ - Always show the organization name in the header so the user has context about which org they are viewing.
183
+ - Format dates as `DD Mon YYYY` (e.g., "2 Apr 2026"). Do not show times in the list view -- they add clutter without value at this level.
184
+ - Right-pad string columns for alignment. The table should look clean even with varying slug and company name lengths.
185
+ - Use double-line box drawing (`═`) for outer borders and single-line (`─`) for the header separator.
186
+ - If the response includes `tabCount`, do not show it in the default table. It is available for other skills (like `/portal status`) but too detailed for a list view.
187
+ - The footer line ("3 portals total") should always be present, even for a single portal ("1 portal total").
188
+
189
+ ## Error Handling
190
+
191
+ - If the preamble fails, stop and display the error. Do not attempt to query the database.
192
+ - If the database query fails, show the error from stderr and suggest checking the connection: "Database query failed. Verify your DATABASE_URL is correct and the database is running."
193
+ - If the org is not found, suggest re-running `/portal setup` to reconfigure.
194
+
195
+ ## Data Freshness
196
+
197
+ The list data comes directly from the database at query time. It reflects the current state of all portals, including any changes made moments ago. There is no caching layer. If the user just created a portal with `/portal create`, it will appear in the list immediately.
198
+
199
+ The `lastUpdated` timestamp reflects when the portal's database record was last modified. This includes credential changes, status changes, and metadata updates. It does NOT reflect when the portal's React source files were last edited -- file changes are tracked by git, not the database.
200
+
201
+ ## Multi-Organization Support
202
+
203
+ In v1, Showpane supports a single organization per installation. The `org_id` parameter is always derived from the config. Future versions may support multiple organizations, at which point the list skill would accept an `--org` flag to filter.
204
+
205
+ If the user asks about portals in a different organization, explain: "Showpane is configured for organization '<orgSlug>'. To work with a different org, update your config: edit ~/.showpane/config.json and change the orgSlug value, or set it via environment variable."
206
+
207
+ ## Output as Input
208
+
209
+ The list output is designed to be scannable and to feed into other skills. When a user sees the list and asks to do something with a specific portal (e.g., "show analytics for whzan"), the slug is immediately available from the list.
210
+
211
+ If the user follows up with an action after seeing the list, carry the context forward. Do not ask them to re-specify the slug if they just referenced it from the list output.
212
+
213
+ ## Learnings Integration
214
+
215
+ The list skill does not write learnings. It is a read-only query. However, if learnings indicate that the user has a preferred portal or frequently works with a specific client, you can highlight that portal in the output (e.g., bold the slug or add a note like "recently active").
216
+
217
+ ## Portal Lifecycle States
218
+
219
+ Portals have two states in the database:
220
+
221
+ - **Active** (`isActive: true`): The portal is accessible to clients. It appears in the default list view. This is the normal operating state.
222
+ - **Inactive** (`isActive: false`): The portal has been deactivated via `/portal delete`. It is hidden from the default list view but still exists in the database. Page files may or may not still be in the repository.
223
+
224
+ There is no "draft" or "pending" state. A portal is either active or inactive. When `/portal create` runs, the portal is immediately active. If the user wants to prepare a portal before making it accessible, they should create it and set up credentials last -- the portal page exists but is behind the login gate.
225
+
226
+ ## Response Format Details
227
+
228
+ The `list-portals.ts` script returns JSON with these fields per portal:
229
+
230
+ - `slug` (string): URL identifier, 2-50 characters, lowercase alphanumeric and hyphens.
231
+ - `companyName` (string): Display name of the client company.
232
+ - `isActive` (boolean): Current status.
233
+ - `lastUpdated` (ISO 8601 string): Last modification timestamp.
234
+ - `hasCredentials` (boolean): Whether the portal has a username/password configured.
235
+ - `tabCount` (number): How many tabs the portal has. Not shown in list view but used by status dashboard.
236
+ - `createdAt` (ISO 8601 string): When the portal was first created.
237
+
238
+ All fields are guaranteed to be present. None are nullable.
239
+
240
+ ## Completion
241
+
242
+ As a final step, log skill completion:
243
+
244
+ ```bash
245
+ echo '{"skill":"portal-list","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'"}' >> "$HOME/.showpane/timeline.jsonl" 2>/dev/null
246
+ ```
247
+
248
+ ## Related Skills
249
+
250
+ - `/portal status` -- richer view with health scores and activity data
251
+ - `/portal analytics` -- deep dive into a specific portal's engagement
252
+ - `/portal create` -- create a new portal
253
+ - `/portal delete` -- deactivate a portal from this list