Skip to content

Startup flow: the CLI skeleton ​

The moment you hit Enter on claude ​

You type claude in the terminal and press Return. What happens between that keystroke and the interactive UI?

Two entry points—why? ​

Claude Code has two entry files: cli.tsx and main.tsx.

cli.tsx runs first. Its job is narrow:

  • Handle “fast exit” commands such as claude --version
  • If full startup is needed, dynamically import the heavy modules

main.tsx is the real application entry. It:

  • Registers CLI commands and flags with Commander.js
  • Runs initialization (config, safety checks, telemetry)
  • Chooses interactive mode vs one-shot execution from the arguments

Why split the entry?

The shipped bundle is a ~13 MB JavaScript file. Loading all of that for claude --version would make simple commands feel sluggish. The split keeps trivial invocations instant and defers the big import until you need the full app.

This is a common CLI pattern: lazy loading.

Initialization: before the model sees you ​

init.ts is where setup happens. Before your first message reaches the model, the runtime has to get its house in order:

Highlights:

  • Config layering: values merge from several sources; later layers override earlier ones:
    默认值 → 全局配置 → 项目配置 → 本地配置 → CLI 参数 → 环境变量
  • Trust prompts: the first time you open a project directory, Claude Code may ask you to confirm you trust it
  • Graceful exit: handlers for SIGINT/SIGTERM so interrupts are less likely to leave half-written files

Two run modes ​

After init, behavior branches based on how you invoked the tool.

Interactive mode (default) ​

The usual experience: a React + Ink terminal UI and an ongoing conversation:

You: Refactor this function for me.
Claude: Let me read it first… (Read)
        Here’s what’s wrong… (analysis)
        Applying edits… (Edit)
        Done—want a quick summary?
You: Add a test too.
Claude: …

One-shot mode (-p) ​

For scripts and automation: run once, print to stdout, exit:

bash
claude -p "Our README is missing install steps—add them."

No full TUI; good for CI/CD, wrappers, and shell pipelines.

Commander.js layout ​

Claude Code uses Commander.js for the CLI. If you have built Node CLIs before, this will feel familiar:

claude                    → interactive (default)
claude -p "..."           → one-shot
claude mcp serve          → MCP server
claude config set ...     → configuration
claude doctor             → diagnostics
claude resume             → resume last session

Each subcommand maps to a file under commands/, wired up from commands.ts.

Recap ​

Startup is a standard CLI story:

  1. Thin entry → cheap commands stay cheap
  2. Full init → config, safety, environment
  3. Mode switch → interactive vs one-shot

No magic—just solid engineering. Next we get to the real engine—how the agent loop works.