Skip to main content

Ensemblr

Agents that drive the app, not just the code.

A macOS orchestrator for the Pi agent harness or the Claude Code CLI you already have installed. I build it, and I use it to build everything else, including this site.

or brew install --cask ensemblr-hq/tap/ensemblr

Platform
macOS, Apple silicon
License
Apache-2.0
Agent runtimes
Pi · Claude Code
Languages
English · Russian · Greek

What it is

Ensemblr drives a coding-agent CLI you installed yourself, either the Pi harness or the Claude Code CLI, on one chat surface. Shared timeline, tool cards, model and thinking pickers, tool-approval prompts, git-backed checkpoints, session branching. Either runtime on its own is enough. The app ships no agent binary at all, which is about 260 MB of Agent SDK I left out deliberately.

There is no account to create and nothing syncs off the machine. State lives in a local SQLite database on Node 24's built-in node:sqlite. Secrets go to the macOS Keychain instead of a file or an environment variable, and GitHub tokens stay with gh, so there is no second copy to leak. No Ensemblr backend sits in the path, and there is no telemetry.

An Ensemblr workspace: a coding agent mid-turn with its tool calls, the file tree, and the terminal dock running a dev server

Isolation is the product

Every stream of work is a workspace: a git worktree with its own branch, working tree, agent sessions, run state and review path. A fan-out of agents cannot collide, because none of them shares a checkout. A workspace either adopts an existing branch or cuts a fresh one, and either way Ensemblr fetches and fast-forwards the base branch first. Retargeting it later leaves the worktree alone.

Workspaces start from an existing branch, a GitHub pull request, or a Linear issue, and they live on a board: Backlog, In progress, In review, Done, Canceled. Backlog holds work with no workspace yet, which means unstarted Linear issues and unassigned open GitHub issues. Drag one rightward and it becomes a workspace. Nothing goes back to the tracker. Dismissing an issue hides it locally, and its status stays yours to change.

Agents drive the app

Ensemblr Control is the permission-gated surface that lets an agent drive the app it is running inside. It can spawn conversations, launch harnesses, run terminals, open file and diff tabs, read the workspace diff and leave review comments on it, read and write Linear issues, ask a multiple-choice question, and move its own workspace across the board. Pi reaches it through a shipped extension; Claude Code and any MCP-capable harness reach the same operations through an embedded MCP server. One set of operations behind two transports, so the surfaces cannot drift.

The part I care most about is orchestration. A root agent delegates one unit of work per sub-agent, each in its own tab and its own context, then blocks until they report back rather than hand-rolling a polling loop. Sub-agents do their own work and never delegate onward, so the tree stays one level deep. Depth, spawn count and spawn rate are all capped.

Code enforces those limits, not a prompt. Sub-agents get no Linear writes at all, and nothing at any depth can move an issue to a completed or canceled state. Agent work stops at In Review, and a human decides whether it is done. Plan mode holds an agent to read-only tools until it submits a plan, checks that on every tool call, and passes it down to every sub-agent it spawns.

Review that ends in GitHub

Review happens locally and ends in GitHub. One panel carries Files, Changes and Checks: source-scoped diffs, per-file discard, a live file tree, and review comments anchored to specific lines. Agents read those comments, answer them and resolve them; they are the same comments a human leaves. From there it is an inline PR editor, commit and push, per-check status through gh, and a two-step merge. Work that does not survive review gets its workspace archived instead.

Terminals sit under all of it: an xterm.js dock over real PTYs, restored across restart. A repository declares any number of named run scripts in a committed .ensemblr/settings.toml, each with a command and an icon, one of them the ⌘R default. Setup and archive scripts run on the same lifecycle, and setup is fingerprinted so an unchanged workspace skips it.

What it took

Electron 43 and Electron Forge with Vite and Fuses hardening; React 19 and strict TypeScript; TanStack Router and Query; Jotai for state; Lexical for the composer; xterm.js over node-pty for the terminal dock; Zod at every boundary; SQLite through node:sqlite. Agent control runs on loopback HTTP plus MCP. Tests are Vitest and electron --test.

I keep fifty-two architecture decision records in the repository. A codebase this opinionated is only navigable if the reasoning is written down, for the humans and for the agents reading it. The app ships in English, Russian and Greek, and any change that adds a user-facing string ships it translated in the same change.

Distribution is the unglamorous half: Developer ID signing, hardened runtime, Apple notarization and stapling, so the build opens without a Gatekeeper prompt and validates offline. Every release goes out as a Homebrew cask and a signed DMG.

Stack

The product site, with the full feature tour and the current download, is ensemblr.dev.