Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 26 additions & 0 deletions .claude/scripts/bot-gh.sh
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,30 @@ if [ -f "$root/.env" ]; then
fi
: "${GH_BOT_TOKEN:?GH_BOT_TOKEN not set — add it to .env (see setup notes in this script)}"

# Preflight: the bot needs collaborator access to EACH (private) repo it acts on
# (setup step 2). Without it, gh fails with an opaque
# "Could not resolve to a Repository with the name '<owner>/<repo>'" that reads like
# a typo, not a missing grant. If a --repo target is given and the bot can't see it,
# print the exact one-time grant + invite-accept commands instead.
target_repo=""
prev=""
for a in "$@"; do
if [ "$prev" = "--repo" ]; then target_repo="$a"; break; fi
case "$a" in
--repo) prev="--repo"; continue;;
--repo=*) target_repo="${a#--repo=}"; break;;
esac
done
if [ -n "$target_repo" ] && ! GH_TOKEN="$GH_BOT_TOKEN" gh repo view "$target_repo" >/dev/null 2>&1; then
bot="$(GH_TOKEN="$GH_BOT_TOKEN" gh api user --jq .login 2>/dev/null || echo '<bot>')"
cat >&2 <<EOF
bot-gh.sh: bot account '$bot' cannot access '$target_repo' (private repo + not a collaborator?).
One-time setup — run as the repo OWNER, then accept the invite as the bot:
gh api -X PUT repos/$target_repo/collaborators/$bot -f permission=push
id=\$("$0" api user/repository_invitations --jq ".[] | select(.repository.full_name==\"$target_repo\") | .id")
"$0" api -X PATCH user/repository_invitations/\$id
EOF
exit 1
fi

GH_TOKEN="$GH_BOT_TOKEN" exec gh "$@"
6 changes: 6 additions & 0 deletions docs/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,12 @@ With `pr-per-agent`, the standing loop per ticket looks like:
`bot-gh.sh` (free machine account → collaborator → classic `repo`-scope PAT → `GH_BOT_TOKEN` in `.env`).
Reuse ONE generically-named bot across all your repos — GitHub ToS allows one free machine account per
person. Only `pr create` uses the bot; commits/pushes stay on the owner's auth.
**Per-repo grant (easy to miss):** the bot must be a **collaborator on every (private) repo** it opens PRs
in — adding it once to one repo does *not* cover the rest. Without it, `gh` fails with an opaque
`Could not resolve to a Repository with the name '<owner>/<repo>'` (looks like a typo, is actually a
missing grant). `bot-gh.sh` preflights this and prints the fix; the one-time setup is, as the **owner**:
`gh api -X PUT repos/<owner>/<repo>/collaborators/<bot> -f permission=push`, then **accept as the bot**:
`bot-gh.sh api -X PATCH user/repository_invitations/<id>` (private-repo invites require acceptance).
4. **Review** — the owner reviews on GitHub. To address comments, feed them back through the orchestrator
(*"address the comments on PR #N"*): same implementer loop, same branch, push updates the PR in place.
5. **Merge** — owner approves, merge per `gates.json.merge`, clean the worktree (below).
Expand Down