How to Write Custom Skills for the Kimi-Code CLI: A Complete Guide

Custom skills in Kimi-Code are reusable Markdown files with YAML front-matter that you place in ~/.kimi-code/skills/ for user-wide access or ./skills/ for project-specific use, then invoke via slash commands like /my-skill or the Node SDK.

The MoonshotAI/kimi-code CLI allows you to extend its capabilities by writing custom skills—reusable prompt templates that automate repetitive coding tasks. This guide explains how to create, store, and activate these skills based on the actual source implementation in the packages/agent-core/src/skill directory.

What Is a Kimi-Code Skill?

A skill is a reusable piece of prompting logic defined by a Markdown file containing a front-matter block with at least a name and description. The file’s body holds the prompt template sent to the LLM. According to the SkillDefinition interface in [packages/agent-core/src/skill/types.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/types.ts), each skill includes:

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
}

Skills can be invoked manually by the user via slash commands or automatically by the model when configured as inline type.

How the Engine Discovers Skills

When a session starts, SessionSkillRegistry.loadRoots() in [packages/agent-core/src/skill/registry.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/registry.ts#L41-L58) receives a list of skill roots (directories). It calls the skill scanner (discoverSkills in scanner.ts) which walks those directories, parses any *.skill.md (or *.md with front-matter), and builds a SkillDefinition for each file.

The registry then indexes the skill by its normalized name (normalizeSkillName) and, if the file belongs to a plugin, also by the plugin-id/key:

// 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);
}

Where to Store Custom Skills

You can place skill files in four locations depending on scope:

  • Project: <repo-root>/skills (or any folder added to extra_skill_dirs in config). Use this for team-specific automation visible only to the current project.
  • User: ~/.kimi-code/skills (or the path set in KIMI_CODE_USER_SKILLS environment variable). These persist across all projects for your user account.
  • Extra: Any directory passed via the extra_skill_dirs configuration option. Useful for administrators shipping shared skill bundles.
  • Built-in: packages/agent-core/src/skill/builtin contains the engine’s default skills (e.g., write-goal.md, update-config.md). Copy these as templates.

Anatomy of a Skill File (Markdown Format)

A skill file requires YAML front-matter followed by the prompt template body:

---
name: my-custom
description: Run a quick lint check on the changed files.
type: prompt          # "prompt" or "inline" → model can invoke it 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`**: `prompt` (default) or `inline`; only *inline* skills can be auto-invoked by the model.
- **`disableModelInvocation`**: Set to `true` to restrict the skill to user-only activation.
- **`whenToUse`**: Human-readable hint displayed when the UI lists available skills.

## Activating Custom Skills

### Via CLI Slash Commands

Type the slash command followed by optional arguments:

```text
/my-custom src/**/*.ts

The slash command triggers a skill activation event defined in [packages/protocol/src/events.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/protocol/src/events.ts). The registry resolves the name, expands parameter placeholders using expandSkillParameters in [parser.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/parser.ts), and injects the resulting prompt into the LLM turn.

Via the Node.js SDK

Activate skills programmatically using the Kimi Harness:

import { KimiHarness } from '@moonshot-ai/kimi-code-sdk';

// 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 a skill with arguments
await session.post(
  `/v1/sessions/my-session-id/skills/${encodeURIComponent('my-custom')}:activate`,
  { args: 'src/**/*.ts' }   // optional arguments string
);

The SDK forwards the request to /v1/sessions/{id}/skills/{skill_name}:activate, which the server translates into a skill.activated event (see skillActivatedEventSchema in [packages/protocol/src/events.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/protocol/src/events.ts#L1459-L1465)).

From Within a Plugin

If your skill belongs to a plugin, add an instructions field in the plugin manifest. When the skill is rendered, SessionSkillRegistry.renderSkillPrompt (lines 100-106 of [registry.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/registry.ts#L100-L106)) wraps those instructions in a <kimi-plugin-instructions> XML block, allowing the model to read plugin-specific guidance.

Complete Working Examples

Example 1: Minimal User-Only Skill

---
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}}

### Example 2: SDK Integration

```typescript
import { KimiHarness } from '@moonshot-ai/kimi-code-sdk';

async function demo() {
  const harness = await KimiHarness.create();
  const sess = await harness.session('demo-session');

  // List all skills the session knows about
  const { skills } = await sess.get('/v1/sessions/demo-session/skills');
  console.log('Skills:', skills.map(s => s.name));

  // Activate with pipe-separated arguments
  await sess.post(
    `/v1/sessions/demo-session/skills/${encodeURIComponent('quick-todo')}:activate`,
    { args: 'src/app.ts|Refactor this function' }
  );
}
demo();

Example 3: Plugin-Specific Instructions

---
name: plugin-greet
description: Greet the user with plugin-provided greeting.
type: prompt
source: project
---

# {{plugin.instructions}}

Say hello in the style defined above.

When rendered, the engine prepends the XML wrapper so the model sees the plugin’s instructions.

Summary

  • Custom skills are Markdown files with YAML front-matter defining reusable LLM prompts.
  • Store them in ~/.kimi-code/skills/ (user scope) or ./skills/ (project scope) for automatic discovery by SessionSkillRegistry.loadRoots().
  • Activate via slash commands (/skill-name) or the REST endpoint /v1/sessions/{id}/skills/{name}:activate.
  • Use {{parameter}} syntax for dynamic values expanded by parser.ts.
  • Set disableModelInvocation: true to restrict skills to manual activation only.
  • Plugin skills can inject additional context via <kimi-plugin-instructions> blocks rendered by registry.ts.

Frequently Asked Questions

What file extension should I use for Kimi-Code skills?

Use .skill.md or any .md file with proper YAML front-matter. The discovery logic in [packages/agent-core/src/skill/scanner.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/scanner.ts) parses both extensions when scanning configured skill roots.

How do I prevent the model from auto-invoking my skill?

Set disableModelInvocation: true in the front-matter. This restricts the skill to user-only activation via slash commands, preventing the model from calling it automatically even if the type is set to inline.

Can I pass arguments to a custom skill?

Yes. Pass them after the slash command (e.g., /my-skill arg1 arg2) or via the args field in the SDK activation call. The expandSkillParameters function in [packages/agent-core/src/skill/parser.ts](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/parser.ts) substitutes {{variables}} in your template with the provided values.

Where are built-in skills located in the source code?

Built-in skills reside in packages/agent-core/src/skill/builtin/. You can copy these Markdown files as templates for your own custom implementations, as they demonstrate best practices for structure and metadata.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →