Open Agent Toolkit

Writing Skills

Use this page when you are creating or updating OAT skills in .agents/skills.

Skill behavior is defined by frontmatter plus the process contract in each SKILL.md. The goal is to make lifecycle behavior explicit, reviewable, and reusable across sessions.

Where Skills Live

  • Canonical skills live in .agents/skills
  • AGENTS.md is the session-facing registry and should stay aligned with skill frontmatter
  • OAT project and review artifacts should reference skill names consistently

Authoring Priorities

  • Make the mode and purpose explicit.
  • Keep prerequisites and expected artifacts concrete.
  • Spell out blocked vs allowed activities for state-advancing skills.
  • Define user-facing progress indicators for longer workflows.
  • Define a pre-work capability and authorization gate for skills that delegate to subagents, workers, or reviewers.
  • Keep output obligations explicit so downstream skills and users know what changed.

Contract components

  • Mode assertion (purpose, blocked/allowed activities)
  • Preconditions and required artifacts
  • User-facing progress indicator expectations
  • Delegation capability detection and fallback behavior, when the skill dispatches helper agents
  • Output obligations
  • Escalation/guardrail behavior

Frontmatter fields in active use

  • name
  • description
  • version
  • disable-model-invocation
  • user-invocable
  • allowed-tools
  • oat_gateable

Practical Authoring Flow

  1. Decide whether you are adding a general reusable skill or an OAT-specific lifecycle skill.
  2. Add or update the skill under .agents/skills/<name>/SKILL.md.
  3. Keep the AGENTS.md skills registry synchronized with the new frontmatter.
  4. Update related docs or lifecycle references if the skill changes user-visible behavior.

Governance rules

  • Prefer skill-first invocation language.
  • Keep AGENTS.md skills table synchronized with .agents/skills.
  • Require explicit user approval for destructive or state-advancing transitions.
  • Use create-oat-skill when the new skill belongs to an OAT lifecycle or maintenance flow.
  • Use create-agnostic-skill when you want a reusable workflow skill that is not OAT-specific.
  • Use existing lifecycle skills as examples for progress banners, prerequisites, and artifact updates.

Delegation-Capable Skills

Skills that dispatch subagents, workers, reviewers, or fresh-context helper sessions need a capability model before work starts. Do not assume delegation is available, and do not silently downgrade just because the runtime needs user authorization.

At minimum, the skill contract should:

  • Probe whether the host can dispatch the required helper role(s).
  • Distinguish available, authorization required, and not resolved.
  • Ask once at skill start when authorization is required, with the approval scope stated clearly.
  • Lock the selected tier for the run unless the user explicitly changes execution mode.
  • Stop before side effects if delegation is required for correctness and authorization remains unresolved.
  • Document the fallback path and any quality or independence tradeoff.

OAT-specific skills should compose with the internal oat-dispatch-subagents contract instead of copying provider selection and recovery rules. The caller still owns decomposition, user interaction, verification of load-bearing claims, cross-lane synthesis, and artifact writes. The dispatch contract owns capability and authorization checks, live catalog evidence, route/model/effort selection, launch acceptance, recovery, and the neutral dispatch record.

Keep project lifecycle policy out of that general layer. A lifecycle caller loads oat-project-dispatch-subagents to resolve project, phase/task, gate, write-boundary, commit, and worktree context, then passes a generic request to oat-dispatch-subagents. Analytical callers such as repository audits can use the general contract directly without requiring an active project. In either case, resolve the provider first and load exactly one provider-specific reference rather than merging provider mechanics into a universal catalog.

Use create-agnostic-skill or create-oat-skill as the starting point; both include the current delegation guidance and optional capability-detection template.

Gate-aware skills

A skill that supports configured final gates must declare oat_gateable: true in frontmatter and include a final Gate Execution step in its process contract. Without both pieces, a configured workflow.gates.skills.<skill> entry is a validation warning rather than an executable contract.

The Gate Execution step should:

  1. Run oat gate resolve <this-skill> --json.
  2. Treat null as "no gate configured."
  3. Export the resolved path with export PROJECT_PATH before launching the command shell.
  4. For oat gate review, require the configured command to include --project "$PROJECT_PATH"; do not append it at runtime, because the resolved command must execute exactly as configured.
  5. Run the resolved command as the skill's last step and use its exit code as the pass/fail signal.
  6. Follow the gate's onFailure policy:
    • block - remediate and rerun up to maxAttempts, then escalate.
    • prompt - surface the failure and ask the user.
    • warn - record the failure and continue.

For OAT review gates, prefer putting oat gate review --project "$PROJECT_PATH" "<prompt>" in the configured gate command rather than hard-coding a provider CLI directly in the skill. Use oat gate cross-provider-exec "<prompt>" for generic cross-runtime commands that should report only the child process status, not review findings. Reusable lifecycle gate commands must omit --target <id> so the dispatcher can avoid the current runtime; reserve explicit targets for manual/debug commands or deliberate local/user-specific overrides. See Workflow Gates for the config and command surface.

Reading project state

Skills that need fields from the active project's state.md (e.g. phase, phaseStatus, workflowMode, docsUpdated, lastCommit) MUST query the CLI instead of hand-parsing YAML with grep/awk.

For one field, use --field:

WORKFLOW_MODE=$(oat project status --field project.workflowMode 2>/dev/null || echo null)

If the skill is reading a resolved project path instead of the active project pointer, add --project-path:

WORKFLOW_MODE=$(oat project status --project-path "$PROJECT_PATH" --field project.workflowMode 2>/dev/null || echo null)

For multiple fields, use --shell so the CLI reads project state once and emits shell-safe assignments:

eval "$(oat project status --shell \
  PHASE=project.phase \
  PHASE_STATUS=project.phaseStatus \
  WORKFLOW_MODE=project.workflowMode 2>/dev/null)"

Skill snippets assume oat is available on PATH. Environments without a global install, including CI or cloud runners, can provide an oat shim backed by npx:

mkdir -p .oat/bin
cat > .oat/bin/oat <<'EOF'
#!/usr/bin/env bash
exec npx @open-agent-toolkit/cli "$@"
EOF
chmod +x .oat/bin/oat
export PATH="$PWD/.oat/bin:$PATH"

Create the shim once per checkout or CI job instead of putting command -v oat fallback branches in every skill. The same snippet also supports setups where oat is intentionally provided on PATH by npx.

The JSON output is a stable contract: the field set consumed by migrated skills is locked by MIGRATED_FIELDS in packages/cli/src/commands/project/status.test.ts, so removing or renaming any of those keys is a real test failure rather than a silent runtime break. See CLI Reference for the full locked field set.

Reference artifacts

  • .agents/skills/*/SKILL.md
  • AGENTS.md
  • .agents/skills/oat-project-implement/SKILL.md
  • .agents/skills/oat-project-complete/SKILL.md
  • .agents/skills/oat-project-review-receive/SKILL.md

On this page