GitHub Integration
Pilot integrates with GitHub to receive issues and create pull requests. This is the primary adapter for most Pilot deployments, supporting both polling and webhook modes for real-time issue detection.
GitHub is Pilot’s flagship integration. It supports label-based issue intake, automatic PR creation, CI status tracking, auto-merge via Autopilot, and rich PR comments with execution metrics.
Setup
1. Create a GitHub Token
You can use either a Personal Access Token (PAT) or a GitHub App token.
Personal Access Token (Classic):
- Go to GitHub → Settings → Developer settings → Personal access tokens → Tokens (classic)
- Click Generate new token (classic)
- Select scopes:
repo— full repository accessworkflow— if you need GitHub Actions integration
Fine-grained PAT (recommended):
- Go to Settings → Developer settings → Personal access tokens → Fine-grained tokens
- Select the target repository
- Set permissions:
- Repository permissions: Contents (Read and write), Issues (Read and write), Pull requests (Read and write), Metadata (Read)
For production deployments, use a dedicated service account and fine-grained PAT scoped to specific repositories for better security isolation.
2. Create the pilot Label
In your GitHub repository, create a label that triggers Pilot:
- Go to your repo → Issues → Labels
- Click New label
- Create a label named
pilot(or your preferred name)
3. Configure Pilot
# ~/.pilot/config.yaml
adapters:
github:
enabled: true
token: ${GITHUB_TOKEN}
repo: owner/repo
project_path: /path/to/local/repo
pilot_label: pilot
polling:
enabled: true
interval: 30s
label: pilotConfiguration Reference
| Field | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable the GitHub adapter |
token | string | required | Personal Access Token or GitHub App token |
repo | string | required | Repository in owner/repo format |
project_path | string | required | Local filesystem path to the cloned repository |
webhook_secret | string | — | HMAC secret for webhook signature verification |
pilot_label | string | "pilot" | Label that triggers Pilot to process an issue |
polling.enabled | bool | false | Enable polling for new issues |
polling.interval | duration | 30s | How often to poll for new issues |
polling.label | string | "pilot" | Label to filter issues by when polling |
stale_label_cleanup.enabled | bool | true | Auto-remove orphaned pilot-in-progress labels |
stale_label_cleanup.interval | duration | 30m | How often to check for stale labels |
stale_label_cleanup.threshold | duration | 1h | Age after which a label is considered stale |
stale_label_cleanup.failed_threshold | duration | 24h | Age after which pilot-failed is removed |
Polling Mode
When polling is enabled, Pilot periodically checks for open issues with the pilot label:
- Fetches issues sorted by creation date (oldest first)
- Filters out issues with
pilot-in-progress,pilot-done, orpilot-failedlabels - Processes the oldest unprocessed issue
- Creates a PR and waits for merge (in sequential mode)
- Moves to the next issue
Sequential vs Parallel Execution
orchestrator:
execution:
mode: sequential # or "parallel"
wait_for_merge: true
poll_interval: 30s
pr_timeout: 1hSequential mode (default):
- Processes one issue at a time
- Waits for PR merge before starting the next issue
- Prevents merge conflicts between concurrent PRs
- Recommended for most workflows
Parallel mode:
- Processes multiple issues concurrently
- Configurable concurrency limit via
max_concurrent - Useful for independent, non-overlapping changes
Dependency Resolution
Pilot respects issue dependencies declared in the issue body:
## Summary
Add user authentication
## Depends on
- #123
- #124Issues with open dependencies are skipped until their dependencies are closed.
Webhook Mode
For real-time issue detection, configure GitHub webhooks:
1. Set Up the Webhook
- Go to your repository → Settings → Webhooks
- Click Add webhook
- Set Payload URL to
https://your-pilot.example.com/webhooks/github - Set Content type to
application/json - Set Secret to match your
webhook_secretconfig - Select events: Issues, Pull request reviews
2. Configure Webhook Secret
adapters:
github:
webhook_secret: ${GITHUB_WEBHOOK_SECRET}If webhook_secret is empty, signature verification is skipped (development mode only). Always set a secret in production.
Signature Verification
GitHub webhooks use HMAC-SHA256 signatures via the X-Hub-Signature-256 header. Pilot validates every incoming webhook against this signature.
Supported Webhook Events
| Event | Action | Behavior |
|---|---|---|
issues | opened | Process if issue has pilot label |
issues | labeled | Process if pilot label was just added |
pull_request_review | submitted | Notify Autopilot of approval status |
Pull Request Management
When Pilot completes work on an issue, it creates a pull request with rich metadata.
PR Features
- Automatic branch naming:
pilot/GH-{issue_number} - Issue linking: PR body references the source issue
- Execution metrics: Duration, phase breakdown, files changed
- CI integration: Waits for checks to pass before considering merge
PR Labels
Pilot applies labels to track issue status:
| Label | Purpose |
|---|---|
pilot | Triggers Pilot to process the issue |
pilot-in-progress | Applied while Pilot is working |
pilot-done | Applied after successful completion |
pilot-failed | Applied if execution fails |
pilot-retry-ready | PR closed without merge, ready for retry |
Merge Methods
Pilot supports all GitHub merge methods:
autopilot:
merge_method: squash # "merge", "squash", or "rebase"| Method | Description |
|---|---|
merge | Create a merge commit |
squash | Squash all commits into one |
rebase | Rebase and merge (linear history) |
CI Status Tracking
Pilot monitors both commit statuses and check runs:
Commit Statuses
| Status | Description |
|---|---|
pending | Checks are running |
success | All checks passed |
failure | One or more checks failed |
error | Check encountered an error |
Check Runs (GitHub Actions)
| Status | Conclusion | Description |
|---|---|---|
queued | — | Waiting to run |
in_progress | — | Currently running |
completed | success | All steps passed |
completed | failure | One or more steps failed |
completed | cancelled | Run was cancelled |
Autopilot Integration
With Autopilot enabled, Pilot can automatically merge PRs after checks pass:
autopilot:
enabled: true
environment: dev
auto_merge: true
require_approval: false
max_failures: 3Auto-Merge Flow
- Pilot creates PR from issue
- CI checks run (build, test, lint)
- If all checks pass and
auto_merge: true:- PR is automatically merged
- Issue is closed
- Next issue is processed
- If checks fail:
- Pilot retries implementation (up to
max_failures) - After max failures, issue is marked
pilot-failed
- Pilot retries implementation (up to
Approval Requirements
autopilot:
require_approval: trueWhen enabled, Pilot waits for a human approval review before auto-merging.
Stale Label Cleanup
Pilot automatically removes orphaned status labels that may remain after restarts or failures:
stale_label_cleanup:
enabled: true
interval: 30m
threshold: 1h
failed_threshold: 24hThe cleaner:
- Runs every
interval - Removes
pilot-in-progressif no active execution exists and label is older thanthreshold - Removes
pilot-failedafterfailed_thresholdto allow automatic retry - Posts a comment explaining the cleanup
Projects V2 Board Sync
Pilot can automatically move issues across your GitHub Projects V2 board columns as tasks progress through the execution pipeline. This keeps your project board in sync without manual card dragging.
Prerequisites
- A GitHub Projects V2 board (classic projects not supported)
- Personal Access Token with
projectscope (classic PAT required — fine-grained tokens don’t support Projects V2 GraphQL API yet)
Board sync requires a classic PAT with the project scope. Fine-grained tokens do not yet support the Projects V2 GraphQL API.
Configuration
adapters:
github:
project_board:
enabled: true
project_number: 5 # From your project URL
status_field: "Status" # Field name (default: "Status")
statuses:
in_progress: "In Progress" # Column for active work
review: "In Review" # Column after PR created (optional)
done: "Done" # Column on merge
failed: "Blocked" # Column on failure (optional)| Field | Type | Default | Description |
|---|---|---|---|
project_board.enabled | bool | false | Enable board sync |
project_board.project_number | int | required | Project number from GitHub URL |
project_board.status_field | string | "Status" | Single-select field name |
project_board.statuses.in_progress | string | — | Status for active execution |
project_board.statuses.review | string | — | Status after PR created |
project_board.statuses.done | string | — | Status on successful merge |
project_board.statuses.failed | string | — | Status on failure |
How It Works
- On task dispatch → moves card to
in_progresscolumn - On PR creation → moves card to
reviewcolumn (if configured) - On merge → moves card to
donecolumn - On failure → moves card to
failedcolumn (if configured)
Finding Your Project Number
Navigate to your project board. The URL is github.com/users/{owner}/projects/{NUMBER} or github.com/orgs/{owner}/projects/{NUMBER}. Use that number.
Status names must match exactly (case-insensitive) — Pilot looks up option IDs by name from your project’s Status field.
Non-Blocking Design
Board sync errors are logged as warnings but never block task execution. If the GraphQL API is down or the project is misconfigured, tasks still run normally.
Performance
- First sync: 3–4 GraphQL calls (resolves project ID, field ID, option IDs)
- Subsequent syncs: 2 calls per update (cached IDs)
- IDs cached for process lifetime — no repeated lookups
Autopilot Integration
Board sync also works with Autopilot. When Autopilot merges a PR, the card moves to Done. When CI fails and Autopilot creates a fix issue, the card moves to Failed.
Partial Configuration
Leave any status empty to skip that transition:
statuses:
in_progress: "" # Skip — don't move on dispatch
review: "" # Skip — don't move on PR creation
done: "Done" # Move to Done on merge
failed: "Blocked" # Move to Blocked on failureMerged PR Guard
Before retrying a failed issue, Pilot checks whether a merged PR already exists for that issue. This prevents wasted execution on work that was already completed (e.g., a PR was merged manually or via a different workflow).
How It Works
- On retry, Pilot searches GitHub:
GH-{number} in:title is:pr is:merged - If a merged PR is found, the issue is marked
pilot-doneand retry is skipped - If no merged PR exists, normal retry proceeds
This guard runs automatically — no configuration needed. It prevents duplicate work when issues are retried after a restart or label cleanup.
Auto-Delete Branches
Pilot automatically deletes remote branches after PR lifecycle events to keep your repository clean.
- On merge: Branch deleted immediately after successful merge
- On close/fail: Branch deleted when PR is closed without merge or execution fails
# Branch cleanup is enabled by default — no configuration requiredBranch names containing slashes (e.g., pilot/GH-42) are URL-encoded before deletion to avoid 404 errors from the GitHub API.
Auto-Rebase on Conflict
When Autopilot detects a merge conflict on a PR, it attempts to resolve it automatically before falling back to a full retry.
Resolution Strategy
- Update branch: Calls the GitHub Update a pull request branch API to merge the latest base branch into the PR branch
- If update succeeds: CI re-runs on the updated branch, normal flow continues
- If update fails (true conflict): Pilot closes the PR, creates a new branch from the latest
main, and retries execution from scratch
Dependency Annotations
When a CI fix creates a new PR that depends on another, Pilot adds a dependency annotation to the PR body:
Depends on: #123This helps reviewers understand the relationship between fix PRs.
Priority Labels
Pilot recognizes priority labels and can process higher-priority issues first:
| Label | Priority | Value |
|---|---|---|
priority:urgent / P0 | Urgent | 1 |
priority:high / P1 | High | 2 |
priority:medium / P2 | Medium | 3 |
priority:low / P3 | Low | 4 |
Task ID Format
GitHub issues are converted to Pilot tasks with the format GH-{number}:
- Issue #42 → Task ID
GH-42 - Branch name:
pilot/GH-42
API Operations
Pilot uses the following GitHub API operations:
| Operation | Endpoint | Purpose |
|---|---|---|
| List issues | GET /repos/{owner}/{repo}/issues | Poll for new issues |
| Get issue | GET /repos/{owner}/{repo}/issues/{number} | Fetch issue details |
| Add labels | POST /repos/{owner}/{repo}/issues/{number}/labels | Mark progress |
| Remove labels | DELETE /repos/{owner}/{repo}/issues/{number}/labels/{name} | Clean up |
| Create PR | POST /repos/{owner}/{repo}/pulls | Submit changes |
| Merge PR | PUT /repos/{owner}/{repo}/pulls/{number}/merge | Complete workflow |
| Add comment | POST /repos/{owner}/{repo}/issues/{number}/comments | Post updates |
| Get check runs | GET /repos/{owner}/{repo}/commits/{sha}/check-runs | Monitor CI |
| Get status | GET /repos/{owner}/{repo}/commits/{sha}/status | Monitor CI |
Troubleshooting
Issue Not Being Picked Up
- Verify the issue has the
pilotlabel - Check that no
pilot-in-progressorpilot-donelabels exist - Ensure the issue is open (not closed)
- Check Pilot logs for polling activity
Webhook Not Triggering
- Verify webhook URL is accessible from GitHub
- Check webhook delivery logs in GitHub (Settings → Webhooks → Recent Deliveries)
- Ensure
webhook_secretmatches between GitHub and Pilot config - Look for signature verification errors in Pilot logs
PR Not Merging
- Check if CI checks are passing
- Verify branch protection rules allow Pilot to merge
- Check if
require_approvalis enabled - Look for merge conflicts in the PR
Rate Limiting
GitHub has API rate limits (5000 requests/hour for authenticated requests). If you hit limits:
- Increase polling interval
- Use webhooks instead of polling
- Check for runaway polling loops