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 carriesdescription(falls back to the spec name) pluscolor,mode, andmodelwhen set. coloris not checked. Kilo accepts hex (#FF5733) or a theme token such asprimary,accent, orerror(custom subagents), socolor: bluemay not show as you expect. Seecolorsupport by target.modetakes OpenCode'sprimary,subagent, orall.- The portable
maxTurnsis written assteps(agent options).x-kilo.stepswins over it. disable,hidden,temperature, andtop_pneedx-kilo, for examplex-kilo: {temperature: 0.1}.- Kilo has no
tools:key, so atoolslist becomes apermissionmap:tools: [Read, Grep]writespermission: {"*": deny, read: {...}, grep: allow, doom_loop: ask, external_directory: ask}. The catch-all comes first because the last matching rule wins.doom_loopandexternal_directoryask 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.jsoncand 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. Usex-kilo.permissionfor an explicit native override. - These names translate:
Read,Glob,Grep,Edit,Write,Bash,WebFetch,WebSearch,Task,Skill,TodoRead,TodoWrite, andmcp__<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 orinstructionsentry, 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, thenAGENTS.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", joinscommandandargsinto onecommandarray, and usesenvironmentfor env vars. - Remote sets
type: "remote"withurlandheaders.oauth: falseturns off automatic OAuth. disabled: truewrites"enabled": false. An enabled server gets no key.- Both keep
timeout(milliseconds, zero included).x-kilocan overridetimeoutandoauth. synckeeps your keys inkilo.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:*)becomesbash: {"npm run *": "allow"},Bash(rm -rf /)becomesbash: {"rm -rf /": "deny"},Read(docs/*)becomesread: {"docs/*": "allow"}, a bareBashbecomesbash: {"*": "allow"}, andmcp__github__list_issuesbecomesgithub_list_issues. - A rule in two lists takes the stricter action.
WriteandEditrules both land underedit, the one permission Kilo's edit and write tools check (packages/opencode/src/tool/write.ts). SoEdit(.env)indenyblocks writing.envtoo, even with aWriteallow, and an agent that listsWritecan 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.permissionwrites 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
| Key | Default | Notes |
|---|---|---|
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/plugin | Kilo only loads plugins from plugin/ or plugins/, so moving this stops the hooks loading |
outputs.kilo.mcp-file | kilo.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.