Summary
When working in a forked repository and opening pull requests against the
upstream repo, Supacode does not display any PR status for the related
worktrees, and PR actions from the command palette (Merge / Close / Mark
ready for review) silently act on the wrong repository.
This is different from #37 / #229, which handled filtering fork PRs inside
the GraphQL response. The issue here is that Supacode picks the wrong
repository to query in the first place.
Symptom
In a local clone where the user pushes branches to their fork and the PRs
are opened against the upstream repo:
- Worktree rows show no PR badge and no CI ring, even when a PR exists.
- "Merge PR", "Close PR", and "Mark ready for review" from the command
palette either no-op or fail — they run gh pr … against the fork,
where the PR does not exist.
Root cause
GitClient.remoteInfo(for:) at supacode/Clients/Git/GitClient.swift:433-468
prefers the origin remote and falls back to other remotes only when
origin is missing or non-GitHub. In a fork workflow origin points at
the fork, so the resolved (host, owner, repo) is the fork's.
That tuple flows into GithubCLIClient.batchPullRequests
(supacode/Clients/Github/GithubCLIClient.swift:232-256), which queries
the fork's repository via GraphQL and returns [:] since no PRs live
there. RepositoriesFeature.refreshRepositoryPullRequests
(supacode/Features/Repositories/Reducer/RepositoriesFeature.swift:2566-2602)
then sets pullRequest = nil for every worktree.
The mutation fetchers (mergePullRequest, closePullRequest,
markPullRequestReady at GithubCLIClient.swift:259-311) similarly
invoke gh inside repoRoot without --repo, so they act on the fork.
Relying on remote names to recover is not a viable workaround: remote
naming is a convention, not a rule. Users routinely have remotes named
`upstream`, `source`, the original author's username, the organization
name, etc.
The information Supacode needs is already provided by `gh`
`gh` resolves the "default" repository for a local clone (upstream of a
fork, PR target, etc.) using its own heuristics and exposes the result
through `gh repo view`. In a clone with multiple remotes and no explicit
`gh repo set-default`, `gh repo view --json owner,name` returns the
upstream owner and name, and `gh pr list --head ` correctly lists
PRs from the upstream repository. The same resolution is applied whenever
`gh pr merge/close/ready` run in the repo without `--repo`.
Supacode is effectively bypassing this resolution by computing its own
`(host, owner, repo)` from the remote list and passing it explicitly.
Suggested direction
- Replace the hand-rolled `origin`-first logic in `GitClient.remoteInfo`
with a call to `gh repo view --json owner,name` executed in `repoRoot`,
falling back to the current remote parsing only if `gh` is unavailable.
- Pass the resolved `owner/name` to `batchPullRequests` so the GraphQL
query hits the right repository. The existing fork-fallback logic in
`pullRequestsByBranch` continues to apply.
- Either drop the explicit repo arguments from the PR mutation fetchers
and let `gh` resolve `repoRoot`, or pass `--repo /` with
the value from step 1.
- Keep CI-related calls (`gh run list`, rerun failed jobs, view run
logs) on `repoRoot`. Workflow runs live on whichever remote received
the push — in fork workflows that is the fork — and the current
`gh run list` without `--repo` already targets it correctly.
An optional override in Repository Settings (similar to "Merge strategy")
would cover edge cases where `gh`'s resolution doesn't match the user's
intent, but the default behavior should work out of the box.
Reproduction
- Fork any upstream repository you don't own and clone the fork.
- Add the upstream as an additional remote.
- Open the clone in Supacode, create a worktree, push a branch, open a
PR against the upstream on GitHub.
- Observe: the worktree shows no PR; command palette actions on the PR
do not affect the upstream PR.
Environment
- Supacode `main` at `7981cf3`
- macOS 26.x
- `gh` 2.x
Affected files
- `supacode/Clients/Git/GitClient.swift`
- `supacode/Clients/Github/GithubCLIClient.swift`
- `supacode/Features/Repositories/Reducer/RepositoriesFeature.swift`
Summary
When working in a forked repository and opening pull requests against the
upstream repo, Supacode does not display any PR status for the related
worktrees, and PR actions from the command palette (Merge / Close / Mark
ready for review) silently act on the wrong repository.
This is different from #37 / #229, which handled filtering fork PRs inside
the GraphQL response. The issue here is that Supacode picks the wrong
repository to query in the first place.
Symptom
In a local clone where the user pushes branches to their fork and the PRs
are opened against the upstream repo:
palette either no-op or fail — they run
gh pr …against the fork,where the PR does not exist.
Root cause
GitClient.remoteInfo(for:)atsupacode/Clients/Git/GitClient.swift:433-468prefers the
originremote and falls back to other remotes only whenoriginis missing or non-GitHub. In a fork workfloworiginpoints atthe fork, so the resolved
(host, owner, repo)is the fork's.That tuple flows into
GithubCLIClient.batchPullRequests(
supacode/Clients/Github/GithubCLIClient.swift:232-256), which queriesthe fork's repository via GraphQL and returns
[:]since no PRs livethere.
RepositoriesFeature.refreshRepositoryPullRequests(
supacode/Features/Repositories/Reducer/RepositoriesFeature.swift:2566-2602)then sets
pullRequest = nilfor every worktree.The mutation fetchers (
mergePullRequest,closePullRequest,markPullRequestReadyatGithubCLIClient.swift:259-311) similarlyinvoke
ghinsiderepoRootwithout--repo, so they act on the fork.Relying on remote names to recover is not a viable workaround: remote
naming is a convention, not a rule. Users routinely have remotes named
`upstream`, `source`, the original author's username, the organization
name, etc.
The information Supacode needs is already provided by `gh`
`gh` resolves the "default" repository for a local clone (upstream of a
fork, PR target, etc.) using its own heuristics and exposes the result
through `gh repo view`. In a clone with multiple remotes and no explicit
`gh repo set-default`, `gh repo view --json owner,name` returns the
upstream owner and name, and `gh pr list --head ` correctly lists
PRs from the upstream repository. The same resolution is applied whenever
`gh pr merge/close/ready` run in the repo without `--repo`.
Supacode is effectively bypassing this resolution by computing its own
`(host, owner, repo)` from the remote list and passing it explicitly.
Suggested direction
with a call to `gh repo view --json owner,name` executed in `repoRoot`,
falling back to the current remote parsing only if `gh` is unavailable.
query hits the right repository. The existing fork-fallback logic in
`pullRequestsByBranch` continues to apply.
and let `gh` resolve `repoRoot`, or pass `--repo /` with
the value from step 1.
logs) on `repoRoot`. Workflow runs live on whichever remote received
the push — in fork workflows that is the fork — and the current
`gh run list` without `--repo` already targets it correctly.
An optional override in Repository Settings (similar to "Merge strategy")
would cover edge cases where `gh`'s resolution doesn't match the user's
intent, but the default behavior should work out of the box.
Reproduction
PR against the upstream on GitHub.
do not affect the upstream PR.
Environment
Affected files