Source code for my personal site—a portfolio, blog, and space for creative experiments.
This project is developed and maintained with assistance from Claude Code. Project-level configuration exists in the .claude/ directory.
See CLAUDE.md for development guidance and available skills.
- Node.js 22+ (see Dockerfile for exact version) (install)
- npm (or equivalent package manager,
npmcomes with Node.js) - Docker for building production images (install)
- gcloud CLI for running deployment scripts (install)
npm install
npm run devOpen localhost:4321.
| Command | Description |
|---|---|
npm run dev |
Development server with hot reload |
npm run build |
Production build to ./dist/ |
npm run preview |
Preview production build locally |
npm run lint |
ESLint (add --fix to auto-fix) |
npm run check |
Astro + TypeScript type checking |
npm run fmt |
Format with Prettier |
npm run fmt:check |
Check formatting without modifying |
| Layer | Technology |
|---|---|
| Framework | Astro 5 (SSR, Node.js adapter) |
| Styling | Tailwind CSS 4 |
| Content | MDX |
| Syntax Highlighting | Shiki (custom CSS variables theme) |
| Animation | p5.js |
| Deployment | Docker → Google Cloud Run |
src/
├── components/ # Astro components, organised by domain
├── config/ # Constants (themes, navigation, DOM selectors)
├── content/ # MDX content (blog posts, uses pages)
├── layouts/ # Page layouts
├── lib/ # Internal libraries (OG image generation, Shiki theme)
├── pages/ # File-based routing
├── plugins/ # Rehype plugins
├── scripts/ # Client-side TypeScript
├── styles/ # Global CSS and design tokens
└── utils/ # Pure utility functions
Static assets in public/. Deployment config in .github/workflows/.
| Route | Page | Description |
|---|---|---|
/ |
src/pages/index.astro |
Home with hero and blog list |
/blog |
src/pages/blog/index.astro |
Blog index |
/blog/[slug] |
src/pages/blog/[slug].astro |
Individual posts (SSR) |
/about |
src/pages/about.astro |
About page |
/uses |
src/pages/uses/index.astro |
Redirects to /uses/now |
/uses/[tab] |
src/pages/uses/[tab].astro |
Uses tabs (then, now, next) |
/rss.xml |
src/pages/rss.xml.ts |
RSS feed |
/sitemap.xml |
src/pages/sitemap.xml.ts |
XML sitemap |
/og/*.png |
src/pages/og/[...slug].png.ts |
Dynamic OG images |
/robots.txt |
public/robots.txt |
Crawler directives |
/llms.txt |
public/llms.txt |
AI context file |
Every push triggers deployment via GitHub Actions:
- main → Production (100% traffic)
- other branches → Preview (tagged, no traffic)
| Secret | Description |
|---|---|
GCP_PROJECT_ID |
Google Cloud project ID |
GCP_SA_KEY |
Service account JSON key |
Requires gcloud CLI authenticated to the project.
./scripts/url.sh # Get production URL
./scripts/logs.sh # View Cloud Run logs
./scripts/revisions.sh # List revisions
./scripts/cleanup-previews.sh # Remove preview tagsThe project includes MCP server configuration in .mcp.json. Some servers require authentication:
gcloud — Google Cloud operations
gcloud auth login
gcloud auth application-default login
gcloud config set project YOUR_PROJECT_IDgithub — GitHub API access (create PAT with repo scope)
This project uses direnv to manage environment variables. Setup:
-
Install direnv:
# macOS brew install direnv # Debian / Ubuntu sudo apt install direnv # Fedora sudo dnf install direnv # Arch sudo pacman -S direnv
-
Add the hook to your shell config (once):
# For bash (~/.bashrc) eval "$(direnv hook bash)" # For zsh (~/.zshrc) eval "$(direnv hook zsh)"
-
Restart your shell or source your config
-
Create
.envrcin the project root:export GITHUB_PAT="your_token_here"
-
Allow direnv to load the file:
direnv allow
Note: If you prefer a different environment management solution (dotenv, 1Password CLI, etc.), ensure
GITHUB_PATis available in your shell when running Claude Code.
For a self-hosted GitHub MCP alternative using Docker, see the GitHub MCP installation guide.