全局視角:不只是 CLI 工具
從 4756 個檔案看 Claude Code 的整體架構:多入口設計、fast-path 分發、101 個命令的背後邏輯
先從數字開始
Claude Code 的程式碼庫有 4756 個檔案。一個「CLI 工具」通常不需要這個體量。
拆開看才明白它在做什麼:CLI 只是最薄的那一層。底下是一套完整的 Agent 執行引擎——帶工具系統、多 Agent 調度、安全層、Plugin 生態,外加一套把所有這些暴露給第三方的 SDK。
把它叫做 CLI 工具,就像把 VSCode 叫做「能開檔案的軟體」。技術上沒錯,但完全沒說到重點。
四個入口,四種使用方式
程式碼庫的頂層有四個入口點,每個面向不同的呼叫者。
終端使用者走 cli.tsx:claude 指令從這裡進來,初始化 TTY、載入設定、啟動 React/Ink 互動介面。CI/CD 或腳本呼叫走 init.ts,不需要介面,靜默把環境設置好就行。mcp.ts 則把整個工具系統以 MCP 伺服器的形式暴露出去,讓其他 Agent 能接入。最後是 sdk/——第三方應用直接 import,繞過所有 CLI 層,直達核心執行邏輯。
四個入口共用同一套引擎,能力層和呈現層分離,同一套工具系統能在不同場景下複用。這是刻意的。
Fast-path 分發
cli.tsx 啟動後的第一件事不是直接進入對話迴圈,而是做 fast-path 分發。
判斷邏輯大致是:有沒有明確的子指令?是不是 --print 模式(非互動單次執行)?是不是 pipe 進來的輸入?
根據這些條件,請求被分流到不同的執行路徑:
- 有子指令(
claude config、claude doctor等)→ 直接執行對應指令,不啟動 Agent 引擎 --print或 pipe 輸入 → 進入無 UI 的執行路徑,輸出到 stdout 後退出- 互動模式 → 初始化完整的 React/Ink UI,進入主迴圈
這個分發層的存在讓 101 個指令能在同一個二進位檔案裡共存,同時不讓「查個設定」這種輕量操作也跑一遍完整的 Agent 初始化流程。
101 個命令的組織方式
101 個內建命令不是平鋪在一個大檔案裡的。它們按功能分組,每個命令是一個獨立模組,符合一個標準介面:
interface Command {
name: string
description: string
handler: (args: string[], context: CommandContext) => Promise<void>
}
命令在啟動時動態載入,按需執行。使用者看到的 /help 列表是執行時掃描所有已載入命令後生成的,不是硬編碼的字串。
這個設計的副作用是 Plugin 也能用同樣的機制注入自訂命令。Plugin 系統不需要特殊的「命令注入 API」,因為命令本身就是資料,是可以動態添加的。
為什麼要這麼複雜
簡單的 CLI 工具只需要:接受輸入 → 呼叫 API → 印出結果。三步走,幾百行搞定。
Claude Code 的複雜度來自它要解決的問題不同。
對話歷史、專案上下文、使用者偏好得在多輪之間保持一致——這是持久狀態問題。模型說「執行這個指令」時,不能直接執行,要先問能不能執行、怎麼執行、失敗了誰負責——這是工具治理問題。再往上,多 Agent 協作意味著拆任務、並行、整合結果,主迴圈必須是個調度器,不只是個對話框。最後一點最難量化:沒人能預測所有使用場景,所以生態擴展得是一等公民,而不是事後補丁。
四個問題,四層架構。4756 個檔案是這些問題累積出來的,不是過度工程。
架構全圖
粗略的層次結構是這樣的:
入口層 (cli.tsx / init.ts / mcp.ts / sdk/)
↓
Fast-path 分發
↓
主迴圈引擎 (query.ts)
↓
工具系統 (42 個工具 + 治理 Pipeline)
↓
多 Agent 調度 (AgentTool.tsx + 6 種 Agent)
↓
安全層 (權限系統 + Hook + Classifier)
↓
生態層 (Skill / Plugin / MCP)
後面七章每章對應一層。從引擎開始,一層一層拆開來看。
參考來源: 本文內容參考 Xiao Tan(@tvytlx)的《Claude Code 源碼架構深度解析 V2.0》,基於原報告的分析框架和研究成果整理。