routes.js reads resp.page?.hasMore and resp.page?.nextUrl — the old
HTTP client returned page as an object from parsePageInfo. The CLI
pivot accidentally returned page as a number, making hasMore always
undefined in routes.js. Restore the object shape in both gitea and
gitlab clients. Update tests to assert the object shape.
Fix critical bugs in the CLI transport layer that were masked by
idealized unit test mocks. All fixes verified against live binaries.
Server (gitea/client.js):
- C1: Remove --paginate flag (tea rejects it with exit 1)
- C2: Pass auth via -H 'Authorization: token' header instead of
GITEA_SERVER_TOKEN env (tea ignores that env var)
- C3: Parse --include output from stderr (tea writes headers to
stderr, not stdout)
- H2: Parse Link header for hasMore/pagination
Server (gitlab/client.js):
- C4: Remove /api/v4 prefix (glab adds it automatically; double
prefix caused every call to 404)
- H2: Parse Link header for hasMore/pagination
Tests (both client.test.js):
- H1: Rewrite mocks to match real CLI behavior: headers on stderr
for tea, no --paginate, auth via -H header, Link header parsing
- Add C3, C1, C4 specific regression tests
UI (GiteaSettings, GitLabSettings):
- U4: Replace return null loading state with animated skeleton
(prevents blank flash)
- U2: Surface actual error message in connect failure toast
- U5: Add CLI transport hint below connect form
- U6 (GitLab): Add transport hint for unconnected state
UI (GiteaIssuePickerDialog, GitLabIssuePickerDialog):
- U3: Add retry button when error state is displayed
Live smoke tests passed:
- tea api -H 'Authorization: token WRONG' /user → 401
- tea api /repos/Vibing/openchamber/issues?state=open&limit=2 → 200
- glab api user with GITLAB_TOKEN=dummy → 401 (not 404)
Replace raw fetch transport with CLI subprocess calls:
- Gitea: spawn 'tea api --include' with GITEA_SERVER_TOKEN env var
- GitLab: spawn 'glab api --include' with GITLAB_TOKEN env var
Binary paths env-overridable (TEA_BIN / GLAB_BIN).
8s request timeout via AbortSignal on spawned process.
ETag cache and rate-limit cooldown dropped (tradeoff documented).
Pagination via --paginate for list endpoints.
Tests mock child_process.spawn instead of globalThis.fetch.
Configure a default API base URL per git provider (github/gitlab/gitea)
in settings.json gitProviders, with GitHub Enterprise support (Octokit
baseUrl + device-flow web origin derived from the API base), and replace
the client-side custom-domains list with server-persisted detection URL
chips (SSH/HTTPS forms normalized to hosts). The configured API base host
auto-counts as a detection host. Settings round-trip through the existing
/api/config/settings sanitizer; the UI store hydrates from server settings
with a one-time localStorage migration.
Repo-scoped assignable-user search for GitHub, GitLab, and Gitea, surfaced as
an assignee combobox in the metadata editor and @-mention autocomplete in
forge comment/reply/review surfaces.
- server: GET /api/{provider}/users/search (assignees / project members),
query + directory/override repo resolution, 429 -> 503, connected:false
degradation; GitLab assignee writes resolve login -> ID server-side
- wire: searchUsers (+ searchLabels/milestones/branches/tags) on the three
API clients with tests
- facade: userSearch capability (all three), searchUsers adapters,
mapGithubAssignee/mapGitlabMember/mapGiteaAssignee -> ForgeUser
- ui: ForgeLookupCombobox (keyboard nav, debounced 30s-TTL cache,
connected-only caching), ForgeMentionTextarea (@ token parsing, caret
restore), free-text fallback when lookup is unavailable; i18n in 12 locales
- extras sharing the same infrastructure: GitLab create-issue dialog and
label/milestone/branch/tag lookups in the metadata editor