From GitHub Issue to Pull Request: Running Claude Code Unattended Sortie, an open-source tool, automates the process of turning GitHub issues into pull requests by running Claude Code in isolated workspaces with retry logic and state persistence. The tool eliminates manual babysitting by polling for labeled issues, executing agents, and opening PRs without developer intervention. You already run Claude Code by hand: copy issues into a prompt, watch it work, check the diff, and if something breaks halfway through, you restart it. This works fine for one task at a time, but it falls apart when you have 10 tasks simultaneously. Claude Code is good at handling routine engineering tasks: bug fixes, dependency bumps, and small features, when the prompt is clear and the task is scoped. But when it comes to scaling, you need an infrastructure with isolated workspaces, retry logic, state that survives a restart, and tracker integration, not to waste time on babysitting. Sortie https://github.com/sortie-ai/sortie removes the manual work. You label an issue, Sortie picks it up, creates an isolated workspace, runs the agent, retries it if it stalls, and opens a pull request when it's done. This article describes how to set the entire process from an empty directory to a GitHub issue turning into a PR without you touching the keyboard in between. A GitHub repository you control Export two environment variables: ANTHROPIC API KEY - authenticates Claude Code GITHUB TOKEN - it's read by tracker.api key: $GITHUB TOKEN for polling/updating issues, and it's the same token gh pr create inside the after run hook uses to open the PR, so it needs Issues: read/write, Contents: read, and Pull requests: read/write scopes on that repository, all on one fine-grained PAT.Push access to the repository over SSH. The after create hook below clones with git@github.com:... , so git authenticates with your SSH key, not with GITHUB TOKEN . Verify with ssh -T git@github.com . If you'd rather stay on one credential, swap the clone URL for https://${GITHUB TOKEN}@github.com/yourorg/yourrepo.git and give the token Contents: read/write. In your repository, create the agent-ready label — you need it to exist before you can put it on an issue, and query filter finds nothing without it. Creating in-progress , review , and done up front is also worth doing: GitHub does create a missing label when Sortie applies it, but it comes out in default gray, and the colors are yours to pick. Claude Code installed on your machine. Verify with command: claude --version The entire process takes about 15 minutes. To install Sortie on Linux or macOS, run the command: curl -sSL https://get.sortie-ai.com/install.sh | sh For Homebrew: brew install --cask sortie-ai/tap/sortie On Windows, use the PowerShell equivalent. It runs on the PowerShell 5.1 that ships with Windows 10 and 11, as well as on PowerShell 7: irm 'https://get.sortie-ai.com/install.ps1' | iex That one installs into %LOCALAPPDATA%\Programs\sortie , so it needs no administrator rights, and it adds the directory to your user PATH for you. Shells you already have open keep their old environment, so reopen PowerShell before continuing. Sortie is a single Go binary file, for which you need no database server, Docker, or Node.js. State lives in an embedded SQLite file .sortie.db that Sortie creates next to your workflow file — no need to create it manually. Confirm it's on your PATH by running the command: sortie --version This file contains everything you need for Sortie, including trackers to pull tasks from, agents to run, and prompts telling them what to do. Create a directory and a file. --- tracker: kind: github api key: $GITHUB TOKEN project: yourorg/yourrepo query filter: "label:agent-ready" active states: - agent-ready - in-progress in progress state: in-progress handoff state: review terminal states: - done polling: interval ms: 30000 workspace: root: ./workspaces hooks: after create: | git clone --depth 1 git@github.com:yourorg/yourrepo.git . before run: | git fetch origin main git checkout -B "sortie/${SORTIE ISSUE IDENTIFIER}" origin/main after run: | git add -A git diff --cached --quiet || \ git commit -m "sortie ${SORTIE ISSUE IDENTIFIER} : automated changes" git push origin "sortie/${SORTIE ISSUE IDENTIFIER}" --force-with-lease gh pr create --fill \ --head "sortie/${SORTIE ISSUE IDENTIFIER}" \ --base main 2 /dev/null || true timeout ms: 120000 agent: kind: claude-code command: claude max turns: 5 max concurrent agents: 1 turn timeout ms: 1800000 stall timeout ms: 300000 claude-code: permission mode: bypassPermissions max budget usd: 5 max turns: 50 server: port: 8888 --- You are a senior engineer working in this repository. Your work is tracked by an automated orchestrator Sortie that manages your session, retries failures, and monitors progress. Task {{ .issue.identifier }} : {{ .issue.title }} {{ if .issue.description }} Description {{ .issue.description }} {{ end }} {{ if .issue.url }} Ticket: {{ .issue.url }} {{ end }} Rules 1. Read existing code and project conventions before writing anything new. 2. Keep changes minimal. Implement exactly what the task requires. 3. Run the project's lint and test commands before finishing. All checks must pass. 4. Write tests for new functionality, covering edge cases, not just the happy path. {{ if not .run.is continuation }} First run Start by understanding the codebase structure. Check for existing patterns and follow them. Write the implementation, add a test, and verify everything passes. {{ end }} {{ if .run.is continuation }} Continuation turn {{ .run.turn number }}/{{ .run.max turns }} You are resuming this task. Run git status and check test output to understand the current state. Continue from where the previous turn left off. Do not repeat work already completed. {{ end }} The file consists of two parts. Everything between the two --- markers is YAML front matter for configuration. Everything after the closing --- is a Go text/template, rendered separately for each issue and sent to the agent as its prompt. In this file: query filter: "label:agent-ready" - Sortie only takes issues with this label. If the label is missing, it ignores the issue. active states / in progress state / handoff state / terminal states - a status map through GitHub labels. active states must include in progress state , otherwise Sortie refuses to start and sortie validate fails with a config error enforced in code . handoff state must not match any of active states or terminal states also enforced in code . agent.max turns: 5 - how many turns the orchestrator runs within one session: the first turn plus up to four continuations. Defaults to 20. claude-code.max turns: 50 - how many internal steps Claude Code itself takes in a single invocation. These are two different fields that happen to share a name, in different sections. At 5x50, that's up to 250 steps per task. claude-code.max budget usd: 5 - a cost cap per invocation, not per session. Sortie passes --max-budget-usd on every turn and Claude Code's counter starts from zero each time, so with agent.max turns: 5 the worst case for one issue is five turns of $5, not $5 total. The cap is also checked at the turn boundary rather than mid-turn, so a single turn can overshoot it. To bound one issue, size it as budget x max turns and lower one of the two. permission mode: bypassPermissions - lets the agent act without stopping to ask for approval on each step; run it against a repository you control. Setting it explicitly is the right move, but omitting it does not make the run interactive: the adapter then falls back to the deprecated --dangerously-skip-permissions , which bypasses the same checks.Before running the config, do the following steps: yourorg/yourrepo with your repository URL cloning in tracker.project and hooks:after create . cmd.exe /C rather than sh -c , so write %SORTIE ISSUE IDENTIFIER% instead of ${SORTIE ISSUE IDENTIFIER} and drop the trailing \ line continuations. sortie validate ./WORKFLOW.md Create a test issue with the agent-ready label and add the description, for example, “Add a /healthz endpoint returning 200” — the more specific your description is, the more specific result you get. Run the sortie ./WORKFLOW.md and watch the running process in the dashboard. All git operations are Sortie's hooks: after create — clones the repository into an isolated workspace under its working directory. before run — creates the sortie/