showpane 0.4.1 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -1
- package/bundle/meta/scaffold-manifest.json +73 -0
- package/bundle/scaffold/VERSION +1 -0
- package/bundle/scaffold/__dot__env.example +24 -0
- package/bundle/scaffold/__dot__gitignore +41 -0
- package/bundle/scaffold/docker/Caddyfile +3 -0
- package/bundle/scaffold/docker/Dockerfile +30 -0
- package/bundle/scaffold/docker-compose.yml +53 -0
- package/bundle/scaffold/next.config.ts +20 -0
- package/bundle/scaffold/package-lock.json +5843 -0
- package/bundle/scaffold/package.json +42 -0
- package/bundle/scaffold/postcss.config.js +6 -0
- package/bundle/scaffold/prisma/migrations/20260408000000_init/migration.sql +143 -0
- package/bundle/scaffold/prisma/migrations/20260408010000_add_visitor_tracking/migration.sql +6 -0
- package/bundle/scaffold/prisma/migrations/20260409040000_add_portal_file_checksum/migration.sql +2 -0
- package/bundle/scaffold/prisma/migrations/migration_lock.toml +3 -0
- package/bundle/scaffold/prisma/schema.local.prisma +131 -0
- package/bundle/scaffold/prisma/schema.prisma +128 -0
- package/bundle/scaffold/prisma/seed.ts +49 -0
- package/bundle/scaffold/public/example-avatar.svg +4 -0
- package/bundle/scaffold/public/example-logo.svg +4 -0
- package/bundle/scaffold/public/robots.txt +2 -0
- package/bundle/scaffold/scripts/backup.sh +19 -0
- package/bundle/scaffold/scripts/e2e-verify.sh +487 -0
- package/bundle/scaffold/scripts/prisma-db-push.mjs +7 -0
- package/bundle/scaffold/scripts/prisma-generate.mjs +3 -0
- package/bundle/scaffold/scripts/prisma-schema.mjs +74 -0
- package/bundle/scaffold/scripts/restore.sh +31 -0
- package/bundle/scaffold/src/__tests__/client-portals.test.ts +80 -0
- package/bundle/scaffold/src/__tests__/portal-contracts.test.ts +32 -0
- package/bundle/scaffold/src/app/(portal)/client/[slug]/page.tsx +79 -0
- package/bundle/scaffold/src/app/(portal)/client/[slug]/s/[token]/route.ts +22 -0
- package/bundle/scaffold/src/app/(portal)/client/example/example-client.tsx +372 -0
- package/bundle/scaffold/src/app/(portal)/client/example/page.tsx +5 -0
- package/bundle/scaffold/src/app/(portal)/client/layout.tsx +7 -0
- package/bundle/scaffold/src/app/(portal)/client/page.tsx +18 -0
- package/bundle/scaffold/src/app/api/client-auth/route.ts +82 -0
- package/bundle/scaffold/src/app/api/client-auth/share/route.ts +30 -0
- package/bundle/scaffold/src/app/api/client-events/route.ts +87 -0
- package/bundle/scaffold/src/app/api/client-files/[...path]/route.ts +80 -0
- package/bundle/scaffold/src/app/api/client-files/client-upload/route.ts +118 -0
- package/bundle/scaffold/src/app/api/client-files/route.ts +37 -0
- package/bundle/scaffold/src/app/api/client-files/upload/route.ts +131 -0
- package/bundle/scaffold/src/app/api/health/route.ts +19 -0
- package/bundle/scaffold/src/app/globals.css +7 -0
- package/bundle/scaffold/src/app/layout.tsx +25 -0
- package/bundle/scaffold/src/app/page.tsx +171 -0
- package/bundle/scaffold/src/components/portal-login.tsx +169 -0
- package/bundle/scaffold/src/components/portal-shell.tsx +373 -0
- package/bundle/scaffold/src/lib/abuse-controls.ts +43 -0
- package/bundle/scaffold/src/lib/branding.ts +50 -0
- package/bundle/scaffold/src/lib/client-auth.ts +98 -0
- package/bundle/scaffold/src/lib/client-portals.ts +134 -0
- package/bundle/scaffold/src/lib/control-plane.ts +100 -0
- package/bundle/scaffold/src/lib/db.ts +7 -0
- package/bundle/scaffold/src/lib/files.ts +124 -0
- package/bundle/scaffold/src/lib/load-app-env.ts +42 -0
- package/bundle/scaffold/src/lib/portal-contracts.ts +69 -0
- package/bundle/scaffold/src/lib/prisma-client.ts +5 -0
- package/bundle/scaffold/src/lib/runtime-state.ts +69 -0
- package/bundle/scaffold/src/lib/storage.ts +204 -0
- package/bundle/scaffold/src/lib/token.ts +186 -0
- package/bundle/scaffold/src/lib/utils.ts +6 -0
- package/bundle/scaffold/src/middleware.ts +61 -0
- package/bundle/scaffold/tailwind.config.ts +15 -0
- package/bundle/scaffold/tests/__dot__gitkeep +0 -0
- package/bundle/scaffold/tsconfig.json +23 -0
- package/bundle/scaffold/vitest.config.ts +13 -0
- package/bundle/toolchain/VERSION +1 -0
- package/bundle/toolchain/bin/check-slug.ts +59 -0
- package/bundle/toolchain/bin/create-deploy-bundle.ts +93 -0
- package/bundle/toolchain/bin/create-portal.ts +71 -0
- package/bundle/toolchain/bin/delete-portal.ts +48 -0
- package/bundle/toolchain/bin/export-file-manifest.ts +84 -0
- package/bundle/toolchain/bin/export-runtime-state.ts +90 -0
- package/bundle/toolchain/bin/generate-share-link.ts +68 -0
- package/bundle/toolchain/bin/list-portals.ts +53 -0
- package/bundle/toolchain/bin/materialize-file.ts +35 -0
- package/bundle/toolchain/bin/query-analytics.ts +88 -0
- package/bundle/toolchain/bin/rotate-credentials.ts +57 -0
- package/bundle/toolchain/bin/showpane-config +63 -0
- package/bundle/toolchain/bin/tsconfig.json +13 -0
- package/bundle/toolchain/skills/VERSION +1 -0
- package/bundle/toolchain/skills/portal-analytics/SKILL.md +263 -0
- package/bundle/toolchain/skills/portal-create/SKILL.md +341 -0
- package/bundle/toolchain/skills/portal-credentials/SKILL.md +274 -0
- package/bundle/toolchain/skills/portal-delete/SKILL.md +265 -0
- package/bundle/toolchain/skills/portal-deploy/SKILL.md +721 -0
- package/bundle/toolchain/skills/portal-dev/SKILL.md +301 -0
- package/bundle/toolchain/skills/portal-list/SKILL.md +253 -0
- package/bundle/toolchain/skills/portal-onboard/SKILL.md +277 -0
- package/bundle/toolchain/skills/portal-preview/SKILL.md +257 -0
- package/bundle/toolchain/skills/portal-setup/SKILL.md +309 -0
- package/bundle/toolchain/skills/portal-share/SKILL.md +234 -0
- package/bundle/toolchain/skills/portal-status/SKILL.md +268 -0
- package/bundle/toolchain/skills/portal-update/SKILL.md +348 -0
- package/bundle/toolchain/skills/portal-upgrade/SKILL.md +235 -0
- package/bundle/toolchain/skills/portal-verify/SKILL.md +265 -0
- package/bundle/toolchain/skills/shared/bin/check-portal-guard.sh +49 -0
- package/bundle/toolchain/skills/shared/platform-constraints.md +33 -0
- package/bundle/toolchain/skills/shared/preamble.md +137 -0
- package/bundle/toolchain/templates/consulting/consulting-client.tsx +205 -0
- package/bundle/toolchain/templates/onboarding/onboarding-client.tsx +237 -0
- package/bundle/toolchain/templates/sales-followup/sales-followup-client.tsx +283 -0
- package/dist/index.js +873 -166
- package/package.json +3 -2
|
@@ -0,0 +1,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
|