Overview: from terminal input to streamed tool results

This walkthrough follows the cloned tanbiralam/claude-code repository at commit 6f6f12b37f529488b10e53928dd5508bb93535c7. It explains the runtime data flow, the shared state objects, and how the main components cooperate during an interactive turn.

One turn in the system

The main path is a streaming loop. Terminal input becomes a normalized message, the REPL builds context and tool availability, the model streams assistant blocks, and any tool_use block becomes a local or MCP tool call. Tool results are appended as user messages and the loop repeats until the assistant stops or a recovery path takes over.

Bootstrap is a selective importer

The CLI entrypoint starts by handling paths that do not need the full application: --version, system prompt dump, MCP native hosts, daemon modes, background sessions, worktree helpers, and bare mode. Only after those checks does it import ../main.js and call cliMain().

node starts cli.tsx
  if cheap flag or special mode: execute fast path and exit
  else:
    start early input capture
    import ../main.js
    await cliMain(process.argv)

Code reference: cli.tsx fast flag path

Main builds the session contract

src/main.tsx defines the Commander program and uses a preAction phase for cross-cutting setup: MDM settings, keychain migration, init, title, telemetry sinks, plugin directories, user migrations, policy, settings sync, and remote config.

The default command then collects options such as prompt, print mode, model, permission mode, MCP config, allowed tools, denied tools, plugin directories, and input/output formats.

Code reference: main preAction and command options

The interactive app is loaded late

The REPL is not imported by the entrypoint. replLauncher.tsx dynamically imports App and REPL, then renderAndRun creates the Ink root, starts deferred prefetch work, and waits until the terminal UI exits.

Code reference: launchRepl dynamic imports

Input is queued before it is queried

PromptInput performs local UI checks, accepts suggestions, routes direct member messages, and then calls the submitted handler. handlePromptSubmit turns the input into a queued command, enforces active-query rules, handles immediate local JSX commands, reserves the query guard, and runs processUserInput.

Code reference: queued command execution

Input processing decides if the model is needed

processUserInput normalizes strings and content blocks, loads attachments, handles bash shortcuts and slash commands, and returns a result that includes shouldQuery. Text prompts produce a user message and set shouldQuery: true; some slash commands are local and do not call the model.

Code reference: bash, slash, and text routing

The query loop is the runtime coordinator

query() delegates to queryLoop(). Each iteration prepares system and user context, calls the streaming model adapter, yields assistant chunks, hands completed tool_use blocks to a streaming executor, appends tool results, refreshes available tools, and loops with the new state.

Code reference: queryLoop initialization

Tools are normal objects plus permission policy

A tool has a name, schema, prompt-facing description, concurrency metadata, permission behavior, and a call method. MCP tools are adapted into the same shape, so the query loop can execute built-in and server-provided tools through the same orchestration path.

Code reference: Tool type

App state is an external store

The UI uses a small external store with getState, setState, and subscribe. React components read it through useSyncExternalStore. REPL code deliberately grabs current store values when constructing tool context, so MCP clients, permission state, and resources stay fresh during a long session.

Code reference: useAppState external store subscription

Main data objects

Object Role Where it appears
AppState Terminal UI and runtime state: permissions, MCP state, notifications, prompt suggestions, plugin state, bridge state, and thinking state. AppStateStore.ts
QueuedCommand A normalized unit of user input waiting for processing or model execution. handlePromptSubmit.ts
ProcessUserInputResult The decision from input processing: local command only, model query needed, attachments, allowed tools, and command permissions. processUserInput.ts
ToolUseContext Runtime services passed into tools: abort controller, permission callbacks, state accessors, MCP clients, resources, UI hooks, and file read state. Tool.ts
Tool The common interface for built-in tools and MCP tools. Tool.ts
QueryParams The query-loop contract: messages, prompt, tools, MCP data, callbacks, model, abort signal, and runtime options. query.ts

Component pages