Pull and merge requests

Your GitHub and GitLab pull and merge requests in the right panel, each one in a tab where you review it, merge it, re-run its CI and hand it to an agent.

Sirio shows the pull requests of a GitHub project and the merge requests of a GitLab one, and does the work on them too. A list sits in the right panel, and each request opens in a tab of its own with its conversation, commits, checks and diff. From that tab you comment, review, merge, re-run CI, open the branch in a worktree and hand the request to an agent. It works with github.com, GitHub Enterprise Server, gitlab.com and self-managed GitLab.

Open the right panel with Ctrl+Shift+I, or Ctrl+Shift+R on Windows, and choose the pull request icon. The icon appears in git projects only, and Sirio asks the forge nothing until you first open the view.

The list

The view reads the project behind the worktree’s upstream remote when there is one, and behind origin otherwise. Its header shows the forge’s mark and the project, with a Refresh button beside them.

At the top, a card with the heading THIS WORKTREE and the branch name shows the request whose branch you are on: its state, how far its CI has got, its review state and its comment count. Click the card to open the request. When the branch has none, the card reads “No PR for main” (“No MR” on GitLab) beside a + whose tooltip says Create on the forge, and the + opens the forge’s page for a new request in your browser. The card reads the branch again on every refresh, so it follows you after a git switch, and it hides itself on a detached HEAD.

Below the card, four filters sit side by side as icons. Their names live in the tooltips, and the chosen one shows its name beside its icon.

  • Mine: the open requests you authored or that name you as an assignee.
  • To review: the ones waiting on your review, with their count beside the icon.
  • All open: every open request, most recently updated first. The view starts here and keeps the filter you pick until you quit.
  • Closed & merged.

Each row shows the title, then the number, the author and how long ago it changed, with its CI, review and comment marks on the right. Click a row to open it, or right-click it for Open in browser and Copy link. The Search button opens a field that searches on the forge itself. Rows arrive a page at a time, with Load more at the end. A page holds up to 50 requests, or up to 100 when the filter joins two lists, as Mine does.

The list refreshes when you show it and every minute while it stays on screen. Out of sight, it asks the forge nothing. When a forge rate-limits Sirio, the list says when it will try again and asks nothing until then.

A request’s tab

A request opens in the right half of the centre, in a tab that carries its number and title, such as #578 Fix the login redirect. GitLab numbers read !231. Opening a request that already has a tab in the worktree switches to that tab, and the tab always stays in the right half, as Tabs and splits explains.

The header carries the title and number, Open in browser, a refresh button, the state, and a line with the author, which branch merges into which, and when it last changed. Close or Reopen and Ready for review or Convert to draft sit in the header too. Below it sit four tabs with their counts, as on the forges:

  • Conversation: the description, which Sirio draws like the Markdown preview, then the comments, the reviews with their outcome, and events such as pushed commits, review requests and merges. A picture shows as its alt text, a link when it is remote. The tab holds the last 100 entries, and when there are more, a link above them leads to the earlier activity on the forge.
  • Commits: each commit with its short hash, title, author and age. A click fetches the commit when your repository lacks it, then opens it in Changes.
  • Checks: failed checks first, then running and queued ones. The rest fold into one row, such as “12 passed, 1 other”, that you can expand. A click on a job opens its log, and a click on a check that has no job opens it on the forge. See CI and job logs.
  • Files: the diff, described below.

A comment on a line names its place as on path:line. Click it to switch to Files with the diff open at that line.

While CI runs, the tab refreshes every minute. Coming back to the tab reloads it, unless it reloaded in the last ten seconds. A restart brings the tab back on the inner tab you left, and it asks the forge nothing until you look at it. A tab that can no longer reach its forge stays open and shows the reason, with Retry and Close.

The Files diff

Files is the request’s diff, and the Changes view draws it. A line above it names the base and head commits, and the toolbar carries Unified and Split, Refresh, Expand All and Collapse All. The forge’s review threads sit under the lines they belong to; see Review threads. Each changed file arrives with its additions and deletions, and Sirio reads a file’s diff when you open its row. The files you opened stay open when the request gets a new head, and a note beside the two commits says “Updated to head” with the new head. The diff’s heading reads Staged (N), a label it inherits from Changes.

The ↗ at the end of an open file’s row opens the file in the editor. When your worktree sits at the request’s head, you get your own file, ready to edit. Otherwise Sirio opens a read-only snapshot of the file at the head, in a tab such as login.rs @ #578. For a file the request deletes, the snapshot shows the base. A bar across the snapshot reads “Read-only · #578 at a1b2c3d”, with Open local copy when the request keeps the file and your worktree has it. A snapshot runs without a language server, and it comes back after a restart.

While the commits are on their way, Files shows the forge’s own list of changed files. When Sirio cannot build the diff, the tab says why and offers Retry and Open on the forge above that list.

Fetching a request’s commits

When you open Files or click a commit and the request’s commits are missing from your repository, Sirio fetches them by itself. It runs a non-interactive git fetch from the remote that points at the request’s project, upstream first, then origin, then any other.

That fetch signs in with git’s own credentials, a credential helper or an ssh key, and never with the token Sirio holds for the forge. It runs without a prompt, gives up after two minutes, and leaves your branches and tags as they were. Sirio keeps what it fetched under refs/sirio/change-requests/, only so that git’s garbage collection leaves it alone, and deletes those refs once no tab needs them.

When the fetch cannot sign in, the tab explains that Sirio fetches with git’s credentials rather than the token, and suggests gh auth setup-git for GitHub.

Signing in

Sirio works out each host on its own, and the first answer wins: your choice in Settings → Git Hosting, the names github.com and gitlab.com, a gh or glab already signed in to the host, a token you saved, and last a question that GitHub Enterprise Server answers for itself. For any other host the view asks “Which forge is git.example.com?” with GitHub Enterprise and GitLab to choose from, and remembers your answer.

When a host has no way in, the view shows the gh auth login --hostname <host> or glab auth login --hostname <host> command with a Copy button, and a field to paste a personal access token instead. A token needs read access to pull requests, checks and metadata on GitHub, and the read_api scope on GitLab. To act on requests it needs write access as well. Sirio checks a token against the forge before it keeps it.

Settings → Git Hosting lists every host you configured and every host Sirio has met since it started, with its forge, how Sirio signs in and the account it signs in as. Next to a saved token it says can read and write, read-only or scopes not reported, read from the forge’s own answer about the token. When the forge refuses a write for a missing scope, the message names the scope to add. A host that goes through gh or glab says nothing about scopes, because the CLI owns that login. For a host other than github.com and gitlab.com you can say whether it runs GitHub or GitLab. Once Sirio knows a host’s forge, you choose Detect, CLI or Token as the way in, paste a token and press Save token, or press Forget token to remove a saved one.

Sirio keeps tokens apart from the settings, in a plain credentials.json of its own in your user data directory. That file lives under $XDG_DATA_HOME/sirio when that variable is set, and otherwise under ~/.local/share/sirio on Linux and macOS and %LOCALAPPDATA%\Sirio on Windows.

A few more things about how Sirio talks to a forge:

  • A connection made with a token trusts the certificates your system trusts, so a self-managed forge behind a company certificate authority works once you install that authority in the system trust store. Until then, the error says so.
  • An older self-managed GitLab works with less detail. CI shows that it runs but not how far it has got, and Sirio cannot tell a review that asks for changes from any other review.
  • When you choose CLI and gh or glab is missing from your PATH, the view says so and points you to a token in Settings → Git Hosting.

Acting on a request

The tab has controls for what the forge lets you do.

  • Comment, Approve and Request changes are three buttons under the Conversation. Ctrl+Enter, or Cmd+Enter on macOS, sends a comment. A comment and a request for changes need text, and an approval does not.
  • Close or Reopen and Ready for review or Convert to draft are in the header, one button for the state the request is in.
  • Edit opens a card at the top of the Conversation for the title, the description and the target branch, with Save and Cancel. You edit your own comments in place the same way.

A button appears only when the forge says you may press it. When the forge does not report what you can do, the control stays hidden, except commenting, which stays unless the conversation is locked. Sirio offers nothing the forge would refuse: it does not offer approving your own pull request, and on GitLab it offers Request changes only on a server that has that state.

Sirio does not draw the result of a write itself. It reads the request again and shows what the forge says. One write runs per tab at a time, and Sirio never retries a write on its own, because a comment posted twice is worse than one not posted. When the connection drops after the request left, the tab says “Could not confirm that it went through. Look at the conversation before sending it again.” and keeps the words you typed in the box. Sirio clears them only when the forge accepts them.

Merging

A bar under the header says whether the request is ready, waiting on checks that still run, or blocked, and why. Merge, auto-merge and deleting the branch are one confirmation away. Sirio refuses the merge when the head moved since you looked.

Reviewers and labels

Edit the reviewers and the labels from the tab. Sirio sends the change when the picker closes.

CI and job logs

Re-run a job, or re-run only the failed ones. A job’s log opens in a read-only tab of its own, coloured and foldable, and the log of a running job keeps reloading while you look at it. A failed job also offers Fix with agent, described under Hand off to an agent.

Review threads

The Files diff shows the forge’s threads under the lines they belong to, and the Conversation lists one entry per thread. Outdated threads fold into a section of their own. You can reply to a thread, resolve or reopen it, and edit your own comments. To comment on a line or a range, use the diff’s gutter.

Reviews

Start a review from a line comment or a reply, add to it, edit or delete what is pending, then submit it with a verdict or discard it. On GitHub the review is the pending review, and on GitLab it is the draft notes. Suggest inserts a suggestion block, and a suggestion you receive shows as a small diff. You apply it on the forge.

Open in a worktree

Open in a worktree brings the branch of a request into a worktree that pushes back to it. A fork’s branch gets a remote of its own, so a plain git push from its worktree reaches that fork and nothing else. Sirio fast-forwards an existing worktree only when it is clean and on that branch. Otherwise it leaves the worktree alone, and the status line says why.

Hand off to an agent

Hand off to an agent, in the tab’s action bar and in the menu of a list row, and Fix with agent, on a review thread and on a failed check, open one dialog with three choices:

  • What for: fix the review comments, fix failing CI, review, or resume the work.
  • Which agent: any of the five, or none.
  • Where: a terminal or a chat.

Sirio reads what that needs from the forge before it touches git, checks the request out, writes a context file under .sirio/handoff/ in the worktree, which it keeps out of git, and starts the agent on it. The file fences everything the forge wrote, such as comments, names and logs, as untrusted text, and tells the agent so.

From a script

sirioctl surface change-requests shows the view, reads it, switches its filter and saves a token. sirioctl surface change-request opens a request, switches its inner tab, reveals a file in the diff, opens a file or a commit, and reads what the tab shows. sirioctl surface settings open --section git-hosting opens Settings → Git Hosting. See the surface commands.