One window for the whole loop. Write the code, run it, see it render, read the diff, and steer the agents doing half the work — without leaving.
Grove is a macOS dev environment built on a single bet: the pieces of your day are not separate apps, they're one loop. You write something, you run it, you look at what changed, you check it in a browser, you hand part of it to an agent, you read what the agent did. Every tool boundary you cross to do that is a tax — a new window, a lost cwd, a re-explained context.
So Grove collapses the loop into one surface. Any pane can be a terminal, a Claude session, a file tree, a diff, or a live browser. They share a working directory, a git branch, and an idea of what you're doing. The terminal remembers each command's full context; the browser is a real Chromium view your agent can drive; the diff is always the one you're about to ship.
The goal is that you open Grove, focus, and stay there.
Status: pre-alpha, macOS only. Everything under What ships today works now. Where it's going is the honest roadmap.
A normal day in a normal terminal:
$ npm run dev → switch to browser, navigate to localhost:5173
$ npm test → squint at a wall of scrollback
$ git diff → squint harder
$ vim src/App.tsx:42 → context switch to an editor
→ paste all of the above into a chat window
In Grove that's one window. Split a pane, point it at the dev server, and it renders next to the terminal that started it. Click src/App.tsx:42 in a stack trace and the file opens beside it. The diff pane already shows what you changed. Open a Claude pane in the same workspace and it inherits the cwd, the branch, and — if you ask it to — control of the browser pane.
A workspace is a recursive split tree. Any leaf can be any pane kind:
| Kind | What it is |
|---|---|
| Terminal | Block-based shell (below) |
| Claude | A Claude Code session as a first-class pane |
| Files | Virtualized tree + CodeMirror viewer with file tabs |
| Diff | Live git diff for the pane's cwd |
| Browser | Real Chromium WebContentsView |
Split horizontally or vertically, nest arbitrarily, and reorder tabs within a split. Every pane keeps its own state, and the layout is per workspace and persists across restarts.
Every command is its own block, and every block knows its exit code, duration, exact cwd, and the live env at that moment — node version, git branch, virtualenv, AWS profile. This runs on OSC 133 plus custom grove-pre / grove-post / grove-env markers, so there's no polling. Shell integration loads through a custom ZDOTDIR, so your aliases and rc still work.
Output is settled through a headless VT grid before it reaches the UI, so progress bars, spinners, and multi-line repaints show their final frame instead of every frame concatenated. Blocks persist to ~/.grove/blocks/ and survive restarts.
Paths (src/App.tsx:10:5), OSC 8 hyperlinks, and URLs in output are all clickable — ⌘-click a path to open it in Files, a URL to open it in the Browser pane.
This is the part Grove is really built around.
- Claude panes — open a Claude Code session anywhere you'd open a terminal. It starts in the pane's cwd with your repo context.
- Agent state, surfaced — Grove watches each session and knows whether it's working or blocked. When Claude stops on a permission prompt, the choices are parsed out and rendered as buttons you can click.
- Agents view — one cross-workspace board of every running session, so a dozen parallel agents don't mean a dozen tabs to babysit.
- Browser control via MCP — when a workspace has a Browser pane, Grove writes a Playwright MCP config pointing at a filtered CDP endpoint. Claude can then drive that browser pane — click through your app, read the console, check its own work — without touching Grove's own UI.
- Inline assistant — a lightweight
claude -pstreamed over SSE for one-shot questions, no session required. - Fork seeding — a Claude tab opened in a freshly forked workspace gets a summary of the parent workspace's recent activity via
--append-system-prompt.
Grouped vertical tabs, each workspace pinned to its own directory. Right-click → Fork workspace runs git worktree add under the hood, slots the fork directly beneath its source, and names it something memorable like otter-a3f2. Forks are ephemeral: close one and both the worktree directory and its grove/<animal>-<hash> branch go away. The close dialog warns about uncommitted or unpushed work first.
Use it for hotfixes off main without disturbing what you're doing, PR review without a context switch, or throwaway experiments — including handing each fork to its own agent.
The Browser pane is an Electron WebContentsView, not an iframe: any cross-origin site, real cookies and login state that persist, WebAuthn / security keys for 2FA on staging and prod, and real setZoomFactor zoom. It discovers running dev servers via lsof, and offers a desktop viewport or a 390px mobile frame.
- Command palette (⌘P) — fuzzy search across tabs and workspaces
- Omnibox — jump to a path or URL from the header
- Pins — a strip of one-click commands, global or per workspace, bound to ⌘⇧1..9
- Status bar — system stats and detected running services
- Streaming context — the backend pushes context over WebSocket instead of clients polling, so chips update live as you
cdor switch branches
Enable remote mode and the daemon serves the full UI over HTTP with a generated token, reachable over LAN or Tailscale. There's a dedicated mobile layout with a key bar for the keys a phone keyboard doesn't have. Your Mac keeps running the actual work; the phone is a client.
Panels register themselves through a small registry (panelRegistry.register({ id, title, icon, component })) and communicate through an action bus (dispatch('open-file', …)). The built-ins — Files, Diff, Browser — go through the same path any future extension would. Conflicts resolve first-registered-wins.
Appearance (mono font family and size, applied live), default tab kind, a shortcut reference, remote access, and maintenance — including cleanup of orphan grove/* branches and worktree directories left by earlier crashes.
The north star is that Grove is where the whole job happens. What's missing for that:
- Editing, not just viewing — the CodeMirror pane is read-only today. Real editing, with the agent and the editor looking at the same buffer, is the biggest gap.
- Test results as a first-class surface — parsed runs with pass/fail navigation, not scrollback you re-read.
- Block-level agent handoff — right-click a block → Ask Claude, shipping command, output, exit code, cwd, branch, and env in one payload. The context is already structured; the action isn't wired.
- Block sharing — the same payload, formatted for a teammate or a bug report.
- Chrome extensions in the Browser pane — 1Password and friends, via
electron-chrome-extensions. - Per-worktree ports and history — so parallel forks stop colliding.
- Block search (⌘F), multi-line input, inline images, notifications for long-running commands.
See FEATURES.md for the full tracking list.
- Electron shell — custom titlebar,
WebContentsViewbrowser, CDP filtering proxy for Playwright MCP, worktree management - React 18 + Chakra v3 + Vite + Zustand
- Fastify + WebSocket + node-pty backend on
127.0.0.1:4317, running as a persistent daemon - @xterm/headless server-side VT grid for settled block output; xterm.js for the raw-mode TUI overlay (vim, htop, ssh, claude…)
- CodeMirror 6 for file viewing
- Custom zsh integration via
ZDOTDIRfor OSC 133 + Grove markers lsoffor dev-server and service discovery- State in
~/.grove/:blocks/,worktrees/,daemon.pid,daemon.log,remote.json
npm install
npm run dev:alldev:all builds the Electron main process, then runs backend + Vite + Electron in parallel. A kill-ports step clears stale listeners on 4318 and 5173 first. The dev backend uses 4318 to stay clear of a packaged Grove daemon on 4317.
npm test # frontend (vitest) + electron + backend (node:test)
npm run dist # package Grove.app + zip into release/A packaged Grove runs its backend as a detached daemon on 127.0.0.1:4317. Closing the window leaves it alive — your dev servers, watchers, long compiles, and running agents keep going. Reopen Grove and it reconnects to the same process. ⌘Q tears it down cleanly; a crash or force-quit leaves it running and the next launch reattaches.
npm run daemon:status
npm run daemon:stop
npm run daemon:startPre-alpha. macOS only. Built in the open. If something breaks, open an issue.
