@oaklandzoo/ostup 0.3.0 → 0.5.0

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 (33) hide show
  1. package/README.md +23 -0
  2. package/bin/cli.mjs +27 -1
  3. package/package.json +1 -1
  4. package/src/brief/classify.mjs +218 -0
  5. package/src/brief/index.mjs +126 -0
  6. package/src/brief/profile-router.mjs +89 -0
  7. package/src/brief/questions.mjs +333 -0
  8. package/src/brief/render-brief.mjs +232 -0
  9. package/src/brief/sample-briefs.mjs +304 -0
  10. package/src/brief/schema.mjs +176 -0
  11. package/src/mvp-flow.mjs +43 -1
  12. package/src/templates.mjs +4 -0
  13. package/templates/.claude/commands/break-into-stories.md +101 -0
  14. package/templates/.claude/commands/handoff-doctor.md +93 -0
  15. package/templates/.claude/commands/resume.md +102 -0
  16. package/templates/AGENTS.md +13 -2
  17. package/templates/CLAUDE.md +3 -0
  18. package/templates/START_HERE.md +11 -4
  19. package/templates/profiles/blog/.env.example.additions +7 -0
  20. package/templates/profiles/blog/README.md +70 -0
  21. package/templates/profiles/blog/section-prompts.md +63 -0
  22. package/templates/profiles/booking/.env.example.additions +16 -0
  23. package/templates/profiles/booking/README.md +61 -0
  24. package/templates/profiles/booking/section-prompts.md +47 -0
  25. package/templates/profiles/lead-gen/.env.example.additions +8 -0
  26. package/templates/profiles/lead-gen/README.md +58 -0
  27. package/templates/profiles/lead-gen/section-prompts.md +47 -0
  28. package/templates/profiles/marketing/README.md +39 -0
  29. package/templates/profiles/marketing/section-prompts.md +36 -0
  30. package/templates/profiles/saas-dashboard/.env.example.additions +21 -0
  31. package/templates/profiles/saas-dashboard/README.md +60 -0
  32. package/templates/profiles/saas-dashboard/section-prompts.md +52 -0
  33. package/templates/scripts/verify.sh +128 -0
@@ -0,0 +1,39 @@
1
+ # Profile: marketing
2
+
3
+ > Single-page or short marketing site. Default for projects with weak signals.
4
+
5
+ ## Day-one scope
6
+
7
+ | Section | Purpose | Required |
8
+ |---|---|---|
9
+ | Hero | Single primary value statement + one CTA (email or "Get started") | yes |
10
+ | Features | 3-6 short benefit cards | yes |
11
+ | Social proof / Testimonials | Quote, logo strip, or "in use at" line | optional |
12
+ | Pricing | One or two tiers, simple | optional |
13
+ | Email signup | Inline form posting to /api/subscribe (no DB; logs or forwards) | optional |
14
+ | Footer | Links, year, attribution | yes |
15
+
16
+ ## Wired infrastructure
17
+
18
+ None by default. Pure static.
19
+
20
+ If the brief includes `email` add-on (newsletter signup), the agent should wire Resend or a generic webhook for the form submission.
21
+
22
+ ## What ships in this overlay
23
+
24
+ - `section-prompts.md` — concrete guidance for each section.
25
+ - Anything in `.additions` is appended to the matching scaffolded file.
26
+
27
+ ## Hard rules
28
+
29
+ - Mobile-first. Test at 420x900 with `scripts/screenshot.sh`.
30
+ - No third-party tracking. No popups. No chat widget.
31
+ - One CTA per page. If a section has its own CTA, route it to the same destination as the hero CTA.
32
+ - All copy comes from `docs/brief.md`. The agent paraphrases for the page, never invents new claims.
33
+
34
+ ## Acceptance
35
+
36
+ - Hero readable at 420x900.
37
+ - Single primary CTA visible above the fold.
38
+ - Lighthouse mobile Performance >= 90, Accessibility >= 95.
39
+ - No console errors.
@@ -0,0 +1,36 @@
1
+ # Marketing profile section prompts
2
+
3
+ Agent: build each section using the brief in `docs/brief.md`. Do not invent claims.
4
+
5
+ ## Hero
6
+
7
+ - 1 headline (5-9 words) drawn from `project.summary`.
8
+ - 1 subhead (one sentence) describing who it is for and what changes.
9
+ - 1 primary CTA button. Text is imperative, e.g. "Get the early access link" or "Start free."
10
+ - Optional: small visual (an SVG mark from `inputs/images/`, NOT a generic stock photo).
11
+
12
+ ## Features (3-6 cards)
13
+
14
+ - Each card has: short title (3-5 words) + one-sentence description.
15
+ - Pull titles from `scope.must_have_sections` (skip "hero" and "footer").
16
+ - Description echoes the brief vibe in `brand.vibe`.
17
+ - Use a grid; on mobile stack 1 column.
18
+
19
+ ## Pricing (if included)
20
+
21
+ - 1-3 tiers. Each tier: name, price (or "free"), 3-5 bullets, single CTA.
22
+ - Use the pricing notes in `business_model.pricing_notes`.
23
+ - If no pricing info in brief, omit the section entirely. Do not invent prices.
24
+
25
+ ## Email signup
26
+
27
+ - One field (email) + one button.
28
+ - Submit to `/api/subscribe` (or `/api/contact`).
29
+ - Inline success message on submit: "You're on the list."
30
+ - No double opt-in flow v1.
31
+
32
+ ## Footer
33
+
34
+ - Copyright year (current).
35
+ - 2-4 links max: GitHub, npm, docs, contact.
36
+ - No social icons unless the brief says social presence matters.
@@ -0,0 +1,21 @@
1
+ # --- saas-dashboard profile additions ---
2
+ # Auth (Better Auth)
3
+ BETTER_AUTH_SECRET=
4
+ BETTER_AUTH_URL=http://localhost:3000
5
+
6
+ # Database (Neon recommended for Postgres)
7
+ DATABASE_URL=
8
+
9
+ # Stripe subscriptions
10
+ STRIPE_SECRET_KEY=
11
+ STRIPE_PUBLISHABLE_KEY=
12
+ STRIPE_WEBHOOK_SECRET=
13
+ STRIPE_PRICE_ID_SOLO=
14
+ STRIPE_PRICE_ID_TEAM=
15
+
16
+ # Public app URL
17
+ NEXT_PUBLIC_APP_URL=http://localhost:3000
18
+
19
+ # Email (for verification + password reset)
20
+ RESEND_API_KEY=
21
+ AUTH_FROM_EMAIL=noreply@example.com
@@ -0,0 +1,60 @@
1
+ # Profile: saas-dashboard
2
+
3
+ > SaaS MVP with auth, subscription billing skeleton, dashboard shell, settings. Pick the workflow over feature depth.
4
+
5
+ ## Day-one scope
6
+
7
+ | Section | Purpose | Required |
8
+ |---|---|---|
9
+ | Landing hero | Convert visitor to signup | yes |
10
+ | Pricing | 1-3 tiers, monthly + annual toggle | yes |
11
+ | Sign up / Log in | Better Auth flow (email+password or magic link) | yes |
12
+ | Dashboard shell | Authenticated route, empty state with onboarding hint | yes |
13
+ | Settings | Profile + billing pages | yes |
14
+ | Billing | Stripe Customer Portal link OR a stub for "manage subscription" | yes |
15
+ | Footer | Links, year, GitHub, status page link if any | yes |
16
+
17
+ ## Wired infrastructure
18
+
19
+ - **Better Auth** for authentication. Email+password v1; magic link v2.
20
+ - **Postgres** for `User` + any product entities from the brief.
21
+ - **Stripe** for subscriptions. Use Stripe Checkout for initial signup, Stripe Customer Portal for management.
22
+ - **Middleware** at the route level: unauthenticated visitors to `/dashboard/*` redirect to `/login`.
23
+
24
+ ## Env additions
25
+
26
+ See `.env.example.additions`.
27
+
28
+ ## API contracts
29
+
30
+ ```
31
+ POST /api/auth/signup (Better Auth handles)
32
+ POST /api/auth/login (Better Auth handles)
33
+ POST /api/auth/logout (Better Auth handles)
34
+ GET /api/auth/session (Better Auth handles; returns user or 401)
35
+
36
+ POST /api/billing/checkout Body: { plan: 'solo' | 'team' }
37
+ Response: { url } (Stripe Checkout URL)
38
+
39
+ GET /api/billing/portal Response: { url } (Stripe Customer Portal URL)
40
+
41
+ POST /api/webhooks/stripe Handles subscription.created, .updated, .deleted
42
+ Updates User.plan
43
+ ```
44
+
45
+ ## Hard rules
46
+
47
+ - Auth gate runs at middleware level. Never serve dashboard UI to unauthenticated requests, even briefly.
48
+ - Session cookies are httpOnly, secure in production, sameSite=lax.
49
+ - Stripe webhook signature verification is mandatory. Reject on bad sig.
50
+ - Empty dashboard state has a clear "Get started" path. No dead-end empty UI.
51
+ - All errors during auth flow are user-facing (not stack traces).
52
+ - Settings → Billing links to Stripe Customer Portal, not a custom billing UI v1.
53
+
54
+ ## Acceptance
55
+
56
+ - New user can sign up, see empty dashboard, log out, log back in.
57
+ - Stripe Checkout can be initiated; webhook updates user plan.
58
+ - Visiting `/dashboard` unauthenticated redirects to `/login`.
59
+ - Visiting `/login` while logged in redirects to `/dashboard`.
60
+ - Lighthouse landing-page Performance >= 90; dashboard >= 80 (slightly lower acceptable due to auth code).
@@ -0,0 +1,52 @@
1
+ # SaaS dashboard profile section prompts
2
+
3
+ Agent: build each section using the brief. Default auth library: Better Auth. Default DB: Postgres (Neon if no preference).
4
+
5
+ ## Landing hero (unauthenticated)
6
+
7
+ - Single big claim: the change the product delivers (echo `project.summary`).
8
+ - One CTA: "Start free" or "Get started" → routes to `/signup`.
9
+ - Optional logo strip or "Used by" line if the brief mentions customers.
10
+
11
+ ## Pricing
12
+
13
+ - 1-3 tiers from `business_model.pricing_notes`. If 1 tier: show it plus "Custom for teams >".
14
+ - Monthly + annual toggle. Annual = 2 months free (standard pattern).
15
+ - Each tier: name, price, 4-6 bullets, single CTA "Start free" or "Upgrade" depending on auth state.
16
+
17
+ ## Sign up / Log in
18
+
19
+ - Two-tab UI on `/auth` OR separate `/signup` and `/login` routes.
20
+ - Email + password v1.
21
+ - Clear error states: "Email already in use", "Wrong password", "Too many attempts" (rate-limit).
22
+ - After signup: email verification flow (Resend).
23
+ - After login: redirect to `/dashboard`.
24
+
25
+ ## Dashboard shell
26
+
27
+ - Sidebar nav (collapsible on mobile): Home, Settings, Billing, Logout.
28
+ - Header bar: user email + dropdown.
29
+ - Empty state: "Welcome to {{DISPLAY_NAME}}. Start by [first action from brief]."
30
+ - One primary action button in the empty state.
31
+
32
+ ## Settings
33
+
34
+ - Two sub-pages: Profile, Billing.
35
+ - Profile: name, email (verified), change password.
36
+ - Billing: link to Stripe Customer Portal via `/api/billing/portal`. Show current plan + next renewal date.
37
+
38
+ ## Billing
39
+
40
+ - Stripe Checkout via `/api/billing/checkout`. Redirect on success to `/dashboard?welcome=1`.
41
+ - Stripe Customer Portal for management.
42
+ - Webhook updates `User.plan` field.
43
+
44
+ ## Footer
45
+
46
+ - Privacy, Terms, Status, GitHub.
47
+ - Year, copyright.
48
+ - No SSO / SAML claims v1 unless brief says so explicitly.
49
+
50
+ ## Hard rule
51
+
52
+ - Middleware-level auth gate. Use Next.js middleware to protect `/dashboard/*` and `/settings/*`. Unauthenticated requests redirect to `/login`. Authenticated requests to `/login` redirect to `/dashboard`.
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env bash
2
+ # Profile-aware verification gate. Runs the right checks for the project's profile.
3
+ # Called after /update-image, /update-gui, /update-backend, or at any time the operator wants to verify.
4
+ #
5
+ # Usage: scripts/verify.sh [profile] [deploy_url]
6
+ # Reads: docs/brief.json (for profile) if no profile arg
7
+ # Defaults: profile=marketing, deploy_url from .vercel/project.json if available
8
+
9
+ set -e
10
+
11
+ PROFILE="${1:-}"
12
+ DEPLOY_URL="${2:-}"
13
+
14
+ # Auto-detect profile from brief.json if not given
15
+ if [ -z "$PROFILE" ] && [ -f docs/brief.json ]; then
16
+ PROFILE=$(python3 -c "import json; print(json.load(open('docs/brief.json'))['scaffold']['profile'])" 2>/dev/null || echo "marketing")
17
+ fi
18
+ PROFILE="${PROFILE:-marketing}"
19
+
20
+ # Auto-detect deploy URL if not given (best-effort; operator may need to pass it)
21
+ if [ -z "$DEPLOY_URL" ]; then
22
+ if [ -f .vercel/project.json ]; then
23
+ PROJECT_NAME=$(python3 -c "import json; print(json.load(open('.vercel/project.json'))['projectName'])" 2>/dev/null || echo "")
24
+ if [ -n "$PROJECT_NAME" ]; then
25
+ DEPLOY_URL="https://${PROJECT_NAME}.vercel.app"
26
+ fi
27
+ fi
28
+ fi
29
+
30
+ echo "=========================================="
31
+ echo " Verifying profile: $PROFILE"
32
+ echo " Deploy URL: ${DEPLOY_URL:-<not detected; pass as arg 2>}"
33
+ echo "=========================================="
34
+
35
+ PASS=0
36
+ FAIL=0
37
+
38
+ check() {
39
+ local desc="$1"
40
+ local cmd="$2"
41
+ echo -n " [$PROFILE] $desc ... "
42
+ if eval "$cmd" > /tmp/verify-out 2>&1; then
43
+ echo "PASS"
44
+ PASS=$((PASS + 1))
45
+ else
46
+ echo "FAIL"
47
+ echo " Output: $(head -3 /tmp/verify-out)"
48
+ FAIL=$((FAIL + 1))
49
+ fi
50
+ }
51
+
52
+ # === Common checks (all profiles) ===
53
+ echo ""
54
+ echo "--- common ---"
55
+ check "build succeeds" "npm run build"
56
+ check ".env.example exists" "[ -f .env.example ]"
57
+ check "no {{TOKEN}} placeholders in CLAUDE.md" "! grep -E '\\{\\{[A-Z_]+\\}\\}' CLAUDE.md || true"
58
+
59
+ if [ -n "$DEPLOY_URL" ]; then
60
+ check "live URL returns 200" "curl -sI '$DEPLOY_URL' | head -1 | grep -E '200|301|302'"
61
+ fi
62
+
63
+ # === Profile-specific checks ===
64
+ case "$PROFILE" in
65
+ lead-gen)
66
+ echo ""
67
+ echo "--- lead-gen ---"
68
+ check "/api/contact route exists" "[ -f src/app/api/contact/route.ts ] || [ -f src/app/api/contact/route.js ] || [ -f app/api/contact/route.ts ]"
69
+ check "JSON-LD LocalBusiness in HTML" "grep -r 'LocalBusiness' src/ 2>/dev/null"
70
+ if [ -n "$DEPLOY_URL" ]; then
71
+ check "POST /api/contact returns 200 or 400" "curl -s -X POST '$DEPLOY_URL/api/contact' -H 'Content-Type: application/json' -d '{\"name\":\"verify\",\"email\":\"verify@example.com\",\"message\":\"verify\"}' -o /dev/null -w '%{http_code}' | grep -E '^(200|400)$'"
72
+ fi
73
+ ;;
74
+ booking)
75
+ echo ""
76
+ echo "--- booking ---"
77
+ check "booking API route exists" "find src/app/api/booking -type f 2>/dev/null | head -1 | grep -q ."
78
+ check "DATABASE_URL referenced in code or env" "grep -r 'DATABASE_URL' src/ .env.example 2>/dev/null | head -1"
79
+ ;;
80
+ saas-dashboard)
81
+ echo ""
82
+ echo "--- saas-dashboard ---"
83
+ check "auth middleware exists" "[ -f src/middleware.ts ] || [ -f src/middleware.js ] || [ -f middleware.ts ]"
84
+ check "/login or /signup page exists" "find src/app -type d \\( -name 'login' -o -name 'signup' -o -name 'auth' \\) 2>/dev/null | head -1 | grep -q ."
85
+ check "/dashboard page exists" "find src/app -type d -name 'dashboard' 2>/dev/null | head -1 | grep -q ."
86
+ if [ -n "$DEPLOY_URL" ]; then
87
+ check "/dashboard redirects unauthenticated" "curl -s -o /dev/null -w '%{http_code}' '$DEPLOY_URL/dashboard' | grep -E '^(301|302|307|401)$'"
88
+ fi
89
+ ;;
90
+ blog)
91
+ echo ""
92
+ echo "--- blog ---"
93
+ check "content/posts/ exists" "[ -d content/posts ] || [ -d src/content/posts ]"
94
+ check "mdx package installed" "grep -q '@next/mdx\\|next-mdx-remote\\|@mdx-js' package.json 2>/dev/null"
95
+ if [ -n "$DEPLOY_URL" ]; then
96
+ check "/rss.xml returns 200" "curl -sI '$DEPLOY_URL/rss.xml' | head -1 | grep -q 200"
97
+ check "/sitemap.xml returns 200" "curl -sI '$DEPLOY_URL/sitemap.xml' | head -1 | grep -q 200"
98
+ fi
99
+ ;;
100
+ marketing|*)
101
+ echo ""
102
+ echo "--- marketing baseline ---"
103
+ if [ -n "$DEPLOY_URL" ]; then
104
+ check "homepage has hero text" "curl -s '$DEPLOY_URL' | grep -iE '<h1' | head -1 | grep -q ."
105
+ fi
106
+ ;;
107
+ esac
108
+
109
+ # === Visual verification reminder ===
110
+ echo ""
111
+ echo "--- visual (per CLAUDE.md Part 19) ---"
112
+ if [ -n "$DEPLOY_URL" ] && [ -x "scripts/screenshot.sh" ]; then
113
+ scripts/screenshot.sh "$DEPLOY_URL" /tmp/verify-screenshot.png 420,900 > /dev/null && \
114
+ echo " Screenshot: /tmp/verify-screenshot.png (READ it to complete Part 19 verification)"
115
+ else
116
+ echo " Skipped (no deploy URL or no scripts/screenshot.sh)"
117
+ fi
118
+
119
+ # === Summary ===
120
+ echo ""
121
+ echo "=========================================="
122
+ echo " Verify summary: PASS=$PASS FAIL=$FAIL"
123
+ echo "=========================================="
124
+
125
+ if [ "$FAIL" -gt 0 ]; then
126
+ exit 1
127
+ fi
128
+ exit 0