A mobile-first PWA application for tracking any kind of session. Written in TypeScript and React and backed by Supabase. Check it out live at https://timer-cx7.pages.dev/.
Built as an experiment in agentic coding workflows; 100% of the application code written by Claude.
The process that created this app was based on Anthropic's autonomous coding demo code. The primary documents that generated the app were the app spec and feature list. The prompts that were used in the original development loop were initializer_prompt.md and coding_prompt.md.
To continue development and add features, I adapted these prompts into a "coding task" skill. For each chunk of work, I create a new file in work_specs. The agent then creates a PR using the coding skill and the spec.
This workflow made it possible to iterate in parallel in worktrees extremely quickly. (See the start-work-branch script.) Each Claude Code instance runs in a container with the skip-permissions flag. Crucially, the agent uses playwright to test its changes in the browser and is instructed to iterate until it satisfies the relevant test cases (see specs).
For more details on the development workflow and on the app itself, including step-by-step instructions for local development, see the rest of this README - the rest of this text is AI-generated.
🤖🤖🤖
- Frontend: React 18+ (Functional components, Hooks)
- Build: Vite
- Backend/DB: Supabase (Auth, PostgreSQL, Realtime)
- State: Zustand
- Styling: Tailwind CSS (monochrome theme)
- PWA: vite-plugin-pwa
- Icons: Lucide React
./init.shOr manually:
npm install
npm run devThe app runs at http://localhost:5173.
Create a .env file for Supabase integration:
VITE_SUPABASE_URL=your-supabase-url
VITE_SUPABASE_PUBLISHABLE_DEFAULT_KEY=your-supabase-publishable-key
The app works in guest-only mode without Supabase credentials.
Run Supabase locally instead of using a remote instance. Requires the Supabase CLI and Docker.
supabase startThis starts Supabase containers on the host and applies the migration in supabase/migrations/. Relevant ports:
| Service | Port |
|---|---|
| API | 54321 |
| Database | 54322 |
| Studio | 54323 |
| Inbucket | 54324 |
After starting, generate a .env.local that points to the local instance:
scripts/setup-local-env.sh.env.local overrides .env automatically (and is gitignored).
The project includes a Docker setup for running Claude Code with Playwright in a container, connected to local Supabase on the host.
- Docker Desktop
- Supabase CLI installed on the host
ANTHROPIC_API_KEYset in your environment (or in theclaude-authvolume)
-
Start local Supabase on the host:
supabase start
-
Generate
.env.localfor Docker networking:scripts/setup-local-env.sh
This writes
.env.localwithVITE_SUPABASE_URL=http://host.docker.internal:54321so the Vite app inside the container can reach Supabase on the host. -
Build the container:
docker build -f claude.Dockerfile -t session-timer-claude . -
Run it:
scripts/claude-in-docker.sh
This maps port 5173, mounts the project at
/app, and addshost.docker.internalfor host access. -
Inside the container, start Claude Code:
scripts/invoke-claude.sh
host.docker.internalresolves to the Docker host from inside the container (native on macOS/Windows, added via--add-hoston Linux).- Vite loads
.env.localat dev server startup, overriding the remote.envvalues. - The Playwright browser (inside the container) hits the Vite dev server (also inside the container), which talks to Supabase on the host.
The init.sh script handles the full setup. When run inside the Docker container it detects host.docker.internal and generates .env.local automatically:
./init.shsrc/
components/ # Reusable UI components
pages/ # Timer, Settings, History screens
stores/ # Zustand state management
services/ # Supabase client, audio, storage
hooks/ # Custom React hooks
assets/ # Static assets (audio files, icons)
MIT