Type to search the docs and updates.

Browse documentation

Kilo (kilo)

Set up this tool

Kilo Code gets AGENTS.md, agents, commands, plugin hooks, shared skills, and a merged kilo.jsonc.

First rule

Select kilo in agnostic-ai.yaml, then follow First rule to add the shared commit-prefix rule and sync it. With the default paths, it reaches AGENTS.md.

Import covers only selected settings, ignore patterns, and generated rule sections. It does not preserve a complete hand-written Kilo setup.

Review import coverage and keep files outside that coverage before syncing.

Verify

Run agnostic-ai sync --check and inspect AGENTS.md for the commit-prefix rule. Open the project in Kilo and ask for a commit subject for a small fix; it should use fix:.

Next, add a skill.

Output

AGENTS.md                          # entry-point pointer body + inlined rules (shared path)
.kilo/rules/<name>.md              # one per rule the root AGENTS.md does not carry
.kilo/agents/<name>.md             # one per agent
.agents/skills/<name>/SKILL.md     # one folder per skill, plus bundled assets (shared cross-tool tree)
.kilo/commands/<name>.md           # one per command
.kilo/plugin/<name>.ts             # one per hook, auto-registered at startup
kilo.jsonc                         # instructions, mcp, and permission maps (merged with existing user config)
.kilocodeignore                    # compatibility input for Kilo's permission migrator

Native mappings

Field mappings, discovery, and compatibility

Shell commands in global skills

sync --global writes skills to ~/.kilo/skills/. Kilo supports inline !`command` placeholders there and in its trusted ~/.agents/skills/ and ~/.claude/skills/ directories (shell commands in skills). Kilo requests approval when loading the skill; KILO_DISABLE_SKILL_SHELL disables execution. Project and remote skills do not execute these commands. An arbitrary output directory does not become trusted because its source is global.

The sync check accepts only inline shell placeholders at those user paths. $ARGUMENTS, positional arguments, and Claude's ```! blocks remain unsupported. Placeholders inside ordinary code blocks are examples.

Agents

(.kilo/agents/<name>.md):

  • The filename sets the agent name, so name: is never written. Frontmatter carries description (falls back to the spec name) plus color, mode, and model when set.
  • color is not checked. Kilo accepts hex (#FF5733) or a theme token such as primary, accent, or error (custom subagents), so color: blue may not show as you expect. See color support by target.
  • mode takes OpenCode's primary, subagent, or all.
  • The portable maxTurns is written as steps (agent options). x-kilo.steps wins over it.
  • disable, hidden, temperature, and top_p need x-kilo, for example x-kilo: {temperature: 0.1}.
  • Kilo has no tools: key, so a tools list becomes a permission map: tools: [Read, Grep] writes permission: {"*": deny, read: {...}, grep: allow, doom_loop: ask, external_directory: ask}. The catch-all comes first because the last matching rule wins. doom_loop and external_directory ask at most, Kilo's default, so the catch-all does not silently deny paths outside the project.
  • Restricted agents retain project permission decisions for their listed tools and deny tools outside the list. Sync reads root kilo.jsonc and a hand-written .kilo/kilo.jsonc, including during preview. It does not read global user policy. Pattern intersections that cannot be represented exactly become denies, so a restricted agent can be stricter than the project. MCP server-name overlaps and Windows case differences are handled conservatively. Use x-kilo.permission for an explicit native override.
  • These names translate: Read, Glob, Grep, Edit, Write, Bash, WebFetch, WebSearch, Task, Skill, TodoRead, TodoWrite, and mcp__<server>__<tool> as {server}_{tool} (permission keys, tool groups). Other names drop with a sync note. If nothing translates, no map is written, because {"*": deny} alone would lock the agent out. x-kilo: {permission: {...}} wins outright.

Skills

go to the shared .agents/skills/<name>/SKILL.md tree. Kilo loads it by default beside its own .kilo/skills/. Codex, amp, zed, crush, openhands, windsurf, and augment share it. Kilo also scans .claude/skills/ (in the VS Code extension, only with Claude Code Compatibility on). Any other outputs.kilo.skills-dir is added to skills.paths in kilo.jsonc (skills). Your skills.paths entries and skills.urls stay.

Rules

  • Unscoped rules go into the root AGENTS.md, which Kilo always loads ("cannot be individually disabled"). A rule already in that block gets no .kilo/rules/ file or instructions entry, so it loads once.
  • A rule whose text differs for Kilo, such as one with a path variable, keeps its file.
  • Scoped rules use nested AGENTS.md. See target behavior.
  • Kilo's order is agent prompt, then project instructions, then AGENTS.md, then global. Kilo still loads the legacy .kilocode/rules/, which this adapter never writes.
No root rules block

Kilo reads no rules block when a file output override moves the entry point off the root AGENTS.md, or when AGENTS.md is in sync.unmanaged. Check the effect on other targets sharing that file. Unscoped rules then get one file each under .kilo/rules/, listed by explicit path (not a glob) in the instructions array of kilo.jsonc. When no rule file is left, sync removes only the instructions entries for rules that AGENTS.md now carries. Your own entries stay.

Commands

use Kilo's slash-command path (workflows). As with agents, the name comes from the filename. Frontmatter carries description, agent, model, variant (a reasoning-effort override such as low or high), and subtask when set, close to OpenCode's list. Kilo reserves the command name goal for its session goals (goals): it ignores a command file by that name, warns, and offers it as /goal:command. A command spec named goal is still written, with a note to rename it.

MCP

servers merge into the mcp map of kilo.jsonc (not the deprecated mcpServers):

  • Stdio sets type: "local", joins command and args into one command array, and uses environment for env vars.
  • Remote sets type: "remote" with url and headers. oauth: false turns off automatic OAuth.
  • disabled: true writes "enabled": false. An enabled server gets no key.
  • Both keep timeout (milliseconds, zero included). x-kilo can override timeout and oauth.
  • sync keeps your keys in kilo.jsonc. It reads JSONC, as Kilo documents, but drops comments and trailing commas with a warning.

Hooks

Kilo auto-registers each plugin module at startup (plugins). Its plugins work like OpenCode's, and sync uses the same code to generate both. PreToolUse and PostToolUse map to tool.execute.before and tool.execute.after. Other documented events use the event hook with an event.type guard. Matchers, exit code 2 blocking, and disabled: true behave the same. Only the module shape differs: export default { id: "<name>", server }. Hooks on shell.env, experimental.session.compacting, or an unmapped event drop with a sync note.

Settings

a settings spec's default model and its x-kilo block (for Kilo-only keys such as sandbox) merge into the top level of kilo.jsonc. An x-kilo permission instead merges tool by tool with the translated rules.

The portable allow, deny, and ask lists merge into the permission key of kilo.jsonc (auto-approving actions):

  • Each rule becomes one glob under one tool key, matched against the tool's arguments. Bash(npm run:*) becomes bash: {"npm run *": "allow"}, Bash(rm -rf /) becomes bash: {"rm -rf /": "deny"}, Read(docs/*) becomes read: {"docs/*": "allow"}, a bare Bash becomes bash: {"*": "allow"}, and mcp__github__list_issues becomes github_list_issues.
  • A rule in two lists takes the stricter action.
  • Write and Edit rules both land under edit, the one permission Kilo's edit and write tools check (packages/opencode/src/tool/write.ts). So Edit(.env) in deny blocks writing .env too, even with a Write allow, and an agent that lists Write can also use Kilo's edit tool.
  • Inside one tool, patterns go allow, then ask, then deny. Kilo takes the last match, and Claude Code lets a deny or ask win over any allow, so a narrower allow never bypasses a broader deny.
  • WebFetch(domain:example.test) drops with a sync note, because Kilo matches URLs. Unknown tool names drop too.
  • x-kilo.permission writes Kilo's own map. It wins for the tool keys it names, and that spec's portable lists are skipped.
  • Your entries for tools agnostic-ai does not set stay.

This adapter never writes .kilo/kilo.jsonc. A hand-written one merges over the root kilo.jsonc (config order), so its mcp or instructions replace the generated ones.

Ignore

specs write .kilocodeignore in the project root. Kilo's migrator, not this adapter, turns it into read and edit denials.

Config keys

KeyDefaultNotes
outputs.kilo.rules-dir.kilo/rules
outputs.kilo.agents-dir.kilo/agents
outputs.kilo.skills-dir.agents/skills
outputs.kilo.commands-dir.kilo/commands
outputs.kilo.hooks-dir.kilo/pluginKilo only loads plugins from plugin/ or plugins/, so moving this stops the hooks loading
outputs.kilo.mcp-filekilo.jsonc
outputs.kilo.ignore-file.kilocodeignore

With builtins: [memory], sync adds both shared memory indexes to instructions. While rules go into AGENTS.md (the default), your own entries stay, and dropping the built-in removes only those two. When sync lists rule files in instructions, it owns the whole list.

With memory.personal: repo, the personal store lies outside the project root, and Kilo ignores project-declared files there. Sync writes only the shared index and prints a note. Add the store's MEMORY.md to instructions in ~/.config/kilo/kilo.jsonc to load it. See repo mode.

Import

agnostic-ai import kilo reads .kilocodeignore and two fields from the root kilo.jsonc: the default model (into settings/kilo.yaml) and portable entries in the permission map (into settings/permissions-kilo.yaml). Keys with no portable spelling (external_directory, lsp, doom_loop, namespaced MCP keys) stay in the file.

Generated rule sections in AGENTS.md are recovered when their source specs are missing. Hand-written instructions, native rule directories, agents, skills, commands, MCP servers, and plugin hooks are not imported.

Protected paths

Not enforced. Sync cannot write an edit-blocking rule or hook for this tool, so it prints a protected-path coverage note.