ghx

module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 15, 2026 License: MIT

README ΒΆ

ghx β€” GitHub CLI Cache Proxy

ghx Dashboard

A caching proxy for the GitHub CLI (gh) that eliminates redundant API calls, prevents rate limiting, and dramatically speeds up repeated commands.

Built for AI agent workflows where multiple agents (Copilot CLI, coding agents, MCP servers) hammer the same gh commands simultaneously.

Highlights

  • πŸš€ 10x faster cached responses (~0.1s vs ~1s)
  • πŸ”„ Singleflight coalescing β€” 5 agents asking the same thing = 1 API call
  • 🎯 Allowlist-based β€” only caches known-safe read-only commands
  • 🧹 Auto-invalidation β€” mutations flush related cache entries
  • πŸ“Š Web dashboard β€” real-time hit rates, per-command stats, request log
  • πŸ”Œ Drop-in replacement β€” just use ghx instead of gh
  • πŸ“¦ No gh required β€” auto-downloads GitHub CLI on first use if not installed

Install

brew tap brunoborges/tap
brew install ghxd
Quick install script
curl -fsSL https://raw.githubusercontent.com/brunoborges/ghx/main/install.sh | bash

This detects your OS and architecture, downloads the latest release, and installs ghx and ghxd to /usr/local/bin. If the GitHub CLI (gh) is not already installed, a lightweight shim is also installed that redirects gh commands through ghx. To install elsewhere:

curl -fsSL https://raw.githubusercontent.com/brunoborges/ghx/main/install.sh | INSTALL_DIR=~/.local/bin bash
Manual download

Download the latest release for your platform from GitHub Releases:

# macOS (Apple Silicon)
curl -fsSL https://github.com/brunoborges/ghx/releases/latest/download/ghx-darwin-arm64.tar.gz | tar xz
sudo cp ghx ghxd /usr/local/bin/

# Linux (x64)
curl -fsSL https://github.com/brunoborges/ghx/releases/latest/download/ghx-linux-amd64.tar.gz | tar xz
sudo cp ghx ghxd /usr/local/bin/

# Linux (arm64)
curl -fsSL https://github.com/brunoborges/ghx/releases/latest/download/ghx-linux-arm64.tar.gz | tar xz
sudo cp ghx ghxd /usr/local/bin/
Build from source
git clone https://github.com/brunoborges/ghx.git
cd ghxd
make build
# Binaries are in bin/ghx and bin/ghxd
sudo cp bin/ghx bin/ghxd /usr/local/bin/
Agents Plugin (Claude Code & Copilot CLI)

If you use Claude Code or GitHub Copilot CLI, install the plugin and your agent will automatically prefer ghx over gh:

# Add the marketplace (one-time)
/plugin marketplace add brunoborges/agent-plugins

# Install the plugin
/plugin install ghx@agent-plugins

Local development / testing: claude --plugin-dir ./agent-plugin

The plugin:

  • Lazy-installs ghx and ghxd binaries on first use
  • Adds ghx to PATH so agents use it automatically
  • Includes a skill that teaches agents to prefer ghx for all GitHub CLI calls

See the plugin README for details. Plugin releases are available on the Releases page with the plugin-v* tag.

Using ghx Without Installing GitHub CLI

You don't need to install the GitHub CLI (gh) separately. When ghx needs the real gh binary and can't find one, it automatically downloads the latest release from cli/cli to ~/.ghx/bin/gh.

How it works

When ghx or ghxd needs to execute a gh command, it resolves the real binary using this order:

  1. User override β€” if gh_path is set in ~/.ghx/config.yaml or via GHX_GH_PATH env var, use that path directly
  2. PATH scan β€” search PATH for a real gh binary (skipping ghx shims)
  3. Managed location β€” check ~/.ghx/bin/gh (previously auto-downloaded)
  4. Auto-download β€” download the latest GitHub CLI from cli/cli releases to ~/.ghx/bin/gh

The download includes SHA-256 checksum verification against the official release checksums.

The gh shim

When you install ghx via the install script and gh is not already installed, a lightweight shim script is placed at the same location as ghx (e.g., /usr/local/bin/gh). This shim simply redirects all gh commands through ghx:

#!/bin/sh
# ghx-shim: this script redirects gh commands through ghx for caching
exec ghx "$@"

This means existing tools, scripts, and CI workflows that call gh will automatically benefit from caching β€” no changes needed. The shim is only installed if no real gh is found in PATH at install time.

Example first-run experience
$ ghx pr list
ghx: GitHub CLI (gh) not found, downloading...
ghx: downloading GitHub CLI v2.74.0...
ghx: GitHub CLI v2.74.0 installed to /Users/you/.ghx/bin/gh
#1  My first PR  (main <- feature-branch)

Subsequent runs use the cached gh binary at ~/.ghx/bin/gh β€” no re-download.

Overriding the gh binary path

If you have gh installed in a non-standard location, point ghx at it:

# ~/.ghx/config.yaml
gh_path: /opt/homebrew/bin/gh

Or via environment variable:

export GHX_GH_PATH=/opt/homebrew/bin/gh
Updating the managed gh binary

When ghx auto-downloads gh, the binary is stored at ~/.ghx/bin/gh. To upgrade it to the latest release:

ghx ghcli upgrade

If the managed binary is more than 30 days old, ghx prints a one-line reminder:

ghx: managed gh binary is 45 days old β€” run 'ghx ghcli upgrade' to update

To check which gh binary is in use:

$ ghx ghcli status
gh binary:  /Users/you/.ghx/bin/gh
source:     managed (~/.ghx/bin/gh)
version:    2.74.0
installed:  3 days ago

Usage

Use ghx exactly like gh β€” the daemon starts automatically on first use:

# These are cached (read-only commands)
ghx pr list --repo owner/repo --json number,title
ghx issue view 42 --json title,state
ghx api /repos/owner/repo --jq '.stargazers_count'
ghx run list --repo owner/repo

# These pass through directly to gh (mutations)
ghx pr create --title "My PR" --body "Description"
ghx issue close 42
Cache behavior
First call:   ghx pr list ...   β†’ 1.1s (cache miss, calls gh)
Second call:  ghx pr list ...   β†’ 0.1s (cache hit, instant)
After 30s:    ghx pr list ...   β†’ 1.0s (TTL expired, fresh call)
Per-command options
ghx --no-cache pr list ...     # Bypass cache for this call
ghx --ttl 120 pr list ...      # Override TTL to 120 seconds
GHX_NO_CACHE=1 ghx pr list ... # Same via env var
GHX_TTL=60 ghx pr list ...     # Same via env var

Daemon Management

ghx xdaemon start          # Start in foreground
ghx xdaemon start -d       # Start detached (background)
ghx xdaemon stop           # Graceful shutdown
ghx xdaemon status         # Show uptime and cache stats
ghx xdaemon restart        # Stop + start

The daemon auto-starts on first ghx call. If the daemon can't start, ghx falls back to running gh directly β€” it never blocks you.

Cache Management

ghx xcache stats           # Show hit rates and per-command breakdown
ghx xcache flush           # Flush all entries
ghx xcache flush pr        # Flush PR-related entries only
ghx xcache keys            # List cached keys (debugging)
Example stats output
Uptime:          2h 34m
Total Requests:  1,247
Cache Hits:      891 (71.4%)
Cache Misses:    203 (16.3%)
Passthrough:     153 (12.3%)
Coalesced:       87
Cache Size:      142 / 1000 entries

Top Commands:
  pr list                  412 hits / 48 misses  (89.6%)
  issue view               198 hits / 32 misses  (86.1%)
  pr view                  143 hits / 67 misses  (68.1%)
  api get                   88 hits / 31 misses  (73.9%)

Web Dashboard

When the daemon is running, a live dashboard is available at:

http://localhost:9847/

It shows:

  • Real-time hit rate and request counters
  • Per-command breakdown with hit/miss rates and average latency
  • Request log β€” live tail of recent requests with cache result and timing

The dashboard auto-refreshes every 2 seconds. No external dependencies β€” it's a single HTML page embedded in the binary.

JSON API

The dashboard data is also available as JSON for scripting:

curl http://localhost:9847/api/stats | jq .
curl http://localhost:9847/api/log?limit=50 | jq .
curl http://localhost:9847/api/ttl-analysis | jq .

What Gets Cached

Only explicitly allowlisted read-only commands are cached:

Command Cached
gh pr list/view/status/checks/diff βœ…
gh issue list/view/status βœ…
gh repo view/list βœ…
gh run list/view βœ…
gh workflow list/view βœ…
gh release list/view βœ…
gh search repos/issues/prs/commits/code βœ…
gh api (GET only) βœ…
gh label list βœ…
gh gist list/view βœ…
gh project list/view βœ…
gh cache list βœ…
gh ruleset list/view/check βœ…
gh org list βœ…
gh pr create/merge/close/edit ❌ (mutation β†’ invalidates PR cache)
gh issue create/edit/delete/close ❌ (mutation β†’ invalidates issue cache)
gh auth/config/codespace/secret ❌ (always passthrough)

Mutations automatically invalidate related cache entries. For example, gh pr merge 42 flushes all cached PR entries for that repo.

Custom Commands

You can add your own commands to the allowlist via ~/.ghx/config.yaml:

additional_cacheable:
  - "gh status"
  - "gh variable list"
  - "gh secret list"

Each entry should be the full command prefix (e.g., "gh status" for a single-word subcommand, or "gh variable list" for two-word). Custom commands are classified with ResourceUnknown β€” they participate in caching but won't be invalidated by mutation detection. To apply changes, restart the daemon: ghxd --restart.

Configuration

Configuration file: ~/.ghx/config.yaml

# Default TTL for all cached commands (default: 30s)
ttl: 30s

# Per-command TTL overrides
ttl_overrides:
  pr_list: 60s
  pr_view: 30s
  issue_list: 60s
  run_list: 15s

# Max cached entries before LRU eviction (default: 1000)
max_cache_entries: 1000

# Dashboard HTTP port (default: 9847)
dashboard_port: 9847

# Auto-start daemon on first ghx call (default: true)
auto_start: true

# Additional commands to cache
additional_cacheable:
  - "gh status"

# Path to gh binary (default: auto-resolved)
# Resolution order: this setting β†’ PATH β†’ ~/.ghx/bin/gh β†’ auto-download
# gh_path: /usr/local/bin/gh

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Agent 1 β”‚  β”‚ Agent 2 β”‚  β”‚ Agent 3 β”‚
β”‚ (ghx)   β”‚  β”‚ (ghx)   β”‚  β”‚ (ghx)   β”‚
β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”˜
     β”‚            β”‚            β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                  β”‚ Unix Domain Socket
           β”Œβ”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”
           β”‚    ghxd     β”‚
           β”‚  (daemon)   β”‚
           β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
           β”‚ Cache (LRU) β”‚
           β”‚ Singleflightβ”‚
           β”‚ Metrics     β”‚
           β”‚ Dashboard   β”‚
           β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
                  β”‚ exec
              β”Œβ”€β”€β”€β”΄β”€β”€β”€β”
              β”‚  gh   β”‚
              β””β”€β”€β”€β”€β”€β”€β”€β”˜

Key design decisions:

  • Allowlist, not denylist β€” only known-safe commands are cached
  • Context-aware cache keys β€” includes repo, branch, host, and auth token hash to prevent cross-context collisions
  • Singleflight β€” concurrent identical requests share a single gh execution
  • Coarse invalidation β€” mutations flush the entire resource namespace (all PR cache for that repo)
  • Graceful fallback β€” if daemon is down or fails, ghx runs gh directly
  • Self-contained β€” auto-downloads gh if not installed; no external dependencies beyond a network connection

Security

  • Unix socket with 0600 permissions (owner-only access)
  • Auth tokens are never stored β€” only a SHA256 fingerprint is used in cache keys
  • Dashboard binds to 127.0.0.1 only (not accessible from network)
  • In-memory cache only (lost on daemon restart)
  • Each user runs their own isolated daemon

Development

# Build
make build

# Run tests
make test

# Clean
make clean

License

MIT

Directories ΒΆ

Path Synopsis
cmd
ghx command
ghxd command
internal
ghcli
Package ghcli manages the discovery and installation of the real GitHub CLI (gh) binary.
Package ghcli manages the discovery and installation of the real GitHub CLI (gh) binary.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL