git-stats: a sidebar card for the working tree and the session's pull requests - #21
Merged
Merged
Conversation
… requests A second plugin in the monorepo, TUI-only, in the shape opencode actually offers: a `sidebar_content` slot at order 450 — directly above the host's own Modified Files list — holding the whole folder's diff figures and a chip per GitHub pull request the session touched. The figures come from `client.vcs.status()`, not `state.session.diff()`. Those are different things: the latter accumulates the files *this session* changed, which is the list the host already draws two lines below. "Diff stats in the folder" means the working tree, so it is the VCS endpoint, refreshed on `session.diff` and `session.idle` and on a 10s tick for edits made elsewhere. A failed or timed-out call keeps the last figures rather than reporting a dirty tree as clean. Chips are sighted from completed tool parts, incrementally via `message.part.updated` — walking the transcript inside a `createEffect` rescans every message on every streamed token — with a count-guarded catch-up scan for whatever happened before the card mounted. A chip only exists because a command actually printed the URL; the assistant's prose is not scanned. Ones the session created lead the ones it only referenced. The state behind a chip is the one question opencode's API cannot answer, so it comes from `gh pr view --json state,isDraft`, and the chip is painted with GitHub's own colours (#59636e draft, #1f883d open, #cf222e closed, #8250df merged) on white, the way the badge on the pull request page is. Merged is terminal and never re-fetched; failures back off exponentially to ~16 minutes and reset at a turn boundary, in case `gh` was just installed or logged into. Only github.com is trusted without configuration. The URLs are found in tool output, which can carry whatever a fetched page put there, and the host goes straight to `gh --repo` — which treats an unknown host as Enterprise and would send it a request with an Enterprise token attached. A real Enterprise host has to be named in the plugin's `hosts` option, and at most 24 chips are tracked per session so one `gh pr list --json url` cannot mint a process per pull request. Chips close by clicking the `×` (the host's own sidebar uses onMouseDown, so mouse works) or through `/prs`, which also hides all or restores them. Dismissals are per session in `kv`, unioned with an in-memory set for the window before `kv` is ready. Each label is an OSC 8 hyperlink to the pull request.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A second plugin in the monorepo:
opencode-git-stats, a TUI-only sidebar cardholding the whole folder's diff figures and a GitHub-coloured chip per pull
request the session touched.
Shape
sidebar_contentslot at order 450 — the host's own sections are 100 Context …400 Todo, 500 Modified Files, drawn lowest first, so the worktree totals land
directly above that file list. Everything else is opencode's own API:
client.vcs.status(),state.vcs,state.session.messages()/state.part(),event.on(...),kv,keymap,lifecycle.The figures are
client.vcs.status(), notstate.session.diff(). Those aredifferent things: despite the name,
session.diffaccumulates per-turn snapshotdiffs for the files this session changed — which is the list the host already
draws two lines below. "Diff stats in the folder" means the working tree.
Chips are sighted incrementally, from completed tool parts via
message.part.updated, with a count-guarded catch-up scan for whatever happenedbefore the card mounted. Walking the transcript inside a
createEffectinsteadwould rescan every message on every streamed token. A chip exists only because a
command actually printed the URL — the assistant's prose is not scanned. Pull
requests the session created lead the ones it merely referenced.
State comes from
gh pr view --json state,isDraft, the one questionopencode's API cannot answer, and the chip is painted with GitHub's own colours
(
#59636edraft,#1f883dopen,#cf222eclosed,#8250dfmerged) on white,the way the badge on the pull request page is. Merged is terminal and never
re-fetched; failures back off exponentially to ~16 minutes and reset at a turn
boundary in case
ghwas just installed or logged into. Dismissed chips arenever fetched at all.
Security
Only
github.comis trusted without configuration. The URLs are found in tooloutput, which can carry whatever a fetched page or a pasted file put there, and
the host goes straight to
gh --repo <host>/…— which treats an unknown host asEnterprise and would send it a request with an Enterprise token attached. So
github.evil.exampleandgithub.com.evil.exampleare ignored, and a realEnterprise host has to be named in the plugin's
hostsoption.ghis spawnedwith an argv array, never a shell string. At most 24 chips are tracked per
session, so one
gh pr list --json urlcannot mint a process per pull request.Closing a chip
Click the
×(the host's own sidebar usesonMouseDown, so mouse works), or/prsto hide one, hide all, or bring the hidden ones back. Dismissals live inkvper session, unioned with an in-memory set for the window beforekvisready. Each chip label is an OSC 8 hyperlink to the pull request.
Verified
79 unit tests, plus a real OpenCode TUI driven under a pty, asserting on composed
screens and raw terminal bytes:
Git Stats/⎇ main/+6 -1 · 3 files, and those figures matchwhat git itself reported (+4/-1 across 2 tracked files, plus a 2-line untracked
one)
#20 Mergedas\x1b[38;2;255;255;255m\x1b[48;2;130;80;223m—#8250dfon white — and carriesan OSC 8 link to the PR; on a non-truecolor terminal it degrades to
48;5;98/prsopens "Pull request chips" with the PR title as the row description;selecting hides the chip, and a second
/prsoffers "Show 1 hidden chip again"and restores it
ghlogged out the chip reads#20 …and the card saysgh: run `gh auth login`rather than guessing a colourghitself is covered against live GitHub and against a stubbed binary for everyfailure path (missing, logged out, deleted PR, unreadable JSON, killed/aborted).
Also
release.ymlandpublish.ymlgaingit-statsin the package choice list, andthe root README gains its row. Per the repo's own note, the first version of a new
package still has to be
npm published by hand once before OIDC trustedpublishing will attach.