A terminal dashboard for live GitHub CI health across multiple repos — no browser required.
- Stoplight-per-repo — 🟢 🔴 🟡 ⚪ aggregated worst-case across all workflows
- PR-level CI tree — expand any repo to see branch CI and each open PR with its own stoplight
- Stacked PRs grouped — PRs in a
gh stackstack collapse into one row that shows the stack number, its health, and each member in merge order - Active-first sorting — in-progress and failing repos/PRs bubble to the top automatically
- Sort toggle — press
sto cycle repos between active-first, name (A→Z) and open PRs (most first) - Inline expand/collapse — navigate with
↑/↓, toggle any row withenter/space - Auto-expand — a repo opens on its own when its CI starts running or failing and closes when it goes green again; a row you toggle by hand stays put until its status changes
- Auto-polling — refreshes every 15 seconds (configurable); retains last-known status on API errors
- Adaptive pacing — watches each token's GitHub REST budget and stretches the poll interval only as far as it must to last the hour, keeping a reserve for your other tools
- In-TUI repo management — add, edit, delete, and enable/disable repos without leaving the terminal
- Multi-org — per-org token config with
gh auth tokenfallback - Single binary — no runtime, no dependencies
- Interactive init —
git-green initwrites a starter config via a terminal form
brew install --cask ericdahl-dev/tap/git-greenAs of v0.2.0, git-green is published as a Homebrew cask rather than a formula — GoReleaser removed the formula config this project used. The tap still holds the old formula under the same name, frozen at v0.1.3, and a plain brew install ericdahl-dev/tap/git-green picks the formula over the cask, so pass --cask. If you installed before v0.2.0 you are on the old formula; move across once with:
brew uninstall --formula git-green
brew install --cask ericdahl-dev/tap/git-greenRequires Go 1.25 or later.
go install github.com/ericdahl-dev/git-green@latestgit-green # launch the dashboard
git-green init # write a starter config
git-green --version # print the version
git-green --help # show usageRun an interactive wizard (writes ~/.config/git-green/config.toml):
git-green initUse git-green init --force to overwrite an existing file.
Create ~/.config/git-green/config.toml by hand, or start from git-green init and edit:
[settings]
poll_interval_seconds = 15
# stuck_threshold_minutes = 30 # optional; default 30
[[orgs]]
name = "your-org"
token = "ghp_xxx" # or token_env = "MY_TOKEN_ENV"
[[repos]]
owner = "your-org"
name = "your-repo"
# branch = "main" # optional; defaults to repo default branch
# workflows = ["CI"] # optional; defaults to all workflows
# enabled = false # optional; disable without deleting (no API calls made)Any owner that doesn't match a [[orgs]] entry automatically falls back to gh auth token — the account you're logged in as via gh auth login. No extra config needed:
# Logged in as Skeyelab via `gh auth login`? Just add the repo:
[[repos]]
owner = "Skeyelab"
name = "your-repo"To use an explicit token for a specific account, add an [[orgs]] entry. If both are set, token wins over token_env; an entry with neither also uses gh auth token. A token_env that names an empty or unset variable stops git-green at startup with an error.
[[orgs]]
name = "some-other-org"
token_env = "SOME_ORG_TOKEN" # or token = "ghp_xxx"
[[repos]]
owner = "some-other-org"
name = "your-repo"git-green POSTs a JSON event to each configured webhook when a branch or PR has
been failing, stuck in progress, or conflicting for longer than
stuck_threshold_minutes (default 30):
[[webhooks]]
url = "https://hooks.example.com/git-green"
# secret = "shared-secret" # optional; enables request signing{
"event": "branch_stuck",
"reason": "prolonged_failure",
"repo": "your-org/your-repo",
"workflow": "CI",
"run_url": "https://github.com/your-org/your-repo/actions/runs/123",
"stuck_since": "2026-08-26T09:00:00Z",
"timestamp": "2026-08-26T09:31:00Z"
}event is branch_stuck or pr_stuck; reason is prolonged_failure,
prolonged_in_progress, or conflict. A pr_stuck event also carries a pr
object with number, title, and url.
Each condition alerts once, on the first poll after it crosses the
threshold — not on every cycle. Recovering re-arms it, so a repo that breaks
again later alerts again. stuck_since is when the condition started, not when
the alert fired.
When a webhook has a secret, the request is signed with HMAC-SHA256 over the
raw body and sent as X-Git-Green-Signature: sha256=<hex>.
| Key | Action |
|---|---|
↑ / k |
Navigate up |
↓ / j |
Navigate down |
enter / space |
Expand / collapse repo, stack, or PR row |
s |
Cycle repo sort: active-first → name (A→Z) → open PRs (most first) |
f |
Re-run the failed run on the selected row (enter confirms, esc cancels) |
r |
Force refresh |
o |
Open run in browser |
m |
Open repo manager |
q / ctrl+c |
Quit |
? |
Toggle help overlay |
esc |
Close help overlay |
f is the only key that writes to GitHub. It calls rerun-failed-jobs for the
run, falling back to re-running the whole run when there are no individually
failed jobs, so the token for that org needs actions: write. A read-only token
gets a 403 and the error is shown in the footer.
| Key | Action |
|---|---|
↑ / k |
Navigate up |
↓ / j |
Navigate down |
t / space |
Toggle enable / disable |
a |
Add repo |
e |
Edit repo |
d |
Delete repo (y confirms, any other key cancels) |
esc |
Back to dashboard |
The add/edit form takes owner/name or a pasted GitHub URL (https://github.com/owner/name, git@github.com:owner/name.git, or any page inside the repo such as /pull/42). git-green init accepts the same.
Changes are written to config.toml immediately and the poller reloads automatically. Disabled repos make no API calls.
Set GIT_GREEN_DEBUG=1 to print per-repo fetch debug lines to stderr.