Claude Code 源码分析Claude Code 源码分析
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
  • 概览

    • Claude Code 源码分析
    • 源码统计
    • CodeGraph 图谱
  • 架构

    • 系统架构
    • UML 图表索引
    • 查询引擎
    • 核心流程
    • 消息系统
    • 状态管理
  • 功能模块

    • 工具系统
    • 斜杠命令
    • 服务层
    • MCP 协议
    • Skills 技能
    • 子代理系统
  • 分层深度

    • 入口层
    • UI / Ink 层
    • utils 基础设施
    • 桥接 / 远程
    • 上下文压缩
  • 原理与安全

    • 底层原理
    • 技术难点
    • 权限与安全
    • 内部机制
    • 遥测与分析
  • 深度专题

    • Hooks 系统
    • 插件系统
    • 记忆系统
    • API 通信层
    • Ink 终端 UI
    • 认证系统
    • 构建与发布
    • 术语表
  • 调用分析

    • 调用链分析
    • 核心文件索引
  • 模块详解

    • utils

      • 模块: utils
      • messages · 消息工厂与规范化
      • session-storage · JSONL 会话持久化
      • permissions · 工具权限决策
      • shell-hooks · 用户 Shell Hook 系统
    • components

      • 模块: components
      • REPL · 主屏编排
      • messages · 消息行渲染
      • PermissionRequest · 权限弹窗
      • PromptInput · 底部输入
    • services

      • 模块: services
      • api-claude · Anthropic API 流式与重试
      • mcp-client · MCP 连接与工具调用
      • compact · 上下文压缩与自动触发
      • analytics · GrowthBook、Datadog 与 1P 事件
    • tools

      • 模块: tools
      • tool-interface · Tool 契约与注册表
      • bash-tool · Shell 执行与权限
      • streaming-executor · 流式工具并发调度
      • agent-tool · 子 Agent 委派
    • commands

      • 模块: commands
      • command-registry · commands.ts 注册与分派
      • model-command · /model 模型选择
      • mcp-commands · /mcp 服务器管理
      • compact-memory-commands · /compact 与 /memory
    • ink

      • 模块: ink
      • Ink 渲染管线 · Screen 与终端输出
      • 终端事件 · resize、paste、stdin
      • Ink Hooks · 输入、搜索、终端状态
      • Ink 组件 · Box、Text、ScrollBox 原语
    • hooks

      • 模块: hooks
      • useCanUseTool · 权限 UI 接缝
      • 输入与快捷键 Hook
      • 合并态 Hook(MCP + 本地)
      • notifs 通知 Hook
    • bridge

      • 模块: bridge
      • repl-bridge · REPL 桥初始化与传输
      • bridge-messaging · 桥消息路由与入站处理
      • remote-bridge-core · env-less 核心与守护主循环
      • bridge-permissions-ui · 权限、API 与 TUI
    • cli

      • 模块: cli
      • Structured IO · NDJSON SDK 协议
      • CLI Transports · Session Ingress 传输层
      • CLI Handlers · 子命令懒加载实现
      • Update & Upload · 自更新与串行上传原语
    • screens

      • 模块: screens
      • REPL 屏 · Screen 类型与顶层路由
      • ResumeConversation · 会话恢复选择器
      • Doctor · 安装诊断全屏
    • entrypoints

      • 模块: entrypoints
      • cli-entrypoint · Bootstrap 与快路径
      • sdk-types · core / control / runtime 类型体系
      • mcp-entrypoint · MCP stdio 服务器
      • sandbox-types · 沙箱配置单一真相源
    • skills

      • 模块: skills
      • skills-loading · 磁盘加载与 bundled 注册表
      • bundled-skills · 内置 skill 与 initBundledSkills
      • mcp-skills · MCP prompt 转 skill
      • skill-tool-integration · SkillTool 与命令注册
    • types

      • 模块: types
      • message-types · Message 联合与 content blocks
      • tool-permission-types · Tool、Permission、Command 类型
      • api-sdk-types · API 与 Hooks 协议类型
      • misc-types · ids、plugin、generated 与其余类型
    • tasks

      • 模块: tasks
      • local-agent-task · 本地 Agent 与主会话后台化
      • remote-agent-task · 远程 CCR 与 In-Process Teammate
      • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
      • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybindings

      • 模块: keybindings
      • keybinding-registry · 注册、Provider 与 useKeybinding
      • default-bindings · 默认键位表与平台差异
      • command-bindings · command:* 动态斜杠命令绑定
      • vim-bindings · Vim 模式与 keybindings 边界
    • memdir

      • 模块: memdir
      • memdir-core · 路径、加载与 MEMORY.md
      • memory-extraction · extractMemories 与 SessionMemory
      • memdir-commands · /memory、/remember 与命令集成
    • state

      • 模块: state
      • app-state-core · store、AppState 类型与 Provider
      • app-state-selectors · selectors 与 onChangeAppState
      • teammate-state · 队友视图与 swarm 状态
      • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query

      • 模块: query
      • query config 与 deps · 配置快照与依赖注入
      • query tokenBudget · +500k 自动续跑
      • query transitions · Continue / Terminal 状态机
      • query stopHooks · Stop 事件与 turn 结束编排
  • 模块详解(扩展)

    • messages · 消息工厂与规范化
    • session-storage · JSONL 会话持久化
    • permissions · 工具权限决策
    • shell-hooks · 用户 Shell Hook 系统
    • REPL · 主屏编排
    • messages · 消息行渲染
    • PermissionRequest · 权限弹窗
    • PromptInput · 底部输入
    • api-claude · Anthropic API 流式与重试
    • mcp-client · MCP 连接与工具调用
    • compact · 上下文压缩与自动触发
    • analytics · GrowthBook、Datadog 与 1P 事件
    • tool-interface · Tool 契约与注册表
    • bash-tool · Shell 执行与权限
    • streaming-executor · 流式工具并发调度
    • agent-tool · 子 Agent 委派
    • command-registry · commands.ts 注册与分派
    • model-command · /model 模型选择
    • mcp-commands · /mcp 服务器管理
    • compact-memory-commands · /compact 与 /memory
    • Ink 渲染管线 · Screen 与终端输出
    • 终端事件 · resize、paste、stdin
    • Ink Hooks · 输入、搜索、终端状态
    • Ink 组件 · Box、Text、ScrollBox 原语
    • useCanUseTool · 权限 UI 接缝
    • 输入与快捷键 Hook
    • 合并态 Hook(MCP + 本地)
    • notifs 通知 Hook
    • repl-bridge · REPL 桥初始化与传输
    • bridge-messaging · 桥消息路由与入站处理
    • remote-bridge-core · env-less 核心与守护主循环
    • bridge-permissions-ui · 权限、API 与 TUI
    • Structured IO · NDJSON SDK 协议
    • CLI Transports · Session Ingress 传输层
    • CLI Handlers · 子命令懒加载实现
    • Update & Upload · 自更新与串行上传原语
    • REPL 屏 · Screen 类型与顶层路由
    • ResumeConversation · 会话恢复选择器
    • Doctor · 安装诊断全屏
    • cli-entrypoint · Bootstrap 与快路径
    • sdk-types · core / control / runtime 类型体系
    • mcp-entrypoint · MCP stdio 服务器
    • sandbox-types · 沙箱配置单一真相源
    • skills-loading · 磁盘加载与 bundled 注册表
    • bundled-skills · 内置 skill 与 initBundledSkills
    • mcp-skills · MCP prompt 转 skill
    • skill-tool-integration · SkillTool 与命令注册
    • message-types · Message 联合与 content blocks
    • tool-permission-types · Tool、Permission、Command 类型
    • api-sdk-types · API 与 Hooks 协议类型
    • misc-types · ids、plugin、generated 与其余类型
    • local-agent-task · 本地 Agent 与主会话后台化
    • remote-agent-task · 远程 CCR 与 In-Process Teammate
    • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
    • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybinding-registry · 注册、Provider 与 useKeybinding
    • default-bindings · 默认键位表与平台差异
    • command-bindings · command:* 动态斜杠命令绑定
    • vim-bindings · Vim 模式与 keybindings 边界
    • memdir-core · 路径、加载与 MEMORY.md
    • memory-extraction · extractMemories 与 SessionMemory
    • memdir-commands · /memory、/remember 与命令集成
    • app-state-core · store、AppState 类型与 Provider
    • app-state-selectors · selectors 与 onChangeAppState
    • teammate-state · 队友视图与 swarm 状态
    • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query config 与 deps · 配置快照与依赖注入
    • query tokenBudget · +500k 自动续跑
    • query transitions · Continue / Terminal 状态机
    • query stopHooks · Stop 事件与 turn 结束编排
  • 工具详解

    • tool-interface · Tool 契约与注册表
    • tool-permission-types · Tool、Permission、Command 类型
    • 工具: Bash
    • 工具: PowerShell
    • 工具: Agent
    • 工具: LSP
    • 工具: FileEdit
    • 工具: FileRead
    • 工具: Skill
    • 工具: WebFetch
    • 工具: MCP
    • 工具: SendMessage
    • 工具: FileWrite
    • 工具: Config
    • 工具: Grep
    • 工具: Brief
    • 工具: ExitPlanMode
    • 工具: ToolSearch
    • 工具: NotebookEdit
    • 工具: TaskOutput
    • 工具: WebSearch
    • 工具: ScheduleCron

本章总览

src/components/ 是 Claude Code 终端 UI 的 React 组件层(约 400 文件),与 screens/REPL.tsx 共同构成用户可见的交互界面。本模块按子主题拆分:REPL 编排壳、消息渲染、权限弹窗、底部输入框。每篇独立成文,含真实源码引用与 SourceTree 导航。

总览图

渲染图表中…

学完本章你应该能

  • 区分 components/ 与 screens/ 的职责边界
  • 建立「REPL 编排 → Messages 列表 → 单条 Message 组件」的渲染链路心智模型
  • 会从子章节进入 REPL、messages、permissions、PromptInput 深度讲解
  • 理解权限 UI 与 useCanUseTool 的接缝位置

建议学习步骤

  1. 浏览下方源码目录树,点击文件名跳转到对应子章节
  2. 先读 REPL 子章节,理解状态机与 overlay 焦点
  3. 再读 message-components,对照 Messages.tsx 的 MessageRow
  4. 最后读 permissions-ui 与 prompt-input,串起 tool_use 全链路

模块在架构中的位置

components 是 src/ 下的一级目录,共 406 个文件、82,023 行。一句话概括:React + Ink 渲染的 TUI:消息流、权限弹窗、Spinner、任务面板、MCP 配置界面。

概览

指标数值
行数82,023
文件406

子章节导航

子章节主题核心路径
REPL 主屏会话编排、权限队列、布局screens/REPL.tsx
消息组件transcript 行渲染components/messages/
权限 UItool_use 确认弹窗permissions/PermissionRequest.tsx
PromptInput底部输入与提交PromptInput/PromptInput.tsx

UI 壳三层分工

Claude Code 终端 UI 可按「壳层」理解,而不是按文件夹平铺。最外层是 screens/REPL.tsx:它持有 session 状态、多个确认队列,并决定 FullscreenLayout 的 scrollable / bottom / overlay / modal 四个槽位。中间层是编排组件:Messages.tsx 负责列表、虚拟滚动与 brief 过滤;PermissionRequest.tsx 只做 tool → 子组件路由;PromptInput/ 负责输入与 footer。最内层是叶子:messages/* 按 message type 渲染单行,*PermissionRequest/ 展示具体 Allow/Deny UI。

REPL.tsx
  ├─ Messages + VirtualMessageList  → components/messages/*
  ├─ PermissionRequest overlay      → components/permissions/*
  └─ PromptInput (bottom slot)        → components/PromptInput/*

读源码时先问「问题在哪一层」:卡顿在 REPL 状态、列表策略,还是某条消息的 Ink 输出?这一分层也解释为何 REPL 很长但仍可维护,业务 JSX 被下沉到 components,通用壳又被抽到 design-system。

components 与 screens 的分工

screens/ 目录目前以 REPL 为主入口(约 5000 行),承担:

  • 会话生命周期:query 循环、loading、abort、turn 完成回调
  • 全局状态订阅:useAppState、MCP、agent、teammate 视图
  • 多队列协调:toolUseConfirmQueue、promptQueue、sandbox 权限、elicitation
  • 布局骨架:FullscreenLayout、transcript 模式、companion 精灵位

components/ 则是可复用的展示与交互单元:

  • Messages.tsx / MessageRow.tsx:消息列表与单行分发
  • messages/:按 message type 拆分的叶子组件(UserPrompt、AssistantToolUse 等)
  • permissions/:按 Tool 类型拆分的权限对话框
  • PromptInput/:输入框、footer、历史搜索、模式指示器

这种拆分使 REPL 保持「导演」角色,避免在单文件内堆积所有 JSX。新增一种消息类型时,通常改 MessageRow 分发 + messages/ 新文件,而非改 REPL。

与 hooks 模块的接缝

REPL 通过 hooks 完成横切逻辑,组件层消费其结果:

接缝Hook / 工具组件消费方
权限决策useCanUseToolPermissionRequest
输入历史useArrowKeyHistoryPromptInput
终端尺寸useTerminalSize多数 layout 组件
全局快捷键useGlobalKeybindingsREPL 包裹的 Handler

阅读 components 专题时,建议对照 hooks 模块 中的 useCanUseTool 章节,理解 Promise 权限队列如何变成 toolUseConfirmQueue 状态。

design-system 子域深度拆解(实现方式)

components/design-system/ 是终端 UI 的原子层,当前约 15 个 TSX 文件,承载「布局壳 + 提示文案 + 主题 token」三类基础能力。该子域不是业务组件仓库,而是把重复 UI 语义抽为低耦合组件,供 permissions、PromptInput、/config、/help 等多处复用。

核心实现模式:

  1. Dialog = 交互壳(键位 + 标题 + 输入引导)

    • 内部统一绑定 confirm:no(Esc)与 Ctrl+C/D 二次确认
    • isCancelActive 支持嵌套 TextInput 临时接管键盘
    • hideBorder 允许挂在 Pane 内,避免双边框
  2. Pane = 结构壳(分隔线 + 横向内边距)

    • 默认渲染顶部 Divider + 内容区 padding
    • 在 FullscreenLayout modal 槽中探测 useIsInsideModal(),切到单层容器模式
    • 注释明确记录了 flexShrink=0 修复(避免 /permissions 在方向键重渲染时空白)
  3. Divider / Byline / KeyboardShortcutHint = 文本微组件

    • Divider 按终端宽度与 ANSI 文本宽度计算左右线段,保持标题居中
    • Byline 专门做 metadata 串联(· 分隔),自动过滤空子元素
    • KeyboardShortcutHint 把快捷键文案标准化,避免各页面拼接字符串

为什么这一层重要:

  • 把复杂键盘行为从业务页抽离,降低 REPL/Permission 组件体积
  • 统一「确认/取消/帮助提示」语气和视觉结构,减少 UX 漂移
  • 为 React Compiler 产物提供稳定边界:业务组件只组合 design-system,不重复实现细节

源码引用: src/components/design-system/Dialog.tsx · 第 34–99 行(共 101 行)

  34| export function Dialog({
  35|   title,
  36|   subtitle,
  37|   children,
  38|   onCancel,
  39|   color = 'permission',
  40|   hideInputGuide,
  41|   hideBorder,
  42|   inputGuide,
  43|   isCancelActive = true,
  44| }: DialogProps): React.ReactNode {
  45|   const exitState = useExitOnCtrlCDWithKeybindings(
  46|     undefined,
  47|     undefined,
  48|     isCancelActive,
  49|   )
  50| 
  51|   // Use configurable keybinding for ESC to cancel.
  52|   // isCancelActive lets consumers (e.g. ElicitationDialog) disable this while
  53|   // an embedded TextInput is focused, so that keys like 'n' reach the field
  54|   // instead of being consumed here.
  55|   useKeybinding('confirm:no', onCancel, {
  56|     context: 'Confirmation',
  57|     isActive: isCancelActive,
  58|   })
  59| 
  60|   const defaultInputGuide = exitState.pending ? (
  61|     <Text>Press {exitState.keyName} again to exit</Text>
  62|   ) : (
  63|     <Byline>
  64|       <KeyboardShortcutHint shortcut="Enter" action="confirm" />
  65|       <ConfigurableShortcutHint
  66|         action="confirm:no"
  67|         context="Confirmation"
  68|         fallback="Esc"
  69|         description="cancel"
  70|       />
  71|     </Byline>
  72|   )
  73| 
  74|   const content = (
  75|     <>
  76|       <Box flexDirection="column" gap={1}>
  77|         <Box flexDirection="column">
  78|           <Text bold color={color}>
  79|             {title}
  80|           </Text>
  81|           {subtitle && <Text dimColor>{subtitle}</Text>}
  82|         </Box>
  83|         {children}
  84|       </Box>
  85|       {!hideInputGuide && (
  86|         <Box marginTop={1}>
  87|           <Text dimColor italic>
  88|             {inputGuide ? inputGuide(exitState) : defaultInputGuide}
  89|           </Text>
  90|         </Box>
  91|       )}
  92|     </>
  93|   )
  94| 
  95|   if (hideBorder) {
  96|     return content
  97|   }
  98| 
  99|   return <Pane color={color}>{content}</Pane>

源码引用: src/components/design-system/Pane.tsx · 第 33–56 行(共 58 行)

  33| export function Pane({ children, color }: PaneProps): React.ReactNode {
  34|   // When rendered inside FullscreenLayout's modal slot, its ▔ divider IS
  35|   // the frame. Skip our own Divider (would double-frame) and the extra top
  36|   // padding. This lets slash-command screens that wrap in Pane (e.g.
  37|   // /model → ModelPicker) route through the modal slot unchanged.
  38|   if (useIsInsideModal()) {
  39|     // flexShrink=0: the modal slot's absolute Box has no explicit height
  40|     // (grows to fit, maxHeight cap). With flexGrow=1, re-renders cause
  41|     // yoga to resolve this Box's height to 0 against the undetermined
  42|     // parent — /permissions body blanks on Down arrow. See #23592.
  43|     return (
  44|       <Box flexDirection="column" paddingX={1} flexShrink={0}>
  45|         {children}
  46|       </Box>
  47|     )
  48|   }
  49|   return (
  50|     <Box flexDirection="column" paddingTop={1}>
  51|       <Divider color={color} />
  52|       <Box flexDirection="column" paddingX={2}>
  53|         {children}
  54|       </Box>
  55|     </Box>
  56|   )

源码引用: src/components/design-system/Divider.tsx · 第 66–97 行(共 98 行)

  66| export function Divider({
  67|   width,
  68|   color,
  69|   char = '─',
  70|   padding = 0,
  71|   title,
  72| }: DividerProps): React.ReactNode {
  73|   const { columns: terminalWidth } = useTerminalSize()
  74|   const effectiveWidth = Math.max(0, (width ?? terminalWidth) - padding)
  75| 
  76|   if (title) {
  77|     const titleWidth = stringWidth(title) + 2 // +2 for spaces around title
  78|     const sideWidth = Math.max(0, effectiveWidth - titleWidth)
  79|     const leftWidth = Math.floor(sideWidth / 2)
  80|     const rightWidth = sideWidth - leftWidth
  81|     return (
  82|       <Text color={color} dimColor={!color}>
  83|         {char.repeat(leftWidth)}{' '}
  84|         <Text dimColor>
  85|           <Ansi>{title}</Ansi>
  86|         </Text>{' '}
  87|         {char.repeat(rightWidth)}
  88|       </Text>
  89|     )
  90|   }
  91| 
  92|   return (
  93|     <Text color={color} dimColor={!color}>
  94|       {char.repeat(effectiveWidth)}
  95|     </Text>
  96|   )
  97| }

源码引用: src/components/design-system/Byline.tsx · 第 37–58 行(共 58 行)

  37| export function Byline({ children }: Props): React.ReactNode {
  38|   // Children.toArray already filters out null, undefined, and booleans
  39|   const validChildren = Children.toArray(children)
  40| 
  41|   if (validChildren.length === 0) {
  42|     return null
  43|   }
  44| 
  45|   return (
  46|     <>
  47|       {validChildren.map((child, index) => (
  48|         <React.Fragment
  49|           key={isValidElement(child) ? (child.key ?? index) : index}
  50|         >
  51|           {index > 0 && <Text dimColor> · </Text>}
  52|           {child}
  53|         </React.Fragment>
  54|       ))}
  55|     </>
  56|   )
  57| }
  58| 

源码引用: src/components/design-system/KeyboardShortcutHint.tsx · 第 38–59 行(共 59 行)

  38| export function KeyboardShortcutHint({
  39|   shortcut,
  40|   action,
  41|   parens = false,
  42|   bold = false,
  43| }: Props): React.ReactNode {
  44|   const shortcutText = bold ? <Text bold>{shortcut}</Text> : shortcut
  45| 
  46|   if (parens) {
  47|     return (
  48|       <Text>
  49|         ({shortcutText} to {action})
  50|       </Text>
  51|     )
  52|   }
  53|   return (
  54|     <Text>
  55|       {shortcutText} to {action}
  56|     </Text>
  57|   )
  58| }
  59| 

读反编译 UI 的 React Compiler 规则

反编译后的 TSX 常出现 $、_c,或源码注释里的 memoCache、Compiler bailout。教学上记三条即可:

  1. memoCache:Compiler 会把 props 钉在 fiber 缓存里,因此 Messages 刻意不把整段 renderableMessages 传给每行 MessageRow,否则多轮对话会累积历史版本。
  2. bailout:组件内有 Compiler 无法证明安全的模式时,不会自动 memo,需要看源码里的手动 useMemo / React.memo。
  3. 性能注释优先读:例如 BashPermissionRequest 把 shimmer 抽成 ClassifierCheckingSubtitle,比纠结 $ 符号更有价值。

后续读 REPL、Messages、permissions-ui 三章时,遇到 Compiler 产物不要跳过,也不要把它当业务变量;先找源码旁边的性能注释和组件拆分意图。

目录树同时包含 screens/REPL.tsx(编排入口)与 components/ 子树。点击文件将跳转到对应子章节或内联源码锚点。

本章小结与延伸

components 是 Ink 终端 UI 的主体。建议阅读顺序:REPL → 消息组件 → 权限 UI → PromptInput。 继续学习:

  • REPL 主屏
Next
REPL · 主屏编排