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
| Skill | For | Delivers |
|---|---|---|
fix | Problems 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 loop | draft PR |
implement | An issue or task with no PR, or in progress with yours: the unambiguous next steps, or questions when it's too vague | draft PR |
review | A 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_review | report |
plan | A 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 |
judge | A gate's judge of commits or a plan's or report's files: verdict fires when a human must decide, one line per decision | report |
sync | Your PR behind its base: awaywork merges and pushes, a model only on conflicts: true; needs deliver: push (sync) | push |
awaywork skills list # every skill a rule can name, and where it comes fromThe 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 endsIt'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) andsync(needsdeliver: push). - No
on_done,deliver,wait, thread or gates of its own (top-level gates still apply): a draft PR on a newaway/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 yourstore. - Nothing goes on from it: after
continueoraskyou 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:
skills/<name>/next to your config, yours (~/.config/awaywork/skills/by default);.awaywork/skills/<name>/in the unit's repo, the team's;- 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 ownskills/;doctorwarns 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 installputs in.awaywork/skills/. doctorsees user and built-in skills only, so it warns about repo skills, which resolve at run time; a skill found nowhere fails the unit withunknown skill.
Tweak a built-in
awaywork skills eject fix
$EDITOR ~/.config/awaywork/skills/fix/SKILL.mdThe 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:
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 withunknown skill. - They only fill in names: a rule's main skill never comes from them, so
.claude/skills/implementdoesn'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 withunknown skill. - They resolve at run time, so
doctorwarns.
Try an edit on a past unit
awaywork replay 20261006-020000 acme-api-1Replay 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 reposecrets; MCP servers don't start. - Only a unit's first step, on its rule's first model. A later
thenstep starts from commits only the chain's clone had, and records noreplay.json, nor does a retry, a unit that never started, or one from before replay existed. - The clone is still the worker's,
.gitincluded: read its files, but run no git in it, not evengit status,git difforgit log, nor its scripts. A worker's submodule config runs itscore.fsmonitoron yourgit status, as would the clone'score.pagerif the replay couldn't restore its config; an.envrc, apackage.jsonscript or editor tasks run as you too, whatuserandcontainerprevent. - awaywork restores
.git/configand 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---
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:
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: reportmakes it report; otherwise it commits, where the rule'sdeliversays.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}givesfeat/ENG-12-login-with-ssoforimplement. The chain's first committing skill decides; otherwisechore.- 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, otherschore.
- It's read at plan time, before the clone, so a repo's
metadata.merge: basehas awaywork merge the PR's base itself before setup and push it, assyncdoes; 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 withdeliver: 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 undermetadata, 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,askorcontinue. A skill writing none works as before (Questions, Continue).verdict: one lowercase word, likepass, shown in the report and matched by a loop'suntil.review.json: line comments,[{ "path": "src/limit.ts", "line": 42, "body": "Burst should come from config." }],lineon the new side, at most 100 comments and 100 KB. Underpending_reviewthey land on lines the diff adds or shows,report.mdthe body; if GitHub refuses one, all go in the body, and an invalid file is dropped, the result saying why. Otherwise unread.questions.md, withaskinstatus: plain text (Jira and Asana show no Markdown), at most 30 KB. awaywork posts it on your item and waits (Questions); the nextitem.mdshows it underawaywork's questions, replies after. Withoutask, a committing step went on and nobody reads it.plan.yml: asked for underdeliver: plan, in the shape the prompt spells out (theplannedrules' 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 withreport.md(Plans); the nextitem.mdshows it underawaywork'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_SKILLfirst.