rickub CLI
rickub is the command-line interface to a rickub git host —
the smartest git in the universe, on the command line. It is a thin, standalone
HTTP client for the rickub JSON API (/api/v1), authenticated with a personal
access token (PAT). It imports none of the server's code: a clean client boundary.
- Repo:
ssh://git@rickub.com/rickub/cli.git - Home: https://rickub.com/rickub/cli
Install
Download a release. Binaries for linux (amd64, arm64) and macos (amd64,
arm64) are attached to each release at https://rickub.com/rickub/cli/releases.
Assets are served at releases/download/<tag>/<asset>, and releases/latest
redirects to the newest release:
# newest release tag, e.g. v0.1.0 (or set it by hand)
tag=$(curl -fsSI https://rickub.com/rickub/cli/releases/latest | awk 'tolower($1)=="location:"{print $2}' | tr -d '\r' | sed 's#.*/##')
v=${tag#v}
os=$(uname -s | tr '[:upper:]' '[:lower:]')
arch=$(uname -m); case "$arch" in x86_64) arch=amd64 ;; aarch64) arch=arm64 ;; esac
base=https://rickub.com/rickub/cli/releases/download/$tag
curl -fsSLO "$base/rickub_${v}_${os}_${arch}.tar.gz" && curl -fsSLO "$base/SHA256SUMS"
sha256sum -c SHA256SUMS --ignore-missing # shasum -a 256 -c on macOS
tar -xzf "rickub_${v}_${os}_${arch}.tar.gz" rickub
sudo install rickub /usr/local/bin/rickub
Each archive holds the rickub binary plus README.md and LICENSE.
Build from source (Go 1.26+):
git clone ssh://git@rickub.com/rickub/cli.git
cd cli
go build -o rickub . # produces ./rickub at the repo root
To stamp a version into the binary (releases use the bare version, no v):
go build -ldflags "-X rickub.com/rickub/cli/cmd.Version=$(cat VERSION)-dev" -o rickub .
Verify:
./rickub version
Authenticate
# browser (device) flow — the default. Prints a code, opens your browser,
# waits for you to approve the sign-in while logged in to the website, and
# stores the PAT the server mints. Nothing is copy-pasted.
rickub auth login
rickub auth login --host https://dev.rickub.com # any rickub host
rickub auth login --scope read # a read-only token
rickub auth login --no-browser # print the URL, don't open it
To use an existing PAT from Settings → Tokens instead, prefer one of the two
forms that keep the secret out of your shell:
# 1. environment variable — nothing is written to disk
export RICKUB_TOKEN=rickub_pat_xxx
rickub repo list
# 2. stdin, for `auth login` to verify and store it
echo "$RICKUB_PAT" | rickub auth login --with-token --host https://rickub.com
There is also a --token rickub_pat_xxx flag on any command. Use it only when
neither of the above fits: arguments are visible to every process on the
machine via ps, and land in your shell history and in CI logs.
The stored token lives in ~/.config/rickub/config.yaml (mode 0600). The
browser flow mints a normal PAT named "CLI device login" — revoke it any time
in Settings → Tokens.
rickub auth status # show the active host, where the token came from, and verify it
rickub auth logout # remove the stored token for the active host
Tokens are bound to their host
A token stored by auth login is saved under the host it was verified
against and is only ever sent back to that host. Pointing the CLI at a
different server — --host, RICKUB_HOST, or a typo — will not hand your
production credential to it; you get "no token stored for that host" instead.
Log in per host as needed:
rickub auth login --host https://rickub.com # stored for rickub.com
rickub auth login --host http://localhost:3000 # stored separately
rickub auth logout --host http://localhost:3000 # removes only that one
A token you pass explicitly with --token or RICKUB_TOKEN is always honoured
for whatever host is in effect — that is your call to make, not the config's.
The CLI also prints a warning to stderr before sending a token to a host over
plain http://, unless that host is loopback (localhost, 127.0.0.1, [::1]),
where the request never reaches the network.
Config & environment
Effective host and token are resolved with this precedence (first wins):
| Value | Precedence |
|---|---|
| host | --host flag → RICKUB_HOST env → config file → https://rickub.com |
| token | --token flag → RICKUB_TOKEN env → config file entry for that host |
Point the CLI at a dev instance with --host http://localhost:3000 (or set
RICKUB_HOST). XDG_CONFIG_HOME is honoured for the config file location.
The config file looks like this:
host: https://rickub.com
hosts:
https://rickub.com:
token: rickub_pat_…
http://localhost:3000:
token: rickub_pat_…
Command reference
Every command supports --help, and --json for raw JSON output instead of a table.
Every --body/-b takes literal text or - to read the body from stdin (a
heredoc works; trailing newlines, CRLF included, are trimmed). A body that is
empty after resolution is refused where one is required, never posted.
rickub auth login|status|logout
rickub repo list [--user H | --org H] [--page N] [--per-page N]
rickub repo create <name> [--org H] [--public|--private] [-d desc]
rickub repo view <owner/repo>
rickub repo edit <owner/repo> [--visibility public|private] [-d desc] [--default-branch B]
rickub repo rename <owner/repo> <new-name> # needs ADMIN; breaks every existing clone URL
rickub repo transfer <owner/repo> <new-owner> [--yes]
# needs ADMIN here and create rights there;
# confirms first — you may not be able to move it back
rickub repo delete <owner/repo> [--yes]
rickub repo clone <owner/repo> [dir] [--protocol ssh|https] [-- git-args…]
rickub repo files <owner/repo> [path] [--ref R]
rickub repo cat <owner/repo> <path> [--ref R]
rickub repo commits <owner/repo> [ref] [--page N] [--per-page N]
rickub repo compare <owner/repo> <base...head>
rickub repo collaborator list <owner/repo>
rickub repo collaborator add <owner/repo> <user> [--permission read|write|admin]
rickub repo collaborator remove <owner/repo> <user>
rickub pr list [-R owner/repo] [--state open|closed|merged|all]
rickub pr view <number> [-R owner/repo]
rickub pr create [-R owner/repo] --base B --head H --title T [-b body] [--draft] [--head-owner O --head-repo R]
rickub pr ready <number> [-R owner/repo] [--undo] # draft → ready for review (or back)
rickub pr merge <number> [-R owner/repo] [--method merge|squash|ff-only]
rickub pr close <number> [-R owner/repo]
rickub pr reopen <number> [-R owner/repo]
rickub pr edit <number> [-R owner/repo] [-t "title"] [-b "text" | -b -]
# correct the title and/or the description in one call (author or
# repo writer); a field you do not name is left untouched, and
# passing neither flag is refused without sending a request
rickub pr comment <number> [-R owner/repo] -b "text"
# whole-MR conversation comment (any reader)
rickub pr comment <number> [-R owner/repo] -b "text" --file PATH --line N
[--side new|old] [--intent info|request_change|kudos] [--commit SHA]
# line-anchored review comment on the diff (needs WRITE access)
rickub pr comment <number> [-R owner/repo] -b "text" --reply-to <comment-id>
# reply in an existing line-comment thread
rickub pr line-comments <number> [-R owner/repo] [--page N] [--per-page N]
# list the MR's line comments with their ids, anchors and state
rickub pr resolve <number> <comment-id> [-R owner/repo] [--reopen]
rickub pr delete-comment <number> <comment-id> [-R owner/repo]
# retract a conversation comment (soft delete: a tombstone stays);
# ids are shown by `rickub pr view <number>`
rickub run list [-R owner/repo]
rickub run view <number> [-R owner/repo]
rickub run logs <number> [-R owner/repo] [--job NAME | --failed]
# --job prints one job (case-insensitive; an unknown name is an
# error listing the run's real jobs, never the whole log);
# --failed prints every job whose status is not success.
# Both compose with --json.
rickub run rerun <number> [-R owner/repo]
rickub run cancel <number> [-R owner/repo]
rickub run dispatch [-R owner/repo] [--ref B]
rickub run watch <number> [-R owner/repo] [--interval 2s] [--timeout 30m] [--logs]
# follow until terminal; exit 0 on success, 1 otherwise
rickub issue list [-R owner/repo] [--state open|closed|all] [--page N]
rickub issue view <number> [-R owner/repo]
rickub issue create [-R owner/repo] -t "title" [-b "body" | -b - < file]
rickub issue edit <number> [-R owner/repo] [-t "title"] [-b "body" | -b - < file]
# same rules as `pr edit`: name a field to change it, omit it to keep it
rickub issue close <number> [-R owner/repo]
rickub issue reopen <number> [-R owner/repo]
rickub issue comment <number> [-R owner/repo] -b "text"
rickub issue delete-comment <number> <comment-id> [-R owner/repo]
rickub issue label <number> [-R owner/repo] --labels "bug,help wanted" | --clear
rickub issue milestone <number> [-R owner/repo] --milestone "v1.0" | --clear
rickub issue assign <number> [-R owner/repo] --user H [--remove]
rickub issue labels [-R owner/repo]
rickub label list [-R owner/repo]
rickub label create <name> [-R owner/repo] [--color d73a4a] # colors come from `label palette`
rickub label edit <name> [-R owner/repo] [--name new] [--color hex]
rickub label delete <name> [-R owner/repo]
rickub label palette
rickub cache list [-R owner/repo] [--page N] [--per-page N] # CI dependency-cache entries
rickub cache delete <key> [-R owner/repo] [--scope refs/heads/main] # purge a poisoned key (repo admin)
rickub milestone list [-R owner/repo] [--state open|closed|all]
rickub milestone create [-R owner/repo] -t "v1.0" [-d desc] [--due YYYY-MM-DD]
rickub milestone close <id> [-R owner/repo]
rickub milestone reopen <id> [-R owner/repo]
rickub milestone delete <id> [-R owner/repo]
rickub org view <handle>
rickub org members <handle>
rickub org teams <handle>
rickub search repos <query> [--page N] [--per-page N]
rickub api <METHOD> <path> [-f key=value] [-F key=value] # raw escape hatch
rickub browse [owner/repo] [--print]
rickub version
Cloning
repo clone derives the git URL from the API host: ssh://git@<host>/<owner>/<repo>.git
by default, which your SSH keys authenticate, or https://git.<host>/<owner>/<repo>.git
with --protocol https, where the token authenticates that clone (git receives it
through its environment, never on the command line or in .git/config; later
fetches of a private repo over HTTPS need a git credential helper). Merge requests
open ready for review; pass --draft to open a draft, and pr ready flips it.
Repo selector
pr, run, issue, label, milestone, and cache subcommands take -R/--repo owner/repo. When omitted, the repo is
inferred from the current directory's git origin remote (any of https://,
ssh://, or git@host:owner/repo forms).
rickub api — raw escape hatch
Like gh api. PATH is relative to /api/v1 (a leading /api/v1 or / is
optional). --field/-f values are type-inferred (true/false/null/numbers);
--raw-field/-F forces a string. Fields become query parameters for GET/HEAD
and a JSON body otherwise.
rickub api GET /user
rickub api GET search/repos -f q=api
rickub api POST /repos -f name=demo -f visibility=public
Errors & exit codes
API errors are surfaced from the {error:{code,message}} envelope, e.g.
rickub: repository not found (not_found), and the process exits non-zero.
Pointing at a dev host
# run a rickub instance on a spare port, then:
echo "$DEV_PAT" | rickub auth login --with-token --host http://localhost:3000
rickub repo list --user <you>
The dev token is stored separately from your rickub.com token; both stay put.
Security
See SECURITY.md: where the token lives and how it is bound to
its host, the redirect and body-size policy, how git and the browser are
invoked, and what the security workflow checks.
Development
go build ./... && go test ./... && go vet ./... && gofmt -l .
CI runs those on every push and pull request (ci.yml), and security.yml
adds go mod verify, govulncheck, gosec, and staticcheck.
Layout:
main.go # entrypoint; maps API errors to exit codes
cmd/ # Cobra command tree (auth, repo, pr, issue, label, milestone, run, cache, org, search, api, browse)
internal/api/ # typed HTTP client for the rickub JSON API
internal/config/ # config load/save + host/token resolution and binding