showpane 0.4.9 → 0.4.11

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 (49) hide show
  1. package/bundle/meta/scaffold-manifest.json +17 -24
  2. package/bundle/scaffold/VERSION +1 -1
  3. package/bundle/scaffold/__dot__env.example +3 -5
  4. package/bundle/scaffold/package.json +2 -1
  5. package/bundle/scaffold/prisma/schema.local.prisma +1 -1
  6. package/bundle/scaffold/prisma/seed.ts +6 -1
  7. package/bundle/scaffold/prisma.config.ts +5 -0
  8. package/bundle/scaffold/scripts/prisma-schema.mjs +1 -53
  9. package/bundle/scaffold/src/__tests__/client-portals.test.ts +4 -37
  10. package/bundle/scaffold/src/__tests__/deploy-bundle.test.ts +48 -0
  11. package/bundle/scaffold/src/app/api/client-auth/route.ts +1 -1
  12. package/bundle/scaffold/src/lib/client-portals.ts +8 -13
  13. package/bundle/scaffold/src/lib/deploy-bundle.ts +106 -0
  14. package/bundle/scaffold/src/lib/portal-contracts.ts +33 -0
  15. package/bundle/scaffold/src/lib/runtime-state.ts +2 -32
  16. package/bundle/scaffold/src/types/adm-zip.d.ts +15 -0
  17. package/bundle/toolchain/VERSION +1 -1
  18. package/bundle/toolchain/bin/create-deploy-bundle.ts +3 -72
  19. package/bundle/toolchain/bin/ensure-cloud-project-link.ts +73 -0
  20. package/bundle/toolchain/skills/portal-analytics/SKILL.md +2 -2
  21. package/bundle/toolchain/skills/portal-create/SKILL.md +2 -2
  22. package/bundle/toolchain/skills/portal-credentials/SKILL.md +3 -3
  23. package/bundle/toolchain/skills/portal-delete/SKILL.md +4 -4
  24. package/bundle/toolchain/skills/portal-deploy/SKILL.md +32 -264
  25. package/bundle/toolchain/skills/portal-dev/SKILL.md +15 -13
  26. package/bundle/toolchain/skills/portal-list/SKILL.md +2 -2
  27. package/bundle/toolchain/skills/portal-onboard/SKILL.md +2 -2
  28. package/bundle/toolchain/skills/portal-preview/SKILL.md +9 -23
  29. package/bundle/toolchain/skills/portal-setup/SKILL.md +19 -28
  30. package/bundle/toolchain/skills/portal-share/SKILL.md +3 -4
  31. package/bundle/toolchain/skills/portal-status/SKILL.md +2 -2
  32. package/bundle/toolchain/skills/portal-update/SKILL.md +2 -2
  33. package/bundle/toolchain/skills/portal-upgrade/SKILL.md +2 -2
  34. package/bundle/toolchain/skills/portal-verify/SKILL.md +21 -33
  35. package/bundle/toolchain/skills/shared/bin/check-portal-guard.sh +1 -5
  36. package/bundle/toolchain/skills/shared/preamble.md +2 -2
  37. package/dist/index.js +56 -10
  38. package/package.json +1 -1
  39. package/bundle/scaffold/docker/Caddyfile +0 -3
  40. package/bundle/scaffold/docker/Dockerfile +0 -30
  41. package/bundle/scaffold/docker-compose.yml +0 -53
  42. package/bundle/scaffold/prisma/migrations/20260408000000_init/migration.sql +0 -143
  43. package/bundle/scaffold/prisma/migrations/20260408010000_add_visitor_tracking/migration.sql +0 -6
  44. package/bundle/scaffold/prisma/migrations/20260409040000_add_portal_file_checksum/migration.sql +0 -2
  45. package/bundle/scaffold/prisma/migrations/migration_lock.toml +0 -3
  46. package/bundle/scaffold/prisma/schema.prisma +0 -128
  47. package/bundle/scaffold/scripts/backup.sh +0 -19
  48. package/bundle/scaffold/scripts/e2e-verify.sh +0 -487
  49. package/bundle/scaffold/scripts/restore.sh +0 -31
@@ -28,7 +28,7 @@ echo "APP_PATH: $APP_PATH"
28
28
 
29
29
  # Predictive next-skill suggestion
30
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/,$//')
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/,$//' || true)
32
32
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
33
33
  fi
34
34
 
@@ -100,7 +100,7 @@ if [ ! -d "$APP_PATH/node_modules" ]; then
100
100
  cd "$APP_PATH" && npm install
101
101
  elif [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
102
102
  echo "Generating Prisma client..."
103
- cd "$APP_PATH" && npx prisma generate
103
+ cd "$APP_PATH" && npm run prisma:generate
104
104
  fi
105
105
  ```
106
106
 
@@ -117,7 +117,7 @@ else
117
117
  echo "WARNING: No .env file found at $APP_PATH/.env"
118
118
  echo "The dev server may fail without DATABASE_URL and AUTH_SECRET."
119
119
  echo "Create $APP_PATH/.env with at minimum:"
120
- echo " DATABASE_URL=postgresql://postgres:postgres@localhost:5432/showpane"
120
+ echo " DATABASE_URL=file:./dev.db"
121
121
  echo " AUTH_SECRET=<any-random-string>"
122
122
  fi
123
123
  ```
@@ -143,15 +143,19 @@ fi
143
143
 
144
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
145
 
146
- ### Step 5: Check for pending migrations
146
+ ### Step 5: Check whether the local schema is ready
147
147
 
148
148
  ```bash
149
- cd "$APP_PATH" && npx prisma migrate status 2>&1 | grep -q "pending"
149
+ if [[ "$DATABASE_URL" == file:* ]]; then
150
+ echo "SQLite detected; local schema uses db push."
151
+ else
152
+ echo "Unexpected non-SQLite DATABASE_URL detected. Reset .env to file:./dev.db for the supported local workflow."
153
+ fi
150
154
  ```
151
155
 
152
- If there are pending migrations, inform the user:
156
+ If the SQLite schema has not been applied yet, inform the user:
153
157
 
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`."
158
+ > "The local database schema has not been applied yet. Run `cd $APP_PATH && npm run prisma:db-push` before continuing."
155
159
 
156
160
  This is informational only — do not block the dev server start.
157
161
 
@@ -232,11 +236,9 @@ Common causes:
232
236
  ### Database-related page errors
233
237
  If the dev server starts but portal pages show errors:
234
238
  - 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
239
+ - Verify DATABASE_URL is correct
240
+ - If `DATABASE_URL` starts with `file:`, run `cd $APP_PATH && npm run prisma:db-push`
241
+ - Run `cd $APP_PATH && npm run prisma:generate` to regenerate the Prisma client if the schema changed
240
242
 
241
243
  ## Working with the Dev Server
242
244
 
@@ -248,7 +250,7 @@ The Next.js dev server watches all files under `src/` for changes. When you edit
248
250
  - Adding a new tab: reflected instantly
249
251
  - Changing PortalShell props: reflected instantly
250
252
  - 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`
253
+ - Changing files under `prisma/`: requires restarting the dev server after running `npm run prisma:generate`
252
254
 
253
255
  ### Accessing portals during development
254
256
 
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
17
17
  exit 1
18
18
  fi
19
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)
20
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
21
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
22
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
23
23
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
34
34
 
35
35
  # Predictive next-skill suggestion
36
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/,$//')
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/,$//' || true)
38
38
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
39
39
  fi
40
40
 
@@ -18,7 +18,7 @@ if [ ! -f "$CONFIG" ]; then
18
18
  exit 1
19
19
  fi
20
20
  APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
21
- DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','docker'))" 2>/dev/null)
21
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
22
22
  ORG_SLUG=$(cat "$CONFIG" | python3 -c "import sys,json; d=json.loads(sys.stdin.read()); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
23
23
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
24
24
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -35,7 +35,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
35
35
 
36
36
  # Predictive next-skill suggestion
37
37
  if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
38
- _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
38
+ _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
39
39
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
40
40
  fi
41
41
 
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
17
17
  exit 1
18
18
  fi
19
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)
20
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
21
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
22
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
23
23
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
34
34
 
35
35
  # Predictive next-skill suggestion
36
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/,$//')
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/,$//' || true)
38
38
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
39
39
  fi
40
40
 
@@ -69,7 +69,7 @@ mention them unless they directly affect the current task.
69
69
 
70
70
  ## Overview
71
71
 
72
- This skill opens a portal in the user's default web browser. It is the fastest way to see what a portal looks like after creating or updating it. The skill determines the correct URL based on the deployment mode and whether a local dev server is running, then opens it using the platform's native command.
72
+ This skill opens a portal in the user's default web browser. It is the fastest way to see what a portal looks like after creating or updating it. The skill determines the correct URL based on whether a local dev server is running and whether a public app URL is configured, then opens it using the platform's native command.
73
73
 
74
74
  This is a lightweight skill -- it does not start a dev server, build the app, or modify anything. It just opens a URL. If the dev server is not running, it tells the user how to start it.
75
75
 
@@ -101,27 +101,14 @@ If a process is listening on port 3000, the URL is:
101
101
  http://localhost:3000/client/<slug>
102
102
  ```
103
103
 
104
- **Option B: Docker deploy mode (check port 8080)**
105
-
106
- If `DEPLOY_MODE` is `docker`, check if the container is running:
107
-
108
- ```bash
109
- lsof -i :8080 -sTCP:LISTEN -t >/dev/null 2>&1 && echo "DOCKER_RUNNING=true" || echo "DOCKER_RUNNING=false"
110
- ```
111
-
112
- If running, the URL is:
113
- ```
114
- http://localhost:8080/client/<slug>
115
- ```
116
-
117
- **Option C: NEXT_PUBLIC_APP_URL is set**
104
+ **Option B: NEXT_PUBLIC_APP_URL is set**
118
105
 
119
106
  If the environment variable `NEXT_PUBLIC_APP_URL` is set (sourced from `.env` by the preamble), use it:
120
107
  ```
121
108
  ${NEXT_PUBLIC_APP_URL}/client/<slug>
122
109
  ```
123
110
 
124
- This handles production/staging URLs, custom domains, and any other deployment target.
111
+ This handles deployed URLs, custom domains, and any other public preview target.
125
112
 
126
113
  **Fallback: Nothing running**
127
114
 
@@ -174,9 +161,8 @@ If the portal is the example portal (slug is "example"), no credentials are need
174
161
  To be explicit about resolution order:
175
162
 
176
163
  1. **Dev server on port 3000** -- always preferred for local development, regardless of other settings.
177
- 2. **Docker on port 8080** -- used when deploy_mode is docker and the container is running.
178
- 3. **NEXT_PUBLIC_APP_URL** -- used for production or staging environments, or when no local server is detected.
179
- 4. **Fallback** -- no URL available, prompt the user to start a server.
164
+ 2. **NEXT_PUBLIC_APP_URL** -- used for deployed environments or when no local server is detected.
165
+ 3. **Fallback** -- no URL available, prompt the user to start a server.
180
166
 
181
167
  This order ensures that during development, the user always sees the latest local version, even if NEXT_PUBLIC_APP_URL points to production.
182
168
 
@@ -213,9 +199,9 @@ If the user is previewing to check content before sharing with a client, suggest
213
199
 
214
200
  ## Previewing After Changes
215
201
 
216
- A common workflow is: edit portal content with `/portal update`, then preview to verify. If the dev server is running with Next.js hot reload, changes to the client component files will appear immediately without a page refresh. If the user is running a production build (docker mode), they may need to rebuild first.
202
+ A common workflow is: edit portal content with `/portal update`, then preview to verify. If the dev server is running with Next.js hot reload, changes to the client component files will appear immediately without a page refresh.
217
203
 
218
- If the user just ran `/portal update` and then `/portal preview`, mention: "If you don't see your changes, make sure the dev server is running (hot reload) or rebuild with /portal deploy for docker mode."
204
+ If the user just ran `/portal update` and then `/portal preview`, mention: "If you don't see your changes, make sure the dev server is running so hot reload can pick them up."
219
205
 
220
206
  ## Multiple Portals Preview
221
207
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: portal-setup
3
3
  description: |
4
- Interactive setup wizard for Showpane. Creates config, installs deps, runs migrations, and creates the organization.
4
+ Interactive setup wizard for Showpane. Creates config, installs deps, applies the local SQLite schema, and creates the organization.
5
5
  Trigger phrases: "portal setup", "configure showpane", "set up showpane", "initialize showpane". (showpane)
6
6
  allowed-tools: [Bash, Read, Write, Edit, Glob, Grep]
7
7
  hooks:
@@ -40,7 +40,7 @@ echo "SHOWPANE: v$SKILL_VERSION | SETUP MODE"
40
40
 
41
41
  # Predictive next-skill suggestion
42
42
  if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
43
- _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
43
+ _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)
44
44
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
45
45
  fi
46
46
 
@@ -93,7 +93,7 @@ Run the detection:
93
93
 
94
94
  ```bash
95
95
  for candidate in "$(pwd)" "$(pwd)/../app" "$HOME/git/showpane/app" "$HOME/showpane/app"; do
96
- if [ -f "$candidate/package.json" ] && [ -f "$candidate/prisma/schema.prisma" ]; then
96
+ if [ -f "$candidate/package.json" ] && { [ -f "$candidate/prisma/schema.local.prisma" ] || [ -f "$candidate/prisma.config.ts" ]; }; then
97
97
  echo "FOUND: $(cd "$candidate" && pwd)"
98
98
  break
99
99
  fi
@@ -102,7 +102,7 @@ done
102
102
 
103
103
  If found, confirm with the user: "Found Showpane app at /path/to/app. Use this? (Y/n)"
104
104
 
105
- If not found, ask the user to provide the path. Validate that the path exists and contains both `package.json` and `prisma/schema.prisma`. If either file is missing, the path is not a valid Showpane app directory — explain which file is missing and ask again.
105
+ If not found, ask the user to provide the path. Validate that the path exists and contains `package.json` plus either `prisma/schema.local.prisma` or `prisma.config.ts`. If those markers are missing, the path is not a valid Showpane app directory — explain which file is missing and ask again.
106
106
 
107
107
  Resolve the path to an absolute path (no `~` or relative components) before storing it. Use `cd "$path" && pwd` to resolve.
108
108
 
@@ -117,14 +117,11 @@ When the user is inside a freshly generated Showpane project, the setup should:
117
117
  - Still ask for org name, contact details, and website URL
118
118
  - Be concise — the user just ran `npx showpane` and wants to get going fast
119
119
 
120
- ### Step 3: Ask for deploy mode
120
+ ### Step 3: Set the workspace mode
121
121
 
122
- Present the options:
122
+ Use `local` as the setup mode.
123
123
 
124
- - **docker** Self-hosted with Docker Compose. Best for VPS, dedicated servers, or local development.
125
- - **vercel** — Deployed via Vercel. Pushes to git trigger automatic deploys.
126
-
127
- Default to `docker` if the user doesn't have a preference. Store as `DEPLOY_MODE`.
124
+ Store `DEPLOY_MODE=local` in the config written later in this flow.
128
125
 
129
126
  ### Step 4: Install dependencies
130
127
 
@@ -137,12 +134,12 @@ cd "$APP_PATH" && npm install
137
134
  If node_modules exists but `.prisma` client is missing, run:
138
135
 
139
136
  ```bash
140
- cd "$APP_PATH" && npx prisma generate
137
+ cd "$APP_PATH" && npm run prisma:generate
141
138
  ```
142
139
 
143
140
  Wait for installation to complete before proceeding.
144
141
 
145
- ### Step 5: Run database migrations
142
+ ### Step 5: Apply the local database schema
146
143
 
147
144
  Source the app's `.env` file to get `DATABASE_URL`:
148
145
 
@@ -150,24 +147,18 @@ Source the app's `.env` file to get `DATABASE_URL`:
150
147
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
151
148
  ```
152
149
 
153
- Check if the database has existing tables. For a fresh database:
154
-
155
- ```bash
156
- cd "$APP_PATH" && npx prisma migrate dev
157
- ```
158
-
159
- For an existing database with pending migrations:
150
+ The supported setup path is SQLite. Apply the local schema:
160
151
 
161
152
  ```bash
162
- cd "$APP_PATH" && npx prisma migrate deploy
153
+ cd "$APP_PATH" && npm run prisma:db-push
163
154
  ```
164
155
 
165
156
  If `DATABASE_URL` is not set in `.env`, inform the user they need to configure it. Provide guidance:
166
- - For local development: `postgresql://postgres:postgres@localhost:5432/showpane`
167
- - For Docker: the docker-compose.yml should provide it
168
- - For Vercel: set it in the Vercel dashboard environment variables
157
+ - For local development: `file:./dev.db`
158
+
159
+ If `DATABASE_URL` is set to anything other than a `file:` URL, explain that the supported setup flow is SQLite-first and suggest resetting `.env` back to `file:./dev.db`.
169
160
 
170
- Do NOT proceed until migrations complete successfully.
161
+ Do NOT proceed until the local schema is applied successfully.
171
162
 
172
163
  ### Step 6: Create or find Organization record
173
164
 
@@ -239,7 +230,7 @@ Write the config file:
239
230
  cat > "$HOME/.showpane/config.json" << 'CONFIGEOF'
240
231
  {
241
232
  "app_path": "<resolved_absolute_path>",
242
- "deploy_mode": "<docker|vercel>",
233
+ "deploy_mode": "local",
243
234
  "orgSlug": "<org_slug>",
244
235
  "telemetry": "<community|anonymous|off>"
245
236
  }
@@ -257,7 +248,7 @@ Display a clear summary:
257
248
  Showpane setup complete!
258
249
 
259
250
  App path: /path/to/showpane-project
260
- Deploy mode: docker
251
+ Deploy mode: local
261
252
  Organization: Acme Consulting (acme-consulting)
262
253
  Brand color: #2563eb
263
254
  Telemetry: off
@@ -283,7 +274,7 @@ Each step can fail independently. Handle failures gracefully:
283
274
 
284
275
  - **App path not found**: Ask the user to run `npx showpane` first or point setup at an existing generated Showpane project
285
276
  - **npm install fails**: Check Node.js version (requires 18+), check internet connectivity, suggest clearing `node_modules` and retrying
286
- - **Prisma migrate fails**: Check DATABASE_URL is correct and the database server is running. For local dev, suggest `docker compose up -d db` if a database service exists in docker-compose.yml
277
+ - **Prisma db push fails**: Check that DATABASE_URL is set to `file:./dev.db` (or another valid SQLite `file:` URL) in `.env`
287
278
  - **Organization creation fails**: Check database connectivity. If the Prisma client throws a connection error, verify DATABASE_URL in `.env`
288
279
  - **Config write fails**: Check that `$HOME/.showpane/` is writable. On some systems, home directory permissions may block directory creation
289
280
 
@@ -306,4 +297,4 @@ echo '{"skill":"portal-setup","event":"completed","ts":"'$(date -u +%Y-%m-%dT%H:
306
297
  - If any step fails, provide a clear error message and suggest how to fix it, but do not silently continue
307
298
  - The setup wizard is interactive — ask one question at a time, don't dump all questions at once
308
299
  - When reconfiguring, show current values as defaults so the user can press enter to keep them
309
- - The setup wizard should complete in under 5 minutes for a user with all prerequisites (Node.js, Postgres, the repo cloned)
300
+ - The setup wizard should complete in under 5 minutes for a user with Node.js, internet access, and a generated Showpane project
@@ -20,7 +20,7 @@ if [ ! -f "$CONFIG" ]; then
20
20
  exit 1
21
21
  fi
22
22
  APP_PATH=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('app_path',''))" 2>/dev/null)
23
- DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','docker'))" 2>/dev/null)
23
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
24
24
  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)
25
25
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
26
26
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -37,7 +37,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
37
37
 
38
38
  # Predictive next-skill suggestion
39
39
  if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
40
- _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
40
+ _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//' || true)
41
41
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
42
42
  fi
43
43
 
@@ -147,8 +147,7 @@ Do NOT log the share URL to learnings or telemetry. The URL contains the signed
147
147
  - Always display the full URL, never truncate or abbreviate it. The user needs to copy-paste it.
148
148
  - Make it clear that the link does not expire automatically and is revoked by credential rotation or portal deactivation.
149
149
  - Use double-line box drawing (`═`) for the border around the link.
150
- - If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user: "This is a local development URL. The client won't be able to access it remotely. Deploy the app first with /portal deploy, then generate the share link."
151
- - If the deploy mode is `docker` and the URL looks like localhost, same warning applies.
150
+ - If NEXT_PUBLIC_APP_URL is `http://localhost:3000`, warn the user: "This is a local development URL. The client won't be able to access it remotely. Deploy the app with /portal deploy, then generate the share link again."
152
151
 
153
152
  ## Error Handling
154
153
 
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
17
17
  exit 1
18
18
  fi
19
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)
20
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
21
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
22
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
23
23
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
34
34
 
35
35
  # Predictive next-skill suggestion
36
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/,$//')
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/,$//' || true)
38
38
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
39
39
  fi
40
40
 
@@ -16,7 +16,7 @@ if [ ! -f "$CONFIG" ]; then
16
16
  exit 1
17
17
  fi
18
18
  APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
19
- DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','docker'))" 2>/dev/null)
19
+ DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null)
20
20
  ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
21
21
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
22
22
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -33,7 +33,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
33
33
 
34
34
  # Predictive next-skill suggestion
35
35
  if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
36
- _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
36
+ _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)
37
37
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
38
38
  fi
39
39
 
@@ -17,7 +17,7 @@ if [ ! -f "$CONFIG" ]; then
17
17
  exit 1
18
18
  fi
19
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)
20
+ DEPLOY_MODE=$(cat "$CONFIG" | python3 -c "import sys,json; print(json.loads(sys.stdin.read()).get('deploy_mode','local'))" 2>/dev/null)
21
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
22
  APP_PATH="${SHOWPANE_APP_PATH:-$APP_PATH}"
23
23
  if [ -f "$APP_PATH/.env" ]; then set -a && source "$APP_PATH/.env" && set +a; fi
@@ -34,7 +34,7 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
34
34
 
35
35
  # Predictive next-skill suggestion
36
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/,$//')
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/,$//' || true)
38
38
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
39
39
  fi
40
40
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: portal-verify
3
3
  description: |
4
- Verify deployed portal health: DNS, SSL, login page, content rendering, engagement tracking.
4
+ Verify a Showpane Cloud portal after publish: DNS, SSL, login page, content rendering, engagement tracking.
5
5
  Trigger phrases: "verify portal", "check deployment", "is my portal working", "portal health". (showpane)
6
6
  allowed-tools: [Bash, Read, Glob, Grep]
7
7
  ---
@@ -16,7 +16,7 @@ if [ ! -f "$CONFIG" ]; then
16
16
  exit 1
17
17
  fi
18
18
  APP_PATH=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('app_path',''))" 2>/dev/null)
19
- DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','docker'))" 2>/dev/null)
19
+ DEPLOY_MODE=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('deploy_mode','local'))" 2>/dev/null)
20
20
  ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('orgSlug','') or d.get('org_slug',''))" 2>/dev/null)
21
21
  CLOUD_API_TOKEN=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('cloud',d).get('api_token', d.get('accessToken','')))" 2>/dev/null)
22
22
  CLOUD_ORG_SLUG=$(python3 -c "import json; d=json.load(open('$CONFIG')); print(d.get('cloud',d).get('org_slug', d.get('orgSlug','')))" 2>/dev/null)
@@ -41,38 +41,26 @@ LEARN_FILE="$HOME/.showpane/learnings.jsonl"
41
41
 
42
42
  ### Step 1: URL Reachability
43
43
 
44
- Determine the portal URL based on deploy mode and check that it responds.
44
+ Determine the portal URL and check that it responds.
45
45
 
46
- **For self-hosted (docker/vercel):**
47
46
  ```bash
48
- PORTAL_URL="${CLOUD_PORTAL_URL:-http://localhost:3000}"
49
- echo "Checking portal URL: $PORTAL_URL"
50
- HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
51
- echo "HTTP status: $HTTP_CODE"
52
- if [ "$HTTP_CODE" = "200" ]; then
53
- URL_STATUS="ok"
54
- else
55
- URL_STATUS="unreachable ($HTTP_CODE)"
47
+ if [ -z "$CLOUD_PORTAL_URL" ] && [ -z "$CLOUD_ORG_SLUG" ]; then
48
+ echo "No hosted portal URL configured. Run /portal deploy first."
49
+ exit 1
56
50
  fi
57
- ```
51
+ PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
52
+ echo "Checking app URL: $CLOUD_API_BASE"
53
+ APP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CLOUD_API_BASE/api/health" 2>/dev/null)
54
+ echo "App health: $APP_CODE"
58
55
 
59
- **For cloud deploys:**
60
- ```bash
61
- if [ "$DEPLOY_MODE" = "cloud" ]; then
62
- PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
63
- echo "Checking app URL: $CLOUD_API_BASE"
64
- APP_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$CLOUD_API_BASE/api/health" 2>/dev/null)
65
- echo "App health: $APP_CODE"
66
-
67
- echo "Checking org URL: $PORTAL_URL"
68
- ORG_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
69
- echo "Org portal: $ORG_CODE"
70
-
71
- if [ "$ORG_CODE" = "200" ]; then
72
- URL_STATUS="ok"
73
- else
74
- URL_STATUS="unreachable ($ORG_CODE)"
75
- fi
56
+ echo "Checking org URL: $PORTAL_URL"
57
+ ORG_CODE=$(curl -s -o /dev/null -w "%{http_code}" "$PORTAL_URL" 2>/dev/null)
58
+ echo "Org portal: $ORG_CODE"
59
+
60
+ if [ "$ORG_CODE" = "200" ]; then
61
+ URL_STATUS="ok"
62
+ else
63
+ URL_STATUS="unreachable ($ORG_CODE)"
76
64
  fi
77
65
  ```
78
66
 
@@ -110,7 +98,7 @@ if [ "$DEPLOY_MODE" = "cloud" ] && [ -n "$CLOUD_ORG_SLUG" ]; then
110
98
  echo "HTTP->HTTPS redirect: $REDIRECT_CODE (expected 301 or 308)"
111
99
  fi
112
100
  else
113
- SSL_STATUS="n/a (self-hosted)"
101
+ SSL_STATUS="n/a (no cloud org configured)"
114
102
  fi
115
103
  ```
116
104
 
@@ -132,7 +120,7 @@ echo "Active portals: $PORTAL_LIST"
132
120
  For each portal, verify the login page returns a response and contains auth-related content:
133
121
 
134
122
  ```bash
135
- PORTAL_URL="${CLOUD_PORTAL_URL:-http://localhost:3000}"
123
+ PORTAL_URL="${CLOUD_PORTAL_URL:-https://$CLOUD_ORG_SLUG.showpane.com}"
136
124
  echo "$PORTAL_LIST" | python3 -c "
137
125
  import sys, json
138
126
  portals = json.load(sys.stdin)
@@ -261,5 +249,5 @@ After the report, suggest next steps:
261
249
  - Cloud checks verify both the control plane (app.showpane.com) and the org portal (orgslug.showpane.com)
262
250
  - The PortalFile model may not exist in all schema versions — catch errors gracefully
263
251
  - If the database is unreachable, skip DB-dependent checks (Steps 3, 5) and note it in the report
264
- - SSL checks only apply to cloud deploys self-hosted SSL is the user's responsibility
252
+ - SSL checks only apply when a cloud portal URL is configured
265
253
  - The events endpoint check uses OPTIONS to avoid creating spurious event records
@@ -24,10 +24,6 @@ if [ -n "$command" ]; then
24
24
  echo '{"permissionDecision":"ask","message":"⚠️ This SQL command will destroy portal or organization data."}'
25
25
  exit 0
26
26
  ;;
27
- *"vercel"*"rm"*|*"vercel"*"remove"*|*"vercel"*"delete"*)
28
- echo '{"permissionDecision":"ask","message":"⚠️ This will delete a Vercel project. Published portals will go offline."}'
29
- exit 0
30
- ;;
31
27
  esac
32
28
  fi
33
29
 
@@ -38,7 +34,7 @@ if [ -n "$file_path" ]; then
38
34
  echo '{"permissionDecision":"ask","message":"⚠️ Modifying credentials/secrets file. Verify this is intentional."}'
39
35
  exit 0
40
36
  ;;
41
- *"prisma/schema.prisma"*)
37
+ *"prisma/schema.local.prisma"*|*"prisma.config.ts"*)
42
38
  echo '{"permissionDecision":"ask","message":"⚠️ Modifying the database schema. This may require a migration."}'
43
39
  exit 0
44
40
  ;;
@@ -32,7 +32,7 @@ export DATABASE_URL="${DATABASE_URL:-}"
32
32
 
33
33
  # 3. Verify app installed
34
34
  if [ ! -d "$APP_PATH/node_modules/.prisma" ]; then
35
- echo "ERROR: Prisma client not generated. Run: cd $APP_PATH && npm install && npx prisma generate" >&2
35
+ echo "ERROR: Prisma client not generated. Run: cd $APP_PATH && npm install && npm run prisma:generate" >&2
36
36
  exit 1
37
37
  fi
38
38
 
@@ -57,7 +57,7 @@ fi
57
57
 
58
58
  # 5b. Predictive next-skill suggestion
59
59
  if [ -f "$HOME/.showpane/timeline.jsonl" ]; then
60
- _RECENT=$(grep '"event":"completed"' "$HOME/.showpane/timeline.jsonl" 2>/dev/null | tail -3 | grep -o '"skill":"[^"]*"' | sed 's/"skill":"//;s/"//' | tr '\n' ',' | sed 's/,$//')
60
+ _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)
61
61
  [ -n "$_RECENT" ] && echo "RECENT_SKILLS: $_RECENT"
62
62
  fi
63
63