Skip to content

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 rules

The 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:

FieldRequiredDescription
nameUnique identifier (letters, numbers, hyphens, underscores)
descriptionWhat this capability provides
versionAuto-managed on save
allowOverridetrue allows user/project tiers to override a bundled skill consumed by 2+ roles (default: false, emits a warning when overridden)
tagsFreeform labels for /skills list filtering

Example:

markdown
---
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:

FieldRequiredDescription
nameUnique identifier
descriptionWhat this role does
agent_typeexplorer, coder, reviewer, tester, general-purpose, custom
versionAuto-managed on save
modelPreferred model (falls back to session default)
when_to_useHint for the coordinator when choosing which agent to spawn
output_fileUsed by /init explorer roles to name their output file

Example:

markdown
---
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 (full CrewV2 / CrewAgent schemas, all bundled crews, activator lifecycle, topology semantics, the crew_match / skill_applied spawn-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:

FieldRequiredDescription
nameUnique crew name
descriptionWhat this crew does
topologysilent · hub · mesh
workflowWorkflow name; use freeform for no specific workflow
workflowsMap of slash-command name → workflow name. Per-command overrides applied while this crew is active (see below).
coordinator.roleRole name for the coordinator agent
coordinator.modelModel override for the coordinator
agents[].roleRole name for this crew member
agents[].skillsAtomic skills to compose into this agent's prompt
agents[].paralleltrue → run alongside other parallel members
agents[].afterall or [role-name, …] → wait for those agents first
agents[].modelModel override for this member
synthesisOptional final agent that runs after all other agents finish
versionAuto-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.

yaml
---
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) in src/skills/workflowResolver.ts. If the active crew maps the command, that workflow is used; otherwise the command falls back to its bundled default (/featurefeature-pipeline, /code-reviewcode-review, /initinit-investigation, /researchadversarial-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.
  • /init caveat. Workflow resolution runs after the /init subcommand 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:

TopologyBehaviorBest for
silentAgents work independentlyParallel reviewers, independent analysis
hubAll communication goes through the coordinatorStructured multi-step workflows
meshAgents can message each other directlyCollaborative 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:

FieldRequiredDescription
nameUnique workflow name
descriptionOne-line summary
stages[].nameStage label
stages[].agentsAgent role names expected in this stage
stages[].parallelWhether this stage runs in parallel
stages[].afterDependency stages
stages[].gatesApproval gates before proceeding
stages[].artifactFilename (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_requiredMinimum 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:

yaml
---
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 without artifact: 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. If workers_required > 0 and the count is zero, the stage is retried with an explicit spawn_worker instruction so a stage that's supposed to fan out (e.g. four parallel researchers) can't silently collapse into single-agent prose. Stages without workers_required (or with 0) 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:

markdown
---
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 readability

Where 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:

markdown
---
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:

markdown
---
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:

markdown
---
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

bash
/crew docs-crew

The 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-conventions

To deactivate and return to defaults:

bash
/crew deactivate

To see all available crews (bundled + user + project):

bash
/crew list

Composition Resolution

When the coordinator calls spawn_worker, agents-fleet calls SkillRegistry.composeAgentPrompt(roleName, skillNames), which assembles the worker's system prompt in this format:

xml
<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:

PriorityTierLocation
1 (lowest)BundledShipped with the CLI (src/skills/bundled/)
2User~/.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)

NameTopologyWorkflowPurpose
agents-fleet-crewhubplanningDefault dev crew for agents-fleet itself
code-review-crewsilentcode-review4 parallel reviewers (security, correctness, architecture, performance)
research-crewhubadversarial-researchParallel researchers + adversarial critic
init-crewhubinit-investigation7 domain explorers + critic for /init

Skills (6)

NamePurpose
agents-fleet-conventionsTypeScript/ESM/branded-types rules for agents-fleet dev
coordinator-worker-architectureCoordinator/worker design patterns
prepared-statements-patternSQLite prepared-statement conventions
react-ink-patternsReact + Ink terminal UI patterns
security-audit-checklistSecurity review checklist
vitest-patternsVitest testing patterns and anti-patterns

Workflows (6)

NamePurpose
freeformNo structured stages — coordinator acts freely
planningAdversarial research → DoD → task breakdown → execution
adversarial-researchParallel research + counter-researcher + critic
code-reviewParallel review → coordinator synthesis
feature-pipelineResearch → design → implement → verify
init-investigationDomain 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:

bash
agents-fleet skills migrate

The 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.