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.