Skip to content

cap_skill — Skill management tools

Source: cap_skill_mgr.ccomponents/claw_capabilities/cap_skill_mgr/src/cap_skill_mgr.c · header: cap_skill_mgr.hcomponents/claw_capabilities/cap_skill_mgr/include/cap_skill_mgr.h

cap_skill is the thin wrapper that exposes core runtime features as tools. It contains no standalone product logic; it forwards claw_skill (see claw_skill)—register/unregister Skills and activate Skills—into LLM-callable tools and Console cap call entry points.

This “thin cap_* over claw_*” pattern is common: frameworks implement behavior, capabilities adapt it to tools and routing.

cap_skill registers four Callables:

Tool IDDescription
list_skillList every Skill discovered from Markdown under the Skills root
register_skillCreate one runtime-managed skills/<skill_id>/SKILL.md and reload the registry
unregister_skillDelete one runtime-managed Skill Markdown and reload the registry
activate_skillActivate a Skill, return the complete Skill document as the tool result, and open bound metadata.cap_groups

Except list_skill, all are flagged CLAW_CAP_FLAG_CALLABLE_BY_LLM.

activate_skill: tool result as context injection

Section titled “activate_skill: tool result as context injection”

activate_skill is the heart of the flow. It shows how a Capability mutates core runtime state while using the tool result for cache-friendly context injection:

Diagram

After activate_skill runs:

  1. It reads the full SKILL.md document for the Skill
  2. It calls claw_skill_activate_for_session to persist the activation state
  3. It calls cap_skill_sync_session_visible_groups to refresh the tool visibility allow-list
  4. It returns the Skill document to the LLM as a tool-call result in the form <skill_content name="skill_id">...</skill_content>

The LLM receives the complete Skill document in the same turn, and sees the newly opened tools on the next turn. When multiple Skills are needed, the LLM can call several activate_skill tools in parallel in one response to fetch multiple documents at once.

Registration enforces integrity:

// 1. Path safety: no leading "/", no "..", no backslashes
if (!cap_skill_path_is_valid(file_item->valuestring)) { ... }

// 2. file must match <skill_id>/SKILL.md under /fatfs/skills/
if (!cap_skill_path_is_valid(skill_id, file)) { ... return ESP_ERR_INVALID_ARG; }

// 3. skill_id must be unique
if (cap_skill_catalog_contains_id(skills, skill_id)) { ... return ESP_ERR_INVALID_ARG; }

After creating or deleting SKILL.md, claw_skill_reload_registry() applies changes; failed reload rolls back to keep the catalog consistent.

activate_skill reads ctx->session_id, so activation sets differ per session:

  • Telegram vs Feishu sessions can load different Skills
  • After reboot, activation state is restored from FATFS

When activate_skill succeeds, it returns the raw Skill document wrapped in XML tags:

<skill_content name="cap_lua">
... SKILL.md body ...
</skill_content>

On failure, it returns structured JSON:

{
  "ok": false,
  "error": "failed to read skill document",
  "skill_id": "nonexistent"
}

Other tools (register_skill, unregister_skill) also return structured JSON describing the operation result.

cap_skill itself has no Skill document—these tools are always visible without activation. The cap_skill group is usually part of the boot-time claw_cap_set_llm_visible_groups allow-list.

LayerModuleResponsibility
Frameworkclaw_skillParse the skills tree, persist activation per session, read documents, and provide the Skills catalog provider
Toolingcap_skillExpose the above as LLM / Console-callable tools; inject documents through tool results on activation

cap_skill is a pure adapter: no private state; everything delegates to claw_skill while handling JSON IO and error strings.