Skills Composition — Worked Example
How to build custom crews by composing skills into roles and roles into crews.
Overview
agents-fleet v0.9.0 uses a 4-artifact composition model. Instead of writing one monolithic agent prompt, you split responsibilities across four focused file types and let the registry assemble them at runtime:
Skills (*.skill.md) — Reusable knowledge / capability fragments
↓ composed into
Roles (*.role.md) — Agent personas (who the worker is)
↓ referenced by
Crews (*.crew.md) — Team definitions (who works together)
↑ optionally guided by
Workflows (*.workflow.md) — Stage-by-stage orchestration rulesThe same docs-conventions skill can be injected into a doc-writer, a doc-reviewer, and any future role — one source of truth, no copy-paste drift.
The Building Blocks
Skills (*.skill.md)
A skill is a reusable knowledge fragment that can be composed into one or more roles. It has no agent_type — it is not an agent on its own. Think of it as a shared style guide, a checklist, or domain expertise that multiple agents need.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | ✅ | Unique identifier (letters, numbers, hyphens, underscores) |
description | ✅ | What this capability provides |
version | ❌ | Auto-managed on save |
allowOverride | ❌ | true allows user/project tiers to override a bundled skill consumed by 2+ roles (default: false, emits a warning when overridden) |
tags | ❌ | Freeform labels for /skills list filtering |
Example:
---
name: docs-conventions
description: Shared documentation formatting standards for this project
allowOverride: false
tags: [docs, conventions, markdown]
---
## Documentation Standards
All documentation files must follow these conventions:
- Use `##` for top-level sections (never `#` inside a document body)
- Code blocks always declare a language (`bash`, `ts`, `yaml`, `markdown`)
- Every public API must have a **Purpose**, **Parameters**, and **Example** section
- Prefer active voice ("Returns X" not "X is returned")
- Table headers use sentence case (not Title Case)
- Emoji are allowed in callout blocks only (`> **Note:** …`)Roles (*.role.md)
A role defines who the worker is — its persona, the type of agent it becomes, and optionally a preferred model. A role's body is its complete system prompt. At spawn time, the registry wraps it in <role name="…">…</role> and appends any requested skills under a ## Capabilities section.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | ✅ | Unique identifier |
description | ✅ | What this role does |
agent_type | ✅ | explorer, coder, reviewer, tester, general-purpose, custom |
version | ❌ | Auto-managed on save |
model | ❌ | Preferred model (falls back to session default) |
when_to_use | ❌ | Hint for the coordinator when choosing which agent to spawn |
output_file | ❌ | Used by /init explorer roles to name their output file |
Example:
---
name: doc-writer
description: Writes project documentation following the shared docs-conventions
agent_type: coder
when_to_use: When creating or updating markdown documentation files
---
You are a **Documentation Writer** agent. Your job is to produce clear, accurate,
and well-structured documentation for this project.
## Approach
1. Read existing docs in `docs/` to understand tone and structure before writing.
2. Follow all conventions (see your Capabilities section).
3. Write for a developer who is new to this codebase — assume no prior context.
4. Cross-link related documents where helpful.
## Output
Return the completed markdown content ready to save. Do not include meta-commentary
about what you did — output the document itself.Crews (*.crew.md)
See also:
docs/crews-reference.md— the authoritative reference for crews (fullCrewV2/CrewAgentschemas, all bundled crews, activator lifecycle, topology semantics, thecrew_match/skill_appliedspawn-response contract, and the SEC-4 project-tier role clamp). This section covers the file shape from a composition perspective; the reference covers runtime behavior and operator surface.
A crew wires a coordinator, a roster of agents, and an optional workflow into a named team. Each agent entry declares a role (its persona) and optional skills (atomic knowledge to compose in). Crews live in crews/ and use the .crew.md suffix.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | ✅ | Unique crew name |
description | ✅ | What this crew does |
topology | ✅ | silent · hub · mesh |
workflow | ✅ | Workflow name; use freeform for no specific workflow |
workflows | ❌ | Map of slash-command name → workflow name. Per-command overrides applied while this crew is active (see below). |
coordinator.role | ✅ | Role name for the coordinator agent |
coordinator.model | ❌ | Model override for the coordinator |
agents[].role | ✅ | Role name for this crew member |
agents[].skills | ❌ | Atomic skills to compose into this agent's prompt |
agents[].parallel | ❌ | true → run alongside other parallel members |
agents[].after | ❌ | all or [role-name, …] → wait for those agents first |
agents[].model | ❌ | Model override for this member |
synthesis | ❌ | Optional final agent that runs after all other agents finish |
version | ❌ | Auto-managed on save |
Per-command workflow overrides (workflows:)
The top-level workflow: field names the default workflow this crew uses (and the one injected into the coordinator's <active-workflow> block for freeform work). The optional workflows: map lets you swap which workflow a specific slash command runs while this crew is active, without forking the command handler.
---
name: strict-docs-crew
description: Docs crew that runs a hardened review pipeline under /code-review
topology: hub
workflow: freeform
workflows:
feature: my-feature-pipeline # /feature uses my-feature-pipeline here
code-review: strict-review # /code-review uses strict-review here
coordinator:
role: default-coordinator
agents:
- role: doc-writer
---Rules:
- Keys are slash-command names in kebab-case, matching
/^[a-z][a-z0-9-]*$/(e.g.feature,code-review,init,research). Unknown commands are ignored. - Values are workflow names. The referenced workflow is resolved at command-invocation time (not at crew parse time), so workflows added later by the user or project tier still resolve correctly.
- Resolution is performed by
resolveCommandWorkflow(registry, activator, commandName, bundledDefault)insrc/skills/workflowResolver.ts. If the active crew maps the command, that workflow is used; otherwise the command falls back to its bundled default (/feature→feature-pipeline,/code-review→code-review,/init→init-investigation,/research→adversarial-research). - Misconfiguration is loud. A typo or a value pointing at a workflow that doesn't exist surfaces as a hard error — it does not silently fall back to the bundled default — so broken overrides can't masquerade as default behavior.
- Banner + warning. The resolved workflow name is printed in the command banner, and a yellow ⚠ warning is emitted whenever a crew-level override is active so users can see at a glance that they're not running the default.
/initcaveat. Workflow resolution runs after the/initsubcommand router (status/scopes/load), so those read-only paths over.fleet/context/don't surface workflow errors that have nothing to do with their job.
Topology quick reference:
| Topology | Behavior | Best for |
|---|---|---|
silent | Agents work independently | Parallel reviewers, independent analysis |
hub | All communication goes through the coordinator | Structured multi-step workflows |
mesh | Agents can message each other directly | Collaborative tasks requiring peer coordination |
Workflows (*.workflow.md)
A workflow gives the coordinator a stage-by-stage execution plan. When a crew declares workflow: planning, the coordinator's system prompt gains an <active-workflow> block containing the workflow's stage list and its full prose body. Agents and their spawning order are still controlled by the crew; the workflow only governs coordinator behavior.
Frontmatter fields:
| Field | Required | Description |
|---|---|---|
name | ✅ | Unique workflow name |
description | ✅ | One-line summary |
stages[].name | ❌ | Stage label |
stages[].agents | ❌ | Agent role names expected in this stage |
stages[].parallel | ❌ | Whether this stage runs in parallel |
stages[].after | ❌ | Dependency stages |
stages[].gates | ❌ | Approval gates before proceeding |
stages[].artifact | ❌ | Filename (relative to the plan dir) the stage is expected to produce. Used by workflow-driven runners for a missing-artifact retry. Authored as artifact:. |
stages[].workers_required | ❌ | Minimum number of new parallel workers this stage must spawn. When > 0, runners may retry the stage with an explicit spawn_worker instruction if no new workers were spawned during the run. Authored as workers_required: (snake_case); workersRequired: (camelCase) is also accepted by the parser. |
The body is plain Markdown prose injected verbatim into the coordinator prompt.
artifact and workers_required — retry hints for workflow runners
Stages that are executed by a workflow-driven runner (currently /feature, which iterates over the resolved workflow's stages[] instead of owning its own phase list) can advertise two retry hints:
---
name: feature-pipeline
description: Brainstorm → requirements → research → design → validation → tasks
stages:
- name: brainstorm
artifact: 01-brainstorm.md
- name: requirements
artifact: 02-requirements.md
after: [brainstorm]
- name: research
artifact: 03-research.md
after: [requirements]
parallel: true
workers_required: 4
- name: design
artifact: 04-technical-design.md
after: [research]
parallel: true
workers_required: 3
- name: validation
artifact: 05-validation.md
after: [design]
parallel: true
workers_required: 3
- name: tasks
artifact: 06-tasks.md
after: [validation]
---How the runner uses these fields:
artifact:→ missing-artifact retry. After the stage finishes, the runner checks for the named file in the plan directory. If it's absent, the stage is retried with an explicit "write to<artifact>" instruction so the coordinator can recover from a worker that produced prose in the transcript but never wrote a file. Stages withoutartifact:are not validated.workers_required: N→ no-workers-spawned retry. After the stage finishes, the runner counts how many new workers were spawned during it. Ifworkers_required > 0and the count is zero, the stage is retried with an explicitspawn_workerinstruction so a stage that's supposed to fan out (e.g. four parallel researchers) can't silently collapse into single-agent prose. Stages withoutworkers_required(or with0) don't trigger this retry.
These hints live on the workflow file, so editing the workflow (or its sibling per-stage prompt files at <workflow-name>/<stage>.prompt.md) changes runner behavior without touching the command handler. Stages without either field behave exactly as before — both retries are opt-in.
Authoring note. The hand-rolled frontmatter parser accepts the snake_case form (workers_required:) used throughout the bundled workflows as well as camelCase (workersRequired:) as belt-and-braces. Integers, booleans, and inline arrays are coerced inside nested list-item properties, so workers_required: 4 parses as a number, not the string "4".
Worked Example: Building a Documentation Crew
We'll build a three-file crew: one shared skill, two roles that use it, and a crew that ties them together.
Step 1 — Create a shared skill
Create .fleet/skills/docs-conventions.skill.md in your project:
---
name: docs-conventions
description: Shared documentation formatting standards for this project
allowOverride: false
tags: [docs, conventions, markdown]
---
## Documentation Standards
All documentation files must follow these conventions:
- Use `##` for top-level sections (never `#` inside a document body)
- Code blocks always declare a language (`bash`, `ts`, `yaml`, `markdown`)
- Every public API must have a **Purpose**, **Parameters**, and **Example** section
- Prefer active voice ("Returns X" not "X is returned")
- Table headers use sentence case (not Title Case)
- Emoji are allowed in callout blocks only (`> **Note:** …`)
- Keep line length under 100 characters for diff readabilityWhere to put it:
.fleet/skills/(project tier, committed to git) makes it available to everyone on the team.~/.fleet/skills/(user tier) is personal.
Step 2 — Create the writer role
Create .fleet/roles/doc-writer.role.md:
---
name: doc-writer
description: Writes project documentation following the shared docs-conventions
agent_type: coder
when_to_use: When creating or updating markdown documentation files
---
You are a **Documentation Writer** agent. Your job is to produce clear, accurate,
and well-structured documentation for this project.
## Approach
1. Read existing docs in `docs/` to understand tone and structure before writing.
2. Follow all formatting rules in your Capabilities section exactly.
3. Write for a developer who is new to this codebase — assume no prior context.
4. Cross-link related documents with relative paths where helpful.
## Output
Return the completed markdown content ready to save. Do not include meta-commentary
about what you changed — output the document itself.Step 3 — Create the reviewer role
Create .fleet/roles/doc-reviewer.role.md:
---
name: doc-reviewer
description: Reviews documentation for accuracy, completeness, and standards compliance
agent_type: reviewer
when_to_use: When reviewing documentation changes before merge
---
You are a **Documentation Reviewer** agent. Your job is to verify that documentation
is accurate, complete, and adheres to project standards.
## Review Checklist
For every document you review, check:
- [ ] All facts are accurate and up to date with the codebase
- [ ] No steps are missing from procedures (try to follow them mentally)
- [ ] All formatting rules in the Capabilities section are followed
- [ ] Cross-links resolve to real files
- [ ] Code blocks are syntactically correct and runnable
## Output
Return a structured report with these headings:
## Review: <document name>
**Status:** APPROVED | CHANGES REQUESTED
### Issues (if any)
- Line N: <issue>
### Suggestions (optional)
- <improvement>Step 4 — Create the crew
Create .fleet/crews/docs-crew.crew.md:
---
name: docs-crew
description: Writes and reviews project documentation with shared formatting standards
topology: hub
workflow: freeform
coordinator:
role: default-coordinator
agents:
- role: doc-writer
skills: [docs-conventions]
parallel: true
- role: doc-reviewer
skills: [docs-conventions]
after: all
version: 1
---
# docs-crew
Generates documentation with `doc-writer` (parallel), then runs `doc-reviewer`
once all writing is done. Both agents share `docs-conventions` for consistent
formatting.What the after: all does: doc-reviewer waits for every parallel: true agent to complete before it starts. Add more writers with parallel: true and they all run concurrently; the reviewer always sees their finished output.
Step 5 — Activate the crew
/crew docs-crewThe coordinator's roster updates immediately. On the next task, spawn workers using agent types from your new roles:
# The coordinator can now spawn:
# agent_type: coder → doc-writer + docs-conventions
# agent_type: reviewer → doc-reviewer + docs-conventionsTo deactivate and return to defaults:
/crew deactivateTo see all available crews (bundled + user + project):
/crew listComposition Resolution
When the coordinator calls spawn_worker, agents-fleet calls SkillRegistry.composeAgentPrompt(roleName, skillNames), which assembles the worker's system prompt in this format:
<role name="doc-writer">
You are a Documentation Writer agent…
[full role body]
</role>
## Capabilities
<skill name="docs-conventions">
## Documentation Standards
All documentation files must follow these conventions…
[full skill body]
</skill>That single string becomes definition.systemPrompt for the spawned worker.
Three-tier override priority
The registry loads artifacts from three sources, each overriding the previous by name:
| Priority | Tier | Location |
|---|---|---|
| 1 (lowest) | Bundled | Shipped with the CLI (src/skills/bundled/) |
| 2 | User | ~/.fleet/{roles,skills,workflows,crews}/ |
| 3 (highest) | Project | .fleet/{roles,skills,workflows,crews}/ |
A project-tier doc-writer.role.md silently replaces the bundled doc-writer role for everyone who uses the project. User-tier files override bundled but yield to project. This allows teams to customize bundled roles without forking the CLI.
Override guard for shared skills: Bundled skills consumed by two or more roles emit a [SkillRegistry] OVERRIDE WARNING when a user/project tier overrides them, unless the bundled skill sets allowOverride: true. This prevents silent behavioral drift across multiple workers that share the skill.
Bundled Assets Reference
agents-fleet ships with the following built-in artifacts (all in src/skills/bundled/):
Crews (4)
| Name | Topology | Workflow | Purpose |
|---|---|---|---|
agents-fleet-crew | hub | planning | Default dev crew for agents-fleet itself |
code-review-crew | silent | code-review | 4 parallel reviewers (security, correctness, architecture, performance) |
research-crew | hub | adversarial-research | Parallel researchers + adversarial critic |
init-crew | hub | init-investigation | 7 domain explorers + critic for /init |
Skills (6)
| Name | Purpose |
|---|---|
agents-fleet-conventions | TypeScript/ESM/branded-types rules for agents-fleet dev |
coordinator-worker-architecture | Coordinator/worker design patterns |
prepared-statements-pattern | SQLite prepared-statement conventions |
react-ink-patterns | React + Ink terminal UI patterns |
security-audit-checklist | Security review checklist |
vitest-patterns | Vitest testing patterns and anti-patterns |
Workflows (6)
| Name | Purpose |
|---|---|
freeform | No structured stages — coordinator acts freely |
planning | Adversarial research → DoD → task breakdown → execution |
adversarial-research | Parallel research + counter-researcher + critic |
code-review | Parallel review → coordinator synthesis |
feature-pipeline | Research → design → implement → verify |
init-investigation | Domain exploration for codebase profiling |
Roles (32)
Bundled roles include generic defaults (default-coder, default-reviewer, default-tester, default-explorer, default-coordinator, default-general-purpose), agents-fleet-specific specialists (fleet-coder, fleet-tester, fleet-architect, fleet-security-reviewer, fleet-correctness-reviewer, fleet-explorer), research roles (primary-researcher, counter-researcher, edge-case-researcher, context-researcher, research-critic, research-coordinator), code review roles (security-reviewer, correctness-reviewer, architecture-reviewer, performance-reviewer, test-reviewer), and /init explorer roles (init-api-explorer, init-arch-explorer, init-build-explorer, init-critic, init-decision-explorer, init-dep-explorer, init-pattern-explorer, init-security-explorer).
Use any bundled role by name in your crew's agents[].role field — no extra configuration required.
Migration from v1
If you have v1 *.md files in ~/.fleet/skills or .fleet/skills, run:
agents-fleet skills migrateThe command classifies each v1 file as a role or atomic skill heuristically and writes the v2 equivalent (*.role.md / *.skill.md). Originals are renamed to *.v1.md for safety. Bundled v1 files were swept during the v2 release; only user and project tiers need migration.
See also: docs/crews-getting-started.md for the quick-start crew reference.