Skip to content

Commit 2ab0e73

Browse files
vintaclaude
andcommitted
docs(skill): improve setup guide accuracy and completeness
Fix step numbering (7b -> 8, 8 -> 9, etc.), update prerequisites to Node.js 22+, add issues_dir/attachments_dir config fields to onboarding table, document all frontmatter fields, and clean up table column alignment. Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 45c06de commit 2ab0e73

File tree

1 file changed

+46
-35
lines changed

1 file changed

+46
-35
lines changed

skills/laughing-man/SKILL.md

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,15 @@ Check current state and skip completed steps:
1414
- `laughing-man.yaml` exists with real values (not placeholders)? Skip steps 1-2.
1515
- `.env` has `CLOUDFLARE_API_TOKEN`? Skip steps 3-4.
1616
- `.env` has `RESEND_API_KEY` and Pages secret is set? Skip steps 5-6. Run `setup newsletter` to verify domain status.
17-
- `.md` issue files already exist with real content (not the init template)? Skip step 8.
17+
- `.md` issue files already exist with real content (not the init template)? Skip step 9.
1818

1919
Tell the user which steps you're skipping and why, then start from the first incomplete step.
2020

2121
## Prerequisites
2222

23-
- Bun installed (`curl -fsSL https://bun.sh/install | bash`)
23+
- Node.js 22+ installed
2424
- A Cloudflare account (free tier works)
25-
- A Resend account with a verified sending domain
25+
- A Resend account (free tier works, domain setup is covered in step 5)
2626

2727
## Steps
2828

@@ -35,6 +35,7 @@ npx @sadcoderlabs/laughing-man init
3535
```
3636

3737
Creates:
38+
3839
- `laughing-man.yaml` with placeholder values
3940
- `your-first-newsletter-issue.md` (a sample draft issue)
4041
- `.gitignore` entries for `output/` and `preview/`
@@ -44,14 +45,18 @@ Creates:
4445

4546
Ask the user for each value, then edit `laughing-man.yaml`:
4647

47-
| Field | Ask | Example |
48-
| ------------------------ | ------------------------------ | -------------------------------- |
49-
| `name` | Newsletter name? | "The Laughing Man" |
50-
| `description` | Short description? (optional) | "A newsletter by [Name](url)" |
51-
| `web_hosting.project` | Cloudflare Pages project name? | "my-newsletter" |
52-
| `web_hosting.domain` | Custom domain? (optional) | "newsletter.example.com" |
53-
| `email_hosting.from` | Sender name and email? | "Vinta <hello@example.com>" |
54-
| `email_hosting.reply_to` | Reply-to email? (optional) | "hello@example.com" |
48+
| Field | Ask | Example |
49+
| ------------------------ | ------------------------------ | ---------------------------------------------- |
50+
| `name` | Newsletter name? | "The Laughing Man" |
51+
| `description` | Short description? (optional) | "A newsletter by [Name](url)" |
52+
| `issues_dir` | Directory with .md files? | "." (default: current dir) |
53+
| `attachments_dir` | Image/attachment directory? | "./attachments" (optional) |
54+
| `web_hosting.project` | Cloudflare Pages project name? | "my-newsletter" |
55+
| `web_hosting.domain` | Custom domain? (optional) | "example.com" |
56+
| `email_hosting.from` | Sender name and email? | "Your Name <your-name@newsletter.example.com>" |
57+
| `email_hosting.reply_to` | Reply-to email? (optional) | "your-name@newsletter.example.com" |
58+
59+
Both `issues_dir` and `attachments_dir` default to `.` in the init template. Only change them if your Markdown files or images live in a different directory.
5560

5661
The site URL is computed automatically: `https://{domain}` if a custom domain is set, otherwise `https://{project}.pages.dev`.
5762

@@ -84,7 +89,7 @@ Create `.env` in the newsletter directory:
8489
CLOUDFLARE_API_TOKEN=<token>
8590
```
8691

87-
Never put real tokens in `laughing-man.yaml` if the repo is public.
92+
The init template writes placeholder values (`cf_xxxxx`, `re_xxxxx`) in the yaml's `env:` section. You can leave them as-is or remove the `env:` block entirely. The `.env` file takes priority over the yaml values.
8893

8994
This env var is used by both `setup web` (Cloudflare SDK) and `deploy` (wrangler). No separate `wrangler login` is needed.
9095

@@ -160,15 +165,16 @@ If the user is using an apex domain (e.g., `example.com` rather than `newsletter
160165
This is expected and correct. Apex domains require:
161166

162167
1. The domain must be a Cloudflare zone on the same account (nameservers pointed to Cloudflare).
163-
2. The custom domain must be added through Pages *before* the CNAME is created (our `setup web` does this in the right order). Doing it backwards causes a 522 error.
168+
2. The custom domain must be added through Pages _before_ the CNAME is created (our `setup web` does this in the right order). Doing it backwards causes a 522 error.
164169
3. Cloudflare automatically flattens the apex CNAME (resolves it to the final IP) on all plans.
165170

166171
Docs:
172+
167173
- https://developers.cloudflare.com/pages/configuration/custom-domains/
168174
- https://developers.cloudflare.com/dns/cname-flattening/
169175
- https://developers.cloudflare.com/dns/manage-dns-records/how-to/create-zone-apex/
170176

171-
### 7b. Run setup newsletter
177+
### 8. Run setup newsletter
172178

173179
```bash
174180
npx @sadcoderlabs/laughing-man setup newsletter
@@ -178,7 +184,7 @@ Expected output:
178184

179185
```
180186
[ok] Resend API key valid
181-
[ok] Sender domain "send.example.com" exists (status: verified) # or "created" if new
187+
[ok] Sender domain "send.example.com" exists (status: verified) # or "created" if new
182188
[ok] Sender domain "send.example.com" is verified # when already verified
183189
[ok] Segment "General" found (seg_xxxxx) # or "[ok] N segments found" if multiple
184190
[ok] Pages secret RESEND_API_KEY set for project "<project>" # when CLOUDFLARE_API_TOKEN is available
@@ -196,26 +202,31 @@ If no sender domain exists yet, the command registers it with Resend automatical
196202

197203
If Cloudflare auth is missing or the Pages secret update fails, the command falls back to printing the manual `wrangler pages secret put` command.
198204

199-
### 8. Write the first issue
205+
### 9. Write the first issue
200206

201207
If you ran `init`, a sample `your-first-newsletter-issue.md` already exists as a draft. Edit it or create a new Markdown file in the newsletter directory:
202208

203209
```markdown
204210
---
205211
issue: 1
206212
status: ready
213+
title: Welcome to My Newsletter
214+
date: 2025-01-15
207215
---
208216

209217
# Welcome to My Newsletter
210218

211219
This is the first issue.
212220
```
213221

214-
The `status` field controls visibility:
215-
- `ready` -- included in `build` and `deploy`
216-
- `draft` -- excluded from `build`, but included in `preview` (unless `--no-drafts` is passed)
222+
Frontmatter fields:
223+
224+
- `issue` (required) -- positive integer, must be unique across all issues
225+
- `status` (required) -- `ready` (included in build/deploy) or `draft` (excluded from build, included in preview)
226+
- `title` (optional) -- overrides the first `#` heading in the body
227+
- `date` (optional) -- publication date in YYYY-MM-DD format
217228

218-
### 9. Build and deploy
229+
### 10. Build and deploy
219230

220231
```bash
221232
npx @sadcoderlabs/laughing-man build
@@ -225,26 +236,26 @@ npx @sadcoderlabs/laughing-man deploy
225236
To preview locally before deploying:
226237

227238
```bash
228-
npx @sadcoderlabs/laughing-man preview # includes drafts
229-
npx @sadcoderlabs/laughing-man preview --no-drafts # published issues only
239+
npx @sadcoderlabs/laughing-man preview # includes drafts
240+
npx @sadcoderlabs/laughing-man preview --no-drafts # published issues only (--production also works)
230241
```
231242

232-
### 10. Verify
243+
### 11. Verify
233244

234245
- Check `https://<project>.pages.dev`
235246
- If custom domain is configured, also check `https://<domain>` (DNS may take a few minutes)
236247

237248
## Troubleshooting
238249

239-
| Problem | Fix |
240-
| --------------------------------------- | ---------------------------------------------------------------------------------------- |
241-
| "Cloudflare API token is invalid" | Regenerate at dash.cloudflare.com/profile/api-tokens |
242-
| 403 Unauthorized on `setup web` | Token needs Account > Cloudflare Pages > Edit. If `web_hosting.domain` is set, also add Zone > DNS > Edit for that specific zone. |
243-
| "API token lacks required permissions" | Token needs Account > Cloudflare Pages > Edit. If `web_hosting.domain` is set, also add Zone > DNS > Edit for that specific zone. |
244-
| "Pages project name X is not available" | Change `web_hosting.project` in laughing-man.yaml |
245-
| "A DNS record managed by Workers already exists" | Another Workers/Pages project owns a record on that host. Managed records can't be deleted from the DNS page directly. Delete the Worker or Pages project that owns the record under Workers & Pages in the dashboard, or use a different domain/subdomain. |
246-
| Custom domain shows 522 error | Wait for DNS propagation (up to 48h), verify CNAME is correct |
247-
| "Resend API key is invalid" | Regenerate at resend.com/api-keys. Must have "Full access" permission. |
248-
| `setup newsletter` shows "not yet verified" | Add the DNS records printed by the command, wait a few minutes, re-run. |
249-
| Subscribe form returns "Failed to subscribe" | Resend secret not set on Pages project. Re-run `setup newsletter` with a valid `CLOUDFLARE_API_TOKEN`, or run `bunx wrangler pages secret put RESEND_API_KEY --project-name <project>`. Verify with `bunx wrangler pages secret list --project-name <project>`. |
250-
| Subscribe form returns "Invalid request" | Request body is not valid JSON or missing `email` field. Check browser console for errors. |
250+
| Problem | Fix |
251+
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
252+
| "Cloudflare API token is invalid" | Regenerate at dash.cloudflare.com/profile/api-tokens |
253+
| 403 Unauthorized on `setup web` | Token needs Account > Cloudflare Pages > Edit. If `web_hosting.domain` is set, also add Zone > DNS > Edit for that specific zone. |
254+
| "API token lacks required permissions" | Token needs Account > Cloudflare Pages > Edit. If `web_hosting.domain` is set, also add Zone > DNS > Edit for that specific zone. |
255+
| "Pages project name X is not available" | Change `web_hosting.project` in laughing-man.yaml |
256+
| "A DNS record managed by Workers already exists" | Another Workers/Pages project owns a record on that host. Managed records can't be deleted from the DNS page directly. Delete the Worker or Pages project that owns the record under Workers & Pages in the dashboard, or use a different domain/subdomain. |
257+
| Custom domain shows 522 error | Wait for DNS propagation (up to 48h), verify CNAME is correct |
258+
| "Resend API key is invalid" | Regenerate at resend.com/api-keys. Must have "Full access" permission. |
259+
| `setup newsletter` shows "not yet verified" | Add the DNS records printed by the command, wait a few minutes, re-run. |
260+
| Subscribe form returns "Failed to subscribe" | Resend secret not set on Pages project. Re-run `setup newsletter` with a valid `CLOUDFLARE_API_TOKEN`, or run `bunx wrangler pages secret put RESEND_API_KEY --project-name <project>`. Verify with `bunx wrangler pages secret list --project-name <project>`. |
261+
| Subscribe form returns "Invalid request" | Request body is not valid JSON or missing `email` field. Check browser console for errors. |

0 commit comments

Comments
 (0)