Wrap the approved CLI, do not re-implement it
The Claude Code CLI is the moving target and the policy-vetted surface. Wrapping it means every CLI improvement lands in the GUI for free, and security review is already done.
A local Electron surface for Claude Code in environments where the official desktop app isn't allowed.
At a lot of companies the Claude desktop app is blocked but the Claude Code CLI is approved. Same vendor. Same data path. The desktop client just adds friction that security teams cannot vet.
Claude Code GUI is the answer. An Electron wrapper around the approved CLI. It does not change the data path. It adds conversation history, markdown rendering, worktree git ops, and a custom MCP permission bridge that surfaces tool approvals as modal dialogs.
Engineers want the agent UX. Security teams will not approve another desktop AI app. The GUI runs on top of the CLI those same teams already approved, so the data surface does not change and the policy does not have to.
The custom permission bridge is the trick. Tool calls that would block on stdin in the CLI now pop a real modal in the renderer. Approve or deny. Reason captured. The CLI keeps moving.
Electron main process spawns claude -p with --permission-prompt-tool pointing at an MCP server bundled in extraResources. The MCP server speaks to the main process over an HTTP bridge on a random 127.0.0.1 port. When the CLI requests permission, the bridge forwards the request to the renderer, the user clicks Allow or Deny, and the response goes back as the undocumented MCP schema (allow / deny with reason).
Session state is split. Conversation history in localStorage. Resume IDs and pending-new-session flags in module memory. Worktree toggles reset the session so claude -p does not look for a UUID that never existed.
The Claude Code CLI is the moving target and the policy-vetted surface. Wrapping it means every CLI improvement lands in the GUI for free, and security review is already done.
Permission prompts are the user-facing escape hatch for tool calls. Running them through a custom MCP server connected to a real modal in the renderer is the only way to make the UX feel native instead of TUI-shaped.
ContextIsolation true means contextBridge clones objects in both directions. So session mutations have to happen in module scope, not on a proxied object. The lib is loaded as a plain <script> in the renderer and as a CommonJS require in tests.