Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Autonomous PR Agent

Turns structured scan findings (scan_results.json) into reviewed Pull Requests. The agent never merges anything itself — it only opens PRs for a human to review.

1. What it does, in order

  1. Loads findings from a JSON file.
  2. For each finding:
    • Skips it if an open PR already references that finding ID (idempotency).
    • Reads the affected file.
    • Calls Claude with a structured prompt to produce a full corrected file.
    • Validates the fix (syntax check — Python via ast, JSON via json, YAML via PyYAML, JS/TS via node --check if Node is installed; optionally your own test suite via --run-tests).
    • Creates a brand-new branch off --base-branch (never touches main/master directly), commits, pushes, and opens a PR.
    • On any failure, discards the change and deletes the local branch — nothing partial is ever left committed.
  3. Prints a summary of created / skipped / failed findings.

2. Install

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

Optional, only needed if you want JS/TS syntax checking: install Node.js (the agent auto-detects it and skips that check gracefully if it's missing).

3. Environment variables

Variable Required? Description
ANTHROPIC_API_KEY Always From https://console.anthropic.com/settings/keys
GITHUB_TOKEN Unless --dry-run A token with permission to push branches and open PRs on the target repo
GITHUB_REPOSITORY Optional owner/repo. If unset, the agent infers it from the repo's origin remote
BASE_BRANCH Optional Default main. Can also be passed as --base-branch
ANTHROPIC_MODEL Optional Default claude-sonnet-5. Check https://docs.claude.com for current model names
TEST_COMMAND Optional Default pytest -q. Used only with --run-tests

Creating the GitHub token

Use a fine-grained personal access token scoped to only the repo(s) this agent should touch, with:

  • Contents: Read and write
  • Pull requests: Read and write

Do not grant it Administration or Actions permissions. If you'd rather use a classic token, the repo scope is sufficient but is broader than necessary.

export ANTHROPIC_API_KEY="sk-ant-..."
export GITHUB_TOKEN="github_pat_..."
export GITHUB_REPOSITORY="your-org/your-repo"   # optional if origin remote is already set

4. Scan file format

{
  "findings": [
    {
      "id": "SEC-001",
      "file": "app/db.py",
      "severity": "high",
      "type": "sql_injection",
      "description": "User input is concatenated directly into a SQL query string.",
      "line_start": 10,
      "line_end": 12,
      "code_snippet": "query = \"SELECT * FROM users WHERE id = \" + user_id"
    }
  ]
}

Only id and file are required; everything else is optional context passed to the model. See scan_results.example.json for a runnable example. file must be a path relative to the repo root — absolute paths and ../ traversal outside the repo are rejected.

5. Testing locally on a sandbox repo (safe, no real GitHub repo needed)

This exercises the full pipeline — including a real Claude API call and syntax validation — without touching any GitHub repository.

# 1. Create a throwaway git repo
mkdir -p /tmp/sandbox-repo/app && cd /tmp/sandbox-repo
git init -b main
git config user.email "you@example.com"
git config user.name "Your Name"

cat > app/db.py << 'EOF'
def get_user(cursor, user_id):
    query = "SELECT * FROM users WHERE id = " + user_id
    cursor.execute(query)
    return cursor.fetchone()
EOF

git add . && git commit -m "initial commit"

# 2. Point a scan file at the vulnerable file
cat > scan_results.json << 'EOF'
{
  "findings": [
    {
      "id": "SEC-001",
      "file": "app/db.py",
      "severity": "high",
      "type": "sql_injection",
      "description": "String concatenation is used to build a SQL query from user input.",
      "code_snippet": "query = \"SELECT * FROM users WHERE id = \" + user_id"
    }
  ]
}
EOF

# 3. Dry run: calls Claude, validates syntax, prints the diff, commits nothing
export ANTHROPIC_API_KEY="sk-ant-..."
python /path/to/auto_pr_agent.py \
  --repo-path /tmp/sandbox-repo \
  --scan-file /tmp/sandbox-repo/scan_results.json \
  --dry-run -v

Confirm the printed diff looks correct. Then, to test the real GitHub flow, push sandbox-repo to a throwaway repo you control and re-run without --dry-run, with GITHUB_TOKEN and GITHUB_REPOSITORY set:

git remote add origin https://github.com/<you>/sandbox-repo.git
git push -u origin main

export GITHUB_TOKEN="github_pat_..."
python /path/to/auto_pr_agent.py \
  --repo-path /tmp/sandbox-repo \
  --scan-file /tmp/sandbox-repo/scan_results.json

Check the repo on GitHub: you should see a new fix/sec-001-... branch and an open PR against main, not a direct commit to main.

6. Recommended defense in depth

The agent enforces "never commit to main" in code, but for a real production setup also add GitHub-side protection so a bug in any tool can't bypass it:

  • Enable branch protection on main/master: require PRs, require at least one human approval, disallow force-pushes.
  • Scope the bot's token to only the repos it needs.
  • Run the agent with --dry-run first in CI on a schedule, and only run it for real after a human has skimmed the dry-run output.
  • Never configure anything to auto-merge these PRs.

7. CLI reference

python auto_pr_agent.py \
  --repo-path PATH          # local git repo (default: ".")
  --scan-file PATH          # default: "scan_results.json"
  --base-branch NAME        # default: "main"
  --dry-run                 # no commit/push/PR; prints diffs
  --run-tests               # run --test-command before committing each fix
  --test-command "..."      # default: "pytest -q"
  --max-findings N          # only process the first N findings
  --anthropic-model NAME    # default: "claude-sonnet-5"
  --max-output-tokens N     # default: 8000
  -v / --verbose            # debug logging

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages