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

本章总览

本章从 screens 层 讲解 REPL:export type Screen、prompt/transcript 视图切换、以及 replLauncher.tsx 如何把 REPL 挂到 Ink 根节点。这与 components/repl 不同——后者深入 toolUseConfirmQueue、canUseTool、FullscreenLayout slot 等编排细节。读完本章你应能回答:「Screen 类型定义在哪?」「transcript 模式何时 early-return?」「ResumeConversation 如何把 initialMessages 交给 REPL?」

学完本章你应该能

  • 说明 Screen = prompt | transcript 的语义与下游消费方(Messages、useGlobalKeybindings)
  • 描述 launchRepl 动态 import 链:App → REPL
  • 解释 transcript 分支的 virtualScroll / dumpMode 降级路径
  • 区分 screens/REPL Props 与 ResumeConversation 传入的 initial* 字段
  • 定位 REPL 导出类型被 hooks/components 引用的接缝

核心概念(先读懂这些)

Screen 是 REPL 内部视图模式,不是应用路由

应用级「页面」有 REPL、ResumeConversation、Doctor 三个 React 根组件;而 Screen 类型仅描述 REPL 内部主区域是 prompt(底部 PromptInput 可见)还是 transcript(全文虚拟滚动、搜索、Ctrl+E 导出)。hooks 如 useCancelRequest、useGlobalKeybindings 通过 import type { Screen } from screens/REPL 读取该类型,保证快捷键行为与当前视图一致。

launchRepl 是最小挂载壳

replLauncher.tsx 仅 22 行动态 import ``,把 AppState 初始化留给 main.tsx。这与 launchResumeChooser 对称——后者包 KeybindingSetup + ResumeConversation。screens 层的职责是「选对根组件 + 传 Props」,不是启动 query 循环。

建议学习步骤

  1. 阅读 Screen 类型与 REPL Props 导出(源码块 A)
  2. 阅读 screen state 初始化与 useSkillsChange(源码块 B)
  3. 阅读 transcript early-return 分支(源码块 C)
  4. 阅读 replLauncher 挂载(源码块 D)
  5. 对照 components/repl 章节的 canUseTool 注册,理解两层分工

常见误区

注意

勿把 mod-components/repl 与 mod-screens/repl-screen 混为一篇——前者编排,后者路由与 Screen 类型

注意

transcript 模式下 PromptInput 不挂载,editorStatus 改在 footer 内联展示

注意

frozenTranscriptState 冻结消息长度,防止 transcript 导出时内存尖峰

screens 层在架构中的位置

Claude Code 终端 UI 可分层理解:

entrypoints/cli.tsx → main.tsx
  ├─ launchRepl(root, appProps, replProps)     → screens/REPL
  ├─ launchResumeChooser(...)                  → screens/ResumeConversation → REPL
  └─ /doctor                                   → screens/Doctor

screens/REPL.tsx 内部:
  useState<Screen>('prompt' | 'transcript')
    prompt    → 常规聊天 + PromptInput bottom slot
    transcript → VirtualMessageList 全文 + TranscriptSearchBar

跨模块 type 导出: export type Props 与 export type Screen 被 replLauncher、ResumeConversation(间接)、Messages、MessageRow、CompactSummary、useGlobalKeybindings 等引用。修改 Screen 联合类型时需全仓搜索 from '../screens/REPL'。

与 components/repl 的分工表:

主题screens/repl-screen(本章)components/repl
Screen prompt/transcript定义、切换条件、transcript JSX 分支提及但不展开
toolUseConfirmQueue不展开队列、overlay、canUseTool
Messages 传参transcript 分支的 props 快照主路径 displayedMessages
挂载入口replLauncher、main.tsxREPL return JSX 结构

Screen 类型与 REPL Props 导出

REPL 文件末尾前导出两个关键类型:

  1. Props — 会话启动参数:initialMessages、remoteSessionConfig、sshSession、thinkingConfig 等
  2. Screen — 仅两值:'prompt' | 'transcript'

ResumeConversation 在 resumeData 就绪后直接 &lt;REPL initialMessages={...} ... /&gt;,不再经过 Resume 专用 Screen 枚举。CLI --resume-session 路径在 main.tsx 预加载消息后同样 launchRepl。

阅读要点:

  • disabled Prop 隐藏输入并禁用 message selector
  • taskListId 启用任务列表自动处理模式
  • thinkingConfig 为必填——screens 层挂载时必须传入

源码引用: src/screens/REPL.tsx · 第 571–598 行(共 7050 行)

 571|   MessageActionsKeybindings,
 572|   MessageActionsBar,
 573|   type MessageActionsState,
 574|   type MessageActionsNav,
 575|   type MessageActionCaps,
 576| } from '../components/messageActions.js'
 577| import { setClipboard } from '../ink/termio/osc.js'
 578| import type { ScrollBoxHandle } from '../ink/components/ScrollBox.js'
 579| import {
 580|   createAttachmentMessage,
 581|   getQueuedCommandAttachments,
 582| } from '../utils/attachments.js'
 583| 
 584| // Stable empty array for hooks that accept MCPServerConnection[] — avoids
 585| // creating a new [] literal on every render in remote mode, which would
 586| // cause useEffect dependency changes and infinite re-render loops.
 587| const EMPTY_MCP_CLIENTS: MCPServerConnection[] = []
 588| 
 589| // Stable stub for useAssistantHistory's non-KAIROS branch — avoids a new
 590| // function identity each render, which would break composedOnScroll's memo.
 591| const HISTORY_STUB = { maybeLoadOlder: (_: ScrollBoxHandle) => {} }
 592| // Window after a user-initiated scroll during which type-into-empty does NOT
 593| // repin to bottom. Josh Rosen's workflow: Claude emits long output → scroll
 594| // up to read the start → start typing → before this fix, snapped to bottom.
 595| // https://anthropic.slack.com/archives/C07VBSHV7EV/p1773545449871739
 596| const RECENT_SCROLL_REPIN_WINDOW_MS = 3000
 597| 
 598| // Use LRU cache to prevent unbounded memory growth

源码引用: src/screens/REPL.tsx · 第 540–570 行(共 7050 行)

 540| /* eslint-disable @typescript-eslint/no-require-imports */
 541| const WebBrowserPanelModule = feature('WEB_BROWSER_TOOL')
 542|   ? (require('../tools/WebBrowserTool/WebBrowserPanel.js') as typeof import('../tools/WebBrowserTool/WebBrowserPanel.js'))
 543|   : null
 544| /* eslint-enable @typescript-eslint/no-require-imports */
 545| import { IssueFlagBanner } from '../components/PromptInput/IssueFlagBanner.js'
 546| import { useIssueFlagBanner } from '../hooks/useIssueFlagBanner.js'
 547| import {
 548|   CompanionSprite,
 549|   CompanionFloatingBubble,
 550|   MIN_COLS_FOR_FULL_SPRITE,
 551| } from '../buddy/CompanionSprite.js'
 552| import { DevBar } from '../components/DevBar.js'
 553| // Session manager removed - using AppState now
 554| import type { RemoteSessionConfig } from '../remote/RemoteSessionManager.js'
 555| import { REMOTE_SAFE_COMMANDS } from '../commands.js'
 556| import type { RemoteMessageContent } from '../utils/teleport/api.js'
 557| import {
 558|   FullscreenLayout,
 559|   useUnseenDivider,
 560|   computeUnseenDivider,
 561| } from '../components/FullscreenLayout.js'
 562| import {
 563|   isFullscreenEnvEnabled,
 564|   maybeGetTmuxMouseHint,
 565|   isMouseTrackingEnabled,
 566| } from '../utils/fullscreen.js'
 567| import { AlternateScreen } from '../ink/components/AlternateScreen.js'
 568| import { ScrollKeybindingHandler } from '../components/ScrollKeybindingHandler.js'
 569| import {
 570|   useMessageActions,

screen state 与命令热重载

const [screen, setScreen] = useState&lt;Screen&gt;('prompt') 与并列状态:

  • showAllInTranscript — transcript 是否展示全部消息(含 thinking)
  • dumpMode — 诊断用:强制 flat render 到终端 scrollback,供 tmux 原生搜索
  • editorStatus — v-for-editor 渲染进度;transcript 时 PromptInput 未挂载,进度显示在 footer

同一段还展示 useSkillsChange 监听 skill 文件变更并重载 localCommands。这是 screens 层「会话壳」与 skills 模块的接缝:skill 变更不重启 REPL,只 refresh 斜杠命令表。

注释强调 standaloneAgentContext 在 main.tsx 或 ResumeConversation 通过 setAppState 初始化,避免 mount useEffect 违反 CLAUDE.md 指南。

源码引用: src/screens/REPL.tsx · 第 676–684 行(共 7050 行)

 676|           <Box flexGrow={1} />
 677|           <Text>{status} </Text>
 678|         </>
 679|       ) : searchBadge ? (
 680|         // Engine-counted — close enough for a rough location hint. May
 681|         // drift from render-count for ghost/phantom messages.
 682|         <>
 683|           <Box flexGrow={1} />
 684|           <Text dimColor>

源码引用: src/screens/REPL.tsx · 第 703–712 行(共 7050 行)

 703|   onCancel,
 704|   setHighlight,
 705|   initialQuery,
 706| }: {
 707|   jumpRef: RefObject<JumpHandle | null>
 708|   count: number
 709|   current: number
 710|   /** Enter — commit. Query persists for n/N. */
 711|   onClose: (lastQuery: string) => void
 712|   /** Esc/ctrl+c/ctrl+g — undo to pre-/ state. */

transcript 模式 early-return

当 screen === 'transcript' 时,REPL 在 main return 之前 early-return 整棵 transcript JSX 树,与 prompt 模式互斥。

设计要点:

  1. virtualScrollActive — 全屏 + 未 disableVirtualScroll + 非 dumpMode 时启用 VirtualMessageList
  2. transcriptMessages — 使用 frozenTranscriptState 切片,避免导出时克隆整个 deferred 数组
  3. TranscriptSearchBar — searchOpen 时 bottom 槽替换为搜索栏;Enter 提交、Esc 恢复 searchQuery
  4. ScrollKeybindingHandler 必须在 CancelRequestHandler 之前,保证有选区时 Ctrl+C 复制

kill switch:CLAUDE_CODE_DISABLE_VIRTUAL_SCROLL 或 dumpMode 时 fall through 到 legacy 30 条 cap + Ctrl+E 路径。

这与 components/repl 章节描述的 mainReturn 是 同一文件的两条渲染路径;本章聚焦分支条件,编排章节聚焦 prompt 路径的权限 overlay。

源码引用: src/screens/REPL.tsx · 第 4380–4402 行(共 7050 行)

4380|               // Inject meta messages (model-visible, user-hidden) into the transcript
4381|               if (doneOptions?.metaMessages?.length) {
4382|                 newMessages.push(
4383|                   ...doneOptions.metaMessages.map(content =>
4384|                     createUserMessage({ content, isMeta: true }),
4385|                   ),
4386|                 )
4387|               }
4388|               if (newMessages.length) {
4389|                 setMessages(prev => [...prev, ...newMessages])
4390|               }
4391|               // Restore stashed prompt after local-jsx command completes.
4392|               // The normal stash restoration path (below) is skipped because
4393|               // local-jsx commands return early from onSubmit.
4394|               if (stashedPrompt !== undefined) {
4395|                 setInputValue(stashedPrompt.text)
4396|                 helpers.setCursorOffset(stashedPrompt.cursorOffset)
4397|                 setPastedContents(stashedPrompt.pastedContents)
4398|                 setStashedPrompt(undefined)
4399|               }
4400|             }
4401| 
4402|             // Build context for the command (reuses existing getToolUseContext).

源码引用: src/screens/REPL.tsx · 第 4392–4428 行(共 7050 行)

4392|               // The normal stash restoration path (below) is skipped because
4393|               // local-jsx commands return early from onSubmit.
4394|               if (stashedPrompt !== undefined) {
4395|                 setInputValue(stashedPrompt.text)
4396|                 helpers.setCursorOffset(stashedPrompt.cursorOffset)
4397|                 setPastedContents(stashedPrompt.pastedContents)
4398|                 setStashedPrompt(undefined)
4399|               }
4400|             }
4401| 
4402|             // Build context for the command (reuses existing getToolUseContext).
4403|             // Read messages via ref to keep onSubmit stable across message
4404|             // updates — matches the pattern at L2384/L2400/L2662 and avoids
4405|             // pinning stale REPL render scopes in downstream closures.
4406|             const context = getToolUseContext(
4407|               messagesRef.current,
4408|               [],
4409|               createAbortController(),
4410|               mainLoopModel,
4411|             )
4412| 
4413|             const mod = await matchingCommand.load()
4414|             const jsx = await mod.call(onDone, context, commandArgs)
4415| 
4416|             // Skip if onDone already fired — prevents stuck isLocalJSXCommand
4417|             // (see processSlashCommand.tsx local-jsx case for full mechanism).
4418|             if (jsx && !doneWasCalled) {
4419|               // shouldHidePromptInput: false keeps Notifications mounted
4420|               // so the onDone result isn't lost
4421|               setToolJSX({
4422|                 jsx,
4423|                 shouldHidePromptInput: false,
4424|                 isLocalJSXCommand: true,
4425|               })
4426|             }
4427|           }
4428|           void executeImmediateCommand()

源码引用: src/screens/REPL.tsx · 第 4342–4365 行(共 7050 行)

4342|                 display?: CommandResultDisplay
4343|                 metaMessages?: string[]
4344|               },
4345|             ): void => {
4346|               doneWasCalled = true
4347|               setToolJSX({
4348|                 jsx: null,
4349|                 shouldHidePromptInput: false,
4350|                 clearLocalJSX: true,
4351|               })
4352|               const newMessages: MessageType[] = []
4353|               if (result && doneOptions?.display !== 'skip') {
4354|                 addNotification({
4355|                   key: `immediate-${matchingCommand.name}`,
4356|                   text: result,
4357|                   priority: 'immediate',
4358|                 })
4359|                 // In fullscreen the command just showed as a centered modal
4360|                 // pane — the notification above is enough feedback. Adding
4361|                 // "❯ /config" + "⎿ dismissed" to the transcript is clutter
4362|                 // (those messages are type:system subtype:local_command —
4363|                 // user-visible but NOT sent to the model, so skipping them
4364|                 // doesn't change model context). Outside fullscreen the
4365|                 // transcript entry stays so scrollback shows what ran.

replLauncher:screens 层挂载 REPL

launchRepl 是 REPL 进入 Ink 树的唯一 launcher(ResumeConversation 选中后同样最终进入 REPL,但由 Resume 组件内部 render,不再次调用 launchRepl)。

函数签名:

launchRepl(root, appProps, replProps, renderAndRun)
  → dynamic import App + REPL
  → renderAndRun(root, <App {...appProps}><REPL {...replProps} /></App>)

AppWrapperProps 携带 getFpsMetrics、stats、initialState——App 组件负责 AppStateStore Provider,REPL 只消费 useAppState。

性能:dynamic import 使非交互路径(如 cli --version)不加载 5000 行 REPL 模块。

源码引用: src/replLauncher.tsx · 第 12–22 行(共 29 行)

  12| }
  13| 
  14| export async function launchRepl(
  15|   root: Root,
  16|   appProps: AppWrapperProps,
  17|   replProps: REPLProps,
  18|   renderAndRun: (root: Root, element: React.ReactNode) => Promise<void>,
  19| ): Promise<void> {
  20|   const { App } = await import('./components/App.js')
  21|   const { REPL } = await import('./screens/REPL.js')
  22|   await renderAndRun(

源码引用: src/replLauncher.tsx · 第 1–11 行(共 29 行)

   1| import React from 'react'
   2| import type { StatsStore } from './context/stats.js'
   3| import type { Root } from './ink.js'
   4| import type { Props as REPLProps } from './screens/REPL.js'
   5| import type { AppState } from './state/AppStateStore.js'
   6| import type { FpsMetrics } from './utils/fpsTracker.js'
   7| 
   8| type AppWrapperProps = {
   9|   getFpsMetrics: () => FpsMetrics | undefined
  10|   stats?: StatsStore
  11|   initialState: AppState

ResumeConversation → REPL 的 Props 传递

ResumeConversation 不定义 Screen 类型;选中 log 后 setResumeData,下一 render 直接:

<REPL
  initialMessages={resumeData.messages}
  initialFileHistorySnapshots={...}
  initialContentReplacements={...}
  initialAgentName={...}
  ...
/>

REPL 注释(约 677、1980 行)说明 standaloneAgentContext 与 --resume-session CLI 路径共用同一初始化约定。screens 层读者应记住:Resume 不是 REPL 的兄弟 Screen 枚举值,而是 REPL 的前置 picker 组件。

跨项目 resume 时展示 CrossProjectMessage,复制 cd ... && claude --resume ... 到剪贴板而非 mount REPL。

源码引用: src/screens/REPL.tsx · 第 676–678 行(共 7050 行)

 676|           <Box flexGrow={1} />
 677|           <Text>{status} </Text>
 678|         </>

源码引用: src/screens/ResumeConversation.tsx · 第 296–298 行(共 469 行)

 296| 
 297|       const standaloneAgentContext = computeStandaloneAgentContext(
 298|         result.agentName,

Screen 类型的下游消费者

以下模块 import Screen 类型但不在 screens/ 目录:

消费者用途
hooks/useGlobalKeybindings.tsxtranscript 下禁用部分全局快捷键
hooks/useCancelRequest.ts取消请求时考虑当前 screen
components/Messages.tsx按 screen 调整渲染(如 transcript verbose)
components/MessageRow.tsx行内 action 在 transcript 模式隐藏
components/CompactSummary.tsxcompact 摘要展示与 screen 联动

修改 Screen 联合类型时,必须同步这些消费方的分支逻辑,否则会出现 transcript 下仍显示 prompt 专属 UI 的 regression。

源码引用: src/hooks/useGlobalKeybindings.tsx · 第 11–11 行(共 265 行)

  11| import type { Screen } from '../screens/REPL.js'

源码引用: src/components/Messages.tsx · 第 17–17 行(共 1159 行)

  17| import type { Tools } from '../Tool.js'

本章小结与延伸

screens 层的 REPL = 顶层全屏页 + Screen 视图枚举 + launcher 挂载。编排细节见 components/repl;会话恢复见 resume-conversation。 继续学习:

  • screens 总览
  • ResumeConversation
  • components REPL 编排
Prev
模块: screens
Next
ResumeConversation · 会话恢复选择器