electric-agent
v0.1.5
Published
CLI tool that turns natural-language app descriptions into running reactive Electric SQL + TanStack DB applications
Readme
create-electric-app
CLI tool that turns natural-language app descriptions into running reactive Electric SQL + TanStack DB applications using Claude as the code generation engine.
electric-agent new "a project management app with boards and tasks"How It Works
The agent follows a multi-phase pipeline to go from a text description to a running app:
┌─────────────────────────────────────────────────────────────────────┐
│ electric-agent new │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 1. SCAFFOLD │
│ │
│ Clone KPB template ──► Overlay Electric/Drizzle files ──► │
│ Merge deps (TanStack DB, Electric, Drizzle, Vitest) ──► │
│ Patch Vite config ──► Install dependencies │
│ │
│ Result: runnable TanStack Start project with Electric infra │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. PLAN (Planner Agent — Claude Opus) │
│ │
│ Reads playbooks (electric-quickstart, tanstack-db) ──► │
│ Generates PLAN.md with data model + phased tasks │
│ │
│ ┌─ User reviews plan ─┐ │
│ │ approve / revise / cancel │
│ └──────────────────────┘ │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 3. GENERATE (Coder Agent — Claude Sonnet) │
│ │
│ Executes PLAN.md tasks in order, reading playbooks just-in-time: │
│ │
│ Phase 1: Schema ──────── Drizzle pgTable + Zod derivation │
│ │ + drizzle-kit generate/migrate │
│ ▼ │
│ Phase 2: Collections ─── Electric collections + shape proxies │
│ │ + mutation API routes │
│ ▼ │
│ Phase 3: Mutations ───── Server-side Drizzle transactions │
│ │ + parseDates for JSON round-trip │
│ ▼ │
│ Phase 4: UI ──────────── React components + useLiveQuery │
│ │ + ClientOnly wrappers for SSR safety │
│ ▼ │
│ Phase 5: Testing ─────── Zod schema smoke tests (no Docker) │
│ + collection insert validation │
│ + JSON round-trip tests │
│ │
│ After each phase: build tool runs pnpm build + check + test │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. RUN │
│ │
│ electric-agent up ──► Docker (Postgres + Electric + Caddy) ──► │
│ drizzle-kit migrate ──► pnpm dev │
└─────────────────────────────────────────────────────────────────────┘Web UI
The web UI provides a browser-based alternative to the CLI with real-time streaming, collapsible tool execution logs, and persistent session history.
electric-agent serve # Start at http://127.0.0.1:4400
electric-agent serve --open # Start and open browser
electric-agent serve -p 8080 # Custom portThe web UI uses durable-streams to persist and stream all events over SSE. Each session gets its own stream, so you can close the browser and reconnect without losing context. Tool executions are collapsible — click to expand the full input/output.
Sandbox Mode (Docker)
Run sessions inside isolated Docker containers instead of on the host machine. Each session spawns a container from the electric-agent-sandbox image — all scaffold, planning, coding, and builds happen inside the container. The generated app's dev server is port-mapped to the host, and a "Preview App" link appears in the web UI.
See Running with Sandbox Mode in the Development section for setup instructions.
Headless Mode
Run the agent via an NDJSON protocol on stdin/stdout — useful for CI/CD, Docker, or programmatic usage:
electric-agent headlessThe first line on stdin must be a JSON config object:
{"command":"new","description":"a todo app","projectName":"my-todo"}Or for iteration on an existing project:
{"command":"iterate","projectDir":"/path/to/project","request":"add dark mode"}Events stream as JSON lines on stdout (same EngineEvent types as the web UI). When the agent needs user input (plan approval, clarification), it emits a gate event and blocks until you send a response on stdin:
{"gate":"approval","decision":"approve"}
{"gate":"clarification","answers":["answer1","answer2"]}
{"gate":"revision","feedback":"change the schema"}
{"gate":"continue","proceed":true}Example with Docker:
docker run -i --rm \
-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY \
electric-agent-sandbox \
electric-agent headlessThen paste the config JSON and interact with gates as needed.
Iteration Mode
After the initial build, use electric-agent iterate for conversational changes:
┌─────────────────────────────────────────────────────────────────────┐
│ electric-agent iterate │
│ │
│ iterate> add a sidebar with project navigation │
│ │ │
│ ├── Read relevant playbooks (live-queries, etc.) │
│ ├── Add iteration section to PLAN.md │
│ ├── Implement changes (following Drizzle Workflow order) │
│ ├── Run build + check + test │
│ └── Done │
│ │
│ iterate> add dark mode │
│ │ │
│ └── ... (same flow) │
└─────────────────────────────────────────────────────────────────────┘Data Flow
Drizzle pgTable()
│
▼
drizzle-kit generate ──► SQL migrations (REPLICA IDENTITY FULL auto-appended)
│
▼
drizzle-kit migrate ──► Postgres
│
▼
Electric sync service ◄── watches Postgres WAL
│
▼
/api/<table> proxy route ──► forwards Electric shape stream to client
│
▼
TanStack DB collection ──► validates with Zod selectSchema
│
▼
useLiveQuery() ──► reactive UI (auto-updates on sync)
│
▼
collection.insert/update/delete ──► validates ALL fields client-side
│ (id + timestamps required)
▼
/api/mutations/<table> ──► parseDates() ──► Drizzle transaction ──► Postgres
│
▼
Returns { txid } for optimistic update correlationGitHub Integration
Every generated project is initialized as a local git repo automatically. GitHub integration is optional — you can checkpoint locally without ever connecting to GitHub.
Flows
New project (no GitHub):
- Describe your app → agent scaffolds + generates code → local git repo created with initial commit
- Checkpoint — commit all current changes locally (works without GitHub)
- Publish — prompts for a repo name and visibility (public/private), creates a new GitHub repo on your account, and pushes the code (requires GitHub PAT)
- Open PR — creates a pull request from the feature branch (appears after publishing)
Resume from GitHub:
- From the home page, click Resume from GitHub → pick a repo from your account
- The repo is cloned locally, and if a
PLAN.mdis detected, the agent enters iterate mode with context
GitHub Personal Access Token (PAT)
A classic GitHub PAT is required for publish, PR, resume, and repo listing. Checkpoint (local commit) works without it.
Required scopes:
repo— create repos, push code, create PRsread:user— verify token and read your GitHub username
Create a token at: https://github.com/settings/tokens/new?scopes=repo,read:user
Enter the token in the web UI Settings panel under GitHub → Personal Access Token. The token is validated on save via gh auth login.
Git Operations
| Action | What it does | Requires GitHub PAT? |
|--------|-------------|---------------------|
| Checkpoint | git add -A + git commit with auto-generated or custom message | No |
| Publish | Creates a GitHub repo via gh repo create, pushes on a feature branch | Yes |
| Open PR | Pushes latest commits and creates a PR via gh pr create | Yes |
| Resume from GitHub | Clones a repo via gh repo clone, creates a new session | Yes |
Guardrail Hooks
The coder agent runs with guardrail hooks that catch common mistakes before they reach the codebase:
| Hook | When | What it does |
|------|------|-------------|
| write-protection | PreToolUse (Write/Edit) | Blocks writes to config files (vite.config.ts, docker-compose.yml, vitest.config.ts, etc.) |
| import-validation | PreToolUse (Write/Edit) | Catches hallucinated imports (wrong package paths, non-existent modules) |
| migration-validation | PreToolUse (Bash) | Auto-appends REPLICA IDENTITY FULL to migration SQL |
| dependency-guard | PreToolUse (Write/Edit) | Prevents removal of required dependencies from package.json |
| schema-consistency | PostToolUse (Write/Edit) | Warns when hand-written Zod schemas are detected (should use drizzle-zod) |
Custom MCP Tools
| Tool | Description |
|------|-------------|
| build | Runs pnpm build + biome check + pnpm test (if tests exist), returns errors |
| read_playbook | Reads a playbook skill (SKILL.md + references) |
| list_playbooks | Lists all available playbook skills |
Generated App Structure
my-app/
├── docker-compose.yml # Postgres + Electric + Caddy
├── Caddyfile # Reverse proxy (5173 → Vite 5174 + Electric 3000)
├── drizzle.config.ts # Drizzle Kit config
├── vitest.config.ts # Vitest with @/ alias
├── PLAN.md # Implementation plan (maintained across iterations)
├── drizzle/ # Generated SQL migrations
├── src/
│ ├── db/
│ │ ├── schema.ts # Drizzle pgTable definitions
│ │ ├── zod-schemas.ts # Derived via createSelectSchema/createInsertSchema
│ │ ├── collections/ # TanStack DB + Electric collections
│ │ ├── index.ts # Drizzle client
│ │ └── utils.ts # generateTxId + parseDates
│ ├── components/
│ │ └── ClientOnly.tsx # SSR-safe wrapper for useLiveQuery components
│ ├── routes/
│ │ ├── __root.tsx # HTML shell (always SSR'd)
│ │ ├── index.tsx # Home page (ssr: false)
│ │ ├── api/<table>.ts # Electric shape proxy routes
│ │ └── api/mutations/ # Drizzle transaction routes
│ └── lib/
│ └── electric-proxy.ts # Shape proxy helper
├── tests/
│ ├── helpers/
│ │ └── schema-test-utils.ts # generateValidRow, generateRowWithout
│ ├── schema.test.ts # Zod schema smoke tests
│ ├── collections.test.ts # Collection insert validation + JSON round-trip
│ └── integration/
│ └── data-flow.test.ts # Drizzle → Postgres → Zod (requires Docker)
└── _agent/ # Working memory (errors.md, session.md)CLI Commands
electric-agent new <description> # Create a new app
electric-agent new <desc> --name my-app # Custom project name
electric-agent new <desc> --no-approve # Skip plan approval
electric-agent iterate # Conversational iteration on existing app
electric-agent headless # NDJSON stdin/stdout mode (for Docker/CI)
electric-agent serve # Start web UI (http://127.0.0.1:4400)
electric-agent serve --sandbox # Start web UI with Docker sandboxing
electric-agent serve --open # Start web UI and open browser
electric-agent serve -p 8080 # Custom port
electric-agent serve --streams-port 5000 # Custom durable-streams port
electric-agent up # Start Docker + migrations + dev server
electric-agent down # Stop all services
electric-agent status # Show project progress
electric-agent --debug <command> # Enable debug loggingDevelopment
npm install # Install dependencies
npm run build # Compile TypeScript + Vite client → dist/
npm run build:server # Compile TypeScript only
npm run build:web # Build Vite client only
npm run check # Biome lint + format check
npm run check:fix # Auto-fix Biome issues
npx tsc --noEmit # Type-check without emittingRunning the Web UI
Build and start the server:
npm run build
npm run serve # http://127.0.0.1:4400Running with Sandbox Mode
Sandbox mode runs each session inside an isolated Docker container instead of on the host machine.
Build the sandbox image (one-time, rebuild after code changes):
npm run build:sandbox # Builds electric-agent-sandbox Docker imageStart the server in sandbox mode:
npm run serve:sandbox # http://127.0.0.1:4400Or equivalently:
npm run serve -- --sandbox
Authentication — the sandbox container needs access to the Claude API. Auth is resolved in this order:
ANTHROPIC_API_KEYenv var — set it before starting, or enter it in the web UI Settings panelCLAUDE_CODE_OAUTH_TOKENenv var — if you have an OAuth token directly- macOS Keychain (automatic) — if you've run
claude loginon macOS, the OAuth token is extracted from the Keychain automatically
On Linux, option 3 is not available — use an API key.
Working on the Web UI
For development with hot-reload, run these in separate terminals:
npm run dev # Terminal 1: tsc --watch (recompiles server on change)
npm run serve # Terminal 2: backend API + durable-streams (port 4400)
npm run dev:web # Terminal 3: Vite dev server with HMR (port 4401)Open http://127.0.0.1:4401 for development (Vite with hot-reload, proxies /api to the backend).
Open http://127.0.0.1:4400 for production-like mode (static build served by Hono).
Source Structure
src/
├── index.ts # CLI entry point (commander)
├── cli/ # Command implementations (new, iterate, serve, headless, up, down, status)
├── engine/ # Shared orchestration (used by CLI + web + headless)
│ ├── events.ts # EngineEvent union type — single source of truth
│ ├── orchestrator.ts # runNew() + runIterate() with callback-driven I/O
│ ├── message-parser.ts # SDK message → EngineEvent[] conversion
│ ├── cli-adapter.ts # OrchestratorCallbacks using readline (CLI mode)
│ └── headless-adapter.ts # OrchestratorCallbacks using NDJSON stdin/stdout
├── agents/
│ ├── planner.ts # Planner agent (Opus) — generates PLAN.md
│ ├── coder.ts # Coder agent (Sonnet) — executes plan tasks
│ ├── prompts.ts # System prompt builders
│ └── patterns.md # Code patterns + hallucination guards (injected into coder prompt)
├── tools/
│ ├── server.ts # MCP tool server wrapper
│ ├── build.ts # Build tool (pnpm build + check + test)
│ └── playbook.ts # Playbook reading tools
├── git/ # Git + GitHub CLI wrappers (checkpoint, publish, PR, clone)
├── hooks/ # Agent SDK guardrail hooks (6 hooks)
├── scaffold/ # KPB clone + template overlay + dep merge
├── working-memory/ # Session state + error log persistence
├── progress/ # CLI output + build result reporting
└── web/ # Web UI server + client
├── server.ts # Hono API server (REST + static files, local + sandbox modes)
├── docker.ts # Docker container lifecycle for sandbox mode
├── container-bridge.ts # Container stdout → durable stream bridge
├── infra.ts # Durable streams server lifecycle
├── gate.ts # Promise-based gate management for user decisions
├── sessions.ts # Session index (JSON file)
└── client/ # React SPA (built with Vite)
├── index.html
├── vite.config.ts
└── src/
├── main.tsx
├── App.tsx
├── hooks/useSession.ts
├── components/
└── lib/
template/ # Files overlaid onto scaffold
├── docker-compose.yml # Postgres + Electric + Caddy
├── vitest.config.ts # Vitest config
├── tests/helpers/ # Test utilities (schema-test-utils.ts)
├── src/db/ # Drizzle client, schema placeholder, utils (parseDates)
├── src/components/ # ClientOnly.tsx
└── src/lib/ # electric-proxy.tsPrerequisites
- Node.js >= 20
- Docker (for generated projects and sandbox mode)
ANTHROPIC_API_KEYenvironment variable, orclaude loginon macOS- GitHub CLI (
gh) — required for GitHub integration (publish, PR, resume)
Stack
- Electric SQL — real-time Postgres sync
- TanStack DB — reactive collections with optimistic mutations
- TanStack Start — full-stack React framework
- Drizzle ORM — type-safe Postgres schema and queries
- KPB — base project template
- Claude Agent SDK — agentic code generation
- Durable Streams — persistent event streaming for the web UI
- Hono — lightweight HTTP server for the web API
- Vite + React — web UI client
