Kodelet / Documentation

Customization

Teach Kodelet your project conventions, reuse prompts and skills, and add capabilities with plugins and SDK extensions.

On this page

Start with instructions before adding code. Use a recipe for a task you repeat, a skill for expertise the model should recognize, and an extension when you need executable behavior.

Workspace resources belong on the runner. Installing something on a remote client does not make it available to the machine doing the work.

Project context with AGENTS.md

Kodelet automatically loads AGENTS.md project context. Include the information a new engineer needs: repository structure, build/test commands, coding conventions, and operational constraints. Prefer concrete instructions over long descriptions of obvious code.

Generate a starting point from the repository, then review it:

kodelet run -r init

Keep the file current when commands or architecture change. A useful instruction is “Run go test ./pkg/parser after parser changes,” not simply “make sure it works.” Context is guidance for the model, not a permission boundary; enforce restrictions in trusted configuration.

Repeatable tasks with recipes

Recipes are user-invoked prompt templates. Store a project recipe in recipes/review.md on the runner:

---
name: Focused review
description: Review the current changes for a chosen concern.
arguments:
  focus:
    description: The main concern to investigate.
    default: correctness
---

Review the current Git diff, focusing on {{.focus}}.
Explain concrete issues with file references and suggest focused tests.
Do not modify files.

Inspect and run it from that workspace:

kodelet recipe list
kodelet recipe show review --arg focus=security
kodelet run -r review --arg focus=security

User-global recipes live in ~/.kodelet/recipes/ on the runner host. Add --runner and --cwd to inspect another workspace; discovery and rendering use the runner’s files, not the client’s.

Recipes support template arguments and Bash substitutions. Inspection is not always inert: recipe list starts extensions to discover dynamic recipes, and recipe show can execute a template’s Bash substitutions. Review unfamiliar recipes and plugins before using them.

Model-invoked expertise with skills

A skill packages instructions and optional supporting files. Unlike a recipe, it is normally invoked by the model when its description matches the task.

Create .kodelet/skills/repository-review/SKILL.md on the runner:

---
name: repository-review
description: Use when reviewing changes to this repository's request handlers.
---

Check input validation, error responses, and authorization before discussing style.
Find the neighboring handler tests and use their conventions.
Read references/checklist.md when a change affects authentication.

If you reference supporting material, add that file alongside the skill. Keep the entrypoint concise; supporting files are read only when needed. Use ~/.kodelet/skills/ for user-global skills. Skills bundled by plugins are also discoverable.

Disable skill loading for a task with kodelet run --no-skills "your task", or set skills.enabled: false in the appropriate trusted configuration.

Share capabilities with plugins

Plugins bundle skills, recipes, and extensions from GitHub repositories. For example, the Kodelet repository includes its own usage skill:

kodelet plugin add jingkaihe/kodelet
kodelet plugin list
kodelet plugin show jingkaihe/kodelet

Run installation commands on the runner host, in the target project for a local installation. Plugins are stored in .kodelet/plugins/; add -g to install into ~/.kodelet/plugins/ instead. Use owner/repo@tag or owner/repo@commit to select a specific revision rather than following a moving branch.

Plugin management operates on the current machine’s files. It does not remotely install a plugin merely because your client selects another server. Review plugin source and any setup steps before allowing it to execute.

Add tools and lifecycle behavior with extensions

Extensions are long-running subprocesses that register model tools, prompt commands, dynamic recipes, and lifecycle handlers. They can also provide interactive UI features where the client supports them.

The old executable custom-tool and lifecycle-hook systems are removed. Do not install new integrations under .kodelet/tools/ or .kodelet/hooks/. Use an executable named kodelet-extension-* in .kodelet/extensions/ or ~/.kodelet/extensions/, either directly under the root or one directory below it. Plugins can bundle the same layout.

A small TypeScript extension

The kodelet TypeScript SDK exports defineExtension and Zod as z; kodelet/runtime provides the stdio runtime. This example adds a tool that counts whitespace-separated words.

On the runner, prepare a directory with Node.js 20 or newer and npm available:

mkdir -p .kodelet/extensions/word-count
npm install --prefix .kodelet/extensions/word-count kodelet tsx

Use compatible CLI and SDK releases. Save this as .kodelet/extensions/word-count/index.mts (the .mts extension selects an ES module):

import { defineExtension, z } from "kodelet";
import { runExtension } from "kodelet/runtime";

const extension = defineExtension((ext) => {
  ext.setMetadata({ name: "word-count", version: "0.1.0" });

  ext.registerTool({
    name: "count_words",
    description: "Count whitespace-separated words in supplied text.",
    inputSchema: z.object({ text: z.string() }),
    async execute({ text }) {
      const trimmed = text.trim();
      const count = trimmed === "" ? 0 : trimmed.split(/\s+/u).length;
      return { content: `${count} words`, data: { count } };
    },
  });
});

await runExtension(extension);

Save an executable wrapper as .kodelet/extensions/word-count/kodelet-extension-word-count:

#!/usr/bin/env bash
exec "$(dirname "$0")/node_modules/.bin/tsx" "$(dirname "$0")/index.mts"

Then make it executable and inspect discovery:

chmod +x .kodelet/extensions/word-count/kodelet-extension-word-count
kodelet extension list
kodelet extension inspect word-count
kodelet run "Use count_words to count the words in: small tools, useful workflows."

The example requires extension loading and its tool to be permitted by both daemon and runner policy. extension list and extension inspect read metadata without starting extension processes; the subsequent agent run loads the extension.

Lifecycle events and protocol

Use ext.on("tool.call", handler) to inspect or block a tool call and ext.on("tool.result", handler) to process its result. These replace before_tool_call and after_tool_call. Other events cover user messages, sessions, and agent turns.

If you sanitize final tool results, apply the same policy to tool.update so streamed output cannot bypass it. Extensions communicate over Content-Length-framed JSON-RPC: reserve standard output for the protocol and send diagnostics to standard error or the SDK logger.

Disable extensions for one run with --no-extensions. For persistent controls, use extensions.enabled, allow, deny, and per-tool settings in the appropriate configuration. Deny rules take precedence over matching allow rules.

Use the SDK in an application

The same TypeScript package provides Client for creating, resuming, and streaming agent sessions through ACP. See the runnable integration pattern for session setup and cleanup.

You can also pass inline extensions to client.createSession({ extensions: [extension] }). Those callbacks run in your application’s process, not on the runner. They do not gain access to a remote filesystem just because the session has a runner-side working directory. Install an executable extension on the runner when its implementation needs runner-local resources.

Inline callbacks and captured state are not serialized into conversation history. When resuming, provide the extensions again in the same order. Host capabilities vary: an extension’s terminal widgets or interactive surfaces are not guaranteed in every client.

For larger integrations, continue with the SDK guide, SDK API examples, and extension protocol reference.