Documentation v0.3.2
Earshot / Internals and project

Roadmap and current status

What is implemented, what needs live QA, and what comes next.

Reviewed for 0.3.2 · Latest documentation

Honest state of the project. Nothing here is marked done until it works and has tests.

M0 — Scaffold — implemented#

Bun workspace monorepo, TypeScript project references, biome, CI on macOS, Linux and Windows, Bun.build bundling, earshot --version.

M1 — Provider layer — implemented#

  • Unified message, request and stream-event types
  • One bridge to the AI SDK's LanguageModelV4 spec, shared by every adapter
  • 7 wire adapters, 19 providers, 896 models
  • models.dev catalog, pruned at build time; live refresh
  • Auth store (0600, atomic) and the CLI → env → file → ambient resolution order
  • Cost accounting including prompt caching
  • earshot models, earshot -p
  • Conformance suite covering every adapter through the shared bridge

Historical M1 deferrals (resolved below): the ChatGPT sign-in (openai-codex-responses) provider, and Ollama's native adapter — At that milestone Ollama used the OpenAI-compatible endpoint, which drops tool calls when streaming.

Verification: the shared conformance suite uses scripted output, and an OpenRouter free-route request has completed against the live API.

M2 — Coding agent — implemented#

The milestone that makes earshot usable.

  • Agent loop: assemble → stream → tool calls → permission gate → execute → repeat. runTurn() is an async generator, interruptible via AbortSignal; read-only calls run concurrently, mutating ones serialised in emission order
  • Steering: a message typed mid-turn is injected at the next model call rather than cancelling the turn
  • Tools: read, write, edit, multi_edit, ls, glob, grep, bash, bash_output, web_fetch, ask_user, todo
  • Permissions: modes plan | ask | accept-edits | auto | yolo; Tool(pattern) rules at global, project and local scope; deny-first and never overridable by allow; writes outside cwd always ask; command rules match every segment of a chained command, not just its prefix
  • Sessions: tree-structured JSONL under the data dir, --resume, --continue
  • AGENTS.md loading (and CLAUDE.md), nearest-wins by ordering
  • Ink TUI: inline scrollback via Static, permission and question prompts, diff view, collapsible tool blocks, status line
  • Undo: per-tool-batch snapshots in a shadow git object store, outside the user's repository

Decision taken: Windows requires Git Bash. bash resolves Git for Windows and fails with an install pointer when it is absent, rather than falling back to PowerShell. Two shell dialects would mean quoting, pipelines and permission-rule matching all differ by machine for the tool the agent uses most.

Historical M2 deferrals (resolved below): context shapers and auto-compaction, /fork, /rewind and /undo — all landed in M3.

Verification: the complete agent loop has run against OpenRouter's live API in headless mode as well as against scripted providers in the suite.

M3 — Listening + context — implemented#

The milestone the project is named after.

  • Context shapers, run before every model call, cheapest first: individual tool results capped head-and-tail, older results reduced to one-line stubs, and auto-compaction at 80% of the window — a model-written summary plus the recent messages verbatim, open todos and files touched. Nothing rewrites history: the request is shaped, and compaction appends a summary entry naming the entries it stands in for
  • Scope contract: declare_scope before the first change, and a guard that stops and asks on a file nobody listed, a dependency, a rename or delete, a formatting sweep, a removed test, or a turn several times its own estimate
  • Preference memory with provenance: files with frontmatter recording the user's own words and the date, an index in every prompt, two-keystroke capture from a correction typed at the prompt, /memory to review and forget
  • Honest completion and verification: a turn that changed files runs the project's detected test command and puts its output in front of the model verbatim, alongside a self-check comparing the request with what changed
  • Status line: context percentage and what compaction has dropped, beside spend
  • Session tree: /tree, /rewind, /fork, /undo

Historical M3 deferrals (resolved below): /plan (behaviour 3) and the intent line (behaviour 6), which move to M4 with the rest of the command surface. Curiosity levels and --max-cost also landed later, after M6.

Verification: the TUI has completed a live-model turn in a real macOS PTY; its detailed interaction suite also runs against a controlled terminal stream.

M4 — Extensibility — implemented#

The milestone where earshot stops being a closed program. Everything in it is a contract with code somebody else wrote, so each boundary is documented by what it is not allowed to do. See Extending earshot.

  • MCP client — stdio and streamable HTTP, tools namespaced server__tool and put through the same gate as built-ins. An MCP tool is never read-only whatever the server claims about itself, and a stdio server a project checked in does not start until earshot mcp trust says so
  • Skills and slash commands — discovered from the project and the config directory. A skill contributes instructions and nothing else; allowed-tools intersects with the session's tools and can only narrow them
  • Hooks — Claude Code's JSON contract, with one deliberate incompatibility: a hook may deny or downgrade an allow to a prompt, never approve. A hook that fails, times out or prints garbage blocks nothing
  • Subagents — a nested agent with its own context window, inheriting the permission rules, the declared scope, the approved plan and the cost total, and returning an answer rather than a transcript
  • Headless JSONearshot.v1 on every record, additive within the version, a new major requested by name. See Headless output
  • /plan — a plan file you edit in $EDITOR and approve; what is pinned is what the file says, not what the model wrote
  • Intent line — a one-line "why" before every tool batch, as an event, so a batch that arrived without one is visible rather than merely undesirable
  • OpenRouter PKCE sign-in, and Ollama's native /api/chat adapter, which keeps tool calls that the OpenAI-compatible endpoint drops

Historical M4 decision: ChatGPT sign-in for Codex models, which moved from "planned" to "deliberately not supported" — see Providers. Curiosity levels and --max-cost landed after M6.

Verification: the suite spawns a real stdio MCP process and covers its environment isolation, tool listing, calls, diagnostics, and shutdown.

M5 — Ship — acceptance remaining#

Documentation, npm i -g @raegent/earshot, compiled binaries (macOS arm64/x64, Linux x64/arm64, Windows x64), earshot doctor, Windows QA in Windows Terminal, changelog, contributor guide.

This website owns the latest-only MDX documentation. The historical VitePress deployment is retired only after a coordinated launch update. Tagged releases validate a clean global npm install, publish the package, and ship five native executables. earshot doctor validates the local runtime without exposing credentials. Windows has native CI plus a release-blocking Windows Terminal acceptance checklist; the checklist result is recorded per release rather than claimed by code.

Release acceptance remaining: record the hands-on Windows Terminal checklist and verify release assets and public destinations. Publication and acceptance are external release actions, not claims established by local builds.

Target: adding an OpenAI-compatible vendor takes ≤30 lines. Currently 1.

M6 — v1.x — acceptance remaining#

ACP server (Zed, JetBrains, Neovim), in-process TypeScript extensions, image input, tool search for large MCP sets.

ACP implemented: stable v1 initialization, session creation and loading, prompt streaming, cancellation, permissions, elicitation and tool events are implemented with protocol-level tests. Zed, JetBrains and Neovim still require the recorded physical QA checklist in Editor integration with ACP; client-provided MCP definitions and draft ACP v2 are not part of this slice.

Image input implemented: unified user prompts now carry text and images into compatible provider adapters from --image and ACP. Unsupported model capabilities and file formats fail before the provider request.

Tool search implemented: past 25 MCP tools, their schemas leave the request and tool_search finds them on demand. A surfaced tool is listed for the rest of the session and goes through the same permission gate it always would; the search itself grants nothing.

In-process extensions implemented: a module in .earshot/extensions/ or the config directory contributes tools directly, namespaced and gated like any other. A project extension is inert until earshot extensions trust names it, because being in-process means it is not sandboxed and cannot be.

Dropped from M6: Anthropic server-side compaction. earshot's own shapers and compaction are provider-agnostic and already do the job; a second, vendor-only context path would mean the loop behaves differently depending on who is serving the model, which is the thing the provider boundary exists to prevent.

Acceptance remaining: the recorded editor QA checklist in Zed, JetBrains and Neovim. Everything else in M6 is implemented and covered by tests.

Since M6: --max-cost and curiosity, the last two behaviours Listening described but the code did not have.

M7 — Trust at Scale — future#

Production hardening and a sustainable ecosystem after M6 has seen real use:

  • Stable v1 compatibility guarantees for configuration, transcripts, headless output, ACP, and extension APIs. Two of those surfaces already carry a version marker and an additive-within-the-version rule — earshot.v1 on headless records and ACP v1 — so for them M7 is writing the promise down and testing it, not inventing it. Configuration, the transcript format and the extension API have no such marker yet
  • A discoverable plugin and extension marketplace with sandboxed third-party code. This is the one item M6 argues with: in-process TypeScript extensions are deliberately not sandboxed and cannot be, which is why a project extension stays inert until earshot extensions trust names it. A marketplace therefore needs an execution boundary that does not exist today — an out-of-process extension host, or distribution restricted to MCP servers — and choosing it is the first design question of this item, not a detail of it
  • Signed standalone binaries, macOS notarization, and Windows code signing
  • Signed automatic updates with stable, beta, and nightly release channels
  • Crash recovery and repair tooling for interrupted or damaged sessions. Sessions are append-only JSONL and history is never rewritten, so a damaged session is a truncated or partial-line tail rather than a corrupt structure; repair means detecting that tail and recovering the tree around it
  • Performance benchmarks and regression budgets for startup, rendering, context shaping, and long-running sessions. The harness and the recorded baselines are in — bun run bench, documented in Performance baselines, with CI recording a run per platform on every push to main. The budgets are deliberately not: the first baselines put startup at a 9% spread and several in-process cases above 200%, so what a threshold may claim has to come from the accumulated history rather than from the first run that produced it
  • A maintained live-provider compatibility matrix. This is the standing gap the suite cannot close: every adapter is covered through the shared bridge with scripted output, and only OpenRouter has answered live
  • Package-manager distribution through Homebrew, WinGet, Scoop, and AUR
  • Independent security audit and a refreshed threat model before v1 stability

Entry condition: M6 has shipped and real editor/extension usage has exposed which interfaces deserve a long-term compatibility promise. Until then, this is direction rather than a frozen contract.

Current acceptance boundary. The 0.3.2 npm package and update implementation exist. Windows Terminal and physical editor acceptance must be recorded per release; this documentation does not infer those results from a passing test suite. Performance baselines and compatibility contracts already exist; signing, distribution channels, and broader live-provider coverage remain future work.

Explicitly out of scope#

  • Subscription workarounds. No Claude Pro/Max OAuth, no Gemini Code Assist OAuth, nothing that circumvents a provider's terms. See Providers.
  • A web UI. earshot is a terminal tool. The ACP server covers editors.
  • Model hosting or fine-tuning. Not this project.
  • Telemetry. Zero, with an optional local usage log.

Where help is most useful#

  1. Live provider testing. The single biggest gap. If you have a key for anything, run a turn and report what breaks.
  2. Adding providersusually one line.
  3. Windows testing. CI covers it; real terminals are another matter.
  4. Driving the TUI for real. An interactive macOS PTY smoke test has completed; broader real-terminal coverage is still useful.
Something unclear?Suggest a correction on GitHub ↗