> ## Documentation Index
> Fetch the complete documentation index at: https://docs.instructorphp.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 19 skills

# Skills

Skills are reusable instruction modules that extend what an agent can do. Each skill is a directory containing a `SKILL.md` file with YAML frontmatter and markdown instructions. The agent discovers available skills at startup, advertises their descriptions to the LLM, and loads full skill content on demand via tool call.

The skill system follows the [Agent Skills Open Standard](https://agentskills.io), a portable specification adopted by 30+ AI tools including Claude Code, OpenAI Codex, Cursor, GitHub Copilot, and others. Skills written for this framework are compatible with those tools and vice versa.

## Directory Structure

Each skill lives in its own directory under a skills root:

```
skills/
├── code-review/
│   ├── SKILL.md           # Main instructions (required)
│   ├── examples/
│   │   └── sample.md      # Example output
│   └── scripts/
│       └── lint.sh         # Helper script
├── deploy/
│   └── SKILL.md
└── api-conventions/
    ├── SKILL.md
    └── references/
        └── openapi.yaml
// @doctest id="1c0b"
```

Resource folders (`scripts/`, `references/`, `assets/`, `examples/`) are automatically discovered and listed in the skill's `resources` property.

## SKILL.md Format

Every skill needs a `SKILL.md` file with optional YAML frontmatter between `---` markers, followed by markdown content:

```yaml theme={null}
---
name: code-review
description: Review code for quality, bugs, and best practices
argument-hint: "[file-or-directory]"
license: MIT
---

When reviewing code, check for:

1. Logic errors and edge cases
2. Security vulnerabilities
3. Performance issues
4. Style consistency

Focus on $ARGUMENTS if provided.
# @doctest id="936a"
```

### Frontmatter Fields

#### Agent Skills Open Standard (portable)

| Field           | Type        | Default        | Description                                         |
| --------------- | ----------- | -------------- | --------------------------------------------------- |
| `name`          | string      | directory name | Skill name (lowercase, hyphens, max 64 chars)       |
| `description`   | string      | `''`           | What the skill does and when to use it              |
| `license`       | string      | `null`         | License (e.g. `MIT`, `Apache-2.0`)                  |
| `compatibility` | string      | `null`         | Environment requirements                            |
| `metadata`      | map         | `[]`           | Arbitrary key-value pairs                           |
| `allowed-tools` | string/list | `[]`           | Space/comma-delimited or YAML list of allowed tools |

#### Cross-platform Extensions

| Field                      | Type   | Default | Description                                         |
| -------------------------- | ------ | ------- | --------------------------------------------------- |
| `disable-model-invocation` | bool   | `false` | Prevent the model from auto-loading this skill      |
| `user-invocable`           | bool   | `true`  | Whether to show in user-facing skill listings       |
| `argument-hint`            | string | `null`  | Hint for expected arguments (e.g. `[issue-number]`) |

#### Execution Context Extensions

| Field     | Type   | Default | Description                          |
| --------- | ------ | ------- | ------------------------------------ |
| `model`   | string | `null`  | Override model when skill is active  |
| `context` | string | `null`  | Set to `fork` for subagent execution |
| `agent`   | string | `null`  | Subagent type when `context: fork`   |

Unknown frontmatter fields are silently ignored, ensuring forward compatibility.

## Setting Up Skills

### Creating a SkillLibrary

The `SkillLibrary` scans a directory for skill subdirectories:

```php theme={null}
use Cognesy\Agents\Capability\Skills\SkillLibrary;

$library = SkillLibrary::inDirectory(__DIR__ . '/skills');

// List all skills (name + description)
$skills = $library->listSkills();

// Check and load a specific skill
if ($library->hasSkill('code-review')) {
    $skill = $library->getSkill('code-review');
}
// @doctest id="271e"
```

Skills are lazy-loaded: only frontmatter is read during discovery, full content is loaded on first `getSkill()` call and cached thereafter.

### Wiring Into an Agent

The `UseSkills` capability registers the `load_skill` tool and injects skill metadata via a hook:

```php theme={null}
use Cognesy\Agents\Builder\AgentBuilder;
use Cognesy\Agents\Capability\Skills\SkillLibrary;
use Cognesy\Agents\Capability\Skills\UseSkills;

$library = SkillLibrary::inDirectory(__DIR__ . '/skills');

$agent = AgentBuilder::base()
    ->withCapability(new UseSkills($library))
    ->build();
// @doctest id="ea86"
```

This does two things:

1. **Registers `load_skill` tool** — the LLM can call `load_skill(skill_name: "code-review")` to load full skill content, or `load_skill(list_skills: true)` to see available skills.
2. **Injects metadata hook** — `AppendSkillMetadataHook` prepends a system message listing skill names and descriptions so the LLM knows what's available.

## Argument Substitution

When loading a skill with arguments, placeholders in the body are replaced:

| Placeholder     | Replaced with                 |
| --------------- | ----------------------------- |
| `$ARGUMENTS`    | Full argument string          |
| `$ARGUMENTS[N]` | Nth argument (0-based)        |
| `$N`            | Shorthand for `$ARGUMENTS[N]` |

If no placeholder is present, arguments are appended as `ARGUMENTS: <value>`.

```yaml theme={null}
---
name: fix-issue
description: Fix a GitHub issue
argument-hint: "[issue-number]"
---

Fix GitHub issue $ARGUMENTS following our coding standards.
# @doctest id="7ffe"
```

When loaded with `load_skill(skill_name: "fix-issue", arguments: "123")`, the body becomes "Fix GitHub issue 123 following our coding standards."

## Invocation Control

Two flags control who can invoke a skill:

| Configuration                    | Model sees it | User sees it | Use case                                     |
| -------------------------------- | ------------- | ------------ | -------------------------------------------- |
| *(default)*                      | Yes           | Yes          | General-purpose skills                       |
| `disable-model-invocation: true` | No            | Yes          | Side-effect workflows (deploy, commit)       |
| `user-invocable: false`          | Yes           | No           | Background knowledge (legacy system context) |

```yaml theme={null}
---
name: deploy
description: Deploy to production
disable-model-invocation: true
---

Deploy the application:
1. Run tests
2. Build
3. Push to production
# @doctest id="7e86"
```

## Components

### Skill

Immutable value object holding parsed skill data:

```php theme={null}
$skill->name;                  // string
$skill->description;           // string
$skill->body;                  // string (markdown content)
$skill->path;                  // string (absolute path to SKILL)
$skill->license;               // ?string
$skill->compatibility;         // ?string
$skill->metadata;              // array<string, string>
$skill->allowedTools;          // list<string>
$skill->disableModelInvocation; // bool
$skill->userInvocable;         // bool
$skill->argumentHint;          // ?string
$skill->model;                 // ?string
$skill->context;               // ?string
$skill->agent;                 // ?string
$skill->resources;             // list<string>

$skill->render();              // Full skill content with XML tags
$skill->render('arg1 arg2');   // With argument substitution
$skill->renderMetadata();      // "[name]: description"
$skill->toArray();             // All non-null fields as array
// @doctest id="d771"
```

### SkillLibrary

Discovery and lazy-loading of skills from a directory:

```php theme={null}
$library = SkillLibrary::inDirectory($path);
$library->listSkills();                           // All skills
$library->listSkills(modelInvocable: true);       // Exclude disabled
$library->listSkills(userInvocable: true);        // Exclude background
$library->hasSkill('name');                       // bool
$library->getSkill('name');                       // ?Skill
$library->renderSkillList();                      // Formatted list
// @doctest id="e357"
```

### LoadSkillTool

Tool exposed to the LLM for loading skills:

```
load_skill(skill_name: "code-review")              // Load a skill
load_skill(skill_name: "fix-issue", arguments: "123")  // With args
load_skill(list_skills: true)                      // List available
// @doctest id="f2ec"
```

### AppendSkillMetadataHook

Fires on `BeforeStep`. Before the first agent step, injects a system message listing available model-invocable skills with their descriptions and argument hints. Skips subsequent steps if already injected.

### TrackActiveSkillHook

Fires on `AfterToolUse`. When `load_skill` completes successfully, updates the agent state metadata with the loaded skill's `allowed-tools` list and `model` override. Clears these values when a skill without them is loaded.

### SkillToolFilterHook

Fires on `BeforeToolUse`. Enforces `allowed-tools` restrictions when a skill with an `allowed-tools` field is active. If the tool being called is not in the list, blocks execution. The `load_skill` tool itself is never blocked, allowing the agent to switch skills.

### SkillModelOverrideHook

Fires on `BeforeStep`. Checks agent state metadata for an active skill's `model` override and applies it by creating a new `LLMConfig` with the specified model. This allows skills to target specific models (e.g., a coding skill that requires a more capable model).

## Shell Preprocessing

Skills can embed shell commands using the `` !`command` `` syntax. When a `SkillPreprocessor` is configured, these patterns are executed and replaced with their output before argument substitution occurs.

```yaml theme={null}
---
name: project-info
description: Show project context
---

Project version: !`cat VERSION`
Current branch: !`git branch --show-current`
Recent changes: !`git log --oneline -5`

Review the code in $ARGUMENTS.
# @doctest id="8b03"
```

When loaded, the `` !`...` `` patterns are replaced with live command output, giving the LLM up-to-date context.

### Enabling Preprocessing

Pass a `SkillPreprocessor` to `UseSkills`:

```php theme={null}
use Cognesy\Agents\Capability\Skills\SkillLibrary;
use Cognesy\Agents\Capability\Skills\SkillPreprocessor;
use Cognesy\Agents\Capability\Skills\UseSkills;

$library = SkillLibrary::inDirectory(__DIR__ . '/skills');
$preprocessor = new SkillPreprocessor(
    workingDirectory: getcwd(),  // optional, defaults to cwd
    timeoutSeconds: 10,          // optional, default 10s
);

$agent = AgentBuilder::base()
    ->withCapability(new UseSkills($library, $preprocessor))
    ->build();
// @doctest id="a666"
```

Commands that fail or time out are replaced with `[error: ...]` markers instead of crashing the skill load.

## Cross-Platform Compatibility

The portable subset that works across all Agent Skills-compatible tools:

* `name` and `description` in frontmatter
* Markdown instructions in the body
* Directory-per-skill layout with `SKILL.md` entry point

Extension fields (`disable-model-invocation`, `context`, `model`, etc.) are tool-specific. Unknown fields are ignored gracefully by all compliant tools, so skills with extensions remain portable — the extensions simply don't activate in tools that don't support them.
