CritiqueCode Leaves the Terminal Without Leaving Your Machine
The author agent still implements, then survives forced review. The surface can now be a localhost browser instead of a TUI.
Leave the TUI
Keep the kernel on the machine.
critique.sh
CritiqueCode now has a local web UI. Run critique-code web in the repository you mean to change. It binds 127.0.0.1, opens a browser, and drives the same author kernel as the TTY: Pi createAgentSession, CAS writes, user-gated critique_run, /review / /repair / /ship with the author transcript withheld. It is not OpenCode’s web app, not the Critique CLI sidecar, and not a cloud session.
The useful coding agents of 2026 live in a directory. Claude Code, Codex CLI, OpenCode, Aider, Gemini CLI, CritiqueCode: you cd into the repo and you talk. The surface has almost always been a TUI. That is a fine surface if you like it. It is a bad reason to skip independent review.
People keep asking for a browser. Not because HTML is morally superior to ANSI, but because a TUI is a tax. You cannot select a tool card with a mouse. You cannot keep a review panel beside the chat without splitting tmux. You cannot sit on a couch with a laptop lid half-closed and still see what the agent wrote. OpenCode already shipped that split: opencode web talks to a local server. Codex published the same lesson as an app-server. The mistake would have been to steal their product instead of their architecture.
Why the TUI was never the product
CritiqueCode’s contract is not “looks like Claude Code in a terminal.” The contract is: the model implements, then the controller forces review and verified repair. Author checks are not Evidence. Slash lines are never sent to the model. none_promoted is not a correctness proof. That contract is easy to lose the moment you wrap someone else’s agent UI.
OpenCode’s web UI is a SolidJS client of OpenCode’s Hono server. It assumes OpenCode sessions, shell, LSP, MCP, share, fork, /tui remote control. CritiqueCode imports the Pi coding agent library. It does not spawn pi. It does not spawn OpenCode. Pointing OpenCode’s packages/app at this harness would mean faking their protocol, then deleting most of it, then looking like OpenCode anyway. The sidecar CLI already embeds OpenCode. Mixing the two products is how you ship a rename nobody asked for.
Pi has a web-ui package too. It is built for an Agent that runs in the browser with IndexedDB keys. That is the wrong runtime. CritiqueCode’s tools are projected on the Node side: CAS file writes, named checks that cannot become Evidence, critique_run that does nothing until you click Approve. The tab can close. The process has to keep going.
What we copied, and what we refused
| Layer | OpenCode | CritiqueCode |
|---|---|---|
| Process | opencode serve / opencode web on loopback | critique-code web on 127.0.0.1 |
| Author runtime | OpenCode sessions and tools | Pi createAgentSession, projected tools |
| Review | Agent-owned, optional in the product | Controller-owned /review, transcript withheld |
| Exec | Shell as a first-class tool | critique_run only after you approve |
| Listen | Can bind LAN with a password | Loopback only. 0.0.0.0 is rejected |
We copied the split. The TTY was already a client of runLocalCritiqueCodeAuthor. The web UI is a second client of that loop: a line hub instead of readline, SSE instead of stderr chrome, a modal instead of a pi-tui select list for critique_run. Commands still parse through parseAuthorCommand. Unknown /foo is still rejected. It is still not chat.
What the local UI actually is
You get a dark page with the repo name, the model, a transcript, changed paths, last review, command chips, and a composer. Enter sends. Shift+Enter is a newline. /review, /review all, /repair, /ship, /skills, /login, /exit are buttons because those lines were never supposed to be typed from muscle memory. A critique_run request opens a modal with purpose and preview. Approve runs it. Deny finishes the tool with a denial. Output is still not Evidence.
APIs sit behind a per-process token. The HTML page injects it. Snapshot and prompt without the token return 401. A foreign Origin on POST returns 403. The server will not listen on 0.0.0.0. That is the whole security story for v1: this is a control surface for a process that can write your tree. It should not be a LAN toy.
Published binary
cd /path/to/your-project
critique-code webOpens http://127.0.0.1:<port> when it can.
Pick a port
critique-code web --port 77340 means ephemeral. Default is an ephemeral bind if you pass --port 0.
/login in the web UI is the same device-approval flow as the TTY. Keys stay in ~/.critique/code/, not in IndexedDB.The TTY is not deprecated
critique-code with no subcommand is still the interactive author session. Voice mode still needs a microphone and, today, still lives on the TTY path. Settings pickers still have a terminal UI. We did not wrap Electron. We did not add a file tree, an LSP, or an unrestricted terminal. Those are how a “web coding agent” quietly becomes a different product.
If you want OpenCode, run OpenCode. If you want a sidecar reviewer for the agent you already use, that is @critiquedotsh/cli. If you want Critique to author the change and then be forced through review, that is @critiquedotsh/harness. The web UI is for the last of those, for people who do not want to live inside a TUI to get the contract.