Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 52 additions & 3 deletions agent-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -706,17 +706,24 @@
"description": "Optional response cache: when the same user question is asked again, replay the previous answer instead of calling the model."
},
"skills": {
"description": "Enable skills discovery for this agent. Set to true to load all discovered skills from local filesystem sources; false disables skills. A list can mix sources (\"local\" or an HTTP/HTTPS URL) and/or skill names to include. If only names are given, local sources are loaded and filtered to just those skills.",
"description": "Enable skills discovery for this agent. Set to true to load all discovered skills from local filesystem sources; false disables skills. A list can mix sources (\"local\" or an HTTP/HTTPS URL), skill names to include, and inline skill definitions (objects). If only names are given, local sources are loaded and filtered to just those skills.",
"oneOf": [
{
"type": "boolean",
"description": "When true, loads all discovered local skills. When false, skills are disabled."
},
{
"type": "array",
"description": "List combining skill sources and/or skill names to include. Items equal to \"local\" or starting with http:// or https:// are treated as sources; any other string is treated as a skill name filter.",
"description": "List combining skill sources, skill names to include, and/or inline skill definitions. String items equal to \"local\" or starting with http:// or https:// are treated as sources; any other string is treated as a skill name filter; object items are inline skill definitions.",
"items": {
"type": "string"
"oneOf": [
{
"type": "string"
},
{
"$ref": "#/definitions/InlineSkill"
}
]
},
"examples": [
[
Expand All @@ -741,6 +748,48 @@
},
"additionalProperties": false
},
"InlineSkill": {
"type": "object",
"description": "A skill defined directly in the agent config instead of a SKILL.md file or remote URL. Supports the subset of the skill format that can be expressed in YAML.",
"properties": {
"name": {
"type": "string",
"description": "Skill identifier used by read_skill / run_skill and the /<name> slash command."
},
"description": {
"type": "string",
"description": "Short description injected into the system prompt so the model knows when the skill applies."
},
"instructions": {
"type": "string",
"description": "The skill body (equivalent to the Markdown content below the SKILL.md frontmatter)."
},
"context": {
"type": "string",
"enum": [
"fork"
],
"description": "When set to \"fork\", the skill runs as an isolated sub-agent (exposed via run_skill) instead of inlining into the conversation."
},
"model": {
"type": "string",
"description": "Optional model override applied while a fork-mode skill runs. Ignored for non-fork skills."
},
"allowed_tools": {
"type": "array",
"items": {
"type": "string"
},
"description": "Tools the skill expects to use (mirrors the SKILL.md allowed-tools field)."
}
},
"required": [
"name",
"description",
"instructions"
],
"additionalProperties": false
},
"CommandConfig": {
"type": "object",
"description": "Advanced command configuration. Set 'instruction' to send a prompt to the current agent. Set 'agent' to switch the active agent to a sub-agent (e.g. /plan -> the 'planner' sub-agent). Both fields can be combined: the agent is switched first, then the instruction is sent to the new agent.",
Expand Down
51 changes: 51 additions & 0 deletions docs/features/skills/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,57 @@ agents:

A name that doesn't match any discovered skill is logged as a warning at startup but is otherwise ignored.

## Inline Skills

Instead of (or alongside) loading skills from files and URLs, you can define skills directly in the agent config. An inline skill is a mapping item in the `skills` list, freely mixed with the string items above:

```yaml
agents:
root:
model: openai/gpt-4o
instruction: You are a helpful assistant.
skills:
- name: changelog
description: Write a concise changelog entry from a diff or description.
instructions: |
Produce a single changelog entry in Keep a Changelog style.
Pick the right category (Added, Changed, Fixed, Removed) and write
one imperative sentence summarising the user-visible change.

# A fork-mode inline skill runs in an isolated sub-agent.
- name: triage
description: Triage a bug report in an isolated context.
context: fork
instructions: |
Restate the problem, list likely root causes most-probable-first,
and propose the smallest reproduction and next concrete action.

# Inline skills mix freely with sources and name filters.
- local
toolsets:
- type: filesystem
```

Inline skills carry their body in the config itself, so they need no `SKILL.md` file and require no filesystem source. They are **always exposed** — the name filter only applies to file- and URL-based skills. Because inline skills travel inside the agent YAML, they also work in `--sandbox` mode without any kit staging, and they can be shared with the agent via `share push`.

### Inline Skill Fields

| Field | Required | Description |
| --------------- | -------- | -------------------------------------------------------------------------- |
| `name` | Yes | Skill identifier used by `read_skill` / `run_skill` and the `/<name>` command |
| `description` | Yes | Short description shown to the agent for skill matching |
| `instructions` | Yes | The skill body (what a `SKILL.md` would contain below its frontmatter) |
| `context` | No | Set to `fork` to run the skill as an isolated sub-agent |
| `model` | No | Override the model used while running a fork-mode skill |
| `allowed_tools` | No | Tools the skill expects to use |

<div class="callout callout-info" markdown="1">
<div class="callout-title">Inline vs. file-based skills
</div>
<p>Inline skills support the subset of the SKILL.md format that fits in YAML. They cannot bundle supporting files (no <code>read_skill_file</code>) or use <code>!`command`</code> expansion. For skills that need bundled resources or executable helpers, use a <code>SKILL.md</code> directory instead.</p>

</div>

## SKILL.md Format

<!-- yaml-lint:skip -->
Expand Down
50 changes: 50 additions & 0 deletions examples/skills_inline.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!yaml
# Inline skills example.
#
# Besides loading skills from the local filesystem or remote URLs, the `skills`
# field can also define skills directly in the agent config. An inline skill is
# a mapping (object) item in the `skills` list, freely mixed with string items
# (sources and name filters).
#
# Inline skills support the subset of the SKILL.md format that fits in YAML:
# - name (required) identifier used by read_skill / the /<name> command
# - description (required) shown to the model so it knows when the skill applies
# - instructions (required) the skill body (what a SKILL.md would contain)
# - context optional; set to "fork" to run the skill as an isolated sub-agent
# - model optional; model override for fork-mode skills
# - allowed_tools optional; tools the skill expects to use
#
# Inline skills are always exposed and are never affected by name filters.
agents:
root:
model: openai/gpt-4o-mini
description: An agent that demonstrates inline skill definitions.
instruction: |
You are a helpful assistant. Use the available skills when the user's
request matches one.
skills:
# A regular, inlined skill: its body is injected when the model reads it.
- name: changelog
description: Write a concise, well-structured changelog entry from a diff or description.
instructions: |
Produce a single changelog entry in Keep a Changelog style.

1. Pick the right category: Added, Changed, Fixed, Removed.
2. Write one imperative sentence summarising the user-visible change.
3. Avoid implementation detail; focus on what changed for users.

# A fork-mode skill: runs in an isolated sub-session via run_skill.
- name: triage
description: Triage a bug report and propose next steps in an isolated context.
context: fork
instructions: |
You are triaging a bug report.

- Restate the problem in one sentence.
- List the most likely root causes, most probable first.
- Propose the smallest reproduction and the next concrete action.

# Inline skills mix freely with sources and name filters.
- local
toolsets:
- type: filesystem
20 changes: 20 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -277,5 +277,25 @@ func validateSkillsConfiguration(_ string, agent *latest.AgentConfig) error {
return fmt.Errorf("agent '%s' has an empty skills entry", agent.Name)
}
}
seenInline := make(map[string]bool, len(agent.Skills.Inline))
for i := range agent.Skills.Inline {
inline := &agent.Skills.Inline[i]
if strings.TrimSpace(inline.Name) == "" {
return fmt.Errorf("agent '%s' has an inline skill with no name", agent.Name)
}
if strings.TrimSpace(inline.Description) == "" {
return fmt.Errorf("agent '%s' inline skill '%s' is missing a description", agent.Name, inline.Name)
}
if strings.TrimSpace(inline.Instructions) == "" {
return fmt.Errorf("agent '%s' inline skill '%s' is missing instructions", agent.Name, inline.Name)
}
if inline.Context != "" && inline.Context != "fork" {
return fmt.Errorf("agent '%s' inline skill '%s' has invalid context '%s' (only 'fork' is supported)", agent.Name, inline.Name, inline.Context)
}
if seenInline[inline.Name] {
return fmt.Errorf("agent '%s' has duplicate inline skill '%s'", agent.Name, inline.Name)
}
seenInline[inline.Name] = true
}
return nil
}
Loading
Loading