0084: Chat reads pull requests through your gh
On this page
Status: accepted
Context
When someone asks about their changes, the useful question is often about the pull request, not the working tree: what reviewers said, which checks failed, what's in it. That lives on GitHub, which needs the person's credentials. Short Leash keeps no secrets in its config (invariant 9), and chat never sends data out (invariant 15, 0062). Developers already sign in to GitHub's own command, gh. This is the first chat tool that reaches the internet and the first time Short Leash runs another program with the user's credentials, so it's recorded apart from the on-device model work in 0080.
Decision
- The user's own
gh, read-only. Short Leash runs theghit finds on the login shell'sPATH, as it finds the programs a site runs, and onlygh pr view --json …andgh pr diff(changes::pull_request). The repository is named with--repo owner/name, taken from the GitHub address of the branch's push remote (itspushRemote,remote.pushDefault, the remote it tracks, elseorigin), then, for a fork, a remote namedupstream, asked forowner:branch. The branch goes after--.ghruns in Short Leash's own folder, never the repository's, so it doesn't read the repository's Git config or run Git in it. Nevergh api,gh auth tokenor anything that posts. - An environment built from nothing.
ghgets only an allowlist from leashd's environment (GH_PASS_ENV: where its sign-in and config are, the proxy and certificates, and what Windows needs to start a program), so noGH_TOKEN,GITHUB_TOKEN,GH_REPO,GH_HOST, debugging or browser variables reach it. Then fixed values (GH_FIXED_ENV):GH_HOST=github.com, and prompts, pagers, color, the spinner and update notices off. It's stopped after 20 seconds (GH_TIMEOUT) or once it prints more than 8 MiB (GH_MAX_OUTPUT), and on Windows it starts without a console window. Short Leash never readsgh's files, never asks it for a token and never stores one:ghreads its own sign-in from the Keychain or Credential Manager. - Off until the person turns it on. Let chat read pull requests from GitHub in Settings › Chat (
[ai] chat_pull_requests, off by default). The engine checks it before anything runs, before even looking forgh(Engine::read_pull_request_on), so chat,leash mcpand the Changes page share one switch, and no tool can change it. One switch, because the data is the person's own pull request, and an assistant's question must never take Short Leash online without their choice. When chat asks while it's off, the read's row says so with Turn on: that row comes from the engine, never the model, and turning on goes through a confirm that says what goes online, with Cancel focused. - The answer is outside text. Every text is capped (title 300 characters, description 8,000, the latest 30 reviews and 30 comments of 2,000 each, the first 300 files) and has invisible characters removed; file paths and diffs show them as codes such as
⟦U+200B⟧instead (parse_view, thepull_requesttool). Open on GitHub is built from the repository and number, never from the address in the answer. Over MCP the answer carriesoutside_text: trueand anaboutnote; in chat the read marks the chat Has outside text. Chat sizes it to the model like every diff read (0080). - Kept a minute, in memory. leashd reuses a pull request for 60 seconds (
PULL_REQUEST_FRESH_FOR) when asked again about the same repositories and branch, unless Refresh asks again; a read that fails, and turning the setting off, forget them. Nothing is written to disk beyond what a chat already keeps (its messages, 90 days). - GitHub only, and only what
gh pr viewshows. github.com, not other hosts. Comments on single lines needgh apiand aren't read. - How invariant 15 reads. This tool sends GitHub only the repository, the branch (
owner:branchfor a fork) and, for a diff, the pull request's number, under the person's own sign-in, after they turned it on. It's the one exception to "chat never sends data out", and CLAUDE.md's invariant 15 names it. It reads and never changes anything, so it'sread, notnever_in_chat.
Consequences
- Short Leash handles no GitHub token, and needs no OAuth app of its own.
- It needs
ghinstalled and signed in; each failure says what to run (gh auth login,gh auth status) or to check the connection. - An answer can be a minute old.
- GitHub Enterprise and other hosts aren't covered, and an organization that blocks
ghblocks this too. - A pull request's text is written by other people. It stays data with the outside text mark, and chat can't act on it without asking (0062).