Skip to content

Skills ​

A skill tells the worker what to do with a unit. Every skill is an Agent Skills folder with a SKILL.md, so one folder works for Claude Code, Codex and Cursor.

Built-in skills ​

SkillForDelivers
fixProblems listed on an item: your PR's failing checks or unresolved threads, threads you started on an awaywork PR, a task back from QA, an error from a custom source, or this run's review in a loopdraft PR
implementAn issue or task with no PR, or in progress with yours: the unambiguous next steps, or questions when it's too vaguedraft PR
reviewA PR awaiting your review, or this run's work as a then step. Posts nothing unless deliver: pending_review leaves it as your pending review. Verdict approve, comments or changes; line comments to review.json under pending_reviewreport
planA task with no PR: a plan, no code, or questions when no reading is likely enough; under deliver: plan, tasks in plan.yml (Plans)report
judgeA gate's judge of commits or a plan's or report's files: verdict fires when a human must decide, one line per decisionreport
syncYour PR behind its base: awaywork merges and pushes, a model only on conflicts: true; needs deliver: push (sync)push
sh
awaywork skills list   # every skill a rule can name, and where it comes from

The skill decides the work; the harness around it decides the limits (no forge access, targeted tests only, no background commands, when to stop), which no skill lifts.

Run one on an item no rule takes ​

No rule matches an item, and you want fix on it this once. --skill runs a skill on that item, foreground or --detach; the /away pane's g on a no rule card does the same:

$ awaywork run --unit CH-1 --skill review --detach
Started CH-1 as run 20261109-112811; awaywork report 20261109-112811 shows it once it ends

It's a one-off rule, last in the queue, for that run alone: plan doesn't show it and other items read as without it. A real rule always wins, so --skill refuses an item one takes (--skill is only for an item no rule takes: #1 plan takes CH-3), and the usual refusals hold (deliver draft_pr: feature-7 is someone else's PR).

  • Any listed skill but judge (a gate) and sync (needs deliver: push).
  • No on_done, deliver, wait, thread or gates of its own (top-level gates still apply): a draft PR on a new away/ branch, or a report.
  • Nothing on the item: no comment, move or thread. What a rule would post, questions included, stays in the report; a draft's .away/out/ files still go to your store.
  • Nothing goes on from it: after continue or ask you finish the draft or report by hand, and the attempt counts for no rule.

Where skills come from ​

A name, a rule's or in skills, is looked up in order:

  1. skills/<name>/ next to your config, yours (~/.config/awaywork/skills/ by default);
  2. .awaywork/skills/<name>/ in the unit's repo, the team's;
  3. the built-ins.

Extra skills also fall back to the repo's own skill folders. Claude Code gets the unit's skills as a plugin outside the clone (--plugin-dir), named awaywork:<name>, on every unit, someone else's PR included; Cursor the same plugin through its --plugin-dir; Codex a copy in the clone's .agents/skills/, where a repo skill of the same name wins except on someone else's PR.

Details:

  • A config outside ~/.config/awaywork/ (--config, AWAYWORK_CONFIG) uses its own skills/; doctor warns while the default folder holds skills it doesn't use.
  • On someone else's PR, .awaywork/skills/ is the author's and skipped.
  • A skill from ~/.claude/skills/: link it in, ln -s ~/.claude/skills/<name> ~/.config/awaywork/skills/<name>.
  • On a runner or in a container, yours are the copy schedule install puts in .awaywork/skills/.
  • doctor sees user and built-in skills only, so it warns about repo skills, which resolve at run time; a skill found nowhere fails the unit with unknown skill.

Tweak a built-in ​

sh
awaywork skills eject fix
$EDITOR ~/.config/awaywork/skills/fix/SKILL.md

The ejected copy shadows the built-in from then on.

Bring your repo's skills ​

Your repo has skills for Claude Code, Codex or Cursor? Name them in a rule scoped to that repo, no copying:

yaml
queue:
  - skill: implement
    skills: [tdd-implementation, writing-migrations]
    where: { type: issue, repo: acme/api }

An extra skill that isn't yours, in .awaywork/skills/ or built in, is looked up in the clone's .claude/skills/, .agents/skills/, then .cursor/skills/. They mostly matter for Codex, which gets a copy: Claude Code loads .claude/skills/ itself, and Cursor all three.

Skills the model reaches for itself port fine: a TDD loop, a migration checklist, a tricky domain's rules. Workflow skills don't: one that waits for your approval, uses gh, or is disable-model-invocation has nobody to approve, no forge login, or can't be called. Keep the built-in and port the standard instead, say an instruction file saying "review against .claude/skills/pr-review/references/review-standard.md".

Details:

  • Not in top-level skills: that joins every unit, other repos and others' PRs too, where it fails with unknown skill.
  • They only fill in names: a rule's main skill never comes from them, so .claude/skills/implement doesn't replace the built-in. Move one to .awaywork/skills/ to make it a main skill.
  • Like .awaywork/skills/, skipped on someone else's PR, where naming one fails with unknown skill.
  • They resolve at run time, so doctor warns.

Try an edit on a past unit ​

sh
awaywork replay 20261006-020000 acme-api-1

Replay rebuilds a past unit's first step, a clone at its start commit with its saved context, and runs it again, so you see what an edited skill or instruction does. The files are read fresh: each SKILL.md, instruction file and repo and provider config. The unit is as recorded: its skills and its rule's model, with and instructions list, so config edits to those change nothing.

It runs the provider and model once, outside the quota ceilings, under your isolation, and never pushes or opens a PR. The clone stays on the unit's branch under replays/ in the run's state dir (its own /tmp/awaywork-* under user), with the worker's out/ beside it. Fetch the branch into your checkout to look (git fetch <clone> away/acme-api-1), which runs nothing of it.

Details:

  • The context is the post-run copy, so files the worker edited under context/ replay as edited. Setup gets no repo secrets; MCP servers don't start.
  • Only a unit's first step, on its rule's first model. A later then step starts from commits only the chain's clone had, and records no replay.json, nor does a retry, a unit that never started, or one from before replay existed.
  • The clone is still the worker's, .git included: read its files, but run no git in it, not even git status, git diff or git log, nor its scripts. A worker's submodule config runs its core.fsmonitor on your git status, as would the clone's core.pager if the replay couldn't restore its config; an .envrc, a package.json script or editor tasks run as you too, what user and container prevent.
  • awaywork restores .git/config and removes .git/hooks, nothing more, unless the clone is lost, and prints where it stays, even on failure.

Write your own ​

~/.config/awaywork/skills/triage-flaky/
  SKILL.md
markdown
---
name: triage-flaky
description: Find out why a test is flaky and make it deterministic. Use when a PR's only failing check is a known flaky test.
metadata:
  deliver: draft_pr
---

# Triage a flaky test

The unit is a pull request whose failing check is a test that passes on
retry. ...

In the PR body, name the test, the cause you found, and the command you
ran 20 times to show it no longer fails.

Then name it in a rule:

yaml
queue:
  - skill: triage-flaky
    where: { type: pr, mine: true, checks: failing, label: flaky }

Say what the output must state, as the built-ins do: what was checked, how, and what couldn't be. A skill that proves a change, with the gates that check what it left, is worked through in Proving a change. The context is in .away/context/<item>/: item.md always, diff.patch, threads.md and checks.md for a PR, and for a plan's task the contract in contract/ and what other repos' tasks ship in shipped/<task>/ (Across repos).

Frontmatter and rule keys:

  • metadata.deliver: report makes it report; otherwise it commits, where the rule's deliver says.
  • metadata.type, a conventional-commit type (feat, fix, docs, refactor, perf, test, build, ci, chore, style, revert), fills {type} in a branch name: {type}/{key}-{title} gives feat/ENG-12-login-with-sso for implement. The chain's first committing skill decides; otherwise chore.
    • It's read at plan time, before the clone, so a repo's .awaywork/skills/ can't set it: a repo skill replacing a built-in gets the built-in's type, others chore.
  • metadata.merge: base has awaywork merge the PR's base itself before setup and push it, as sync does; the body matters only on a conflict, for the worker resolving it, which can edit or delete the merge's files but not add one. The rule must be one step with deliver: push, else it's skipped.
  • with: { key: value } on a rule or step lists values in the prompt's Parameters section (multi-line values JSON-quoted on one line). Document them under metadata, e.g. metadata: { depth: "quick or thorough" }; awaywork doesn't check them.
  • skills: [name] on a rule, or top-level, adds skills the agent may call.

What a skill writes in .away/out/:

  • status: one word when a step stops, as the harness asks: done, ask or continue. A skill writing none works as before (Questions, Continue).
  • verdict: one lowercase word, like pass, shown in the report and matched by a loop's until.
  • review.json: line comments, [{ "path": "src/limit.ts", "line": 42, "body": "Burst should come from config." }], line on the new side, at most 100 comments and 100 KB. Under pending_review they land on lines the diff adds or shows, report.md the body; if GitHub refuses one, all go in the body, and an invalid file is dropped, the result saying why. Otherwise unread.
  • questions.md, with ask in status: plain text (Jira and Asana show no Markdown), at most 30 KB. awaywork posts it on your item and waits (Questions); the next item.md shows it under awaywork's questions, replies after. Without ask, a committing step went on and nobody reads it.
  • plan.yml: asked for under deliver: plan, in the shape the prompt spells out (the planned rules' skills, the repos a task may name, task ids a closed PR retired), optionally with a contract file; any skill can write it. awaywork checks it and posts it with report.md (Plans); the next item.md shows it under awaywork's plan.
  • Anything else but a plan's contract is kept with the run and reaches notify as $AWAYWORK_OUT_DIR, unread by awaywork. Treat it as untrusted: the model wrote it after reading others' text. It also holds earlier chain steps' files, so check $AWAYWORK_SKILL first.