Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgBEW6qAwgn2fcQbA2f4ZQ
69 lines
2.8 KiB
Markdown
69 lines
2.8 KiB
Markdown
# pr-monitor
|
|
|
|
Single-user dashboard for tracking PRs across multiple GitHub repos: CI checks
|
|
segmented into lint / specs / e2e / other (every check visible, with per-chip
|
|
dropdowns), review state derived from review-thread resolution, and a live
|
|
unified timeline of all comments and reviews with persistent read/unread state.
|
|
|
|
## How it works
|
|
|
|
- Node/TypeScript server polls the GitHub GraphQL API every 60s using a two-tier
|
|
query: a cheap `search()` list query whose fields double as change signals
|
|
(PR `updatedAt` does **not** change when checks or thread resolution change),
|
|
then batched detail queries only for changed PRs.
|
|
- State is cached in SQLite (`data/pr-monitor.sqlite`, `node:sqlite`) so the UI
|
|
loads instantly on restart; timeline events and read/unread survive restarts.
|
|
- The browser gets a `sync` event over SSE each poll and refetches via
|
|
TanStack Query. React 19 + Vite SPA, light/dark/system theme, responsive
|
|
(two-column desktop, tabbed mobile).
|
|
|
|
## Config
|
|
|
|
`config.json` — repos, the `involves:<user>` filter flag, poll interval, port,
|
|
and per-repo check-name → category mappings (exact names or `*` globs, e.g.
|
|
`"Preview E2E *"`). Unmatched checks land in `other`; nothing is ever hidden.
|
|
|
|
Auth: `GITHUB_TOKEN` env var, falling back to `gh auth token`.
|
|
|
|
`timeline.mute` hides timeline events by actor (optionally kind / body substring)
|
|
at read time — data is kept, so unmuting is retroactive.
|
|
|
|
`failureReports` (per repo) maps failing check names to workflow artifacts that
|
|
contain structured results: `rspec` parser reads RSpec JSON-formatter output
|
|
(`rspec_results_*.json`), `playwright` reads the `data/*.md` failure attachments
|
|
inside Playwright HTML-report artifacts. Failing specs are stored (capped at
|
|
1000 per report), summarized on PR cards, and enumerated in the per-PR focus
|
|
view grouped by file. Reports re-fetch only when the workflow run id changes.
|
|
|
|
## Commands
|
|
|
|
```sh
|
|
pnpm dev # server on :4000 (tsx watch)
|
|
pnpm dev:web # vite dev server on :5173, proxies /api
|
|
pnpm sync:once # one poll, prints a PR/category table (smoke test)
|
|
pnpm build # vite build + tsc server build
|
|
pnpm start # run the production build
|
|
```
|
|
|
|
## Deploy
|
|
|
|
systemd user unit (survives reboots via linger):
|
|
|
|
```sh
|
|
pnpm build
|
|
cp deploy/pr-monitor.service ~/.config/systemd/user/
|
|
systemctl --user daemon-reload
|
|
systemctl --user enable --now pr-monitor
|
|
loginctl enable-linger $USER
|
|
```
|
|
|
|
Serves the built SPA + API on http://localhost:4000.
|
|
|
|
## Notes
|
|
|
|
- Own activity appears in the timeline but is never marked unread; the first-ever
|
|
sync marks the historical backfill as read (`BACKFILL_UNREAD=1` to override).
|
|
- Events are append-only: comments later deleted on GitHub remain in the timeline.
|
|
- Polling stretches to 5 min automatically when the GraphQL rate budget dips
|
|
below 500 points.
|