@oaklandzoo/ostup 0.2.0 → 0.4.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.
- package/README.md +23 -0
- package/bin/cli.mjs +27 -1
- package/package.json +1 -1
- package/src/brief/classify.mjs +218 -0
- package/src/brief/index.mjs +126 -0
- package/src/brief/profile-router.mjs +89 -0
- package/src/brief/questions.mjs +333 -0
- package/src/brief/render-brief.mjs +232 -0
- package/src/brief/sample-briefs.mjs +304 -0
- package/src/brief/schema.mjs +176 -0
- package/src/mvp-flow.mjs +43 -1
- package/src/templates.mjs +2 -0
- package/templates/.claude/commands/generate-image-prompt.md +118 -0
- package/templates/.claude/commands/generate-image.md +168 -0
- package/templates/.claude/commands/preflight.md +8 -0
- package/templates/AGENTS.md +11 -1
- package/templates/CLAUDE.md +3 -0
- package/templates/START_HERE.md +10 -4
- package/templates/profiles/blog/.env.example.additions +7 -0
- package/templates/profiles/blog/README.md +70 -0
- package/templates/profiles/blog/section-prompts.md +63 -0
- package/templates/profiles/booking/.env.example.additions +16 -0
- package/templates/profiles/booking/README.md +61 -0
- package/templates/profiles/booking/section-prompts.md +47 -0
- package/templates/profiles/lead-gen/.env.example.additions +8 -0
- package/templates/profiles/lead-gen/README.md +58 -0
- package/templates/profiles/lead-gen/section-prompts.md +47 -0
- package/templates/profiles/marketing/README.md +39 -0
- package/templates/profiles/marketing/section-prompts.md +36 -0
- package/templates/profiles/saas-dashboard/.env.example.additions +21 -0
- package/templates/profiles/saas-dashboard/README.md +60 -0
- package/templates/profiles/saas-dashboard/section-prompts.md +52 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Compose an image-generation prompt AND call Vercel AI Gateway to actually generate the image. Saves to inputs/images/ and appends to MANIFEST.md. Requires VERCEL_AI_GATEWAY_KEY. Per CLAUDE.md Part 19, use /update-image to promote into the project.
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Generate image (composer plus API call)
|
|
6
|
+
|
|
7
|
+
Same flow as `/generate-image-prompt`, but calls Vercel AI Gateway and saves the result. Operator skips the paste-into-another-tool step.
|
|
8
|
+
|
|
9
|
+
## Step 1: preflight
|
|
10
|
+
|
|
11
|
+
Check the key exists:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
if [ -n "$VERCEL_AI_GATEWAY_KEY" ]; then
|
|
15
|
+
echo "VERCEL_AI_GATEWAY_KEY: present"
|
|
16
|
+
else
|
|
17
|
+
echo "VERCEL_AI_GATEWAY_KEY: MISSING"
|
|
18
|
+
echo "Get a key: https://vercel.com/dashboard/ai-gateway"
|
|
19
|
+
echo "Set it in .env.local (export VERCEL_AI_GATEWAY_KEY=...) and rerun."
|
|
20
|
+
echo "Aborting."
|
|
21
|
+
fi
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If the key is missing, stop and ask the operator to set it. Do not call the API without a key.
|
|
25
|
+
|
|
26
|
+
## Step 2: identify the asset type, read context, ask clarifying questions
|
|
27
|
+
|
|
28
|
+
Run Steps 1-3 of `/generate-image-prompt`. Compose the prompt in memory (or print it first for operator review if scope is significant).
|
|
29
|
+
|
|
30
|
+
## Step 3: surface cost estimate before calling
|
|
31
|
+
|
|
32
|
+
Print exactly:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
About to call Vercel AI Gateway:
|
|
36
|
+
Model: openai/dall-e-3
|
|
37
|
+
Size: 1024x1024
|
|
38
|
+
Quality: standard
|
|
39
|
+
Estimated cost: ~$0.04 (DALL-E 3 standard 1024x1024)
|
|
40
|
+
|
|
41
|
+
Proceed? (y/n)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Wait for operator confirmation. If they answer no, stop. If they answer yes (or the agent was invoked with `--yes` semantics from /update-image), proceed.
|
|
45
|
+
|
|
46
|
+
Cost reference (verify against current Vercel AI Gateway pricing before claiming numbers):
|
|
47
|
+
|
|
48
|
+
| Model | Size | Cost per image |
|
|
49
|
+
|---|---|---:|
|
|
50
|
+
| openai/dall-e-3 | 1024x1024 standard | ~$0.04 |
|
|
51
|
+
| openai/dall-e-3 | 1024x1024 hd | ~$0.08 |
|
|
52
|
+
| openai/dall-e-3 | 1792x1024 standard | ~$0.08 |
|
|
53
|
+
| openai/dall-e-3 | 1792x1024 hd | ~$0.12 |
|
|
54
|
+
| stability-ai/stable-diffusion-xl | 1024x1024 | ~$0.003 |
|
|
55
|
+
|
|
56
|
+
If size or model differs from defaults, recompute and surface.
|
|
57
|
+
|
|
58
|
+
## Step 4: call the API
|
|
59
|
+
|
|
60
|
+
Use bash and curl. OpenAI-compatible endpoint via Vercel AI Gateway.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
TIMESTAMP=$(date +%s)
|
|
64
|
+
TYPE="<asset-type>"
|
|
65
|
+
OUT="inputs/images/${TYPE}-${TIMESTAMP}.png"
|
|
66
|
+
mkdir -p inputs/images
|
|
67
|
+
|
|
68
|
+
# Use a heredoc to safely encode the prompt
|
|
69
|
+
PROMPT_JSON=$(cat <<'JSON_EOF'
|
|
70
|
+
{
|
|
71
|
+
"model": "openai/dall-e-3",
|
|
72
|
+
"prompt": "<COMPOSED_PROMPT_GOES_HERE>",
|
|
73
|
+
"size": "1024x1024",
|
|
74
|
+
"quality": "standard",
|
|
75
|
+
"n": 1,
|
|
76
|
+
"response_format": "b64_json"
|
|
77
|
+
}
|
|
78
|
+
JSON_EOF
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
curl -s https://ai-gateway.vercel.sh/v1/images/generations \
|
|
82
|
+
-H "Authorization: Bearer $VERCEL_AI_GATEWAY_KEY" \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-d "$PROMPT_JSON" > /tmp/ostup-img-response.json
|
|
85
|
+
|
|
86
|
+
# Verify success
|
|
87
|
+
if ! grep -q "b64_json" /tmp/ostup-img-response.json 2>/dev/null; then
|
|
88
|
+
echo "API call failed. Response:"
|
|
89
|
+
cat /tmp/ostup-img-response.json
|
|
90
|
+
exit 1
|
|
91
|
+
fi
|
|
92
|
+
|
|
93
|
+
# Extract base64 and write the PNG
|
|
94
|
+
python3 -c "
|
|
95
|
+
import json, base64, sys
|
|
96
|
+
with open('/tmp/ostup-img-response.json') as f:
|
|
97
|
+
d = json.load(f)
|
|
98
|
+
b64 = d['data'][0]['b64_json']
|
|
99
|
+
with open('$OUT', 'wb') as f:
|
|
100
|
+
f.write(base64.b64decode(b64))
|
|
101
|
+
print('Saved:', '$OUT')
|
|
102
|
+
"
|
|
103
|
+
|
|
104
|
+
rm -f /tmp/ostup-img-response.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If the model returns a URL instead of base64 (some configurations), fall back to:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
URL=$(python3 -c "import json; print(json.load(open('/tmp/ostup-img-response.json'))['data'][0]['url'])")
|
|
111
|
+
curl -sL -o "$OUT" "$URL"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Step 5: append to manifest
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
MANIFEST="inputs/images/MANIFEST.md"
|
|
118
|
+
|
|
119
|
+
# Initialize if missing
|
|
120
|
+
if [ ! -f "$MANIFEST" ]; then
|
|
121
|
+
echo "# Image manifest" > "$MANIFEST"
|
|
122
|
+
echo "" >> "$MANIFEST"
|
|
123
|
+
echo "Generated images, prompts used, models, and timestamps." >> "$MANIFEST"
|
|
124
|
+
echo "" >> "$MANIFEST"
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
cat >> "$MANIFEST" <<EOF
|
|
128
|
+
|
|
129
|
+
## ${TIMESTAMP} — <asset-type>
|
|
130
|
+
|
|
131
|
+
- **File:** \`${OUT}\`
|
|
132
|
+
- **Model:** openai/dall-e-3
|
|
133
|
+
- **Size:** 1024x1024
|
|
134
|
+
- **Quality:** standard
|
|
135
|
+
- **Cost:** ~\$0.04
|
|
136
|
+
- **Prompt:**
|
|
137
|
+
> <COMPOSED_PROMPT_SUMMARY_ONE_LINE>
|
|
138
|
+
|
|
139
|
+
EOF
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Step 6: report and offer to promote
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
Generated <asset type>: inputs/images/<asset-type>-<timestamp>.png
|
|
146
|
+
Manifest updated: inputs/images/MANIFEST.md
|
|
147
|
+
|
|
148
|
+
Want to promote it into the project now? Running /update-image <slot> will:
|
|
149
|
+
1. Copy this image into public/<slot>/
|
|
150
|
+
2. Update any code references
|
|
151
|
+
3. Commit + push
|
|
152
|
+
4. Wait for Vercel deploy
|
|
153
|
+
5. Run scripts/screenshot.sh and read the PNG to verify visually
|
|
154
|
+
|
|
155
|
+
Proceed with /update-image? (y/n)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
If operator agrees, run the full `/update-image` routine. Otherwise stop.
|
|
159
|
+
|
|
160
|
+
## Hard rules
|
|
161
|
+
|
|
162
|
+
- Generated images land in `inputs/images/` ONLY. Never directly in `public/`. The operator (or `/update-image`) promotes.
|
|
163
|
+
- Every generation appends a manifest line with prompt + model + size + timestamp + cost.
|
|
164
|
+
- Cost is always surfaced before the API call. No surprise charges.
|
|
165
|
+
- Missing `VERCEL_AI_GATEWAY_KEY` fails fast with a clear pointer to the dashboard URL.
|
|
166
|
+
- Brand palette from the brief must be in the prompt. No "free interpretation."
|
|
167
|
+
- This command does not promote the image. `/update-image` handles promotion + Part 19 visual verification.
|
|
168
|
+
- If the API call fails (rate limit, content policy, network), report the full error and do NOT retry silently.
|
|
@@ -34,6 +34,14 @@ echo "=== Local tools ==="
|
|
|
34
34
|
node --version
|
|
35
35
|
[ -d "/Applications/Google Chrome.app" ] && echo "Chrome: present" || echo "Chrome: MISSING (operator must install for visual verification per CLAUDE.md Part 19)"
|
|
36
36
|
[ -x "scripts/screenshot.sh" ] && echo "scripts/screenshot.sh: ready" || echo "scripts/screenshot.sh: MISSING or not executable"
|
|
37
|
+
|
|
38
|
+
echo ""
|
|
39
|
+
echo "=== AI Gateway (for /generate-image) ==="
|
|
40
|
+
if [ -n "$VERCEL_AI_GATEWAY_KEY" ]; then
|
|
41
|
+
echo "VERCEL_AI_GATEWAY_KEY: present (image generation enabled)"
|
|
42
|
+
else
|
|
43
|
+
echo "VERCEL_AI_GATEWAY_KEY: not set (/generate-image will fail until set; get one at https://vercel.com/dashboard/ai-gateway; /generate-image-prompt still works as composer-only)"
|
|
44
|
+
fi
|
|
37
45
|
```
|
|
38
46
|
|
|
39
47
|
## Step 2: print the SESSION READY summary
|
package/templates/AGENTS.md
CHANGED
|
@@ -29,10 +29,20 @@ Operator materials live in `{{INPUTS_PATH}}`. Read `{{INPUTS_PATH}}README.md` fo
|
|
|
29
29
|
|
|
30
30
|
Session lifecycle: `/bootstrap`, `/prompt-start`, `/prompt-mid`, `/prompt-end`, `/preflight`
|
|
31
31
|
|
|
32
|
-
Building: `/create-prd`, `/generate-tasks`, `/update-image`, `/update-gui`, `/update-backend`, `/add-storage`
|
|
32
|
+
Building: `/create-prd`, `/generate-tasks`, `/update-image`, `/update-gui`, `/update-backend`, `/add-storage`, `/generate-image-prompt`, `/generate-image`
|
|
33
33
|
|
|
34
34
|
See each file under `.claude/commands/` for the full routine.
|
|
35
35
|
|
|
36
|
+
## ostup CLI subcommands (run outside Claude Code)
|
|
37
|
+
|
|
38
|
+
- `ostup brief` — 10-question operator intake; writes `docs/brief.md`, `docs/brief.json`, `tasks/prd-initial-build.md`.
|
|
39
|
+
- `ostup init --brief <path>` — scaffold using an existing brief.json; applies the matching profile overlay.
|
|
40
|
+
- `ostup init` — interactive scaffold without brief.
|
|
41
|
+
|
|
36
42
|
## Helpers
|
|
37
43
|
|
|
38
44
|
- `scripts/screenshot.sh <url> [out] [WxH]` — headless Chrome screenshot. Required for visual verification per `CLAUDE.md` Part 19.
|
|
45
|
+
|
|
46
|
+
## If `docs/brief.md` is present in this project
|
|
47
|
+
|
|
48
|
+
Treat it as authoritative for scope, profile, brand, business model, and constraints. Downstream commands (`/create-prd`, `/generate-tasks`, etc.) should read `docs/brief.json` first to avoid re-asking the operator the same questions. Profile-specific guidance lives at `templates/profiles/<profile>/` (copied into your project root as `README.md` and `section-prompts.md` when the overlay is applied).
|
package/templates/CLAUDE.md
CHANGED
|
@@ -227,6 +227,9 @@ Detailed file map: `docs/ARCHITECTURE.md`.
|
|
|
227
227
|
> Things unique to THIS repo. Filled by `/bootstrap` or as discovered.
|
|
228
228
|
|
|
229
229
|
- Operator materials live in `{{INPUTS_PATH}}`. Read `{{INPUTS_PATH}}README.md` for the layout. If `{{INPUTS_PATH}}INGEST_MANIFEST.md` exists, read it before starting work.
|
|
230
|
+
- If `docs/brief.md` exists, it is the **authoritative source** for scope, profile, brand, business model, and constraints. Read `docs/brief.json` for the machine-readable version. Do not re-ask the operator the questions already answered there.
|
|
231
|
+
- If `tasks/prd-initial-build.md` exists, it is the **first PRD seed** generated from the brief. Start there before writing code.
|
|
232
|
+
- If a `README.md` and `section-prompts.md` exist at the project root from a profile overlay (e.g. `lead-gen`, `booking`, `saas-dashboard`, `blog`), follow that profile's hard rules and section guidance.
|
|
230
233
|
- {{CONVENTION_1}}
|
|
231
234
|
- {{CONVENTION_2}}
|
|
232
235
|
- {{CONVENTION_3}}
|
package/templates/START_HERE.md
CHANGED
|
@@ -17,9 +17,9 @@ Or use `codex`, `gemini`, or whichever CLI agent you prefer.
|
|
|
17
17
|
### 2. Paste this as your first message
|
|
18
18
|
|
|
19
19
|
```
|
|
20
|
-
Read CLAUDE.md, AGENTS.md, and everything in {{INPUTS_PATH}}. If {{INPUTS_PATH}} has source materials (research, reference repos, brand assets, notes), use them as context. If it is empty or only has a README, that is fine. Either way, ask me up to 3 clarifying questions about what I want to build. Then propose a 30 to 60 minute MVP scope as a numbered list. Do not write code until I approve the scope.
|
|
20
|
+
Read CLAUDE.md, AGENTS.md, docs/brief.md (if it exists), tasks/prd-initial-build.md (if it exists), and everything in {{INPUTS_PATH}}. If {{INPUTS_PATH}} has source materials (research, reference repos, brand assets, notes), use them as context. If it is empty or only has a README, that is fine. Either way, ask me up to 3 clarifying questions about what I want to build. Then propose a 30 to 60 minute MVP scope as a numbered list. Do not write code until I approve the scope.
|
|
21
21
|
|
|
22
|
-
After I approve, run /bootstrap to fill in this project's metadata, then start building.
|
|
22
|
+
After I approve, run /bootstrap to fill in this project's metadata, then start building. If docs/brief.md exists, treat it as authoritative for scope, brand, and constraints.
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
That is the whole jump-in. The agent reads, asks, proposes, you approve, it builds.
|
|
@@ -38,6 +38,10 @@ That is the whole jump-in. The agent reads, asks, proposes, you approve, it buil
|
|
|
38
38
|
| `docs/ARCHITECTURE.md` | Stack, file map, env vars, deploy details. | Agent |
|
|
39
39
|
| `{{INPUTS_PATH}}` | Your source materials. May be empty. You can add files anytime. | Both |
|
|
40
40
|
| `tasks/` | PRDs and task lists for features. | Both |
|
|
41
|
+
| `docs/brief.md` | Operator brief (only if you scaffolded with `ostup brief`). Source of truth for scope, brand, constraints. | Both |
|
|
42
|
+
| `docs/brief.json` | Machine-readable brief (same trigger). Used by downstream commands. | Agent |
|
|
43
|
+
| `tasks/prd-initial-build.md` | Auto-seeded first PRD from the brief (same trigger). | Both |
|
|
44
|
+
| `templates/profiles/<profile>/` | (if applicable) Profile-specific guidance the agent reads on day one. | Agent |
|
|
41
45
|
| `.claude/commands/` | Slash command definitions. | Agent |
|
|
42
46
|
|
|
43
47
|
## Slash commands
|
|
@@ -57,6 +61,8 @@ Type these in your CLI agent. Each runs a structured routine.
|
|
|
57
61
|
| `/update-gui` | Make a UI change, deploy, screenshot, confirm the visual change matches intent. Enforces visual verification. | Any UI tweak. |
|
|
58
62
|
| `/update-backend` | Make a backend/API change, deploy, probe the live endpoint, confirm behavior. | Any server/API change. |
|
|
59
63
|
| `/add-storage` | Provision a Vercel Blob, KV, Postgres, or Edge Config store + scaffold a typed `src/lib/<type>.ts` helper + pull env vars. | When you need persistent storage. |
|
|
64
|
+
| `/generate-image-prompt` | Compose an image-generation prompt from the project brief + 2-3 questions. No API call. Paste the prompt into your preferred image tool. | When you need a visual asset and want to use Midjourney, DALL-E, Imagen, etc. directly. |
|
|
65
|
+
| `/generate-image` | Compose the prompt AND call Vercel AI Gateway, saving the image to `inputs/images/`. Requires `VERCEL_AI_GATEWAY_KEY`. | When you want the image generated in-line without leaving your agent. |
|
|
60
66
|
|
|
61
67
|
## Three workflows
|
|
62
68
|
|
|
@@ -64,13 +70,13 @@ Type these in your CLI agent. Each runs a structured routine.
|
|
|
64
70
|
|
|
65
71
|
1. `claude` in this folder.
|
|
66
72
|
2. Paste the prompt from step 2 above.
|
|
67
|
-
3. The agent reads everything, asks 2 or 3 questions, proposes an MVP.
|
|
73
|
+
3. The agent reads everything (including `docs/brief.md` if present), asks 2 or 3 questions, proposes an MVP.
|
|
68
74
|
4. You approve (or refine).
|
|
69
75
|
5. The agent runs `/bootstrap` to fill in project metadata.
|
|
70
76
|
6. The agent builds the first cut and pushes commits.
|
|
71
77
|
7. `/prompt-end` when you stop.
|
|
72
78
|
|
|
73
|
-
Works the same whether `{{INPUTS_PATH}}` is empty or full. With materials, the agent uses them. Without, it asks you what to build outright.
|
|
79
|
+
Works the same whether `{{INPUTS_PATH}}` is empty or full. With materials, the agent uses them. Without, it asks you what to build outright. If you scaffolded with `ostup brief` or `ostup init --brief`, the agent has a richer starting point: scope, profile, business model, and add-ons already chosen.
|
|
74
80
|
|
|
75
81
|
### B. Return session (pick up where you left off)
|
|
76
82
|
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Profile: blog
|
|
2
|
+
|
|
3
|
+
> Content engine. MDX posts, RSS, sitemap, tag/author pages. SEO-first. No CMS v1.
|
|
4
|
+
|
|
5
|
+
## Day-one scope
|
|
6
|
+
|
|
7
|
+
| Section | Purpose | Required |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Homepage | Latest 6-10 posts, intro paragraph | yes |
|
|
10
|
+
| Post page | Single post by slug, MDX-rendered | yes |
|
|
11
|
+
| Tag index | List of all tags with post counts | yes |
|
|
12
|
+
| Tag page | All posts under a tag | yes |
|
|
13
|
+
| Author page (if multi-author) | Author bio + their posts | conditional |
|
|
14
|
+
| RSS feed | `/rss.xml` valid RSS 2.0 | yes |
|
|
15
|
+
| Sitemap | `/sitemap.xml` valid sitemap | yes |
|
|
16
|
+
| About | Static about page | yes |
|
|
17
|
+
| Footer | Links, RSS, year | yes |
|
|
18
|
+
|
|
19
|
+
## Wired infrastructure
|
|
20
|
+
|
|
21
|
+
- **MDX** for post bodies. Posts live as `.mdx` files in `content/posts/`.
|
|
22
|
+
- Frontmatter schema: `title`, `slug`, `date`, `tags[]`, `author?`, `description?`, `draft?`.
|
|
23
|
+
- `next-mdx-remote` or `@next/mdx` (agent picks; both work).
|
|
24
|
+
- Open Graph + Twitter card metadata per post.
|
|
25
|
+
- Article + Organization JSON-LD schema.
|
|
26
|
+
|
|
27
|
+
## Env additions
|
|
28
|
+
|
|
29
|
+
See `.env.example.additions` (mostly empty for blog v1).
|
|
30
|
+
|
|
31
|
+
## File contracts
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
content/posts/<slug>.mdx
|
|
35
|
+
---
|
|
36
|
+
title: "Post title"
|
|
37
|
+
slug: "post-slug"
|
|
38
|
+
date: "2026-05-21"
|
|
39
|
+
tags: ["agents", "tooling"]
|
|
40
|
+
author: "GG"
|
|
41
|
+
description: "One-sentence summary for SEO."
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
Body in MDX.
|
|
45
|
+
|
|
46
|
+
content/authors/<author>.mdx (optional)
|
|
47
|
+
---
|
|
48
|
+
name: "GG"
|
|
49
|
+
bio: "One-paragraph bio."
|
|
50
|
+
social: { x: "@goodshin", github: "DubsFan" }
|
|
51
|
+
---
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Hard rules
|
|
55
|
+
|
|
56
|
+
- Posts ship as committed MDX files. No DB v1.
|
|
57
|
+
- `draft: true` excludes from production builds AND from RSS/sitemap.
|
|
58
|
+
- RSS / sitemap regenerate on build.
|
|
59
|
+
- Code blocks use one syntax highlighter (Shiki recommended). Pick at build time, not client-side.
|
|
60
|
+
- Reading width capped at 720px or so for body comfort.
|
|
61
|
+
- Light + dark mode via `prefers-color-scheme`.
|
|
62
|
+
|
|
63
|
+
## Acceptance
|
|
64
|
+
|
|
65
|
+
- Homepage shows latest 6 posts sorted by date desc.
|
|
66
|
+
- Individual post page renders MDX with syntax-highlighted code.
|
|
67
|
+
- RSS feed validates at https://validator.w3.org/feed/.
|
|
68
|
+
- Sitemap includes every published post (no drafts).
|
|
69
|
+
- Tag pages reachable from each post's tag list.
|
|
70
|
+
- Lighthouse Performance >= 95 (static content, should be fast).
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Blog profile section prompts
|
|
2
|
+
|
|
3
|
+
Agent: build each section using the brief. Default MDX library: `next-mdx-remote`. Use `gray-matter` for frontmatter parsing.
|
|
4
|
+
|
|
5
|
+
## Homepage
|
|
6
|
+
|
|
7
|
+
- Title + tagline from the brief.
|
|
8
|
+
- Latest 6 posts: title (clickable), date, 1-2-sentence excerpt from `description` or first 160 chars of body.
|
|
9
|
+
- "All posts" link to a chronological archive (optional v1).
|
|
10
|
+
- Tag cloud or top 5 tags if there are 3+ tagged posts.
|
|
11
|
+
|
|
12
|
+
## Post page (`/posts/[slug]`)
|
|
13
|
+
|
|
14
|
+
- Title (h1), date, tags (clickable to tag pages), author (if multi-author).
|
|
15
|
+
- MDX-rendered body.
|
|
16
|
+
- Code blocks: syntax-highlighted with Shiki. Light + dark theme.
|
|
17
|
+
- Body width capped (~65ch).
|
|
18
|
+
- Reading time estimate (optional).
|
|
19
|
+
- Open Graph: title, description, type=article, published_time, author.
|
|
20
|
+
|
|
21
|
+
## Tag index (`/tags`)
|
|
22
|
+
|
|
23
|
+
- All tags from all published posts, sorted by post count desc.
|
|
24
|
+
- Each tag: name + post count + link.
|
|
25
|
+
|
|
26
|
+
## Tag page (`/tags/[tag]`)
|
|
27
|
+
|
|
28
|
+
- All posts under that tag, chronological desc.
|
|
29
|
+
- Same card format as homepage list.
|
|
30
|
+
|
|
31
|
+
## Author page (`/authors/[author]`) — only if multi-author
|
|
32
|
+
|
|
33
|
+
- Author bio from `content/authors/<author>.mdx`.
|
|
34
|
+
- All posts by that author.
|
|
35
|
+
|
|
36
|
+
## RSS feed (`/rss.xml`)
|
|
37
|
+
|
|
38
|
+
- RSS 2.0.
|
|
39
|
+
- Include title, link, description, pubDate, category for each post.
|
|
40
|
+
- Published posts only (`draft: false`).
|
|
41
|
+
- Validate the output structure manually before claiming done.
|
|
42
|
+
|
|
43
|
+
## Sitemap (`/sitemap.xml`)
|
|
44
|
+
|
|
45
|
+
- All published posts + tag pages + author pages + homepage + about.
|
|
46
|
+
- `<lastmod>` on each entry.
|
|
47
|
+
- No drafts.
|
|
48
|
+
|
|
49
|
+
## About
|
|
50
|
+
|
|
51
|
+
- Pulled from the brief: who runs the site, what it covers, contact.
|
|
52
|
+
- Single page. Plain text + maybe a portrait from `inputs/images/`.
|
|
53
|
+
|
|
54
|
+
## Footer
|
|
55
|
+
|
|
56
|
+
- RSS link with feed icon.
|
|
57
|
+
- GitHub link (if applicable).
|
|
58
|
+
- Year + copyright.
|
|
59
|
+
|
|
60
|
+
## First post
|
|
61
|
+
|
|
62
|
+
- If the brief mentions a launch announcement, write `content/posts/hello-world.mdx` as a placeholder with the project summary.
|
|
63
|
+
- Otherwise leave `content/posts/` with a `.gitkeep` only and let the operator write the first post.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# --- booking profile additions ---
|
|
2
|
+
# Database for Booking entity (Neon recommended; any Postgres works)
|
|
3
|
+
DATABASE_URL=
|
|
4
|
+
|
|
5
|
+
# Email confirmation
|
|
6
|
+
RESEND_API_KEY=
|
|
7
|
+
BOOKING_TO_EMAIL=
|
|
8
|
+
BOOKING_FROM_EMAIL=noreply@example.com
|
|
9
|
+
|
|
10
|
+
# Optional: Stripe deposit checkout (enable only if `payments` addon is on)
|
|
11
|
+
STRIPE_SECRET_KEY=
|
|
12
|
+
STRIPE_PUBLISHABLE_KEY=
|
|
13
|
+
STRIPE_WEBHOOK_SECRET=
|
|
14
|
+
|
|
15
|
+
# Public app URL (Vercel sets this for production)
|
|
16
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Profile: booking
|
|
2
|
+
|
|
3
|
+
> Appointment / reservation site. Availability request, booking form, optional Stripe deposit, email confirmations.
|
|
4
|
+
|
|
5
|
+
## Day-one scope
|
|
6
|
+
|
|
7
|
+
| Section | Purpose | Required |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Hero | Single value statement + "Check availability" or "Book now" CTA | yes |
|
|
10
|
+
| Availability / Booking form | Date range or appointment slot picker + guest info | yes |
|
|
11
|
+
| Rooms / Services | List of bookable units with descriptions and rates | yes |
|
|
12
|
+
| Deposit checkout | Stripe Checkout for the deposit if `payments` addon is on | conditional |
|
|
13
|
+
| Confirmation email | Auto-send on submission to both guest and admin | yes |
|
|
14
|
+
| About / Amenities | Trust-building content from the brief | yes |
|
|
15
|
+
| Footer | Contact, cancellation policy summary | yes |
|
|
16
|
+
|
|
17
|
+
## Wired infrastructure
|
|
18
|
+
|
|
19
|
+
- **Postgres** for the `Booking` entity (date_start, date_end, guest info, status, deposit_status).
|
|
20
|
+
- **Resend** for guest + admin confirmation emails.
|
|
21
|
+
- **Stripe Checkout** for deposit (only if `payments` addon is on per the brief).
|
|
22
|
+
- Mobile-first calendar / date picker.
|
|
23
|
+
|
|
24
|
+
## Env additions
|
|
25
|
+
|
|
26
|
+
See `.env.example.additions`.
|
|
27
|
+
|
|
28
|
+
## API contracts
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
POST /api/booking/request
|
|
32
|
+
Body: { name, email, phone?, date_start, date_end, room_or_service?, notes? }
|
|
33
|
+
Response: { ok, booking_id }
|
|
34
|
+
Behavior: insert Booking with status='pending', email both parties.
|
|
35
|
+
|
|
36
|
+
POST /api/booking/deposit
|
|
37
|
+
Body: { booking_id }
|
|
38
|
+
Response: { ok, checkout_url }
|
|
39
|
+
Behavior: create Stripe Checkout session, return URL.
|
|
40
|
+
|
|
41
|
+
POST /api/webhooks/stripe
|
|
42
|
+
Behavior: on payment_intent.succeeded, mark booking deposit_status='paid'.
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Hard rules
|
|
46
|
+
|
|
47
|
+
- Date pickers MUST be mobile-friendly (no tiny native date inputs without testing).
|
|
48
|
+
- Server-side validate the date range (no past dates, no end-before-start).
|
|
49
|
+
- Confirmation emails go out immediately on form submit, even before deposit.
|
|
50
|
+
- Deposit is OPTIONAL: if `payments` addon is off, skip Stripe entirely. Booking still goes through.
|
|
51
|
+
- Calendar availability check should be server-side; do not trust client.
|
|
52
|
+
- If `Booking.status` is 'pending' for >48h with no deposit, mark as 'expired'.
|
|
53
|
+
|
|
54
|
+
## Acceptance
|
|
55
|
+
|
|
56
|
+
- Visitor can request dates and submit.
|
|
57
|
+
- Both visitor and admin receive emails within 30 seconds.
|
|
58
|
+
- Deposit Checkout opens if enabled, completes round-trip to the booking record.
|
|
59
|
+
- Past dates rejected with clear error.
|
|
60
|
+
- Hero CTA above the fold at 420x900.
|
|
61
|
+
- Lighthouse mobile Performance >= 85 (acceptable lower than marketing because of date picker JS).
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Booking profile section prompts
|
|
2
|
+
|
|
3
|
+
Agent: build each section using the brief in `docs/brief.md`. Honor `must_avoid` strictly.
|
|
4
|
+
|
|
5
|
+
## Hero
|
|
6
|
+
|
|
7
|
+
- Headline (5-9 words) about the experience being booked.
|
|
8
|
+
- Subhead: one sentence on what the visitor gets when they book.
|
|
9
|
+
- Primary CTA: "Check availability" or "Book now" — opens the booking form modal or scrolls to it.
|
|
10
|
+
|
|
11
|
+
## Availability / Booking form
|
|
12
|
+
|
|
13
|
+
- Date range picker (or single-date for appointments). Mobile-friendly is mandatory.
|
|
14
|
+
- Guest info: name, email, phone, party size (if relevant), notes.
|
|
15
|
+
- Validate server-side: no past dates, end after start, party size within room limits.
|
|
16
|
+
- Loading state on submit. Success state with booking ID. Error state with clear message.
|
|
17
|
+
|
|
18
|
+
## Rooms / Services
|
|
19
|
+
|
|
20
|
+
- Pull entity list from the brief's `data_model.entities`. If a Room entity exists, list its fields visually.
|
|
21
|
+
- Each unit: name, description (short), rate per night/hour, photo placeholder if `inputs/images/` has anything matching.
|
|
22
|
+
- "Book this" CTA scrolls back to the booking form with the room pre-selected.
|
|
23
|
+
|
|
24
|
+
## Deposit checkout (conditional)
|
|
25
|
+
|
|
26
|
+
- Only render if `payments` addon is in the brief.
|
|
27
|
+
- After successful booking submission, present a "Pay deposit to confirm" CTA.
|
|
28
|
+
- Clicking opens Stripe Checkout via `/api/booking/deposit`.
|
|
29
|
+
- On Checkout success, return to a `/booking/[id]/confirmed` page.
|
|
30
|
+
- Webhook `/api/webhooks/stripe` updates `deposit_status='paid'`.
|
|
31
|
+
|
|
32
|
+
## Confirmation emails
|
|
33
|
+
|
|
34
|
+
- Guest receives: "Your booking request is in. We'll confirm within X hours. Booking ID: ABC123."
|
|
35
|
+
- Admin receives: full booking details + a link to the admin route (TBD v2).
|
|
36
|
+
- Use Resend. Plain text + HTML versions.
|
|
37
|
+
|
|
38
|
+
## About / Amenities
|
|
39
|
+
|
|
40
|
+
- Pull from the brief's `must_have_sections`. Echo the brief's vibe.
|
|
41
|
+
- Photos from `inputs/images/` if any. Otherwise text-only placeholders.
|
|
42
|
+
|
|
43
|
+
## Footer
|
|
44
|
+
|
|
45
|
+
- Cancellation policy summary (1-2 sentences from the brief or a placeholder asking the operator).
|
|
46
|
+
- Contact email + phone.
|
|
47
|
+
- Year + copyright.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Profile: lead-gen
|
|
2
|
+
|
|
3
|
+
> Service-company site that captures contact requests. Resend for email, optional CRM webhook, local SEO basics.
|
|
4
|
+
|
|
5
|
+
## Day-one scope
|
|
6
|
+
|
|
7
|
+
| Section | Purpose | Required |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| Hero | Single value statement + primary CTA "Request a quote" or "Contact us" | yes |
|
|
10
|
+
| Services | 3-8 service cards or list. Tap-to-call on mobile. | yes |
|
|
11
|
+
| Service area | Map placeholder OR text list of cities / zip codes covered | yes |
|
|
12
|
+
| Testimonials | 2-4 quotes (placeholders OK if brief has none) | yes |
|
|
13
|
+
| FAQ | 4-8 questions, accordion or stacked | yes |
|
|
14
|
+
| Contact form | Name, phone, email, message. Posts to /api/contact. | yes |
|
|
15
|
+
| Footer | Phone, email, license number if applicable, year | yes |
|
|
16
|
+
|
|
17
|
+
## Wired infrastructure
|
|
18
|
+
|
|
19
|
+
- **Resend** for email notifications when a lead submits the form.
|
|
20
|
+
- Optional **CRM webhook** (env: `CRM_WEBHOOK_URL`). If set, also POST the lead payload as JSON.
|
|
21
|
+
- **JSON-LD LocalBusiness schema** in the head of every page.
|
|
22
|
+
- Open Graph + Twitter metadata using `{{DISPLAY_NAME}}` and the brand summary.
|
|
23
|
+
|
|
24
|
+
## Env additions
|
|
25
|
+
|
|
26
|
+
See `.env.example.additions` for the variables this profile expects.
|
|
27
|
+
|
|
28
|
+
## API contract for /api/contact
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
POST /api/contact
|
|
32
|
+
Content-Type: application/json
|
|
33
|
+
Body: { name: string, email: string, phone?: string, message: string }
|
|
34
|
+
Response: { ok: boolean, id?: string }
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The route:
|
|
38
|
+
1. Validates required fields (name, email, message).
|
|
39
|
+
2. Sends an email via Resend to `CONTACT_TO_EMAIL` from `CONTACT_FROM_EMAIL`.
|
|
40
|
+
3. If `CRM_WEBHOOK_URL` is set, POSTs the lead payload there.
|
|
41
|
+
4. Returns 200 with `{ ok: true }` on success; 400 on validation; 500 on send failure.
|
|
42
|
+
|
|
43
|
+
No persistent storage v1. Leads live in the operator's email inbox.
|
|
44
|
+
|
|
45
|
+
## Hard rules
|
|
46
|
+
|
|
47
|
+
- Phone number on every page (mobile sticky CTA acceptable).
|
|
48
|
+
- Service area is explicit. Do not claim to cover areas the brief does not mention.
|
|
49
|
+
- No JS-heavy carousels. Stacked content reads better on phones.
|
|
50
|
+
- Honor `constraints.must_avoid` strictly.
|
|
51
|
+
|
|
52
|
+
## Acceptance
|
|
53
|
+
|
|
54
|
+
- Visitor can submit the contact form, sees an inline success state.
|
|
55
|
+
- Owner receives the lead by email at the address in `CONTACT_TO_EMAIL`.
|
|
56
|
+
- JSON-LD LocalBusiness schema validates at https://search.google.com/test/rich-results.
|
|
57
|
+
- Hero CTA above the fold at 420x900.
|
|
58
|
+
- Lighthouse mobile Performance >= 90, Accessibility >= 95.
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# Lead-gen profile section prompts
|
|
2
|
+
|
|
3
|
+
Agent: build each section using the brief in `docs/brief.md`. Do not invent service offerings, prices, or testimonials.
|
|
4
|
+
|
|
5
|
+
## Hero
|
|
6
|
+
|
|
7
|
+
- Headline: 5-9 words. Echo the project summary in the operator's voice.
|
|
8
|
+
- Subhead: one sentence on who it serves and what they get.
|
|
9
|
+
- Primary CTA: "Request a quote" or "Contact us today" (use the brief's wording if it specifies).
|
|
10
|
+
- Secondary trust signal: years in business, license number, or service area at a glance (only if in brief).
|
|
11
|
+
|
|
12
|
+
## Services
|
|
13
|
+
|
|
14
|
+
- List 3-8 services from the brief's `scope.must_have_sections` (filter out non-service sections like hero/footer/contact).
|
|
15
|
+
- Each service card: short title + one or two sentences + (if available from brief) a starting-price hint.
|
|
16
|
+
- Tap-to-call phone link visible on mobile.
|
|
17
|
+
|
|
18
|
+
## Service area
|
|
19
|
+
|
|
20
|
+
- If the brief mentions specific cities, towns, or zip codes, list them.
|
|
21
|
+
- If only a general area, use that phrasing exactly.
|
|
22
|
+
- If no specific area info: ask the operator to fill in `docs/brief.md` before building this section. Do NOT default to "nationwide."
|
|
23
|
+
|
|
24
|
+
## Testimonials
|
|
25
|
+
|
|
26
|
+
- 2-4 short quotes. If the brief has actual quotes, use them.
|
|
27
|
+
- If none: use clear placeholders like `[Testimonial pending — operator to add]` and DO NOT fabricate.
|
|
28
|
+
|
|
29
|
+
## FAQ
|
|
30
|
+
|
|
31
|
+
- 4-8 questions tied to the services from the brief.
|
|
32
|
+
- Suggested defaults: "How fast can you get here?", "Do you provide free estimates?", "Are you licensed and insured?", "Do you offer financing?". Match to the brief's business model.
|
|
33
|
+
- Use an accordion or stacked Q/A. No JS framework needed.
|
|
34
|
+
|
|
35
|
+
## Contact form
|
|
36
|
+
|
|
37
|
+
- Fields: name (required), email (required), phone (optional), message (required).
|
|
38
|
+
- POSTs to `/api/contact`.
|
|
39
|
+
- Inline success message on submit: "Got it. We will be in touch within one business day."
|
|
40
|
+
- On error: inline message with retry, never alert dialogs.
|
|
41
|
+
- Form labels visible, not just placeholders (accessibility).
|
|
42
|
+
|
|
43
|
+
## Footer
|
|
44
|
+
|
|
45
|
+
- Phone, email, address (if relevant), license number (if relevant).
|
|
46
|
+
- Current year, copyright.
|
|
47
|
+
- Privacy / Terms links if the operator has them (otherwise omit; do not invent legalese).
|