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

> Learn to write custom skills for the Kimi-Code CLI. Enhance your workflow with reusable Markdown files and slash commands. Get started with our complete guide.

- Repository: [Moonshot AI/kimi-code](https://github.com/MoonshotAI/kimi-code)
- Tags: how-to-guide
- Published: 2026-07-25

---

**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)](https://github.com/MoonshotAI/kimi-code/blob/main/packages/agent-core/src/skill/types.ts)**, each skill includes:

```typescript
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)](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`](https://github.com/MoonshotAI/kimi-code/blob/main/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:

```typescript
// 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`](https://github.com/MoonshotAI/kimi-code/blob/main/write-goal.md), [`update-config.md`](https://github.com/MoonshotAI/kimi-code/blob/main/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:

```markdown
---
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)](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/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:

```typescript
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)](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/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

```markdown
---
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

```markdown
---
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`](https://github.com/MoonshotAI/kimi-code/blob/main/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`](https://github.com/MoonshotAI/kimi-code/blob/main/registry.ts).

## Frequently Asked Questions

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

Use [`.skill.md`](https://github.com/MoonshotAI/kimi-code/blob/main/.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)](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)](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/`](https://github.com/MoonshotAI/kimi-code/tree/main/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.