Loading...
Loading...
Configure GitHub for Cyrus — gh CLI login and git config for PRs, with optional webhook setup to enable @mention responses in PR comments, automated rebases and merges, and auto-fixing based on CI failures (coming soon).
npx skill4agent add cyrusagents/cyrus cyrus-setup-githubReadEditWrite~/.cyrus/.env~/.cyrus/Bashgrepprintf >>ghgh auth status 2>&1git config --global user.name
git config --global user.emailghGitHub is already configured. Skipping to webhook setup.
ghgh auth logingh auth statusWhat name should appear on commits made by Cyrus? (e.g., your name, or "Cyrus Bot")
What email should appear on commits? (e.g., your email, or a noreply address)
git config --global user.name "<name>"
git config --global user.email "<email>"gh auth status
git config --global user.name
git config --global user.emailDo you want Cyrus to respond to GitHub @mentions in PR comments and reviews?
- Yes — enable @mentions: Creates a GitHub App so Cyrus can receive PR comments and reviews via webhooks, respond when @mentioned, and act on "changes requested" reviews.
- No — PRs only: Cyrus will create branches, commits, and PRs but won't respond to comments.
grep -c '^GITHUB_WEBHOOK_SECRET=.' ~/.cyrus/.env 2>/dev/null
grep -c '^GITHUB_APP_ID=.' ~/.cyrus/.env 2>/dev/null
grep -c '^GITHUB_APP_INSTALLATION_ID=.' ~/.cyrus/.env 2>/dev/null
test -f ~/.cyrus/github-app.pem && echo "PEM exists" || echo "PEM missing"grep '^CYRUS_BASE_URL=' ~/.cyrus/.env | cut -d= -f2-CYRUS_BASE_URLAGENT_NAME/cyrus-setupWhat should the GitHub App be named? (e.g., "Cyrus", "My Code Agent")
@your-botBefore choosing a name, check if the same name is available as a GitHub username at. If it is:https://github.com/<name>
- Create a free GitHub user account with that name
- Invite it to your org and/or repo as a collaborator
- Set
to that username (the one users will type in @mentions, notGITHUB_BOT_USERNAME)<slug>[bot]This is the simplest way to get autocomplete working — a silly GitHub limitation, but the known workaround.Without a matching GitHub user, @mentions still work if typed manually, but won't autocomplete. In that case users would need to set up co-authorship (via a git message template orhook addingprepare-commit-msg) to get the bot's name showing as a repo contributor, which is more involved.Co-authored-by:
Where should the GitHub App be created?
- Personal account (github.com/settings/apps)
- Organization — which org? (github.com/organizations/
/settings/apps)<ORG>
What homepage URL should the GitHub App use? This is displayed on the app's settings page as its website. It has no functional impact — GitHub just shows it as a link. Most users use their company website, GitHub org page, or any placeholder URL.(e.g.,, your company URL, or justhttps://github.com/your-org)https://example.com
AGENT_NAMEHOMEPAGE_URLCYRUS_BASE_URL{
"name": "<AGENT_NAME>",
"url": "<HOMEPAGE_URL>",
"redirect_url": "http://localhost:8976",
"hook_attributes": {
"url": "<CYRUS_BASE_URL>/github-webhook",
"active": true
},
"public": false,
"default_permissions": {
"contents": "write",
"issues": "write",
"pull_requests": "write",
"repository_hooks": "write"
},
"default_events": [
"issue_comment",
"organization",
"pull_request_review",
"pull_request_review_comment",
"repository"
]
}redirect_url?code=manifestcodehttps://github.com/settings/apps/newhttps://github.com/organizations/<ORG>/settings/apps/newmanifest""# Escape the manifest JSON for safe embedding in an HTML attribute
MANIFEST_HTML_ESCAPED=$(echo '<MANIFEST_JSON>' | sed 's/"/\"/g')
cat > /tmp/github-app-manifest.html << HTMLEOF
<form method="post" action="<CREATION_URL>">
<input type="hidden" name="manifest" value="$MANIFEST_HTML_ESCAPED">
<p>Click the button to create the GitHub App:</p>
<button type="submit" style="font-size:18px;padding:12px 24px;">Create GitHub App</button>
</form>
HTMLEOF
# Serve the page on a local port (works for headless/remote setups)
python3 -m http.server 8976 --directory /tmp &
HTTP_SERVER_PID=$!
echo "Serving at http://localhost:8976/github-app-manifest.html"kill $HTTP_SERVER_PID 2>/dev/nullclaude-in-chromeagent-browserhttp://localhost:8976/github-app-manifest.htmlcodeagent-browser
- Open
in your browser and click the buttonhttp://localhost:8976/github-app-manifest.html- Review the permissions on GitHub and click Create GitHub App
- After redirect, copy the entire URL from the browser address bar and paste it here
code/app-manifests/<CODE>/conversions# Store the full response temporarily (one-time-use endpoint — do NOT call twice)
gh api /app-manifests/<CODE>/conversions --method POST > /tmp/github-app-response.json
# Extract values (these are all secrets — handle via Bash only)
GITHUB_APP_ID=$(cat /tmp/github-app-response.json | jq -r '.id')
GITHUB_APP_SLUG=$(cat /tmp/github-app-response.json | jq -r '.slug')
GITHUB_WEBHOOK_SECRET=$(cat /tmp/github-app-response.json | jq -r '.webhook_secret')
GITHUB_APP_PEM=$(cat /tmp/github-app-response.json | jq -r '.pem')
# Clean up
rm /tmp/github-app-response.json# Ensure directory exists (may not if running standalone outside cyrus-setup)
mkdir -p ~/.cyrus
# Webhook secret (for signature verification)
printf 'GITHUB_WEBHOOK_SECRET=%s\n' "$GITHUB_WEBHOOK_SECRET" >> ~/.cyrus/.env
# App ID (for token minting)
printf 'GITHUB_APP_ID=%s\n' "$GITHUB_APP_ID" >> ~/.cyrus/.env
# Bot username (for mention filtering — see note below about GitHub autocomplete)
printf 'GITHUB_BOT_USERNAME=%s\n' "$GITHUB_APP_SLUG" >> ~/.cyrus/.env
# Private key (multi-line — stored as a separate file)
printf '%s\n' "$GITHUB_APP_PEM" > ~/.cyrus/github-app.pem
chmod 600 ~/.cyrus/github-app.pem
# Ensure self-hosted mode is active (required for signature verification)
grep -q '^CYRUS_HOST_EXTERNAL=' ~/.cyrus/.env || printf 'CYRUS_HOST_EXTERNAL=true\n' >> ~/.cyrus/.envgrep -c '^GITHUB_WEBHOOK_SECRET=.' ~/.cyrus/.env
grep -c '^GITHUB_APP_ID=.' ~/.cyrus/.env
grep -c '^GITHUB_BOT_USERNAME=.' ~/.cyrus/.env
test -f ~/.cyrus/github-app.pem && echo "PEM exists" || echo "PEM missing"Go to:https://github.com/apps/<GITHUB_APP_SLUG>/installations/newSelect which repositories (or "All repositories") and click Install.
# Re-read GITHUB_APP_ID from .env (shell vars don't persist between blocks)
GITHUB_APP_ID=$(grep '^GITHUB_APP_ID=' ~/.cyrus/.env | cut -d= -f2-)
# Generate a JWT to authenticate as the app
GITHUB_APP_JWT=$(node -e "
const crypto = require('crypto');
const fs = require('fs');
const key = fs.readFileSync(process.env.HOME + '/.cyrus/github-app.pem', 'utf8');
const now = Math.floor(Date.now()/1000);
const header = Buffer.from(JSON.stringify({alg:'RS256',typ:'JWT'})).toString('base64url');
const payload = Buffer.from(JSON.stringify({iat:now-60,exp:now+600,iss:'$GITHUB_APP_ID'})).toString('base64url');
const sig = crypto.createSign('RSA-SHA256').update(header+'.'+payload).sign(key,'base64url');
console.log(header+'.'+payload+'.'+sig);
")
# List installations — if multiple, show all and let user pick
INSTALLATIONS=$(curl -s -H "Authorization: Bearer $GITHUB_APP_JWT" -H "Accept: application/vnd.github+json" https://api.github.com/app/installations)
echo "$INSTALLATIONS" | jq '.[] | {id, account: .account.login}'printf 'GITHUB_APP_INSTALLATION_ID=%s\n' "<INSTALLATION_ID>" >> ~/.cyrus/.envgrep -c '^GITHUB_APP_INSTALLATION_ID=.' ~/.cyrus/.env✓ GitHub CLI authenticated ✓ Git identity configured:<<name>>
✓ GitHub App created:✓ Webhook secret and app credentials saved to<GITHUB_APP_SLUG>✓ Private key saved to~/.cyrus/.env✓ App installed (installation ID:~/.cyrus/github-app.pem) ✓ Cyrus will respond to<GITHUB_APP_INSTALLATION_ID>mentions in PR comments@<GITHUB_BOT_USERNAME>