Pular para o conteúdo

skill-format

kode specs/kode/skill-format.kmd

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):

AspectKode skill (this spec)Claude-style command (#103)
Directory~/.kode/skills/<slug>/SKILL.md~/.claude/commands/*.md
GranularityFolder bundle (md + assets)Single file
FrontmatterMandatory (validated)Optional
Executionkode skill exec <slug> [args...]/commands resolve (prompt only) today
Tool whitelistDeclared per skillInherits parent
DistributionKoder Hub (type=skill)Operator-curated, manual sync
Format authorKode nativeAdapter 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:
    1. Cfg.NativeSkillsDir (explicit override; YAML config key native_skills_dir).
    2. ~/.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):

  • /skills or /skills list — registered skills with slug + description.
  • /skills show <slug> — frontmatter + body of one skill.
  • /skills resolve <slug> [args ...] — render body with $ARGUMENTS substituted (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 family
  • skill:* — disable all
  • tool: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:

  1. Local path: kode skill install <path> copies into ~/.kode/skills/.
  2. Koder Hub: kode skill install <slug> resolves through hub.koder.dev/api/v1/skills/<slug> (depends on DKODE-026 type=skill Hub adapter).

Hub-unavailable falls back to a clear error; manual operators can always install by path.

Out of scope

  • Sandboxing of kode skill exec body execution — that's a separate ticket (engines/sandbox per #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.