/init 的设计哲学——为什么 AI 的项目记忆只需要写「它猜不到的」

一句话总结

  • /init 不是一个"生成器"------它是一段注入给 LLM 的 prompt,让 Claude 自己去分析你的代码库、自己写 CLAUDE.md
  • 它有两种模式:默认模式生成基础 CLAUDE.md,新模式(CLAUDE_CODE_NEW_INIT=1)额外生成 skills 和 hooks
  • 核心设计原则:只写 Claude 猜不到的------这一条规则决定了整个命令的行为边界

源码定位

bash 复制代码
src/commands/init.ts        ← 命令本体(256行,20KB)
src/commands.ts             ← 命令注册中心(引入 init)
src/projectOnboardingState.ts ← 控制"/init 提示"是否显示

命令类型prompt

这是理解 /init 的第一个关键------它的类型是 prompt,不是 local(本地执行)也不是 dialog(弹窗交互)。

typescript 复制代码
const command = {
  type: 'prompt',    // ← 本质是向 LLM 注入一段 prompt
  name: 'init',
  progressMessage: 'analyzing your codebase',
  source: 'builtin',
  async getPromptForCommand() {
    maybeMarkProjectOnboardingComplete()
    return [{ type: 'text', text: 选择哪个prompt }]
  },
} satisfies Command

这意味着:当你输入 /init,Claude Code 不会执行任何本地逻辑,而是把一段精心设计的 prompt 塞进对话,让 LLM 自己去调用 GlobTool、FileReadTool 等工具来分析代码库并生成文件。

一句话总结:/init = 一段 prompt + LLM 自由发挥。


两种模式:新旧之争

源码里定义了两个 prompt 常量:

typescript 复制代码
const OLD_INIT_PROMPT = `Please analyze this codebase and create a CLAUDE.md file...`
const NEW_INIT_PROMPT = `Set up a minimal CLAUDE.md (and optionally skills and hooks)...`

选择逻辑:

typescript 复制代码
feature('NEW_INIT') &&
(process.env.USER_TYPE === 'ant' || isEnvTruthy(process.env.CLAUDE_CODE_NEW_INIT))
  ? NEW_INIT_PROMPT
  : OLD_INIT_PROMPT

翻译成人话:

  • 默认 :所有用户走 OLD_INIT_PROMPT
  • 新模式 :Anthropic 内部员工(USER_TYPE === 'ant')或手动设置 CLAUDE_CODE_NEW_INIT=1 的用户走 NEW_INIT_PROMPT

默认模式:OLD_INIT_PROMPT 全文解读

这是大多数人实际触发的 prompt,一共不到 20 行,但每一行都是设计决策:

css 复制代码
Please analyze this codebase and create a CLAUDE.md file, which will be
given to future instances of Claude Code to operate in this repository.

开场白设计:直接告诉 LLM 产出物的用途------"给未来的 Claude Code 实例看的"。这决定了写作视角:不是写给人看的文档,是写给 AI 看的指令。

「要写什么」------两条正面规则

vbnet 复制代码
What to add:
1. Commands that will be commonly used, such as how to build, lint,
   and run tests. Include the necessary commands to develop in this
   codebase, such as how to run a single test.
2. High-level code architecture and structure so that future instances
   can be productive more quickly. Focus on the "big picture" architecture
   that requires reading multiple files to understand.

只要求两类信息:

  1. 常用命令(build/lint/test,特别是跑单个测试的方式)
  2. 高层架构(需要读多个文件才能理解的全局观)

「不要写什么」------六条禁止规则

这才是 prompt 的精华。Anthropic 发现,不加限制的话 LLM 会写一堆废话,所以用了六条 "Usage notes" 来约束:

禁止规则 设计意图
不要重复自己 避免 LLM 的复读机倾向
不要写显而易见的指令(如"写单测"、"不要提交密钥") AI 本来就知道的事不用教
不要列出文件结构 ls 一下就知道了
不要写通用开发实践 这些是任何项目都适用的废话
不要编造章节(如"常见开发任务") 防止 LLM 幻觉
要整合已有的 Cursor/Copilot 规则 兼容其他 AI 工具的配置

这六条禁止规则,本质上是一个过滤器:只保留"Claude 自己推断不出来的信息"。

固定前缀

vbnet 复制代码
- Be sure to prefix the file with the following text:

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working
with code in this repository.

强制输出格式一致性。


新模式:NEW_INIT_PROMPT 的 8 阶段流程

新模式的 prompt 长达 200+ 行,设计了完整的 8 阶段交互流程:

yaml 复制代码
Phase 1: Ask what to set up          ← 问用户要什么
Phase 2: Explore the codebase        ← 派子 Agent 扫描代码
Phase 3: Fill in the gaps            ← 追问代码回答不了的问题
Phase 4: Write CLAUDE.md             ← 写项目级指令
Phase 5: Write CLAUDE.local.md       ← 写个人级指令(gitignore)
Phase 6: Suggest and create skills   ← 生成可复用技能
Phase 7: Suggest additional optimizations ← 推荐 hooks/插件
Phase 8: Summary and next steps      ← 总结

Phase 1 的设计巧思

AskUserQuestion 工具让用户做选择题,而不是开放式提问:

vbnet 复制代码
Options: "Project CLAUDE.md" | "Personal CLAUDE.local.md" | "Both"
Options: "Skills + hooks" | "Skills only" | "Hooks only" | "Neither"

为什么这么设计? 减少用户认知负荷------大多数人不知道 skills 和 hooks 是什么,给选项比解释概念更高效。

Phase 2 的核心逻辑

diff 复制代码
Launch a subagent to survey the codebase, and ask it to read key files:
- manifest files (package.json, Cargo.toml, pyproject.toml...)
- README, Makefile/build configs, CI config
- existing CLAUDE.md, .claude/rules/
- AGENTS.md, .cursor/rules, .cursorrules
- .github/copilot-instructions.md, .windsurfrules, .clinerules
- .mcp.json

重点:它会读取所有竞品的 AI 配置文件(Cursor、Copilot、Windsurf、Cline),并整合进 CLAUDE.md。这是一个产品策略------降低迁移成本。

Phase 4 的写入规则

新模式的核心过滤器更极端:

Every line must pass this test: "Would removing this cause Claude to make mistakes?" If no, cut it.

然后给出了明确的 Include/Exclude 清单:

Include(写)

  • Claude 猜不到的命令(非标准脚本、特殊 flag)
  • 与语言默认不同的风格规则
  • 测试的特殊姿势
  • 仓库礼仪(分支命名、PR 规范)
  • 必需的环境变量
  • 非显而易见的坑

Exclude(不写)

  • 文件结构(Claude 自己看得到)
  • 标准语言惯例(Claude 已经知道)
  • 通用建议
  • 详细 API 文档(用 @path/to/import 引用)
  • 频繁变化的信息
  • 从 manifest 能推断的标准命令(如 npm testpytest

启动提示逻辑:projectOnboardingState.ts

当你打开一个新项目时,Claude Code 会提示"Run /init to create a CLAUDE.md"。这个逻辑在 projectOnboardingState.ts

typescript 复制代码
export function getSteps(): Step[] {
  const hasClaudeMd = getFsImplementation().existsSync(
    join(getCwd(), 'CLAUDE.md'),
  )
  const isWorkspaceDirEmpty = isDirEmpty(getCwd())

  return [
    {
      key: 'claudemd',
      text: 'Run /init to create a CLAUDE.md file...',
      isComplete: hasClaudeMd,     // ← 只检查根目录
      isEnabled: !isWorkspaceDirEmpty,
    },
  ]
}

注意这个 Bug :它只检查根目录的 CLAUDE.md,不检查 .claude/CLAUDE.md。所以即使你把文件放在 .claude/ 目录下(这是官方支持的位置),启动时还是会提示你运行 /init。(GitHub Issue #45377)

控制显示次数的逻辑:

typescript 复制代码
export const shouldShowProjectOnboarding = memoize((): boolean => {
  if (
    projectConfig.hasCompletedProjectOnboarding ||
    projectConfig.projectOnboardingSeenCount >= 4 ||  // ← 最多显示4次
    process.env.IS_DEMO
  ) {
    return false
  }
  return !isProjectOnboardingComplete()
})

看了 4 次还不运行 /init?那就不再烦你了。


命令注册机制

commands.ts 中,init 的注册方式极其简单:

typescript 复制代码
import init from './commands/init.js'

// 在命令数组中直接引入
const commands = [
  // ...其他命令
  init,
  // ...
]

所有命令共享 Command 类型接口:

typescript 复制代码
type Command = {
  type: 'prompt' | 'local' | 'dialog'
  name: string
  description: string
  getPromptForCommand(): Promise<Content[]>  // prompt 类型必须实现
  // ...
}

三种命令类型决定了执行方式:

  • prompt:注入 prompt 让 LLM 自己执行(/init、/commit、/review)
  • local:本地直接执行,不需要 LLM(/clear、/exit、/model)
  • dialog:弹出交互对话框(/config、/permissions)

设计亮点:三个可迁移的 Pattern

1. Prompt-as-Command 模式

/init 不是代码生成器,是 prompt 注入器。这种设计让命令的"智能"完全来自 LLM 本身------你不需要写复杂的代码逻辑来分析代码库,只需要写一段好的 prompt。

可迁移到你的项目:如果你在做 AI Agent,很多命令不需要写后端逻辑,只需要设计好 prompt 然后让 LLM 调用工具自己搞定。

2. 负面约束 > 正面指令

整个 OLD_INIT_PROMPT 的精华不在"要写什么"(2 条),而在"不要写什么"(6 条)。这是 prompt engineering 的核心经验:告诉 LLM 什么不该做,比告诉它该做什么更有效。

可迁移:写 system prompt 时,花更多精力在"禁止规则"上,而不是"要求规则"上。

3. 幂等设计

css 复制代码
- If there's already a CLAUDE.md, suggest improvements to it.

/init 不是一次性命令------重复运行不会覆盖,而是"改进"。这让用户可以安全地在项目演进过程中反复运行。

可迁移:设计 AI 工具的命令时,默认做成幂等的(可重复执行,结果稳定),而不是破坏性的。


实战建议

启用新模式

bash 复制代码
# 单次
CLAUDE_CODE_NEW_INIT=1 claude

# 永久
echo 'export CLAUDE_CODE_NEW_INIT=1' >> ~/.zshrc

什么时候重新运行 /init

  • 大型重构后(框架迁移、目录结构变更)
  • 删除了已废弃模块后
  • 新人入职前(确保 CLAUDE.md 是最新的)

CLAUDE.md 写得好的标准

问自己一个问题:如果删掉这一行,Claude 会犯错吗?

如果不会------删掉它。


下篇预告

第 2 篇:128K 上下文也会爆?看 Claude Code 怎么用一条命令「瘦身」对话

/compact 命令解决的是 AI Agent 最核心的工程问题:当对话越来越长,上下文窗口不够用时怎么办?我们从源码看 Anthropic 的压缩策略。


本文基于 Claude Code CLI 源码(TypeScript,反编译自 source map)分析。源码路径:src/commands/init.ts。如有更新,以最新版本为准。

相关推荐
沉默王二2 小时前
又一款国产模型诞生,StepPlan性价比杀疯了!
agent·ai编程·claude
小七-七牛开发者3 小时前
周一上线|瑞幸把咖啡做进 CLI,Fable 5 短暂登场,Stonk Rider 骑上 K 线图
ai·chatgpt·大模型·agent·claude·codex·skill·claudecode·ai coding
字节逆旅14 小时前
Claude Code Router 接入过程的爬坑记录
人工智能·claude
乘风gg15 小时前
🤡PUA AI Coding 工具 的 10 条终极语录
前端·ai编程·claude
IT 行者18 小时前
GitHub Spec Kit 实战(五):/speckit.tasks 怎么拆——Spec Kit 五部曲收官
java·ai编程·claude
ZzT19 小时前
费时费力做的 Spec 和知识库,Agent 真的受到约束了么?
openai·ai编程·claude
IT 行者21 小时前
GitHub Spec Kit 实战(六):/speckit.implement 怎么用、怎么审、怎么发现 spec 阶段的遗漏——五部曲收官
java·驱动开发·github·ai编程·claude
崔庆才丨静觅1 天前
Claude Code 对接 NanoBanana MCP
api·claude·mcp
IT 行者1 天前
GitHub Spec Kit 实战(四):读懂和干预 /speckit.plan——AI 最自由发挥的一步
java·人工智能·github·ai编程·claude