Skip to content

cap_skill — Skill management tools

Source: cap_skill_mgr.c · header: 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, activate/deactivate them—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 five Callables:

Tool IDDescription
list_skillList every Skill declared in skills_list.json
register_skillAdd a Skill to the catalog (.md must already exist on disk)
unregister_skillRemove a Skill from the catalog
activate_skillActivate for the current session and open bound cap_groups
deactivate_skillDeactivate a Skill (or "all" to clear)

Except list_skill, all are flagged CLAW_CAP_FLAG_CALLABLE_BY_LLM.

activate_skill: tool call → session sync

Section titled “activate_skill: tool call → session sync”

activate_skill is the heart of the flow—how a capability mutates core session state:

Diagram

The last hop, claw_cap_set_session_llm_visible_groups, immediately widens the current session’s tool surface so the LLM can call newly opened caps on the next turn.

Registration enforces integrity:

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

// 2. File must already exist under the managed skills tree
// Path must live under /fatfs/skills/ and end with .md
if (!cap_skill_file_exists(skill_path)) { ... return ESP_ERR_NOT_FOUND; }

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

After editing skills_list.json, claw_skill_reload_registry() applies changes; failed reload rolls back to keep the catalog consistent.

activate_skill / deactivate_skill read 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

Tools return structured JSON the LLM can parse:

// Successful activate_skill
{
  "action": "activate_skill",
  "session_id": "tg::-123456",
  "skill_id": "cap_lua_run",
  "ok": true,
  "active_skill_ids": ["cap_lua_run", "cap_files"]
}

When deactivate_skill is blocked by a guard, it also returns structured JSON (instead of a silent failure), for example:

{
  "ok": false,
  "error": "deactivate blocked by skill guard",
  "skill_id": "all",
  "blocked_by": "cap_lua_run",
  "reason": "1 Lua async job(s) still running..."
}

When deactivating a single Skill, the response usually includes skill_id and reason; when using "all", it may additionally include blocked_by to indicate the first blocker.

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, inject docs into claw_core
Toolingcap_skillExpose the above as LLM / Console-callable tools

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