6.4 KiB
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
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 utilitieslinks.ts— Link collection helpersremark-obsidian-links.mjs— Custom remark plugin for Obsidian-style[[links]]
Scripts (scripts/)
mastodon-syndicate.mjs— POSSE: scans blog/notes for posts withoutsyndicationfield, posts to Mastodon, writes status URL back to frontmatter. Env vars:MASTODON_BASE_URL,MASTODON_ACCESS_TOKEN,MASTODON_VISIBILITY,MASTODON_DRY_RUN,MASTODON_LIMITvision.ts— Uses Claude Vision API to auto-generate EXIF sidecars + metadata for photo collections. RequiresANTHROPIC_API_KEYpublish-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.shnew-post.sh,new-note.sh— Content scaffolding templatescopy-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.mdwith 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.mdwith the pattern - Review
tasks/lessons.mdat 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.