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

本章总览

Doctor.tsx(约 575 行)渲染 Claude Code 安装与配置诊断全屏报告:版本、包管理器、MCP 解析警告、Agent 目录、沙箱状态、插件错误、权限规则 unreachable 警告等。由 /doctor 斜杠命令或 claude doctor CLI 挂载,完成后 onDone 返回 REPL。本章从 screens 层讲解数据收集与分区渲染;底层 diagnostic 逻辑在 utils/doctorDiagnostic.ts。

学完本章你应该能

  • 说明 Doctor Props(onDone)与命令注册路径
  • 描述 getDoctorDiagnostic 与并行 agent/context/lock 探测
  • 理解 validationErrors、envValidationErrors、pluginsErrors 分区
  • 掌握 SandboxDoctorSection 与 McpParsingWarnings 子组件接缝
  • 区分 Doctor 与 REPL 内嵌 settings 错误通知的差异

核心概念(先读懂这些)

Doctor 是只读诊断页,不参与 query 循环

Doctor 不 import REPL、不持有 messages state。useExitOnCtrlCDWithKeybindings 允许 Ctrl+C 退出;confirm:yes/no 键绑定调用 onDone。这与 ResumeConversation(handoff 到 REPL)和 REPL(长生命周期)形成 screens 层三分类:主会话 / 向导 / 一次性工具页。

诊断数据分 sync 与 async 两波

distTagsPromise 在 render 时启动 getDoctorDiagnostic().then;useEffect 内并行 pathExists agents 目录、checkContextWarnings、pid lock 清理。diagnostic 未返回前显示 "Checking installation status…"。

建议学习步骤

  1. 阅读 export function Doctor 与 state 声明(源码块 A)
  2. 阅读 useEffect 数据收集(源码块 B)
  3. 阅读 diagnostic 就绪后的安装信息区(源码块 C)
  4. 阅读 Sandbox/MCP/Plugin/Context 警告区(源码块 D)
  5. 对照 commands/doctor 与 cli handlers 懒加载(源码块 E)

常见误区

注意

DoctorWithPlugins 在 cli/handlers/util.tsx 用 React.lazy 包裹,避免非 doctor 路径加载

注意

envValidationErrors 硬编码 BASH_MAX_OUTPUT、TASK_MAX_OUTPUT、CLAUDE_CODE_MAX_OUTPUT_TOKENS 上限

注意

pluginsErrors 来自 AppState,与 useManagePlugins 写入路径一致

注意

agentInfo.failedFiles 与 loadAgentsDir 解析失败同源,修 agent.md 后需重跑 /doctor

注意

Dismiss 后 transcript 插入 system 行

screens 层入口:命令与 CLI

Doctor 有两条挂载路径:

1. 交互 REPL 内 /doctor
   commands/doctor/doctor.tsx
     → Promise.resolve(<Doctor onDone={onDone} />)

2. claude doctor(非交互/util handler)
   cli/handlers/util.tsx
     → DoctorWithPlugins = React.lazy(() => import('screens/Doctor'))
     → Suspense fallback={null}

onDone 签名:(result?: string, options?: { display?: CommandResultDisplay }) =&gt; void。Dismiss 时传递 "Claude Code diagnostics dismissed" 与 display: 'system',在 transcript 插入 system 行。

Doctor 不是 Screen 枚举值;执行完毕返回 REPL prompt 视图,不修改 REPL 的 screen state。

源码引用: src/commands/doctor/doctor.tsx · 第 1–5 行(共 8 行)

   1| import React from 'react'
   2| import { Doctor } from '../../screens/Doctor.js'
   3| import type { LocalJSXCommandCall } from '../../types/command.js'
   4| 
   5| export const call: LocalJSXCommandCall = (onDone, _context, _args) => {

源码引用: src/cli/handlers/util.tsx · 第 52–64 行(共 127 行)

  52|           </Box>
  53|         </KeybindingSetup>
  54|       </AppStateProvider>,
  55|     )
  56|   })
  57|   root.unmount()
  58|   process.exit(0)
  59| }
  60| 
  61| // DoctorWithPlugins wrapper + doctor handler
  62| const DoctorLazy = React.lazy(() =>
  63|   import('../../screens/Doctor.js').then(m => ({ default: m.Doctor })),
  64| )

Doctor 组件 state 与 AppState 订阅

Doctor 从 AppState 读取:

  • agentDefinitions — activeAgents、failedFiles
  • mcpTools — 默认 [],供 checkContextWarnings
  • toolPermissionContext — async 传入 context warnings
  • plugins.errors — 插件加载失败列表

本地 state:

  • diagnostic — DiagnosticInfo(安装类型、版本、路径)
  • agentInfo — 用户/项目 agents 目录存在性与 failedFiles
  • contextWarnings — CLAUDE.md / agent / MCP token 占用
  • versionLockInfo — PID lock 清理结果

useSettingsErrors() 提供 settings.json 校验错误;errorsExcludingMcp 过滤 MCP 相关项单独展示。

源码引用: src/screens/Doctor.tsx · 第 32–56 行(共 517 行)

  32| } from '../utils/autoUpdater.js'
  33| import {
  34|   type ContextWarnings,
  35|   checkContextWarnings,
  36| } from '../utils/doctorContextWarnings.js'
  37| import {
  38|   type DiagnosticInfo,
  39|   getDoctorDiagnostic,
  40| } from '../utils/doctorDiagnostic.js'
  41| import { validateBoundedIntEnvVar } from '../utils/envValidation.js'
  42| import { pathExists } from '../utils/file.js'
  43| import {
  44|   cleanupStaleLocks,
  45|   getAllLockInfo,
  46|   isPidBasedLockingEnabled,
  47|   type LockInfo,
  48| } from '../utils/nativeInstaller/pidLock.js'
  49| import { getInitialSettings } from '../utils/settings/settings.js'
  50| import {
  51|   BASH_MAX_OUTPUT_DEFAULT,
  52|   BASH_MAX_OUTPUT_UPPER_LIMIT,
  53| } from '../utils/shell/outputLimits.js'
  54| import {
  55|   TASK_MAX_OUTPUT_DEFAULT,
  56|   TASK_MAX_OUTPUT_UPPER_LIMIT,

源码引用: src/screens/Doctor.tsx · 第 100–123 行(共 517 行)

 100|   )
 101| }
 102| 
 103| export function Doctor({ onDone }: Props): React.ReactNode {
 104|   const agentDefinitions = useAppState(s => s.agentDefinitions)
 105|   const mcpTools = useAppState(s => s.mcp.tools)
 106|   const toolPermissionContext = useAppState(s => s.toolPermissionContext)
 107|   const pluginsErrors = useAppState(s => s.plugins.errors)
 108|   useExitOnCtrlCDWithKeybindings()
 109| 
 110|   const tools = useMemo(() => {
 111|     return mcpTools || []
 112|   }, [mcpTools])
 113| 
 114|   const [diagnostic, setDiagnostic] = useState<DiagnosticInfo | null>(null)
 115|   const [agentInfo, setAgentInfo] = useState<AgentInfo | null>(null)
 116|   const [contextWarnings, setContextWarnings] =
 117|     useState<ContextWarnings | null>(null)
 118|   const [versionLockInfo, setVersionLockInfo] =
 119|     useState<VersionLockInfo | null>(null)
 120|   const validationErrors = useSettingsErrors()
 121| 
 122|   // Create promise once for dist-tags fetch (depends on diagnostic)
 123|   const distTagsPromise = useMemo(

useEffect:并行诊断收集

mount 后 useEffect 触发:

  1. getDoctorDiagnostic().then(setDiagnostic) — npm/gcs dist tags、安装路径
  2. 异步 IIFE:
    • pathExists userAgentsDir / projectAgentsDir
    • checkContextWarnings(tools, { activeAgents, allAgents, failedFiles }, ...)
    • isPidBasedLockingEnabled() 时 cleanupStaleLocks + getAllLockInfo

validateBoundedIntEnvVar 检查环境变量是否在 default..upperLimit 内,生成 envValidationErrors。

distTagsPromise 同时喂给 Suspense 包裹的 DistTagsDisplay 子组件,展示 latest/stable 版本行。

源码引用: src/screens/Doctor.tsx · 第 124–161 行(共 517 行)

 124|     () =>
 125|       getDoctorDiagnostic().then(diag => {
 126|         const fetchDistTags =
 127|           diag.installationType === 'native' ? getGcsDistTags : getNpmDistTags
 128|         return fetchDistTags().catch(() => ({ latest: null, stable: null }))
 129|       }),
 130|     [],
 131|   )
 132|   const autoUpdatesChannel =
 133|     getInitialSettings()?.autoUpdatesChannel ?? 'latest'
 134| 
 135|   const errorsExcludingMcp = validationErrors.filter(
 136|     error => error.mcpErrorMetadata === undefined,
 137|   )
 138| 
 139|   const envValidationErrors = useMemo(() => {
 140|     const envVars = [
 141|       {
 142|         name: 'BASH_MAX_OUTPUT_LENGTH',
 143|         default: BASH_MAX_OUTPUT_DEFAULT,
 144|         upperLimit: BASH_MAX_OUTPUT_UPPER_LIMIT,
 145|       },
 146|       {
 147|         name: 'TASK_MAX_OUTPUT_LENGTH',
 148|         default: TASK_MAX_OUTPUT_DEFAULT,
 149|         upperLimit: TASK_MAX_OUTPUT_UPPER_LIMIT,
 150|       },
 151|       {
 152|         name: 'CLAUDE_CODE_MAX_OUTPUT_TOKENS',
 153|         // Check for values against the latest supported model
 154|         ...getModelMaxOutputTokens('claude-opus-4-6'),
 155|       },
 156|     ]
 157|     return envVars
 158|       .map(v => {
 159|         const value = process.env[v.name]
 160|         const result = validateBoundedIntEnvVar(
 161|           v.name,

源码引用: src/screens/Doctor.tsx · 第 164–221 行(共 517 行)

 164|           v.upperLimit,
 165|         )
 166|         return { name: v.name, ...result }
 167|       })
 168|       .filter(v => v.status !== 'valid')
 169|   }, [])
 170| 
 171|   useEffect(() => {
 172|     void getDoctorDiagnostic().then(setDiagnostic)
 173| 
 174|     void (async () => {
 175|       const userAgentsDir = join(getClaudeConfigHomeDir(), 'agents')
 176|       const projectAgentsDir = join(getOriginalCwd(), '.claude', 'agents')
 177| 
 178|       const { activeAgents, allAgents, failedFiles } = agentDefinitions
 179| 
 180|       const [userDirExists, projectDirExists] = await Promise.all([
 181|         pathExists(userAgentsDir),
 182|         pathExists(projectAgentsDir),
 183|       ])
 184| 
 185|       const agentInfoData = {
 186|         activeAgents: activeAgents.map(a => ({
 187|           agentType: a.agentType,
 188|           source: a.source,
 189|         })),
 190|         userAgentsDir,
 191|         projectAgentsDir,
 192|         userDirExists,
 193|         projectDirExists,
 194|         failedFiles,
 195|       }
 196|       setAgentInfo(agentInfoData)
 197| 
 198|       const warnings = await checkContextWarnings(
 199|         tools,
 200|         {
 201|           activeAgents,
 202|           allAgents,
 203|           failedFiles,
 204|         },
 205|         async () => toolPermissionContext,
 206|       )
 207|       setContextWarnings(warnings)
 208| 
 209|       // Fetch version lock info if PID-based locking is enabled
 210|       if (isPidBasedLockingEnabled()) {
 211|         const locksDir = join(getXDGStateHome(), 'claude', 'locks')
 212|         const staleLocksCleaned = cleanupStaleLocks(locksDir)
 213|         const locks = getAllLockInfo(locksDir)
 214|         setVersionLockInfo({
 215|           enabled: true,
 216|           locks,
 217|           locksDir,
 218|           staleLocksCleaned,
 219|         })
 220|       } else {
 221|         setVersionLockInfo({

安装信息与版本区

diagnostic 就绪后渲染 Diagnostics 标题块:

  • Currently running: {installationType} ({version})
  • Package manager(若有)
  • Path: installationPath
  • Invoked binary
  • Auto-update channel(来自 getInitialSettings)
  • DistTagsDisplay:latest / stable(Suspense)

PressEnterToContinue 底部等待用户确认;键绑定 confirm:yes / confirm:no 均触发 handleDismiss。

loading 态:&lt;Pane&gt;&lt;Text dimColor&gt;Checking installation status…&lt;/Text&gt;&lt;/Pane&gt;。

源码引用: src/screens/Doctor.tsx · 第 256–265 行(共 517 行)

 256|       <Box flexDirection="column">
 257|         <Text bold>Diagnostics</Text>
 258|         <Text>
 259|           └ Currently running: {diagnostic.installationType} (
 260|           {diagnostic.version})
 261|         </Text>
 262|         {diagnostic.packageManager && (
 263|           <Text>└ Package manager: {diagnostic.packageManager}</Text>
 264|         )}
 265|         <Text>└ Path: {diagnostic.installationPath}</Text>

源码引用: src/screens/Doctor.tsx · 第 266–297 行(共 517 行)

 266|         <Text>└ Invoked: {diagnostic.invokedBinary}</Text>
 267|         <Text>└ Config install method: {diagnostic.configInstallMethod}</Text>
 268|         <Text>
 269|           └ Search: {diagnostic.ripgrepStatus.working ? 'OK' : 'Not working'} (
 270|           {diagnostic.ripgrepStatus.mode === 'embedded'
 271|             ? 'bundled'
 272|             : diagnostic.ripgrepStatus.mode === 'builtin'
 273|               ? 'vendor'
 274|               : diagnostic.ripgrepStatus.systemPath || 'system'}
 275|           )
 276|         </Text>
 277| 
 278|         {/* Show recommendation if auto-updates are disabled */}
 279|         {diagnostic.recommendation && (
 280|           <>
 281|             <Text></Text>
 282|             <Text color="warning">
 283|               Recommendation: {diagnostic.recommendation.split('\n')[0]}
 284|             </Text>
 285|             <Text dimColor>{diagnostic.recommendation.split('\n')[1]}</Text>
 286|           </>
 287|         )}
 288| 
 289|         {/* Show multiple installations warning */}
 290|         {diagnostic.multipleInstallations.length > 1 && (
 291|           <>
 292|             <Text></Text>
 293|             <Text color="warning">Warning: Multiple installations found</Text>
 294|             {diagnostic.multipleInstallations.map((install, i) => (
 295|               <Text key={i}>
 296|                 └ {install.type} at {install.path}
 297|               </Text>

源码引用: src/screens/Doctor.tsx · 第 57–99 行(共 517 行)

  57| } from '../utils/task/outputFormatting.js'
  58| import { getXDGStateHome } from '../utils/xdg.js'
  59| 
  60| type Props = {
  61|   onDone: (
  62|     result?: string,
  63|     options?: { display?: CommandResultDisplay },
  64|   ) => void
  65| }
  66| 
  67| type AgentInfo = {
  68|   activeAgents: Array<{
  69|     agentType: string
  70|     source: SettingSource | 'built-in' | 'plugin'
  71|   }>
  72|   userAgentsDir: string
  73|   projectAgentsDir: string
  74|   userDirExists: boolean
  75|   projectDirExists: boolean
  76|   failedFiles?: Array<{ path: string; error: string }>
  77| }
  78| 
  79| type VersionLockInfo = {
  80|   enabled: boolean
  81|   locks: LockInfo[]
  82|   locksDir: string
  83|   staleLocksCleaned: number
  84| }
  85| 
  86| function DistTagsDisplay({
  87|   promise,
  88| }: {
  89|   promise: Promise<NpmDistTags>
  90| }): React.ReactNode {
  91|   const distTags = use(promise)
  92|   if (!distTags.latest) {
  93|     return <Text dimColor>└ Failed to fetch versions</Text>
  94|   }
  95|   return (
  96|     <>
  97|       {distTags.stable && <Text>└ Stable version: {distTags.stable}</Text>}
  98|       <Text>└ Latest version: {distTags.latest}</Text>
  99|     </>

警告与错误分区

Doctor JSX 树按类别堆叠 Box:

分区数据源组件
Sandbox沙箱服务状态SandboxDoctorSection
MCP 解析MCP config 警告McpParsingWarnings
快捷键keybindings 冲突KeybindingWarnings
环境变量envValidationErrors内联 map
Version LocksversionLockInfostale locks / active locks
Agent ParseagentInfo.failedFileserror 色
Plugin ErrorspluginsErrorsgetPluginErrorMessage
Unreachable RulescontextWarningspermission rules
Context UsageclaudeMd/agent/mcp warningfigures.warning 前缀

ValidationErrorsList 展示 settings 校验(排除 MCP 后)。

这种分区使 screens/Doctor 保持「报告排版」,具体检测逻辑留在 utils/。

源码引用: src/screens/Doctor.tsx · 第 400–410 行(共 517 行)

 400|             └ Failed to parse {agentInfo.failedFiles.length} agent file(s):
 401|           </Text>
 402|           {agentInfo.failedFiles.map((file, i) => (
 403|             <Text key={i} dimColor>
 404|               {'  '}└ {file.path}: {file.error}
 405|             </Text>
 406|           ))}
 407|         </Box>
 408|       )}
 409| 
 410|       {/* Plugin Errors */}

源码引用: src/screens/Doctor.tsx · 第 425–439 行(共 517 行)

 425|           ))}
 426|         </Box>
 427|       )}
 428| 
 429|       {/* Unreachable Permission Rules Warning */}
 430|       {contextWarnings?.unreachableRulesWarning && (
 431|         <Box flexDirection="column">
 432|           <Text bold color="warning">
 433|             Unreachable Permission Rules
 434|           </Text>
 435|           <Text>
 436|             └{' '}
 437|             <Text color="warning">
 438|               {figures.warning}{' '}
 439|               {contextWarnings.unreachableRulesWarning.message}

源码引用: src/screens/Doctor.tsx · 第 448–478 行(共 517 行)

 448|       )}
 449| 
 450|       {/* Context Usage Warnings */}
 451|       {contextWarnings &&
 452|         (contextWarnings.claudeMdWarning ||
 453|           contextWarnings.agentWarning ||
 454|           contextWarnings.mcpWarning) && (
 455|           <Box flexDirection="column">
 456|             <Text bold>Context Usage Warnings</Text>
 457| 
 458|             {contextWarnings.claudeMdWarning && (
 459|               <>
 460|                 <Text>
 461|                   └{' '}
 462|                   <Text color="warning">
 463|                     {figures.warning} {contextWarnings.claudeMdWarning.message}
 464|                   </Text>
 465|                 </Text>
 466|                 <Text>{'  '}└ Files:</Text>
 467|                 {contextWarnings.claudeMdWarning.details.map((detail, i) => (
 468|                   <Text key={i} dimColor>
 469|                     {'    '}└ {detail}
 470|                   </Text>
 471|                 ))}
 472|               </>
 473|             )}
 474| 
 475|             {contextWarnings.agentWarning && (
 476|               <>
 477|                 <Text>
 478|                   └{' '}

键绑定与退出

useExitOnCtrlCDWithKeybindings() — 标准 Ctrl+C 双击退出行为。

useKeybindings 注册 Confirmation 上下文:

confirm:yes → handleDismiss
confirm:no  → handleDismiss

handleDismiss 调用 onDone("Claude Code diagnostics dismissed", { display: "system" })。

与 REPL 的区别:Doctor 不设置 focusedInputDialog,不占用 tool-permission overlay 焦点栈。

源码引用: src/screens/Doctor.tsx · 第 222–255 行(共 517 行)

 222|           enabled: false,
 223|           locks: [],
 224|           locksDir: '',
 225|           staleLocksCleaned: 0,
 226|         })
 227|       }
 228|     })()
 229|   }, [toolPermissionContext, tools, agentDefinitions])
 230| 
 231|   const handleDismiss = useCallback(() => {
 232|     onDone('Claude Code diagnostics dismissed', { display: 'system' })
 233|   }, [onDone])
 234| 
 235|   // Handle dismiss via keybindings (Enter, Escape, or Ctrl+C)
 236|   useKeybindings(
 237|     {
 238|       'confirm:yes': handleDismiss,
 239|       'confirm:no': handleDismiss,
 240|     },
 241|     { context: 'Confirmation' },
 242|   )
 243| 
 244|   // Loading state
 245|   if (!diagnostic) {
 246|     return (
 247|       <Pane>
 248|         <Text dimColor>Checking installation status…</Text>
 249|       </Pane>
 250|     )
 251|   }
 252| 
 253|   // Format the diagnostic output according to spec
 254|   return (
 255|     <Pane>

源码引用: src/screens/Doctor.tsx · 第 109–109 行(共 517 行)

 109| 

DistTagsDisplay 与 Suspense

Doctor 使用 React 19 use(promise) 模式(DistTagsDisplay 子组件)消费 distTagsPromise,避免在 Doctor 主组件阻塞 render。

promise 来源 getDoctorDiagnostic().then(...) 与 useEffect 内 setDiagnostic 同一 API 两次调用——一次喂 Suspense 子树展示版本行,一次写入 diagnostic state 展示安装类型。缓存应在 doctorDiagnostic 层 dedupe,避免双份 network fetch。

Suspense fallback={null} 使版本行在加载完成前不占位,减少布局跳动。

PressEnterToContinue 与 Pane 布局

Doctor 使用 Pane design-system 组件包裹内容,与 /config 等 local-jsx 命令视觉一致。

PressEnterToContinue 在报告末尾等待确认;配合 Confirmation 键绑定 context,Enter/ y / n 均可 dismiss。

长报告分区滚动由 Ink Box flexDirection column 自然堆叠;error/warning 色来自 Ink Text color prop(error/warning),非 Emoji 前缀。

源码引用: src/screens/Doctor.tsx · 第 12–14 行(共 517 行)

  12| import { McpParsingWarnings } from 'src/components/mcp/McpParsingWarnings.js'
  13| import { getModelMaxOutputTokens } from 'src/utils/context.js'
  14| import { getClaudeConfigHomeDir } from 'src/utils/envUtils.js'

与 main.tsx 和 update 路径

main.tsx 约 4345 行注释 "Doctor command - check installation health" 标记 CLI 路由分支。

cli/update.ts 也调用 getDoctorDiagnostic 做非 UI 更新检查——doctorDiagnostic 是共享服务,Doctor.tsx 仅是 screens 层呈现。

维护建议:新增诊断项时,先在 doctorDiagnostic / checkContextWarnings 加数据,再在 Doctor.tsx 增 Box 分区,保持 screens 文件为薄 presentation 层。

源码引用: src/utils/doctorDiagnostic.ts · 第 1–30 行(共 626 行)

   1| import { execa } from 'execa'
   2| import { readFile, realpath } from 'fs/promises'
   3| import { homedir } from 'os'
   4| import { delimiter, join, posix, win32 } from 'path'
   5| import { checkGlobalInstallPermissions } from './autoUpdater.js'
   6| import { isInBundledMode } from './bundledMode.js'
   7| import {
   8|   formatAutoUpdaterDisabledReason,
   9|   getAutoUpdaterDisabledReason,
  10|   getGlobalConfig,
  11|   type InstallMethod,
  12| } from './config.js'
  13| import { getCwd } from './cwd.js'
  14| import { isEnvTruthy } from './envUtils.js'
  15| import { execFileNoThrow } from './execFileNoThrow.js'
  16| import { getFsImplementation } from './fsOperations.js'
  17| import {
  18|   getShellType,
  19|   isRunningFromLocalInstallation,
  20|   localInstallationExists,
  21| } from './localInstaller.js'
  22| import {
  23|   detectApk,
  24|   detectAsdf,
  25|   detectDeb,
  26|   detectHomebrew,
  27|   detectMise,
  28|   detectPacman,
  29|   detectRpm,
  30|   detectWinget,

源码引用: src/hooks/useManagePlugins.ts · 第 71–71 行(共 305 行)

  71|       // Errors are added to the errors array for user visibility in Doctor UI

本章小结与延伸

Doctor = screens 层健康检查页。改诊断项先查 doctorDiagnostic.ts;改 UI 分区查 Doctor.tsx JSX 树。Dismiss 后 onDone 写入 system 行并返回 REPL。 继续学习:

  • screens 总览
  • services mcp-client
Prev
ResumeConversation · 会话恢复选择器
Next
cli-entrypoint · Bootstrap 与快路径