# Agent Skills Standard Loader The **Agent Skills loader** (`cleveragents.skills.agent_skills_loader`) implements the [AgentSkills.io](https://AgentSkills.io) standard for packaging instruction-driven, multi-step workflows as `SKILL.md` files with optional resource sub-directories. Agent Skills differ from MCP tools: instead of a single function call, they load procedural *instructions* that an LLM agent follows—potentially invoking multiple tools in sequence. --- ## Folder Layout ``` my-skill/ ├── SKILL.md ← required — frontmatter + instructions ├── scripts/ ← optional — Python/shell scripts the agent may run │ └── deploy.py ├── references/ ← optional — supplementary Markdown documents │ └── runbook.md └── assets/ ← optional — any binary or static resources └── config.json ``` Only `SKILL.md` is required. The three resource sub-directories are discovered automatically when present and made available on demand during Tier 3 disclosure. --- ## SKILL.md Format `SKILL.md` files follow Markdown with a YAML frontmatter block: ```markdown --- name: local/deploy-to-staging description: Deploy the current branch to the staging environment. steps: # optional — structured steps with stable ordering - Verify all tests pass - Push the branch to the remote - Run the CI pipeline - Execute scripts/deploy.py to deploy - Check the health endpoint version: 1.0.0 # optional compatibility: # optional min_agent_version: "3.0.0" metadata: # optional — arbitrary key/value pairs author: team environment: staging allowed-tools: # optional — restrict which tools the skill may call - builtin/shell - builtin/git-status --- # Deployment Details Additional prose context for each step, loaded on activation. ``` ### Required Frontmatter Fields | Field | Type | Description | |-------|------|-------------| | `name` | `string` | Namespaced name: `namespace/short-name` (e.g. `local/deploy-staging`) | | `description` | `string` | Short human-readable description of the skill | ### Optional Frontmatter Fields | Field | Type | Default | Description | |-------|------|---------|-------------| | `steps` | `list[string]` | `[]` | Ordered step instructions parsed into `SkillStep` objects with stable 1-based indexing | | `version` | `string` | `"0.0.0"` | Semantic version of the skill | | `compatibility` | `mapping` | `{}` | Compatibility constraints (e.g. `min_agent_version`) | | `metadata` | `mapping` | `{}` | Arbitrary key/value pairs (author, tags, etc.) | | `allowed-tools` | `list[string]` | `[]` | Tools the skill is permitted to invoke | ### Structured Steps When the `steps` key is present, each list entry becomes a `SkillStep` with a stable 1-based index and non-empty content string: ```python spec = AgentSkillSpec.from_file(Path("my-skill/SKILL.md")) for step in spec.steps: print(f"{step.index}. {step.content}") # 1. Verify all tests pass # 2. Push the branch to the remote # ... ``` Steps are stored in insertion order and never reordered — `step.index` is always equal to the step's position in the original list. --- ## Namespaced Naming Skill names follow the same `namespace/short-name` convention as tools and actors (see [Tool Model](tool_model.md)): ``` local/deploy-to-staging ← user-defined, default namespace devops/code-review-pipeline ← custom namespace team/onboarding-workflow ← team namespace ``` Rules: - Both `namespace` and `short-name` must start with an alphanumeric character. - Allowed characters: letters, digits, hyphens (`-`), underscores (`_`). - No spaces or special characters. --- ## Three-Tier Progressive Disclosure The loader implements the three-tier model defined in the specification (`docs/specification.md` § AgentSkillAdapter): | Tier | Method | What loads | Token cost | |------|--------|-----------|------------| | **1 — Metadata** | `discover()` | `name` + `description` only | ~50–100 tokens per skill | | **2 — Instructions** | `activate()` | Full `SKILL.md` Markdown body | Recommended < 5000 tokens | | **3 — Resources** | `list_resources()` | Paths from `scripts/`, `references/`, `assets/` | Variable, on demand | This means an actor can have dozens of Agent Skills available but only pay the token cost for their metadata at startup. Full instructions load only when the LLM determines a task matches the skill. --- ## Tool Mapping Agent Skills are registered in the Tool Registry with `source="agent_skill"`. The `AgentSkillToolDescriptor` carries: | Field | Value | Notes | |-------|-------|-------| | `source` | `"agent_skill"` | Fixed | | `name` | From frontmatter `name` | e.g. `local/deploy-to-staging` | | `description` | From frontmatter `description` | Used in system prompt | | `read_only` | `True` | Default — skill itself does not write | | `writes` | `False` | Default | | `agent_skill_path` | Absolute folder path | Runtime uses this to locate `SKILL.md` | | `allowed_tools` | From frontmatter `allowed-tools` | Optional restrict list | | `body` | Empty at discover; full body after activate | Progressive disclosure | | `resource_slots` | List of `AgentSkillResourceSlot` | One slot per discovered resource directory | ### Resource Binding Slots Each discovered sub-directory (`scripts/`, `references/`, `assets/`) becomes a **read-only resource binding slot** in the tool descriptor: ```python td = loader.to_tool_descriptor() for slot in td.resource_slots: print(slot.name) # "scripts", "references", or "assets" print(slot.resource_type) # "agent_skill_scripts", etc. print(slot.access) # "read_only" print(slot.path) # absolute path on disk ``` Slots are only created for directories that exist — if a skill has no `references/` folder, no `references` slot is emitted. All slots are unconditionally `access="read_only"`. ### Read-Only Default Rationale The skill descriptor is **read-only by default** because the skill's instructions do not themselves perform writes — the LLM agent decides which (potentially write-capable) tools to invoke during execution. Individual tools in `allowed-tools` may write; the skill descriptor's own capability metadata does not. --- ## Python API ```python from pathlib import Path from cleveragents.skills.agent_skills_loader import AgentSkillLoader, AgentSkillSpec # Parse a SKILL.md string directly spec = AgentSkillSpec.from_string(""" --- name: local/my-skill description: Does something useful. --- Step 1: Do this. """) # Load a full folder loader = AgentSkillLoader.from_folder(Path("./skills/my-skill")) # Tier 1 — discover (metadata only) descriptor = loader.discover() print(descriptor.name) # "local/my-skill" print(descriptor.body) # "" (empty) # Tier 2 — activate (load instructions) activated = loader.activate() print(activated.body) # "Step 1: Do this." # Tier 3 — list resources on demand resources = loader.list_resources() for path in resources: print(path.name) # Deactivate to free token budget loader.deactivate() print(loader.active_body) # None # Convert to Tool Registry descriptor tool_descriptor = loader.to_tool_descriptor() print(tool_descriptor.source) # "agent_skill" print(tool_descriptor.read_only) # True print(tool_descriptor.agent_skill_path) # "/abs/path/to/my-skill" ``` --- ## Path Safety All file paths returned by `list_resources()`, `script_paths`, `reference_paths`, and `asset_paths` are **absolute paths** that have been verified to reside within the skill folder. The loader uses `Path.resolve()` + `rglob()` to prevent path traversal outside the skill boundary. --- ## Error Handling | Condition | Exception | Message pattern | |-----------|-----------|-----------------| | `SKILL.md` not found in folder | `ValueError` | mentions `"SKILL.md"` | | Folder path does not exist | `FileNotFoundError` | mentions `"not found"` | | Missing `---` frontmatter | `ValueError` | mentions `"frontmatter"` | | Missing required `name` field | `ValueError` | mentions `"name"` | | Missing required `description` field | `ValueError` | mentions `"description"` | | Invalid `namespace/name` format | `ValueError` | mentions `"namespace"` | | Malformed YAML in frontmatter | `ValueError` | mentions `"YAML"` | --- ## Related - [Skills Protocol](skills_protocol.md) — `SkillDefinition`, `SkillMetadata` - [Skill CLI](skill_cli.md) — `agents skill` commands - [Tool Model](tool_model.md) — `Tool`, `ToolSource.AGENT_SKILL` - [MCP Adapter](mcp_adapter.md) — MCP tool source (counterpart) - `docs/specification.md` § AgentSkillAdapter — authoritative spec - `ADR-028` — Agent Skills Standard adoption decision