claude-computer · the build side
My dev workflow
The rest of this site is about the machine. This page is about what I build on it, and how: the approach, the stack, the infrastructure, and the path a project takes from an idea to production. It is written for anyone working the same way — one developer, a Mac, a Claude Max subscription, and Claude Code in the terminal doing most of the typing.
All of it is written down in the template as
docs/DEV-GUIDELINES.md,
and every project's CLAUDE.md points there, so Claude builds each new project the same way without
being told twice. It is prescriptive on purpose. A project may deviate, but only by writing down
why in its own CLAUDE.md.
On this page 5 sections
The approach#
Claude does the work; I keep three jobs. I authenticate — every login, every password, every consent screen. I set the mode — auto, never bypass. And I decide: what to build, what good looks like, and whether it ships. Everything else is a conversation.
Fewer choices, made once. Two languages, one tool per job, one database engine in three forms. Every choice that is already made is one Claude doesn't have to guess at, and one I don't have to review.
- A brain per projectevery repo has a
CLAUDE.md(what it is, how to run it) and aTASKS.md(Now / Next / Later / Done) - Scripts over proseanything deterministic becomes a script with
--helpand exit codes; a slash command only orchestrates them - CLI first, MCP lasta thin wrapper in
bin/per service costs one paragraph of context; an MCP server costs its schemas every session - One session per worktreeparallel work gets its own git worktree (
wt <branch>); never two sessions in one working tree - Small commitsconventional commits, one concern each; a
gitleaksscan runs before every commit as the backstop - Ask before the irreversibleClaude asks before deleting, publishing, spending money or touching a credential — and does the rest without asking
- Long tasks get a dashboardmore than five steps gets a live progress page first, updated after every step
- Questions don't stop the worka question goes on the dashboard with a proposed default, and the work carries on with whatever doesn't depend on it
- Decisions are written down
DECISIONS.mdin each project, append-only, one line per decision with the reason - Nothing reads a secret at importbuilds, tests and type checks run without keys; a secret is read where it's used
The stack#
Two languages#
Python and TypeScript, and nothing else unless a platform forces it — Swift for a Mac-only app that needs deep OS hooks.
- Version manager
misemise - Packages
uv— no pip, no poetrypnpm— no npm, no yarn - Lint + format
ruff check+ruff formatbiome - Typestype hints +
pyrightstrict: true - Tests
pytestvitest; Playwright for end-to-end on web apps - One-off scripts
uv runwith an inline header (PEP 723)pnpm dlx/tsx
Claude reads both by symbol, not by text: the official pyright-lsp and typescript-lsp plugins
give it definitions, references and the type errors after each edit.
Four targets#
Web app
Next.js + Tailwind + shadcn/ui; Hono on Workers for an API alone; FastAPI when it's Python
Cloudflare Workers through OpenNext; Python APIs on Fly.io or Railway
Mobile app
Expo (React Native) + Expo Router + NativeWind
EAS Build and EAS Submit
CLI or TUI
Python: Typer + Rich, Textual for a TUI
uv tool install; PyPI or a private Homebrew tapDesktop app
Tauri v2 with a TypeScript front end, no hand-written Rust
a signed, notarized
.dmgbuilt by GitHub Actions
The same answer every time#
- DatabaseSQLite everywhere: a plain file, Cloudflare D1, or Turso — same schema and ORM, so moving is a connection string
- ORM
drizzlein TypeScript;SQLModelorsqlite-utilsin Python - Auth
better-auth, on the same database; never hand-rolled - Product analyticsPostHog — events, session replay, flags and errors; self-hosted Bugsink when it must stay private
- Marketing analyticsGA4 through Google Tag Manager, kept in an
analytics.yamlthatanalyticsapplies; consent banner first - EmailResend
- Domains and DNSCloudflare; every public domain redirects
http://tohttps://, checked byhttps-check - CIGitHub Actions on every push: lint → test → build
- Healthevery deployed web app answers
GET /api/health— version, missing variables, database state - ContainersDocker only for Python services and self-hosted tools on a headless box
- Documents
.docx,.xlsx,.pptxand PDFs are generated from code and checked with headless LibreOffice - Headless browserPlaywright's Chromium, one pinned version for the whole fleet; Chrome stays the human's browser
The infrastructure#
Nothing here needs a server I look after by hand. Cloudflare runs what's public, GitHub holds everything, and the boxes on my tailnet are managed by Claude over SSH.
GitHub
required
a private repo per project, Actions for CI, the PRs Claude opens
Cloudflare
requiredfor web apps
Workers and Pages to run it, D1 for data, R2 for files, DNS for domains
sops + age
required
every key in one encrypted file in my own repo; no secrets service
Tailscale
requiredfor more than one machine
how a laptop reaches the headless boxes
PostHog
optional
product analytics, replay, flags and errors
Google
optional
GA4 and Tag Manager for public sites
Turso
optional
SQLite off Cloudflare, or one database per product
Expo EAS
optional
mobile builds and store submissions
A headless box
optional
Bugsink and other self-hosted containers, reached over Tailscale
Telegram
optional
one line when a long job finishes or Claude is waiting on me
Secrets never touch a project. A key lives in the encrypted store as <name>.<field>. Locally,
a project runs under secrets exec --only NAME,… -- <cmd>, or direnv loads it from .envrc; there
is no .env with a key in it. In a deploy, it goes in with wrangler secret put, EAS secrets or
GitHub Actions secrets, set from the store and never pasted.
My machines — a laptop, a Mac Studio running a local model, a Mac Mini, a Hetzner VPS and a Raspberry Pi — are listed on What's inside. Yours will differ; the workflow doesn't depend on them.
From idea to production#
/new-appI say what it does and who it's for. Claude proposes a target and a name, runsnew-app <type> <name>— the stack's own scaffolder, then the template'sCLAUDE.md,TASKS.md,justfile,docs/and secret scan — and creates a private GitHub repo. It writes the brain: the stack chosen, any deviation and why, and a first## Nowof three to five tasks.Build
In~/projects/<name>, one Claude session per worktree.just setup,just test,just lint,just runmean the same thing in every repo. Anything longer than five steps gets a dashboard first.Document
docs/is Markdown with front matter, one file per topic.docs-buildchecks every link and anchor and buildsllms.txtfor agents and one offline page for people;just lintfails when the docs are stale.Ship
just deploy. Where there are migrations, deploys are by hand and migrate first; sites without migrations deploy on push tomain. The deploy is checked against/api/health.Go live
https-check <domain>must pass.analytics setupandanalytics syncbuild the GA4 property and the Tag Manager container fromanalytics.yaml;analytics publishonly when I say so./graduateonce it's in production, it moves to~/products/<name>, itsCLAUDE.mdgains a Production section (where it runs, how to deploy and roll back, where errors go), and the weekly/reviewre-runshttps-checkon every live domain.
Client work follows the same path one level down: each client is a private repo under
~/clients/<name>, and the code for that client is a separate repo inside it, made with
new-app <type> <name> --dir ~/clients/<client>.
Taking it#
The guidelines are one file, and nothing in them depends on my machines. Copy
docs/DEV-GUIDELINES.md
into your own setup and point your projects' CLAUDE.md at it, or take the whole
template and get new-app, docs-build,
dashboard, analytics and https-check with it. Change what doesn't fit — and write down why.