{"id":44767461,"url":"https://github.com/fini-net/gh-observer","last_synced_at":"2026-06-10T15:00:35.274Z","repository":{"id":337369618,"uuid":"1153300384","full_name":"fini-net/gh-observer","owner":"fini-net","description":"🔎 Terminal UI for watching GitHub Actions CI/CD workflows with runtime metrics, queue times, and check status - alternative to gh pr checks --watch","archived":false,"fork":false,"pushed_at":"2026-06-03T19:43:37.000Z","size":11263,"stargazers_count":7,"open_issues_count":8,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-03T21:13:21.032Z","etag":null,"topics":["error-logs","gh-extension","github-actions","github-cli","latency-monitor","pull-requests","tui-app"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fini-net.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-09T06:19:29.000Z","updated_at":"2026-06-03T19:41:48.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fini-net/gh-observer","commit_stats":null,"previous_names":["fini-net/gh-observer"],"tags_count":28,"template":false,"template_full_name":"fini-net/template-repo","purl":"pkg:github/fini-net/gh-observer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fini-net%2Fgh-observer","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fini-net%2Fgh-observer/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fini-net%2Fgh-observer/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fini-net%2Fgh-observer/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fini-net","download_url":"https://codeload.github.com/fini-net/gh-observer/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fini-net%2Fgh-observer/sbom","scorecard":{"id":1246278,"data":{"date":"2026-04-19T20:54:09Z","repo":{"name":"github.com/fini-net/gh-observer","commit":"d3772416ce339f0844de2bf09f4c652e59f71bc0"},"scorecard":{"version":"v5.3.0","commit":"c22063e786c11f9dd714d777a687ff7c4599b600"},"score":5.7,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/19 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#code-review"}},{"name":"Dependency-Update-Tool","score":10,"reason":"update tool detected","details":["Info: detected update tool: Dependabot: .github/dependabot.yml:1","Info: detected update tool: RenovateBot: .github/renovate.json:1"],"documentation":{"short":"Determines if the project uses a dependency update tool.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dependency-update-tool"}},{"name":"Security-Policy","score":10,"reason":"security policy file detected","details":["Info: security policy file detected: .github/SECURITY.md:1","Info: Found linked content: .github/SECURITY.md:1","Info: Found disclosure, vulnerability, and/or timelines in security policy: .github/SECURITY.md:1","Info: Found text in security policy: .github/SECURITY.md:1"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#security-policy"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"project was created within the last 90 days. Please review its contents carefully","details":["Warn: Repository was created within the last 90 days."],"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Info: jobLevel 'contents' permission set to 'read': .github/workflows/actionlint.yml:20","Info: jobLevel 'contents' permission set to 'read': .github/workflows/auto-assign.yml:18","Info: jobLevel 'actions' permission set to 'read': .github/workflows/checkov.yml:24","Info: jobLevel 'contents' permission set to 'read': .github/workflows/checkov.yml:22","Info: jobLevel 'contents' permission set to 'read': .github/workflows/claude-code-review.yml:27","Info: jobLevel 'issues' permission set to 'read': .github/workflows/claude-code-review.yml:29","Info: jobLevel 'actions' permission set to 'read': .github/workflows/claude.yml:30","Info: jobLevel 'contents' permission set to 'read': .github/workflows/claude.yml:26","Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/claude.yml:27","Info: jobLevel 'issues' permission set to 'read': .github/workflows/claude.yml:28","Info: jobLevel 'actions' permission set to 'read': .github/workflows/codeql.yml:31","Info: jobLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:32","Info: jobLevel 'contents' permission set to 'read': .github/workflows/cue-verify.yml:16","Info: jobLevel 'contents' permission set to 'read': .github/workflows/markdownlint.yml:16","Warn: jobLevel 'checks' permission set to 'write': .github/workflows/reviewdog.yml:14","Info: jobLevel 'contents' permission set to 'read': .github/workflows/reviewdog.yml:13","Info: jobLevel 'checks' permission set to 'read': .github/workflows/scorecards.yml:35","Info: jobLevel 'contents' permission set to 'read': .github/workflows/scorecards.yml:29","Info: jobLevel 'actions' permission set to 'read': .github/workflows/scorecards.yml:30","Info: jobLevel 'issues' permission set to 'read': .github/workflows/scorecards.yml:32","Info: jobLevel 'pull-requests' permission set to 'read': .github/workflows/scorecards.yml:33","Info: jobLevel 'contents' permission set to 'read': .github/workflows/super-linter.yml:16","Info: jobLevel 'packages' permission set to 'read': .github/workflows/super-linter.yml:17","Warn: jobLevel 'statuses' permission set to 'write': .github/workflows/super-linter.yml:18","Info: found token with 'none' permissions: .github/workflows/actionlint.yml:1","Info: found token with 'none' permissions: .github/workflows/auto-assign.yml:1","Info: found token with 'none' permissions: .github/workflows/checkov.yml:1","Info: found token with 'none' permissions: .github/workflows/claude-code-review.yml:1","Info: found token with 'none' permissions: .github/workflows/claude.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/codeql.yml:24","Info: found token with 'none' permissions: .github/workflows/cue-verify.yml:1","Info: topLevel 'contents' permission set to 'read': .github/workflows/dependency-review.yml:13","Info: topLevel 'contents' permission set to 'read': .github/workflows/govulncheck.yaml:11","Info: found token with 'none' permissions: .github/workflows/markdownlint.yml:1","Warn: topLevel 'contents' permission set to 'write': .github/workflows/release.yml:9","Info: found token with 'none' permissions: .github/workflows/reviewdog.yml:1","Info: topLevel permissions set to 'read-all': .github/workflows/scorecards.yml:18","Info: found token with 'none' permissions: .github/workflows/super-linter.yml:1"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":9,"reason":"dependency not pinned by hash detected -- score normalized to 9","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependency-review.yml:20: update your workflow using https://app.stepsecurity.io/secureworkflow/fini-net/gh-observer/dependency-review.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/dependency-review.yml:22: update your workflow using https://app.stepsecurity.io/secureworkflow/fini-net/gh-observer/dependency-review.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecards.yml:39: update your workflow using https://app.stepsecurity.io/secureworkflow/fini-net/gh-observer/scorecards.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecards.yml:66: update your workflow using https://app.stepsecurity.io/secureworkflow/fini-net/gh-observer/scorecards.yml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/scorecards.yml:74: update your workflow using https://app.stepsecurity.io/secureworkflow/fini-net/gh-observer/scorecards.yml/main?enable=pin","Warn: npmCommand not pinned by hash: .just/lib/install-prerequisites.sh:141","Info:  16 out of  21 GitHub-owned GitHubAction dependencies pinned","Info:  24 out of  24 third-party GitHubAction dependencies pinned","Info:   0 out of   1 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#pinned-dependencies"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#vulnerabilities"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#cii-best-practices"}},{"name":"SAST","score":10,"reason":"SAST tool is run on all commits","details":["Info: SAST configuration detected: CodeQL","Info: all commits (30) are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#sast"}},{"name":"Signed-Releases","score":0,"reason":"Project has not signed or included provenance with any releases.","details":["Warn: release artifact v1.4 not signed: https://api.github.com/repos/fini-net/gh-observer/releases/307951799","Warn: release artifact v1.3 not signed: https://api.github.com/repos/fini-net/gh-observer/releases/302572922","Warn: release artifact v1.2 not signed: https://api.github.com/repos/fini-net/gh-observer/releases/299047851","Warn: release artifact v1.1 not signed: https://api.github.com/repos/fini-net/gh-observer/releases/297734560","Warn: release artifact v1.0 not signed: https://api.github.com/repos/fini-net/gh-observer/releases/297105097","Warn: release artifact v1.4 does not have provenance: https://api.github.com/repos/fini-net/gh-observer/releases/307951799","Warn: release artifact v1.3 does not have provenance: https://api.github.com/repos/fini-net/gh-observer/releases/302572922","Warn: release artifact v1.2 does not have provenance: https://api.github.com/repos/fini-net/gh-observer/releases/299047851","Warn: release artifact v1.1 does not have provenance: https://api.github.com/repos/fini-net/gh-observer/releases/297734560","Warn: release artifact v1.0 does not have provenance: https://api.github.com/repos/fini-net/gh-observer/releases/297105097"],"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#signed-releases"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#fuzzing"}},{"name":"Branch-Protection","score":3,"reason":"branch protection is not maximal on development and all release branches","details":["Info: 'allow deletion' disabled on branch 'main'","Info: 'force pushes' disabled on branch 'main'","Info: 'branch protection settings apply to administrators' is required to merge on branch 'main'","Warn: 'stale review dismissal' is disabled on branch 'main'","Warn: branch 'main' does not require approvers","Info: codeowner review is required on branch 'main'","Warn: 'last push approval' is disabled on branch 'main'","Warn: no status checks found to merge onto branch 'main'","Info: PRs are required in order to make changes on branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#branch-protection"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: GNU General Public License v2.0: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#license"}},{"name":"Contributors","score":10,"reason":"project has 6 contributing companies or organizations","details":["Info: found contributions from: MocentricInc, adRise, fini-net, ortelius, sepisolar, tubi"],"documentation":{"short":"Determines if the project has a set of contributors from multiple organizations (e.g., companies).","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#contributors"}},{"name":"CI-Tests","score":10,"reason":"30 out of 30 merged PRs checked by a CI test -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project runs tests before pull requests are merged.","url":"https://github.com/ossf/scorecard/blob/c22063e786c11f9dd714d777a687ff7c4599b600/docs/checks.md#ci-tests"}}]},"last_synced_at":"2026-04-19T21:28:41.270Z","repository_id":337369618,"created_at":"2026-04-19T21:28:41.271Z","updated_at":"2026-04-19T21:28:41.271Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34157453,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-10T02:00:07.152Z","response_time":89,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-logs","gh-extension","github-actions","github-cli","latency-monitor","pull-requests","tui-app"],"created_at":"2026-02-16T04:10:56.289Z","updated_at":"2026-06-10T15:00:35.233Z","avatar_url":"https://github.com/fini-net.png","language":"Go","funding_links":[],"categories":["🆕 Recently Updated"],"sub_categories":[],"readme":"# gh observer\n\n[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/fini-net/gh-observer/badge)](https://scorecard.dev/viewer/?uri=github.com/fini-net/gh-observer)\n[![OpenSSF Baseline](https://www.bestpractices.dev/projects/12633/baseline)](https://www.bestpractices.dev/projects/12633)\n![GitHub Issues](https://img.shields.io/github/issues/fini-net/gh-observer)\n![GitHub Pull Requests](https://img.shields.io/github/issues-pr/fini-net/gh-observer)\n![GitHub License](https://img.shields.io/github/license/fini-net/gh-observer)\n![GitHub watchers](https://img.shields.io/github/watchers/fini-net/gh-observer)\n\nA GitHub PR check and Actions run watcher that improves on `gh pr checks --watch`\nby showing runtime metrics, queue latency, and better handling of startup delays.\n\n![project banner: abstract representation of code flowing through a pull request, using interconnected nodes and lines.](docs/gh-observer-banner.jpeg)\n\n## Why?\n\nThe existing `gh pr checks --watch` doesn't show how long checks have been\nrunning, doesn't handle the 30-90s startup delay well, and doesn't show queue\nlatency. This creates anxiety when watching CI runs - \"is it stuck or just\nslow?\"\n\n## Features\n\n- ⏱️ **Runtime metrics** - Shows elapsed time: `3m 52s` tells you exactly how\n  long checks have been running\n- ⏳ **Queue latency** - Displays wait time: `15s` shows how long GitHub queued\n  the job before starting\n- 🔄 **Real-time updates** - Auto-refreshes every 5s (configurable) without\n  manual polling\n- ⚡ **Startup phases** - Helpful messages like \"Waiting for Actions to\n  start...\" during the 30-90s GitHub delay\n- 🔧 **Actions run watching** - Monitor any GitHub Actions workflow run by\n  URL, not just PR checks\n- 🛡️ **Rate limits** - Backs off automatically when approaching API limits to\n  avoid interruptions\n- 📊 **Historical averages** - Shows average runtime for each job based on\n  recent completed runs, so you know if things are taking longer than usual\n- ⚡ **`--quick` mode** - Skip the historical averages fetch when you just want\n  a fast snapshot\n- ✅ **CI-friendly** - Returns exit codes (0=success, 1=failure) for script\n  automation\n\n## Example Output\n\n```ShellOutput\nPR #123: Add new feature\n\nStartup Phase (37s elapsed):\n  ⏳ Waiting for Actions to start...\n  💡 GitHub typically takes 30-90s to queue jobs after PR creation\n\nPR #5: 🔶 [claude] /init 21:04:15 UTC\nUpdated 0s ago  •  Pushed 43h 8m 11s ago\n\nStartup   Workflow/Job                                Duration   Avg\n\n  15s ✗ MarkdownLint / lint                             5s        6s\n   .github:13 - Failed with exit code: 1\n   CLAUDE.md:100 - Lists should be surrounded by blank lines: CLAUDE.md:100 MD032/blanks-around-lists Lists should be surr\n\n  15s ✓ Auto Assign / run                               5s        4s\n  15s ✓ CUE Validation / verify                         6s        7s\n  15s ✓ Checkov / scan                                 27s       31s\n  15s ✓ Claude Code Review / claude-review          3m 52s   4m 10s\n  15s ✓ Lint GitHub Actions workflows / actionlint      8s        9s\n  39s ✓ Checkov                                         2s        2s\n\nPress q to quit\n```\n\n## Example Animations\n\nThanks to [asciinema](https://asciinema.org/) we can show you how our extension\nlooks in practice.  You can compare to [old animations](docs/OldAnimations.md) to\nsee how we have evolved.\n\n### PR that was already merged\n\n![animation of checking the GHA status on a merged PR](docs/gh-observer-merged-pr2.gif)\n\nThis shows in real-time (not accelerated) how it goes when you check out a\nPR that is already merged.\n\n### PR in remote repo that was already merged\n\nRunning `gh observer https://github.com/MartinDelille/nautilus/pull/13` results in:\n\n![animation of checking the GHA status on a merged PR in a remote repo](docs/gh-observer-uncloned-repo.gif)\n\nAgain in real-time, but now checking out a PR on a repo that we do not\nhave cloned locally.  Note: it requires the full URL.\n\n### PR that was just created\n\n![animation of watching checks after creating a PR](docs/gh-observer-active-pr.gif)\n\nThis was sped up 2x.  In this example, the Claude check fails, illustrating how\nerror log output is integrated alongside the list of jobs.\n\n### PR that was just created with GHAs that use descriptions\n\n![animation of watching checks after creating a PR with GHAs that use descriptions](docs/gh-observer-descriptions2.gif)\n\nThe [Super-Linter](https://github.com/super-linter/super-linter) and a few other\nGitHub Actions utilize the description field to convey success or failure.  Our\nextension doesn't show descriptions for successful checks and displays them for\ncases with errors to be consistent with the GitHub Actions that make it easier\nto show the right bit of the logs.  Since we don't try to show the logs for\nsuper-linter, you're \"stuck\" clicking on the title of the job in your terminal\nand it will open up in your favorite web browser.\n\nThis was sped up 10x.\n\n## Installation\n\n### Install precompiled binary\n\nThe easiest way to install gh-observer is as a GitHub CLI extension:\n\n```bash\ngh extension install fini-net/gh-observer\n```\n\nThis installs a precompiled binary for your platform - no Go toolchain required. To install a specific version:\n\n```bash\ngh extension install fini-net/gh-observer --pin v1.0.0\n```\n\n### Or install via go install\n\nIf you prefer installing via Go:\n\n```bash\ngo install github.com/fini-net/gh-observer@latest\n```\n\n### Or build from source\n\nTo build from source:\n\n```bash\ngit clone https://github.com/fini-net/gh-observer.git\ncd gh-observer\njust build\n```\n\n## Usage\n\n### Auto-detect PR from current branch\n\n```bash\ngh observer\n```\n\n### Watch specific PR in current repo\n\n```bash\ngh observer 123\n```\n\n### Watch external PR by URL\n\nYou can watch checks on any public PR by passing the full URL - no need to clone the repo:\n\n```bash\ngh observer https://github.com/StackExchange/dnscontrol/pull/3941\n```\n\nThis works for any GitHub PR URL and is useful for:\n\n- Reviewing checks on a colleague's PR in another repo\n- Monitoring upstream dependencies before merging\n- Following CI status on projects you don't have cloned locally\n\n### Watch an Actions workflow run\n\nYou can also watch any GitHub Actions workflow run by passing its URL. This is\nuseful for monitoring CI triggered by a merge to main, a scheduled workflow,\nor a `workflow_dispatch` event - things that aren't tied to a PR:\n\n```bash\ngh observer https://github.com/fini-net/gh-observer/actions/runs/25856656092\n```\n\nThe display shows each job in the run with runtime metrics and historical\naverages, just like PR mode but without the queue latency column:\n\n```ShellOutput\nfini-net/gh-observer: CI  15:04:05 UTC\nUpdated 5s ago  •  Pushed 2m ago\n\n    Workflow/Job                                ThisRun   HistAvg\n\n✓ CI / test                                      1m 30s    1m 25s\n◐ CI / lint                                        45s        --\n✗ CI / deploy                                    2m 10s    1m 50s\n\nPress q to quit\n```\n\nThe header shows the repo name, the run's display title, and how long ago\nthe head commit was pushed (or when the run was created if commit info is\nunavailable). Exit code follows the same convention: 0 if all jobs succeed,\n1 if any job fails.\n\n### Skip historical averages for a faster snapshot\n\nIf you just want a quick look without waiting for the historical averages\nfetch, use `--quick` (or `-q`):\n\n```bash\ngh observer --quick\ngh observer -q 123\n```\n\nThis skips the extra API calls for historical job runtimes and prints\nimmediately. Useful when you're in a hurry or don't have the API budget\nto spare.\n\n### Use in CI pipelines\n\nOur primary focus is on improving the interactive experience, but we also\nset the `exit` code for the process in a potentially useful way.\n\n```bash\n# Wait for PR checks to complete and exit with their status\ngh observer \u0026\u0026 echo \"All checks passed!\"\n\n# Wait for an Actions run to complete and exit with job status\ngh observer https://github.com/owner/repo/actions/runs/123456789 \u0026\u0026 echo \"All jobs passed!\"\n```\n\n## Configuration\n\nCreate `~/.config/gh-observer/config.yaml` to customize settings:\n\n```yaml\n# Refresh interval for polling GitHub API\nrefresh_interval: 5s\n\n# Color codes for terminal output (ANSI 256-color palette)\ncolors:\n  success: 10  # Green - completed successfully\n  failure: 9   # Red - completed with failure\n  running: 11  # Yellow - currently in progress\n  queued: 8    # Gray - waiting to start\n```\n\nSee `.config.example.yaml` for reference.\n\n## Authentication\n\ngh-observer uses GitHub authentication in this order:\n\n1. `GITHUB_TOKEN` environment variable\n2. `gh` CLI authentication (`gh auth token`)\n\nMake sure you have either set up.\n\n## Supported Platforms\n\nPrecompiled binaries are available for:\n\n- **macOS**: Intel (amd64) and Apple Silicon (arm64)\n- **Linux**: x86-64 (amd64) and ARM64\n- **Windows**: x86-64 (amd64)\n\nAll binaries include supply chain security attestations for verification.\n\n## Verifying Release Assets\n\nEvery release includes three independent mechanisms for verifying the integrity\nand authenticity of downloaded binaries. You can use any combination of these\nmethods.\n\n### Option 1: GitHub Build Attestations (recommended)\n\nThis is the simplest method and verifies that the binary was built by our\nrelease workflow on GitHub's infrastructure:\n\n```bash\ngh attestation verify darwin-arm64 --owner fini-net\n```\n\nReplace `darwin-arm64` with the binary matching your platform. Available\nbinaries: `darwin-amd64`, `darwin-arm64`, `linux-amd64`, `linux-arm64`,\n`freebsd-amd64`, `freebsd-arm64`, `windows-amd64.exe`, `windows-arm64.exe`.\n\n### Option 2: Cosign Signatures (keyless)\n\nEach binary is signed using Sigstore keyless (certificate-based) signing. To\nverify a binary against its signature and certificate:\n\n```bash\ncosign verify-blob darwin-arm64 \\\n  --certificate darwin-arm64.pem \\\n  --signature darwin-arm64.sig \\\n  --certificate-identity https://github.com/fini-net/gh-observer/.github/workflows/release.yml@refs/tags/v1.0.0 \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com\n```\n\nAlternatively, you can verify using the cosign bundle file:\n\n```bash\ncosign verify-blob darwin-arm64 \\\n  --bundle darwin-arm64.bundle \\\n  --certificate-identity https://github.com/fini-net/gh-observer/.github/workflows/release.yml@refs/tags/v1.0.0 \\\n  --certificate-oidc-issuer https://token.actions.githubusercontent.com\n```\n\nReplace `v1.0.0` with the release version you are verifying. The `.sig`,\n`.pem`, and `.bundle` files are available on the\n[releases page](https://github.com/fini-net/gh-observer/releases).\n\n### Option 3: SLSA Provenance\n\nEach release includes a `.intoto.jsonl` provenance attestation generated by the\n[SLSA GitHub Generator](https://github.com/slsa-framework/slsa-github-generator),\nproviding SLSA Build Level 3 guarantees. This attestation is non-forgeable proof\nof the build origin and can be verified using the\n`slsa-verifier` tool:\n\n```bash\nslsa-verifier verify-artifact darwin-arm64 \\\n  --provenance-path gh-observer.intoto.jsonl \\\n  --source-uri github.com/fini-net/gh-observer \\\n  --builder-id https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml\n```\n\n### Verifying Checksums Manually\n\nYou can also verify SHA-256 checksums directly. Download the checksums from the\nrelease assets and compare:\n\n```bash\nsha256sum -c checksums.txt\n```\n\n### Verifying the Release Author\n\nTo verify the identity of the person or process that authored the release:\n\n1. **Check the release tag commit** - View the author and DCO signature on the\n   commit the release tag points to:\n\n   ```bash\n   git log -1 --format='Author: %an \u003c%ae\u003e%nSigned-off-by: %(trailers:key=Signed-off-by)' v1.8.0\n   ```\n\n2. **Check the GitHub release author** - Each release on the\n   [releases page](https://github.com/fini-net/gh-observer/releases) shows the\n   GitHub username of the maintainer who created it.\n\n3. **Verify build attestations** - Confirms the binary was produced by the\n   authorized release workflow (see Option 1 above).\n\nSee [Verifying Release Author Identity](.github/SECURITY.md#verifying-release-author-identity)\nin the security policy for full details.\n\n## Known Limitation: Live Logs for Slow Jobs\n\nWe'd love to show you live (non-error) logs while your jobs are still running —\nespecially for jobs that take over a minute. Unfortunately, the\n[GitHub Actions API no longer returns logs in real-time](https://github.com/orgs/community/discussions/154834).\nThe logs endpoint returns a 404 while a job is in progress and only serves logs\nafter the job completes. This makes streaming or tailing job logs via the API\nimpossible today.\n\nMultiple attempts to work around this (see\n[#127](https://github.com/fini-net/gh-observer/issues/127)) have confirmed\nthere is no viable alternative. GitLab supports this; GitHub does not (yet).\n\nIf live job logs would help your workflow, please\n[upvote and comment on the community discussion](https://github.com/orgs/community/discussions/154834)\nto let GitHub know this matters. The more voices, the more likely we'll get a\nreal-time log streaming API.\n\n## Testing\n\nAll major changes (including new features, bug fixes, and behavior changes)\nmust add or update tests that verify the changed functionality. Tests run\nautomatically in CI on every push to `main` and on every pull request (via the\n[CI workflow](.github/workflows/ci.yml)). They also run locally before creating\na PR (via the `just pr` hook).\n\n### Running tests locally\n\n```bash\n# Run all unit tests\njust test\n# Or equivalently\ngo test ./...\n\n# Run a specific package\ngo test ./internal/timing/...\ngo test ./internal/github/...\ngo test ./internal/tui/...\ngo test ./internal/config/...\ngo test ./internal/debug/...\n```\n\n### What the tests cover\n\nUnit tests cover timing calculations (queue latency, runtime, duration\nformatting), GitHub API parsing (GraphQL check runs, REST workflow runs,\nhistory fetching, PR URL and Actions run URL parsing), TUI logic (display\nformatting, state updates, exit codes for both PR and run modes),\nconfiguration loading, and debug logging. TUI rendering and live GitHub API\ninteractions are tested manually by running `just build` and pointing\nthe binary at a real PR or Actions run URL.\n\n## Development\n\nThis project uses [just](https://github.com/casey/just) for task automation:\n\n```bash\n# Build the binary and install locally as gh extension\njust build\n\n# Run on current PR\ngh observer\n\n# Create a PR\njust pr\n\n# Merge a PR\njust merge\n```\n\n## Architecture\n\nBuilt with:\n\n- [Bubbletea](https://github.com/charmbracelet/bubbletea) - TUI framework\n- [Lipgloss](https://github.com/charmbracelet/lipgloss) - Terminal styling\n- [go-github](https://github.com/google/go-github) - GitHub API client\n- [Viper](https://github.com/spf13/viper) - Configuration management\n\nSee `CLAUDE.md` for detailed implementation notes.  Read the [linear walkthrough](docs/linear-walkthrough.md)\nto get a detailed walkthrough of the code and to learn why some\nof the design choices were made.  See [Design Documentation](docs/DESIGN.md)\nfor a comprehensive description of all actors, actions, and data flows\nwithin the system.\n\n## Contributing\n\n- [Code of Conduct](.github/CODE_OF_CONDUCT.md)\n- [Contributing Guide](.github/CONTRIBUTING.md) includes a step-by-step guide to our\n  [development process](.github/CONTRIBUTING.md#development-process).\n\n## Support \u0026 Security\n\n- [Getting Support](.github/SUPPORT.md)\n- [Security Policy](.github/SECURITY.md) (including [release support scope and end-of-life policy](.github/SECURITY.md#release-support))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffini-net%2Fgh-observer","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffini-net%2Fgh-observer","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffini-net%2Fgh-observer/lists"}