From task-agent
Reads a YAML tasks file and performs one pending task per invocation: clones a GitHub repo, makes changes, commits, and opens a PR. Supports unified and legacy file formats.
How this skill is triggered — by the user, by Claude, or both
Slash command
/task-agent:task-agentopusThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Each invocation does **exactly one task**: the next pending item from the tasks file.
Each invocation does exactly one task: the next pending item from the tasks file. The same file (or a sibling state file, in legacy mode) tracks completion, so reruns always pick up where the previous run left off.
Arguments (all optional, key=value form):
tasks=<path> — path to the tasks file. Default: agent-tasks.yml or agent-tasks.yaml in cwd.state=<path> — path to a separate state file. Forces legacy mode. Default: auto-derived (<stem>-state.yml) and only used if the tasks file is in legacy format.Two file formats are supported:
id, a description, and a status (pending | done | failed | skipped). On completion the same entry gains branch, pr_url, date. Unknown keys (e.g. external_ref, labels, priority) are preserved verbatim across rewrites so external sync tools can attach metadata.*-state.yml listing completed entries. Auto-detected when the tasks file has no status: field. Existing setups keep working unchanged.See ../../references/format.md for the full schema, the status enum, and the passthrough-metadata contract.
Prereqs: python3, git.
Before doing any work, call TaskCreate for each phase below. Call TaskUpdate (status in_progress) when you begin a phase and TaskUpdate (status completed) when you finish it.
Parse tasks=<path> and state=<path> from the invocation arguments. If tasks= is not
given, search the current directory for agent-tasks.yml then agent-tasks.yaml. Stop and
show the expected format if no file is found.
All YAML reading and writing goes through the helper script
${CLAUDE_PLUGIN_ROOT}/scripts/tasks_io.py, which guarantees that unknown keys on task
entries (e.g. external_ref, labels) survive every round-trip. The script's contract:
status: field. The tasks file is the single source of truth; no state file is read.status). The script
reads the state file from the explicit state=<path> argument if given, else from
<tasks-stem>-state.yml next to the tasks file. A missing state file is treated as empty.{repo, id, description, status, ...}.
Missing ids are synthesized as md5(repo + "\n" + description)[:6]; missing statuses
default to done (legacy task whose (repo, description) is in state.completed) or
pending.Invoke it and capture the JSON:
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/tasks_io.py" load "$TASKS_PATH" \
${STATE_PATH:+--state "$STATE_PATH"}
Remember the mode field from the output — Phase 4 needs it to write back in the same
format.
Walk the normalized list in order and pick the first task whose status == 'pending'.
Tasks with status of done, failed, or skipped are passed over silently (failures
and skips were intentional outcomes of earlier runs).
If nothing is pending, tell the user — all done, nothing left to do.
Print the chosen task clearly before proceeding:
Today's task:
Repo: owner/repo-name
ID: abc123
Task: "Add unit tests for the authentication module"
Only clone the one repo containing today's task if not already cloned locally.
Each repo lives under a task-agent directory in the OS temp dir (so the local clone
is reused across runs and Windows/Linux/macOS all work). The exact base directory is:
WORKDIR="${TASK_AGENT_WORKDIR:-$(python3 -c 'import os,tempfile;print(os.path.join(tempfile.gettempdir(),"task-agent"))')}"
Override with the TASK_AGENT_WORKDIR env var if you need a different location (e.g. a
persistent disk on a build agent).
Spawn a new agent to do this phase.
Call mcp__github__search_repositories with query: "repo:OWNER/REPO_NAME" and
minimal_output: false. Read the default_branch field from the returned repository object.
LOCAL_PATH="$WORKDIR/REPO_NAME"
if [ -d "$LOCAL_PATH/.git" ]; then
git -C "$LOCAL_PATH" fetch origin
git -C "$LOCAL_PATH" checkout DEFAULT_BRANCH
git -C "$LOCAL_PATH" reset --hard origin/DEFAULT_BRANCH
else
mkdir -p "$WORKDIR"
git clone "https://github.com/OWNER/REPO_NAME.git" "$LOCAL_PATH"
fi
Spawn a new agent to do this phase.
Slugify the task text and append a short hash for uniqueness:
TASK="<today's task text>"
SLUG=$(echo "$TASK" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/-\+/-/g' | sed 's/^-\|-$//g' | cut -c1-45)
HASH=$(echo "$TASK" | python3 -c "import sys,hashlib; print(hashlib.md5(sys.stdin.read().encode()).hexdigest()[:6])")
BRANCH="task/${SLUG}-${HASH}"
git -C "$LOCAL_PATH" checkout DEFAULT_BRANCH
# -B is idempotent: creates the branch, or resets it to the current commit if a stale
# copy was left behind by an interrupted prior run.
git -C "$LOCAL_PATH" checkout -B "$BRANCH"
You are working on a git repository located at: LOCAL_PATH
Repository: OWNER/REPO_NAME
Current branch: BRANCH_NAME
Your task:
TASK_DESCRIPTION
Instructions:
1. Read the codebase to understand its structure and conventions.
2. Implement the task — be focused, do only what is asked.
3. Stage your changes: git -C LOCAL_PATH add -A
4. Commit with a clear message: git -C LOCAL_PATH commit -m "YOUR_MESSAGE"
If nothing needed to change (task already done), say so explicitly instead.
5. Do NOT push — the caller handles that.
Return a short paragraph summarising what you changed and why.
git -C "$LOCAL_PATH" push origin "$BRANCH" --force-with-lease
If there are no commits to push, mark the task as "nothing to commit" and skip to Phase 4 (still update state so we don't retry it tomorrow).
Call mcp__github__create_pull_request with:
owner: OWNERrepo: REPO_NAMEtitle: TASK_DESCRIPTIONhead: BRANCHbase: DEFAULT_BRANCHbody:
## Summary
AGENT_SUMMARY
---
*Opened by multi-repo-tasks via Claude Code.*
Capture the html_url field from the response as the PR URL.
Extract the PR number from the URL (last path segment).
Immediately after the PR is open, spawn Phase 5 in the background (run_in_background: true), passing it: OWNER, REPO_NAME, PR_NUMBER, BRANCH, LOCAL_PATH, DEFAULT_BRANCH. Then continue to Phase 4 without waiting for Phase 5 to finish.
The write path branches on the mode detected in Phase 1. Both branches must preserve every key the user (or an external sync tool) put on the task — never drop unknown fields.
Pick one of:
done — the agent committed changes and a PR was opened (or the task was already done
and there was nothing to commit). Always sets branch, date. Sets pr_url when a PR
was opened.failed — the agent could not complete the task. Sets error: "<short reason>".skipped — the task is no longer applicable (e.g. repo gone). Sets reason: "<why>".The same helper script does the writeback, picking the right strategy based on the mode detected in Phase 1:
status flips in place and the completion keys
(branch, pr_url, date, or error / reason) are merged onto it. Unknown keys on
the entry are preserved.completed: in the state file.python3 "${CLAUDE_PLUGIN_ROOT}/scripts/tasks_io.py" record "$TASKS_PATH" \
--mode "$MODE" \
--task-id "$TASK_ID" \
--repo "$REPO" \
--description "$TASK_DESCRIPTION" \
--status "$NEW_STATUS" \
${STATE_PATH:+--state "$STATE_PATH"} \
--completion-json "$COMPLETION_JSON"
COMPLETION_JSON is a JSON object — for status=done use
{"branch": "...", "pr_url": "...", "date": "YYYY-MM-DD"}; for failed use
{"error": "<short reason>"}; for skipped use {"reason": "<why>"}.
## Task — Done
✅ owner/repo-name
Task: "Add unit tests for the authentication module"
Branch: task/add-unit-tests-abc123
PR: https://github.com/owner/repo-name/pull/42
Progress: 1 of 5 tasks completed across 2 repos.
Next up: "Fix the typo in README.md" (owner/repo-name)
This phase runs in the background, in parallel with Phase 4. Spawn it as a background agent immediately after the PR is opened.
Spawn a background agent using the Agent tool with:
subagent_type: task-agent:copilot-review-fixerrun_in_background: trueprompt: the real values for the placeholders, e.g.:
OWNER=<owner>
REPO_NAME=<repo>
PR_NUMBER=<number>
BRANCH=<branch>
LOCAL_PATH=<path>
DEFAULT_BRANCH=<branch>
After all tasks are done, spawn an agent to clean up the local clones in $WORKDIR to
free up disk space:
rm -rf "$WORKDIR"
github and the other is github2. Use github2 only to create and manage PRs into repos that are not owned by me.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub dan323/easier-life-skills --plugin task-agent