# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Prerequisites

Node.js >= 22. Package manager is pnpm (enforced via `preinstall` script). Husky pre-commit hook runs `pnpm check`.

## Commands

```bash
pnpm dev               # Start dev server (localhost:4321)
pnpm build             # astro check + build + copy-sw (use this to verify changes)
pnpm build:production  # Production build (skips astro check, uses --mode production)
pnpm check             # Type check + Biome lint
pnpm check:fix         # Type check + Biome lint with auto-fix
pnpm stylelint         # Lint CSS/Astro styles
pnpm stylelint:fix     # Fix style issues
pnpm generate:icons    # Regenerate PWA icon assets
```

There are no automated tests. Verification is done via `pnpm build` (0 errors required) and the preview MCP tools.

## Architecture

Astro 6 site running in SSR mode (Node.js standalone adapter) with static output for most routes. Pure Astro components — no React/Vue/Svelte. TypeScript strict mode (`astro/tsconfigs/strictest`). Path alias `@/*` → `src/*`.

**Formatter/linter:** Biome (not ESLint/Prettier). Run `check:fix` after larger edits. Biome config disables some rules for `.astro` files (`useConst`, `useImportType`, `noUnusedVariables`, `noUnusedImports`).

**Markdown plugins:** `remarkObsidianLinks` (custom, `src/lib/remark-obsidian-links.mjs`) for `[[wiki-link]]` syntax, `rehypeExternalLinks` for `target="_blank"` on external links.

### Content Collections (`src/content.config.ts`)

Seven collections defined with Zod schemas:

| Collection | Loader | Path | Notes |
|---|---|---|---|
| `blog` | glob | `src/content/blog/posts/` | Series support (`seriesParent`, `seriesOrder`), tags, category ref, syndication URLs |
| `categories` | glob | `src/content/blog/categories/` | Referenced by blog posts |
| `notes` | glob | `src/content/notes/` | Short-form with optional cover image |
| `links_json` | file | `src/content/links/links.json` | JSON file with auto-generated IDs (`json/date/slug`) |
| `projects` | glob | `src/content/projects/project/` | Portfolio items with optional URL/GitHub links |
| `projects_categories` | glob | `src/content/projects/categories/` | Referenced by projects |
| `collections_photos` | glob | `src/content/photos/collections/` | Matches `**/index.{md,mdx}`; photos as JPG + JSON sidecar files in `img/` subdirs |

### Key Routing Patterns

- `/blog/[...slug]` — Blog posts use the full content path as slug (e.g. `2026/03/01/post-name`)
- `/og/blog/[...slug].png` — OG images generated server-side via Satori (`src/lib/og.ts`)
- `/rss/blog.xml`, `/rss/notes.xml`, `/rss/links.xml`, `/rss/photos.xml` — Separate RSS feeds per content type
- `/photos/collections/[...slug]` — Nested photo collections with breadcrumb navigation
- `/projects/[...slug]`, `/projects/category/[...slug]` — Project portfolio
- `/tags/[slug]` — Cross-content-type tag pages
- `/archives/` — Timeline of all content
- `/api/webmentions.json` — Webmention data endpoint

### Lib Utilities (`src/lib/`)

- `collections.ts` — Photo collection helpers: `collectionSlug()`, `buildCollectionPhotos()`, `buildBreadcrumbs()`, `getChildCollections()`
- `og.ts` — OG image generation using Satori (`buildArticleVNode`, `renderOgImage`)
- `webmentions.ts` — Fetch and filter webmentions from webmention.io (includes Mastodon/Bluesky validation)
- `photo-albums.ts` — Photo album organisation utilities
- `links.ts` — Link collection helpers
- `remark-obsidian-links.mjs` — Custom remark plugin for Obsidian-style `[[links]]`

### Scripts (`scripts/`)

- `mastodon-syndicate.mjs` — POSSE: scans blog/notes for posts without `syndication` field, posts to Mastodon, writes status URL back to frontmatter. Env vars: `MASTODON_BASE_URL`, `MASTODON_ACCESS_TOKEN`, `MASTODON_VISIBILITY`, `MASTODON_DRY_RUN`, `MASTODON_LIMIT`
- `vision.ts` — Uses Claude Vision API to auto-generate EXIF sidecars + metadata for photo collections. Requires `ANTHROPIC_API_KEY`
- `publish-all.sh` — Full deploy orchestration: rsync content to VPS → rebuild container → send webmentions → run mastodon-syndicate. Per-collection variants: `publish-blog.sh`, `publish-notes.sh`, `publish-links.sh`, `publish-photos.sh`, `publish-projects.sh`
- `new-post.sh`, `new-note.sh` — Content scaffolding templates
- `copy-sw.js` — Post-build service worker copy

### IndieWeb / Syndication

Blog posts support POSSE via `mastodon-syndicate.mjs`. After posting, the Mastodon status URL is written to frontmatter as `syndication: ["https://..."]`. The `SyndicationLinks` component renders `u-syndication` microformat links. Webmentions are fetched at build time from webmention.io and displayed via `WebMentions.astro`.

### Deployment

Multi-stage container build (`Containerfile`): Node 22 with pnpm for build, Node 22 slim for runtime. Runs `node dist/server/entry.mjs` on port 4321. Orchestrated via `compose.yml`. Deploy scripts rsync content to VPS and rebuild the container.

---

## Workflow

### 1. Plan First
- Enter plan mode for ANY non-trivial task (3+ steps or architectural decisions)
- If something goes sideways, STOP and re-plan immediately - don't keep pushing
- Write plan to `tasks/todo.md` with checkable items (not just TodoWrite tool)
- Check in before starting implementation

### 2. Subagent Strategy
- Use subagents liberally to keep main context window clean
- Offload research, exploration, and parallel analysis to subagents
- For complex problems, throw more compute at it via subagents

### 3. Self-Improvement Loop
- After ANY correction from the user: update `tasks/lessons.md` with the pattern
- Review `tasks/lessons.md` at session start
- Write rules that prevent the same mistake from recurring

### 4. Verification Before Done
- Never mark a task complete without proving it works
- Run `pnpm build` — must complete with 0 errors
- Use preview MCP tools to visually verify UI changes

### 5. Demand Elegance (Balanced)
- For non-trivial changes: pause and ask "is there a more elegant way?"
- Skip for simple, obvious fixes — don't over-engineer

### 6. Autonomous Bug Fixing
- When given a bug report: just fix it. Point at logs/errors, then resolve them.

## Core Principles

- **Simplicity First**: Make every change as simple as possible. Impact minimal code.
- **No Laziness**: Find root causes. No temporary fixes. Senior developer standards.
- **Minimal Impact**: Changes should only touch what's necessary.
