简介Claude Code 源码是一份面向 AI 编程工具研究者、前端/Node.js 开发者及开源爱好者的完整代码仓库适合用于学习现代 CLI 应用架构、模块化拆分与类型安全实践。资源共 1903 个文件以 1332 个 TypeScript 文件与 552 个 TSX 组件文件为主辅以少量 JS 工具脚本和 1 份 Markdown 说明文档压缩包约 9.43MB目录结构清晰便于按功能模块检索阅读。目前已有 286 人浏览学习。源码不仅覆盖核心算法与数据处理逻辑还包含详尽注释、测试用例、开发者指南、API 文档和用户手册能够帮助读者理解大型开源项目如何通过 Git 管理版本、组织模块和保障代码质量。对于希望提升工程能力、研究 Agent 类工具实现原理或参与社区贡献的开发者而言这份源码提供了难得的参考资料。1. 找不到源码的“Claude Code源码”终端AI代理值得啃的其实是四层内核把“Claude Code源码”当成一个能直接下载的仓库去找多半会扑空。Claude Code 是 Anthropic 出品的终端 AI 编码代理核心并不开源网上流传的各种“源码”也基本都是包装脚本或者二次封装。可我更愿意把“源码”理解成另一件事这个工具靠什么逻辑跑起来、改了哪里能改变它的行为、出了问题从哪一层开始查。实际操作下来它的可观察内核一共四层磁盘上的配置目录、工具调用循环、钩子与 MCP 扩展点、以及会话日志。这四层不需要你读过一行闭源代码也能照样定位问题、加能力甚至照着复刻一个最小可用版本。适合谁读呢想基于 Claude Code 做二次开发的人和那些在“源码”里翻半天却连配置目录都找不到的新手。2. 先拆磁盘~/.claude 与项目 .claude 目录就是离你最近的“源码”所有闭源终端工具最诚实的那份“源码”就是它写在磁盘上的配置与状态。Claude Code 把全局状态放在~/.claude项目级设置放在.claude/这两片目录看明白了剩下的运行逻辑全是顺着它们展开的。2.1 全局层~/.claude 里值得先读的六个文件完成 Claude Code 安装、跑过一次会话之后主目录下会出现~/.claude。别急着删这一目录就是终端工具“源码级”的现场。真正值得读的六个东西我按排查频率排个序~/.claude.json账号级状态文件注意它是个文件不在~/.claude目录里面。它记录已登录账号、projects 列表、每个项目的 git 仓库路径与运行次数。我一般说它是“户口本”后面排查权限和上下文污染都用得上。~/.claude/settings.json全局配置权限、环境变量、钩子都允许在这里出现。它的典型长这样{ env: { CLAUDE_CODE_MAX_OUTPUT: 12000 }, permissions: { allow: [Bash(npm test:*), Read(.*), Edit(.*)], deny: [Bash(rm -rf:*), Write(.*/secrets.*)] } }逻辑说明env里的变量会当启动参数注入子进程permissions.allow是白名单deny是黑名单。我习惯把“防止误删”这类规则写进全局deny因为它真的救过我一次细节在第 5 章展开。参数说明allow里每一项的语法是工具名(正则模式)比如Bash(npm test:*)只在命令以npm test:开头时免确认。这种精确授权法比直接写Bash(.*)稳得多权限检查是顺序匹配先看 deny 再看 allow通配符越多越容易给出意想不到的放行。~/.claude/logs/会话日志目录文件以SL_开头例如SL_时间戳.jsonl。这是我觉得最接近“源码运行时”的东西每一轮工具调用、HTTP 请求、token 统计都在里面。~/.claude/hooks/全局钩子的落点。~/.claude/commands/全局个性化指令也就是/命令名。~/.claude/CLAUDE.md全局记忆文件每次会话都会注入适合写跨项目的通用约束。这里有个从源码视角特别容易误会的点~/.claude.json和~/.claude/是两回事前者是文件、后者是目录备份和导出时要分开处理。我见过同事把.claude.json当成目录删了登录态全丢全部项目历史记录一起没。提示~/.claude.json里可能包含与账号相关的敏感信息提交到 git 仓库前务必确认已经被.gitignore排除。2.2 项目层.claude/ 与 CLAUDE.md 的加载顺序进到项目根目录Claude Code 会把.claude/当成项目级“源码目录”。常见的结构是这样.claude/ ├── settings.json # 项目级权限与 env ├── settings.local.json # 个人本地覆盖不进 git ├── .mcp.json # MCP 服务器声明 ├── commands/ │ └── review.md # 自定义指令 /review ├── hooks/ │ └── post_tool_use.sh # 工具后置钩子 └── CLAUDE.md # 项目说明文档settings.local.json是最容易被忽略的一个点它按机器生效冲突时覆盖settings.json。我会把本地代理、私有 token 放这里这样整个仓库继续提交给别人也不会泄露个人信息。它和settings.json的关系类似 git 里config和config.local的分工。CLAUDE.md的加载顺序是团队协作里反复出问题的点。实际顺序是全局~/.claude/CLAUDE.md→ 仓库根目录CLAUDE.md→ 当前子目录的CLAUDE.md。也就是说在子目录执行claude时它会同时读多层。用表格看一眼就明白加载顺序文件位置作用域典型用途先读~/.claude/CLAUDE.md全局个人代码风格、跨项目禁用项次读仓库根CLAUDE.md项目架构约定、构建命令、行为红线后读当前目录CLAUDE.md会话当前模块的局部说明一个关键理解越后读的越靠近最终提示词但这不代表“后读优先”。相反经验上后加载的内容更容易把前两层覆盖掉。如果你发现模型突然像变了一个人多半是根目录那份和子目录那份打架了改其中任意一份前先确认是谁在生效。2.3 会话层/status 暴露的运行时状态在会话里敲/status可以得到当前会话快照项目路径、模型、权限模式、当前目录以及最近一次工具调用结果。这是我理解“源码可观察性”的第一现场。新手排查问题第一反应是去翻日志我的习惯是先/status三秒钟确认模型、路径和权限模式没有跑偏再往下查。/status里还有一个容易被忽略的点它会显示当前会话的工作目录。很多人把 Claude Code 当成项目根目录专属工具其实它在任意子目录都能启动而“它以为自己身处哪里”直接决定了工具调用的相对路径。同一个命令在根目录和子目录启动行为可能完全不同这算是终端类 agent 特有的心智负担。3. 运行时四件套工具调用循环、上下文构建、日志落盘与进程权限读出配置只是开始。Claude Code 的“源码级”行为全在一个循环里Claude 模型收到上下文 → 决定调用一个工具 → 工具执行并返回结果 → 结果附加进上下文 → 再来一轮。理解这个循环就能解释 90% 的实际现象。3.1 工具调用循环六类内建工具与两次确认Claude Code 内置工具大致分六类读文件Read、编辑Edit、写文件Write、执行命令Bash、搜索代码与文件Glob/Grep、子任务Task。每次调用如果权限没被白名单放过终端会弹确认半自动模式下你只要按回车就会通过全自动模式下完全跳过。用Bash举例子常见的调用景观是# 让 Claude 运行测试并在失败时尽早停 pytest tests/ -x --tbshort --no-header逻辑说明Bash工具默认以你的系统 shell 执行输出截断进入上下文失败时退出码也是可见信息。我建议在所有常用命令前加上--no-header或--quiet类似的参数因为它能把“成功但不重要”的输出压下去减少上下文膨胀。参数说明-x让 pytest 在第一个失败处停住--tbshort压缩错误回溯为几行模型更容易把失败原因读出来。这一步不是玄学是给上下文省字。观察日志你会发现一个高频行为模式Claude 做完批量修改之后紧接着会调Read或Glob去复核然后才进入下一轮计划。Edit 后面跟 Read这个“写后读”节奏是复现它方案时最值得模仿的一点自己写 agent 时保留这个习惯能少犯一半低级错误。3.2 上下文构建单元CLAUDE.md 与工具结果如何拼进提示词每次请求真正发给模型的不只是你刚输入的那句话。常见的上下文组装顺序是系统提示含安全约束与工具定义~/.claude/CLAUDE.md全局记忆根目录和当前目录的CLAUDE.md最近会话历史带着工具调用结果当前用户输入当 token 逼近模型上限时历史会被压缩对最早几轮做摘要较近的轮次保留原文。这个“压缩点”你看不到但能感觉到——如果你发现 Claude 突然忘了三天前让它记住的某个路径多半是历史被摘要吃掉而不是它变笨。对应解法是把关键路径写回CLAUDE.md因为文件内容在每次请求都会重新读取比历史可靠得多。这里有个值得专门强调的边界压缩后的摘要是不透明的。你无法从日志直接还原“模型到底忘掉了哪句”。所以我的原则是凡是“必须记住”的内容不依赖历史全放进CLAUDE.md凡是“想起来才用”的内容放进按需指令或者技能里。3.3 日志落盘SL_ 文件里能挖出什么会话日志~/.claude/logs/SL_时间戳.jsonl是逐行的 JSON。大体是一个事件一行常见能看到工具调用类型、请求方向、时间戳。我排查问题时最常用的三件事搜工具调用行看某个工具是否真的被调用以及调用顺序。搜结果行看工具输出是否异常截断。搜 token 相关字段看哪一轮把上下文吃爆了。# 在最近一个会话日志里统计工具调用频率 cd ~/.claude/logs ls -t SL_*.jsonl | head -1 | xargs grep -o name:[A-Za-z_]* | sort | uniq -c | sort -rn逻辑说明ls -t取最近修改的日志文件grep -o提取工具名再统计频率能快速看出当前会话里哪个工具是被反复用的从而判断是不是陷入了某种死循环。参数说明head -1取最新一份就够了如果要跨会话统计把head -1去掉直接cat SL_*.jsonl再管道代价是数据量大会拖慢统计速度。日志还藏着权限相关的行为本地权限规则是“先匹配 deny再匹配 allow都没有则弹窗”。我把这个顺序背下来以后很多“为什么偏偏它要问”的问题就迎刃而解。如果是 deny 命中它不弹窗而是直接拒绝如果是 allow 命中它不弹窗直接执行。只有两边都没命中才出现那个你熟悉的确认框。3.4 进程模型为什么编辑配置不热加载Claude Code 的每次会话是一个常驻进程配置只在启动时读一次。你在另一个终端改了settings.json正在跑的会话不会热加载。这一条看着简单实际踩坑率极高。很多人开着一个长会话外面改了 MCP 配置回来敲命令测试发现新工具没出现第一反应是“坏了”第二反应是去翻网络配置折腾一圈才发现是进程根本没重新读文件。我在实际工作里的判断顺序是先敲/status看当前会话状态再敲对应的管理命令比如 MCP 相关就敲/mcp最后才决定要不要重启会话。盲重启十有八九会发现“状态根本没问题是配置本身没写对”。4. 扩展层就是可变源码hooks、个性化命令与技能、MCP 接入闭源软件找“源码”的意义一半是为了改行为。Claude Code 给普通用户留的可变面是 hooks、个性化命令和 MCP——不动主程序也能在工具调用前后插自己的判断。4.1 hooks在工具调用前后插一手的标准写法hooks 配置在settings.json的hooks字段里事件名我常用的有UserPromptSubmit用户输入后、发给模型前触发PreToolUse工具执行前触发PostToolUse工具执行后触发StopClaude 完成一轮回复后触发Notification通知回调PreCompact历史压缩前触发一个典型用法在PreToolUse里拦截Bash把危险路径挡在外面。{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: test \$TOOL_INPUT\ \rm -rf /\ exit 2 || exit 0 } ] } ] } }逻辑说明PreToolUse的 hook 退出码非 0 表示拒绝该调用0 表示放行。上面这个例子虽然粗暴但能挡住最危险的那条命令。参数说明matcher可以指定工具名事件触发时TOOL_INPUT、TOOL_NAME、HOOK_EVENT_NAME这些环境变量会被自动填入你的脚本可以读取它们做判断hook 自身的输出会作为一条观察结果回到上下文所以不用怕“写不出后台日志”。在一个安全要求高的环境里我会把所有Write(.*\.env)都通过 hook 拒绝同时输出提示“请在 .env.example 里写模板”。这个逻辑放在代码层去做会漏放在工具调用层做反而漏不了因为它是绕不过去的必经之路。4.2 个性化命令与技能/命令名 的落点在.claude/commands/下放一个.md文件就能获得一个斜杠命令。文件名去掉.md就是命令名文件内容的第一段描述会显示在命令面板剩下的全是给 Claude 的指令。--- description: 在提交前做一次上下文自检 --- 检查当前工作区未提交改动里是否存在调试代码console.log / print / TODO 如果有先列出文件清单逐个询问我是否删除 全部处理完再输出一份改动摘要。逻辑说明自定义命令本质上是一段可复用提示词适合把高频动作固化成团队约定。参数说明文件头的description会在命令面板里优先显示建议写成“动词 对象”的结构比如上面的“在提交前做一次上下文自检”比单写“自检命令”好用得多。技能Skill这类更高级的自动化起手式也类似先拆成一段可复用的指令文件再逐步给配套脚本和资源目录。我见过不少人一上来就想做“全自动技能”结果第一版就把所有逻辑塞进一条命令里跑不通也难调试。更稳的做法是先把单文件命令跑顺再从命令升级成技能。4.3 MCP把外部工具挂进工具调用循环MCP 接入的本质是让外部服务暴露成新的工具名。项目级配置写在.claude/.mcp.json用claude mcp add命令也能登记。{ mcpServers: { internal-docs: { command: npx, args: [-y, your-org/doc-mcp], env: { DOC_BASE_URL: https://docs.example.internal } } } }一个关键认知.mcp.json第一次导入后工具定义会固化到会话之后改文件不一定能热生效。常见现象是“我改了配置但新工具没出现”。我顺手排 MCP 故障的顺序是先claude mcp list看注册状态再打开.mcp.json检查命令路径最后翻日志看握手是否成功。把这个顺序反过来的人往往先怀疑网络最后才发现是npx版本路径不对。提示MCP 服务器的日志通常不和主会话日志混在一起。先确认进程真的被拉起来了再去查网络和鉴权能省掉 80% 的排查时间。5. 源码向使用中的五个避坑现场现象、原因、解决基于上面的机制我把常见问题压成五条。每一条都是我在真实跑动中踩过或者看别人踩过的坑按“现象 → 原因 → 解决”写。5.1 权限拒绝命令能在我终端跑Claude 却说没有权限现象让 Claude 执行npm run build它反复说需要授权甚至出现权限相关的报错。原因settings.json里 deny 规则写得太宽或某一项规则的正则语法写错权限检查直接抛异常而不是平滑降级。解决先敲/status确认当前权限模式再打开settings.json检查正则。我的原则是 deny 里只写确定不干的allow 里让 Claude 精确到命令前缀比如Bash(npm run build:*)而不是Bash(.*)。通配符太多权限检查看起来优雅实际上把安全兜底也删了。5.2 钩子输出污染上下文一条 stdout 让模型前后判若两人现象加了PostToolUse钩子之后Claude 开始重复“我已经处理过”这种话或者在某次大改后突然忘了之前的约定。原因钩子的 stdout 会被当成工具结果注入上下文如果钩子里有echo ok这类无意义输出模型会把“ok”理解成一次成功信号并在后续推理中引用它。解决钩子的命令遵循“安静”原则——成功时输出为空失败时只输出一行错误。调试钩子期间我故意让命令打印当前事件名定位问题定位完立刻删掉输出。5.3 中途改配置不生效同一个会话里摸不到新能力现象开了新 MCP 服务或者改了 hooks当前会话里测试新命令永远失败。原因Claude Code 进程启动时就把配置持久化在状态里运行中不会重新读文件。解决涉及 MCP、hooks、permissions 的修改直接退出会话重进。我前面说过重进前先确认配置本身没有写错这个顺序比啥都重要。5.4 CLAUDE.md 越长约束越弱现象CLAUDE.md写到 300 行以后模型开始频繁遗漏关键约束甚至在犯错之后自我辩解。原因长指令在信息密度上不如短指令而且历史压缩发生时靠后的约束最容易被摘要掉。解决把CLAUDE.md拆成两层根目录那份只留“行为红线”和“项目架构一句话描述”具体的命名规范、目录说明挪进.claude/commands/里按需调用。本质上是把指令从常驻内存移到按需加载压缩损失会小很多这个做法我下一章给出完整方案。5.5 会话日志里找不到想看的东西现象想定位一次模型回答为什么崩翻SL_*.jsonl却没找到完整的请求负载。原因日志默认是事件流不是 HTTP 抓包它记录“发生了什么”不记录“完整请求体长什么样”。解决别在日志里硬翻复现问题时先开调试模式把复现步骤固定下来再把现象、日志片段、配置一起存下来。我一般用一个最小仓库三句话描述复现步骤再附上引发问题的工具调用日志比漫无目的地翻日志效率高一个量级。6. 进阶把三千行 CLAUDE.md 瘦身成可按需加载的“指令源码”高约束项目的终局是把记忆文件做成“索引 按需加载”。做法是根目录CLAUDE.md只保留四段——项目一句话、行为红线、目录索引、以及“遇到 X 请先读.claude/specs/下的 Y.md”。# 项目 CLI 一句话这是一个处理订单导出的 Node CLI只允许操作 orders 表。 行为红线 - 不允许删除任何记录只允许打 soft-delete 标记。 - 不允许直接改 prod 库只允许通过迁移脚本。 按需加载 - 涉及订单状态机 → 先读 .claude/specs/order-state.md - 涉及导出格式 → 先读 .claude/specs/export-format.md 目录索引 - 业务代码在 src/modules - 测试在 tests/e2e逻辑说明把原来的 3000 行拆成specs/下若干独立文件CLAUDE.md退化成一张索引表。模型平时只扛着索引跑真正遇到订单状态时才会去读order-state.md。这比把 3000 行全部塞进每次请求省下的 token 不是一点半点而且压缩发生时索引本身的稳定性远高于长文。配合 hooks可以做成更自动的按需加载在UserPromptSubmit事件里 grep 用户输入如果命中“订单”“状态机”这类关键词就把order-state.md的内容追加到本次上下文再交回模型。这段逻辑用不到 20 行 bash 就能写完但它把“CLAUDE.md 长了就不遵守”的毛病直接改成了“不长就不会不遵守”。这套做法的边界我也说清楚它依赖目录规整到“索引一句话能找到”。如果仓库本身结构混乱索引写不好反而会带偏模型。我会先用/status和日志确认模型当前是否真的在遵守指令再做这个改造别一上来就拆。收个尾我最常用的排错动作永远是先/status、再翻SL_日志最后才看代码——这个顺序帮我省下的时间比任何一条配置技巧都多。希望帮到你。本文还有配套的精品资源点击获取