Commands
All commands follow the pattern wh <domain> <verb> [args] [--flags]. Global flags --repo, --json, --live, --format, and --profile (-P) are available on most commands. Pass --no-update-check on any invocation to skip the CLI update notice for that run.
--format controls output mode: pretty (default), json, or jsonl. When --live is active, json and jsonl emit structured output per update; pretty (or unset) renders a human-readable display.
use — Local repo context
Section titled “use — Local repo context”| Command | Description |
|---|---|
wh use <org/repo> | Set the default repo for the current directory |
wh use | Show the active repo context and where it comes from |
wh use --clear | Remove the .wh context file |
Writes a .wh JSON file in the current directory. The CLI resolves the target repo using this priority: --repo flag > WARMHUB_REPO env > .wh file. The repo is validated against the backend before writing.
wh use myorg/world # writes .wh, validates repo existswh use # shows active context with provenancewh use --clear # removes .whwh thing list # uses .wh repo (no --repo needed)org — Organization management
Section titled “org — Organization management”| Command | Description |
|---|---|
wh org create <name> [--display-name "..."] [--description "..."] | Create a new organization. Description is trimmed; max 2000 characters. |
wh org view <name> | View organization details |
wh org list [--include-archived] | List organizations (archived hidden by default) |
wh org rename <oldName> <newName> | Rename an organization |
wh org member add <org> <email> [--role role] | Add a member or send an invite. Only owners can assign owner role. If the email is not a WarmHub user, a pending invite is created and an invite email is attempted (best-effort, async). |
wh org member remove <org> <email> | Remove a member or revoke a pending invite |
wh org member list <org> [--pending] | List members of an organization. Requires authentication and org membership. --pending filters to pending invites only. |
wh org member set-role <org> <email> --role <role> | Change a member’s role. Only owners can promote to or demote from owner. Cannot demote the last owner. |
wh org archive <name> [--yes] | Archive an organization (blocks new repos and members) |
wh org update <name> --description "..." | Update org description. Trimmed; empty strings clear the value; max 2000 characters. |
wh org unarchive <name> | Unarchive an organization |
Aliases: info → view; get → view
Roles: owner (full control), admin (manage members and repos), editor (read/write data), viewer (read-only). Default: editor.
Reserved org names and personal orgs
Section titled “Reserved org names and personal orgs”wh org create and wh org rename both reject these reserved slugs (case-insensitive): admin, api, billing, blog, docs, help, login, public, settings, signup, status, support, system, warmhub, www.
When a user signs in for the first time, WarmHub provisions a personal org for them. GitHub-linked personal orgs use a login-based slug when available, and collisions receive a suffix. GitHub-linked personal orgs can’t be renamed — wh org rename returns FORBIDDEN. Personal orgs without a linked GitHub login can be renamed normally.
wh org create acme --display-name "Acme Corp"wh org listwh org member add acme alice@example.com --role editorwh org member list acmewh org member remove acme alice@example.comrepo — Repository management
Section titled “repo — Repository management”| Command | Description |
|---|---|
wh repo create <org/name> [--description "..."] [--visibility <public|private>] | Create a repo (auto-creates org if needed). Description is trimmed; max 2000 characters. |
wh repo list [org] [--include-archived] | List repos in an org (archived hidden by default) |
wh repo view [org/repo] | Show repo details |
wh repo describe [org/repo] | Describe repo schema: shapes, fields, inline descriptions, and per-shape counts. Shapes from installed component manifests appear with count 0 when no things have been written yet. |
wh repo rename <org/oldName> <newName> | Rename a repo |
wh repo archive <org/repo> [--yes] | Archive a repo (blocks new commits) |
wh repo update <org/repo> --description "..." | Update repo description. Trimmed; empty strings clear the value; max 2000 characters. |
wh repo visibility <org/repo> <public|private> | Set repo visibility |
wh repo unarchive <org/repo> | Unarchive a repo |
wh repo delete <org/repo> [--hard] [--yes] | Delete a repo. Soft by default (hidden immediately; purged after 30-day grace window). --hard runs the purge cascade immediately (owner-only, irreversible). Blocks if another repo holds inbound cross-repo references or subscriptions. |
Aliases: init → create; info → view; get → view
wh repo create also accepts --org <org> as a tolerance fallback — wh repo create <name> --org <org> is combined into <org>/<name> and the CLI prints a one-line hint pointing at the canonical positional form. Prefer <org>/<name> in scripts.
wh repo create myorg/world -d "Game world"wh repo create myorg/world -d "Game world" --visibility publicwh repo list myorgrepo content — README, AGENTS.md, llms.txt
Section titled “repo content — README, AGENTS.md, llms.txt”WarmHub stores per-repo markdown content as built-in Content instances. Two are stored and editable: Content/Readme (human-facing) and Content/Agents (agent-facing). A third, Content/LlmsTxt, is synthesized per request from the repo’s shapes and content and is read-only.
| Command | Description |
|---|---|
wh repo content get <org/repo> --kind readme|agents|llms-txt | Fetch repo Content markdown by kind. |
wh repo content set <org/repo> --kind readme|agents|llms-txt [--content "..." | --file <path>] | Set Content markdown (file, inline, or piped stdin). llms-txt is read-only — set attempts are rejected. |
wh repo content generate <org/repo> --kind readme|agents|llms-txt [--save] | AI-generate Content from the repo’s shapes; --save commits it. llms-txt is read-only — generate attempts are rejected. |
set reads from --file <path>, --content "...", or stdin when neither flag is passed. Use --file - to force stdin.
wh repo content get myorg/world --kind readmewh repo content set myorg/world --kind readme --file README.mdcat README.md | wh repo content set myorg/world --kind readme
wh repo content set myorg/world --kind agents --content "# Agents"wh repo content generate myorg/world --kind agents --save
wh repo content get myorg/world --kind llms-txtshape — Shape management
Section titled “shape — Shape management”| Command | Description |
|---|---|
wh shape list [--match "glob"] [--include-retracted] [--component id] [--exclude-components] | List shapes. Use --include-retracted to show retracted shapes and --exclude-components to hide component-owned shapes. |
wh shape view <name> [--include-retracted] | Show shape details with field definitions |
wh shape create <name> --fields '<json>' [--description '...'] | Create a new shape |
wh shape revise <name> --fields '<json>' [--description '...'] | Revise shape fields (creates new version) |
wh shape retract <name> [--reason '<text>'] [-m msg] [--committer <wref>] | Retract a shape, marking it inactive |
wh shape history <name> [--include-retracted] [--limit n] [--cursor c] [--all] | Show shape version history |
wh shape rename <oldName> <newName> | Rename a shape |
Aliases: head → list; get → view; update → revise
Teaching rejection: wh shape retract <name> exits with a hint to use wh shape retract <name>.
wh shape create Location --fields '{"x":"number","y":"number"}'wh shape listwh shape view Locationwh shape revise Location --fields '{"x":"number","y":"number","label":"string"}'wh shape retract OldShape -m "Withdraw old shape"thing — Thing operations
Section titled “thing — Thing operations”| Command | Description |
|---|---|
wh thing list [--shape s] [--kind k] [--limit n] [--cursor c] [--all] [--match glob] [--include-retracted] [--count] [--component id] [--exclude-components] | Show current HEAD state. Use --count to return only the count of matching items. System-managed component infrastructure records are hidden by default. Use --exclude-components to also hide component-owned data things. |
wh thing create <name|Shape/name> [--shape s] --data '<json>' [-m msg] [--committer <wref>] | Create a thing. Pass either a qualified Shape/name or an unqualified name with --shape, not both. |
wh thing view [<wref>...] [--file <path>] [--version n] [--depth n] [--include-retracted] | Show a thing’s details. Variadic: a single wref routes to the single-thing path; multiple wrefs or --file routes to batch fetch (max 500). --depth returns an embedded graph view and cannot be combined with --live. Pretty output includes a by: row with the committer wref when the version was written with --committer. Batch returns { requested, items, missing } in --json; --format jsonl emits one record per deduped requested wref. |
wh thing history [wref] [--shape] [--about] [--resolve-collections] [--limit] [--cursor] [--all] [--include-retracted] | Show version history. Each version line ends with by <committerWref> when attribution is recorded; the field is also exposed on --json output as versions[].committerWref. |
wh thing resolve <wref> | Resolve wref to thing identity |
wh thing lease <wref> [--ttl <ms>] | Acquire a short read lease on a thing and read it in one step. Prints the lease id and expiry. --ttl defaults to 5000 (min 1000, capped at a maximum); a value outside that range is rejected, never clamped. |
wh thing revise <name> --data '<json>' [-m msg] [--committer <wref>] [--expected-version <n>] [--lease-id <id>] | Revise a thing. --committer is optional; omit to attribute the commit to the authenticated user. Pass --expected-version to apply the revise only if the thing is still at that version (optimistic concurrency). Pass --lease-id to write under a read lease; the lease auto-releases on a successful or no-op write. |
wh thing retract <wref> [--kind thing|assertion|shape|collection] [--reason '<text>'] [-m msg] [--committer <wref>] [--lease-id <id>] | Retract an entity (thing, assertion, shape, or collection), marking it inactive. --kind is an optional safety check that errors if the resolved entity’s kind doesn’t match. --committer is optional; omit to attribute the commit to the authenticated user. --lease-id writes under a read lease. |
wh thing release-lease <wref> --lease-id <id> | Release a read lease early when you decide not to write. Idempotent — a non-matching or already-released lease is not an error. |
wh thing query [--shape s] [--kind k] [--about wref] [--resolve-collections] [--limit n] [--cursor c] [--all] [--match glob] [--include-retracted] [--count] [--component id] [--exclude-components] | Query by filters. Use --count to return only the count. When no --shape is given, system-managed component infrastructure records are hidden by default. |
wh thing search <query> [--shape s] [--kind k] [--about wref] [--mode text|vector|hybrid] [--resolve-collections] [--include-retracted] [--limit n] [--cursor c] [--all] [--component id] [--exclude-components] | Search by text content. --cursor and --all only apply in text mode. --component and --exclude-components only apply in text mode. In text mode, system-managed component infrastructure records are hidden by default when no --shape is given. --resolve-collections also only applies in text mode. |
wh thing refs <wref> [--inbound|--outbound] [--field path] [--limit n --cursor c] [--all] | Show things that store this wref in a field (inbound, default) or wrefs stored in this thing’s fields (outbound). Use wh thing about when you want assertions whose about target is this thing. --field filters inbound refs by field path. --cursor requires --limit (cursors are scoped to a specific page size). |
wh thing about <wref> [--shape s] [--match glob] [--depth n] [--resolve-collections] [--limit n --cursor c] [--all] [--include-retracted] | Show assertions whose about target is this thing. Use --shape or --match to narrow the assertions, --depth to include child assertions about the returned assertions, and --resolve-collections to include assertions about collections containing the target. --cursor requires --limit (cursors are scoped to a specific page size). |
wh thing rename <Shape/oldName> <newName> | Rename a thing |
Aliases: head → list; status → list; show → view; get → view
wh thing list --shape Locationwh thing view Location/cave --version 3wh thing view Location/cave --depth 2wh thing query --kind assertion --about Location/cavewh thing search "safe location" --shape Belief --mode hybridwh thing history Location/cave --limit 10wh thing refs Location/cave # inbound refs (default)wh thing refs Location/cave --field target # filter by field pathwh thing refs Observation/obs1 --outbound # outbound refswh thing about Location/cave # assertions about this thingwh thing about Location/cave --shape Belief # filter assertions by shapewh thing about Location/cave --depth 2 # include child assertionswh thing list --count # count all active itemswh thing list --shape Location --count # count by shapewh thing query --about Location/cave --count # count matching query results
# wh thing view is variadic — batch fetch up to 500 wrefs in one callwh thing view Player/alice Player/bob Player/cara # variadic positionalscat wrefs.txt | wh thing view # piped stdin (one wref per line)wh thing view --file wrefs.txt --version 3 # pin all to @v3 (implies --include-retracted)wh thing view Player/alice@v1 Player/bob@v2 # per-wref pinningwh thing view --file=- < wrefs.txt --format jsonl # `--file=-` reads stdin (use the `=`; `--file -` errors with `Flag --file requires a value`)Batch wh thing view (multiple wrefs or --file) supports three output modes:
-
Pretty (default):
Requested N, found N, missing Nheader, then one line per resolved item with kind label and[RETRACTED]/[draft]markers, then aMissing:block listing wrefs that didn’t resolve. -
--json(or--format json): a single{ requested, items, missing }object —items[]carries the same fields as single-thing view,missing[]isstring[]of unresolved wrefs (qualified when applicable). -
--format jsonl: one JSON record per deduped requested wref, in input order. Inputs from positionals +--file+ stdin are unioned and deduped before the round-trip, so a wref supplied twice produces one row. Each row is{requested, found, wref, ...}. The fields below distinguish two wref shapes — the local form (Shape/name, repo-relative) and the canonical form (wh:org/repo/Shape/name, fully qualified for cross-repo reads):requested— the original input string verbatim, so canonical inputs likewh:org/repo/Loc/caveare still identifiable downstream after the backend normalizes them to local form.wref— the local form on hits (e.g.Loc/caveeven when the input was canonical), or the version-qualified form fromresult.missingon misses.found— boolean.
Correlate input lines to result lines by
requestedand filter byfoundwithout reconstructing the envelope.
Passing --version automatically implies --include-retracted, so retract versions can be retrieved by their pinned id.
--live is rejected in batch mode — batch reads are one-shot. Use wh thing view <wref> --live for per-thing polling.
Thing read leases
Section titled “Thing read leases”A read lease gives you a short, exclusive window on a single thing for a read-modify-write cycle. While you hold the lease, another caller’s revise/retract of that thing fails fast with a recoverable LEASE_UNAVAILABLE error. You learn about the conflict before computing a new value, not after the write. Leasing is opt-in: plain wh thing view reads are unaffected, and an expired or released lease falls back to a normal optimistic write.
# 1. Acquire + read in one step (prints lease id and expiry).wh thing lease Player/alice --ttl 5000
# 2. Compute the new value, then write it back under the lease.# The lease auto-releases on a successful or no-op write.wh thing revise Player/alice --data '{"score":2}' --lease-id <id>
# Or hand the lease back early if you decide not to write.wh thing release-lease Player/alice --lease-id <id>Notes:
wh thing leaseis the acquire verb; the plain read verb stayswh thing view. Acquiring requires write access. If another caller already holds an active lease, it fails fast withLEASE_UNAVAILABLEinstead of waiting.- TTL defaults to 5s and has a fixed 1s floor and an upper bound. An out-of-range
--ttlis rejected, never silently clamped. - On
wh commit submit,--lease-idbinds to a single--revise/--retractshort-form op and is broadcast to every op of a multi-target--retract. For--ops/--file/--streamwrites, carryleaseIdinline on the operation instead. - A
LEASE_UNAVAILABLEerror tells you when the current holder’s lease expires, so you can retry after that time.
thing graph — Embedded graph reads
Section titled “thing graph — Embedded graph reads”wh thing graph is a sub-command of the thing domain. It returns a thing with readable about assertions and wref fields embedded as objects. Depth defaults to 2 and is capped at 5. Only data the caller can read is resolved — inaccessible refs stay as string wrefs and the output does not expose internal IDs or denial reasons.
wh thing graph Location/cavewh thing graph Location/cave --depth 3wh thing graph Location/cave --depth 2 --jsonassertion — Assertion operations
Section titled “assertion — Assertion operations”| Command | Description |
|---|---|
wh assertion list [--about wref] [--shape s] [--depth n] [--limit n] [--cursor c] [--all] [--match glob] [--resolve-collections] [--include-retracted] [--count] | Browse assertions in HEAD, optionally scoped to assertions about a thing. Use --count to return only the count. |
wh assertion view <wref> [--version n] [--depth n] [--include-retracted] | Show an assertion’s details (equivalent to wh thing view — assertions are things). --depth returns an embedded graph view instead of the flat assertion record and cannot be combined with global --live. |
wh assertion create --shape <s> --about <wref|type:members> [--name n] --data '<json>' [-m msg] [--committer <wref>] | Create an assertion. --about accepts either a single wref or a collection (pair:a,b, triple:a,b,c, set:a,b,..., list:a,b,...). |
wh assertion revise <wref> --data '<json>' [-m msg] [--committer <wref>] | Revise an assertion |
wh assertion retract <wref> [--reason '<text>'] [-m msg] [--committer <wref>] | Retract an assertion, marking it inactive |
wh assertion history <wref> [--include-retracted] [--limit n] [--cursor c] [--all] | Show assertion version history |
Aliases: head → list; about → list --about; assert → create; show → view; get → view
Prefer --about for target-scoped assertion lists. A bare target after list is accepted as an agent convenience when translating user phrasing like “assertions about X”.
wh thing view <Assertion/name> also works since assertions are things.
wh assertion create --shape Belief --about Location/cave --data '{"safe":true}'wh assertion create --shape Pairing --about pair:Location/cave,Location/lake --data '{"connected":true}'wh assertion list --about Location/cave --shape Beliefwh assertion view Belief/cave-safe --depth 2wh assertion revise Belief/cave-safe --data '{"safe":true,"confidence":0.9}'wh thing view Belief/cave-safewh assertion list --shape Beliefwh assertion list --countwh assertion history Belief/cave-safe --include-retractedcommit — Write operations
Section titled “commit — Write operations”| Command | Description |
|---|---|
wh commit submit [--ops json] [-f file] [--stream] [--skip-existing] [--add name] [--revise wref] [--retract wref] [--reason text] [flags] | Submit a stream of operations. Bare wh commit is equivalent. |
wh commit submit is the write entrypoint. Use wh thing history <wref> for
the per-thing version trail.
See Commit Submit Deep-Dive for the full breakdown of commit submit flags, including JSONL streaming with --stream, --file *.jsonl, --progress, --chunk-size, and --skip-existing.
wh commit submit --add cave --shape Location --data '{"x":3,"y":7}' -m "Add cave"wh commit submit --retract Location/old-cave --reason "duplicate" -m "Retract duplicate cave"wh commit submit --add alice --data '{"score":1}' --add bob --data '{"score":2}' --shape Player -m "seed players"wh commit submit --file dataset.jsonl --progress -m "Bulk import"wh commit submit --file dataset.jsonl --skip-existing -m "Resume seed"cat ops.jsonl | wh commit submit --stream -m "Pipe stream"--add is repeatable up to 20 operations; pair each with its own --data. See Commit Submit Deep-Dive for cardinality rules.
shape template — Scaffold write operations
Section titled “shape template — Scaffold write operations”wh shape template is a sub-command of the shape domain. It generates sample operations from shape definitions for use with wh commit submit.
| Command | Description |
|---|---|
wh shape template <shape> [shape2 ...] [--operation add|revise|retract] [--kind thing|assertion|shape|collection] [--about wref] [--count n] [--output file] | Generate sample operations from shapes. --kind shape and --kind collection are only valid with --operation retract; add/revise accept thing or assertion. |
wh shape template Hypothesiswh shape template Hypothesis --operation retractwh shape template Hypothesis Evidence --count 3 -o experiment.jsonwh shape template Hypothesis --kind assertion --about ResearchTopic/exampleinit — Bootstrap local harness
Section titled “init — Bootstrap local harness”| Command | Description |
|---|---|
wh init [org/repo] [-d "description"] | Install harness hooks and optionally onboard a repo |
Aliases: init passes unknown verbs as positional args, so wh init myorg/myrepo works directly.
wh initwh init myorg/myrepowh init myorg/myrepo --description "Demo repo"doctor — Diagnostics and health checks
Section titled “doctor — Diagnostics and health checks”| Command | Description |
|---|---|
wh doctor [--fix] | Run environment and backend health checks |
Checks API URL, default repo, backend connectivity, authenticated identity (email plus server-validated scopes and token name for PATs), Claude hooks, and AGENTS.md. If the backend doesn’t recognize your token — for example, when it was issued for a different API URL — the auth check warns, shows the API URL it tried, and suggests running wh auth login. The auth check is skipped when the backend is unreachable. Use --fix to auto-install missing hooks and stanzas.
wh doctorwh doctor --fixsub — Subscription management
Section titled “sub — Subscription management”See Subscriptions for concepts (kinds, filters, retry behavior) and Creating Subscriptions for detailed setup guides.
| Command | Description |
|---|---|
wh sub create <name> --kind webhook --on <shape> --filter '<json>' --webhook-url <url> [flags] | Create an event-driven webhook subscription bound to a target shape |
wh sub create <name> --kind webhook --filter '{"kind":"shape", ...}' --webhook-url <url> [flags] | Create a shape lifecycle subscription — --on is omitted because the filter scopes matching to shape ops |
wh sub create <name> --kind cron --cronspec '<expr>' --webhook-url <url> [flags] | Create a cron subscription |
wh sub update <name> [flags] | Update an existing subscription |
wh sub view <name> | View subscription details |
wh sub list [--limit n] | List all subscriptions |
wh sub log <name> [--limit n] | Tail subscription delivery feed (shows run status and errors) |
wh sub attempts <runId> | View per-attempt detail for a specific run |
wh sub pause <name> | Pause a subscription |
wh sub resume <name> | Resume a paused subscription |
wh sub delete <name> | Delete a subscription |
wh sub bind <name> --credentials <credentialSet> | Bind a credential set for webhook auth |
wh sub unbind <name> | Remove credential binding |
Aliases: get → view
Delivery / fallback flags
| Flag | Description |
|---|---|
--on | Target shape — required for most webhook subscriptions; omit for shape lifecycle subscriptions whose filter is {"kind":"shape", ...} |
--filter | JSON filter expression |
--webhook-url | Destination URL for webhook deliveries and cron subscriptions |
--fallback-webhook-url | Optional fallback URL called after a terminal delivery failure |
--clear-fallback-webhook-url | Remove the configured fallback URL on wh sub update for any subscription kind |
Webhook flags
| Flag | Description |
|---|---|
--on | Target shape — required for most webhook subscriptions; omit for shape lifecycle subscriptions whose filter is {"kind":"shape", ...} |
--filter | JSON filter expression |
--allow-trace-reentry | Allow same-chain reentry (write-triggered only) |
Cron flags
| Flag | Description |
|---|---|
--cronspec | Cron schedule expression |
--timezone | IANA timezone for the schedule |
Shared flags (webhook only)
| Flag | Description |
|---|---|
--source | Watch a different repo in the same org; accepts repoName or orgName/repoName. Not supported for cron subscriptions. |
--source pins the subscription to watch a different repo in the same organization. When set, the subscription lives in the repo specified by --repo but fires on writes to the source repo. The action runs in the context of the subscription’s home repo. Not supported for cron subscriptions.
--allow-trace-reentry defaults to false. When omitted, a write-triggered subscription runs at most once in the same causal chain for the same shape. When present, same-chain reentry is allowed, but a global chain-depth safety limit still stops runaway recursion. Cron subscriptions ignore this flag.
# Create a webhook subscriptionwh sub create signal-hook --on Signal --kind webhook \ --filter '{"shape":"Signal"}' --webhook-url https://example.com/hook \ --fallback-webhook-url https://example.com/fallback
# Create a shape lifecycle subscription (no --on; filter scopes to shape ops)wh sub create shape-changes --kind webhook \ --filter '{"kind":"shape"}' --webhook-url https://hooks.example.com/shapes
# Create a cross-repo subscription (watch signals in another repo)wh sub create cross-hook --on Signal --kind webhook \ --filter '{"shape":"Signal"}' --source myorg/other-repo \ --webhook-url https://example.com/hook
# Allow same-trace reentry for a self-chaining webhook subscriptionwh sub create signal-loop --on Echo --kind webhook \ --filter '{"shape":"Echo"}' --webhook-url https://example.com/hook \ --allow-trace-reentry
# Create a cron subscriptionwh sub create daily-sync --kind cron \ --cronspec "0 8 * * *" \ --timezone America/New_York \ --webhook-url https://example.com/daily-sync
# Update a webhook subscriptionwh sub update signal-hook --on Signal \ --filter '{"shape":"Signal"}' \ --webhook-url https://example.com/new-hook
# Clear a fallback webhook URLwh sub update signal-hook --clear-fallback-webhook-url
# List and inspectwh sub listwh sub view signal-hook
# Tail delivery feed (shows run status, attempt count, errors)wh sub log signal-hookwh sub log signal-hook --live
# Drill into a specific run's attemptswh sub attempts 019d90f0-0000-7000-8000-000000000000
# Credential bindingwh sub bind signal-hook --credentials webhook-keyswh sub unbind signal-hookwh sub attempts expects the full dashed run UUID shown by wh sub log. MCP and HTTP action-attempt surfaces use the same canonical run ID.
notifications — Action notifications
Section titled “notifications — Action notifications”| Command | Description |
|---|---|
wh notifications [--limit n] [--since ts] | List repo-scoped action failure notifications |
wh notifications [--limit n] [--since ts] | Explicit form of the same command |
These commands return repo-scoped terminal failure notification records for action deliveries. They are distinct from the web app’s cross-repo user notification feed.
--since accepts either epoch milliseconds or an ISO timestamp.
wh notificationswh notifications --since 2026-03-30T12:00:00Zwh notifications --limit 10auth — Authentication
Section titled “auth — Authentication”| Command | Description |
|---|---|
wh auth login | Log in via browser (device authorization) |
wh auth login --with-token | Log in with a JWT piped via stdin |
wh auth login --profile <name> | Log in to a named profile |
wh auth logout | Log out the default profile |
wh auth logout --profile <name> | Log out a specific profile |
wh auth status | Show all profiles and their auth state |
wh auth status --profile <name> | Show auth state for a specific profile |
wh auth whoami | Show the authenticated identity for all profiles |
wh auth whoami --profile <name> | Show the authenticated identity for a specific profile |
The --profile (-P) flag selects which named credential set to use. Without it, commands use the default profile. Each profile stores its own tokens and the API endpoint used at login time.
If you pass a named --profile that doesn’t exist in ~/.warmhub/auth.json, the CLI fails early with a clear error that names the missing profile, lists your available profiles, and suggests wh auth login --profile <name> to create it. (The default profile still returns the standard “not logged in” path when missing, so interactive users aren’t forced through a login hint on first run.)
# Default profilewh auth loginwh auth statuswh auth logout
# Named profiles (e.g. for different environments)wh auth login --profile staging --api-url https://api.example.comwh auth login --profile prodwh auth status --profile stagingwh auth logout --profile staging
# Piped token into a named profileecho "$TOKEN" | wh auth login --with-token --profile ci
# Use a profile for any commandwh thing list --repo myorg/myrepo --profile stagingSee Getting Access for the full authentication guide.
token — Personal access tokens
Section titled “token — Personal access tokens”| Command | Description |
|---|---|
wh token create --name <name> [--scope scope]... [--scopes-json json] [-d desc] [--expires duration] | Create a new PAT |
wh token list | List all your tokens |
wh token get --name <name> | View a token’s details |
wh token revoke --name <name> | Revoke a token |
All token commands require interactive login — PATs cannot manage other PATs. --scope and --scopes-json are mutually exclusive — use one or the other, not both.
# Repo-scoped tokenwh token create --name ci-bot --scope myorg/myrepo=repo:read,repo:write
# Org-level wildcard (all repos in org, capped by role)wh token create --name org-reader --scope myorg=repo:read
# Global wildcard (all resources, capped by role)wh token create -n deploy --scope repo:write -d "Deploy pipeline" --expires 90d
# Multiple scopes — repeat --scope for each entrywh token create --name ci-bot \ --scope myorg/private-repo=repo:read,repo:write \ --scope myorg=repo:read \ --expires 90d
# Full access (no --scope flag)wh token create --name full-access
# Name-restricted scopes (--scopes-json, mutually exclusive with --scope)wh token create --name scoped-bot --scopes-json '[ {"resource":"myorg/myrepo","permissions":["repo:read"],"allowedMatches":["Signal/*"]}]'
wh token listwh token get --name ci-botwh token revoke --name ci-botSee Personal Access Tokens for scopes, lifecycle, and security details.
credential — Credential sets
Section titled “credential — Credential sets”Credential sets store named keys that subscriptions and actions can bind to. Key names are visible in list/view; values are only accessible via export with a short-lived token.
| Command | Description |
|---|---|
wh credential create <name> [--repo org/repo | --org org] [--scope org|repo] [--description "..."] | Create an empty credential set. Org-scoped sets are available across repos in the org; repo-scoped sets are repo-only. |
wh credential list [--repo org/repo | --org org] | List credential sets accessible from a repo or org |
wh credential view <name> [--repo org/repo | --org org] | View a credential set (key names only, no values) |
wh credential set <name> <key> [--repo org/repo | --org org] [--value <value>] | Add or update a single key. Reads value from --value or stdin. |
wh credential set <name> [--repo org/repo | --org org] | Bulk add or update keys. Reads a JSON object from stdin, e.g. {"KEY":"val"}. |
wh credential unset <name> <key> [--repo org/repo | --org org] | Remove a key |
wh credential audit <name> [--repo org/repo | --org org] [--limit n] | View the audit log for a credential set |
wh credential revoke <name> [--repo org/repo | --org org] [--reason "..."] | Revoke the set (blocks new binds and strips auth from existing webhook deliveries) |
wh credential delete <name> [--repo org/repo | --org org] | Delete a credential set |
Aliases: get → view
component — Component management
Section titled “component — Component management”| Command | Description |
|---|---|
wh component init <name> | Scaffold a new component repository |
wh component install com.warmhub.identity --repo org/repo | Install the bundled Identity system component into an existing repo. Bundled system components do not require registry registration or a component package. |
wh component install <github-url[@ref]> [--ref ref] | Install a component from GitHub. Use @ref suffix or --ref to pin a git ref. |
wh component install <org/name> [--ref ref] | Install a registered component identity. The CLI resolves the registry entry, downloads source via the backend, and runs optional setup. |
wh component install <local-path> | Install a component from a local directory |
wh component list | List installed components |
wh component search <query> | Search GitHub for component repositories |
wh component view <name> | Show component details |
wh component validate <path> | Validate a component package without installing |
wh component update <name> [--ref ref] | Update a GitHub-sourced or registered component. Local-path installs must be reinstalled with install <path>. |
wh component doctor <name> | Run health checks on an installed component |
wh component teardown <name> | Pause subscriptions and mark the component as torn down (non-destructive — shapes and data are preserved) |
wh component register <name> --org <org> [flags] | Register a component identity in an org for reuse via install <org/name> |
wh component unregister <org/name> | Remove a registered component identity |
wh component registry list --org <org> | List registered components in an org |
wh component registry view <org/name> | View one registered component |
wh component registry update <org/name> [flags] | Update a registered component |
Aliases: show → view; get → view
Components are declarative packages that bundle shapes, subscriptions, credentials, and seed data into a single installable unit. Each component is identified by a unique componentId and owns the resources it creates — external writes to component-owned shapes, things, and assertions are blocked.
wh component init my-componentwh component install https://github.com/acme/my-componentwh component install warmhub/veritas --repo myorg/worldwh component install ./local-component --repo myorg/worldwh component register veritas --org warmhub --source-url https://github.com/warmhub/veritas-componentwh component registry list --org warmhubwh component search researchwh component list --repo myorg/worldwh component view my-component --repo myorg/worldwh component validate ./local-componentwh component doctor my-component --repo myorg/worldwh component teardown my-component --repo myorg/worldprime — Agent context
Section titled “prime — Agent context”| Command | Description |
|---|---|
wh prime | Print CLI context in markdown |
wh prime --json | Print CLI context as structured JSON |
See wh prime for details on output format and token budget.
channel — Real-time repo events in Claude Code
Section titled “channel — Real-time repo events in Claude Code”| Command | Description |
|---|---|
wh channel --repo <org/repo> [--repo <org/repo> ...] [--profile name] | Start an MCP channel server that pushes repo events into a Claude Code session |
wh channel runs as an MCP server subprocess. Claude Code receives a notification each time a commit is applied or an action run status changes in any watched repo. Each notification includes the affected shapes and things.
Pass --repo multiple times to watch several repos in parallel from one channel server. Use --profile to select a named auth profile.
Setup
Register the channel as an MCP server using the Claude Code CLI:
claude mcp add warmhub -- wh channel --repo org/repoOr add it manually to your project’s .mcp.json:
{ "mcpServers": { "warmhub": { "command": "wh", "args": ["channel", "--repo", "org/repo"] } }}During the research preview, start Claude Code with the --dangerously-load-development-channels flag:
claude --dangerously-load-development-channels server:warmhubExample usage
# Watch a repo and push events into Claude Codewh channel --repo acme/world
# Watch multiple repos in parallelwh channel --repo acme/world --repo acme/datasets
# With a named auth profilewh channel --repo acme/world --profile stagingNotification format
When a commit is applied, Claude sees:
Commit applied to acme/world — affected Player/alice, Score/round-1 (shapes: Player, Score)When an action run status changes:
Action updated in acme/world — affected Player/alice (shapes: Player)Limitations
- Claude Code only — not supported in Claude Desktop, claude.ai, or the web app
- Events are one-way; use existing
whcommands or MCP tools to query data - Requires
--dangerously-load-development-channelsduring the research preview
Hit a problem or have a question? Get in touch.