本项目不是按 MarcelLeon/claude-code 的文件形态逐个翻译到 Java,而是按其外部行为和核心运行模式做 clean-room 复刻。
这里的“行为等价”指:
- CLI 负责接收任务、恢复会话、装配运行时
- Runtime 负责多轮决策、工具执行、结果回填、收敛结束
- Model Gateway 负责把统一领域模型映射到具体大模型供应商
- Tool Layer 负责在工作区内执行本地能力
- Persistence 负责 transcript 持久化与续跑
- Session Metadata 负责会话标题等轻量状态
参考 MarcelLeon/claude-code 的源码结构,可以把行为上最重要的部分抽象成下表:
| Claude Code 抽象 | Java 当前抽象 |
|---|---|
| CLI bootstrap / main entry | cli |
| Query engine / agent loop | runtime |
| API client / provider routing | model |
| tools / tool registry | tool |
| transcript / history | persistence |
| runtime config / init | config + bootstrap |
这意味着我们追求的是“模块职责等价”,而不是“文件名等价”。
当前最核心的领域对象如下:
RunRequest- 表示一次 CLI 任务输入
- 对应外部入口参数,而不是内部推理状态
AgentRuntimeContext- 表示一次运行的静态上下文
- 包括 workspace、provider、model、maxTurns、session 等
ConversationState- 表示一次运行内的动态会话状态
- 持有历史 transcript 和本轮运行产生的工具结果
AgentDecision- 表示模型对“下一步动作”的决策
- 当前只有两种动作:直接回答、调用一个工具
ToolCall- 表示一次工具调用请求
- 当前同时兼容旧的字符串
argument和结构化arguments
ToolArgumentDescriptor- 表示工具对外暴露的参数契约
- 当前用于同时驱动 prompt 注入和
/tools展示
ToolExecutionResult- 表示工具执行结果
TranscriptEntry- 表示持久化会话中的一条记录
- 当前包含 role、content、timestamp,用于续跑和本地成本统计
SessionMetadata- 表示会话的轻量侧边元数据
- 当前用于承载
/rename写入的自定义标题,以及/files所需的上下文文件索引
SessionStatusSummary- 表示当前会话的状态摘要
- 当前用于承载
/status所需的应用版本、工作区、模型配置和工具/上下文统计
SessionCostSummary- 表示当前会话的本地成本摘要
- 当前用于承载
/cost所需的 transcript 计数与上下文统计
这些对象的关系是:
flowchart TD
A["RunRequest"] --> B["AgentRuntimeContext"]
B --> C["ConversationState"]
C --> D["AgentRequestContext"]
D --> E["AgentDecision"]
E --> F["ToolCall"]
F --> G["ToolExecutionResult"]
G --> C
C --> H["TranscriptEntry"]
对交互式 chat,还有一组会话级对象:
ChatSessionState- 表示 REPL 当前选中的会话,以及 provider / model / baseUrl 覆盖
ChatSlashCommand- 表示一个交互式本地命令
ChatSlashCommandDispatcher- 负责按名称/别名分发 slash command
ModelSettingService- 负责统一管理模型设置语义、provider 默认模型解析和
/model命令可观察行为
- 负责统一管理模型设置语义、provider 默认模型解析和
SessionService- 负责统一管理 transcript 与会话元数据
DoctorSummaryService- 负责统一汇总运行环境诊断信息
StatusSummaryService- 负责统一汇总会话状态和版本信息
它们与 runtime 的关系是:
flowchart TD
A["ChatSessionRunner"] --> B["ChatSessionState"]
A --> C["ChatSlashCommandDispatcher"]
C --> D["/status /tools /files /cost /doctor /version /help /exit"]
C --> E["/clear /resume /rename /model /provider /base-url"]
C --> F["ModelSettingService"]
A --> G["AgentRunnerFacade"]
B --> G
当前的主流程按 Java 习惯拆成三段:
-
CLI 装配阶段
CodingAgentApplicationCodingAgentCliCommandRootCommand/RunCommand/ChatCommand/DoctorCommand
-
Runtime 编排阶段
AgentRunnerFacadeAgentRuntimeFactoryCodingAgentEngine
-
Provider / Tool / Persistence 协作阶段
RoutingAgentModelGatewayWorkspaceTool+ToolExecutorTranscriptStore
对应执行流程:
flowchart TD
A["CLI Command"] --> B["AgentRunnerFacade"]
A --> R["RootCommand default chat"]
R --> B
B --> C["SessionService"]
B --> D["AgentRuntimeFactory"]
B --> E["CodingAgentEngine"]
E --> F["TranscriptStore.loadTranscript"]
E --> G["ConversationState"]
E --> H["AgentModelGateway.decide"]
H --> I["RoutingAgentModelGateway"]
I --> J["Mock/OpenAI Gateway"]
E --> K["ToolExecutor"]
K --> L["WorkspaceTool"]
L --> E
E --> M["TranscriptStore.append"]
职责:
- 解析外部命令
- 渲染终端输出
- 收口异常
- 管理交互式 chat 的 slash command 分发
- 持有 chat 会话级状态,例如当前
session-id、provider / model / baseUrl 覆盖
不负责:
- 工具执行细节
- 模型决策逻辑
- transcript 存储细节
职责:
- 管理一次 run 的生命周期
- 维护多轮 loop
- 组装静态上下文与动态会话状态
不负责:
- 供应商 SDK 细节
- 单个工具的实现细节
- CLI 参数解析
职责:
- provider 路由
- 模型设置与 provider-aware 默认模型解析
- prompt 组装
- 结构化决策协议说明、解析与校验
不负责:
- 直接操作工作区文件
- 决定如何落盘 transcript
当前在 Java 实现里,model 进一步拆成两层:
- provider gateway
- 负责请求具体模型供应商
- 如
OpenAiAgentModelGateway
- decision protocol
- 负责定义“模型该返回什么结构”以及“如何把文本解码成领域对象”
- 如
AgentDecisionProtocol/JsonAgentDecisionProtocol
- model setting service
- 负责让 CLI 中的模型设置行为与 runtime 中的实际模型解析保持一致
- 如
ModelSettingService
这样做的原因是:
- provider 更换时,不需要复制粘贴 JSON 提取逻辑
- prompt builder 和 response parser 可以围绕同一协议对象协作
- 后续从 JSON 迁移到更强的结构化协议时,影响面更小
/model命令看到的“当前模型/默认模型”与真正运行时使用的模型不会分叉- 工具层可以先吃下结构化
arguments,再逐步向 provider-native function calling 迁移,而不必一次性重写全部工具实现
职责:
- 实现本地可执行能力
- 暴露工具参数契约
- 保持工作区边界
- 统一返回工具结果
不负责:
- 多轮对话编排
- 模型协议解析
当前 tool 内部进一步分成两层:
- execution contract
- 负责真正执行工具
- 如
WorkspaceTool.execute(...)
- argument contract
- 负责描述工具的输入格式、字段和示例
- 如
ToolArgumentDescriptor
职责:
- transcript 读写
- 会话元数据读写
- 为续跑提供历史记录
- 为会话标题与上下文文件等轻量状态提供侧边存储
不负责:
- 决策下一步动作
为了保持后续可持续迭代,当前实现遵守:
- 不以 TS 文件名或类名为翻译目标
- 优先沉淀 Java 侧稳定的领域模型
- 外部行为尽量向 Claude Code 靠拢
- 内部实现优先遵守 Java/Spring 的模块边界
- 单类尽量控制在 500 行以内
下一轮继续保持同一原则推进:
- 在现有
chat/ slash command 模型上继续丰富交互体验,并让会话内覆盖链路与run命令保持一致 - 把当前 JSON 决策协议升级为更稳的结构化协议
- 把 transcript 续跑从“原始记录回放”提升为更完整的 session runtime
- 为工具权限、流式输出和命令策略增加独立边界