# 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
