Command reference
roost is a single roost binary with subcommands. Every global flag and every stay flag can
appear anywhere on the command line, before or after the subcommand and its arguments —
global and stay flags are attached to each command directly, and their values are merged
across the context chain. roost search kyoto --json and roost --json search kyoto behave
identically.
Global flags
Section titled “Global flags”These attach to every command, including roost itself. They implement the agent-CLI
contract shared across a fleet of tools. Several are inert no-ops in roost — present so
the flag surface stays identical across tools that do mutate, even though roost never does.
| Flag | Type | Default | Behavior |
|---|---|---|---|
--format | json | plain | tsv | plain | Output format. |
--json | flag | off | Shorthand for --format=json. |
--no-color | flag | off | Disable colored output (color is only ever applied to plain output on a TTY anyway). |
--allow-mutations | flag | off | Inert no-op. roost is read-only and never books; present for contract uniformity. |
--dry-run | flag | off | Print the request plan without fetching. Only dates gives this real meaning (it prints the sweep plan and exits 0 without an upstream call); on other commands it has no effect. |
--yes | flag | off | Inert no-op. Nothing in roost ever asks for confirmation. |
--force | flag | off | Inert no-op. There are no safety checks to bypass. |
--no-input | flag | off | Never prompt; fail with exit 13 (INPUT_REQUIRED) instead. |
--limit | int | 50 | Max items returned for list-shaped output. |
--select | string | none | Comma-separated dot-path field projection. |
--concise | flag | on (default) | Inert no-op. Accepted for contract uniformity; roost’s output shape doesn’t currently change based on this flag. |
--detailed | flag | off | Inert no-op. Accepted for contract uniformity; roost’s output shape doesn’t currently change based on this flag. |
Shared stay flags
Section titled “Shared stay flags”search, rates, and dates all take the same stay flags — the shape of the trip you’re
asking about. Like the global flags, these can appear anywhere on the command line.
| Flag | Type | Default | Behavior |
|---|---|---|---|
--backend | google | serpapi | google | Data backend. google is keyless; serpapi needs an API key. Overridable with ROOST_BACKEND. |
--check-in | YYYY-MM-DD | today | Check-in date. |
--check-out | YYYY-MM-DD | --check-in + --nights | Check-out date. If both --check-out and --nights are given, --check-out wins. |
--nights | int | 2 | Length of stay when --check-out is omitted. Must be at least 1. |
--adults | int | 2 | Adult guests. Must be at least 1. |
--children | int | 0 | Child guests. |
--currency | string | USD | ISO currency code for rates. |
--brand | string | none | Comma-separated brand/chain ids — see roost brands. |
--wait | flag | off | Sleep through the politeness throttle instead of failing fast with exit 7. |
--max-wait | float | 30.0 | Cap, in seconds, on how long --wait will sleep. |
--no-throttle | flag | off | Skip the local politeness throttle entirely. Use sparingly — it exists to keep roost welcome upstream. |
--no-wrap-untrusted | flag | off | Disable untrusted-text fencing (on by default — see Politeness). |
roost search
Section titled “roost search”roost search LOCATION [OPTIONS]Search available properties for a location and stay. The core command: what is available in
LOCATION for these dates, and at what rate.
Argument
| Name | Required | Description |
|---|---|---|
LOCATION | yes | Free-text place to search, e.g. "kyoto" or "chicago". |
Options (in addition to the global flags and stay flags)
| Flag | Type | Default | Behavior |
|---|---|---|---|
--class | int | none | Minimum hotel class in stars (2-5). |
--min-rating | float | none | Minimum guest rating. |
--max-price | float | none | Maximum nightly rate. |
--free-cancellation | flag | off | Only properties offering free cancellation. Rejected on the google backend with exit 2 / UNSUPPORTED_ON_BACKEND — Google’s result cards carry no cancellation signal, so honoring it would return an empty set that reads as “none offer it”. Use --backend serpapi. |
--amenity | string | none | Comma-separated amenities that must ALL be present (substring, case-insensitive match). |
--property-type | string | none | Comma-separated property-type filters. |
--sort | relevance | price | rating | reviews | relevance | Result ordering. |
On the google backend, --class, --min-rating, --max-price, --free-cancellation,
--amenity, and --property-type are applied client-side over the page of results Google
returned — they narrow, they don’t search deeper. --brand is not applied server-side on
google at all (Google’s brand filter ids are only exposed via serpapi); results still span
all chains. Either case sets scope.partial: true and adds an explanatory note. See
Comparing rates across backends.
roost search "chicago" --backend serpapi --brand hyatt,marriott --free-cancellation --jsonExit codes: 0 ok, 2 usage (bad date/nights), 3 empty results, 4 auth required (serpapi
backend, no key), 5 not found (LOCATION couldn’t be resolved to a place), 7 rate limited,
8 retryable, 20 blocked, 21 schema drift.
roost rates
Section titled “roost rates”roost rates PROPERTY_ID --location LOCATION [OPTIONS]Per-source rate breakdown for one property — brand-direct vs each OTA.
Argument
| Name | Required | Description |
|---|---|---|
PROPERTY_ID | yes | The property to price, as returned by roost search. |
Options
| Flag | Type | Default | Behavior |
|---|---|---|---|
--location | string | — | Required. The location to look the property up in — rates are date + place scoped. |
Plus the global flags and stay flags.
Depth differs by backend. serpapi exposes the full per-source list. google reports what
the search surface carries — its property page is client-rendered and its rate matrix comes
over an RPC roost declines to call — and sets scope.partial: true with a note pointing at
--backend serpapi. It never silently returns less without saying so.
roost rates "Sakura Cross Hotel" --location kyoto --nights 3 --jsonExit codes: 0 ok, 2 usage, 3 empty results (the location matched no properties at all for
the stay), 4 auth required, 5 not found (unknown PROPERTY_ID, or --location couldn’t be
resolved to a place), 7 rate limited, 8 retryable, 20 blocked, 21 schema drift.
roost dates
Section titled “roost dates”roost dates LOCATION [OPTIONS]Sweep check-in dates to find when a stay is available and cheapest. The only command that
issues more than one upstream request per invocation — so it is hard-capped by
--max-requests and always reports requestsIssued.
Argument
| Name | Required | Description |
|---|---|---|
LOCATION | yes | Free-text place to search. |
Options
| Flag | Type | Default | Behavior |
|---|---|---|---|
--window | int | 30 | How many days forward to sweep. Must be at least 1. |
--step | int | 1 | Days between sampled check-in dates. Must be at least 1. |
--max-requests | int | 8 | Hard cap on upstream requests. Must be at least 1. |
Plus the global flags and stay flags.
--dry-run prints the request plan (window, step, maxRequests, requestsPlanned,
sampledDates) and exits 0 without fetching anything — use it to see the cost of a sweep
before spending requests.
roost dates "lisbon" --window 30 --nights 2 --dry-runWhen not dry-run, the response envelope’s dates key carries one entry per sampled date, plus
cheapest (the lowest-priced available date, or null), requestsIssued, and truncated
(true when --max-requests capped the sweep short of the full window). See
Finding cheap dates.
Exit codes: 0 ok, 2 usage (bad --window/--step/--max-requests, or bad date/nights),
4 auth required, 5 not found (LOCATION couldn’t be resolved to a place — this fails the whole
sweep, since every sampled date depends on the same place lookup), 7 rate limited (only if the
very first sampled date hits it — after that, a mid-sweep block or rate limit is instead
recorded as a row with available: null and the sweep stops there, see
output schema), 8 retryable, 20 blocked (same first-date caveat as
7), 21 schema drift. Unlike the other two commands, dates never itself exits 3 — a date with
no availability is reported as a row with available: false, not a command failure.
roost brands
Section titled “roost brands”roost brands [OPTIONS]List the hotel chain/brand filter ids usable with --brand. On the default google backend
this is bundled data — the command makes no network request. brands has no --backend flag
of its own, but it still honors the ROOST_BACKEND environment variable; setting
ROOST_BACKEND=serpapi makes it fetch the live brand taxonomy from SerpApi instead, which
needs a key configured (see Configuration).
Options
| Flag | Type | Default | Behavior |
|---|---|---|---|
--search | string | none | Filter brands by name substring. |
Plus the global flags.
On the google backend, listed brands carry id: null — Google’s brand filter ids are only
exposed via serpapi, so google returns a curated chain list for reference, not filter ids
that take effect server-side.
roost brands --search hyattExit codes: 0 ok (the default google backend). On the google backend this command is
offline and takes no stay flags, so none of the network/auth/usage failure modes apply; via
ROOST_BACKEND=serpapi it can also exit 4 (auth required, no key), 7/8 (rate limited/retryable
from SerpApi), same as the other commands on that backend.
roost doctor
Section titled “roost doctor”roost doctor [OPTIONS]Diagnose setup — backend reachability, throttle and circuit-breaker state, and auth/keyring
status. Emits {"ok": ..., "checks": [...]}, one entry per check (name, ok, detail).
Plus the global flags.
Exit codes: 0 all checks passed, 10 config error (one or more checks failed — see the
failing check’s detail). If ROOST_BACKEND=serpapi is set with no key configured, doctor
exits 4 (auth required) immediately instead of running its checks — the same backend
resolution every command uses (see Configuration) applies before
doctor’s own diagnostics get a chance to report the missing key as just another check.
roost auth
Section titled “roost auth”roost auth login --token-stdinroost auth statusroost auth logoutManage the optional SerpApi backend key. The default google backend needs none of this.
roost auth login
Section titled “roost auth login”| Flag | Type | Default | Behavior |
|---|---|---|---|
--token-stdin | flag | off | Read the API key from stdin — the only supported path. |
The key is read from stdin, never from argv, so it never leaks into ps, /proc, shell
history, or an agent’s own command log. Omitting --token-stdin fails with exit 13
(INPUT_REQUIRED); so does an empty or all-whitespace stdin read, or a TTY stdin combined
with --no-input.
echo -n "$SERPAPI_KEY" | roost auth login --token-stdinThere is no auth refresh — the key is static, not a token that expires.
Exit codes: 0 ok, 13 input required.
roost auth status
Section titled “roost auth status”Show authentication status. The key itself is redacted in the output.
Exit codes: 0 ok.
roost auth logout
Section titled “roost auth logout”Remove the stored SerpApi key. Idempotent — succeeds whether or not a key was present, and
reports existed: true|false. Removes the local copy only; it does not revoke the key
upstream.
Exit codes: 0 ok.
roost schema
Section titled “roost schema”roost schema [OPTIONS]Print the machine-readable command schema as JSON: tool, version, schemaVersion, a
conformance block (spec name, version, level), the full commands tree, the exit_codes
table, a safety block, a boundary block (allowed_paths, disallowed_paths, evasion
policy), and the current throttle snapshot for the active backend.
Plus the global flags.
Exit codes: 0 ok.
roost agent
Section titled “roost agent”roost agent [OPTIONS]Print the bundled agent SKILL.md to stdout — the reference doc written for an LLM agent
driving roost.
Plus the global flags.
Exit codes: 0 ok.
roost version
Section titled “roost version”roost version [--check] [OPTIONS]Print the installed version, or with --check, also report whether a newer release exists.
| Flag | Type | Default | Behavior |
|---|---|---|---|
--check | flag | off | Check for a newer release: network call, short timeout, fail-silent. |
Plus the global flags.
--check never mutates anything — it only reports the upgrade command (uv tool install --upgrade roost, or an override via ROOST_RELEASES_URL) for a human or package manager to
run; roost never self-updates. If the version check can’t reach a release source, latest is
null and a note explains it — this never fails the command.
Exit codes: 0 ok (always, --check failures degrade gracefully rather than erroring).
Full exit code table
Section titled “Full exit code table”| Code | Name | Meaning |
|---|---|---|
| 0 | ok | Success. |
| 1 | generic_error | Unclassified failure. |
| 2 | usage | Bad arguments/flags (e.g. malformed date, --nights < 1). |
| 3 | empty_results | The request succeeded but matched nothing. |
| 4 | auth_required | The serpapi backend is selected (via --backend or ROOST_BACKEND) with no key configured — including on commands with no --backend flag of their own, since they still honor ROOST_BACKEND. |
| 5 | not_found | A specific lookup failed — a LOCATION couldn’t be resolved to a place, or a rates PROPERTY_ID doesn’t exist. |
| 6 | permission | Permission denied. |
| 7 | rate_limited | Local politeness throttle — wait or pass --wait. |
| 8 | retryable | Transient upstream failure; retrying may help. |
| 10 | config_error | Setup problem, e.g. roost doctor found a failing check. |
| 12 | mutation_blocked | Defined but unreachable — roost has no mutating operations to block. |
| 13 | input_required | A prompt was needed but input is disabled or stdin was empty. |
| 20 | blocked | Upstream served a challenge or soft block. Stop; do not retry immediately. |
| 21 | schema_drift | Upstream response no longer parses as expected. Retrying never helps — the tool needs an update. |
| 130 | cancelled | User interrupted (Ctrl-C). |