Shim gh pr list and gh repo view for Forgejo repositories

Added Forgejo-backed implementations for the two commands that were still falling through to stock GitHub CLI, so Codex.app has more of the GitHub-shaped metadata it expects in Forgejo-only repositories.

Date: 2026-05-31 Beads: gh-forgejo-shim-ini Validation: 45 unit tests passing

Summary

The shim now routes gh pr list and gh repo view for allowlisted Forgejo hosts. Both commands return compact GitHub-shaped JSON for the fields most likely to be requested by Codex.app and similar tools.

Changes Made

Context

Codex.app showed “GitHub CLI unavailable” even though the installed wrapper was healthy and gh pr create, gh pr view, and gh pr status worked. The missing piece was that gh pr list and gh repo view were outside the supported routing table, so they fell through to the real GitHub CLI, which cannot understand a Forgejo-only remote.

During documentation, the first draft used client-side Diffs rendering with a plain fallback. That was corrected to static @pierre/diffs/ssr output, and the global Codex instructions were clarified to make SSR mandatory for future diff snippets.

Important Implementation Details

The route decision remains conservative: unsupported commands still delegate to the real GitHub CLI. The new behavior only activates when the repository is detected and its host is allowlisted in shim configuration.

gh pr list uses the existing Forgejo pull listing endpoint, then normalizes each pull with the same GitHub-shaped PR field mapping already used by view and status. gh repo view uses the Forgejo repository endpoint and returns a small repository field subset.

Compatibility note: search-oriented list flags such as --author, --label, and --search are accepted to avoid immediate CLI failure, but they are not yet fully implemented as Forgejo filters.

Relevant Diff Snippets

The turn document uses the Diffs SSR path from diffs.com/docs: the patch was pre-rendered with @pierre/diffs/ssr via preloadPatchDiff, so this section remains readable offline without client-side JavaScript.

src/gh_forgejo_shim/routing.py
-2+20
27 unmodified lines
22
23
24
25
23 unmodified lines
42
43
44
45
77 unmodified lines
111
112
113
114
115
114
115
116
117
27 unmodified lines
parse_repo_spec,
)
SUPPORTED_PR_COMMANDS = {"create", "new", "status", "view"}
23 unmodified lines
cwd: str | None = None,
) -> RouteDecision:
if len(argv) < 2 or argv[0] != "pr" or argv[1] not in SUPPORTED_PR_COMMANDS:
return RouteDecision("delegate", "unsupported command")
77 unmodified lines
rest = argv[2:]
if command in {"create", "new"}:
return _run_create(rest, repo, client, cwd=cwd, stdout=stdout, stdin=stdin)
if command == "view":
return _run_view(rest, repo, client, cwd=cwd, stdout=stdout)
return 1
def run_forgejo_pr(
27 unmodified lines
28
29
30
31
32
23 unmodified lines
59
60
61
62
77 unmodified lines
143
144
145
146
147
148
149
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
27 unmodified lines
parse_repo_spec,
)
SUPPORTED_PR_COMMANDS = {"create", "list", "new", "status", "view"}
SUPPORTED_REPO_COMMANDS = {"view"}
23 unmodified lines
cwd: str | None = None,
) -> RouteDecision:
if len(argv) < 2 or not _is_supported_command(argv):
return RouteDecision("delegate", "unsupported command")
77 unmodified lines
rest = argv[2:]
if command in {"create", "new"}:
return _run_create(rest, repo, client, cwd=cwd, stdout=stdout, stdin=stdin)
if command == "list":
return _run_list(rest, repo, client, stdout=stdout)
if command == "view":
return _run_view(rest, repo, client, cwd=cwd, stdout=stdout)
return 1
def run_forgejo(
argv: list[str],
repo: RepoRef,
client: ForgejoClient,
*,
cwd: str | None = None,
stdout: TextIO,
stderr: TextIO,
stdin: TextIO | None = None,
) -> int:
if argv[0] == "repo":
return run_forgejo_repo(argv, repo, client, stdout=stdout, stderr=stderr)
return run_forgejo_pr(argv, repo, client, cwd=cwd, stdout=stdout, stderr=stderr, stdin=stdin)
def run_forgejo_pr(

Expected Impact for End-Users

Tools that probe repository and PR metadata through gh pr list or gh repo view should get Forgejo-backed answers instead of a GitHub remote error. In Codex.app, this should reduce false “GitHub CLI unavailable” fallbacks for supported Forgejo PR workflows.

Validation

Issues, Limitations, and Mitigations

Follow-up Work