# Agent Instructions Compact guidance for OpenCode sessions working on this repository. --- ## Quick Start ```sh npm install npm run dev # dev server at http://localhost:4321 npm run build # production build to ./dist/ npm run preview # serve production build locally npm run format # Prettier across entire codebase ``` **Never skip `npm install` after checkout.** Dependencies are not vendored. --- ## Architecture - **Framework**: Astro 5 with Tailwind CSS v4 (Vite plugin) - **Content**: Astro Content Collections — markdown files in `src/content/blog/` - **Schema**: `src/content.config.ts` — posts require `title`, `description`, `date`; optional `draft`, `tags` - **Build output**: `dist/` (gitignored). Never commit built files. - **Deployment**: Docker multi-stage build → `static-web-server` behind Traefik. `compose.yml` defines production service. --- ## Content Blog posts live in `src/content/blog/`. Frontmatter must match the schema in `src/content.config.ts`. ### Blog Post Style - **Voice:** First-person, direct, opinionated. No preamble, no "Hope this helps" closings. - **Opening:** Hook into a specific frustration or observation. Skip the generic intro. - **Headings:** - `##` — lede/subtitle (one per post, immediately after the title, punchy) - `###` — main sections - `---` — horizontal rule between sections - No `#` (H1) anywhere in the post body - **Closing:** End decisively. A statement, not a sendoff. - **Grammar:** Correct. No "Do you ever wondering", no "Let's get started!" - **Tone reference:** See the three most recent posts by date. ### Tag System #### Current Vocabulary | Tag | Description | | --------------------- | ------------------------------------------------------------------------- | | `linux` | Linux-specific content — distros, tools, fixes | | `flutter` | Flutter framework, mobile development | | `android` | Android-specific content, ADB, AVD | | `windows` | Windows-specific content, PowerShell, Win tooling | | `git` | Git commands, workflows, hosting | | `hardware` | Physical hardware, drivers, firmware quirks | | `ai` | AI-assisted development, LLM tools, agents | | `dev-setup` | Developer environment, tooling, workflow, DX | | `dart` | Dart language, Dart-specific libraries | | `go` | Go language | | `craftsmanship` | Software design, architecture, TDD, DDD, Clean Architecture, code quality | | `self-hosting` | Self-hosted services, VPS, infrastructure | | `distributed-systems` | Distributed data, sync, CRDTs, consensus | | `misc` | Catch-all for posts that don't fit elsewhere | #### Per-Post Tags (current state) | Post | Tags | | -------------------------------------------- | ------------------------------ | | stop-stashing-use-git-worktree | `git`, `dev-setup` | | vibe-coding-still-needs-a-craftsman | `ai`, `craftsmanship` | | building-my-own-self-hosted-music-library | `linux`, `self-hosting` | | building-load-testing-script-with-claude | `go`, `ai` | | crdt-conflict-free-replicated-data-types | `dart`, `distributed-systems` | | fix-adb-unsufficient-permission-linux | `linux`, `android` | | fix-infinix-air-pro-plus-quad-speakers-linux | `linux`, `hardware` | | fix-infinix-air-pro-plus-screen-color | `windows`, `linux`, `hardware` | | flutter-android-emulator-not-showing | `flutter`, `android` | | flutter-clean-architecture | `flutter`, `craftsmanship` | | immutable-workstation-fedora-kinoite | `linux`, `dev-setup` | | kuwot (draft) | `flutter`, `dart` | | remap-copilot-key-infinix-air-pro-plus | `linux`, `hardware` | | sign-github-commit-on-windows | `git`, `windows` | | using-direnv-in-powershell-on-windows | `windows`, `dev-setup` | | welcome | `misc` | #### Rules for Tags **When tagging a post:** - Use 2–4 tags per post. More than 4 is a sign you're being too specific. - Prefer tags from the existing vocabulary above. - A tag should describe what the post _is about_, not every concept it _mentions_. **When adding a new tag:** - Ask: will this tag apply to at least one other existing post, or is it clearly a category this blog will write about again? - If yes: add it to the vocabulary table above and apply it. - If no: fold into an existing tag or leave it out. - Do not add tags for topics covered once and unlikely to recur (e.g. `music`, `testing`, `open-source`). - Document new tags in this file. --- ## Styling - Tailwind v4 via `@tailwindcss/vite`. Global styles in `src/styles/global.css`. - Custom theme: Fira Code font, neutral-900 background, neutral-300 text. - Expressive Code (`ec.config.mjs`) — code blocks use `dark-plus` theme, no border radius, transparent shadows. - Pagefind integration provides search index built at `npm run build`. --- ## Prettier **Format before committing.** The config (`.prettierrc`) uses: - `prettier-plugin-astro` — parses `.astro` files - `prettier-plugin-organize-imports` — sorts imports automatically `npm run format` writes changes in place. No separate lint step exists. --- ## Production Caching `sws.config.toml` (mounted into Docker container) defines cache headers: - HTML: `no-cache, must-revalidate` — always revalidate - Astro assets (`/_astro/*`): `max-age=31536000, immutable` — fingerprinted, cache forever - Images/fonts: `max-age=604800` — 1 week Do not change without understanding fingerprinting behavior. --- ## Docker & Deployment - **Build**: `docker build -t site .` (multi-stage: node:24-alpine → static-web-server) - **Run**: `docker compose up -d` (uses external `traefik-proxy` network) - **Port**: Container serves on port 80; Traefik routes `fiatcode.dev` and `www.fiatcode.dev` - **Config**: `sws.config.toml` mounted at `/config.toml`; `SERVER_CONFIG_FILE` env var points to it. - **No build args or secrets** — site is fully static. --- ## Important Constraints - **No test suite** — no `vitest`, `jest`, or similar configured. - **No CI/CD** — no GitHub Actions workflows in repo. - **No ESLint** — only Prettier for formatting. - **Single package** — not a monorepo; all code lives under `src/`. - **Node version** — Docker uses Node 24; local dev should match (nvm recommended). - **Generated files** — `.astro/` directory is generated; never edit files there. - **Content assets** — images in `public/images/`; reference with absolute paths like `/images/piko-1.webp`. --- ## Common Tasks ### Add a blog post 1. Create `src/content/blog/.md` with required frontmatter (title, description, date, draft: false, tags) 2. Run `npm run dev` to verify rendering 3. Run `npm run format` 4. Commit with conventional commit: `feat: add post ` ### Update tag vocabulary Edit the "Current Vocabulary" table and "Per-Post Tags" matrix in this file. Keep both in sync. ### Change site metadata - Site URL: `astro.config.mjs` → `site` field - Title/description per-page: in the page/layout component frontmatter - Favicon/manifest: replace files in `public/` ### Modify Tailwind theme Edit `src/styles/global.css` — `@theme` block and `@layer base` utilities. --- ## Gotchas - **Port conflict**: Astro dev defaults to port 4321. Change via `npm run dev -- --port <n>` if needed. - **Content cache**: Astro caches content collections in `.astro/data-store.json` (large, gitignored). Delete if content changes aren't reflected. - **Image paths**: In markdown, use absolute paths starting with `/` (e.g., `![alt](/images/file.webp)`). Relative paths break in production. - **Date format**: Use ISO 8601 with timezone offset (e.g., `2024-03-18T14:16:19+07:00`). Astro's date parser is strict. - **Draft posts**: Set `draft: true` to exclude from builds. `draft` field is optional in schema but recommended for clarity. - **Prettier on Astro files**: Must have the plugin installed; otherwise formatting corrupts `.astro` syntax. Verified in `devDependencies`. --- ## References - `astro.config.mjs` — integrations (sitemap, pagefind, expressive-code) - `src/content.config.ts` — content collection schema and loader - `sws.config.toml` — production server and cache headers - `compose.yml` — Docker Compose production service - `README.md` — basic commands and tech stack