dsh-pr-watch
Verifieddsh-pr-watch · v1.0.0 · MIT · Web UI
PR status window for the DeepSeek Harness Web GUI — floating pull-request chips with CI check tallies, review/mergeability badges, auto-discovery of your open PRs, and per-session branch scoping. Uses your existing gh CLI auth.
Install
dsh plugin add dsh-pr-watch Confirm the layer applied with dsh --profile default --dump-config — see the install guide.
Source
Tags
Creators
Readme
dsh-pr-watch
A pull-request status window for the DeepSeek Harness Web GUI — a stack of
small floating chips, one per watched PR, pinned to the corner of the chat:
live CI check-tallies (5✓ 1✗ …), draft / mergeable / review-decision
badges, and a click-to-expand panel listing every check with a link to its CI
run.
Everything flows through the gh CLI that's already signed in on your
machine, so private and SSO-gated repos work with zero token setup.
Features
- Per-session scoping — the chips attach to the chat session you're actually viewing. Each session's window shows only the PRs whose head branch matches the workspace checked out for that session; home, settings and sessions with no open PR show nothing. No more overlay spam across tabs.
- Auto-discovery — every poll re-runs
gh pr list --author @me, so a PR you open (or close/merge) appears and disappears on its own. Pinned PRs can be added alongside. - Multi-PR — one chip per pull request (capped at 12 to protect the corner), click any chip to see its checks.
- Truthful staleness — if the host bridge blinks, the chips keep your last good data and say it's stale instead of flicking to "offline".
- Honest failure surface — failed lookups render as a small
⚠ nchip instead of failing silently. - Self-authenticating — the status endpoint only answers same-machine callers, or callers holding the app's own auth cookie. It picks up no tokens and stores nothing.
Requirements
- DeepSeek Harness desktop app with the Web GUI (plugins ecosystem),
- GitHub CLI (
gh) installed and authenticated (gh auth login) — that's the only credential the plugin uses.
Install
# in ~/.dsh/profiles/<your-profile>
npm install dsh-pr-watch
pnpm install # profiles are pnpm workspaces
Then add the package name to dsh.profile.bundles in that profile's
package.json (bundle order = apply order; anywhere near the end is fine),
and restart the app. The plugin activates itself through its own bundle
patch — no hand-editing of cordis.patch.yml (the app regenerates that
file on boot and would wipe any row you add there).
Configuration
The plugin ships with a minimal default config. Put your own preferences in
~/.dsh/pr-watch.config.json — it overrides the shipped defaults, and it
is re-read on every poll so edits take effect without restarting the app:
{
"repo": "owner/repo", // optional: derived from `dir`'s git remote when omitted
"dir": "~/code/my-repo", // a local checkout gh runs in
"pollSeconds": 120, // host refresh cadence (min 60)
"autoDetect": "mine", // "mine" | "branch" | "off"
"prs": [ // optional pins, always watched
{ "repo": "owner/repo", "number": 123, "dir": "~/code/my-repo" }
]
}
| Field | Default | Notes |
|---|---|---|
repo |
derived from dir's remote |
GitHub owner/name used for discovery and pinning |
dir |
~ |
Any local path; needed for repo derivation and branch scoping to be useful |
pollSeconds |
120 |
Min 60 enforced |
autoDetect |
"mine" when a repo is known, else "off" |
"mine": every open PR by the gh user (appears/vanishes within one poll). "branch": the PR of the current branch in dir. "off": pins only |
prs |
[] |
Pinned targets, deduped against discovery; total target cap is 12 |
Zero-config mode
If your chat sessions run in worktrees/checkouts of the repos you care about, you can leave everything defaulted: the session-scoped request already carries the session's workspace dir, and the host answers with the PRs that match its branch.
How it works
┌──────── Desktop app host ────────┐ ┌──────── Web GUI client ────────┐
│ poll every N s: │ │ visible session: │
│ gh pr list/view + gh pr checks │ GET │ uiSession → sessions store │
│ ─ branch filter per ?dir= │◀───────│ cwd → ?dir=<cwd> │
│ auth: loopback or app cookie │ JSON │ chip per PR, panel per click │
└──────────────────────────────────┘ └────────────────────────────────┘
- The host half (
lib/index.js) is a cordis plugin: it pollsghfor the pinned + discovered targets and serves the snapshot at/plugins/pr-watch/status.json.gh pr checksexits non-zero when a check fails — its TSV is read from the rejection's stdout (gh 2.42-era CLIs have no--jsonfor checks). - The client half (
lib/client.js) figures out which chat session is on screen (uiSession service → active conversation wrapper → largest visible conversation), resolves that session's workspace dir via the sessions store, and asks the host for the branch-scoped snapshot. Sessions whose branch has no open PR simply display nothing.
Security
Named webserver routes are not behind the app's browser-cookie gate, so this
plugin trusts only loopback callers, plus remote callers that satisfy the
app's own browserAuth.isAuthenticated(req). The branch probe runs
git -C <dir> branch --show-current through execFile argument arrays (no
shell), and check rows are rendered with textContent only.
Development
node --test # parse/tally unit tests
License
MIT — see LICENSE.