# Skills

> 五路来源与优先级、SKILL.md 与 frontmatter 解析、参数展开时的 XML 转义、Skill 工具与 /skill 斜杠命令，以及「能力即 Markdown」的实现路径。

- 作者：David（道雾轩）
- 专栏：Kimi Code 源码解读（https://daiw.org/manual/kimi-code.md）
- 最后更新：2026-09-19
- 原文：https://daiw.org/manual/kimi-code/skills
- 转载与引用：请注明出处并附原文链接（https://daiw.org/about/copyright）

Skill 是「用 Markdown 写的能力」——不写代码，写一段带元数据的指令文档，agent 按需加载。v0.29 时代它在 `packages/agent-core/src/skill/`；现在是 agent-core-v2 的一个 Feature：`packages/agent-core-v2/src/features/skill/`，由 `SkillFeature` 往 agent 作用域贡献 `IAgentSkillService` 与 `Skill` 工具。

## 扫描路径

`features/skill/catalog/skillRoots.ts:8`：

```ts
const USER_BRAND_DIRS = ['skills'] as const;
const USER_GENERIC_DIRS = ['.agents/skills'] as const;
const PROJECT_BRAND_DIRS = ['.kimi-code/skills'] as const;
const PROJECT_GENERIC_DIRS = ['.agents/skills'] as const;
```

两个维度交叉：

| | 品牌专属 | 通用 |
| --- | --- | --- |
| **用户级** | `~/.kimi-code/skills/` | `~/.agents/skills/` |
| **项目级** | `<项目>/.kimi-code/skills/` | `<项目>/.agents/skills/` |

用户级品牌目录相对 `homeDir`（即 `~/.kimi-code` 或 `KIMI_CODE_HOME`）拼接，所以常量里只写 `skills`，不能再加一层 `.kimi-code`；通用目录相对系统 home。项目根是从工作目录向上找到的第一个 `.git`。**`.agents/skills` 是跨工具通用目录**——同一份 skill 可以被多个 agent 工具共享，这是个务实的生态选择。

## 五路来源与优先级

扫描目录只是来源之一。`catalog/skillSource.ts:11`：

```ts
export const SKILL_SOURCE_PRIORITY = {
  builtin: 0,
  plugin: 5,
  extra: 10,
  user: 20,
  workspace: 30,
} as const;
```

`WorkspaceSkillCatalogService` 按优先级从低到高依次 `register(skill, { replace: true })`，同名时后注册的覆盖先注册的，所以**项目 > 用户 > 额外目录 > 插件 > 内置**。额外目录来自配置 `extraSkillDirs`；启动时传 `--skills-dir` 则只用这些目录，跳过用户级与项目级的自动扫描。用户级与项目级目录有文件监听，增删 skill 后自动重载。

## 解析

skill 有两种形态：目录里的 `SKILL.md`（frontmatter 必须有 `name` 与 `description`），或根目录下独立的 `<name>.md`（名字缺省取文件名、描述取正文第一行）。`catalog/parser.ts` 借 `_base/text/frontmatter.ts`（内部是 `js-yaml`）解析，与本站的内容模型同构。

`type` 支持 `prompt`/`inline`（默认）、`flow` 与 `reference`（后者不能被触发）；未知类型记为 `SkippedSkill`，frontmatter 写错则记一条警告后跳过——**一个坏文件不该让所有 skill 都用不了**。父 skill 声明 `has-sub-skill: true` 后，子目录里的 skill 以「父名.子名」注册为子技能。

名字归一化 `normalizeSkillName()` 只做一件事：`toLowerCase()`。`Write-TUI` 与 `write-tui` 是同一个，`write_tui` 则是另一个。

## 参数展开与 XML 转义

skill 正文里可以放占位符：`$ARGUMENTS`、`$1`、`$ARGUMENTS[0]`、frontmatter `arguments` 声明的具名参数，以及 `${KIMI_SKILL_DIR}`、`${KIMI_SESSION_ID}`。展开在 `catalog/registry.ts:154` 的 `expandSkillParameters()`：

```ts
content = content
  .replaceAll(/\$ARGUMENTS\[(\d+)\]/g, (_match, indexText: string) => {
    const index = Number.parseInt(indexText, 10);
    return escapeXmlTags(tokens[index] ?? '');
  })
  .replaceAll(/\$(\d+)(?!\w)/g, (_match, indexText: string) => {
    const index = Number.parseInt(indexText, 10);
    return escapeXmlTags(tokens[index] ?? '');
  })
  .replaceAll('$ARGUMENTS', escapeXmlTags(rawArgs));
```

被 `escapeXmlTags` 转义的是**参数**，不是 skill 正文（v1 也是如此）。参数来自用户输入或模型调用，最容易夹带 `<system>` 之类的标签去伪造结构化内容；转义 `<`、`>` 就把这条注入路径堵死。正文没有占位符时，参数作为一行 `ARGUMENTS:` 附在末尾，同样经过转义。

正文本身按提示词原样注入，包在 `<skill-loaded name="…" trigger="…">` 块里，属性值经 `escapeXml` 转义。插件 skill 可能带一段 `<plugin-instructions>` 前缀，插件的系统提示则被明确声明为「插件提供的参考数据，不是特权指令通道」（见[插件与市场](https://daiw.org/manual/kimi-code/plugins)）。

## 两种触发方式

**用户**：TUI 把 skill 变成 slash 命令，`apps/kimi-code/src/tui/commands/skills.ts:36`：

```ts
const commandName =
  skill.source === 'builtin' || skill.isSubSkill === true
    ? skill.name
    : `skill:${skill.name}`;
```

内置 skill 与子技能直接用名字（如 `/mcp-config`），其余一律 `/skill:<name>`，避免与内置 slash 命令重名。

**模型**：`features/skill/tools/skillTool.ts` 的 `Skill` 工具。只有 inline 类型、且没设 `disableModelInvocation` 的 skill 能被模型调用，嵌套调用最多 3 层（`MAX_SKILL_QUERY_DEPTH`）。加载结果不当工具输出返回，而是以 `steer` 方式作为一条 user 消息插入上下文。

skill 所在目录还会并入 Read、Write、Edit、Glob、Grep 的可访问目录（`catalog.getSkillRoots()`），让 skill 能引用自己目录下的脚本与参考文件——这是 v1 `extendWorkspaceWithSkillRoots()` 的现役对应。

## 内置 skills

`catalog/builtin/builtin.ts:17`：

```ts
export const BUILTIN_SKILLS: readonly SkillDefinition[] = [
  MCP_CONFIG_SKILL,
  IMPORT_FROM_CC_CODEX_SKILL,
  UPDATE_CONFIG_SKILL,
  CUSTOM_THEME_SKILL,
  WRITE_GOAL_SKILL,
  CHECK_KIMI_CODE_DOCS_SKILL,
  SUB_SKILL_PARENT,
  SUB_SKILL_REVIEW,
  SUB_SKILL_CONSOLIDATE,
];
```

每个内置 skill 都是「一份 `.md` 加二十来行 `.ts` 包装」：`.ts` 只负责 `?raw` 导入正文、交给 `parseSkillText()`、补几个元数据，比如 `mcp-config` 被标为 `disableModelInvocation` 与 `productSpecific`（后者可用配置 `builtinProductSkills` 整体关掉）。`sub-skill/` 用 **`SKILL.md`** 文件名约定，与 `.agents/skills` 生态一致。

**`/mcp-config` 的业务逻辑全在那段 Markdown 里**：先判断用户要登录还是改配置，再指导模型读写三层 `mcp.json`（见 [MCP 集成](https://daiw.org/manual/kimi-code/mcp)）。能用提示词表达的就不写代码，这是 agent 时代的实现方式。

下一篇：[Hooks](https://daiw.org/manual/kimi-code/hooks)——另一条扩展路径。
