skip to content
terminalhire documentation

CLI reference

Every terminalhire subcommand, grouped by what it's for. th works as a short alias everywhere you see terminalhire. terminalhire --help prints a shorter list in the terminal; this page is the full one.

Account — profile and sync are covered in Privacy

Command What it does
init One-command onboarding — start here
login Sign in with GitHub (enriches your profile instantly)
logout Clear the stored GitHub token
link Connect this terminal to your terminalhire account
link --logout Revoke this terminal's linked session
profile --show Display your encrypted local profile
profile --edit Set display name, contact email, remote-only, comp floor
profile --delete Wipe your profile and encryption key from disk
config --show Print current config
refresh Fetch the latest index + re-match, update the local cache
sync --push Opt-in: send your profile to staqs — consent in the browser
sync --enable The same thing under another name
sync --status Show whether you've consented (local read)
sync --delete Hard-delete your synced profile (revocation)
update Update to the latest release (--check, --force)
feedback Send feedback; --full asks the longer form

Matching — see Getting started

Command What it does
jobs Fetch the index, browse roles matched to your profile
jobs --all / --page N / --limit N Page through the full ranked list
jobs --status <s> Filter by applied/saved/dismissed/clicked/unactioned
jobs --remote-only Drop anything that is not remote
jobs --stable Hold the ordering steady between runs
jobs mark <id> <status> Track your funnel — see Job statuses
save <jobId> / saved / unsave <jobId> Save, list, or remove a saved job

Credential — see Proof of Work

Command What it does
trajectory Trajectory from your local Claude Code corpus
trajectory --export Write a derived score + Markdown locally
trajectory --push Opt-in: link your trajectory to your dashboard (typed-yes)
trajectory --inward Read your own corpus without sending anything
trajectory --push --unlink (or --delete) Unlink a pushed trajectory from your dashboard
contribute Feed of credential-building open issues (on by default; opt out with contributeEnabled: false)
contribute <owner/repo> Open, unassigned issues in ONE project you name, ranked to your stack; and, when this terminal is linked, adds the repo to the shared index

Postings — see Postings and claims

Command What it does
work [--priced] Paid tasks: first-party postings by default; --priced adds scraped paid open-source supply
bounties [--priced] Same as work
claim preview <id|url> Read-only preview of a posting before claiming
claim record <id|url> [--start|--no-start] Claim a paid task locally; a first-party posting is also registered server-side, to the GitHub account you proved in the browser
claim start <id|url> [--watch] The one-verb path: records if unrecorded, then provisions — OSS forks + clones; a first-party posting delivers your workspace, --watch waiting out a pending approval
claim attach <id> --worktree … --branch … Link a claim to your working branch
claim slice <id> First-party postings: deliver the workspace — the whole repository at the posted commit, or a file copy if the poster chose a narrower tier (claim start runs this for you)
claim update <id> <state> Move a claim along by hand — ready is the one a non-interactive submit needs first
claim note <id> --body "…" Send a note to the poster and print the text that went out. Turn it on once in the dashboard settings first
claim audit <id> [--remember] Your PR's lifecycle — opened, response, rounds, merged. No score, and nothing kept unless you pass --remember
claim --push / claim --push --revoke Turn the poster's "active N minutes ago" signal on, or off and revoke it
claim submit [id] Open the PR from your fork — or, on a first-party posting, submit the patch over HTTP (no fork) and, with --screenshots <file>, up to six PNGs you took of the change. From the claim’s worktree the id is optional
claim runs <id> First-party postings: read the poster’s own CI verdict on your submitted patch (none on a poster-reviewed posting)
claim status [<id>] Check poster approvals or poll source PR merge state
claim list [--active] Your claims + accepted-PR rate
claim release <id> Give up a claim on this machine. terminalhire is not told, so the poster hears nothing
claim resolve <id> --reason <r> [--note …] Hand a claim back and tell the poster why. Some reasons take the posting off the market until they act; the rest hand it on. --help lists which is which
claim notes [--json] Read what the poster wrote on your claims. Clears the 📝 statusline badge
decline --forget Revoke the consent the bounties list remembered when you passed on a posting with d<number>
protocol register / unregister / status Wire (or unwire) th://claim/<token> links on this machine — see Claim links

terminalhire claim <token> with no verb records the claim that token names — that is what a th:// link runs.

claim start takes --here to use the directory you are in, --fork / --assign / --no-assign to choose what happens on the GitHub side, and --intent "…" / --eta "…" to say what you are doing and by when. claim submit takes --yes, --title <t>, --body-file <path> / --no-body, --no-closes, and, on a first-party posting, --screenshots <file>: a JSON list of up to six PNGs with captions, shown to the poster beside the verification run's own pictures (see Verifying your work).

claim start and claim slice both take --dir <path>, which names where the workspace lands, and --open <agent>, which hands that directory to claude, codex, agy or cursor-agent once it is ready. A directory that already holds files is never written into: the delivery goes to a numbered sibling and prints where it went.

Both commands finish by leaving you in the workspace — your own shell, started there, with exit bringing you back where you were. --open <agent> starts that agent instead. --stay prints the cd and returns, which is also what you get with no terminal attached, so scripts and --watch are unaffected.

Verification — see Verifying your work

Command What it does
run Apply your working diff to a clean clone and run the project's tests in a container
run --watch Re-run on every save
run --keep <s> / --no-preview Hold the preview URL open (local runs), or skip the preview. A preview that cannot start never costs the run its verdict; a hosted run has no preview until venue ingress exists
run --test-command <cmd> Disclosed override of the derived test command
run --placement <kind> Where the run happens. local-docker (default) or hosted — a per-run cloud VM; needs launch credentials, and is what our dispatch worker runs once pointed at it
run --claim <id> / --target <owner/repo> / --sha <sha> Name what is being verified, rather than letting the checkout say
run --image <ref> Run against a named image instead of the one derived from the repo's runtime
run --local Run against the checkout you are standing in
run --json The run document on stdout, for a script rather than a person

Defaults come from .th-run.json in the checkout, so a claim you run repeatedly needs no flags. Needs Docker running.

Read the exit code. 0 the tests were green. 1 we judged the work and it is not green. 2 we rendered no verdict — our environment could not run it honestly, so nothing was said about your work. See how verification runs work.

Connect — see Connect

Command What it does
devs / devs --as-project Rank opted-in builders locally
project "<title>: <skills>" / project --show Declare or view a local project
intro <login> / intro --list / intro --accept @<login> Request, list, or accept intros
connect / connect --mute / connect --unmute Overview + toggle inbound nudges
chat / chat <login> --read / --send Inbox, read, or send in a connection thread
inbox Interactive inbox TUI
th hub Full-screen TUI over claims, jobs, paid tasks, devs, profile and inbox

Craft knowledge — your own notes on a repo

Command What it does
th repo <owner/repo> Continuity, calibration and your notes for one project
th repo note <owner/repo> "…" Add a private note about a repo — ten at most
th repo sync <owner/repo> Backfill your merged-PR history from GitHub, when you ask

Posting from the terminal — see Post a paid task

Command What it does
setup [--repo owner/name] Poster setup: sign in with GitHub and approve a connector in your browser, then install the GitHub App, save a card from your dashboard, confirm your email, and optionally connect Claude Code. The card and email steps need "act on my behalf"; the email step needs an interactive terminal and is for first-time setup only. --repo names the repository when the App covers more than one. Re-run to resume
setup --status Print what publishing still needs, and exit
setup --reconnect Approve a new connector, for example one allowed to act for you. The old one keeps working until you remove it under Settings → Agent connections
post draft --symptom "…" Start a draft. --title, --command and --repo are optional
post show <id> / list / edit <id> / withdraw <id> Read and change drafts; none of this leaves your machine
post submit <id> Interactive terminal only. Prints the exact body and sends it. After setup, the draft is saved to your account; without it, or if the server no longer accepts the stored connector (which is then removed), you get a browser link to confirm
post publish <draft> --price <usd> Interactive terminal only. Needs setup with "act on my behalf" ticked. Shows what a claimant would receive and publishes only when you type yes. --repo defaults to this directory's origin; --title, --outcome, --scope, --acceptance are optional

Audits — for TerminalHire reviewers

These work only for TerminalHire reviewers on the allowlist; the server refuses everyone else. They use the session from terminalhire link. Add --json to any of them for JSON on stdout.

Command What it does
audits list Paid and delivered human audits, who holds each, and the report version
audits show <id> The repository, the paid commit, what the poster asked, and, when you hold the order, a command that clones that commit through the poster's App installation with a one-hour read-only token. You are never added to the repository
audits take <id> / release <id> Take an order so nobody else can, or give it back
audits publish <id> --dir <folder> Reads review.md, writeup.md and findings.json from the folder (2 MB at most), runs a dry run and prints the counts, then publishes and prints the report URL and whether the poster was emailed. --dry-run stops after the counts. On terminalhire.com you type the repository name first. --notify emails again; --commit <sha> --commit-override records a different commit

Exit codes: 0 done; 1 a usage or network failure, a server error, or HTTP 408 or 429 (try again); 2 the server refused, including an expired session (run terminalhire link).

Surfaces — see Claude Code surfaces

Command What it does
spinner --show / --off / --mode append|replace Toggle and configure the spinner
statusline --on / --off Toggle the status line — unread chats, intros, approved claims, poster notes, paid tasks, updates
mcp / mcp --print-config Run the MCP server; print host config — see MCP
th mcp-chat Opt-in reply server: lets a host answer a connection thread, off unless you start it
config --nudge session|always|every:N Tune nudge frequency
config --connect on|off Opt in/out of ambient peer/poster surfacing (default off)
config set mix jobs|balanced|credential / config get mix Balance roles vs. contribution items on the surface (default balanced; env TH_MIX overrides)

Spinner tips lead with claimable first-party paid tasks when available. Otherwise they favor roughly ten contribution opportunities per role; relevant public paid tasks follow the role.

Pointing the CLI somewhere other than terminalhire.com

Every command talks to https://terminalhire.com unless you say otherwise. If you are working against the dev deployment, or against a copy of the web app running on your own machine, a bare command asks production instead — and production answers about a claim it has never heard of, which reads as "this work does not exist".

Variable What it does
TERMINALHIRE_API_URL The deployment to talk to. Allowlisted: the apex, dev.terminalhire.com, loopback
TERMINALHIRE_ALLOW_LOCAL_API Set to 1 before any http://localhost:<port> base is accepted
TERMINALHIRE_ALLOW_LOCAL_OAUTH Set to 1 as well before login will open a browser sign-in against loopback
TERMINALHIRE_DIR Where sessions and claims live. Unset: ~/.terminalhire, or ~/.terminalhire-dev when pointed at dev

For the dev deployment, use thdev instead — it is the same CLI with that origin pinned, so it needs no variable at all and works the same in PowerShell, cmd.exe and bash. VAR=value command is a POSIX-shell one-command assignment: on Windows it does not set anything, and the command silently reaches production.

# the dev deployment — every command, no variable
thdev claim slice <id>
thdev bounties
thdev login

# a web app running on your own machine, on whatever port it serves
TERMINALHIRE_ALLOW_LOCAL_API=1 TERMINALHIRE_API_URL=http://localhost:3000 \
  terminalhire claim slice <id>

The dev deployment has its own credential store, ~/.terminalhire-dev, and every way of pointing at dev uses it: thdev, the variable, an MCP server entry that sets the variable. So thdev login and terminalhire login are separate accounts on disk, neither overwrites the other, and a sign-in through one spelling is visible to the others. Set TERMINALHIRE_DIR to choose a store yourself; it wins over that default. Loopback keeps ~/.terminalhire and still uses the variables above: there is no pinned binary for an arbitrary local port.

Any other host is refused outright rather than quietly falling back to production — writing a claim to the wrong environment is not something a later command can undo. The two loopback flags are separate because they grant different things: the first lets stored credentials reach a local listener, the second lets a browser sign-in happen there, and neither is implied by the other.

Your session belongs to one deployment. Signing in on production does not sign you in on dev, so point login at the same place before anything that says a claim is not registered to you:

thdev login

The claim page prints the right form into its own copy buttons — thdev … on dev, a prefixed command on loopback — so a command copied from a page is already pointed at the deployment that served it.

Not opening a browser

login, sync --push and the claim sign-in steps print the address, then try to open it in your browser. Set TERMINALHIRE_NO_BROWSER=1 to skip that on a headless machine and open the printed address yourself.