How to Create Custom Skills for Kimi Code CLI: A Complete Guide
Custom skills in Kimi Code CLI are reusable prompt templates defined in Markdown files with YAML front-matter, discovered automatically from configured directories, and invoked via slash commands or the Node SDK.
Kimi Code CLI treats skills as modular units of prompting logic that can be triggered manually by developers or automatically by the language model. Each skill consists of a Markdown file containing metadata headers and a template body that defines the actual instructions sent to the LLM. Learning how to create custom skills allows you to extend the CLI with project-specific workflows, coding standards, and automation routines tailored to your repository.
Understanding the Skill Structure
According to the Kimi Code source code, a skill is formally defined by the SkillDefinition interface in packages/agent-core/src/skill/types.ts. This structure encapsulates everything the engine needs to identify, register, and execute a skill.
export interface SkillDefinition {
readonly name: string; // "write-goal", "lint-code", ...
readonly description: string; // Human-readable summary
readonly path: string; // Full path on disk
readonly dir: string; // Directory containing the file
readonly content: string; // Prompt template (the Markdown body)
readonly metadata: SkillMetadata; // Optional settings (type, whenToUse, ...)
readonly source: SkillSource; // project | user | extra | builtin
readonly plugin?: SkillPluginContext; // If supplied by a plugin
}
The name field becomes the slash command identifier (e.g., /my-custom), while the content field contains the prompt template that handles parameter substitution when invoked.
Skill Discovery and Registry
The SessionSkillRegistry class in packages/agent-core/src/skill/registry.ts orchestrates skill loading. When a session initializes, the loadRoots() method receives a list of skill roots (directories) and calls the scanner to discover files.
// From registry.ts – loading roots and indexing
await this.discoverImpl({
roots,
onWarning: this.onWarning,
onSkippedByPolicy: (skill) => this.skipped.push(skill),
onDiscoveredSkill: (skill) => {
this.indexPluginSkill(skill);
},
});
for (const skill of skills) {
this.byName.set(normalizeSkillName(skill.name), skill);
}
The scanner (in packages/agent-core/src/skill/scanner.ts) walks these directories, parsing any *.skill.md or *.md files containing valid front-matter, and builds a SkillDefinition for each. The registry indexes these by normalized name and, if applicable, by plugin ID.
Where to Place Your Custom Skills
Kimi Code supports four distinct skill sources, each with specific directory conventions:
| Source | Directory | Explanation |
|---|---|---|
| Project | <repo-root>/skills or paths in extra_skill_dirs config |
Visible only to the current project. |
| User | ~/.kimi-code/skills or $KIMI_CODE_USER_SKILLS |
Shared across all projects for the current user. |
| Extra | Directories passed via extra_skill_dirs config |
Allows administrators to ship additional skill bundles. |
| Built-in | packages/agent-core/src/skill/builtin |
Provided by the engine itself; use these as templates. |
Place your custom files in one of the first three locations depending on whether the skill should be project-specific or shared across your development environment.
Writing a Custom Skill Markdown File
A skill file is a Markdown document with a YAML front-matter block followed by the prompt template body. The front-matter must include at least name and description fields.
---
name: my-custom
description: Run a quick lint check on the changed files.
type: prompt # "prompt" or "inline" → model can invoke automatically
disableModelInvocation: false # `true` makes it user-only
whenToUse: "When the repo has new TypeScript files"
---
# Lint changed TypeScript files
Run the following command in the project root and capture its output:
```bash
npx eslint {{changedFiles}}
If the command exits with a non-zero code, report the errors back to the user.
**Key front-matter fields:**
- **name**: Identifier used in slash commands (`/my-custom`) and API calls.
- **description**: Shown in the skill picker UI.
- **type**: Set to `prompt` (default) or `inline`; only *inline* skills can be auto-invoked by the model.
- **disableModelInvocation**: When `true`, restricts the skill to manual invocation only.
- **whenToUse**: Human-readable hint displayed when listing available skills.
The body supports parameter substitution using `{{variable}}` syntax, processed by the parser in **[`packages/agent-core/src/skill/parser.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/parser.ts)**.
## Activating Custom Skills
### Via Slash Commands
In the TUI, type the slash command followed by optional arguments:
/my-custom src/**/*.ts
The `skill_activation` event (defined in **[`packages/protocol/src/events.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/protocol/src/events.ts)**) triggers the registry to resolve the name, expand parameters, and inject the prompt into the LLM turn.
### Via the Node SDK
You can programmatically activate skills using the `@moonshot-ai/kimi-code-sdk` package:
```typescript
import { KimiHarness } from '@moonshot-ai/kimi-code-sdk';
async function runSkill() {
// 1. Create a session
const harness = await KimiHarness.create({ /* config */ });
const session = await harness.session('my-session-id');
// 2. List available skills
const { skills } = await session.get('/v1/sessions/my-session-id/skills');
console.log('Available skills:', skills.map(s => s.name));
// 3. Activate the skill with arguments
await session.post(
`/v1/sessions/my-session-id/skills/${encodeURIComponent('my-custom')}:activate`,
{ args: 'src/**/*.ts' }
);
}
runSkill();
This POST request targets /v1/sessions/{id}/skills/{skill_name}:activate, which the server translates into a skill.activated event handled by skillActivatedEventSchema in packages/protocol/src/events.ts.
From Within a Plugin
If your skill belongs to a plugin, include an instructions field in the plugin manifest. When the skill is rendered (via SessionSkillRegistry.renderSkillPrompt in registry.ts), those instructions are wrapped in a <kimi-plugin-instructions> XML block (lines 100-106), providing context to the model.
Complete Workflow Example
Follow this end-to-end process to create and test a custom skill:
- Create the file at
~/.kimi-code/skills/quick-todo.skill.md:
---
name: quick-todo
description: Add a TODO comment to a file.
type: prompt
disableModelInvocation: true
---
Add the following comment to the top of {{file}}:
```ts
// TODO: {{comment}}
2. **Restart or refresh** your Kimi Code session to trigger `SessionSkillRegistry.loadRoots()` and discover the new skill.
3. **Verify registration** by listing skills via the SDK or checking the TUI skill picker.
4. **Invoke the skill** in the TUI with `/quick-todo src/app.ts|Refactor this function` (the pipe character separates arguments based on your template logic).
5. **Iterate** by modifying the Markdown content, adjusting front-matter metadata, and repeating steps 2-4.
## Summary
- **Skills are Markdown files** with YAML front-matter containing `name`, `description`, and optional behavior controls like `type` and `disableModelInvocation`.
- **Store skills** in `~/.kimi-code/skills` for user-wide access or `<repo>/skills` for project-specific workflows.
- **Discovery happens automatically** via `SessionSkillRegistry.loadRoots()` and the scanner in **[`packages/agent-core/src/skill/scanner.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/scanner.ts)**.
- **Activation works three ways**: slash commands in the TUI, REST API calls to the activation endpoint, or automatic model invocation (for `type: inline` skills).
- **Parameter expansion** uses `{{variable}}` syntax handled by the parser in **[`packages/agent-core/src/skill/parser.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/parser.ts)**.
## Frequently Asked Questions
### What file extension should I use for custom skills?
Use [`.skill.md`](https://github.com/MoonshotAI/kimi-code/blob/main/.skill.md) for explicit identification, or standard `.md` files containing valid YAML front-matter with `name` and `description` fields. The scanner in **[`packages/agent-core/src/skill/scanner.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/scanner.ts)** discovers both patterns during the `loadRoots()` initialization.
### How do I prevent the model from automatically invoking my skill?
Set `disableModelInvocation: true` in the front-matter block. This restricts the skill to manual activation only, preventing the LLM from calling it automatically during conversation turns.
### Can skills accept dynamic parameters when activated programmatically?
Yes. Pass an `args` string in the POST request body when calling the activation endpoint (`/v1/sessions/{id}/skills/{name}:activate`). The engine uses `expandSkillParameters` from **[`packages/agent-core/src/skill/parser.ts`](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/parser.ts)** to substitute `{{placeholders}}` in your template with the provided arguments.
### Where can I find reference examples of built-in skills?
Examine the files in **`packages/agent-core/src/skill/builtin/`**, such as [`write-goal.md`](https://github.com/MoonshotAI/kimi-code/blob/main/write-goal.md) or [`update-config.md`](https://github.com/MoonshotAI/kimi-code/blob/main/update-config.md). These are production-grade templates that demonstrate proper front-matter structure and prompt engineering patterns used by the Kimi Code engine itself.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →