Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
fa931ca
feat(ci): add issue auto-implement action and workflow
leggetter Mar 16, 2026
be2fe94
feat(ci): pass context_files into assess and include in Claude prompt
leggetter Mar 16, 2026
4bff7cb
feat(ci): fetch all issue comments for assessment
leggetter Mar 16, 2026
4b27fdd
fix(ci): stabilize issue-auto-implement assess tests workflow
leggetter Mar 16, 2026
876526d
fix(ci): do not run main() when assess script is loaded by Vitest
leggetter Mar 16, 2026
f75cf43
feat(ci): wire Claude implement step and commit-and-push
leggetter Mar 16, 2026
d3aa192
feat(ci): true implement-verify loop with PREVIOUS_VERIFY_OUTPUT
leggetter Mar 16, 2026
27bb381
refactor(issue-auto-implement): move assess into src/ and test/
leggetter Mar 16, 2026
77b46cd
refactor(issue-auto-implement): consolidate tests under test/ (unit, …
leggetter Mar 16, 2026
44839d7
chore: ignore auto-implement worktrees
leggetter Mar 16, 2026
16d47d9
fix(issue-auto-implement): use Claude Code CLI only, single API key
leggetter Mar 16, 2026
caca558
fix(issue-auto-implement): create new PR when previous closed; TS pus…
leggetter Mar 16, 2026
27a5e56
docs(issue-auto-implement): quick start, remove TEST_PLAN refs, add a…
leggetter Mar 16, 2026
38dbe17
feat(issue-auto-implement): add setup workflow to create labels
leggetter Mar 16, 2026
746c055
Merge origin/main into feat/issue-auto-implement-setup
leggetter Mar 16, 2026
803b3b1
chore: remove REMOTE_TEST_PLAN.md from source control
leggetter Mar 16, 2026
dead048
feat(issue-auto-implement): add permission-based trigger gate (works …
leggetter Mar 16, 2026
764532b
Merge origin/main into feat/issue-auto-implement-setup (resolve READM…
leggetter Mar 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion .github/actions/issue-auto-implement/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,12 @@ From the workflow event payload, derive:

## Restricting who can trigger

Only members of the `github_allowed_trigger_team` (input; set via repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`) may trigger the flow. First step checks `github.actor` against that team; if the variable is unset or the actor is not a member, fail immediately. Token must have `read:org`.
The first step enforces one of two gates (exactly one must be configured; no bypass):

1. **Permission check** — If `github_allowed_trigger_min_permission` is set (repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION`: `triage`, `push`, `maintain`, or `admin`), the action calls the repo collaborator permission API and requires the actor to have at least that permission. Works with the default `GITHUB_TOKEN`.
2. **Team check** — Otherwise, if `github_allowed_trigger_team` is set (repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`, e.g. `org/team`), the action checks `github.actor` against that team; if unset or not a member, fail. Token must have `read:org` (PAT if `GITHUB_TOKEN` lacks it).

If neither variable is set, the step fails. When both are set, the permission check is used and the team value is ignored.

## Labels

Expand Down
22 changes: 14 additions & 8 deletions .github/actions/issue-auto-implement/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,31 @@ Reusable composite action for label-triggered issue automation: assess (request
## How to use (quick start)

1. **Workflow** — Ensure `.github/workflows/issue-auto-implement.yml` exists and calls this action (see the workflow in this repo for the exact `on:` and `uses:`).
2. **Secrets and variable** — In the repo: Settings → Secrets and variables → Actions. Add secret **`AUTO_IMPLEMENT_ANTHROPIC_API_KEY`** (Anthropic API key). Add variable **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (e.g. `your-org/your-team`); only members of this team can trigger the action.
2. **Secrets and variables** — In the repo: Settings → Secrets and variables → Actions. Add secret **`AUTO_IMPLEMENT_ANTHROPIC_API_KEY`** (Anthropic API key). For who can trigger, set **one** of: **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION`** (e.g. `push` or `maintain`; works with default token) or **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (e.g. `org/team`; token needs `read:org`).
3. **Trigger label** — Create the labels once so you can add them to issues. Either run the **Issue auto-implement setup** workflow (Actions → Issue auto-implement setup → Run workflow), which creates `automation/auto-implement`, `automation/needs-info`, and `automation/pr-created`; or create the trigger label **`automation/auto-implement`** manually in the repo (Settings or Issues → Labels). The main action also ensures these labels exist when it runs, but the trigger label must exist before you can add it to an issue.
4. **Trigger** — On an issue, add the label `automation/auto-implement`. The workflow runs: it assesses the issue (request more info vs implement), and if implement, runs the Claude Code CLI and opens a PR. You can also comment on the issue (to add context and re-trigger) or review the PR (to iterate).

## Usage (reference)

Used by `.github/workflows/issue-auto-implement.yml`. Requires `anthropic_api_key` (e.g. from repo secret `AUTO_IMPLEMENT_ANTHROPIC_API_KEY`), `github_allowed_trigger_team` (e.g. from repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`), and `github_token` from the workflow.
Used by `.github/workflows/issue-auto-implement.yml`. Requires `anthropic_api_key` (e.g. from repo secret `AUTO_IMPLEMENT_ANTHROPIC_API_KEY`), one of `github_allowed_trigger_min_permission` or `github_allowed_trigger_team` (repo variables), and `github_token` from the workflow.

## Inputs

| Input | Required | Default | Description |
|-------|----------|---------|-------------|
| `anthropic_api_key` | Yes | - | Claude API key. Set via repo secret `AUTO_IMPLEMENT_ANTHROPIC_API_KEY` so multiple actions can use different keys. |
| `github_token` | Yes | - | Token with contents, issues, pull-requests, read:org |
| `github_token` | Yes | - | Token (contents, issues, pull-requests; read:org only if using team check) |
| `context_files` | No | AGENTS.md,REFERENCE.md | Comma-separated paths for assessment context |
| `assessment_reference_issue` | No | 192 | Reference issue number for "enough information" |
| `label_prefix` | No | automation | Prefix for labels (e.g. automation/auto-implement) |
| `verify_commands` | No | go test ./... | Commands run for verification |
| `max_implement_retries` | No | 3 | Max retries on verify failure (cap 5) |
| `github_allowed_trigger_team` | Yes | - | GitHub Team slug (e.g. org/team); only members can trigger. Set via repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`. |
| `github_allowed_trigger_team` | No* | - | Team slug (e.g. org/team); only members can trigger. Repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`. Ignored if min_permission is set. Token needs read:org. |
| `github_allowed_trigger_min_permission` | No* | - | Require actor has at least this repo permission: triage, push, maintain, or admin. Repo variable `AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION`. Works with default GITHUB_TOKEN. |
| `post_pr_comment` | No | false | When true, post a comment on the issue linking to the new PR when one is created. |

*One of `github_allowed_trigger_min_permission` or `github_allowed_trigger_team` must be set (via repo variables).

Secrets and variables use an action-specific prefix (e.g. `AUTO_IMPLEMENT_`) so each action can have its own keys/variables and it's clear which workflow uses which. This also avoids clashing with platform-reserved names (e.g. `GITHUB_*`).

## CI/CD: what you need to run this workflow
Expand All @@ -35,17 +38,20 @@ To use this action in GitHub Actions:

1. **Workflow** — Call the action from a workflow (e.g. `.github/workflows/issue-auto-implement.yml`) on `issues.labeled`, `issue_comment`, `pull_request_review`, and/or `pull_request_review_comment`. The job needs `contents: write`, `issues: write`, `pull-requests: write`.
2. **Secrets** — Add **`AUTO_IMPLEMENT_ANTHROPIC_API_KEY`** (repo secret). Used for the assess step and passed to the Claude Code CLI in the implement step.
3. **Variables** — Add **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (repo variable, required): GitHub Team slug (e.g. `org/team-name`) whose members may trigger the run.
4. **Token** — Pass `github_token` (e.g. `secrets.GITHUB_TOKEN`) to the action. The team check needs `read:org`; if `GITHUB_TOKEN` lacks it, use a PAT with `read:org` and pass it as the token.
3. **Variables (trigger gate)** — Set **one** of:
- **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION`** (repo variable): `triage`, `push`, `maintain`, or `admin`. Only users with at least this repo permission can trigger. Works with default `GITHUB_TOKEN`.
- **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (repo variable): org/team slug (e.g. `org/team-name`). Only team members can trigger. Token must have `read:org` (use a PAT if `GITHUB_TOKEN` lacks it).
4. **Token** — Pass `github_token` (e.g. `secrets.GITHUB_TOKEN`). If using the team check, the token needs `read:org`; the permission check works with the default token.
5. **Implement in CI** — The action installs the Claude Code CLI (`@anthropic-ai/claude-code`) when the assess outcome is `implement`, so the workflow does not need to install it. Implement runs in the repo with Read/Edit/Bash; the CLI uses `AUTO_IMPLEMENT_ANTHROPIC_API_KEY`.

No other setup is required. Optionally set `verify_commands` (default `go test ./...`) and `context_files` (default `AGENTS.md,REFERENCE.md`) to match your repo.

## Secrets and variables (repo setup)

- **`AUTO_IMPLEMENT_ANTHROPIC_API_KEY`** (repo secret) — Claude API key for the assess and implement steps. Add under Settings → Secrets and variables → Actions.
- **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (repo variable, required) — GitHub Team slug (e.g. `org/team-name`) whose members may trigger the workflow. Add under Settings → Secrets and variables → Actions. The first step checks `github.actor` against this team; if unset or not a member, the run fails.
- **Token for team check** — The workflow passes `github_token` (usually `secrets.GITHUB_TOKEN`) to the action. The team check needs `read:org`. If your default `GITHUB_TOKEN` does not have `read:org`, use a PAT with that scope and pass it as the token (e.g. a repo secret) instead.
- **Trigger gate (set one):**
- **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION`** (repo variable) — Require the triggering user to have at least this repo permission: `triage`, `push`, `maintain`, or `admin`. Works with the default `GITHUB_TOKEN`. Add under Settings → Secrets and variables → Actions → Variables.
- **`AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM`** (repo variable) — GitHub Team slug (e.g. `org/team-name`) whose members may trigger. The first step checks `github.actor` against this team. The token needs `read:org`; if `GITHUB_TOKEN` lacks it, use a PAT and pass it as `github_token`.

## Triggers

Expand Down
55 changes: 46 additions & 9 deletions .github/actions/issue-auto-implement/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,34 +28,71 @@ inputs:
required: false
default: '3'
github_allowed_trigger_team:
description: 'GitHub Team slug whose members may trigger the flow (e.g. hookdeck/automation-triggers). Set via repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM.'
required: true
description: 'GitHub Team slug whose members may trigger (e.g. org/team). Requires token with read:org. Set via repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM. Ignored if github_allowed_trigger_min_permission is set.'
required: false
github_allowed_trigger_min_permission:
description: 'Alternative to team check: require actor has at least this repo permission (triage, push, maintain, admin). Works with default GITHUB_TOKEN. Set via repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION.'
required: false
post_pr_comment:
description: 'When true, post a comment on the issue linking to the new PR when one is created'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- name: Check allowed trigger team
- name: Check allowed trigger (permission or team)
shell: bash
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
REPO: ${{ github.repository }}
ACTOR: ${{ github.actor }}
MIN_PERM: ${{ inputs.github_allowed_trigger_min_permission }}
TEAM: ${{ inputs.github_allowed_trigger_team }}
run: |
TEAM="${{ inputs.github_allowed_trigger_team }}"
# Option A: Require minimum repo permission (works with default GITHUB_TOKEN, no read:org)
if [ -n "$MIN_PERM" ]; then
case "$MIN_PERM" in
triage|push|maintain|admin) ;;
*) echo "::error::github_allowed_trigger_min_permission must be one of: triage, push, maintain, admin"; exit 1 ;;
esac
RESP=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/repos/$REPO/collaborators/$ACTOR/permission")
HTTP=$(echo "$RESP" | tail -n1)
BODY=$(echo "$RESP" | sed '$d')
if [ "$HTTP" != "200" ]; then
echo "::error::Actor $ACTOR has no collaborator permission on this repo (HTTP $HTTP). Only users with at least $MIN_PERM can trigger."
exit 1
fi
PERM=$(echo "$BODY" | jq -r '.permission // "read"')
# Order: read/pull < triage < push < maintain < admin
level() { case "$1" in read|pull) echo 1;; triage) echo 2;; push) echo 3;; maintain) echo 4;; admin) echo 5;; *) echo 0;; esac; }
ACTOR_LEVEL=$(level "$PERM")
MIN_LEVEL=$(level "$MIN_PERM")
if [ "$ACTOR_LEVEL" -lt "$MIN_LEVEL" ]; then
echo "::error::Actor $ACTOR has permission $PERM; at least $MIN_PERM is required to trigger this workflow."
exit 1
fi
echo "Actor $ACTOR has permission $PERM (>= $MIN_PERM)."
exit 0
fi
# Option B: Require team membership (token needs read:org)
if [ -z "$TEAM" ]; then
echo "::error::github_allowed_trigger_team must be set (e.g. repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM). Only team members can trigger this workflow."
echo "::error::Set one of: repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION (e.g. push) or AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM (e.g. org/team)."
exit 1
fi
ORG="${TEAM%/*}"
SLUG="${TEAM#*/}"
HTTP=$(curl -s -o /dev/null -w "%{http_code}" \
-H "Authorization: Bearer ${{ inputs.github_token }}" \
-H "Authorization: Bearer $GITHUB_TOKEN" \
-H "Accept: application/vnd.github+json" \
"https://api.github.com/orgs/${ORG}/teams/${SLUG}/members/${{ github.actor }}")
"https://api.github.com/orgs/${ORG}/teams/${SLUG}/members/$ACTOR")
if [ "$HTTP" != "204" ]; then
echo "::error::Actor ${{ github.actor }} is not in team $TEAM (HTTP $HTTP). Only team members can trigger this workflow."
echo "::error::Actor $ACTOR is not in team $TEAM (HTTP $HTTP). Only team members can trigger. Token may need read:org."
exit 1
fi
echo "Actor ${{ github.actor }} is in team $TEAM."
echo "Actor $ACTOR is in team $TEAM."
- name: Ensure labels exist
shell: bash
env:
Expand Down
5 changes: 3 additions & 2 deletions .github/workflows/issue-auto-implement.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Label-triggered issue automation: assess, implement-verify loop, create PR or iterate on PR.
# Triggers: issue labeled (automation/auto-implement), issue comment, PR review.
# Require repo variable AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM (org/team slug). Token may need read:org (PAT) for team check.
# Gate: set one of AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION (e.g. push; works with default token) or AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM (org/team; token needs read:org).
name: Issue auto-implement

on:
Expand All @@ -26,7 +26,7 @@ jobs:
contents: write
issues: write
pull-requests: write
# Team check requires read:org; use a PAT as github_token if GITHUB_TOKEN lacks it
# read:org only needed if using team check (AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM)
steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -37,4 +37,5 @@ jobs:
with:
anthropic_api_key: ${{ secrets.AUTO_IMPLEMENT_ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
github_allowed_trigger_min_permission: ${{ vars.AUTO_IMPLEMENT_ALLOWED_TRIGGER_MIN_PERMISSION }}
github_allowed_trigger_team: ${{ vars.AUTO_IMPLEMENT_ALLOWED_TRIGGER_TEAM }}
Loading