Skip to content
源码分析手册

内部专属技能 (Internal Bundled Skills)

Claude Code 的源码中隐藏了一系列特殊的“斜杠命令”,它们被统称为 Bundled Skills。有趣的是,其中最强大的一部分命令被 process.env.USER_TYPE === 'ant' 严密包裹着 —— 它们是 Anthropic 工程师们用来调优、诊断和“调教” Claude 的秘密武器。

它解决了什么问题

它们是硬编码在客户端中的高级宏指令。不同于普通的工具调用,这些技能直接操作会话上下文、读取底层系统指标、甚至能直接向 Slack 频道发送诊断报告。它们展示了 Anthropic 内部是如何进行“吃自家狗粮”(Dogfooding)的。

从源码看实现

这些技能都通过 registerBundledSkill 注册,并根据用户身份决定是否激活:

  1. /stuck:冻结诊断器 (src/skills/bundled/stuck.ts) 这是最硬核的命令。当工程师觉得 Claude 响应变慢时,运行 /stuck。它会调用 ps 命令检查系统中所有 Claude 进程,分析 CPU 占用、RSS 内存,甚至通过进程状态码(如 D 状态表示 I/O 挂起,T 状态表示被 Ctrl+Z 暂停)来定位故障。最神奇的是,它能直接通过 Slack MCP 工具将诊断报告发往内部频道 #claude-code-feedback

  2. /skillify:会话转技能 (src/skills/bundled/skillify.ts) 这其实是 Claude Code “技能系统”的元编辑器。它会分析当前会话的 session_memory 和用户消息,识别出用户完成某项任务的重复性步骤,然后引导用户创建一个新的 .SKILL.md 文件。它能自动识别哪些步骤需要“人工检查点”(Human checkpoint),哪些步骤可以“派生子代理”(Forked context)执行。

  3. /remember:记忆升舱 (src/skills/bundled/remember.ts) Claude 会在会话中自动产生“自动记忆”(Auto-memory)。/remember 命令负责将这些零散的记忆进行分类:

    • 属于项目规范的,建议推广(Promote)到 CLAUDE.md
    • 属于个人偏好的,移动到 CLAUDE.local.md
    • 它还会检测不同层级记忆之间的冲突和重复,保持知识库的整洁。
  4. /lorem-ipsum:压力测试工具 (src/skills/bundled/loremIpsum.ts) 为了测试 Claude 在长上下文下的表现,这个命令可以一键生成多达 50 万个 Token 的填充文本。它内置了一组“单 Token 词表”(ONE_TOKEN_WORDS),确保生成的文本量大的同时,token 计数也极其精准。

  5. /verify:变更验证 (src/skills/bundled/verify.ts) 这是一个被保护的实验性技能,用于在模型修改代码后,通过预定义的流程(通常涉及运行应用或测试套件)来自动化验证代码变更的正确性。

限制与陷阱

  • Ant 身份隔离:绝大多数此类技能在普通用户的 USER_TYPE 下是不可见的。如果你在非 Ant 环境下修改源码强行开启,可能会因为缺少相应的 MCP 工具(如 Slack 发送权限)或特定的文件路径而报错。
  • 无模型调用 (disableModelInvocation):像 /debug/skillify 这样的命令通常设置了 disableModelInvocation: true。这意味着当你输入这些命令时,Claude 不会先去咨询大模型,而是直接运行本地代码逻辑并返回一段特定的 Prompt。这既节省了 Token,也保证了这些管理指令的确定性。

延伸阅读

  • 了解这些技能如何存储在项目里,请阅读 src/skills/bundled/index.ts
  • 看看普通用户也能用的技能是如何实现的,例如 /keybindings/config

源码锚点

  • src/skills/bundled/stuck.ts: 进程监控与 Slack 集成的实现。
📄 src/skills/bundled/stuck.ts — 进程监控与 Slack 集成的实现。L42-71 of 80
typescript
**Only post to Slack if you actually found something stuck.** If every session looks healthy, tell the user that directly — do not post an all-clear to the channel.

If you did find a stuck/slow session, post to **#claude-code-feedback** (channel ID: \`C07VBSHV7EV\`) using the Slack MCP tool. Use ToolSearch to find \`slack_send_message\` if it's not already loaded.

**Use a two-message structure** to keep the channel scannable:

1. **Top-level message** — one short line: hostname, Claude Code version, and a terse symptom (e.g. "session PID 12345 pegged at 100% CPU for 10min" or "git subprocess hung in D state"). No code blocks, no details.
2. **Thread reply** — the full diagnostic dump. Pass the top-level message's \`ts\` as \`thread_ts\`. Include:
   - PID, CPU%, RSS, state, uptime, command line, child processes
   - Your diagnosis of what's likely wrong
   - Relevant debug log tail or \`sample\` output if you captured it

If Slack MCP isn't available, format the report as a message the user can copy-paste into #claude-code-feedback (and let them know to thread the details themselves).

## Notes
- Don't kill or signal any processes — this is diagnostic only.
- If the user gave an argument (e.g., a specific PID or symptom), focus there first.
`

export function registerStuckSkill(): void {
  if (process.env.USER_TYPE !== 'ant') {
    return
  }

  registerBundledSkill({
    name: 'stuck',
    description:
      '[ANT-ONLY] Investigate frozen/stuck/slow Claude Code sessions on this machine and post a diagnostic report to #claude-code-feedback.',
    userInvocable: true,
    async getPromptForCommand(args) {
  • src/skills/bundled/skillify.ts: 从会话历史中提取结构化技能的逻辑。
📄 src/skills/bundled/skillify.ts — 从会话历史中提取结构化技能的逻辑。L6-20 of 198
typescript
function extractUserMessages(messages: Message[]): string[] {
  return messages
    .filter((m): m is Extract<typeof m, { type: 'user' }> => m.type === 'user')
    .map(m => {
      const content = m.message.content
      if (typeof content === 'string') return content
      return content
        .filter(
          (b): b is Extract<typeof b, { type: 'text' }> => b.type === 'text',
        )
        .map(b => b.text)
        .join('\n')
    })
    .filter(text => text.trim().length > 0)
}
  • src/skills/bundled/remember.ts: 跨文件记忆管理的 Prompt 模板。
📄 src/skills/bundled/remember.ts — 跨文件记忆管理的 Prompt 模板。L72-80 of 83
typescript
    async getPromptForCommand(args) {
      let prompt = SKILL_PROMPT

      if (args) {
        prompt += `\n## Additional context from user\n\n${args}`
      }

      return [{ type: 'text', text: prompt }]
    },
  • src/skills/bundled/loremIpsum.ts: 基于统计概率的高性能填充文本生成器。
📄 src/skills/bundled/loremIpsum.ts — 基于统计概率的高性能填充文本生成器。L5-34 of 283
typescript
const ONE_TOKEN_WORDS = [
  // Articles & pronouns
  'the',
  'a',
  'an',
  'I',
  'you',
  'he',
  'she',
  'it',
  'we',
  'they',
  'me',
  'him',
  'her',
  'us',
  'them',
  'my',
  'your',
  'his',
  'its',
  'our',
  'this',
  'that',
  'what',
  'who',
  // Common verbs
  'is',
  'are',
  'was',
  • src/skills/bundled/debug.ts: 差异化的 /debug 命令实现(对 Ant 提供日志预览,对普通用户提供引导)。
📄 src/skills/bundled/debug.ts — 差异化的 `/debug` 命令实现(对 Ant 提供日志预览,对普通用户提供引导)。L9-38 of 104
typescript
const DEFAULT_DEBUG_LINES_READ = 20
const TAIL_READ_BYTES = 64 * 1024

export function registerDebugSkill(): void {
  registerBundledSkill({
    name: 'debug',
    description:
      process.env.USER_TYPE === 'ant'
        ? 'Debug your current Claude Code session by reading the session debug log. Includes all event logging'
        : 'Enable debug logging for this session and help diagnose issues',
    allowedTools: ['Read', 'Grep', 'Glob'],
    argumentHint: '[issue description]',
    // disableModelInvocation so that the user has to explicitly request it in
    // interactive mode and so the description does not take up context.
    disableModelInvocation: true,
    userInvocable: true,
    async getPromptForCommand(args) {
      // Non-ants don't write debug logs by default — turn logging on now so
      // subsequent activity in this session is captured.
      const wasAlreadyLogging = enableDebugLogging()
      const debugLogPath = getDebugLogPath()

      let logInfo: string
      try {
        // Tail the log without reading the whole thing - debug logs grow
        // unbounded in long sessions and reading them in full spikes RSS.
        const stats = await stat(debugLogPath)
        const readSize = Math.min(stats.size, TAIL_READ_BYTES)
        const startOffset = stats.size - readSize
        const fd = await open(debugLogPath, 'r')

基于 Claude Code v2.1.88 开源快照的深度分析