Browser automation for AI agents. Gives Claude Code (and other AI coding tools) autonomous access to a real Firefox browser so they can verify their own work, debug UI issues, and test web apps without asking you what's on screen.
You're building a web app with an AI agent. Instead of you being the middleman ("what does the page look like?", "is the button there?", "any errors?"), the agent launches Firefox, navigates to your app, reads the page via ARIA snapshots, clicks buttons, fills forms, checks for console errors and failed network requests, takes screenshots, and verifies everything works. Autonomously.
You: "build a signup page and make sure it works"
Agent: *writes the code*
Agent: firecode browse app navigate "http://localhost:3000/signup"
Agent: firecode snapshot app
→ sees textbox "Email" [ref=e3], button "Sign Up" [ref=e5]
Agent: firecode browse app fill e3 "[email protected]"
Agent: firecode browse app click e5
Agent: firecode snapshot app
→ sees "Welcome! Check your email."
Agent: firecode console app
→ No console errors
Agent: firecode network app
→ No failed requests
Agent: "signup page is working, no errors"
Prerequisites: Node.js 20+ and pnpm (npm install -g pnpm).
git clone https://github.com/samoht9277/firecode.git
cd firecode
./install.shgit clone https://github.com/samoht9277/firecode.git
cd firecode
./install.ps1The installer builds the CLI, downloads the Playwright Firefox browser, puts firecode on your PATH, and installs the Claude Code skill. Pass --no-skill (bash) or -NoSkill (PowerShell) to skip the skill.
If
firecodeisn't found after install, open a new terminal (the installer added a directory to your PATH). On macOS/Linux that's~/.local/bin— make sure it's on your PATH.
Using it with Claude Code: the installer symlinks the skill into ~/.claude/skills/firecode. In any Claude Code session, just ask Claude to verify something in the browser (e.g. "open localhost:3000 and check the signup flow works") and it'll use firecode automatically.
If you'd rather not run the script:
pnpm install
pnpm build
pnpm --filter @firecode/server exec playwright install firefox
# put packages/cli/dist/index.js on your PATH as `firecode`, e.g.:
ln -sf "$(pwd)/packages/cli/dist/index.js" ~/.local/bin/firecode
# (optional) install the Claude Code skill:
ln -sfn "$(pwd)/skills/firecode" ~/.claude/skills/firecode./uninstall.sh # macOS / Linux
./uninstall.ps1 # Windows# Start Firefox (auto-starts in headless mode if you skip this)
firecode start
# Navigate to your app
firecode browse main navigate "http://localhost:3000"
# See the page structure
firecode snapshot main
# Interact using ref IDs from the snapshot
firecode browse main click e4
firecode browse main fill e5 "[email protected]"
# Check for problems
firecode console main # JS errors, warnings
firecode network main # failed HTTP requests
firecode screenshot main # visual check
# Done
firecode stop| Command | Description |
|---|---|
firecode start [--headless] |
Launch Firefox and start the server |
firecode stop |
Shut down (force kills if unresponsive) |
firecode status |
Show server state and open pages |
| Command | Description |
|---|---|
browse <page> navigate <url> |
Go to a URL (creates page if needed) |
browse <page> click <ref> [--force] |
Click an element by ref ID |
browse <page> fill <ref> <value> |
Clear and fill a text input |
browse <page> type <ref> <text> |
Type text character by character |
browse <page> select <ref> <value> |
Select a dropdown option |
browse <page> hover <ref> |
Hover over an element |
browse <page> click-text "<text>" |
Click by visible text (no snapshot needed) |
browse <page> keyboard <key> |
Press a key (ArrowRight, Enter, Tab, etc.) |
browse <page> scroll down|up|<ref> |
Scroll the page or to an element |
browse <page> evaluate "<js>" |
Run JavaScript and get the result |
browse <page> viewport mobile|tablet|desktop|<w> <h> |
Set viewport size |
browse <page> reload |
Refresh the page |
browse <page> back / forward |
Browser history navigation |
browse <page> wait <ms> |
Wait for a duration |
browse <page> wait-for "<text>" |
Wait for text to appear |
browse <page> wait-idle |
Wait for network to be idle |
browse <page> assert-text "<text>" |
Check if text exists (fails if not) |
| Command | Description |
|---|---|
snapshot <page> |
ARIA accessibility tree with ref IDs |
screenshot <page> [path] |
Capture PNG |
screenshot <page> [path] --diff <baseline> |
Pixel-level visual comparison |
text <page> |
Visible text content (lighter than snapshot) |
console <page> [--clear] |
Browser console logs |
network <page> [--all] [--clear] |
Network requests (failures by default) |
cookies <page> |
Page cookies |
storage <page> [--session] [--clear] |
localStorage/sessionStorage |
pdf <page> [path] |
Export page as PDF (headless only) |
| Command | Description |
|---|---|
record start <page> |
Start recording actions |
record stop <page> |
Stop and show captured steps |
record save <page> <path> |
Save recording to JSON |
replay <page> <path> |
Replay a saved recording |
| Command | Description |
|---|---|
test [--target unstaged|branch|changes] |
Generate and run tests from git diff |
test --base-url <url> |
Set app URL (default: localhost:3000) |
test -y |
Skip plan review |
firecode snapshot returns a YAML-like accessibility tree. Interactive elements get [ref=eN] tags:
- navigation:
- link "Dashboard" [ref=e1]
- link "Settings" [ref=e2]
- main:
- heading "Profile" [ref=e3]
- textbox "Name" [ref=e4]
- button "Save" [ref=e5]
- button [ref=e6]:Use refs to interact: firecode browse main click e5 clicks "Save". Unnamed elements (like buttons with HTML content) also get refs.
Refs are tied to the last snapshot. If the page changes, take a new snapshot.
| Preset | Size |
|---|---|
mobile |
375x812 |
tablet |
768x1024 |
desktop |
1920x1080 |
desktop-hd |
3840x2160 |
Or custom: firecode browse main viewport 1440 900
# Take a baseline
firecode screenshot app /tmp/before.png
# Make changes, then compare
firecode screenshot app /tmp/after.png --diff /tmp/before.png
# → "Changed: 2.3% (1847/80000 pixels differ)"
# → Diff image saved to /tmp/after-diff.pngIf the server isn't running when you run a command, it auto-starts in headless mode. No need to manually firecode start every time.
firecode/
packages/
server/ Playwright Firefox + Fastify HTTP API
cli/ Commander.js CLI
testgen/ Git diff → test generation
skills/
firecode/ Claude Code skill instructions
Single Firefox instance, one OS window with tabs. The CLI talks to the server over HTTP. State file at ~/.firecode/server.json.
- Playwright (Firefox)
- Fastify
- Commander.js
- pixelmatch
- TypeScript, pnpm, turbo
Because we're Firefox loyalists. No Chromium fallback, no browser selection flag.
Each firecode server is isolated by instance. To run two Claude sessions against firecode on the same machine without collisions, set FIRECODE_INSTANCE per session:
FIRECODE_INSTANCE=projectA firecode browse main navigate "http://localhost:3000"
FIRECODE_INSTANCE=projectB firecode browse main navigate "http://localhost:4000"Each instance gets its own Firefox process, port, auth token, and pages.
MIT — see LICENSE.