Skip to content
Install

docs/claude-code.md

Claude Code

Run a session on your own Claude Code: pick a Claude model in /model and that session's turns go to claude.

A session can run on your own Claude Code instead of MendCode's native engine. Pick a Claude model in /model (or in a role, recipe or profile for main) and that session's turns go to claude.

Requirements

  • Claude Code installed (claude --version works) and logged in (claude auth login).
  • claude outside your PATH: set MENDCODE_CLAUDE_BIN=/path/to/claude for the daemon.
  • MENDCODE_CLAUDE=0 hides the Claude models.

The models appear in /model only when claude answers. The daemon asks when it starts; a list requested meanwhile waits for the answer (two seconds at most), and a negative answer is asked again once it is ten seconds old, so a slow first start does not hide them until the next restart. They are the CLI's aliases, so each one always means the latest of its family:

modelruns
claude-fableclaude --model fable
claude-opusclaude --model opus
claude-sonnetclaude --model sonnet
claude-haikuclaude --model haiku

Effort (low, medium, high, xhigh) is passed as --effort. minimal becomes low. When you pick none, the default is the effortLevel of your Claude Code settings (~/.claude/settings.json, or $CLAUDE_CONFIG_DIR), so the footer names the level the session runs at; a level MendCode has no name for (max), or no setting, leaves the models without a default and Claude Code decides. The footer's effort carries no :JEV for these models: adaptive reasoning applies to the daemon's own generations only (reasoning).

A full id of one of those families pins the session to that model: --model claude-haiku-5-5, /model claude-opus-5-5, a dated id such as claude-haiku-4-5-20251001, or the same id in a role or profile for main. The id is a family alias followed by a version (-5, -5-5) and optionally an eight-digit date; it goes to claude --model unchanged and takes the family's efforts. It reads Haiku 5.5 and is listed in /model beside its alias while a session or role uses it. MendCode keeps no table of versions, so a version Claude Code does not have fails on the first turn with Claude Code's own error. Anything else (claude-nonsense, claude-haiku-fast) is an unknown model.

An alias reads by its family (Opus) until Claude Code says which version it resolved to. The daemon remembers that, per alias, in <dataDir>/model-resolutions.json (written atomically, a display hint only: requests keep sending the alias), so the next launch and every other session already show Opus 5.5 in the model list, /setup, the status line and the bottom bar, and the model reads it too: ai_config lists claude-opus with resolved: "claude-opus-5-5". It is replaced when Claude Code reports a different version (after an upgrade); an alias that never answered on this machine stays Opus. A session pinned to a full id does not change what its alias shows.

The context window in the model list is 200K for every Claude model until Claude Code states the real one, which it does with the first answer of each model (1M for some). The daemon keeps that figure in the same file and lists it from then on. The context percentage of a session never uses the listed figure: it appears after the first answer and is measured against the window that answer stated.

How it works

MendCode drives the unmodified claude binary through the same stream-json protocol the official Agent SDK uses:

  • Claude Code runs its own agent loop, tools, CLAUDE.md, skills, hooks, output style and MCP servers, from your normal Claude settings (user, project, local).
  • Its system prompt stays its own. MendCode adds a short note after it (--append-system-prompt): what this terminal draws (the "Terminal rendering" lines of the native prompt: Markdown, math, Mermaid diagrams and charts, colour swatches, callouts, clickable paths, diff cards), so the model writes a diagram or a chart when one helps; the one-line mendcode docs pointer for questions about MendCode itself; and that a changed MCP server is there on the next message. The minimal prompt mode leaves out the drawing lines and the pointer. Codex, pi and ACP agents get the same two (pi, ACP); Codex as its thread's developerInstructions, a developer message after its own instructions.
  • It uses its own login. MendCode never reads, copies or forwards Claude credentials, and there is no proxy or relay.
  • It runs with your Claude Code's own configuration and login (~/.claude, or your CLAUDE_CONFIG_DIR), on a new install as on an old one (setup); ownLogins changes only ChatGPT's login. The setup reads claude auth status --json for installed, signed in and the plan (subscriptionType, only when reported); a turn that fails on "Not logged in" says claude auth login.
  • One claude process serves one session. It stays up between turns, and stops after 10 idle minutes or when the model or effort changes. The next turn resumes the same Claude Code conversation (--resume), also after a daemon restart.

What MendCode does

  • Turns: text, thinking and tool calls stream into the transcript. Tool calls are drawn with MendCode's own cards: a command as a shell card, a read with its lines, a search grouped by file, a write with what it did, and an edit with the diff Claude Code made (line numbers, added and removed rows in colour). Chats saved before this keep their plain cards. Claude Code leaves the text of the model's thinking out unless asked, so each process starts with --thinking-display summarized and what streams is the API's summary of the thinking. A claude that does not know the flag exits at start; the engine then starts it again without the flag and keeps it off, and for that install Claude Code keeps the thinking text to itself: the working row still says Thinking... while such a block is open, and the transcript gets no card for it (run --thinking prints nothing for it either).
  • Sub-agents: Claude Code's own sub-agents (its Agent tool) show like MendCode's. Each one at work is a row of the Agents strip above the prompt (what it was asked, its agent type, await or bg, the tool it is running and how many it has run). In the full tier the row also says its model: the one the call asked for (model: "haiku" reads Haiku) from the start, then the one its first message names (Haiku 5.5); a call that names none shows no model until then, never the session's own. Under its call in the conversation one line says what it is doing, and once it ends what it took (tools, time, tokens). ctrl+x ↓ or a click on its row opens its thread in the chat's place, under a header with its name in its colour, 2 of 4 · ←→ · ↑ back, and its type, bg or await, what it is doing, its tools and time: the prompt it was given, each tool it calls and its report; ← → change sibling and ↑ (or ctrl+x ↑) returns to the conversation at the row you left. The thread is read-only: a message typed there goes to the session. A background sub-agent (run_in_background) goes on filling its thread after the turn that started it ended, and during later turns: each tool it calls and its report arrive under that turn's call as they happen (up to 8 earlier turns are followed per Claude Code process). Its call says running in the background instead of Claude Code's note to its model, and it stays in the Agents strip, during later turns too, while Claude Code still runs it.
  • Background commands and agents: what Claude Code runs in the background shows right over the prompt while it runs (one muted row in the minimal and balanced views, ◇ background command · <its description> · 3m · /ps, or the counts by kind; one row per item in the full view, up to three then +N more; while the Agents strip shows sub-agents, each command is a row of that same tree instead, └ bun run dev cmd 11m 13s, and the strip's head counts them and names /ps), in /agents and in the ctrl+t dock's Jobs, and its process is kept alive meanwhile: no daemon restart (automatic, /restart or /daemon restart) stops it, and a terminal that hosts the daemon waits for it before it closes. An agent call whose prompt takes a while to write opens its card as soon as it starts, and thinking that streams no words still counts as activity, so neither reads as no response. Any call with a long input (a whole file, a long script) opens its card while the model still writes it: the card says how much is written so far (writing 84 lines · 6.2 KB, every 2,000 characters) and the working row says Writing...; once the input is whole the tool runs and both return to the tool's own words. Esc gives Claude Code three seconds to stop the turn, then ends it here; whatever that turn still sends is dropped, never mixed into the next one. When it ends after the turn did, Claude Code answers on its own; MendCode shows that as a notice in the conversation (the command's summary and output file) followed by Claude's answer, nothing is sent twice. A message you send before that notice runs goes as usual.
  • MendCode's tools (feature harness_tools, balanced and full; /tier harness_tools on|off): Claude Code keeps its own tools and also gets MendCode's, as mcp__mendcode__<tool>: the code graph (graph_find, graph_callers, graph_file_api, graph_map), computer use when it is on, memory, customize_ui, loops, worktrees and the rest of what the session's tier offers.
    • Not passed on: what Claude Code does itself (files, shell, code search, questions, plans, sub-agents, skills) and the MCP servers of MendCode's own mcp.json; Claude Code has its own MCP configuration.
    • Web: MendCode's webfetch and websearch are passed on even though Claude Code has its own, because Claude Code refuses some sites (it will not fetch reddit.com) and has no search on some plans. Claude Code is told to call them when its own fetch or search refuses a site or is missing; they run in MendCode from your machine, with your search key or ChatGPT login.
    • A call runs in MendCode as a call of the turn: the same approvals, plan-mode refusal, secret masking, output limits and card as in a native session. Claude Code is not asked a second time.
    • How: the daemon serves a loopback MCP server (127.0.0.1, random port, no browser origins) and starts claude with --mcp-config <private file>. One bearer per session, kept in memory and in a 0600 temporary file; it runs nothing unless that session has a turn running. Your Claude settings are never edited.
    • Claude Code lists MCP tools through its own tool search, so their schemas cost nothing until one is used.
    • Claude Code's own sub-agents (Explore, general-purpose, yours) get a system prompt of their own, without the server's instructions. MendCode registers a SubagentStart hook at initialize and gives each one the same instructions as additionalContext, so they also reach for the code graph before grep/sed. Nothing is added when the feature is off or the session has no MendCode tools.
    • Images: mcp__mendcode__image_gen is MendCode's image_gen, with the same approval, wait canvas and finished image card as a native session. A native ChatGPT session uses the provider's hosted image tool instead; Claude Code has none, so here image_gen also works with no image_generation setting, through your ChatGPT login (MendCode's own or the Codex CLI's) or a saved OpenAI provider key, with model gpt-image-2 and the image saved under <dataDir>/generated_images. image_generation.enabled: false, MENDCODE_IMAGE_GEN=0 or --offline remove it. Images attached to the message are the "recent images" an edit can take. The model is told in one line of the server's instructions that it can make images.
    • Codex (a codex:<model> session on the native daemon) gets the same tools, gated the same way, as its mendcode MCP server. Its codex app-server process belongs to the turn, so it starts with -c mcp_servers.mendcode.url=<bridge> and bearer_token_env_var="MENDCODE_MCP_TOKEN_MENDCODE": the bearer is in that process's environment only, never in its arguments or the traces, and its name has TOKEN in it, so Codex keeps it out of the model's commands (with /env credentials it is in shell_environment_policy.exclude by name). default_tools_approval_mode="approve" stops Codex from asking about these calls (by default it asks, through an MCP elicitation); MendCode asks once, when it runs the call, and should a Codex ask anyway its elicitation is accepted without the user (another server's is declined). tool_timeout_sec=86400, since a call can wait on an approval card or a long computer-use step (Codex's own default is 60 s). Codex 0.162 does not put an MCP server's instructions in the model's prompt, so they go into the thread's developerInstructions after MendCode's note (in every prompt mode, as Claude's server instructions; minimal still leaves the note out), told that the tools are nested under exec (await tools.mcp__mendcode__graph_find({ query }), the rest in ALL_TOOLS), with no ToolSearch. Codex's card for such a call is not drawn: MendCode's is. Not for the older daemon started with the Codex engine (--engine codex), whose one process serves every session.
  • MCP servers changed in a turn: Claude Code reads its servers when its process starts. A turn that ran claude mcp add|remove… or wrote .mcp.json / .claude.json ends that process; the next message starts a new one that resumes the conversation, with the new server's tools.
  • Approvals: when Claude Code asks for permission, MendCode shows its normal approval.
    • "Always" keeps Claude's suggested rule for this session only; it is never written to your Claude settings.
    • edits approves file changes; full approves everything; plan refuses changes.
  • Questions: Claude's AskUserQuestion becomes a MendCode question: one question as a single question, several (Claude asks up to four) as one batch with tabs. Each answer goes back under its question; a multiple choice is its labels joined with ", ". The desktop notice "Claude needs your permission to use AskUserQuestion" is not MendCode's: it comes from a Notification hook in your own Claude settings, which Claude Code runs for every permission request, this one included.
  • Plans: ExitPlanMode becomes a plan approval. Approving leaves plan mode.
  • Modes: the permission mode maps to Claude's (ask → default, edits → acceptEdits, full → bypassPermissions, plan → plan) and follows mode changes between turns. Bypass mode still lets Claude Code raise its own safety checks (a dangerous rm, a protected path): in full access MendCode allows them unattended; with the engineSafetyPrompts setting true they reach you as an approval with Claude's reason, allow once or reject (permissions).
  • Stop: esc interrupts the Claude turn; Claude Code then also stops the background agents the session runs, and it will not resume them (its SendMessage answers that the agent "was stopped by the user and won't be resumed"). Each one stays in the Agents strip as stopped by you · /relaunch until a turn begun after the stop ends; /relaunch (or /relaunch <words of its description>) sends a message asking Claude to start it again as a new agent with the same type and prompt, told how far the stopped one got (its tool count and last tool). An agent Claude Code runs in the background on its own, without run_in_background (its result says async_launched), counts as a background agent too. Send now on a queued message (ctrl+s, its SEND button) does not: the message goes into the running turn the way Claude Code's own send-now does, the step at hand ends (a command it was waiting on moves to the background), background agents keep running, and Claude answers the message as its next turn.
  • Restarts: background agents live inside the claude process, so they end with it.
    • After an MCP change (claude mcp add, .mcp.json), the restart waits until no background task is running.
    • A new model or effort, and an idle process (10 min with nothing in the background), restart the process; /env credentials wait like an MCP change.
    • Each end is logged to the daemon's stderr with its reason and how many background tasks ended with it ([CLAUDE]: <session> process ended: …).
    • The Agents strip drops those tasks at once, so no row stays behind for an agent that stopped.
  • Fast mode: with /fast on, a process on Opus 5.5, Opus 5 or Opus 4.8 (or the opus alias) starts with --settings '{"fastMode": true}', Claude Code's own fast mode, for that process only; it is never saved to your Claude settings. Turning it on or off restarts the process like an effort change. On other models the footer mark reads n/a and /fast says why.
  • Quota: the 5h and 7d windows of your Claude subscription show in /usage and the footer, from Claude Code's rate-limit events.
  • Usage: tokens are counted under provider claude. No cost is shown, because the subscription has no per-token charge.
  • Cache lifetime (opt-in, off in every tier): with claudeCacheTtl: true (/settings, Behavior, row claude ttl) and promptCacheTtl: "1h", each new claude process is started with CLAUDE_CODE_PROMPT_CACHE_TTL=1h and CLAUDE_CODE_SUBAGENT_PROMPT_CACHE_TTL=1h in its environment, the two variables Claude Code documents for the main conversation and for everything else (sub-agents, workflows, compaction; Claude Code v2.1.242 or later, older versions ignore them). At 5m nothing is passed, so Claude Code keeps its own defaults: on a subscription within plan usage the main conversation already gets an hour and its sub-agents 5 minutes; with an API key or a cloud provider both get 5 minutes. A process already running keeps what it was started with.

Per-turn selections and forks

session send and --command accept --model, --agent and --variant on a Claude Code session, or to borrow a Claude model for one turn of a native session. They apply to that turn only and leave the saved model and agent alone.

  • Model: the turn runs on that model; the claude process restarts on it (resuming the Claude session) and restarts again on the saved model at the next turn.
  • Effort: --effort LEVEL (low, medium, high, xhigh, as the model lists them) is the documented spelling, on run, session create and session send.
  • Variant: Claude Code has no provider variants, so a variant naming one of the model's efforts is that effort (still accepted; --effort is preferred). Any other variant, and models without efforts (Haiku), are refused.
  • Agent: Claude Code runs its own agent loop, so a MendCode agent applies as a permission ceiling (the plan agent makes the turn read-only) and its instructions go in front of the message Claude receives. The agent's tool allow/deny lists do not bind Claude's own tools. Subtask commands need the native task tool and are refused.
  • Fork: session fork --message-id (and /fork) cut the copied transcript as for native sessions. Claude Code keeps its own conversation, which is not forked, so the fork's first Claude turn begins with the copied conversation as a budgeted handoff (newest turns, the original request, command outcomes; handoffTokenCap, default 16,000 tokens; secrets as markers). Later turns do not repeat it. A daemon whose engine fixes its own model (Codex, faux) still refuses per-turn selections.

Limits

  • Switching a session between a native and a Claude model (or an ACP agent) keeps each engine's own history, and the next turn on the engine that missed turns gets them as a budgeted handoff in front of your message, never summarised by a model; see sessions.md. /model says what will be handed over. Making a Claude model the default moves no session that already ran, and a switch made while a native turn runs applies from the next turn (sessions.md).
  • /context reports what MendCode knows, since Claude Code keeps the request itself: the last call's measured context (exact), MendCode's transcript of the turns by role (user, assistant, tool results, at chars / 4) and the rest of the measurement as Claude Code's own prompt and tools. A Codex session gets the same report. MendCode never compacts a Claude Code session on its own; Claude Code manages its own context. /compact (with its instructions, if any) goes to Claude Code as its own /compact, and the transcript gets a compaction card with the summary Claude Code continues from. A /compact that does nothing (no Claude Code conversation yet, or a model switched to one with no history of the session) says why. With claude_compact (balanced and full; /tier claude_compact off) it does so at MendCode's point: each new claude process gets CLAUDE_CODE_AUTO_COMPACT_WINDOW set to the absolute compaction point MendCode's own models use (MENDCODE_COMPACT_AT_TOKENS, the model's modelContext compactAt, compactAtTokens, compaction.token_limit), else 200,000 tokens, and Claude Code compacts a summary's room before it. Without it Claude Code waits for its own window (1M for Opus), and every tool call re-reads the whole context from the cache: a 400k context costs twice a 200k one on each call. A value already in the daemon's environment is kept.
  • Claude models are for whole sessions; Dream and compaction use native models. Full-tier task {engine:"claude", model:"claude-sonnet", ...} can launch a daemon-owned Claude child. The opt-in thread MCP bridge exposes delegation and thread verbs inside Claude/Codex processes: from a Claude Code session on Opus, delegate_task starts a sub-agent on a native model (gpt-6.1-terra on your ChatGPT login), a Codex model or an ACP agent, and the bridge's instructions name those models, since Claude Code's own Agent tool lists only Claude's. Without the bridge, the session is told in one line how to turn it on (full tier, /tier thread_mcp on claude).
  • A message sent while a turn runs is not held back: as in Claude Code, it goes in at the turn's next tool result and Claude Code reads it after the step at hand; one it takes only once the turn's answer is done is answered in the same turn. A saved /queue policy wins.
  • A quiet turn is never cut, as in Claude Code itself: it times out and retries its own API calls, and a long think (over images, at high effort) can be silent for minutes. Esc stops it. The working row shows no no response for hint for it either.
  • Attachments: images are sent; other files are named in the message but not sent.

Started from Claude Code

A daemon started from a Claude Code shell (an agent ran mendcode --dir X session list, mendcode daemon restart, mendcode __daemon) would otherwise hand that Claude Code session's variables to every command, engine and app its sessions start; a claude opened there would take itself for that session's child and turn transcript saving off. They are never passed on, so nothing needs to be unset by hand:

  • Always dropped (set by Claude Code 2.1.314 in its tool shells or on its own process): CLAUDECODE, CLAUDE_PID, CLAUDE_EFFORT, CLAUDE_CODE_CHILD_SESSION, CLAUDE_CODE_SESSION_ID, CLAUDE_CODE_SESSION_ATTENDED, CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_EXECPATH, CLAUDE_CODE_INVOKED_SKILLS, CLAUDE_CODE_BRIDGE_SESSION_ID, CLAUDE_CODE_MESSAGING_SOCKET, CLAUDE_CODE_MESSAGING_TOKEN, and CLAUDE_CODE_SSE_PORT (the IDE window of that terminal, which Claude Code itself drops for a detached process).
  • Dropped only with Claude Code's value: AI_AGENT when it starts with claude-code; GIT_EDITOR=true and TRACEPARENT when CLAUDECODE=1 is set too.
  • A tuios pane's: a terminal in a tuios pane that starts the daemon would hand it that pane's identity and token, so every command of every session would speak to tuios as that one pane. TUIOS_ENV, TUIOS_HOST, TUIOS_SOCKET, TUIOS_SESSION, TUIOS_WINDOW_ID, TUIOS_PANE_ID, TUIOS_PANE_TOKEN, TUIOS_PANE_GRANTS, TUIOS_PANE_TTY and TUIOS_RESTORED are dropped too, except in a terminal that hosts the daemon in its own process: it is that pane.
  • Kept: everything you set for the engines: ANTHROPIC_* (ANTHROPIC_API_KEY enables the provider, ANTHROPIC_BASE_URL), CLAUDE_CONFIG_DIR, CLAUDE_CODE_USE_BEDROCK and the other CLAUDE_CODE_* settings, cache lifetimes.

The CLI starts the daemon without them; launchd and systemd services never had them; the daemon program started with them restarts once in place (same pid) without them, and so do daemon start --foreground, serve, web and a compiled terminal that hosts the daemon. Only removing them from the running process would not be enough: Bun gives a child started without an explicit environment the one the process started with. On Windows, and in a terminal run from source that hosts the daemon, they are removed from the process only, which every child given that environment honours. The daemon log names what was dropped, never a value: [ENV]: dropped inherited session variables: CLAUDECODE, ....

Debugging

MENDCODE_CLAUDE_TRACE=/tmp/claude.jsonl writes every protocol line, in and out, capped at 2 KB each.