Claude Code 源码分析Claude Code 源码分析
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
首页
源码统计
系统架构
UML 图表
工具系统
CodeGraph
  • 概览

    • Claude Code 源码分析
    • 源码统计
    • CodeGraph 图谱
  • 架构

    • 系统架构
    • UML 图表索引
    • 查询引擎
    • 核心流程
    • 消息系统
    • 状态管理
  • 功能模块

    • 工具系统
    • 斜杠命令
    • 服务层
    • MCP 协议
    • Skills 技能
    • 子代理系统
  • 分层深度

    • 入口层
    • UI / Ink 层
    • utils 基础设施
    • 桥接 / 远程
    • 上下文压缩
  • 原理与安全

    • 底层原理
    • 技术难点
    • 权限与安全
    • 内部机制
    • 遥测与分析
  • 深度专题

    • Hooks 系统
    • 插件系统
    • 记忆系统
    • API 通信层
    • Ink 终端 UI
    • 认证系统
    • 构建与发布
    • 术语表
  • 调用分析

    • 调用链分析
    • 核心文件索引
  • 模块详解

    • utils

      • 模块: utils
      • messages · 消息工厂与规范化
      • session-storage · JSONL 会话持久化
      • permissions · 工具权限决策
      • shell-hooks · 用户 Shell Hook 系统
    • components

      • 模块: components
      • REPL · 主屏编排
      • messages · 消息行渲染
      • PermissionRequest · 权限弹窗
      • PromptInput · 底部输入
    • services

      • 模块: services
      • api-claude · Anthropic API 流式与重试
      • mcp-client · MCP 连接与工具调用
      • compact · 上下文压缩与自动触发
      • analytics · GrowthBook、Datadog 与 1P 事件
    • tools

      • 模块: tools
      • tool-interface · Tool 契约与注册表
      • bash-tool · Shell 执行与权限
      • streaming-executor · 流式工具并发调度
      • agent-tool · 子 Agent 委派
    • commands

      • 模块: commands
      • command-registry · commands.ts 注册与分派
      • model-command · /model 模型选择
      • mcp-commands · /mcp 服务器管理
      • compact-memory-commands · /compact 与 /memory
    • ink

      • 模块: ink
      • Ink 渲染管线 · Screen 与终端输出
      • 终端事件 · resize、paste、stdin
      • Ink Hooks · 输入、搜索、终端状态
      • Ink 组件 · Box、Text、ScrollBox 原语
    • hooks

      • 模块: hooks
      • useCanUseTool · 权限 UI 接缝
      • 输入与快捷键 Hook
      • 合并态 Hook(MCP + 本地)
      • notifs 通知 Hook
    • bridge

      • 模块: bridge
      • repl-bridge · REPL 桥初始化与传输
      • bridge-messaging · 桥消息路由与入站处理
      • remote-bridge-core · env-less 核心与守护主循环
      • bridge-permissions-ui · 权限、API 与 TUI
    • cli

      • 模块: cli
      • Structured IO · NDJSON SDK 协议
      • CLI Transports · Session Ingress 传输层
      • CLI Handlers · 子命令懒加载实现
      • Update & Upload · 自更新与串行上传原语
    • screens

      • 模块: screens
      • REPL 屏 · Screen 类型与顶层路由
      • ResumeConversation · 会话恢复选择器
      • Doctor · 安装诊断全屏
    • entrypoints

      • 模块: entrypoints
      • cli-entrypoint · Bootstrap 与快路径
      • sdk-types · core / control / runtime 类型体系
      • mcp-entrypoint · MCP stdio 服务器
      • sandbox-types · 沙箱配置单一真相源
    • skills

      • 模块: skills
      • skills-loading · 磁盘加载与 bundled 注册表
      • bundled-skills · 内置 skill 与 initBundledSkills
      • mcp-skills · MCP prompt 转 skill
      • skill-tool-integration · SkillTool 与命令注册
    • types

      • 模块: types
      • message-types · Message 联合与 content blocks
      • tool-permission-types · Tool、Permission、Command 类型
      • api-sdk-types · API 与 Hooks 协议类型
      • misc-types · ids、plugin、generated 与其余类型
    • tasks

      • 模块: tasks
      • local-agent-task · 本地 Agent 与主会话后台化
      • remote-agent-task · 远程 CCR 与 In-Process Teammate
      • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
      • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybindings

      • 模块: keybindings
      • keybinding-registry · 注册、Provider 与 useKeybinding
      • default-bindings · 默认键位表与平台差异
      • command-bindings · command:* 动态斜杠命令绑定
      • vim-bindings · Vim 模式与 keybindings 边界
    • memdir

      • 模块: memdir
      • memdir-core · 路径、加载与 MEMORY.md
      • memory-extraction · extractMemories 与 SessionMemory
      • memdir-commands · /memory、/remember 与命令集成
    • state

      • 模块: state
      • app-state-core · store、AppState 类型与 Provider
      • app-state-selectors · selectors 与 onChangeAppState
      • teammate-state · 队友视图与 swarm 状态
      • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query

      • 模块: query
      • query config 与 deps · 配置快照与依赖注入
      • query tokenBudget · +500k 自动续跑
      • query transitions · Continue / Terminal 状态机
      • query stopHooks · Stop 事件与 turn 结束编排
  • 模块详解(扩展)

    • messages · 消息工厂与规范化
    • session-storage · JSONL 会话持久化
    • permissions · 工具权限决策
    • shell-hooks · 用户 Shell Hook 系统
    • REPL · 主屏编排
    • messages · 消息行渲染
    • PermissionRequest · 权限弹窗
    • PromptInput · 底部输入
    • api-claude · Anthropic API 流式与重试
    • mcp-client · MCP 连接与工具调用
    • compact · 上下文压缩与自动触发
    • analytics · GrowthBook、Datadog 与 1P 事件
    • tool-interface · Tool 契约与注册表
    • bash-tool · Shell 执行与权限
    • streaming-executor · 流式工具并发调度
    • agent-tool · 子 Agent 委派
    • command-registry · commands.ts 注册与分派
    • model-command · /model 模型选择
    • mcp-commands · /mcp 服务器管理
    • compact-memory-commands · /compact 与 /memory
    • Ink 渲染管线 · Screen 与终端输出
    • 终端事件 · resize、paste、stdin
    • Ink Hooks · 输入、搜索、终端状态
    • Ink 组件 · Box、Text、ScrollBox 原语
    • useCanUseTool · 权限 UI 接缝
    • 输入与快捷键 Hook
    • 合并态 Hook(MCP + 本地)
    • notifs 通知 Hook
    • repl-bridge · REPL 桥初始化与传输
    • bridge-messaging · 桥消息路由与入站处理
    • remote-bridge-core · env-less 核心与守护主循环
    • bridge-permissions-ui · 权限、API 与 TUI
    • Structured IO · NDJSON SDK 协议
    • CLI Transports · Session Ingress 传输层
    • CLI Handlers · 子命令懒加载实现
    • Update & Upload · 自更新与串行上传原语
    • REPL 屏 · Screen 类型与顶层路由
    • ResumeConversation · 会话恢复选择器
    • Doctor · 安装诊断全屏
    • cli-entrypoint · Bootstrap 与快路径
    • sdk-types · core / control / runtime 类型体系
    • mcp-entrypoint · MCP stdio 服务器
    • sandbox-types · 沙箱配置单一真相源
    • skills-loading · 磁盘加载与 bundled 注册表
    • bundled-skills · 内置 skill 与 initBundledSkills
    • mcp-skills · MCP prompt 转 skill
    • skill-tool-integration · SkillTool 与命令注册
    • message-types · Message 联合与 content blocks
    • tool-permission-types · Tool、Permission、Command 类型
    • api-sdk-types · API 与 Hooks 协议类型
    • misc-types · ids、plugin、generated 与其余类型
    • local-agent-task · 本地 Agent 与主会话后台化
    • remote-agent-task · 远程 CCR 与 In-Process Teammate
    • shell-workflow-tasks · Bash 后台、Workflow 与 stopTask
    • dream-monitor-tasks · Dream、Monitor MCP 与 pill 文案
    • keybinding-registry · 注册、Provider 与 useKeybinding
    • default-bindings · 默认键位表与平台差异
    • command-bindings · command:* 动态斜杠命令绑定
    • vim-bindings · Vim 模式与 keybindings 边界
    • memdir-core · 路径、加载与 MEMORY.md
    • memory-extraction · extractMemories 与 SessionMemory
    • memdir-commands · /memory、/remember 与命令集成
    • app-state-core · store、AppState 类型与 Provider
    • app-state-selectors · selectors 与 onChangeAppState
    • teammate-state · 队友视图与 swarm 状态
    • state-boundaries · bootstrap、sessionStorage、FileStateCache
    • query config 与 deps · 配置快照与依赖注入
    • query tokenBudget · +500k 自动续跑
    • query transitions · Continue / Terminal 状态机
    • query stopHooks · Stop 事件与 turn 结束编排
  • 工具详解

    • tool-interface · Tool 契约与注册表
    • tool-permission-types · Tool、Permission、Command 类型
    • 工具: Bash
    • 工具: PowerShell
    • 工具: Agent
    • 工具: LSP
    • 工具: FileEdit
    • 工具: FileRead
    • 工具: Skill
    • 工具: WebFetch
    • 工具: MCP
    • 工具: SendMessage
    • 工具: FileWrite
    • 工具: Config
    • 工具: Grep
    • 工具: Brief
    • 工具: ExitPlanMode
    • 工具: ToolSearch
    • 工具: NotebookEdit
    • 工具: TaskOutput
    • 工具: WebSearch
    • 工具: ScheduleCron

本章总览

SkillTool 是模型 invocation skill 的入口 tool(SKILL_TOOL_NAME),串联 findCommand、权限(SkillPermissionRequest)、fork context(executeForkedSkill)与 prompt 注入。commands.ts 在启动时 merge bundled + disk skills。本章描述 SkillTool 与 skills 加载层的集成,以及 slash command 注册热重载。

学完本章你应该能

  • 说明 SkillTool 与 getCommands 的能力差异(MCP merge)
  • 解释 executeForkedSkill 与 context:fork frontmatter
  • 描述 SkillPermissionRequest 与 getRuleByContentsForTool
  • 理解 addInvokedSkill / recordSkillUsage telemetry
  • 掌握 builtInCommandNames 与 bundled skill 命名

核心概念(先读懂这些)

SkillTool 是模型侧;slash 是用户侧

用户输入 /verify → processSlashCommand → findCommand。模型发 SkillTool tool_use → SkillTool.call → 同一 getPromptForCommand 路径。disableModelInvocation frontmatter 阻断后者保留前者。

fork context 隔离 token

executeForkedSkill 用 runAgent 在 subagent 跑 skill prompt,parent transcript 只收到摘要。loadSkillsDir frontmatter context:fork 与 bundled registerBundledSkill context 字段一致。

建议学习步骤

  1. 阅读 getAllCommands MCP merge(源码块 A)
  2. 阅读 executeForkedSkill 开头(源码块 B)
  3. 阅读 SkillTool buildTool / schema(源码块 C)
  4. 阅读 commands.ts skills import(源码块 D)
  5. 阅读 SkillPermissionRequest 接缝(源码块 E)

常见误区

注意

custom skill 名在 fork telemetry sanitizes 为 custom

注意

remote skill search 用 require gated by EXPERIMENTAL_SKILL_SEARCH

注意

SkillTool prompt.ts 描述可用 skill 列表给模型

SkillTool 在 tool 体系中的位置

getTools(toolPermissionContext)
  └─ SkillTool (buildTool)

模型 tool_use: SkillTool { command: "verify", args: "..." }
  → getAllCommands(context)  // local + mcp skills
  → findCommand(name)
  → permission: SkillPermissionRequest / getRuleByContentsForTool
  → inline: inject prompt blocks → continue query
  → fork: executeForkedSkill → runAgent → extractResultText

用户 /verify args
  → processSlashCommand (utils/processUserInput)
  → 同一 findCommand + getPromptForCommand

constants/tools.ts 注册 SKILL_TOOL_NAME;prompts 教模型何时用 Skill vs 直接工具。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 1–75 行(共 1109 行)

   1| import { feature } from 'bun:bundle'
   2| import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
   3| import uniqBy from 'lodash-es/uniqBy.js'
   4| import { dirname } from 'path'
   5| import { getProjectRoot } from 'src/bootstrap/state.js'
   6| import {
   7|   builtInCommandNames,
   8|   findCommand,
   9|   getCommands,
  10|   type PromptCommand,
  11| } from 'src/commands.js'
  12| import type {
  13|   Tool,
  14|   ToolCallProgress,
  15|   ToolResult,
  16|   ToolUseContext,
  17|   ValidationResult,
  18| } from 'src/Tool.js'
  19| import { buildTool, type ToolDef } from 'src/Tool.js'
  20| import type { Command } from 'src/types/command.js'
  21| import type {
  22|   AssistantMessage,
  23|   AttachmentMessage,
  24|   Message,
  25|   SystemMessage,
  26|   UserMessage,
  27| } from 'src/types/message.js'
  28| import { logForDebugging } from 'src/utils/debug.js'
  29| import type { PermissionDecision } from 'src/utils/permissions/PermissionResult.js'
  30| import { getRuleByContentsForTool } from 'src/utils/permissions/permissions.js'
  31| import {
  32|   isOfficialMarketplaceName,
  33|   parsePluginIdentifier,
  34| } from 'src/utils/plugins/pluginIdentifier.js'
  35| import { buildPluginCommandTelemetryFields } from 'src/utils/telemetry/pluginTelemetry.js'
  36| import { z } from 'zod/v4'
  37| import {
  38|   addInvokedSkill,
  39|   clearInvokedSkillsForAgent,
  40|   getSessionId,
  41| } from '../../bootstrap/state.js'
  42| import { COMMAND_MESSAGE_TAG } from '../../constants/xml.js'
  43| import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
  44| import {
  45|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  46|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
  47|   logEvent,
  48| } from '../../services/analytics/index.js'
  49| import { getAgentContext } from '../../utils/agentContext.js'
  50| import { errorMessage } from '../../utils/errors.js'
  51| import {
  52|   extractResultText,
  53|   prepareForkedCommandContext,
  54| } from '../../utils/forkedAgent.js'
  55| import { parseFrontmatter } from '../../utils/frontmatterParser.js'
  56| import { lazySchema } from '../../utils/lazySchema.js'
  57| import { createUserMessage, normalizeMessages } from '../../utils/messages.js'
  58| import type { ModelAlias } from '../../utils/model/aliases.js'
  59| import { resolveSkillModelOverride } from '../../utils/model/model.js'
  60| import { recordSkillUsage } from '../../utils/suggestions/skillUsageTracking.js'
  61| import { createAgentId } from '../../utils/uuid.js'
  62| import { runAgent } from '../AgentTool/runAgent.js'
  63| import {
  64|   getToolUseIDFromParentMessage,
  65|   tagMessagesWithToolUseID,
  66| } from '../utils.js'
  67| import { SKILL_TOOL_NAME } from './constants.js'
  68| import { getPrompt } from './prompt.js'
  69| import {
  70|   renderToolResultMessage,
  71|   renderToolUseErrorMessage,
  72|   renderToolUseMessage,
  73|   renderToolUseProgressMessage,
  74|   renderToolUseRejectedMessage,
  75| } from './UI.js'

源码缺失

File not found: c:\Users\123\Desktop\jd\Claude code源码\claude-code-complete\claude-code-complete\src-readable\tools\SkillTool\constants.js

getAllCommands:为何 SkillTool 不只用 getCommands

getCommands(projectRoot)(commands.ts)merge builtin + disk + bundled,不含 MCP AppState commands。

SkillTool getAllCommands 追加:

context.getAppState().mcp.commands.filter(
  cmd => cmd.type === 'prompt' && cmd.loadedFrom === 'mcp',
)

uniqBy name 去重。注释说明防止模型猜测 mcp__server__prompt 名 invoke 非 skill 化 MCP prompt。

getCommands 供 PromptInput slash 补全;SkillTool 供模型发现完整 skill 集。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 77–94 行(共 1109 行)

  77| /**
  78|  * Gets all commands including MCP skills/prompts from AppState.
  79|  * SkillTool needs this because getCommands() only returns local/bundled skills.
  80|  */
  81| async function getAllCommands(context: ToolUseContext): Promise<Command[]> {
  82|   // Only include MCP skills (loadedFrom === 'mcp'), not plain MCP prompts.
  83|   // Before this filter, the model could invoke MCP prompts via SkillTool
  84|   // if it guessed the mcp__server__prompt name — they weren't discoverable
  85|   // but were technically reachable.
  86|   const mcpSkills = context
  87|     .getAppState()
  88|     .mcp.commands.filter(
  89|       cmd => cmd.type === 'prompt' && cmd.loadedFrom === 'mcp',
  90|     )
  91|   if (mcpSkills.length === 0) return getCommands(getProjectRoot())
  92|   const localCommands = await getCommands(getProjectRoot())
  93|   return uniqBy([...localCommands, ...mcpSkills], 'name')
  94| }

源码引用: src/commands.ts · 第 155–165 行(共 755 行)

 155| import { logForDebugging } from './utils/debug.js'
 156| import {
 157|   getSkillDirCommands,
 158|   clearSkillCaches,
 159|   getDynamicSkills,
 160| } from './skills/loadSkillsDir.js'
 161| import { getBundledSkills } from './skills/bundledSkills.js'
 162| import { getBuiltinPluginSkillCommands } from './plugins/builtinPlugins.js'
 163| import {
 164|   getPluginCommands,
 165|   clearPluginCommandCache,

executeForkedSkill

async function executeForkedSkill(command, commandName, args, context, canUseTool, ...)

要点:

  • createAgentId() 新 subagent
  • builtInCommandNames().has(commandName) → telemetry sanitized name
  • isBundled = command.source === 'bundled'
  • prepareForkedCommandContext 组装 isolated messages
  • runAgent 执行 with separate token budget
  • extractResultText 返回 parent-facing 摘要

onProgress 渲染 SkillTool UI progress(UI.tsx)。

frontmatter context: 'fork' 或 bundled definition context 字段触发此路径。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 118–155 行(共 1109 行)

 118| /**
 119|  * Executes a skill in a forked sub-agent context.
 120|  * This runs the skill prompt in an isolated agent with its own token budget.
 121|  */
 122| async function executeForkedSkill(
 123|   command: Command & { type: 'prompt' },
 124|   commandName: string,
 125|   args: string | undefined,
 126|   context: ToolUseContext,
 127|   canUseTool: CanUseToolFn,
 128|   parentMessage: AssistantMessage,
 129|   onProgress?: ToolCallProgress<Progress>,
 130| ): Promise<ToolResult<Output>> {
 131|   const startTime = Date.now()
 132|   const agentId = createAgentId()
 133|   const isBuiltIn = builtInCommandNames().has(commandName)
 134|   const isOfficialSkill = isOfficialMarketplaceSkill(command)
 135|   const isBundled = command.source === 'bundled'
 136|   const forkedSanitizedName =
 137|     isBuiltIn || isBundled || isOfficialSkill ? commandName : 'custom'
 138| 
 139|   const wasDiscoveredField =
 140|     feature('EXPERIMENTAL_SKILL_SEARCH') &&
 141|     remoteSkillModules!.isSkillSearchEnabled()
 142|       ? {
 143|           was_discovered:
 144|             context.discoveredSkillNames?.has(commandName) ?? false,
 145|         }
 146|       : {}
 147|   const pluginMarketplace = command.pluginInfo
 148|     ? parsePluginIdentifier(command.pluginInfo.repository).marketplace
 149|     : undefined
 150|   const queryDepth = context.queryTracking?.depth ?? 0
 151|   const parentAgentId = getAgentContext()?.agentId
 152|   logEvent('tengu_skill_tool_invocation', {
 153|     command_name:
 154|       forkedSanitizedName as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
 155|     // _PROTO_skill_name routes to the privileged skill_name BQ column

源码引用: src/utils/forkedAgent.ts · 第 1–30 行(共 690 行)

   1| /**
   2|  * Helper for running forked agent query loops with usage tracking.
   3|  *
   4|  * This utility ensures forked agents:
   5|  * 1. Share identical cache-critical params with the parent to guarantee prompt cache hits
   6|  * 2. Track full usage metrics across the entire query loop
   7|  * 3. Log metrics via the tengu_fork_agent_query event when complete
   8|  * 4. Isolate mutable state to prevent interference with the main agent loop
   9|  */
  10| 
  11| import type { UUID } from 'crypto'
  12| import { randomUUID } from 'crypto'
  13| import type { PromptCommand } from '../commands.js'
  14| import type { QuerySource } from '../constants/querySource.js'
  15| import type { CanUseToolFn } from '../hooks/useCanUseTool.js'
  16| import { query } from '../query.js'
  17| import {
  18|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  19|   logEvent,
  20| } from '../services/analytics/index.js'
  21| import { accumulateUsage, updateUsage } from '../services/api/claude.js'
  22| import { EMPTY_USAGE, type NonNullableUsage } from '../services/api/logging.js'
  23| import type { ToolUseContext } from '../Tool.js'
  24| import type { AgentDefinition } from '../tools/AgentTool/loadAgentsDir.js'
  25| import type { AgentId } from '../types/ids.js'
  26| import type { Message } from '../types/message.js'
  27| import { createChildAbortController } from './abortController.js'
  28| import { logForDebugging } from './debug.js'
  29| import { cloneFileStateCache } from './fileStateCache.js'
  30| import type { REPLHookContext } from './hooks/postSamplingHooks.js'

SkillTool 定义与 prompt

buildTool 包装 SkillTool 定义:

  • input schema: command string + optional args(zod lazySchema)
  • getPrompt(prompt.ts)— 系统 prompt 中列出 invocable skills
  • renderToolUseMessage / Progress / Rejected UI(UI.tsx)
  • call 主逻辑:findCommand → permission → inline/fork 分支

addInvokedSkill(getSessionId()) 记录 session 内已 invoke skill,防重复或 analytics。

resolveSkillModelOverride 应用 skill frontmatter model 字段。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 67–75 行(共 1109 行)

  67| import { SKILL_TOOL_NAME } from './constants.js'
  68| import { getPrompt } from './prompt.js'
  69| import {
  70|   renderToolResultMessage,
  71|   renderToolUseErrorMessage,
  72|   renderToolUseMessage,
  73|   renderToolUseProgressMessage,
  74|   renderToolUseRejectedMessage,
  75| } from './UI.js'

源码引用: src/tools/SkillTool/prompt.ts · 第 1–40 行(共 242 行)

   1| import { memoize } from 'lodash-es'
   2| import type { Command } from 'src/commands.js'
   3| import {
   4|   getCommandName,
   5|   getSkillToolCommands,
   6|   getSlashCommandToolSkills,
   7| } from 'src/commands.js'
   8| import { COMMAND_NAME_TAG } from '../../constants/xml.js'
   9| import { stringWidth } from '../../ink/stringWidth.js'
  10| import {
  11|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  12|   logEvent,
  13| } from '../../services/analytics/index.js'
  14| import { count } from '../../utils/array.js'
  15| import { logForDebugging } from '../../utils/debug.js'
  16| import { toError } from '../../utils/errors.js'
  17| import { truncate } from '../../utils/format.js'
  18| import { logError } from '../../utils/log.js'
  19| 
  20| // Skill listing gets 1% of the context window (in characters)
  21| export const SKILL_BUDGET_CONTEXT_PERCENT = 0.01
  22| export const CHARS_PER_TOKEN = 4
  23| export const DEFAULT_CHAR_BUDGET = 8_000 // Fallback: 1% of 200k × 4
  24| 
  25| // Per-entry hard cap. The listing is for discovery only — the Skill tool loads
  26| // full content on invoke, so verbose whenToUse strings waste turn-1 cache_creation
  27| // tokens without improving match rate. Applies to all entries, including bundled,
  28| // since the cap is generous enough to preserve the core use case.
  29| export const MAX_LISTING_DESC_CHARS = 250
  30| 
  31| export function getCharBudget(contextWindowTokens?: number): number {
  32|   if (Number(process.env.SLASH_COMMAND_TOOL_CHAR_BUDGET)) {
  33|     return Number(process.env.SLASH_COMMAND_TOOL_CHAR_BUDGET)
  34|   }
  35|   if (contextWindowTokens) {
  36|     return Math.floor(
  37|       contextWindowTokens * CHARS_PER_TOKEN * SKILL_BUDGET_CONTEXT_PERCENT,
  38|     )
  39|   }
  40|   return DEFAULT_CHAR_BUDGET

权限:SkillPermissionRequest

skill 可能触发 SkillPermissionRequest(components/permissions/SkillPermissionRequest):

  • getRuleByContentsForTool 检查 permission rules 是否 allow skill content
  • 与 bash/edit 权限并列,REPL focusedInputDialog 栈管理

disableModelInvocation — 仅允许用户 /slash,SkillTool.call 拒绝。

userInvocable: false — 仅模型 SkillTool 可调用,slash 补全隐藏。

bundled remember 设 userInvocable: true + isEnabled gate。

源码引用: src/components/permissions/SkillPermissionRequest/SkillPermissionRequest.tsx · 第 1–30 行(共 254 行)

   1| import React, { useCallback, useMemo } from 'react'
   2| import { logError } from 'src/utils/log.js'
   3| import { getOriginalCwd } from '../../../bootstrap/state.js'
   4| import { Box, Text } from '../../../ink.js'
   5| import { sanitizeToolNameForAnalytics } from '../../../services/analytics/metadata.js'
   6| import { SKILL_TOOL_NAME } from '../../../tools/SkillTool/constants.js'
   7| import { SkillTool } from '../../../tools/SkillTool/SkillTool.js'
   8| import { env } from '../../../utils/env.js'
   9| import { shouldShowAlwaysAllowOptions } from '../../../utils/permissions/permissionsLoader.js'
  10| import { logUnaryEvent } from '../../../utils/unaryLogging.js'
  11| import { type UnaryEvent, usePermissionRequestLogging } from '../hooks.js'
  12| import { PermissionDialog } from '../PermissionDialog.js'
  13| import {
  14|   PermissionPrompt,
  15|   type PermissionPromptOption,
  16|   type ToolAnalyticsContext,
  17| } from '../PermissionPrompt.js'
  18| import type { PermissionRequestProps } from '../PermissionRequest.js'
  19| import { PermissionRuleExplanation } from '../PermissionRuleExplanation.js'
  20| 
  21| type SkillOptionValue = 'yes' | 'yes-exact' | 'yes-prefix' | 'no'
  22| 
  23| export function SkillPermissionRequest(
  24|   props: PermissionRequestProps,
  25| ): React.ReactNode {
  26|   const {
  27|     toolUseConfirm,
  28|     onDone,
  29|     onReject,
  30|     verbose: _verbose,

源码引用: src/tools/SkillTool/SkillTool.ts · 第 29–30 行(共 1109 行)

  29| import type { PermissionDecision } from 'src/utils/permissions/PermissionResult.js'
  30| import { getRuleByContentsForTool } from 'src/utils/permissions/permissions.js'

commands.ts 与启动注册

commands.ts import loadSkillsDir 函数 merge disk skills。

main.tsx 顺序:

init()
initBundledSkills()
...
getCommands(projectRoot) → 传入 REPL commands prop

REPL useSkillsChange — disk skill 变更 → setLocalCommands(refresh)

bundled skill 变更需新版本;MCP skill 随连接动态变化,SkillTool 每次 call 读 fresh AppState。

源码引用: src/main.tsx · 第 1920–1930 行(共 6604 行)

1920|         // These replace the CLAUDE_CODE_* environment variables
1921|         const teammateOpts = extractTeammateOptions(options)
1922|         storedTeammateOpts = teammateOpts
1923| 
1924|         // If any teammate identity option is provided, all three required ones must be present
1925|         const hasAnyTeammateOpt =
1926|           teammateOpts.agentId ||
1927|           teammateOpts.agentName ||
1928|           teammateOpts.teamName
1929|         const hasAllRequiredTeammateOpts =
1930|           teammateOpts.agentId &&

源码引用: src/commands.ts · 第 160–160 行(共 755 行)

 160| } from './skills/loadSkillsDir.js'

builtInCommandNames 与 official marketplace

builtInCommandNames() 返回内置 slash 名集合;SkillTool fork telemetry 对 builtIn/bundled/official 保留原名,custom skill sanitize 为 'custom'。

isOfficialMarketplaceSkill 检查 plugin 来源是否官方 marketplace,影响 telemetry 字段。

parsePluginIdentifier / isOfficialMarketplaceName 防混淆第三方 plugin skill。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 130–145 行(共 1109 行)

 130| ): Promise<ToolResult<Output>> {
 131|   const startTime = Date.now()
 132|   const agentId = createAgentId()
 133|   const isBuiltIn = builtInCommandNames().has(commandName)
 134|   const isOfficialSkill = isOfficialMarketplaceSkill(command)
 135|   const isBundled = command.source === 'bundled'
 136|   const forkedSanitizedName =
 137|     isBuiltIn || isBundled || isOfficialSkill ? commandName : 'custom'
 138| 
 139|   const wasDiscoveredField =
 140|     feature('EXPERIMENTAL_SKILL_SEARCH') &&
 141|     remoteSkillModules!.isSkillSearchEnabled()
 142|       ? {
 143|           was_discovered:
 144|             context.discoveredSkillNames?.has(commandName) ?? false,
 145|         }

inline skill 执行路径

非 fork skill 在 SkillTool.call 内:

  1. findCommand + type==='prompt' 校验
  2. getPromptForCommand(args, context) → ContentBlockParam[]
  3. 注入 messages 为 user/attachment 块(COMMAND_MESSAGE_TAG)
  4. 继续 parent query 循环

resolveSkillModelOverride 临时切换 model;recordSkillUsage 更新 suggestions。

disableModelInvocation 时 SkillTool.call early reject,用户 /slash 仍可用 processSlashCommand。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 200–250 行(共 1109 行)

 200|         : 'third-party') as AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
 201|       ...buildPluginCommandTelemetryFields(command.pluginInfo),
 202|     }),
 203|   })
 204| 
 205|   const { modifiedGetAppState, baseAgent, promptMessages, skillContent } =
 206|     await prepareForkedCommandContext(command, args || '', context)
 207| 
 208|   // Merge skill's effort into the agent definition so runAgent applies it
 209|   const agentDefinition =
 210|     command.effort !== undefined
 211|       ? { ...baseAgent, effort: command.effort }
 212|       : baseAgent
 213| 
 214|   // Collect messages from the forked agent
 215|   const agentMessages: Message[] = []
 216| 
 217|   logForDebugging(
 218|     `SkillTool executing forked skill ${commandName} with agent ${agentDefinition.agentType}`,
 219|   )
 220| 
 221|   try {
 222|     // Run the sub-agent
 223|     for await (const message of runAgent({
 224|       agentDefinition,
 225|       promptMessages,
 226|       toolUseContext: {
 227|         ...context,
 228|         getAppState: modifiedGetAppState,
 229|       },
 230|       canUseTool,
 231|       isAsync: false,
 232|       querySource: 'agent:custom',
 233|       model: command.model as ModelAlias | undefined,
 234|       availableTools: context.options.tools,
 235|       override: { agentId },
 236|     })) {
 237|       agentMessages.push(message)
 238| 
 239|       // Report progress for tool uses (like AgentTool does)
 240|       if (
 241|         (message.type === 'assistant' || message.type === 'user') &&
 242|         onProgress
 243|       ) {
 244|         const normalizedNew = normalizeMessages([message])
 245|         for (const m of normalizedNew) {
 246|           const hasToolContent = m.message.content.some(
 247|             c => c.type === 'tool_use' || c.type === 'tool_result',
 248|           )
 249|           if (hasToolContent) {
 250|             onProgress({

UI 渲染与 progress

SkillTool 使用 UI.tsx 四个 render 函数:

  • renderToolUseMessage — 模型发起 skill 时行内展示
  • renderToolUseProgressMessage — fork 或长 prompt 进度
  • renderToolUseRejectedMessage — 权限拒绝
  • renderToolResultMessage — 完成后摘要

与 BashTool 等共用 Tool 渲染管线;MessageRow 按 tool name 分发。

源码引用: src/tools/SkillTool/UI.tsx · 第 1–40 行(共 182 行)

   1| import type { ToolResultBlockParam } from '@anthropic-ai/sdk/resources/index.mjs'
   2| import * as React from 'react'
   3| import { SubAgentProvider } from 'src/components/CtrlOToExpand.js'
   4| import { FallbackToolUseErrorMessage } from 'src/components/FallbackToolUseErrorMessage.js'
   5| import { FallbackToolUseRejectedMessage } from 'src/components/FallbackToolUseRejectedMessage.js'
   6| import type { z } from 'zod/v4'
   7| import type { Command } from '../../commands.js'
   8| import { Byline } from '../../components/design-system/Byline.js'
   9| import { Message as MessageComponent } from '../../components/Message.js'
  10| import { MessageResponse } from '../../components/MessageResponse.js'
  11| import { Box, Text } from '../../ink.js'
  12| import type { Tools } from '../../Tool.js'
  13| import type { ProgressMessage } from '../../types/message.js'
  14| import { buildSubagentLookups, EMPTY_LOOKUPS } from '../../utils/messages.js'
  15| import { plural } from '../../utils/stringUtils.js'
  16| import type { inputSchema, Output, Progress } from './SkillTool.js'
  17| 
  18| type Input = z.infer<ReturnType<typeof inputSchema>>
  19| 
  20| const MAX_PROGRESS_MESSAGES_TO_SHOW = 3
  21| const INITIALIZING_TEXT = 'Initializing…'
  22| 
  23| export function renderToolResultMessage(output: Output): React.ReactNode {
  24|   // Handle forked skill result
  25|   if ('status' in output && output.status === 'forked') {
  26|     return (
  27|       <MessageResponse height={1}>
  28|         <Text>
  29|           <Byline>{['Done']}</Byline>
  30|         </Text>
  31|       </MessageResponse>
  32|     )
  33|   }
  34| 
  35|   const parts: string[] = ['Successfully loaded skill']
  36| 
  37|   // Show tools count (only for inline skills)
  38|   if (
  39|     'allowedTools' in output &&
  40|     output.allowedTools &&

telemetry 与 invoked skill 跟踪

SkillTool 集成:

  • recordSkillUsage — suggestions/skillUsageTracking
  • buildPluginCommandTelemetryFields — plugin 来源 skill
  • logEvent skill invoke success/failure
  • addInvokedSkill / clearInvokedSkillsForAgent — bootstrap state

pluginTelemetry 与 skillLoadedEvent 在 load 阶段 vs invoke 阶段分别打点。

官方 marketplace skill 用 isOfficialMarketplaceSkill 识别 telemetry 字段。

源码引用: src/tools/SkillTool/SkillTool.ts · 第 38–60 行(共 1109 行)

  38|   addInvokedSkill,
  39|   clearInvokedSkillsForAgent,
  40|   getSessionId,
  41| } from '../../bootstrap/state.js'
  42| import { COMMAND_MESSAGE_TAG } from '../../constants/xml.js'
  43| import type { CanUseToolFn } from '../../hooks/useCanUseTool.js'
  44| import {
  45|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_NOT_CODE_OR_FILEPATHS,
  46|   type AnalyticsMetadata_I_VERIFIED_THIS_IS_PII_TAGGED,
  47|   logEvent,
  48| } from '../../services/analytics/index.js'
  49| import { getAgentContext } from '../../utils/agentContext.js'
  50| import { errorMessage } from '../../utils/errors.js'
  51| import {
  52|   extractResultText,
  53|   prepareForkedCommandContext,
  54| } from '../../utils/forkedAgent.js'
  55| import { parseFrontmatter } from '../../utils/frontmatterParser.js'
  56| import { lazySchema } from '../../utils/lazySchema.js'
  57| import { createUserMessage, normalizeMessages } from '../../utils/messages.js'
  58| import type { ModelAlias } from '../../utils/model/aliases.js'
  59| import { resolveSkillModelOverride } from '../../utils/model/model.js'
  60| import { recordSkillUsage } from '../../utils/suggestions/skillUsageTracking.js'

源码缺失

File not found: c:\Users\123\Desktop\jd\Claude code源码\claude-code-complete\claude-code-complete\src-readable\bootstrap\state.js

本章小结与延伸

SkillTool = skills 系统的模型入口。加载见 skills-loading;内置见 bundled-skills。用户 /slash 与模型 SkillTool 共用 findCommand,disableModelInvocation 仅阻断后者。 继续学习:

  • skills-loading
  • components permissions-ui
Prev
mcp-skills · MCP prompt 转 skill
Next
message-types · Message 联合与 content blocks