SkillKit.Skill (SkillKit v0.3.0)

Copy Markdown View Source

Pure data struct representing a registered skill in SkillKit.

SkillKit.Skill is a plain Elixir struct that carries all identity, execution, and hook configuration for a skill. When :tool names a module, that module executes the skill and is exposed to the model when the skill is activated. :tool defaults to nil — a knowledge-only skill whose body is injected on activation but which exposes NO executable tool. A tool (e.g. SkillKit.Tools.Shell) is opt-in, never a silent default: set it explicitly via the SkillKit.Kit macro (use SkillKit.Kit, which patches :tool to the kit module) or by constructing the struct directly.

Struct Fields

FieldTypeDefaultDescription
:name`String.t()nil`nilFully-qualified "namespace:skill_name"
:namespace`String.t()nil`nilNamespace segment extracted from name
:description`String.t()nil`nilHuman-readable description
:body`String.t()nil`nilSkill body / prompt template
:location`String.t()nil`nilFile path or source location for this skill

| :required_scope | [String.t()] | [] | Scopes required to call this skill | | :tool | module() | nil | nil | Module that executes the skill, or nil for a knowledge-only skill (no tool exposed) | | :hooks | [SkillKit.Hook.t()] | [] | Lifecycle hooks attached to this skill | | :metadata | %{String.t() => term()} | %{} | Arbitrary key-value metadata from frontmatter |

Naming Convention

Skill names follow the format "namespace:skill_name", where:

  • Both segments are lowercase, starting with a letter
  • Segments may contain letters, digits, underscores, and hyphens
  • Exactly one colon separates the namespace from the skill name

Examples: "files:read", "tools:web-search", "my-org:analyze_data"

Reserved Metadata Keys

Most :metadata is opaque and passed through untouched. One key is reserved by the framework:

  • "model" — runs the skill's activation sub-loop on a specific model instead of the parent agent's. The value is a provider-URI string in the same form as AGENT.md's model: (e.g. "anthropic://claude-sonnet-4-6", "openinfer://...", or a bare model name resolving to the default provider; ?max_tokens=...-style query params ride along to the provider). When unset, blank, or naming a provider that isn't configured, the activation falls back to the parent agent's model (the fallback is logged). Declared in SKILL.md frontmatter under metadata::

    ---
    name: design
    description: Authors the OG template Liquid.
    metadata:
      model: "anthropic://claude-sonnet-4-6?max_tokens=8000"
    ---

Summary

Functions

Renders the skill body by substituting template tokens with values from args.

Types

t()

@type t() :: %SkillKit.Skill{
  body: String.t() | nil,
  description: String.t() | nil,
  hooks: [SkillKit.Hook.t()],
  location: String.t() | nil,
  metadata: %{optional(String.t()) => term()},
  name: String.t() | nil,
  namespace: String.t() | nil,
  required_scope: [String.t()],
  tool: module() | nil
}

Functions

render(skill, args, scope \\ nil, scope_context \\ nil)

@spec render(t(), map(), term(), map() | nil) :: {:ok, String.t()}

Renders the skill body by substituting template tokens with values from args.

Supported tokens:

  • $ARGUMENTS — all arguments as a single string (from args["arguments"])
  • $ARGUMENTS[N] — positional argument by 0-based index (space-split)
  • $N — shorthand for $ARGUMENTS[N] (e.g., $0, $1)
  • $SKILL_DIR / ${SKILL_DIR} — directory of the skill (derived from :location)
  • $SESSION_ID / ${SESSION_ID} — session ID (from args["session_id"])
  • Legacy ${CLAUDE_SKILL_DIR} and ${CLAUDE_SESSION_ID} are still supported.

When a scope implementing SkillKit.Scope is provided, any remaining $VAR / ${VAR} tokens are resolved via Scope.resolve/3 as a final fallback. Unresolved scope variables are left as-is.

If $ARGUMENTS (or $N) is NOT present in the body but arguments are provided, appends \n\nARGUMENTS: <value> to the end.

Returns {:ok, ""} when body is nil.