Wonder Claude Code - 一个可运行的、教学级的 Claude Code 精简复刻

一个可运行的、教学级的 Claude Code 精简复刻 :在终端里运行的 AI 编程助手(CLI Agent)。 用 TypeScript + Bun 编写,不依赖任何模型 SDK ,直接用 fetch 调 HTTP / SSE。

它对照 Claude Code 的分层结构,从零实现了一个 Agent 的核心骨架:流式对话循环、工具调用、权限闸门、上下文压缩、会话持久化、子助手、MCP 接入、容错重试。目标是让你读完代码就能理解"Claude Code 这类工具到底是怎么转起来的"。

  • 命令名:wonder-cc
  • 数据目录:.wonder-cc/
  • 项目资料文件:AGENTS.md(自动注入系统提示,类似 CLAUDE.md

特性

  • 多模型适配:内置 Anthropic 与 OpenAI 兼容两套适配器。DeepSeek、OpenAI 官方、以及任意 OpenAI 兼容网关都可用;通过环境变量切换,自动探测。
  • 流式对话:SSE 逐字输出,带思考转圈、工具过程、底部状态栏。
  • 工具系统read_file / write_file / edit_text / bash / TodoWrite / Task,外加 MCP 外部工具。
  • 三级权限闸门 :只读命令直接放行;写操作征求同意;rm -rf /sudocurl|bash 等危险命令任何模式都拦截。支持"这次允许 / 永远允许 / 拒绝",规则可跨会话记忆。
  • 上下文工程:粗估 token、三档水位(正常 / 偏满 / 危险),危险水位自动把旧对话压缩成摘要,保留最近几轮原文。
  • 会话持久化 :JSONL 转录逐条追加(崩溃不丢历史),/resume 从压缩快照恢复;命令历史、用量记账、全局偏好都落盘。
  • 子助手(Task) :主助手可派活给独立上下文的子助手,三工种 explore(只读探索)/ plan(方案规划)/ code(动手实现),中间过程不占主对话。
  • MCP 客户端 :通过 stdio + JSON-RPC 接入外部工具生态,工具名统一为 mcp__<server>__<tool>
  • 斜杠命令/init 自动扫描项目生成 AGENTS.md/resume/compact/usage、权限切换等。
  • @提及文件 :输入里写 @src/foo.ts,自动读取文件内容附进消息。
  • 容错重试:429 / 5xx / 网络抖动按指数退避自动重试,401 / 400 等立即报错;流式开始后不重试(避免文字重复打印)。

技术栈

选择
运行时 / 包管理 / 测试 Bunbun run / bun test,自动加载 .env
语言 TypeScript(ESM)
模型接入 原生 fetch + SSE,无 SDK
终端交互 node:readline/promises + ANSI 转义码(自绘 spinner / 状态栏)
子进程 node:child_process spawn(bash 工具、MCP server)
持久化 JSONL 追加写 + JSON 档案

架构分层

目录结构对照 Claude Code 布局:

bash 复制代码
src/
├── entrypoints/cli.ts    # 薄入口:终端 REPL,只做"显示"与"提问"
├── main.ts               # 启动编排:装配 config → adapter → tools → 系统提示 → 权限
├── query.ts              # 核心对话循环(async generator,产出事件,不碰界面)
├── context.ts            # 系统提示拼装(身份/环境/工具清单/规矩 + AGENTS.md)
├── Tool.ts               # Tool 接口定义(说明书 + execute)
├── config/environment.ts # 环境变量读取与服务商自动探测
├── constants/product.ts  # 产品级常量
├── services/
│   ├── api/
│   │   ├── adapter.ts    # 模型适配器(Anthropic / OpenAI 兼容)+ SSE 解析
│   │   ├── types.ts      # 内部统一"普通话"类型(Message / ContentBlock)
│   │   ├── errors.ts     # ApiError + 指数退避重试
│   │   └── usage.ts      # 用量记账与成本估算
│   ├── mcp/
│   │   ├── client.ts     # MCP stdio 客户端(JSON-RPC 2.0)
│   │   └── loader.ts     # MCP 工具加载与包装
│   ├── session.ts        # JSONL 会话转录(meta/message/snapshot)
│   └── task.ts           # 子助手(sub-agent)
├── tools/                # 内置工具 + 权限闸门 + 路径防护
│   ├── permission.ts     # 命令分级(safe/write/danger)与闸门
│   ├── paths.ts          # safePath:防 ../ 越界与符号链接逃逸
│   ├── readFile.ts / writeFile.ts / editText.ts / bash.ts / todoWrite.ts / task.ts
├── query/
│   ├── tokens.ts         # token 粗估
│   ├── tokenBudget.ts    # 三档水位
│   └── compression.ts    # 上下文压缩
├── bootstrap/
│   ├── paths.ts          # 数据目录约定
│   ├── state.ts          # 全局档案 state.json
│   └── history.ts        # 命令历史 history.log
├── commands/
│   ├── registry.ts       # 斜杠命令登记
│   ├── init.ts           # /init 生成 AGENTS.md
│   └── mentions.ts       # @提及文件展开
└── ui/
    ├── display.ts        # 终端渲染(颜色/工具过程/状态栏)
    └── spinner.ts        # 思考转圈

核心设计 :内核与界面解耦。query() 是一个 async generator,只 yield 事件 (assistant_text_delta / tool_start / tool_result / usage / compressing / done), 不打印任何东西;CLI 层消费事件来渲染。权限闸门也不直接提问,而是通过 ToolContext.ask 回调把问题抛给上层(终端弹窗 / 子助手场景自动拒绝)。


安装与运行

需要 Bun ≥ 1.0。

bash 复制代码
# 1. 安装依赖(本项目零运行时依赖,这步主要是建 Bun 环境)
bun install

# 2. 配置模型密钥(二选一或多选)
cp .env.example .env
# 编辑 .env 填入 DEEPSEEK_API_KEY 等

# 3. 直接运行
bun start
# 或
bun run src/entrypoints/cli.ts

想在任意目录用 wonder-cc 启动,可加一个 shell 别名(入口是 .ts,需由 Bun 执行):

bash 复制代码
alias wonder-cc='bun run /绝对路径/wonder-claude-code/src/entrypoints/cli.ts'

模型配置

所有配置通过环境变量(写进 .envexport 到 shell 均可)。

服务商与密钥

环境变量 说明 默认值
DEEPSEEK_API_KEY DeepSeek 密钥 ---
DEEPSEEK_BASE_URL DeepSeek 地址 https://api.deepseek.com
DEEPSEEK_MODEL 模型名 deepseek-chat(推理模型可用 deepseek-reasoner
OPENAI_API_KEY OpenAI 官方 / 兼容网关密钥 ---
OPENAI_BASE_URL OpenAI 地址 https://api.openai.com/v1
OPENAI_MODEL 模型名 gpt-4o-mini
ANTHROPIC_API_KEY Anthropic 密钥 ---
ANTHROPIC_BASE_URL Anthropic 地址 https://api.anthropic.com
ANTHROPIC_MODEL 模型名 claude-sonnet-4-20250514

自动探测规则(未显式指定时):

  • 若设置了 OPENAI_API_KEY → 优先用 OpenAI;
  • 否则若有 DEEPSEEK_API_KEY → 用 DeepSeek;
  • 否则若有 ANTHROPIC_API_KEY → 用 Anthropic。

DeepSeek 与 OpenAI 走同一套 OpenAI 兼容协议;baseURL 支持裸域名、以 /v1 结尾、 或已含 /chat/completions 三种写法(会自动补齐路径)。

其他环境变量

环境变量 说明
WONDER_PROVIDER 强制指定服务商:deepseek / openai / anthropic
WONDER_CONTEXT_WINDOW 覆盖上下文窗口大小(token 数,教学/测试用)
WONDER_PERMISSION_MODE 启动时的权限模式:default / acceptEdits / bypassPermissions
WONDER_MCP_SERVERS MCP server 列表,格式见下

使用

启动后直接输入问题即可。模型会自己决定是否调用工具(读文件、跑命令、改代码等)。

斜杠命令

命令 作用
/init 派探索子助手扫描项目,生成 AGENTS.md(已存在会先确认覆盖)
/resume 列出历史会话,选号恢复(从压缩快照继续)
/compact 手动压缩当前上下文
/usage 查看上下文占用水位与累计 token / 估算成本
/mode 查看当前权限模式
/default 切换到默认模式(写 / 危险操作征求同意)
/acceptEdits 自动允许文件编辑(bash 写命令仍会问)
/bypass 跳过所有权限确认(谨慎使用
/help 显示帮助
/exit(或 /quitexitquit 退出(自动保存命令历史)

内置工具

工具 能力 权限
read_file 读文本文件(超 4 万字符截断) 只读,免确认
write_file 新建 / 覆盖 / 追加文件(自动建父目录) write 级
edit_text 精确小改:replace(要求原文唯一匹配)或 insert(按行号插入) write 级
bash 执行 shell 命令(30s 超时,输出截断 2 万字符) 按命令分级
TodoWrite 维护多步骤待办清单(落盘 .wonder-cc/todos.json 纯记录,免确认
Task 派活给子助手 子助手内部再走闸门

权限模式与命令分级

  • safels / cat / grep / git status 等只读命令,直接执行。
  • writerm / mv / 安装依赖 / git commit/push / 输出重定向等,默认征求同意。
  • dangerrm -rf /sudomkfscurl ... | bash、fork 炸弹等,任何模式都拦截确认。

询问时可选 y(这次允许)/ a(以后同类都允许,规则落盘记忆)/ n(拒绝,默认)。

子助手(Task)

主助手可调用 Task 工具,把活儿外包给独立上下文的子助手:

  • explore:代码探索,只给只读工具,绝不改文件;
  • plan:方案规划,只读 + 待办清单,产出分步计划;
  • code:动手实现,给全套工具,继承主会话权限闸门。

子助手的中间读文件 / 搜索过程通过进度行汇报、不刷爆主对话,最终只回传一份结论。

@提及文件

在输入里写 @相对路径,程序会自动读取该文件并把内容包成 <file path="..."> 块附在消息末尾:

bash 复制代码
帮我看看 @src/query.ts 这个循环有没有问题

路径必须在工作目录内(../ 越界会被拒绝);单个文件超 2 万字符截断;读不到会提示而不报错。

MCP 外部工具

通过环境变量配置 stdio 类型的 MCP server(逗号分隔多台,等号左边是名字、右边是启动命令):

bash 复制代码
export WONDER_MCP_SERVERS='mock=bun scripts/mock-mcp-server.ts, fs=npx -y @modelcontextprotocol/server-filesystem .'

启动时会自动握手(initializetools/list),把外部工具包装成 mcp__<server>__<tool>。 MCP 工具一律按 write 级过权限闸门;某台 server 连不上只跳过并提示,不影响启动。


数据目录

所有落盘数据都在工作目录下的 .wonder-cc/

python 复制代码
.wonder-cc/
├── state.json                       # 全局档案:权限模式、"永远允许"规则、最近会话 id
├── history.log                      # 命令历史(每行一条,上下箭头翻阅)
├── usage.jsonl                      # 用量记账(每次模型请求一行,含估算成本)
├── todos.json                       # TodoWrite 待办清单
└── projects/<工作目录编码>/
    └── <session-id>.jsonl           # 会话转录(meta / message / snapshot 三种行)

会话转录采用追加写 :每条消息、每次压缩快照写一行 JSON,写到一半崩溃也不丢历史。 压缩后会写一个 snapshot 作为恢复点,/resume 时从最后一个快照起叠加后续消息,跳过冗长旧历史。

成本单价是教学用的粗略参考价(人民币 / 百万 token),会随官方调价过时,真实计费以厂商账单为准。


测试与脚本

bash 复制代码
bun test                 # 运行单元测试(适配器翻译 / 权限分级 / token 预算 / 重试 / @提及 / 会话转录)
bun run smoke            # 冒烟脚本

scripts/ 下还有端到端验证脚本(需要配置真实密钥):

  • e2e-stage1/2/3-*.ts:最小闭环、真实 DeepSeek 流式工具调用、上下文压缩;
  • e2e-stage5-persistence.ts:持久化与"重启记忆"验证;
  • e2e-stage6-extensions.ts:MCP 全链路 + 子助手;
  • mock-mcp-server.ts:最小 MCP server(提供 echo / add 两个工具),供测试接入。

与真实 Claude Code 的差距(教学简化点)

这是一个骨架级教学实现,刻意省略了大量工程:

  • 文件编辑是字符串替换(edit_text),非 AST / unified diff 补丁;
  • 无图片 / 多模态、无 plan-mode、hooks、自定义斜杠命令文件、设置 UI;
  • MCP 只实现 stdio 传输与 tools 能力(无 resources / prompts / SSE 传输);
  • 权限是正则黑名单,非静态分析;token 为经验公式粗估,非官方分词器;
  • 无流式中断 / 编辑、无多会话并行、无命令自动补全。

源码地址:github.com/yzsunlei/wo...

配套学习文档

guides/ 目录下有按主题分章的学习笔记(part-01 ~ part-12 + 术语表 / 源码地图 / 阅读指南), 对照本项目的实现讲解 Claude Code 的各个子系统。

第一部分 · 认识它

内容
第 1 篇 · 先认识它 它是什么、四种露面方式、靠什么跑起来、代码仓库导览
第 2 篇 · 启动的秘密 敲下命令后的一瞬间、快速通道、命令参数管理、全局小本本

第二部分 · 核心机制

内容
第 3 篇 · 一问一答怎么转起来 对话循环、流式输出、长对话编排、聊天记录格式、任务中断
第 4 篇 · 工具:AI 的手 工具是什么、随身工具箱、为什么不一次给全、危险操作把关、排队与打断
第 5 篇 · 每次提问都塞了什么资料 十几份背景资料、省钱的缓存、字数预算、自动划重点、记忆、图片语音

第三部分 · 界面与连接

内容
第 6 篇 · 终端里的图形界面 终端怎么画图、用做网页的思路做界面、自动排版、只改变化的地方、键鼠事件
第 7 篇 · 连接外面的世界 人脑、出错自救、外接能力、编辑器指挥、手机远程控制

第四部分 · 数据与扩展

内容
第 8 篇 · 数据放哪、钱怎么算 两个记事本、对话找回、后悔药、记账
第 9 篇 · 怎么给它加功能 斜杠命令、关键时刻插一脚、插件、派小弟并行干活

第五部分 · 高级与工程

内容
第 10 篇 · 高级能力 后台常驻、放手让它自己干、几种工作模式、语音与看屏幕
第 11 篇 · 工程上的讲究 一份代码变多个版本、打包拆分、性能优化、怎么测试
第 12 篇 · 串起来 完整走一遍修 bug 全过程、十五个关键设计选择、出问题怎么查、和同类工具的思路差异
相关推荐
Rain5091 小时前
谁动了我的 URL?——记一次微前端“灵异 Bug“的排查实录
前端·vue.js·人工智能·前端框架·bug·ai编程
何以解忧,唯有..2 小时前
Vue 3 响应式核心:ref() 与 reactive() 的深入解析与实战
前端·javascript·vue.js
zzzzzz3103 小时前
看 react-bits,不要只看“酷炫”:一套阅读动画交互组件库的框架
javascript·react.js·动效
CesareCheung10 小时前
高级测试面试中的 AI 面试题:从原理到实战全解析
ai编程
香芋芋圆11 小时前
AI 冲击内卷之下,普通前端如何破局?WebGIS—— 低门槛突围赛道
前端·javascript·人工智能·学习·职场发展
INS_KF11 小时前
【编程笔记】成员函数中两个 const 的区别(const Data &getData() const;)
前端·javascript·笔记
宿67412 小时前
vue3-async
前端·javascript·vue.js
excel13 小时前
Nuxt + Twin CSS 中宽度超过屏幕时底部出现空白的原因与解决方案
前端·javascript
kyriewen14 小时前
GPT-6 发布当晚,三大 AI 集体宕机 4 小时——我扒完时间线,发现最该慌的不是宕机
人工智能·程序员·ai编程