skill-format
Corpo da especificação
Kode Skill Format (SKILL.md)
Native skill packaging for products/dev/kode. A skill is a reusable unit of agent capability — a system-prompt + tool whitelist
- optional inline scripts the kode agent can invoke by name.
Distinct from Claude-style commands (meta/context/commands/*.md,
adapted via claude-commands-compat.md):
| Aspect | Kode skill (this spec) | Claude-style command (#103) |
|---|---|---|
| Directory | ~/.kode/skills/<slug>/SKILL.md | ~/.claude/commands/*.md |
| Granularity | Folder bundle (md + assets) | Single file |
| Frontmatter | Mandatory (validated) | Optional |
| Execution | kode skill exec <slug> [args...] | /commands resolve (prompt only) today |
| Tool whitelist | Declared per skill | Inherits parent |
| Distribution | Koder Hub (type=skill) | Operator-curated, manual sync |
| Format author | Kode native | Adapter for Claude Code 1:1 |
Both can coexist; /commands lists Claude-style, /skills lists
native. The agent path (#047 router) dispatches a skill_exec tool
to the native form; Claude-style commands flow through the prompt
template path until the SKILL.md format covers them too.
R1 — Directory layout
~/.kode/skills/
└── <slug>/
├── SKILL.md # mandatory — frontmatter + body
├── ... # optional assets the skill references
└── *.sh|*.py|*.kd # optional executable scripts
Slug must match [a-z0-9][a-z0-9-]*. Directory name = slug.
R2 — Frontmatter (mandatory)
YAML between two --- lines at the top of SKILL.md:
---
name: <slug> # MUST match directory name
description: One-line description
version: <semver> # 0.1.0 first; bump on breaking change
metadata:
kode:
primary_env: bash|python|node|kd|none
requires:
bins: [git, jq] # binaries that must resolve in PATH
env: [FOO_TOKEN] # env vars that must be set
permission_mode: default|acceptEdits|plan|auto # optional override
tools: # tool whitelist; nil = inherit AllowedTools
- read_file
- shell
---
The kode loader rejects skills missing name, description, or
version. Other keys are preserved on the NativeSkill.Extra map for
forward-compat without loader churn.
R3 — Body
Markdown. The body is the prompt template the agent receives when
the skill is invoked. $ARGUMENTS (matching Claude commands' convention)
substitutes for the arg list at runtime. Code blocks tagged bash,
python, etc., are inert templates the agent may quote in its response.
R4 — Loader
internal/skills/native.go (this implementation):
LoadNativeSkillsDir(dir) ([]NativeSkill, error)— scans<dir>/*/SKILL.md.LoadNativeSkillFile(path) (NativeSkill, error)— parses one file.DiscoverNativeSkillsDirs() []string— default order:Cfg.NativeSkillsDir(explicit override; YAML config keynative_skills_dir).~/.kode/skills/.
First-match wins on slug collisions.
The loader is best-effort by file: a malformed SKILL.md skips
that one directory but does not fail the registry load. Validation
errors surface via NativeSkill.Err so the operator can inspect
why a skill didn't load.
R5 — Runner (interface)
internal/skills.SkillRunner (forward-declared; concrete impl
ships in engines/sdk/koder_kit Go binding when #025 lands):
type SkillRunner interface {
Run(ctx context.Context, skill NativeSkill, args []string) (string, error)
}
kode binaries ship NoopSkillRunner which returns
ErrSkillRuntimeMissing so unwired binaries fail loud — same fail-loud
pattern as subagent.NoopRunner (DKODE-100).
The koder_kit KodeSkillRunner (DKODE-025) registers a real Runner
via tools.SetSkillRunner once the Go binding ships.
R6 — Surfaces
Operator slash commands (read-only inspection until Runner lands):
/skillsor/skills list— registered skills with slug + description./skills show <slug>— frontmatter + body of one skill./skills resolve <slug> [args ...]— render body with$ARGUMENTSsubstituted (same convention as/commands resolve).
Once Runner lands, also:
kode skill exec <slug> [args ...]— CLI invocation through Runner.skill_exec(slug, args)agent tool — harness dispatch.
R7 — Permission model
Permission verb skill + target <slug>. Examples:
skill:dangerous-*— disable a familyskill:*— disable alltool:skill_exec(legacy) — covers the agent tool wholesale
Sub-tool invocations from the skill body still pass through the
permission matcher individually (the Runner forwards them), so deny
rules on bash/fs:write apply transitively.
R8 — Distribution
Skills resolve from:
- Local path:
kode skill install <path>copies into~/.kode/skills/. - Koder Hub:
kode skill install <slug>resolves throughhub.koder.dev/api/v1/skills/<slug>(depends on DKODE-026type=skillHub adapter).
Hub-unavailable falls back to a clear error; manual operators can always install by path.
Out of scope
- Sandboxing of
kode skill execbody execution — that's a separate ticket (engines/sandboxper #032). - Cross-platform binary distribution — Hub side; this spec only prescribes the on-disk format.
- TOML loader (the Claude convention uses TOML; kode uses YAML for consistency with its own config tree — both can land if the broader Stack standardizes).
Test contract
T1. Empty SKILL.md rejects with a clear validation error.
T2. Missing name rejects.
T3. Missing description rejects.
T4. Missing version rejects.
T5. Mismatched name ↔ directory name rejects.
T6. Round-trip: file → load → Resolve(args) substitutes $ARGUMENTS.
T7. Discovery: override > ~/.kode/skills/; first-match wins.
T8. NoopSkillRunner.Run returns ErrSkillRuntimeMissing.
T9. Real Runner registration via tools.SetSkillRunner round-trips.
T10. Multi-skill directory with one malformed entry: the rest load.