Skip to content

Repository files navigation

prdash

prdash is a local monitor for GitHub pull requests with both an interactive terminal dashboard and a headless watch mode. It shows open PRs authored by the authenticated user plus any configured authors, current-head GitHub Actions jobs, adaptive refresh state, event hooks, and confirmed rerun actions.

prdash preview

Install

From source:

go install ./cmd/prdash
make install

With Go:

go install github.com/danielwolfman/prdash/cmd/prdash@latest

Release binaries are produced by GoReleaser for Linux and macOS on amd64 and arm64. Each release also includes a checksums.txt file.

Setup

prdash init
prdash doctor
prdash auth status
prdash config list
prdash config include-owner my-company
prdash config remove-owner my-company
prdash config include-author dependabot
prdash config include-author dependabot my-company/service my-company/worker
prdash config remove-author dependabot
prdash config exclude owner/repo
prdash config include owner/repo
prdash config rerun enable
prdash config rerun disable
prdash logs path
prdash logs tail --lines 80
prdash watch
prdash version

prdash init creates the default config without overwriting an existing file unless --force is passed. Rerun actions require the GitHub CLI token to have the workflow scope; prdash doctor prints the exact gh auth refresh command when scopes are missing.

Debug logs are enabled by default and write to the user cache directory unless [logging].path is set. Logs include startup/config state, loader refresh cycles, GitHub request method/status/duration, per-PR job fetch timing, rerun actions, and hot-refresh triggers. Tokens are redacted and PR titles are omitted by default.

Configured authors are searched across included owners by default. Pass one or more owner/repo arguments to prdash config include-author to restrict that author to specific repositories.

Headless Watch Mode

Run prdash watch to monitor PRs and dispatch configured hooks without opening the terminal UI. It uses the same filters, adaptive refresh intervals, hook state, authentication, and logs as the dashboard, and stops cleanly on SIGINT or SIGTERM.

prdash watch
prdash watch --limit 10

Only one monitoring process can use a hook state file at a time, even when hooks are disabled. If either the TUI or headless watch is already running, starting the other exits with a clear ownership error. Stop the running process before switching modes. Operating-system file locks are released automatically when the owner exits or crashes.

PR Event Hooks

prdash can run local commands when observed PR activity crosses useful boundaries. Hooks are disabled by default and are configured with argv arrays so users can invoke a script directly or explicitly opt into shell behavior.

[hooks]
enabled = true
[[hooks.commands]]
event = "first_check_failure"command = ["/path/to/prdash-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "merge_conflict"command = ["/path/to/prdash-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "stack_rebase_required"command = ["/path/to/prdash-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "checks_completed"command = ["/path/to/another-hook"]
timeout_seconds = 30
[[hooks.commands]]
event = "new_pr_comment_or_review"command = ["/path/to/pr-activity-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "unresolved_review_thread_changed"command = ["/path/to/review-thread-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "pr_discovered"command = ["/path/to/pr-lifecycle-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "new_pr_by_author"command = ["/path/to/pr-lifecycle-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "pr_ready_for_review"command = ["/path/to/pr-lifecycle-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "pr_merged"command = ["/path/to/pr-lifecycle-hook"]
timeout_seconds = 60
[[hooks.commands]]
event = "pr_closed"command = ["/path/to/pr-lifecycle-hook"]
timeout_seconds = 60

Hook commands receive a JSON payload on stdin. Hook state is stored under the user cache directory by default; set [hooks].state_path to override it.

Supported events:

  • first_check_failure: fires once per visible PR head SHA when prdash first observes at least one failed job.
  • merge_conflict: fires when a visible PR enters GitHub's DIRTY merge state. It can fire again for the same head SHA after the conflict clears and is later reintroduced.
  • stack_rebase_required: fires when a visible PR in a native GitHub stack has a recorded base SHA that differs from the current tip of its base branch. It can fire again for the same head SHA after the stack layer becomes current and is later made stale again. The PR payload includes stack_number, stack_position, stack_size, base_sha, and stack_needs_rebase.
  • checks_completed: fires when all observed jobs for a visible PR head reach a terminal state, whether the final result is success, failure, cancellation, neutral, or action required. If checks are rerun or replaced and prdash observes that head move back to a non-terminal state, it fires again when the new check epoch completes.
  • new_pr_comment_or_review: establishes a baseline on first observation, then fires for newly observed top-level PR comments and submitted PR reviews.
  • unresolved_review_thread_changed: fires for currently unresolved inline review threads on first observation, then when a thread is created or changed while unresolved, including new or edited comments and threads that become unresolved again. Resolving a thread updates the saved state without firing the hook.
  • pr_discovered: fires once per monitored open PR in each prdash process, including the initial discovery baseline. This lets idempotent hook consumers ensure external per-PR state after either side restarts or is reinstalled.
  • new_pr_by_author: establishes a monitored-PR baseline on first observation, then fires when a new monitored open PR appears in authored/configured-author discovery.
  • pr_ready_for_review: fires once per PR head SHA when a monitored PR changes from draft to ready for review.
  • pr_merged: fires when a previously observed monitored open PR disappears from open discovery and a direct GitHub lookup verifies it was merged.
  • pr_closed: fires when a previously observed monitored open PR disappears from open discovery and a direct GitHub lookup verifies it was closed without merging.

Check and merge-conflict event payloads include PR metadata, a check summary, workflow runs, failed jobs, and primary_job for the earliest completed failed job when one exists. Merge-conflict events may have no failed jobs and no primary_job; use pr.merge_state_status to identify that case. Stack-rebase-required events contain stack metadata and no workflow runs. PR activity payloads include an activity object with the activity kind, author, URL, body text, review state, and timestamps. Review-thread payloads include a review_thread object with nullable current positions, original line/range positions, diff sides, resolution and outdated state, and the complete comment history. PR lifecycle payloads include PR metadata and no workflow runs. Hook commands run with a global concurrency limit of four; additional events wait instead of launching unbounded subprocesses.

Example payload fragment:

{
"schema_version": 1,
"event": "first_check_failure",
"observed_at": "2026-06-08T07:13:00Z",
"pr": {
"repo_full_name": "my-company/my-repo",
"number": 42,
"url": "https://github.com/my-company/my-repo/pull/42",
"head_sha": "abc123"
},
"summary": {
"state": "failure",
"total": 12,
"failure": 1,
"running": 2
},
"primary_job": {
"name": "ci / unit",
"url": "https://github.com/my-company/my-repo/actions/runs/100/job/200",
"state": "failure"
}
}

The hook contract is intentionally generic: prdash emits events; your bridge decides what to do with them. For example, this bridge launches Claude Code for failed checks and new PR comments/reviews:

#!/usr/bin/env bashset -euo pipefail
payload=$(mktemp)
cat >"$payload"
event=$(jq -r '.event'"$payload")
repo=$(jq -r '.pr.repo_full_name'"$payload")
number=$(jq -r '.pr.number'"$payload")
pr_url=$(jq -r '.pr.url'"$payload")
head_sha=$(jq -r '.pr.head_sha // ""'"$payload")
job_name=$(jq -r '.primary_job.name // ""'"$payload")
job_url=$(jq -r '.primary_job.url // ""'"$payload")
activity_url=$(jq -r '.activity.url // ""'"$payload")
activity_author=$(jq -r '.activity.author // ""'"$payload")case"$event"in
first_check_failure|merge_conflict|stack_rebase_required|new_pr_comment_or_review) ;;
*) exit 0 ;;
esac
prompt=$(mktemp)
{
printf'You are handling a prdash PR event.\n\n'printf'Repository: %s\n'"$repo"printf'PR: #%s\n'"$number"printf'PR URL: %s\n'"$pr_url"printf'Head SHA: %s\n'"$head_sha"printf'Event: %s\n'"$event"if [[ -n"$job_url" ]];thenprintf'Failed job: %s\n'"$job_name"printf'Failed job URL: %s\n'"$job_url"fiif [[ -n"$activity_url" ]];thenprintf'Activity author: %s\n'"$activity_author"printf'Activity URL: %s\n'"$activity_url"fiprintf'\nUse live GitHub state before changing code. Keep fixes focused.\n'
} >"$prompt"
claude --print "$(cat "$prompt")"

Register the bridge in your config:

[[hooks.commands]]
event = "first_check_failure"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120
[[hooks.commands]]
event = "merge_conflict"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120
[[hooks.commands]]
event = "stack_rebase_required"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120
[[hooks.commands]]
event = "new_pr_comment_or_review"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120
[[hooks.commands]]
event = "pr_discovered"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120
[[hooks.commands]]
event = "new_pr_by_author"command = ["/path/to/prdash-claude-bridge"]
timeout_seconds = 120

Development

make test
make build
./dist/prdash version
./dist/prdash doctor
go run ./cmd/prdash
go run ./cmd/prdash --limit 3
go run ./cmd/prdash --limit 3 --allow-rerun
go run ./cmd/prdash watch --limit 3

The default command opens the TUI immediately, discovers authored open PRs, then fills in current GitHub Actions jobs as background workers complete. It refreshes on a conservative interval derived from the configured rate budget, marks stale rows, and highlights status changes. Press j/k or arrows to move across PRs and visible jobs, o to open the selected PR or job in Chrome/browser, and q to quit. Use --limit 3 for a faster local smoke test.

Rerun actions are disabled by default. Use --allow-rerun for one run, or set [actions].allow_rerun = true in the config. Press r on a selected PR to rerun failed jobs for completed workflow runs, then confirm with Enter/y or cancel with Esc/n. Runs that are still queued or in progress are not rerun. A successful rerun request wakes the loader immediately instead of waiting for the next scheduled refresh.

About

Dense terminal dashboard for authored GitHub PRs

Resources

Stars

5 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages