Skip to content

Welcome to Pullwatch

Your GitHub PR inbox. Sorted. No tokens. No noise.

Pullwatch is a Chrome extension that keeps the pull requests you care about visible without making you live on github.com. It reuses the GitHub session your browser already has, so there are no personal access tokens, no OAuth app, and no sign in flow to worry about. A small Manifest V3 service worker quietly refreshes review requests, the PRs you authored, and your recently merged work in the background, and it backs off politely when GitHub asks it to.

These docs are the long form tour of how Pullwatch works. If the README tells you what Pullwatch is, these pages tell you how it is built. The goal is to read like a book: calm explanations, real code, and diagrams that show you the flow instead of making you guess at it.


The docs have three tiers. You are free to jump around, but the tiers are meant to be read roughly in order.

TierPagesWho it is for
WelcomeHome, Getting StartedAnyone, including curious users.
The big pictureArchitecture OverviewEngineers starting the tour.
Deep divesThe Service Worker Lifecycle, The Parser Waterfall, GitHub Health and Outages (with List Trust and Suspect Lists and Outage Banner and Statuspage), Remote Configuration, Data Hydration and Storage, Popup and Background Communication, Inside the Popup, Onboarding and Session Gates, Notifications and Sound, The Canary MonitorEngineers going one concept at a time.

FeatureWhat it does
Session based accessReads the GitHub HTML you can already see while signed in. No personal access tokens, no OAuth, nothing to authorise.
Three tab inboxTo review (pending vs already reviewed), Authored (sorted by review state: changes requested, approved, pending, draft), and Merged (recently shipped).
NotificationsOptional desktop alerts and sounds for assigned (to review) and merged PRs. The Authored tab is for visibility only (no toasts). Draft alerts for assigned PRs are off by default and can be turned on.
Themes35 built in DaisyUI themes on Tailwind CSS 4.
Background syncDefault refresh cadence is 3 minutes. The worker pauses when you go offline.
Resilient parsingA three stage parser handles both the new and legacy GitHub list pages. Regex updates can be shipped from a public config repo without releasing a new extension build.
Fast popupThe UI hydrates instantly from chrome.storage.local so the panel is filled before any network call.

For the mechanics behind “resilient parsing” head to The Parser Waterfall. For the mechanics behind “fast popup” head to Data Hydration and Storage.

Two extras stay out of the way until you find them: a small squash minigame that reveals itself after the popup has been opened enough times (the minigame docs cover it), and a hidden debug panel for development that has its own quiet way in.


Pullwatch is built so that you do not have to take its word for it. The whole codebase is open and the rules below are easy to verify.

  • No tokens. No OAuth app. Pullwatch never asks for a token and never creates an OAuth integration on your account. It reads the same pages your browser would render if you typed github.com/pulls in the address bar yourself.
  • Outbound network (four host origins, all declared in the manifest). Pullwatch does not run its own servers and does not use analytics or third-party SDKs. The only destinations it contacts are:
    • https://github.com/*: the background worker fetches your signed-in pulls list HTML; the popup may open PR links and load pages on this origin using your existing session.
    • https://avatars.githubusercontent.com/*: avatar images shown next to PR rows in the popup.
    • https://raw.githubusercontent.com/dragosdev-code/pr-live-config/*: a public patterns.json file used to update parser regexes without a new extension release.
    • https://www.githubstatus.com/*: GitHub’s public Statuspage API (summary.json) so outage banners can be corroborated against real Pull Requests incidents. No credentials are sent; responses are cached locally in chrome.storage.local.
  • Your data stays on your machine. PR lists, route hints, rate limit state, and other operational data live in chrome.storage.local on this device only. Your appearance and notification preferences live in chrome.storage.sync so Chrome can carry them across your own signed in Chrome instances if you have Chrome sync turned on. Nothing is uploaded anywhere by Pullwatch itself.
  • Non goals. Pullwatch does not act on PRs for you, does not write anything back to GitHub, and does not sync your PR data across devices. It is read only by design.

The Remote Configuration page covers exactly what is in the config file and how it is validated before the extension will use it.


Pullwatch has two halves that talk to each other through Chrome’s own storage rather than through direct messages. The diagram below shows that split end to end: the popup on the left, the background service worker on the right, and chrome.storage.local sitting between them as the hand-off point. The arrows are the only ways the two halves interact, so it is worth a slow read before the deep dives fill in the detail.

flowchart LR
  subgraph ui [Popup]
    React[React UI]
    RQ[TanStack Query]
  end
  subgraph bg [Service worker]
    Alarm[AlarmService]
    Events[EventService]
    GH[GitHubService]
    Store[chrome.storage.local]
  end
  React --> RQ
  RQ -->|runtime messages| Events
  Alarm -->|onAlarm| Events
  Events --> GH
  GH -->|fetch GitHub HTML| GitHub[github.com]
  Events --> Store
  React --> Store

Read from left to right: the popup renders from storage (so it paints instantly when you open it), and separately asks the worker to do things like “fetch again now.” Read from right to left: the alarm fires every few minutes, the worker fetches GitHub and writes the result to storage, and the popup (if it happens to be open) picks the change up through a storage listener.

If you want the full story of why it is split this way, Popup and Background Communication is the page.


Each entry below explains what the package actually does inside Pullwatch, not just that it is installed.

AreaPackageWhat it does here
UIReact 19Renders the popup and settings.
Data@tanstack/react-queryHolds PR lists in the popup; hydrated from chrome.storage.local before the first paint.
StatezustandSmall UI stores for global error, debug mode, and tab control.
Formsreact-hook-formPowers the settings forms in the settings overlay.
ValidationvalibotValidates the remote patterns.json at runtime before any regex is compiled or stored.
Datesdate-fnsRenders relative timestamps on PR rows (“3h ago”, etc.).
Icons@heroicons/reactIconography used across the popup.
Animation@react-spring/webList entrance animations and the theme picker ripple effect.
A11yreact-focus-lockTraps focus inside the onboarding overlay so keyboard users do not escape it by accident.
Stylingtailwindcss 4 + daisyui 5Styling and the 35 ready made themes.
Buildvite 8 + typescript ~5.8Builds the popup, service worker, and offscreen document. Strict TypeScript across the repo.
Static assetsvite-plugin-static-copyCopies the manifest, icons, and offscreen HTML into dist/.
Unit testsvitest + @testing-libraryTest runner and React testing helpers.
Browser testsplaywrightDrives the canary’s Tier 2 logins and screenshot capture.
LintoxlintFast linter used across the repo.

  • You are a new user or a developer who just cloned the repo: head to Getting Started. It walks through the install, the four commands that matter, and every permission Pullwatch asks for, explained in plain English.
  • You are an engineer who wants the full tour: head to Architecture Overview. That page names every moving part and points you at the deep dive for each one.
  • You are here for one specific thing: jump straight to the deep dive from the table at the top.

Pullwatch is a personal project that is built and maintained solo, so code contributions are not being accepted at the moment. Bug reports and feature ideas are very welcome and genuinely useful.

If something is off, please open an issue at github.com/dragosdev-code/pullwatch/issues. What helps most:

  • The extension version (visible in chrome://extensions).
  • The browser and OS you are on.
  • A short description of what you expected and what you saw.
  • A screenshot if it is a UI issue, and a console log from the service worker if it is a background issue.

Thanks for taking the time. It really does help.