简介面向AI工具研究者、前端架构学习者与逆向工程爱好者这套Claude Code源码整理了三套完整方案覆盖从底层破解研究到开箱即用的全流程需求。包内共2000个文件以TypeScript源码为主体1324个ts、541个tsx辅以60个js脚本、41个md分析文档、19个json配置及少量html、txt、yaml文件压缩包约94.78MB结构分层清晰。第一套原始破解版保留npm安装包、自动化还原脚本、经source map反编译的真实源码及构建文档适合深入逆向还原过程第二套学术研究版给出纯净src快照与架构分析系统梳理模块设计、通信机制和核心逻辑是学习大型AI应用源码的优质教材第三套可运行版已配好shim补丁、依赖环境与tsconfig编译选项下载后执行bun install bun run dev即可启动。目前已有971人学习下载适合想快速搭建本地环境或深入研究Claude Code原理的开发者。1. 终端里的 AI 助手到底怎么转起来的这份源码包拆解的价值真正让人上头的不是那个交互界面而是背后那个一轮一轮转的 agent 主循环。这份资源来自某 AI 厂商的命令行智能体工具 Claude Code 的源码整理包下文统一称 CC。它分三种形态原始源码版、学术研究注释版、可直接运行版分别对应三种诉求——研究架构、二次开发、快速跑通。想搞懂“终端型 AI Agent 内部机制”的开发者想在自己的 CLI 里接入模型能力的工具作者以及被各种环境问题折腾到没脾气的学生党都能在这个包里找到对应入口。先说结论研究架构看原始版省时间看运行版要在源码上动手改先看学术研究版的注释。这份资源不保证你能写出一模一样的产品但能让你把一个黑匣子拆到能自己开口说话的程度。2. 三个源码分支怎么选原始版、研究版、运行版的差异与适用场景2.1 原始版最接近真实执行的架构骨架拿到原始版第一件事不是急着 npm install而是先读目录。这个版本保留了最原始的目录结构主要模块大致分成五块agent 目录放智能体主循环负责接收用户输入、维护消息历史、调用模型tools 目录是内置工具集覆盖文件读写、命令执行、文本搜索这一类基础能力config 目录负责配置加载与校验把环境变量、CLI 参数、本地配置三方合并utils 目录是通用工具比如 token 统计、输出格式化、日志分级runtime 目录处理会话持久化与状态管理把对话记录写到本地目录方便断点续聊。入口一般藏在 package.json 的 bin 字段里常见写法是cc: ./bin/cc.js。bin 目录下的脚本启动后做三件事解析命令行参数、加载环境变量、实例化 Agent 对象然后进入主循环。CC 的核心逻辑大部分是纯 JavaScript没有复杂的编译步骤这也是它比很多重量级框架更容易被整理成可读版本的原因。启动文件通常很短十几行搞定真正的业务逻辑都落在 agent/ 的 loop 模块里后面第四章会展开讲。看源码时我建议先顺着入口往下走一遍调用链parser → config → agent.loop → model.call → tools.run。这个链路几乎就是所有 CLI 型 Agent 的通用骨架哪怕你后面换一个模型、换一套工具骨架都不会变。走完这条链再回头看 tools 目录里具体某个文件怎么实现会轻松很多。2.2 学术研究版带注释与修正的阅读理解版本学术研究版的改动集中在三块。第一块是注释它不只是解释“这行在干嘛”而是把“为什么这样写”和“不这样写会怎样”标在关键节点上。比如在工具调用解析函数上方研究版会用注释说明这里兼容了流式输出里超时返回空数组的情况并标出对应日志字段。这类注释对二次开发的价值远比源代码本身更大它能帮你少读三分之二无关文件。第二块是跨平台修正。原始版有一些明显的平台假设比如路径分隔符直接用/在 Windows 上就会翻车再比如 shell 命令默认用bash -cWindows 原生环境根本没有。研究版把这些位置改成path.join和process.platform分支处理保证在不同系统上至少能跑起来。第三块是容错补充。原始版对流式响应里的空行处理得比较粗糙模型返回内容里只要出现连续换行解析结果就可能出现“前半段正常、后半段全丢”的怪现象。研究版在解析层加了一个状态机把空行和真实内容区分开。如果你的目标是往源码里加自定义工具或换模型接入我建议以研究版为基准省去自己填坑的时间。2.3 可直接运行版从拿到源码到跑通第一句话的最短路径可直接运行版做的事情很直接把陌生环境中最容易出问题的点全部预置好。一是依赖锁定它在 package-lock.json 里固定了所有依赖版本并且把可选的原生编译模块标记为 optional避免npm install卡在编译阶段二是预置了.env.example配置文件密钥字段留空其他参数给了合理默认值三是默认配置里关闭了输出缓存和历史记录轮转减少磁盘读写带来的权限问题。源码包的结构在运行版里被精简过原先十几个目录合并成几个核心目录run.js 作为统一入口start.sh 封装了环境变量导入和启动命令。对只想“看看这个工具到底怎么跟模型对话”的用户来说运行版是阻力最小的一条路对想深挖的开发者运行版也是一个很好的对照样本——打开原始版和研究版做 diff能清楚看到“为了让程序跑起来到底改了哪些地方”。表 2-1 三个版本差异对比版本典型用途主要改动上手难度原始版研究架构设计无改动需要自己补依赖学术研究版二次开发与定制增加注释、修复跨平台与容错问题低可直接运行版快速跑通全链路锁定依赖、内置配置与启动脚本最低节奏上我推荐三步走先用运行版把全链路跑通建立“这个工具一定能动”的信心再用原始版做 diff看运行版到底改了什么最后回到学术研究版以注释放基准做自己的扩展。3. 把可直接运行版跑起来环境准备、配置项与第一次对话3.1 环境准备Node 版本、npm 镜像与可选原生模块我的建议是先用 Node 20如果你机器上版本比较旧先切过来。低于 18 的版本会在启动时报fetch is not defined因为代码里直接用 Node 原生 fetch 调用模型接口没有引入 polyfill。这个报错很隐蔽报错栈指向 node_modules 内部很难第一时间想到是 Node 版本问题。nvm install 20 nvm use 20 cd cc-direct-run npm ci --omitoptional这里的npm ci会严格按 lockfile 安装依赖比npm install快且不容易产生版本漂移--omitoptional跳过 optionalDependencies 里那些需要本地编译的原生模块。CC 里原生模块起的是性能加速作用并不是功能必需去掉之后跑 demo 完全没问题反而能避开最常见的编译报错。装完依赖后创建一份.env配置文件。常见做法是直接复制示例文件再改cp .env.example .envCC_MODEL_ENDPOINThttp://127.0.0.1:8000/v1 CC_MODEL_NAMEmy-assistant CC_API_KEYlocal-skip CC_WORK_DIR./workspace CC_LOG_LEVELinfo CC_MAX_TURNS20CC_MODEL_ENDPOINT 是模型服务的地址兼容 OpenAI 格式本机调试时指向本地服务即可CC_MODEL_NAME 建议改成自己服务实际部署的模型名很多“模型返回空内容”的问题就是模型名写错导致的CC_API_KEY 本地模拟服务可以随便填但要保证服务端不在鉴权模式CC_WORK_DIR 是工作目录工具执行的文件操作都限制在这个目录下建议用绝对路径避免启动时相对路径解析混乱CC_MAX_TURNS 控制单次会话最多几轮工具调用设成 20 足够演示大部分场景。3.2 配置加载顺序与合并规则配置的合并顺序是CLI 参数 环境变量 默认配置。对应的代码逻辑相当直观本质是一个三层对象展开const cfg { ...defaults, ...parseEnv(process.env), ...parseCli(process.argv), };后加载的配置会覆盖前一层CLI 参数优先级最高。这片代码虽然只有三行却对应一个很常见的坑——环境变量里如果写了空字符串它会被...展开成“空值”而不是“未定义”直接把默认配置覆盖掉。比如你不再需要CC_MAX_TURNS又懒得删留了一行CC_MAX_TURNS那么循环轮次会被强制设成 0对话请求直接不发送。正确习惯是不需要的配置项要么删掉要么注释掉不要留空值。这个原则在研究版注释里也被标成了一级警告网上有太多“配置没问题但怎么都启动不了”的帖子最后定位都是这一类“幽灵空值”。3.3 启动交互会话并确认日志输出环境变量就绪后直接启动node bin/cc.js --workdir ./workspace如果配置正确终端会进入交互模式出现提示符。此时输入“你好”或“列出当前目录”程序会先把消息发给模型模型返回工具调用指令程序执行后再把结果送回模型。正常现象是终端里能看到执行流程一点一点出现包括工具名、参数和结果摘要。如果这一步没走通先把日志级别调到 debug 再跑一次CC_LOG_LEVELdebug node bin/cc.jsdebug 日志会显式输出每一轮请求的耗时、token 用量和工具调用结果。关注下面的表格字段基本能定位 80% 的问题表 3-1 debug 日志关键字段字段含义异常情况[request]出站请求的模型名与输入长度模型名错误时这里显示异常[duration]单次模型调用耗时耗时过长检查网络和服务端负载[tool_call]模型选择的工具名与参数参数为空说明 schema 定义有问题[tokens]输入输出 token 数量超出窗口上限会被截断第一次跑通之后再回头去看原始版的代码你会对“这段是在做什么”有清晰的位置感而不是被细节糊住。4. 核心调度链路拆解Agent 循环、工具注册与上下文管理4.1 从启动到首次回复事件循环的五个阶段所有 CLI 型 Agent 的核心都是一个“循环”CC 也不例外。它的主循环逻辑可以压缩成下面的伪代码async function runTurn(userInput) { messages.push({ role: user, content: userInput }); for (let i 0; i maxTurns; i) { const resp await model.call(messages); if (!resp.toolCalls.length) { console.log(resp.text); return; } for (const call of resp.toolCalls) { const result await tools.run(call.name, call.args); messages.push({ role: tool, name: call.name, content: result, }); } } }关键点在于模型返回分成两类要么是最终回答文本要么是工具调用指令。当返回 toolCalls 时程序不直接回复用户而是逐个执行工具把执行结果以role: tool追加回消息列表然后带着完整历史再次调用模型。这个循环会一直执行直到模型认为不再需要调用工具、直接给出文本答复为止。maxTurns是安全阀防止模型陷入无限循环。我见过最离谱的一次循环里连续执行工具 43 次都没给出最终答案所以这个上限一定要设别相信模型“自己会停”。一次完整对话的请求顺序如下用户输入 → 模型返回工具调用 → 执行工具 → 结果回填 → 再次调用模型 → 模型返回最终文本。整个链路中工具执行是唯一一个开发者的代码可以直接介入的环节也是最有扩展价值的地方。4.2 工具注册schema 定义与参数校验工具的注册机制决定了这个 Agent 能“看到”什么、能操作什么。CC 里有一个全局工具注册表本质上是一个 Mapkey 是工具名value 是包含描述、参数 schema 和执行函数的对象tools.register(read_file, { description: 读取工作目录下指定文件的内容返回原始文本, schema: { filePath: { type: string, required: true }, encoding: { type: string, default: utf8 }, }, handler: async (args) { return fs.readFile(args.filePath, args.encoding || utf8); }, });这段代码有三个点值得细看。第一description不是给人看的注释它会被拼进系统提示词作为模型决定“该不该用这个工具”的依据。描述写得越具体模型选错的概率越低写得太泛模型会莫名其妙调用它。第二schema是 JSON Schema 的简化形式模型从里面读取参数名和类型生成合法的调用参数。第三handler里不要自己 throw建议把所有异常捕获后返回错误字符串模型的容错能力比你想的强错误信息返回给它它能自己决定下一步怎么补救。新增工具时一定要遵循一个原则工具返回值必须是文本。无论是读取文件、执行命令还是查数据库最终都要序列化成字符串。CC 的上下文机制不会处理二进制结果你如果返回一个对象日志里只会出现[object Object]模型拿不到任何信息。4.3 上下文窗口截断、摘要与消息压缩模型上下文窗口是硬约束对话轮次一多历史消息迟早会超限。原始版给的处理策略非常直白超出窗口就把最旧的消息丢掉直到长度合适function truncateMessages(messages, maxTokens) { while (countTokens(messages) maxTokens) { messages.shift(); } }这个实现能跑但效果不稳定。一旦把早期消息全部丢弃模型就失去了“用户一开始想干什么”的上下文回答开始答非所问。学术研究版把这个逻辑改成了摘要式当历史信息超过阈值时不是直接丢而是先把最旧的部分调用模型生成一段摘要再把摘要压回消息列表头部。代价是多一次模型请求但对话的连贯性会好很多。实际使用中countTokens的实现也对稳定性有直接影响。原始版用的是字符数除以 4 来估算对英文还行对中文偏差很大。中文字符一个 token 和一个汉字并不是 4:1 的关系建议调试时打开 debug 日志观察[tokens]字段对照模型返回的实际 token 用量微调估算系数。这类“数字对不上”的问题不会导致报错但会造成截断过早或过晚属于典型的隐性翻车点。5. 避坑手册上手三天我踩过的五个坑5.1 npm install 卡死或长时间编译现象执行npm install后长时间停在某个原生模块的编译阶段终端没有报错但就是不动最后超时失败。原因CC 的依赖里有多个需要本地编译的原生模块它默认会去下载预编译二进制如果下载不到则回退到源码编译。在一些受限网络环境里下载失败后系统不会快速报错而是反复重试。解决用npm ci --omitoptional跳过本地编译依赖或者在.npmrc里关闭 optional 支持。演示和二次开发场景不需要原生模块的加速能力跳过不会影响功能等真正需要性能再单独补装。5.2 模型返回空内容现象交互模式下输入问题能看到请求已经发出去但迟迟没有回复debug 日志显示模型返回内容为空可服务端明明有响应。原因多半是流式解析的结束条件问题。原始版代码里对流式响应的处理是按行读取如果模型返回的内容里出现空行解析器可能提前判定“已经结束”导致后续内容全部被丢弃。解决先是检查.env里的模型名是否和服务端部署的模型一致排除模型名错误再启动时把CC_LOG_LEVELdebug打开看请求参数里是否带了stream: true如果带了且服务端不支持流式改为在配置里强制关闭流式最后如果还不行就在研究版里找解析函数的补丁代码把空行过滤逻辑合进你的版本。5.3 Windows 终端中文乱码现象日志里的中文全部变成“锟斤拷”一类的乱码字符串看起来面目全非英文和数字完全正常。原因CC 默认按 UTF-8 输出而 Windows 命令行环境的默认编码是 GBK两边不一致所有中文字符都被错误解码。解决启动前先执行chcp 65001切换终端到 UTF-8检查系统环境变量里是否设置了PYTHONIOENCODING之类影响编码的变量在.env里加上CC_FORCE_UTF81代码内部会跳过原生编码检测直接以 UTF-8 输出。5.4 会话记录写入失败导致启动即退出现象启动时立刻报错EACCES: permission denied堆栈指向一个以.cc开头的隐藏目录。原因程序启动后会检查历史会话存储目录默认放在用户主目录下如果主目录没有写权限或者磁盘配额已满启动过程直接失败代码没有提供降级方案。解决显式指定工作目录CC_WORK_DIR/tmp/cc-demo或项目下的./workspace确保该目录存在且可写如果仍然失败检查是否被安全软件拦截了在主目录下创建目录的操作。5.5 三个分支混用导致“幽灵报错”现象从运行版切到研究版明明按 README 重新装了依赖启动后却报Cannot find module指向的文件在另一个版本里当前版本根本没有。原因两个版本共用一个 node_modules 目录依赖树的符号链接指向了旧版本安装的路径或者复制目录时把旧依赖一起带过去了运行时加载了错误的模块。解决三个分支放在独立目录里各自维护 node_modules切换分支前先删除 node_modules 再重新安装还有一个偷懒的办法复制目录前先删掉 node_modules 和 package-lock.json让它在新目录里重新解析。6. 进阶给源码加一个自定义工具并用调试日志验证链路6.1 扩展点在哪里工具注册表的注册函数就是扩展点。在tools/index.js里找到初始化的位置调用register把新工具挂进去即可。这里不需要动主循环代码也不需要对协议做任何修改——只要 schema 描述清楚模型会自动学会使用它。6.2 写一个最小自定义工具现在注册一个工作区文件树工具用于生成当前工作目录的文件结构tools.register(list_workspace, { description: 递归列出工作目录下的文件返回带缩进的树形文本用于查看项目结构, schema: { depth: { type: number, default: 1, description: 递归层数默认1层 }, }, handler: async ({ depth }) { const root process.cwd(); const lines []; function walk(dir, level) { if (level depth) return; for (const name of fs.readdirSync(dir)) { const full path.join(dir, name); const stat fs.statSync(full); const indent .repeat(level); lines.push(indent (stat.isDirectory() ? name / : name)); if (stat.isDirectory()) walk(full, level 1); } } walk(root, 0); return lines.join(\n); }, });之后重启程序对模型说“看一下工程里都有什么”如果 schema 和描述合理模型会主动调用这个工具并展示文件结构。6.3 验证方法与回归检查新工具接完之后我习惯用三条固定步骤验证第一步先跑空请求确认注册不报错第二步在 debug 日志里观察工具调用记录看模型传的参数是否匹配 schema第三步故意让工具执行失败比如在depth传入一个极大值确认日志里返回的是错误信息而不是进程崩溃。调试时要重点检查[tool_call]字段和[tokens]字段前者确认工具被正确调用后者确认长输出有没有冲爆上下文。从那以后我每次拿到这个工具链的源码包都会强制走一遍“运行版跑通、原始版看骨架、研究版找注释”的流程改任何自定义工具前先开 debug 日志验证完再关掉已经习惯成自然了。这套流程看着朴素但确实帮我避开了绝大多数“改完就跑不起来”的尴尬局面。这份源码包里的三个版本我建议也都按照这个顺序走一遍先跑再读最后改。如果你主要目的是快速体验直接上手运行版即可剩下的坑日志会替你说话。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?