From e9f117fd41f2c5102d5bc3e5f649417be7ea8a6d Mon Sep 17 00:00:00 2001 From: star Date: Sun, 28 Jun 2026 11:34:26 +0800 Subject: [PATCH] feat(agent-core): add Kimi Code docs system skill --- .changeset/system-docs-skill.md | 5 ++ apps/kimi-code/src/tui/commands/skills.ts | 4 +- .../test/tui/commands/skills.test.ts | 5 +- docs/en/customization/skills.md | 4 +- docs/en/reference/slash-commands.md | 7 +-- docs/zh/customization/skills.md | 4 +- docs/zh/reference/slash-commands.md | 7 +-- .../agent-core/src/profile/default/system.md | 2 +- packages/agent-core/src/rpc/core-api.ts | 2 +- packages/agent-core/src/session/index.ts | 10 +++- packages/agent-core/src/skill/index.ts | 1 + packages/agent-core/src/skill/registry.ts | 1 + packages/agent-core/src/skill/system.ts | 23 +++++++++ .../src/skill/system/kimi-code-docs/SKILL.md | 45 +++++++++++++++++ packages/agent-core/src/skill/types.ts | 2 +- .../test/harness/skill-session.test.ts | 20 ++++++++ .../agent-core/test/skill/registry.test.ts | 50 ++++++++++++++++++- packages/protocol/src/events.ts | 4 +- 18 files changed, 176 insertions(+), 20 deletions(-) create mode 100644 .changeset/system-docs-skill.md create mode 100644 packages/agent-core/src/skill/system.ts create mode 100644 packages/agent-core/src/skill/system/kimi-code-docs/SKILL.md diff --git a/.changeset/system-docs-skill.md b/.changeset/system-docs-skill.md new file mode 100644 index 000000000..2a20a006d --- /dev/null +++ b/.changeset/system-docs-skill.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Add a bundled Kimi Code docs Skill for product self-help. Run `/kimi-code-docs` to load it directly. diff --git a/apps/kimi-code/src/tui/commands/skills.ts b/apps/kimi-code/src/tui/commands/skills.ts index 2997a8b15..9e8619cea 100644 --- a/apps/kimi-code/src/tui/commands/skills.ts +++ b/apps/kimi-code/src/tui/commands/skills.ts @@ -26,7 +26,7 @@ function compareSkillSlashCommands(a: SkillSummary, b: SkillSummary): number { } function getSkillSlashCommandGroup(source: SkillSummary['source']): number { - return source === 'builtin' ? 0 : 1; + return source === 'builtin' || source === 'system' ? 0 : 1; } export function buildSkillSlashCommands(skills: readonly SkillSummary[]): SkillSlashCommands { @@ -34,7 +34,7 @@ export function buildSkillSlashCommands(skills: readonly SkillSummary[]): SkillS const sortedSkills = [...skills].toSorted(compareSkillSlashCommands); const commands = sortedSkills.filter(isUserActivatableSkill).map((skill) => { const commandName = - skill.source === 'builtin' || skill.isSubSkill === true + skill.source === 'builtin' || skill.source === 'system' || skill.isSubSkill === true ? skill.name : `skill:${skill.name}`; commandMap.set(commandName, skill.name); diff --git a/apps/kimi-code/test/tui/commands/skills.test.ts b/apps/kimi-code/test/tui/commands/skills.test.ts index b9cf46bd6..d2505a126 100644 --- a/apps/kimi-code/test/tui/commands/skills.test.ts +++ b/apps/kimi-code/test/tui/commands/skills.test.ts @@ -60,21 +60,24 @@ describe('skill slash commands', () => { ]); }); - it('sorts built-in skill slash commands before external skill commands', () => { + it('sorts bundled skill slash commands before external skill commands', () => { const built = buildSkillSlashCommands([ skill('zeta', 'prompt', { source: 'user' }), skill('alpha', 'prompt', { source: 'project' }), + skill('kimi-code-docs', 'inline', { source: 'system' }), skill('update-config', 'inline', { source: 'builtin' }), skill('mcp-config', 'inline', { source: 'builtin' }), ]); expect(built.commands.map((command) => command.name)).toEqual([ + 'kimi-code-docs', 'mcp-config', 'update-config', 'skill:alpha', 'skill:zeta', ]); expect([...built.commandMap.entries()]).toEqual([ + ['kimi-code-docs', 'kimi-code-docs'], ['mcp-config', 'mcp-config'], ['update-config', 'update-config'], ['skill:alpha', 'alpha'], diff --git a/docs/en/customization/skills.md b/docs/en/customization/skills.md index 70b36e1e8..1941bc1d7 100644 --- a/docs/en/customization/skills.md +++ b/docs/en/customization/skills.md @@ -63,7 +63,7 @@ Positional arguments support single and double quoting, so in `/skill:commit "fi ## Skill Locations -Kimi Code CLI scans four tiers by scope; more specific scopes take higher priority: **Project > User > Extra > Built-in** +Kimi Code CLI scans five tiers by scope; more specific scopes take higher priority: **Project > User > Extra > System > Built-in** **User level** (applies to all projects): - `$KIMI_CODE_HOME/skills/` (default: `~/.kimi-code/skills/`) @@ -81,6 +81,8 @@ The Kimi-specific user Skill directory moves with `KIMI_CODE_HOME`, so isolated extra_skill_dirs = ["~/team-skills", ".agents/team-skills"] ``` +**System Skills** are distributed with the CLI and registered at runtime. They provide product-aware workflows that should be available without user installation, while still being lower priority than project, user, and extra Skill directories. + **Built-in Skills** are distributed with the CLI and have the lowest priority. They provide out-of-the-box workflows for common tasks — for example, configuring MCP servers, customizing the TUI theme, and editing config files. See [Built-in skill commands](../reference/slash-commands.md#built-in-skill-commands) for the full list. ## Invoking a Skill diff --git a/docs/en/reference/slash-commands.md b/docs/en/reference/slash-commands.md index f74812c0e..78fe67cea 100644 --- a/docs/en/reference/slash-commands.md +++ b/docs/en/reference/slash-commands.md @@ -115,17 +115,18 @@ Prompt mode exits with code `0` when the goal completes, `3` when it blocks, and ## Built-in skill commands -Kimi Code CLI ships with a set of built-in Skills that appear directly as `/` slash commands. Unlike external Skills, they do not require the `skill:` prefix and are available out of the box. +Kimi Code CLI ships with a set of system and built-in Skills that appear directly as `/` slash commands. Unlike external Skills, they do not require the `skill:` prefix and are available out of the box. | Command | Description | | --- | --- | +| `/kimi-code-docs` | Load Kimi Code product documentation guidance for setup, configuration, MCP, skills, plugins, commands, and troubleshooting | | `/mcp-config` | Configure MCP servers and handle MCP OAuth login. See [MCP](../customization/mcp.md) | | `/custom-theme []` | Create or edit a custom TUI color theme. See [Themes](../customization/themes.md) | | `/update-config` | Inspect or edit `config.toml` (model, provider, permission, hooks) and `tui.toml` (theme, editor, notifications, auto-update) | | `/import-from-cc-codex` | Import Claude Code and Codex instructions, skills, and MCP settings into Kimi Code | | `/sub-skill` | Discover and reorganize the local skill inventory into hierarchical sub-skill bundles. Includes `/sub-skill.review` (read-only proposal) and `/sub-skill.consolidate` (apply the reorganization) | -All built-in Skill commands are only available in the idle state. +All system and built-in Skill commands are only available in the idle state. ## Skill Dynamic Commands @@ -147,7 +148,7 @@ For example, a child Skill named `review` inside a parent Skill named `code-styl For convenience, external Skill commands also support a shorthand form that omits the `skill:` prefix — `/` — as long as the name is not taken by a system slash command. That is, `/code-style` falls back to matching `/skill:code-style`. -Built-in Skills shipped with Kimi Code CLI appear directly as `/` in the slash command panel. For example, `/mcp-config` helps configure MCP servers and handle MCP OAuth login, and `/custom-theme [extra text]` invokes the custom-theme workflow to create or edit a TUI theme. +System and built-in Skills shipped with Kimi Code CLI appear directly as `/` in the slash command panel. For example, `/kimi-code-docs` loads Kimi Code product documentation guidance, `/mcp-config` helps configure MCP servers and handle MCP OAuth login, and `/custom-theme [extra text]` invokes the custom-theme workflow to create or edit a TUI theme. ::: info All Skill commands are only available in the idle state. `flow`-type Skills are also exposed via `/skill:` — there is no separate `/flow:` namespace. diff --git a/docs/zh/customization/skills.md b/docs/zh/customization/skills.md index 0ee0ce444..c4240deab 100644 --- a/docs/zh/customization/skills.md +++ b/docs/zh/customization/skills.md @@ -63,7 +63,7 @@ arguments: ## Skill 存放位置 -Kimi Code CLI 按作用域分四档扫描,越具体的作用域优先级越高:**Project > User > Extra > Built-in** +Kimi Code CLI 按作用域分五档扫描,越具体的作用域优先级越高:**Project > User > Extra > System > Built-in** **用户级**(对所有项目生效): - `$KIMI_CODE_HOME/skills/`(默认:`~/.kimi-code/skills/`) @@ -81,6 +81,8 @@ Kimi 专属用户级 Skill 目录会随 `KIMI_CODE_HOME` 移动,因此隔离 extra_skill_dirs = ["~/team-skills", ".agents/team-skills"] ``` +**System Skills** 随 CLI 一起分发,并在运行时注册。它们提供无需用户安装即可使用的产品感知工作流,但优先级仍低于项目级、用户级和额外 Skill 目录。 + **内置 Skills** 随 CLI 一起分发,优先级最低。它们为常见任务提供开箱即用的工作流,例如配置 MCP server、定制 TUI 主题和编辑配置文件。完整列表详见[内置 Skill 命令](../reference/slash-commands.md#内置-skill-命令)。 ## 调用 Skill diff --git a/docs/zh/reference/slash-commands.md b/docs/zh/reference/slash-commands.md index 218010835..8c8d67c51 100644 --- a/docs/zh/reference/slash-commands.md +++ b/docs/zh/reference/slash-commands.md @@ -113,17 +113,18 @@ Prompt 模式在目标完成时以退出码 `0` 退出,在目标阻塞时以 ` ## 内置 Skill 命令 -Kimi Code CLI 随包内置了一组 Skill,直接以 `/` 形式出现在斜杠命令面板中。与外部 Skill 不同,它们不需要 `skill:` 前缀,开箱即用。 +Kimi Code CLI 随包提供了一组 System 和内置 Skill,直接以 `/` 形式出现在斜杠命令面板中。与外部 Skill 不同,它们不需要 `skill:` 前缀,开箱即用。 | 命令 | 说明 | | --- | --- | +| `/kimi-code-docs` | 加载 Kimi Code 产品文档指引,用于安装、配置、MCP、skills、plugins、命令和故障排查 | | `/mcp-config` | 配置 MCP server 并处理 MCP OAuth 登录。详见 [MCP](../customization/mcp.md) | | `/custom-theme []` | 创建或编辑自定义 TUI 配色主题。详见 [主题](../customization/themes.md) | | `/update-config` | 查看或编辑 `config.toml`(模型、供应商、权限、hooks)和 `tui.toml`(主题、编辑器、通知、自动更新) | | `/import-from-cc-codex` | 从 Claude Code 和 Codex 导入 instructions、skills 和 MCP 设置 | | `/sub-skill` | 发现并将本地 skill 库存重组为分层子 skill 包。包含 `/sub-skill.review`(只读提案)和 `/sub-skill.consolidate`(执行重组) | -所有内置 Skill 命令仅在空闲状态下可用。 +所有 System 和内置 Skill 命令仅在空闲状态下可用。 ## Skill 动态命令 @@ -145,7 +146,7 @@ Kimi Code CLI 随包内置了一组 Skill,直接以 `/` 形式出现在 为方便输入,外部 Skill 命令同时支持省略 `skill:` 前缀的简写形式 `/`,前提是该名称未被系统斜杠命令占用——即 `/code-style` 会回退匹配到 `/skill:code-style`。 -Kimi Code CLI 随包内置的 Skill 会直接以 `/` 形式出现在斜杠命令面板中。例如,`/mcp-config` 用于配置 MCP server 和处理 MCP OAuth 登录,`/custom-theme [附加文本]` 用于进入自定义主题流程,创建或编辑 TUI 主题。 +Kimi Code CLI 随包提供的 System 和内置 Skill 会直接以 `/` 形式出现在斜杠命令面板中。例如,`/kimi-code-docs` 用于加载 Kimi Code 产品文档指引,`/mcp-config` 用于配置 MCP server 和处理 MCP OAuth 登录,`/custom-theme [附加文本]` 用于进入自定义主题流程,创建或编辑 TUI 主题。 ::: info 说明 所有 Skill 命令仅在空闲状态下可用。`flow` 类型的 Skill 同样通过 `/skill:` 暴露,没有独立的 `/flow:` 命名空间。 diff --git a/packages/agent-core/src/profile/default/system.md b/packages/agent-core/src/profile/default/system.md index d1102d395..21228906b 100644 --- a/packages/agent-core/src/profile/default/system.md +++ b/packages/agent-core/src/profile/default/system.md @@ -121,7 +121,7 @@ Identify the skills relevant to your current task and read the skill file for it ## Available skills -Skills are grouped by scope (`Project`, `User`, `Extra`, `Built-in`) so you can tell where each came from. When the user refers to "the skill in this project" or "the user-scope skill", use the scope heading to disambiguate. When multiple scopes define a skill with the same name, the more specific scope takes precedence: **Project overrides User overrides Extra overrides Built-in**. +Skills are grouped by scope (`Project`, `User`, `Extra`, `System`, `Built-in`) so you can tell where each came from. When the user refers to "the skill in this project", "the user-scope skill", or "the system skill", use the scope heading to disambiguate. When multiple scopes define a skill with the same name, the more specific scope takes precedence: **Project overrides User overrides Extra overrides System overrides Built-in**. {{ KIMI_SKILLS }} {% endif %} diff --git a/packages/agent-core/src/rpc/core-api.ts b/packages/agent-core/src/rpc/core-api.ts index ce9f2dd12..c0b8cacc7 100644 --- a/packages/agent-core/src/rpc/core-api.ts +++ b/packages/agent-core/src/rpc/core-api.ts @@ -260,7 +260,7 @@ export interface SkillSummary { readonly name: string; readonly description: string; readonly path: string; - readonly source: 'builtin' | 'user' | 'extra' | 'project'; + readonly source: 'builtin' | 'system' | 'user' | 'extra' | 'project'; readonly type?: string | undefined; readonly disableModelInvocation?: boolean | undefined; readonly isSubSkill?: boolean | undefined; diff --git a/packages/agent-core/src/session/index.ts b/packages/agent-core/src/session/index.ts index a2bb022b5..0317f9c94 100644 --- a/packages/agent-core/src/session/index.ts +++ b/packages/agent-core/src/session/index.ts @@ -41,6 +41,7 @@ import { import type { ProviderManager } from './provider-manager'; import { registerBuiltinSkills, + registerSystemSkills, SessionSkillRegistry, resolveSkillRoots, summarizeSkill, @@ -635,10 +636,14 @@ export class Session { } private async loadSkills(): Promise { + const userHomeDir = this.options.skills?.userHomeDir ?? homedir(); + const brandHomeDir = + this.options.skills?.brandHomeDir ?? this.options.kimiHomeDir ?? join(userHomeDir, '.kimi-code'); + const roots = await resolveSkillRoots({ paths: { - userHomeDir: this.options.skills?.userHomeDir ?? homedir(), - brandHomeDir: this.options.skills?.brandHomeDir ?? this.options.kimiHomeDir, + userHomeDir, + brandHomeDir, workDir: this.options.kaos.getcwd(), }, explicitDirs: this.options.skills?.explicitDirs, @@ -648,6 +653,7 @@ export class Session { builtinDir: this.options.skills?.builtinDir, }); await this.skills.loadRoots(roots); + registerSystemSkills(this.skills); registerBuiltinSkills(this.skills); } diff --git a/packages/agent-core/src/skill/index.ts b/packages/agent-core/src/skill/index.ts index 924027bfa..187f183c7 100644 --- a/packages/agent-core/src/skill/index.ts +++ b/packages/agent-core/src/skill/index.ts @@ -2,4 +2,5 @@ export * from './builtin'; export * from './parser'; export * from './registry'; export * from './scanner'; +export * from './system'; export * from './types'; diff --git a/packages/agent-core/src/skill/registry.ts b/packages/agent-core/src/skill/registry.ts index 65b207e27..65015789e 100644 --- a/packages/agent-core/src/skill/registry.ts +++ b/packages/agent-core/src/skill/registry.ts @@ -151,6 +151,7 @@ const SOURCE_GROUPS: ReadonlyArray<{ readonly source: SkillSource; readonly labe { source: 'project', label: 'Project' }, { source: 'user', label: 'User' }, { source: 'extra', label: 'Extra' }, + { source: 'system', label: 'System' }, { source: 'builtin', label: 'Built-in' }, ]; diff --git a/packages/agent-core/src/skill/system.ts b/packages/agent-core/src/skill/system.ts new file mode 100644 index 000000000..7ac9943fa --- /dev/null +++ b/packages/agent-core/src/skill/system.ts @@ -0,0 +1,23 @@ +import KIMI_CODE_DOCS_BODY from './system/kimi-code-docs/SKILL.md?raw'; +import { parseSkillText } from './parser'; +import type { SessionSkillRegistry } from './registry'; +import type { SkillDefinition } from './types'; + +const KIMI_CODE_DOCS_PSEUDO_PATH = 'system://kimi-code-docs'; + +const parsedKimiCodeDocsSkill = parseSkillText({ + skillMdPath: '/system/skills/kimi-code-docs/SKILL.md', + skillDirName: 'kimi-code-docs', + source: 'system', + text: KIMI_CODE_DOCS_BODY, +}); + +export const KIMI_CODE_DOCS_SKILL: SkillDefinition = { + ...parsedKimiCodeDocsSkill, + path: KIMI_CODE_DOCS_PSEUDO_PATH, + dir: KIMI_CODE_DOCS_PSEUDO_PATH, +}; + +export function registerSystemSkills(registry: SessionSkillRegistry): void { + registry.register(KIMI_CODE_DOCS_SKILL); +} diff --git a/packages/agent-core/src/skill/system/kimi-code-docs/SKILL.md b/packages/agent-core/src/skill/system/kimi-code-docs/SKILL.md new file mode 100644 index 000000000..d501abed9 --- /dev/null +++ b/packages/agent-core/src/skill/system/kimi-code-docs/SKILL.md @@ -0,0 +1,45 @@ +--- +name: kimi-code-docs +description: Use when the user asks about Kimi Code CLI itself, including setup, configuration, MCP, skills, plugins, slash commands, agents, hooks, sessions, IDE/ACP integration, login, updates, troubleshooting, or choosing the right Kimi Code surface. +whenToUse: The request is about Kimi Code as a product/runtime rather than about the user's project code. This includes how Kimi Code works, how to configure it, how to extend it, and how its documented commands or files should be used. +--- + +# Kimi Code Docs + +Use this skill for Kimi Code product self-knowledge. The goal is to answer from Kimi Code's own docs or source, not from memory. + +## Source route + +1. For current user-facing product behavior, use the official Kimi Code docs first: + - `https://moonshotai.github.io/kimi-code/en/` + - `https://moonshotai.github.io/kimi-code/zh/` +2. If online docs are unavailable and the local repository has `docs/`, read the matching local docs page instead. +3. If docs and source disagree, say so and prefer source for current local behavior. +4. For implementation questions, inspect the owning source after locating the relevant product surface in the docs. +5. Do not invent pricing, rollout status, account entitlement, or undocumented model names. + +## Product map + +- Setup and first run: `guides/getting-started` +- Interactive usage: `guides/interaction`, `reference/slash-commands`, `reference/keyboard` +- Sessions and goals: `guides/sessions`, `guides/goals` +- Configuration: `configuration/config-files`, `configuration/providers`, `configuration/env-vars`, `configuration/data-locations` +- MCP: `customization/mcp` +- Skills: `customization/skills`, `reference/slash-commands#built-in-skill-commands` +- Plugins: `customization/plugins` +- Agents and hooks: `customization/agents`, `customization/hooks` +- IDE and ACP: `guides/ides`, `reference/kimi-acp` +- CLI flags and subcommands: `reference/kimi-command` + +## Related built-in workflows + +- Use `/update-config` for editing `config.toml` or `tui.toml`. +- Use `/mcp-config` for MCP server configuration and MCP OAuth login. +- Use `/custom-theme` for custom TUI theme files. +- Use `/import-from-cc-codex` for importing selected Claude Code or Codex assets. + +## Boundaries + +- Do not treat generic Moonshot or Open Platform API questions as Kimi Code CLI questions unless the user is configuring Kimi Code providers. +- For Kimi Code CLI or Kimi Code for VS Code, keep Kimi Code platform endpoints distinct from Open Platform endpoints. +- If a user asks how to change project behavior, prefer project instructions or project skills over this product-docs skill. diff --git a/packages/agent-core/src/skill/types.ts b/packages/agent-core/src/skill/types.ts index 7e030adf9..aef760108 100644 --- a/packages/agent-core/src/skill/types.ts +++ b/packages/agent-core/src/skill/types.ts @@ -1,4 +1,4 @@ -export type SkillSource = 'project' | 'user' | 'extra' | 'builtin'; +export type SkillSource = 'project' | 'user' | 'extra' | 'system' | 'builtin'; export interface SkillMetadata { readonly name?: string | undefined; diff --git a/packages/agent-core/test/harness/skill-session.test.ts b/packages/agent-core/test/harness/skill-session.test.ts index 223fe64dc..273b74845 100644 --- a/packages/agent-core/test/harness/skill-session.test.ts +++ b/packages/agent-core/test/harness/skill-session.test.ts @@ -94,9 +94,16 @@ describe('HarnessAPI session skills', () => { const created = await rpc.createSession({ id: 'ses_builtin_skill_list', workDir }); const skills = await rpc.listSkills({ sessionId: created.id }); + const selfDocs = skills.find((skill) => skill.name === 'kimi-code-docs'); const mcpConfig = skills.find((skill) => skill.name === 'mcp-config'); const importer = skills.find((skill) => skill.name === 'import-from-cc-codex'); + expect(selfDocs).toMatchObject({ + name: 'kimi-code-docs', + description: expect.stringContaining('Kimi Code CLI itself'), + source: 'system', + }); + expect(selfDocs?.path).toBe('system://kimi-code-docs'); expect(mcpConfig).toMatchObject({ name: 'mcp-config', description: 'Configure MCP servers and handle MCP OAuth login.', @@ -110,6 +117,7 @@ describe('HarnessAPI session skills', () => { disableModelInvocation: true, }); expect(importer?.path).toBe('builtin://import-from-cc-codex'); + expect(JSON.stringify(skills)).not.toContain('Use this skill for Kimi Code product self-knowledge'); expect(JSON.stringify(skills)).not.toContain('Your tool list contains one synthetic tool'); expect(JSON.stringify(skills)).not.toContain('Do not migrate Claude custom commands'); }); @@ -587,6 +595,18 @@ describe('HarnessAPI session skills', () => { expect(promptInput?.[0]?.text).toContain('AskUserQuestion'); }); + it('loads the bundled system docs skill into the model skill listing', async () => { + const { core, rpc } = await createTestRpc(); + const created = await rpc.createSession({ id: 'ses_skill_system_docs', workDir }); + + const session = core.sessions.get(created.id); + expect(session).toBeDefined(); + const invocable = session!.skills.listInvocableSkills(); + expect(invocable.some((skill) => skill.name === 'kimi-code-docs')).toBe(true); + expect(session!.skills.getModelSkillListing()).toContain('kimi-code-docs'); + expect(session!.skills.getModelSkillListing()).toContain('### System'); + }); + it('lets a user-supplied skill override the builtin of the same name', async () => { await writeSkill('mcp-config', [ '---', diff --git a/packages/agent-core/test/skill/registry.test.ts b/packages/agent-core/test/skill/registry.test.ts index 7e9e26056..15969c080 100644 --- a/packages/agent-core/test/skill/registry.test.ts +++ b/packages/agent-core/test/skill/registry.test.ts @@ -1,12 +1,13 @@ import { describe, expect, it } from 'vitest'; -import { SessionSkillRegistry } from '../../src/skill'; +import { registerSystemSkills, SessionSkillRegistry } from '../../src/skill'; import type { SkillDefinition, SkillSource } from '../../src/skill'; describe('skill registry prompt rendering', () => { it('groups skills by scope under canonical section headings', () => { const registry = makeRegistry([ makeSkill('builtin-a', 'builtin'), + makeSkill('system-a', 'system'), makeSkill('user-a', 'user'), makeSkill('proj-a', 'project'), makeSkill('extra-a', 'extra'), @@ -17,19 +18,23 @@ describe('skill registry prompt rendering', () => { expect(rendered).toContain('### Project'); expect(rendered).toContain('### User'); expect(rendered).toContain('### Extra'); + expect(rendered).toContain('### System'); expect(rendered).toContain('### Built-in'); const projectIdx = rendered.indexOf('### Project'); const userIdx = rendered.indexOf('### User'); const extraIdx = rendered.indexOf('### Extra'); + const systemIdx = rendered.indexOf('### System'); const builtinIdx = rendered.indexOf('### Built-in'); expect(projectIdx).toBeLessThan(userIdx); expect(userIdx).toBeLessThan(extraIdx); - expect(extraIdx).toBeLessThan(builtinIdx); + expect(extraIdx).toBeLessThan(systemIdx); + expect(systemIdx).toBeLessThan(builtinIdx); expect(sectionFor(rendered, '### Project')).toContain('proj-a'); expect(sectionFor(rendered, '### User')).toContain('user-a'); expect(sectionFor(rendered, '### Extra')).toContain('extra-a'); + expect(sectionFor(rendered, '### System')).toContain('system-a'); expect(sectionFor(rendered, '### Built-in')).toContain('builtin-a'); expect(sectionFor(rendered, '### Project')).not.toContain('user-a'); expect(sectionFor(rendered, '### User')).not.toContain('proj-a'); @@ -43,6 +48,7 @@ describe('skill registry prompt rendering', () => { expect(rendered).toContain('### User'); expect(rendered).not.toContain('### Project'); expect(rendered).not.toContain('### Extra'); + expect(rendered).not.toContain('### System'); expect(rendered).not.toContain('### Built-in'); }); @@ -131,6 +137,46 @@ describe('getModelSkillListing description truncation', () => { }); }); +describe('system skill registration', () => { + it('registers Kimi Code docs as an in-memory system skill', () => { + const registry = new SessionSkillRegistry(); + + registerSystemSkills(registry); + + const skill = registry.getSkill('kimi-code-docs'); + expect(skill).toMatchObject({ + name: 'kimi-code-docs', + source: 'system', + path: 'system://kimi-code-docs', + dir: 'system://kimi-code-docs', + }); + expect(skill?.description).toContain('Kimi Code CLI itself'); + }); + + it('lets external skills shadow a bundled system skill', () => { + const registry = makeRegistry([ + makeSkill('kimi-code-docs', 'extra', 'team docs override', '/tmp/extra/kimi-code-docs/SKILL.md'), + ]); + + registerSystemSkills(registry); + + const skill = registry.getSkill('kimi-code-docs'); + expect(skill?.source).toBe('extra'); + expect(skill?.description).toBe('team docs override'); + }); + + it('lets a bundled system skill shadow a same-named builtin skill', () => { + const registry = new SessionSkillRegistry(); + + registerSystemSkills(registry); + registry.registerBuiltinSkill(makeSkill('kimi-code-docs', 'builtin', 'builtin fallback')); + + const skill = registry.getSkill('kimi-code-docs'); + expect(skill?.source).toBe('system'); + expect(skill?.description).toContain('Kimi Code CLI itself'); + }); +}); + function makeRegistry(skills: readonly SkillDefinition[]): SessionSkillRegistry { const registry = new SessionSkillRegistry(); for (const skill of skills) registry.register(skill); diff --git a/packages/protocol/src/events.ts b/packages/protocol/src/events.ts index d0e22e0d6..a41885822 100644 --- a/packages/protocol/src/events.ts +++ b/packages/protocol/src/events.ts @@ -30,7 +30,7 @@ export interface UsageStatus { export type PermissionMode = 'manual' | 'yolo' | 'auto'; -export type SkillSource = 'project' | 'user' | 'extra' | 'builtin'; +export type SkillSource = 'project' | 'user' | 'extra' | 'system' | 'builtin'; export interface UserPromptOrigin { readonly kind: 'user'; @@ -687,7 +687,7 @@ export const usageStatusSchema = z.object({ export const permissionModeSchema = z.enum(['manual', 'yolo', 'auto']) satisfies z.ZodType; -export const skillSourceSchema = z.enum(['project', 'user', 'extra', 'builtin']) satisfies z.ZodType; +export const skillSourceSchema = z.enum(['project', 'user', 'extra', 'system', 'builtin']) satisfies z.ZodType; export const userPromptOriginSchema = z.object({ kind: z.literal('user'),