Claude Code Task
claude-code-task@1.0.0 Template Give Claude Code a task in your repo and get the result back as a draft pull request.
Start from it
flightstack template use claude-code-task What it does
Runs Claude Code headless in the checked-out repository with the task you give it, an explicit tool allowlist and a dollar cap. If it changes files, the job commits them to a new branch, pushes, and opens a (draft) pull request for a human to review. It never pushes to the base branch.
Inputs
-
taskstring required - What Claude should do, written as you would brief a teammate.
-
repositorystring required - GitHub repository as owner/name, usually "${{ context.repository }}".
-
baseBranchstring defaultmain - Branch to start from and open the pull request against.
-
branchNamestring required - New branch for the change, e.g. "claude/fix-${{ context.runNumber }}". Must not be the base branch.
-
allowedToolsstring defaultRead Edit Write Glob Grep - Space-separated Claude Code tools Claude may use without asking. Anything else is denied. Add narrow Bash patterns for the commands the task needs, e.g. "Bash(npm test *)". Avoid a bare "Bash".
-
modelstring defaultclaude-opus-5-5 - Claude model id.
-
maxBudgetUsdnumber default5 - Hard cap on API spend for the run, in US dollars.
-
openPullRequestboolean defaulttrue - Open a pull request. Off pushes the branch only.
-
draftboolean defaulttrue - Open the pull request as a draft.
-
claudeCodeVersionstring defaultlatest - Version of @anthropic-ai/claude-code to run.
Definition
name: Claude Code Task
description: >-
Give Claude Code a task in the repository - fix failing tests, bump a
dependency, update docs - and get the result back as a pull request.
triggers:
- type: manual
- type: schedule
cron: "0 6 * * 1"
jobs:
deps:
name: Weekly dependency bump
type: plugin/claude-code-task
with:
repository: "${{ context.repository }}"
baseBranch: main
branchName: "claude/deps-${{ context.runNumber }}"
task: >-
Upgrade the Dart dependencies in pubspec.yaml to their latest
compatible versions, run `flutter pub get` and `flutter test`, and fix
anything the upgrade breaks. Keep the change minimal.
allowedTools: "Read Edit Write Glob Grep Bash(flutter pub *) Bash(flutter test *) Bash(dart format *)"
maxBudgetUsd: 5
plugin:
name: claude-code-task
displayName: Claude Code Task
description: >-
Runs Claude Code headless in the checked-out repository with the task you
give it and an explicit tool allowlist. If it changes files, the job
commits them to a new branch, pushes, and opens a pull request for a
human to review. It never pushes to the base branch.
category: utility
requiredTools:
- name: nodejs
version: ">=20.0.0"
supportedPlatforms:
- linux
- macos
parameters:
type: object
properties:
task:
type: string
description: What Claude should do, written as you would brief a teammate.
repository:
type: string
description: GitHub repository as owner/name, usually "${{ context.repository }}".
baseBranch:
type: string
default: main
description: Branch to start from and open the pull request against.
branchName:
type: string
description: >-
New branch for the change, e.g. "claude/fix-${{ context.runNumber }}".
Must not be the base branch.
allowedTools:
type: string
default: "Read Edit Write Glob Grep"
description: >-
Space-separated Claude Code tools Claude may use without asking.
Anything else is denied. Add narrow Bash patterns for the commands
the task needs, e.g. "Bash(npm test *)". Avoid a bare "Bash".
model:
type: string
default: claude-opus-5-5
description: Claude model id.
maxBudgetUsd:
type: number
default: 5
description: Hard cap on API spend for the run, in US dollars.
openPullRequest:
type: boolean
default: true
description: Open a pull request. Off pushes the branch only.
draft:
type: boolean
default: true
description: Open the pull request as a draft.
claudeCodeVersion:
type: string
default: latest
description: Version of @anthropic-ai/claude-code to run.
required: [task, repository, branchName]
script: |
set -euo pipefail
for tool in git curl jq npx; do
command -v "$tool" >/dev/null 2>&1 || { echo "$tool is required on this agent." >&2; exit 1; }
done
if [ -z "${ANTHROPIC_API_KEY:-}" ]; then
echo "Set ANTHROPIC_API_KEY as a secret variable for this pipeline." >&2
exit 1
fi
if [ -z "${GITHUB_TOKEN:-}" ]; then
echo "Set GITHUB_TOKEN (contents and pull-requests write) as a secret variable." >&2
exit 1
fi
repo="{{repository}}"
base="{{baseBranch}}"
branch="{{branchName}}"
if [ "$branch" = "$base" ]; then
echo "branchName must differ from baseBranch - this step never pushes to $base." >&2
exit 1
fi
task="$(cat <<'FS_TASK'
{{task}}
FS_TASK
)"
git fetch --quiet origin "$base"
git checkout --quiet -B "$branch" "origin/$base"
git config user.name "Claude (FlightStack)"
git config user.email "claude-bot@users.noreply.github.com"
guard="You are running unattended in CI. Nobody can answer questions, so make reasonable decisions and finish the task. Do not commit, push, or change git configuration - the pipeline commits your file changes afterwards. When you are done, reply with a short summary of what you changed and anything a reviewer should check."
result="$(mktemp)"
echo "--- claude -p (model {{model}}, budget {{maxBudgetUsd}} USD)"
set +e
npx --yes "@anthropic-ai/claude-code@{{claudeCodeVersion}}" \
-p "$task" \
--model "{{model}}" \
--permission-mode dontAsk \
--allowedTools "{{allowedTools}}" \
--append-system-prompt "$guard" \
--max-budget-usd "{{maxBudgetUsd}}" \
--output-format json > "$result"
claude_status=$?
set -e
summary="$(jq -r '.result // empty' "$result" 2>/dev/null || true)"
cost="$(jq -r '.total_cost_usd // empty' "$result" 2>/dev/null || true)"
echo "$summary"
[ -n "$cost" ] && echo "cost: \$$cost"
if [ "$claude_status" -ne 0 ] || [ "$(jq -r '.is_error // false' "$result" 2>/dev/null)" = "true" ]; then
echo "Claude Code did not finish the task (exit $claude_status)." >&2
exit 1
fi
if [ -z "$(git status --porcelain)" ]; then
echo "Claude made no file changes - nothing to commit."
fs-output changed false
exit 0
fi
git add -A
git commit --quiet -m "$(printf '%s\n\n%s' "Claude Code: $(echo "$task" | head -n 1 | cut -c1-60)" "$summary")"
git push --quiet --force-with-lease \
"https://x-access-token:$GITHUB_TOKEN@github.com/$repo.git" "HEAD:refs/heads/$branch"
echo "pushed $branch"
fs-output changed true
fs-output branch "$branch"
if [ "{{openPullRequest}}" != "true" ]; then
exit 0
fi
body="$(printf '%s\n\n---\n**Task**\n\n%s\n\n<sub>Opened by Claude Code ({{model}}) via FlightStack. Review before merging.</sub>' "$summary" "$task")"
payload="$(jq -n \
--arg title "Claude Code: $(echo "$task" | head -n 1 | cut -c1-60)" \
--arg head "$branch" --arg base "$base" --arg body "$body" \
--argjson draft {{draft}} \
'{title: $title, head: $head, base: $base, body: $body, draft: $draft}')"
pr_url="$(curl -sS --fail-with-body -X POST \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"https://api.github.com/repos/$repo/pulls" -d "$payload" | jq -r '.html_url')"
echo "opened $pr_url"
fs-output pullRequestUrl "$pr_url"