fj/cmd
sid 133fb2fea4 feat(cmd): pagination unification + fj api --paginate
Before this, only `release list` walked pages. `repo list`, `pr list` (the
non-filter branch), and `issue list` all passed `PageSize: limit` directly
to the gitea SDK — which silently caps PageSize at 50, so any request for
more than 50 results was truncated to 50 with no warning. `--limit` was
effectively a per-page hint, not a real limit.

## Changes

- New `cmd/paginate.go` — generic `paginateGitea[T any]` that walks pages
  until the response is short or the limit is reached. Uses Go 1.20
  generics so each list command keeps its existing typed slice without
  conversion overhead.

- `repo list` — paginates ListUserRepos.
- `pr list` — paginates ListRepoPullRequests in both branches:
  - With client-side filters (assignee, author, labels, search, draft,
    head, base): pull all pages then filter+limit.
  - Without filters: paginate up to limit.
- `issue list` — paginates ListRepoIssues. Overshoots 2x because the API
  returns both issues AND PRs and we filter PRs out client-side; the
  overshoot keeps us bounded but reduces the chance of returning fewer
  results than `--limit`.

## `fj api --paginate`

Mirrors `gh api --paginate`:
- Follows RFC 5988 `Link: rel="next"` headers (Forgejo emits these on
  list endpoints).
- Concatenates each page's JSON array into a single array via
  `concatPaginatedJSON`. If a page is not a JSON array, errors with a
  clear message — `--paginate` only makes sense for paginatable endpoints.
- GET-only (errors on POST/PUT/DELETE).
- Reuses the same auth and custom headers across pages; the body-size
  limit applies per-page.

Refactored the request execution into a `doOnce` closure so the loop body
isn't a copy of the single-request path.

Verified live:

  $ fj api 'repos/public/claude-code-proxy/commits?limit=2' \
        --paginate --jq '. | length'
  44

(44 = total commits in the repo, walked via Link headers from a 2-per-page
starting query.)

Out of scope for this commit, deferred:
- De-duplicating cmd/aliases.go ↔ cmd/actions.go subtrees (the type
  mismatch they caused is already fixed in the prior commit; the
  duplication itself is polish).
2026-05-02 15:46:22 -06:00
..
actions.go rename fgj to fj 2026-04-26 08:16:52 -06:00
actions_test.go feat: implement workflow list/view/run 2026-01-16 10:52:15 +01:00
aliases.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
api.go feat(cmd): pagination unification + fj api --paginate 2026-05-02 15:46:22 -06:00
auth.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
completion.go rename fgj to fj 2026-04-26 08:16:52 -06:00
errors.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
ios_init.go rename fgj to fj 2026-04-26 08:16:52 -06:00
issue.go feat(cmd): pagination unification + fj api --paginate 2026-05-02 15:46:22 -06:00
json.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
label.go rename fgj to fj 2026-04-26 08:16:52 -06:00
manpages.go complete fgj → fj rename: env vars, config migration, docs 2026-04-26 08:23:48 -06:00
milestone.go rename fgj to fj 2026-04-26 08:16:52 -06:00
paginate.go feat(cmd): pagination unification + fj api --paginate 2026-05-02 15:46:22 -06:00
pr.go feat(cmd): pagination unification + fj api --paginate 2026-05-02 15:46:22 -06:00
pr_checks.go rename fgj to fj 2026-04-26 08:16:52 -06:00
pr_diff.go rename fgj to fj 2026-04-26 08:16:52 -06:00
pr_review.go rename fgj to fj 2026-04-26 08:16:52 -06:00
release.go rename fgj to fj 2026-04-26 08:16:52 -06:00
repo.go feat(cmd): pagination unification + fj api --paginate 2026-05-02 15:46:22 -06:00
repo_create_test.go feat: implement repo create command 2026-03-13 17:44:44 +01:00
root.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00
wiki.go fix(cmd): correctness + audit hardening across cmd/ + internal/ 2026-05-02 15:41:48 -06:00