# hive-forge CLI `hive-forge` is the Forgejo API wrapper available in every agent container (installed via `nix/agent-modules/forge.nix`; lives in `/hive-forge` as a proper Rust binary). Use it instead of ad-hoc curl pipelines. ## Credentials and repo defaults - Credentials: `$HYPERHIVE_STATE_DIR/forge-token` - Active repo resolves, highest priority first: global `-r/--repo` flag > the `origin` remote of the cwd's git checkout > `$HIVE_FORGE_REPO` (last-resort override, unset by default) > a hard error. No single repo is assumed by default — see `client::Client::from_env`. ## Verbs **Kind-namespaced commands (preferred):** issue/PR operations are grouped under `issue` and `pr` parent commands — `hive-forge pr close 42`, `hive-forge issue create --title …`, `hive-forge pr status --pr 42`. The `pr ` / `issue ` forms validate the number's kind (e.g. `pr close` refuses an issue number, which the old generic `close` couldn't). Run `hive-forge pr --help` / `hive-forge issue --help` for the full subcommand list (show/create/edit/status/merge/reviews/commits/diff/view/comment/ comments/close/reopen/labels/assign/dependency/timeline as applicable). The flat forms below (`close 42`, `pr-create …`, `pr-status …`, …) still work as **hidden back-compat aliases** during the transition and are dropped from `--help`; prefer the namespaced form. They'll be removed in a later sweep. ```bash hive-forge pr close 42 # close a PR (kind-validated) hive-forge pr reopen 42 # reopen a closed PR (kind-validated) hive-forge issue close 42 # close an issue (kind-validated) hive-forge issue reopen 42 # reopen a closed issue (kind-validated) hive-forge pr status --pr 42 # PR health (mergeable / CI / reviews) hive-forge issue create --title "..." --body "..." # --- flat aliases below remain valid (hidden) --- hive-forge view 42 # title + body + comments (marks thread read) hive-forge comments 42 # list all comments (human-readable; marks thread read); for PRs, review bodies merged in hive-forge comments 42 --tail 10 # last 10 comments (count-then-page; efficient on long threads) hive-forge --json comments 42 # same as above, JSON array (global flag) hive-forge comment 42 --body "..." # post comment (inline body) hive-forge comment 42 --body-file - < # CI-only fast path for an explicit commit sha hive-forge pr-merge 42 # merge (refuses unless mergeable + CI not red + no changes-requested); deletes head branch hive-forge pr-merge 42 --method rebase # rebase-merge instead of a merge commit (no squash option) hive-forge pr-merge 42 --keep-branch --force # keep the head branch; override the readiness gate hive-forge timeline 42 # audit trail: closes, label changes, assignments, commit refs hive-forge attach-issue 42 /path/to/file # upload a file attachment to an issue; prints download URL hive-forge attach-comment 18042 /path/to/file # upload a file attachment to a comment; prints download URL hive-forge attachment-get # download an attachment; prints resolved path to stdout hive-forge artifact-get pr1ma-paper-pdf --run 51 # download a CI run's Actions artifact zip (run number from the run-page URL) hive-forge ci-log --run 51 # print a CI run's job step logs (run number from the run-page URL); --job i / --step i to narrow hive-forge ci-rerun --pr 42 # re-run CI without an empty commit (dispatches a fresh run; --run n / --branch name also work) hive-forge subscription --watch # subscribe to repo notifications hive-forge subscription --unwatch # unsubscribe hive-forge subscription --list # list every repo you watch (audit the notification firehose) hive-forge -r internal/knowledge clone # clone with creds auto-injected hive-forge -r internal/knowledge pr-create --agit --topic foo --title "..." # open PR via AGit (no fork) # --- repo management --- hive-forge repo-create my-repo # create under authenticated user; prints URL hive-forge repo-create my-repo --org myorg --private --auto-init # under an org, private, with initial commit hive-forge repo-add-collaborator alice # add write collaborator (default permission) hive-forge repo-add-collaborator alice --permission read # read-only collaborator hive-forge repo-labels # list every label defined on the repo hive-forge repo-labels bug # filter by name substring hive-forge repo-labels --json # full label objects (id, name, color, description) ``` ### Which read verb when Several verbs read an issue/PR and look overlapping, but each has a distinct output shape — pick by what you need: | Need | Verb | Shape | | ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The full human view of one item — title, body, **and** every comment in order | `view ` | prose dump; **marks the thread read** (clears the read-before-comment guard). Top-level `view` auto-detects issue-or-PR by number; scoped `issue view ` / `pr view ` do the same when you already know the kind | | Just the structured metadata of one item (state, labels, assignees, head sha…) | `issue ` / `pr ` | JSON object | | Only the comments (skip the body), e.g. to catch up a long thread; for PRs, review bodies (approve/request-changes/comment summaries) are merged in too | `comments ` [`--tail N`] | comment + review-body list; **marks read** | | One specific comment by its id | `comment-show ` | single comment | | The audit trail — closes, label/assignee changes, commit refs, merges (no comment bodies) | `timeline ` | event list | | The code change | `diff ` | unified diff | | Is this PR ready to merge — mergeable + CI + reviews | `pr-status --pr ` | health view; exit code = merge-readiness | | The review verdicts + inline review comments | `pr-reviews ` | review list | Rule of thumb: **`view` first** when you're about to act on a thread (it shows everything and clears the unread guard so your follow-up `comment` isn't rejected); the JSON verbs (`issue`/`pr`) when a script needs one field; `pr-status` when the only question is "can I merge it". ### Contributing to a read-only repo (`clone` + `pr-create --agit`) Agents are read-only collaborators on some repos (e.g. `internal/knowledge`) and so can't push branches. Forgejo's AGit flow lets a read-only user open a PR by pushing the current `HEAD` to the magic ref `refs/for//`. Two verbs cover the workflow: ``` hive-forge -r internal/knowledge clone # clone with token auto-injected cd knowledge # add / edit / delete any files, then commit normally git add -A && git commit -m "add foo runbook" hive-forge -r internal/knowledge pr-create --agit \ --topic foo-runbook \ # groups pushes into ONE PR; reuse to update it --title "add foo runbook" \ [--body "details"] # PR description (also accepts --body-file) ``` `clone` derives the dest dir from the repo basename (override with a positional arg); `--branch` / `--depth` are passed through. The token is injected into the clone's `origin` remote so `pr-create --agit` (default remote `origin`) can push without re-auth. `pr-create --agit` prints the PR URL. Re-running with the same `--topic` force-updates the existing open PR (the AGit ref is agent-owned scratch). Opens a reviewable PR the operator merges — never commits straight to `main`. `hive-forge --help` prints the full signature for any verb. ### `pr-status` One-stop PR health view (`--pr `): mergeable state, CI checks, requested reviewers + review verdicts, and the last-comment timestamp — the things you need to know whether a PR is ready to merge (CI must pass before merge). `--sha ` is a CI-only fast path for a raw commit. A failing/erroring CI context prints its job link. The process exit code is a **merge-readiness** verdict for `--pr` (0 only when CI is green AND the PR is mergeable AND no review requests changes), so it composes: `hive-forge pr-status --pr 42 && echo ready`. `--sha` mirrors the CI verdict alone. ``` hive-forge pr-status --pr 42 # PR #42: feat(...): ... # state: open (mergeable: yes) # CI: e39a87ea3949: ✅ success (1 context(s)) # ✅ CI / nix flake check (pull_request): success — Successful in 1m50s # reviewers: (none requested) # reviews: ✅ argus: APPROVED # last comment: 2026-06-05T19:13:28+02:00 by argus ``` Note: review verdicts come from _formal_ Forgejo reviews (the approve / request-changes API). Reviewers who post their verdict as a plain comment show under `last comment`, not `reviews`. ### Repo management Agents **cannot create repos directly via forge token** — Forgejo disables push-to-create and the agent token doesn't have the Create scope. Two paths exist depending on where the repo should live: **Agent repos (`agents/`)** — Use the `mcp__hyperhive__create_repo` MCP tool (requires the `forge` tool group). hive-c0re creates the repo in the c0re-owned `agents/` org, adds you as a write collaborator (not owner), and enables branch protection (operator-team merge approval required — you cannot self-merge). Clone URL is returned immediately. This is the standard path for agents that need a working repo. **Other repos** — Use the CLI verbs below (`repo-create` / `repo-add-collaborator`). These use the agent's own forge token so the repo lands under the agent's user account or an org the agent belongs to. **`repo-create `** — create a repo under the authenticated user and print its URL. Key flags: - `--org ` — create under an org namespace instead - `--private` — private repo (default: public) - `--auto-init` — seed an initial commit so the repo is non-empty and immediately cloneable; omit for a bare repo you push into - `--description ` — repo description **`repo-add-collaborator `** — grant a forge user access to the active repo (see the repo-resolution chain above). Companion to `repo-create`. The `--permission` flag accepts `read` / `write` (default) / `admin`. `hive-c0re` uses this internally when an agent's config repo is initialised. **`repo-labels [PATTERN]`** — list every label defined on the repo, optionally filtered by a name substring (case-sensitive). Distinct from `labels ` which shows the labels on one specific issue/PR. Use this to discover valid label names before triaging or to audit the label set. `--json` emits full label objects (id, name, color, description). ## Notes - **Read-before-comment guard:** `comment` refuses to post when forge still has an unread notification for the thread (i.e. someone commented since you last read it). This prevents accidentally replying to old activity without seeing the new context. Read the thread with `hive-forge view ` or `hive-forge comments ` (which mark the notification read and clear the block), or pass `--force` to skip the guard. - `comment --body "..."` with backticks in the body: always use `--body-file -` with a HEREDOC to avoid shell escaping issues. - `pr-create --push` also runs `git push forge ` and suppresses the post-push "Create a pull request" hint (we print the canonical URL ourselves). - `attachment-get` saves to `/tmp/forge-attachment-{uuid}` by default and prints the resolved path. Pass `-o -` to stream to stdout. - `artifact-get --run ` downloads a CI Actions artifact. `` is the run number from the run-page URL (`/actions/runs/`, which `pr-status` surfaces as a CI context's target_url). Forgejo 15 serves artifacts only via the web route, not REST, and that route is keyed by the run's internal global id, so the verb translates the run number first. Saves a zip to `/tmp/forge-artifact-.zip` by default; pass `-o -` to stream to stdout. - `ci-log --run [--job i] [--step i] [--attempt n]` prints a CI run's job step logs. `` is the run number from the run-page URL (same value `artifact-get` takes; `pr-status` surfaces it as a CI context's target_url). Two log sources are tried in order: first the web run-view **streamer** the run page polls (rich per-step framing, honors `--step`) — but that reads the live `act_runner` task record, which Forgejo prunes once a run completes; then, when the streamer is pruned (500 / no lines), the **durable persisted-log download** the run page's "view raw logs" link uses (`…/runs//jobs//attempt//logs`), a flat whole-job log that survives the prune (`--step` is not honored on this path). So quick / older runs that the streamer can no longer serve still print instead of erroring. `--job` selects the job (0-based, default 0); `--attempt` picks the run attempt for the durable path (default 1; re-runs increment it). `--json` wraps the output. - `ci-rerun` re-runs CI without pushing an empty commit (the old retrigger path, which littered PR history). Forgejo has no token-usable REST endpoint to re-run an _existing_ run (the run-page rerun buttons are CSRF-gated web routes a token POST 404s), so this dispatches a **fresh** run of the workflow via the workflow-dispatch API (`POST …/actions/workflows//dispatches {"ref":""}`). Resolve the branch with exactly one of: `--pr ` (the PR's head branch), `--run ` (the same run number `ci-log` / `artifact-get` take — resolves the branch + workflow from that run), or `--branch ` (directly). `--workflow ` picks the workflow file for `--pr` / `--branch` (default `ci.yml`). Dispatch re-runs the whole workflow — there is no single-job variant. - `issue dependency add ` / `pr dependency add ` set "blocked by" links via Forgejo's dependency feature — the operator's preferred way to track blocking relationships over labels (labels can go stale; dependencies are structured and show in the forge UI's dependency panel). Same-repo only, matching the web UI (no cross-repo dependency support). `list` (the default action) and `add`/ `remove` all print the resulting dependency list as JSON. - Do NOT use raw `curl` for forge access -- the CLI handles auth, error checking, and output formatting. - `issue-create --label ` / `pr-create --label ` are repeatable and take the same spelling `labels add` does. **An unknown name is an error, not a silent drop** — the command fails listing the names that didn't resolve plus every label the repo has, so it's fixable from the message without a second call. On `pr-create --agit`, labels are applied as a follow-up call once the PR number is parsed back out of the push output (the AGit push itself has no label field), so they're silently skipped if that parse fails — same fallback as the deferred multi-line body. - `list --label ` / `list --milestone ` are repeatable and validated the same way, for a sharper reason: the forge **discards** a filter value it doesn't recognise, so a typo returns the **unfiltered** list rather than an empty one. That doesn't waste a query, it inverts the answer — "is anything open in this milestone" comes back as every open issue. `--milestone` takes a title or an id, and closed milestones count as valid (filtering on a shipped one is a normal query). - ⚠️ `--assignee` / `--author` are deliberately **not** validated: someone who has left still legitimately appears on old issues, so a login that isn't a current member is not necessarily a typo. - `list --limit N` is a *request*: the forge clamps page size to its own `api.MAX_RESPONSE_ITEMS` (50 by default), so `--limit 400` returns at most 50 rows. The stderr trailer reports the real total from the response's `X-Total-Count` (`… 50 of 187 shown … 137 more`), so trust the trailer, not the row count, when deciding whether you've seen everything.