Your first plugin

Scaffold a Lua plugin, give the agent a tool, and validate it.

Scaffold

kcode plugins scaffold hello

This writes ~/.kcode/plugins/hello/ with four files:

  • plugin.toml: the manifest. Name, version, kind = "Lua", and a [capabilities] table that says which host APIs the plugin may call.
  • main.lua: the entry point. The scaffold registers a tool, subscribes to an event, and stores a counter in durable storage.
  • commands/hello.md: a slash command whose body is a prompt.
  • skills/hello.md: a skill the agent can read.

Add --project to scaffold into <project>/.kcode/plugins/ instead, so the plugin travels with the repository.

The manifest

name = "hello"
version = "0.1.0"
description = "My first plugin"
kind = "Lua"

[capabilities]
kv = true
hooks = true

Capabilities are grants. kv allows the kcode.kv_* storage functions, hooks allows kcode.on_event. Network access is scoped to hosts ("net.http" = ["api.github.com"]) and file reads to globs ("fs.read" = ["**/*.md"]). A call without its grant fails with an error that names the plugin and the exact key to add. Registering tools needs no grant; what a tool does is governed by the session’s permission engine like any other tool call.

A tool

kcode.register_tool("todo_count", "Count TODO lines in the notes", function(args)
  local total = 0
  for _, entry in ipairs(kcode.fs_list("notes")) do
    for line in kcode.fs_read(entry.path):gmatch("[^\n]+") do
      if line:find("TODO", 1, true) then total = total + 1 end
    end
  end
  return { count = total }
end)

The handler gets the arguments the model passed and returns a table. kcode.fs_list takes an optional path prefix and returns { path, is_dir } rows; it and kcode.fs_read only see the files inside the manifest’s fs.read globs. The tool appears in /tools next to the built-ins, and the permission dialog treats it the same way.

Validate and load

kcode plugins validate ~/.kcode/plugins/hello

The validator runs the same checks a session load runs: manifest shape, unknown capability keys, the retired flat permissions list, a Lua entry point that loads. Start a session and run /plugins to see the plugin and its grants listed.

Where next

The worked examples page has a checked example for every surface: commands, events, cron, input transforms, tool gates, intercept points, Wasm, MCP, content bundles, hooks, themes, and the keymap.