A self-hosted, third-party dashboard for your GitHub pull requests and CI. ghelper tracks the PRs you care about and shows their check status, reviews, and failed-job logs in one place — as a terminal TUI or a web UI — so you don't have to bounce between GitHub tabs to see where everything stands.
Think of it as a focused status board for the PRs you're shepherding right now: paste in PR or run URLs (or pull in everything assigned to you), watch their CI live, drill into the first failing job's log without leaving the page, and group backport PRs together. Because it runs as a server, the board stays current even when your laptop is off — and it can optionally re-run failed CI for you while you're away, so a flaky job doesn't block a PR overnight.
Pairs naturally with the Backport Tracker userscript: pipe its copy-summary output directly into ghelper watch to populate the dashboard.
- Python 3.9+
- A GitHub personal access token (see Authentication)
pip install -e .This installs the ghelper command.
You need a GitHub token with permission to read and trigger workflow reruns.
The default sign-in flow uses GitHub's device authorization flow:
ghelper auth
prints a browser URL and short code, then polls GitHub until you approve the device login.
If you prefer to keep using a PAT, pass --pat to fall back to the previous
token-based flow.
- Run
ghelper auth. - Open the URL printed by the command if your browser does not open automatically.
- Enter the short code shown in the terminal.
- Approve the login in GitHub.
Device-flow tokens use the repo scope and work for repos you can access,
including org-owned repos.
auth options:
| Flag | Description |
|---|---|
--device / --pat |
Device flow (default) or PAT consent-page mode |
--classic |
Use the classic-PAT consent page (PAT mode only) |
--org ORG_OR_USER |
Pre-select an org/user on the fine-grained PAT page (PAT mode only) |
--no-browser |
Don't auto-open the browser; just print the URL |
On success the token is validated against the GitHub API and saved to
~/.ghelper.json (chmod 600), so later watch / ls / logs invocations pick
it up without --token or GITHUB_TOKEN.
Use ghelper auth --pat if you want to keep using a PAT.
Fine-grained PATs:
- Follow the link printed by
ghelper auth --pat. - Select the target repository.
- Under Repository permissions → Actions, choose Read and write.
- No other permissions are needed.
Classic PATs:
- Follow the classic link printed by
ghelper auth --pat --classic. - The
reposcope will be pre-selected — that is sufficient.
The
reposcope grants write access to all repository resources, not just Actions. Use a fine-grained PAT if you want tighter access control.
export GITHUB_TOKEN=ghp_your_token_hereFor device flow, the GitHub OAuth client ID is built in. You only need to set
the client secret for the /auth?code=... OAuth exchange path:
export GHELPER_GITHUB_CLIENT_SECRET=your_client_secret_hereYou can still override the embedded client ID by setting
GHELPER_GITHUB_CLIENT_ID if needed.
| Command | What it does |
|---|---|
ghelper auth |
Start device flow by default; --pat keeps PAT mode |
ghelper watch [TARGETS]... |
The dashboard: live PR/CI status as a TUI and/or web UI, with optional auto-rerun |
ghelper ls |
List assigned PRs as Backport-Tracker-compatible markdown |
ghelper logs [TARGETS]... |
Print failed-job logs (--grep to filter) |
ghelper config show | set | clear |
Manage per-repo defaults at ~/.ghelper.json |
ghelper config edit-log-filter |
Edit the per-repo failed-CI log-extraction filter in $EDITOR |
ghelper config export | import |
Back up / restore a repo's full config (requirements + log filter) |
watch is the dashboard itself. The TUI is just one front-end onto an embedded server — the same tracker model is exposed over HTTP/JSON-RPC and the web UI when --serve is set, and you can run it purely as a headless server with --serve --no-tui. Auto-rerun is a per-tracker option layered on top of the live view, not the point of the tool.
Opens the dashboard over a set of tracked targets. Each target becomes a row
showing its live CI checks, review/label state, and (on demand) the first
failing job's log; runs against the same repo are grouped, and backport sets are
collapsed together. Add --serve for the web UI, or --serve --no-tui to run it
as a pure background server.
ghelper watch [OPTIONS] [TARGETS]...
TARGETS can be any mix of:
| Format | Example |
|---|---|
| Actions run URL | https://github.com/owner/repo/actions/runs/12345 |
| PR URL | https://github.com/owner/repo/pull/456 |
| Bare run ID | 12345 (requires -R owner/repo) |
| Session ref | #last, #3 — resume a previous invocation |
You can also pipe markdown summaries (e.g. from ghelper ls or Backport Tracker) on stdin. Targets can be added live from the TUI with a, or via the web UI / JSON-RPC when --serve is set.
Options:
| Flag | Default | Description |
|---|---|---|
-t, --token |
$GITHUB_TOKEN |
GitHub PAT (required) |
-R, --repo OWNER/REPO |
— | Required for bare run IDs |
--rerun / --no-rerun |
--no-rerun |
Auto-rerun failed CI for tracked PRs (off by default — dashboard only) |
-n, --retries N |
3 |
Maximum rerun attempts per run (only applies with --rerun) |
-i, --interval SECS |
30 |
Server-side polling interval |
--ignore JOB |
— | CI job substring to ignore; repeatable |
-a, --assigned |
off | Watch PRs assigned to the current user |
--filter REGEX |
— | Regex filter (with --assigned) |
--include-closed |
off | Include closed PRs (with --assigned) |
--include-drafts |
off | Include draft PRs (with --assigned) |
--serve |
off | Expose HTTP/JSON-RPC + web UI |
--host HOST |
127.0.0.1 |
HTTP bind host (with --serve) |
--port PORT |
53210 |
HTTP bind port (with --serve) |
--no-tui |
off | Skip the Rich dashboard (useful with --serve) |
--quiet |
off | Suppress streaming event lines (non-TTY mode) |
The embedded server also exposes GET /auth. Pass ?code=... to exchange an
OAuth code for a token, or ?token=... to set a PAT directly. The OAuth code
exchange requires GHELPER_GITHUB_CLIENT_ID and
GHELPER_GITHUB_CLIENT_SECRET.
| Key | Action |
|---|---|
Tab |
Cycle panes (targets → runs → jobs → logs) |
j/k or ↑/↓ |
Move selection / scroll |
←/→ or PgUp/PgDn |
Page step within the current pane |
g/G or Home/End |
Jump to top / bottom |
Space |
Expand / collapse the selected aggregate group |
a |
Add a tracker via modal prompt |
d |
Remove the selected tracker |
r |
Force-refresh the selected tracker (all if not on targets) |
l |
Toggle the logs pane |
o, Enter |
Open the selected item in your browser |
Ctrl-C |
Exit |
ghelper watch --serve --no-tuistarts the JSON-RPC server (/rpc) and web UI on 127.0.0.1:53210 (override with --host/--port). Trackers added via the web UI or RPC are persisted to ~/.ghelper-trackers.json and reload across restarts.
Export PRs assigned to the authenticated user, in the same markdown format Backport Tracker emits. Useful as input to watch.
ghelper ls
Options:
| Flag | Default | Description |
|---|---|---|
-t, --token |
$GITHUB_TOKEN |
GitHub PAT |
-R, --repo OWNER/REPO |
— | Optional repo scope |
--include-closed |
off | Include closed PRs |
--include-drafts |
off | Include draft PRs |
--filter REGEX |
— | Regex filter against branch/title/url/repo |
Print failed workflow jobs and their logs. By default the per-repo
log filter chooses which failed job to show and how to
grep it; --grep overrides that filter for this run, keeping only matching lines
plus adjacent context, with matched text highlighted.
ghelper logs --grep 'AssertionError|Traceback' --context 3 \
https://github.com/owner/repo/actions/runs/12345
Options:
| Flag | Default | Description |
|---|---|---|
-t, --token |
$GITHUB_TOKEN |
GitHub PAT |
-R, --repo OWNER/REPO |
— | Required for bare run IDs |
--grep REGEX |
— | Override the saved log filter: print only matching lines (supports trailing +N/-N context) |
--context N |
2 |
Symmetric lines around each --grep match (when --grep has no +N/-N tokens) |
--all-jobs |
off | Show every failed job, bypassing the log filter's job-name selection |
You can pipe summary text containing GitHub URLs instead of listing TARGETS.
Persistent per-repo defaults at ~/.ghelper.json.
ghelper config show
ghelper config show -R owner/repo
ghelper config set -R owner/repo --ignore lint --ignore build-docs --required-label release-ready --required-reviews 1
ghelper config clear -R owner/repoSaved fields per repo:
ignore_ci: CI job-name substrings to ignore in rerun decisionsrequired_labels: label substrings required on PR targetsrequired_reviews: minimum approvals on PR targets (advisory inwatch)log_filter: the failed-CI log-extraction filter (see below)
ghelper logs, the TUI logs pane, and the web UI all decide which failed job
to surface and how to grep its log from a per-repo log filter — a small DSL
stored under log_filter in ~/.ghelper.json. Edit it in $EDITOR (the seeded
template documents the full syntax):
ghelper config edit-log-filter -R owner/repoThe DSL has two keys:
grep: <regex> [+N] [-N]— applied to each failed job's log (empty = whole log). Trailing+NincludesNlines after each match,-NincludesNbefore.^/$are per-line.job: <name-regex> [=> <grep override> [+N] [-N]]— match failed jobs by name, in priority order; the first match is treated as the "first failing CI".=> …overrides the global grep for that job.
Share or back it up with the dedicated subcommands:
ghelper config export-log-filter -R owner/repo > filter.conf
ghelper config import-log-filter -R owner/repo filter.confconfig export / config import round-trip a repo's full config — the
requirements (ignore jobs, required reviews/labels, including the backport-specific
variants the server and web UI enforce, stored in ~/.ghelper-repo-configs.json)
plus the log filter — as a single JSON bundle:
ghelper config export -R owner/repo > config.json
ghelper config import -R owner/repo config.json# Export assigned PRs in backport-tracker format
ghelper ls
# Pipe to watch (dashboard only)
ghelper ls | ghelper watch
# Watch a single Actions run
ghelper watch https://github.com/owner/repo/actions/runs/12345
# Watch all runs for a PR
ghelper watch https://github.com/owner/repo/pull/456
# Bare run ID — requires -R
ghelper watch -R owner/repo 12345
# Opt into auto-rerun: up to 5 retries, check every minute
ghelper watch --rerun -n 5 -i 60 https://github.com/owner/repo/pull/456
# Ignore two CI jobs
ghelper watch --ignore lint --ignore docs https://github.com/owner/repo/pull/456
# Watch all assigned PRs
ghelper watch -a
# Resume the previous session
ghelper watch #last
# Pipe backport-tracker "Copy summary" output
pbpaste | ghelper watch
# Inspect failed logs
ghelper logs --grep 'AssertionError|Traceback' --context 3 \
https://github.com/owner/repo/pull/456
# Headless server mode (no TUI, web UI on localhost:53210)
ghelper watch --serve --no-tuiIn the Backport Tracker sidebar panel, click Copy summary, then:
pbpaste | ghelper watch -n 3ghelper watch extracts all GitHub URLs from the markdown automatically and reads any ignore_ci="..." directive embedded in the comment header.
A pasted summary may also include a leading title line and status rows that have no PR link yet, e.g.:
fix(plugins): oidc compliance of redirect_uri in different grant_types
[MISSING] next/3.10.x.x
[MISSING] next/3.11.x.x
[MERGED] next/3.14.x.x: https://github.com/Kong/kong-ee/pull/18741
[MERGED] ai-master: https://github.com/Kong/kong-ee/pull/18745
The title line is captured for display, rows with a PR URL become watch targets,
and [MISSING]-style rows (branches without a backport PR yet) are surfaced as
"nothing to track" instead of being treated as targets. The same parsing applies
in the web UI's Add Targets import box.
Run it on a small box so the dashboard is always live and reachable from a browser, independent of your laptop:
python -m venv .venv && source .venv/bin/activate
pip install -e /path/to/gh-helper
export GITHUB_TOKEN=ghp_...
# Headless: web UI + tracker engine, no TUI
nohup ghelper watch --serve --no-tui --host 0.0.0.0 --port 9999 \
>> ~/ghelper.log 2>&1 &Trackers persist in ~/.ghelper-trackers.json, so the server can be restarted without losing state. Open the dashboard at http://<host>:9999/ or drive it via POST /rpc.
ghelper keeps its state in your home directory:
| Path | Contents |
|---|---|
~/.ghelper.json |
Saved token (chmod 600) and per-repo defaults (ignore_ci, required_labels, required_reviews, log_filter) |
~/.ghelper-repo-configs.json |
Per-repo requirements enforced by the server / web UI, including backport-specific variants |
~/.ghelper-trackers.json |
Trackers added via the web UI / RPC, reloaded across server restarts |
~/.ghelper-sessions.json |
Recent watch invocations, resumable with #last / #N (last 20 kept) |
~/.ghelper-cache.json |
Cached PR CI status for ls / --assigned (1-hour TTL; merged PRs cached indefinitely) |