Fetch happens.
When CI goes red, gh-hound retrieves the failing step, the guilty log line, and the rerun verdict — no browser tab involved. TUI for humans, JSON for agents.
gh extension install indrasvat/gh-hound
View source
Sit. Stay. Triage.
CI triage is the same loop every time. gh-hound binds each step of it to a keystroke and keeps the answer in your terminal, next to the code.
Is my branch green?
Scoped run list with status, age, event and live rate budget.
Just the failures?
One key cycles the status filter: failing → running → passed. ⎋ drops it.
What failed?
Drill into jobs and steps, then jump to the next failure.
Where is the useful log line?
Full log with folds, search, line numbers and highlights.
Who broke main?
Walks run history to the last green → first red boundary and names the suspects. No bisect.
Flaky or real?
Reads attempt flips, cross-run flaps and retry masks, then calls it: squirrel or genuine fault.
Can I retry safely?
Rerun, rerun failed, cancel: local, visible, confirmation-gated.
Deploy is waiting on me?
Gated runs wear a badge. Pick environments, then open or hold the gate — comment included.
Where did the time go?
Step picker, slowest-gap detection, jump or range-filter by time.
Where’s the build output?
Download with live progress; the verdict stays on the row — o opens the folder, y copies the path.
Caches near the cap?
Pressure against the 10 GB eviction cap, biggest or stalest first, confirm-gated digs.
Noisy workflow needs benching?
Enable or disable in place — state badges stay truthful, dispatch refuses benched packs.
Can my agent consume this?
Schema-stable runs, failures, annotations, excerpts, exit codes.
Best in show.
One tab per trick: failure forensics, log time travel, a live watch, the palette, the deploy gate, the trail, the scent check, the good ending. Every frame rendered by gh-hound itself — the full log is this repo's own CI, caught red-handed.










Born to run. Trained to heel.
gh-hound is built around the parts of the GitHub Actions API that make triage quick, and trained away from the parts that burn your rate limit.
server-side filtering
Branch, status, event and repo-wide scope are filtered by the API, not in your terminal.
etag polling
Unchanged resources answer from conditional requests. Idle views spend no primary rate budget.
serial request queue
Calls and mutations are paced to stay clear of secondary rate-limit spikes.
no keystroke waits
Every fetch is async behind one spinner: dimmed reloads, instant skeletons, byte-progress bars on fat logs. esc cancels the work itself.
viewport-only rendering
Large logs parse once and render by visible window; long run lists virtualize, with explicit page-load affordances when GitHub has more.
flicker-free frames
Every frame is line-diffed against the last and flushed as one synchronized write. The screen never blanks, however fast you scroll.
Good with humans.
Good with agents.
Same hound, two leashes: one usecase core feeds both surfaces, so CI state, failures, annotations and exit codes read identical whichever species is asking.
Agents don’t click dashboards — they read stdout. Every verb speaks --no-tui --json in a schema-stable envelope; branch on the exit code. Deterministic fake scenarios let your harness play fetch without ever touching the network.
The leash works both ways: rerun sends failures back out, diff names who broke main, flakes calls rerun-vs-investigate — one verb per tab, evidence attached either way.
| 0 | all good, branch is green |
| 1 | CI failure, action needed |
| 2 | API, network or config error |
| 3 | runs still pending |
{
"repo": "indrasvat/gh-hound",
"branch": "main",
"runs": [{
"workflow": "CI",
"run_number": 571,
"event": "pull_request",
"status": "completed",
"conclusion": "failure",
"failed": [{
"job": "build",
"step": "go test ./...",
"exit_code": 1,
"annotations": [{
"path": "internal/parser/lexer.go",
"line": 142,
"message": "identifier mismatch"
}],
"log_excerpt": "--- FAIL: TestLexIdent/..."
}]
}]
} // exit code 1
{
"repo": "indrasvat/gh-hound",
"run_id": 30433642,
"action": "rerun_failed",
"accepted": true,
"html_url": "https://github.com/indrasvat/gh-hound/actions/runs/30433642"
} // exit code 0 — one API call; cancel calls them off
{
"repo": "indrasvat/gh-hound",
"workflow": "CI",
"status": "located",
"last_good": { "run_number": 572, "head_sha": "c2b3a49", "conclusion": "success" },
"first_bad": { "run_number": 573, "head_sha": "d3c4b5a", "conclusion": "failure" },
"suspect_commits": [{
"sha": "d3c4b5a9f0e1...",
"author": "indrasvat",
"message": "feat: sharpen the lexer"
}, {
"sha": "cc99aa1b2c3d...",
"author": "dependabot[bot]",
"message": "chore(deps): bump charmbracelet/x/ansi"
}],
"compare_url": "https://github.com/.../compare/c2b3a49...d3c4b5a",
"runs_scanned": 4,
"verdict": "scent picked up: #572 was clean, #573 wasn't."
} // exit code 1 — a culprit exists; no bisect
{
"repo": "indrasvat/gh-hound",
"workflow": "ci.yml",
"status": "flaky",
"sample_size": 6,
"signals_evaluated": ["attempt_flips", "cross_run_flaps", "retry_masks"],
"jobs": [{
"job": "build",
"flake_score": 1,
"verdict": "flaky",
"attempt_flips": 2,
"retry_masks": 2,
"evidence": [{
"run_number": 570,
"kind": "attempt_flip",
"detail": "#570: failed on attempt 1, passed on a later attempt"
}]
}],
"verdict": "seen this one before: it's a squirrel — build flaked 2 of the last 6 runs."
} // exit code 0 — rerun it, don't investigate
Adopt, don’t browse.
One command and the hound is yours — no paperwork, no home visit. Four routes below; the gh extension even keeps itself updated.
latestv0.1.0
gh extension
the recommended path, updates with gh
$ gh extension install indrasvat/gh-hound
installer script
standalone binary into ~/.local/bin
$ curl -sSfL https://gh-hound.pages.dev/install.sh | bash
from source
Go 1.25+, builds in seconds
$ git clone https://github.com/indrasvat/gh-hound && cd gh-hound && make install
agent skill
teaches your coding agent the JSON surface
$ npx skills add indrasvat/gh-hound --global --yes