A Go GitHub CLI extension to copy, display, synchronise, and discover files in GitHub repositories and mapped local projects. Invoke it as gh arrow.
Release installation requires GitHub CLI and a published release with an artifact for your platform. To build from this checkout, use the development setup. When a release is available, run:
gh extension install flarebyte/gh-arrow
gh arrow --version
gh arrow --help
gh arrow cat github:flarebyte:gh-arrow:README.mdTo pin a specific published tag, replace <release-tag> below:
gh extension install flarebyte/gh-arrow --pin '<release-tag>'Configured release builds target darwin-arm64, linux-amd64, and windows-amd64.
gh arrow cp github:owner:project:path/to/file.yaml ./local.yaml
gh arrow cat local:project2:file.md
gh arrow cat github:owner:project:README.md --ref main
gh arrow ls 'github:owner:project:docs/**/*.md'
gh arrow ls --org tools-org --topic cli README.md --jsonSources use github:<owner>:<repo>:<path> or local:<alias>:<path>. Paths use forward slashes; colon is reserved. Quote shell globs. ls supports filename * and recursive directory **; cat and cp treat filename metacharacters literally. Organization/user discovery checks exact file paths.
GitHub sources prefer configured local working trees. A mapping deliberately selects local files: missing mapped files fail without a remote fallback. --ref selects a remote branch, tag, or commit and is rejected for mapped sources and discovery.
Mappings are loaded from the global CUE file ~/.gh-arrow, or from --config <path>. Arrow never discovers repository mappings in a checkout; sync reads its separate project manifest. Roots must be absolute local paths. A missing default config means no mappings; a missing explicitly selected config is an error. No separate CUE executable is needed.
local: mappings: [{
repo: "project2"
root: "/absolute/path/to/project2"
}]The embedded schema rejects unknown fields and incomplete values; imports are not allowed. See the full configuration example. Keep credentials out of this file.
Remote access uses GitHub.com and existing gh credentials. For private repositories, authenticate separately with gh auth login. Arrow never prompts, launches a browser, or stores tokens in its config. Public access can work without stored credentials. GitHub Enterprise hosts are outside this version's scope.
cp copies one file, requires an existing destination parent, and refuses an existing destination unless --force is given. It stages complete bytes before publication and rejects symlink destinations. Source permissions and timestamps are not copied; new files use private permissions.
cat writes exact bytes, with no added newline. Successful cp writes no stdout. Listings are sorted; text escapes control characters, while --json preserves Unicode path values. JSON listings are arrays, including [] for no matches; discovery records additionally contain repository. Diagnostics go to stderr with stable ARROW_* codes. Exit status is 0 for success, 1 for I/O/network/authentication failures, 2 for invalid arguments or configuration, and 3 for sync check drift.
Declare the files your project consumes in sync.gh-arrow.cue:
version: 1
files: [{
source: "github:owner:project:path/to/file.yaml"
destination: "local.yaml"
}]Run from that project directory:
gh arrow sync status # List unchanged, modified, or missing files
gh arrow sync diff # Review local-to-upstream differences
gh arrow sync --dry-run # Preview without writing
gh arrow sync # Create missing files and replace changed files
gh arrow sync check --json # Audit: exit 0 when in sync, 3 for driftUse --file <path> to select another manifest. Destinations are relative to its
directory; parents must already exist. Sync handles exact files only, rejects
symlinks, traversal and duplicate destinations, and never deletes undeclared files.
A declaration authorises replacement of local edits; sync does not merge.
Mapped sources read the current working tree, including uncommitted changes;
unmapped GitHub sources read the current default branch. Sync does not accept
ref or --ref. Each remote repository resolves once per run. --config still
selects machine-local repository mappings; status output identifies the selected
local or github backend. --json is available for status, check, dry-run and
apply; records contain source, destination, status, and backend, sorted
by destination. Apply reports the statuses observed before writing.
All sources are staged before any destination changes. Each changed file is replaced atomically; publication failure can leave earlier files applied, with the applied count in the diagnostic. Binary differences, text containing unsafe terminal controls, and missing empty files receive a notice; text diffs use a unified replacement hunk with three lines of surrounding context. Files exceeding 8 MiB per side receive a difference notice instead of a patch.
- Contributing: development setup, checks, tests, and specification changes.
- Technical design: code boundaries, safety guarantees, and runtime limits.
- Specification: command contracts and acceptance scenarios.
- Documentation index: design sources, decisions, publishing, and history.
AI agents should start with AGENTS.md. This project includes an MIT license.