Skip to content

Latest commit

 

History

198 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PRison

CI Quality Gate Status Coverage Docker Pulls

A read-only GitHub dashboard that shows which pull requests need your attention, and for how long — across your personal account and every organization you can access. Four lists, oldest first, plus two history sections:

  • Ready to merge — PRs GitHub reports as mergeable now. An out-of-date branch still counts, with a "Needs update" hint (a bot/manual update handles it) — but only once its checks are green; while one is red or still running it belongs under Stuck on checks, and no PR is ever in both lists.
  • Comments awaiting your reply — review comments where the last word isn't yours: every unresolved inline thread on your own PRs, the threads you raised on PRs you reviewed, and questions left in the body of a review rather than on a line of the diff. Each row opens the comment itself, not the top of the PR.
  • Waiting on your review — PRs you're blocking others on.
  • Stuck on checks — your open PRs with failing/pending checks, or otherwise blocked from merging (required checks, review, or conflicts).
  • Recently reviewed — open PRs you already reviewed, badged with the verdict you left (Dismissed, struck through, when the author has cleared it), and flagged "Updated since" when the author has pushed after it. A PR that comes back for another review leaves this list and returns to Waiting on your review.
  • Recently merged / closed — your own PRs that were merged or closed, newest first, so finished work confirms itself.

The lower half of the board is two columns paired by subject: other people's PRs on the left — the ones waiting on your review, then the ones you have already reviewed — and your own on the right, blocked above finished. Each history sits under the queue it is the history of, and the columns grow independently, so expanding one never moves the other. Both histories start collapsed with a count and expand to the latest 15, with Load more for the rest.

The PRison dashboard in dark mode. The filter bar's grouping and draft filters and a search box sit at the top; under them three summary tiles (waiting on you, awaiting your reply, stuck on checks), each showing a count and its oldest wait, then a row of links to all six sections with their counts. Ready to merge and Comments awaiting your reply run full width; below them two columns pair each queue with its history — PRs waiting on your review above Recently reviewed on the left, PRs stuck on checks above Recently merged / closed on the right. One stuck PR is tagged both Merge conflict and a failing check

Features

  • Unanswered review comments. An inline thread is waiting on you when it is unresolved and its most recent comment isn't yours — replying adds a comment, so your own last word means the ball is back in the reviewer's court. A review body has neither replies nor a resolve bit, so it waits until you say something on the PR after it; a reaction dismisses one, the same emoji that dismisses a thread. Rows say which surface they came from, because the two are answered in different places. The age counts from the comment, so you can see what you've been sitting on for four days. Bots write most review comments, so they're hidden behind a Show bot comments toggle.
  • Tracked checks → Awaiting. GitHub's API hides "expected" required checks (e.g. a manually-triggered qa/smoke or automation) from non-admins. Name the checks you care about — a default per owner (your personal account and every org) plus per-repo overrides, with a type-to-search repo picker — and PRison shows them as "⏳ Awaiting: <name>" on a blocked PR until they report. Each check is added with a Required box answered up front, and the board stops guessing: a required one is drawn solid and holds the PR out of Ready to merge; an unticked one is shown for information, drawn dashed, and blocks nothing — including when GitHub does report it, so a red job that cannot hold the merge up stops reading like one that can. A check you never named keeps the dashed chip, which is what "PRison doesn't know" looks like.
  • Ignored checks. Some checks are broken rather than failing — the flaky one, the one somebody else's team owns. Right-click (or click) its chip on the board and choose Ignore this check. It stays on the card, muted instead of red, but its result stops counting: the PR is no longer held out of Ready to merge over it, it gets no bucket of its own under By check, and it is never awaited. Everything ignored is listed in Settings → Ignored checks, under the repo it was ignored on, where it can be renamed, widened to the whole owner, or taken back. A conflict or a review gate still holds the PR — ignoring a check says its result means nothing, never "merge it anyway" — but GitHub's own blocked state doesn't, since a red required check is exactly what puts a PR there.
  • History you can walk back into. Merged and closed-without-merging PRs, each badged Merged or Closed with how long ago it ended — and next to them the PRs you reviewed, so a discussion you left open is one click away instead of lost in GitHub's notification list.
  • Summary tiles above the lists: how many PRs wait on your review, how many comments wait on your reply, how many of yours are stuck on checks — each with how long that queue's oldest one has been sitting, so every age says which list it belongs to. The two where someone else is held up by you are coloured; the rest stay quiet. They count the lists as filtered, so a tile never disagrees with the list beneath it, and they cost no extra API calls — it is all data already on the page. Ready to merge has no tile: it is the one queue you want to be long.
  • A section index under the tiles: every section, in page order, with its count — so nothing is more than a click from the top however long the board gets, including the two histories at the foot of it. They are plain links, so the Back button is the way back and keyboard focus travels with the view.
  • Everything folds by its header. Each of the four lists, each repo/check group inside them, and both history sections collapse to a single line that keeps its title and count — so a section you are not working in today stops taking up the screen without ever hiding how much is in it. Folds last the sitting, not forever: a board that quietly omitted your review queue tomorrow morning would be worse than a long one.
  • Grouping — flat, by repository, or by check. Next to it in the filter bar, a draft filter: All, Only drafts to see just what you have in progress, or No drafts to get them out of the way.
  • One search box over the whole board. Type a word and every list narrows at once — title, repository, PR number, author, comment text, file path, and the names of the checks that are red on a PR. Terms are ANDed and each may land on a different field, so acme 482 finds that one PR. It filters what is already on the page rather than asking GitHub anything, it never touches what auto refresh considers new, and it is forgotten when you close the tab.
  • Auto refresh (opt-in). Turn it on in Settings and pick how often to check — every 5, 15, or 30 minutes, or hourly (30 minutes by default). PRison then tells you what moved, not just what's new: a PR of yours that was approved — you hear that as soon as it happens, not when CI eventually catches up and the PR turns mergeable — a PR that became ready to merge, one that fell into a merge conflict because somebody else merged first, checks that went red, changes requested, a review asked of you, an author answering the review you left with code, a fresh reply on a thread, or one of your own PRs getting merged — while a PR simply falling back to waiting (you pushed a fix) stays quiet. Works only while a PRison tab is open — there's no background service.
  • Activity feed. Everything a poll detects is kept, newest first, behind the bell in the header. The bell carries an unseen count and pulses while there is something new (never against a reduced-motion preference), and each row names the PR, says what happened, and links straight to it — a comment lands on the thread itself. The last 100 events survive a reload. Opening the panel is what marks it read, so the count waits for you instead of vanishing the moment you return to the tab; the (3) PRison tab-title badge follows the same count.
  • Desktop notifications (opt-in). On the same schedule, changes that land while you're on another tab also raise a desktop notification naming them — acme/web #42 is ready to merge — up to three at a time, then +N more. Settings shows whether your browser has actually granted permission and offers a test notification, and says where else to look if nothing appears: your operating system decides separately whether the browser may show anything, and it refuses in silence. Whatever the notification misses is in the activity feed regardless — and, if your operating system keeps swallowing them, in the menu bar: extras/menubar/ puts an unread count there, where nothing has to be permitted for you to see it and nothing disappears after five seconds.
  • Catch-up on open. PRison remembers what everything was doing when you last closed it, so a review that arrived overnight is still news in the morning — the first load reports what moved while no tab was polling, instead of quietly accepting it as the way things always were. It fills the feed and the bell, never a desktop notification: what it reports is by definition old, and a browser reloads a tab on its own — a discarded one restored in the background is not you opening PRison.
  • Last refreshed indicator. The filter bar says how long ago the data landed, so you always know whether you're looking at something stale.
  • Shows what a refresh costs. GitHub prices every query, the allowance is hourly, and it is shared with anything else signed in as you — so how often you refresh is a spending decision. Settings → Auto refresh names the price of the last refresh and what is left of the hour, and the board warns while there is still enough left to act on rather than after the wall.
  • Says when GitHub's budget is spent. The API allowance is hourly and account-wide, so a busy morning can empty it — and every list then fails at once, which looks exactly like the app being broken. PRison tells the two apart: a spent budget gets its own notice, with the clock time it comes back and the setting that stops it happening again, instead of a retry that is guaranteed to fail.
  • Partial-data notice. When GitHub drops part of a response (an org restricts the token, a search times out), a banner says so instead of silently showing less — with a Retry button, like every failed list.
  • Rides out GitHub's rate limiting. A refresh needs seven queries, and asking for them all at once trips GitHub's secondary rate limit — which applies to your whole account, so lists fail together and the page looks broken. PRison sends three at a time instead. That costs nothing: the refresh is as fast as it was, because its speed was always set by the slowest single query, and each query actually comes back quicker once GitHub isn't throttling the burst. Should the limit still land, and GitHub says how long the block runs and it's short, PRison waits that out and asks once more instead of making you press Retry yourself. When GitHub asks for longer than a refresh should take — or doesn't say at all — PRison steps aside and shows the banner rather than guessing, since coming back early only extends the block.
  • Eight themes, each with a light and a dark ground. A theme owns its accent, its three status colours and its typefaces; the ground underneath is a separate choice, so switching it reads as the light changing rather than the theme changing. Default is the original pair; Aurora is built from four atmospheric emission wavelengths, İznik from the metal oxides the 16th century tile workshops fired, and Cyanotype from the 1842 blueprint reaction and its negative. Coral takes a reef and the algae that colour it, its two grounds a depth rather than a mood — at 30 m the sea has absorbed the red end of the spectrum. Saffron takes four parts of one crocus, Lichen four species off one rock face, wet and dry. Reflectivity is a weather radar's dBZ scale, and the one accent here that isn't blue or green. All sixteen palettes are measured against WCAG AA by a test, with nothing exempt. Pick one in Settings, where each row previews itself — the swatch is stamped with that theme, so it renders in the real palette rather than a copy that can drift.
  • Responsive two-column layout, minute-level ages, colour-coded lists, and a Refresh button.
  • Personal account + per-org filter in the top-right switcher.
  • Your own access — sign in with the GitHub CLI or a token; no third-party app to approve. Every row deep-links to GitHub; PRison never writes anything.

Getting started

PRison runs on your own machine — no third-party app to approve. The easiest way is Docker (one command); or run it locally with Node.

Sign Out ends your session — it clears the encrypted cookie. It cannot revoke the host's credentials: on a GITHUB_TOKEN-configured instance, one click signs you back in, and anyone who can reach the instance can do the same. That is what the warning below is about.

Warning

Sign-in mints a session from the host's GitHub credentials (your gh CLI token or a GITHUB_TOKEN). PRison is designed to run on your own machine — do NOT expose a gh-authenticated or GITHUB_TOKEN-configured instance on a reachable network without adding your own access control.

Run with Docker (recommended)

Zero-config — AUTH_SECRET is auto-generated and persisted in a volume (nothing to set).

From the published image, no clone needed:

docker run -p 3000:3000 -v prison-data:/data \
  -e GITHUB_TOKEN="$(gh auth token)" mfozmen/prison   # http://localhost:3000

Or build it yourself from a checkout:

GITHUB_TOKEN="$(gh auth token)" docker compose up --build   # http://localhost:3000

The prison-data volume holds the generated AUTH_SECRET, so your session survives a restart. Images are published for linux/amd64 and linux/arm64.

Passing your gh token signs you in automatically — needed for SSO-restricted orgs (where SSO/SAML enforcement blocks classic PATs). The token rotates, so re-run when it expires. Without GITHUB_TOKEN, just open the app and paste a token.

Then put it in the menu bar (macOS)

The container is the dashboard; on a Mac this is the other half. A desktop notification is only as reliable as the four switches between the page and your screen, and it is gone in seconds whether or not you were looking. extras/menubar/prison.1m.py is a SwiftBar (or xbar) plugin that puts an unread count up there instead: it appears when something lands and stays until you click it.

The macOS menu bar with the PRison plugin clicked open. In the bar, the PRison icon and the number 2. The menu below lists Unread (2) and Mark all read, then the live totals — Ready to merge 2, Waiting on your review 2, Comments awaiting reply 3, Stuck on checks 4 — each opening a submenu of its own PRs, then Open PRison and Refresh now

brew install --cask swiftbar
mkdir -p ~/SwiftBar                          # the folder SwiftBar asks for on first launch
cp extras/menubar/prison.1m.py ~/SwiftBar/
chmod +x ~/SwiftBar/prison.1m.py             # the bit survives cp, not a raw download

It reads the PRison you just started — no token of its own, no configuration, and no schedule of its own: it asks GitHub on the same interval the dashboard uses, so the two never spend the hourly budget twice. Clicking an unread row opens that PR and marks it read; the live totals sit one click below. See extras/menubar/README.md.

Run locally (development)

npm install
npm run dev        # http://localhost:3000

npm run dev generates AUTH_SECRET into .env.local on first run (it encrypts the session cookie) — nothing to configure. Open the app and click Sign in with GitHub CLI; the server reads your CLI token and stores it only in an encrypted, httpOnly cookie — never in the browser.

No GitHub CLI? Paste a token

If gh isn't installed or signed in, the app falls back to a paste-a-token form:

  1. Go to github.com/settings/tokensGenerate new token (classic).
  2. Select the read:org and repo scopes, generate, and copy it.

Note

For SAML SSO orgs, click Configure SSO on the token and Authorize it — self-service, no org owner approval. Some orgs forbid classic PATs entirely; there the GitHub CLI token is the only way in.

Usage

Sign in with the GitHub CLI or paste a token.

Header controls

Top-right, left to right:

Control What it does
Switcher Scopes the board to All / your personal account / a single org.
Bell Opens the activity feed and marks it read.
Sliders icon Opens Settings (below).
Sun/moon Switches between the current theme's two grounds, and names them — it reads "Switch to Aurora Night" rather than "Switch to dark theme".
Sign Out Clears the stored token.

The Settings menu has six sections:

  • Comments — show bot comments, hide comments you reacted to.
  • Auto refresh — on/off, how often to check, and the notification permission.
  • Tracked checks — add the checks to see as "Awaiting" one at a time, each with its own Required box, and rename, re-mark, or remove any of them later. Both check panels work one scope at a time: pick an owner or a repository from the scope picker at the top and edit just that list, so the panel stays the height of one list however many repos you configure.
  • Ignored checks — everything you wrote off from the board, under the repo it was ignored on, plus an owner scope for a check that is broken everywhere. A repo can also be picked here, to write a check off before the board has ever drawn its chip.
  • Appearance — pick one of the eight themes, each previewing its own colours and typeface.
  • About — version, repository, license, and Check for updates, asked for on the spot. Tell me when a newer version is out turns it into a standing question: the version in the header becomes a link to the release notes once your container has fallen behind. Off by default; asks GitHub once a day, without your token, and stays quiet when it can't reach it.

Filter bar

Control What it does
Flat / By repo / By check Groups the lists. Group headers fold and unfold, and under By repo the ↗ beside one opens that repository on GitHub.
All / Only drafts / No drafts Filters by draft state.
Search Narrows every list to the rows matching all your terms; Esc clears it.
Refresh Re-fetches without reloading the page.
Updated Xm ago How fresh the data is, next to Refresh.

Click a PR title (or a suggested-action link) to jump to GitHub — a comment row lands on that exact thread. On a Mac, the same counts are in the menu bar without opening the tab.

Documentation

Licensed under MIT.

About

A read-only GitHub dashboard showing which pull requests need your attention, and for how long.

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages