Part of #224. Depends on the repo_aliases table + the query-time grouping.
CRUD endpoints the CLI and web app both drive. Must accept the same auth as the rest of the dashboard — both Bearer api-key (CLI, backend/crates/api/src/main.rs:1851 resolver) and Supabase JWT (web).
Routes (register near backend/crates/api/src/main.rs:183)
GET /v1/repo-groups → list current aliases grouped by group_name: [{ group, members: [repo,...] }].
POST /v1/repo-groups → body { group, repos: [repo,...] }. Upserts one alias row per repo (on conflict (user_id, repo) do update set group_name). Validates non-empty group + repos; trims; rejects a repo that equals an existing other group name to avoid cycles.
DELETE /v1/repo-groups/:group → remove all aliases for that group (unmerge → repos split back to raw).
- (optional)
DELETE /v1/repo-groups/:group/members/:repo → pull a single repo out.
Acceptance
- Round-trip: POST a group, GET reflects it,
/v1/repos folds, DELETE restores the split.
- Auth: rejects unauthenticated; works with CLI Bearer key and web JWT.
- Idempotent POST (re-merging the same set is a no-op).
Part of #224. Depends on the
repo_aliasestable + the query-time grouping.CRUD endpoints the CLI and web app both drive. Must accept the same auth as the rest of the dashboard — both Bearer api-key (CLI,
backend/crates/api/src/main.rs:1851resolver) and Supabase JWT (web).Routes (register near
backend/crates/api/src/main.rs:183)GET /v1/repo-groups→ list current aliases grouped bygroup_name:[{ group, members: [repo,...] }].POST /v1/repo-groups→ body{ group, repos: [repo,...] }. Upserts one alias row per repo (on conflict (user_id, repo) do update set group_name). Validates non-empty group + repos; trims; rejects a repo that equals an existing other group name to avoid cycles.DELETE /v1/repo-groups/:group→ remove all aliases for that group (unmerge → repos split back to raw).DELETE /v1/repo-groups/:group/members/:repo→ pull a single repo out.Acceptance
/v1/reposfolds, DELETE restores the split.