Commands
Most commands follow the pattern wh <domain> <verb> [args] [--flags]. A few are flat single commands that take no verb:
| Flat command | Description |
|---|---|
wh use | Local repo context |
wh init | Bootstrap local harness |
wh doctor | Diagnostics and health checks |
wh prime | Agent context |
wh onboard | Agentic onboarding script |
wh update | Update the CLI |
wh notifications | Action notifications |
wh channel | Real-time repo events in Claude Code |
Global flags available on most commands:
| Flag | Description |
|---|---|
--repo | Target repo |
--json | Alias for --format json |
--live | Stream live updates |
--format | Output mode: pretty (default), json, or jsonl |
--profile / -P | Named credential profile |
--no-update-check | Skip the CLI update notice for this run |
--dry-run | Preview command dispatch without calling the backend. Exception: wh commit submit --dry-run calls the server’s real commit evaluator and returns a non-persisted operation preview. |
--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. --json is the constant alias for --format json. Choose either --json or one --format <mode> spelling; combining them is rejected as a duplicate scalar flag.
Two additional flags apply to any command that supports --live: --max-updates <n> exits automatically after receiving n updates, and --live-timeout-ms <ms> exits after ms milliseconds of inactivity. During normal execution both require live mode to be enabled; omitting --live or passing --live=false is rejected.
Retrying a write: --event-request-id
Section titled “Retrying a write: --event-request-id”Several write commands accept --event-request-id <uuid>. Pass your own UUID, and if the command fails in a way that leaves you unsure whether it landed — a timeout, a dropped connection — run it again with the same id. The second run returns the original outcome instead of writing twice. Omit the flag and the CLI generates one per run, which is fine until you need to retry.
The commands that take it are: wh repo content set, wh shape create / revise / rename, wh thing rename, wh thing rewrite, wh thing remove, wh thing redact, and wh collection create / revise. Not every write accepts it — wh shape retract, for instance, does not. Component lifecycle commands take --submission-id, which is not the same guarantee: it lets you recover what a run already changed, but it does not make re-running the command safe — see wh component install.
REQID=$(uuidgen)wh repo content set myorg/world --kind readme --file README.md --event-request-id "$REQID"# unclear result? run the identical command again with the same idwh repo content set myorg/world --kind readme --file README.md --event-request-id "$REQID"use — Local repo context
Section titled “use — Local repo context”| Command | Description |
|---|---|
wh use <org/repo> [--profile <name>] | Set the default repo (and optionally the auth profile) for the current directory |
wh use | Show the active repo and profile context and where each comes from |
wh use --clear | Remove the .wh context file |
Writes a .wh JSON file in the current directory holding the repo and, when --profile is passed, the auth profile. The CLI resolves the target repo using this priority: --repo flag > WARMHUB_REPO env > .wh file, and the auth profile using: --profile / -P flag > WH_PROFILE env > .wh file. The repo is validated against the backend before writing.
wh use myorg/world # writes .wh, validates repo existswh use myorg/world --profile work # also records the auth profile in .whwh use # shows active repo + profile 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. Requires an interactive browser sign-in — personal access tokens can’t create organizations. |
wh org view <name> | View organization details |
wh org list [--include-archived] | List the organizations you belong to (archived hidden by default) |
wh org rename <orgName> [<newName>] [--slug <newSlug>] [--display-name "..."] | Rename an organization and/or update its display name. The positional <newName> updates the slug; --slug is the named equivalent. Use --display-name alone to update only the display name without changing the slug. |
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 service-account create <org> <name> --role <role> [-d desc] | Create a service account with role viewer or editor. Requires org:configure on the org; --role is required. Refused in an archived org. |
wh org service-account list <org> [--all] | List the org’s service accounts. --all includes disabled ones. |
wh org service-account view <org> <name> | View a service account, including its external id and who created it |
wh org service-account set-role <org> <name> --role <role> | Change a service account’s role (viewer or editor). Its tokens follow at their next request. |
wh org service-account disable <org> <name> | Disable a service account. Its tokens are rejected until it is enabled. |
wh org service-account enable <org> <name> | Enable a disabled service account. Its existing tokens work again. |
wh org service-account delete <org> <name> | Delete a service account: revokes its tokens and frees the name. Its history stays attributed to it. |
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 |
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): _app, _static, admin, api, billing, blog, docs, health, healthz, help, login, mcp, public, readyz, robots.txt, settings, signup, sse, status, support, system, trpc, version, 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. Personal orgs can be renamed like any other org — wh org rename works for both GitHub-linked and non-linked personal orgs.
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.comwh org service-account create acme hub-x --role viewer -d "Hub X reader"wh org rename acme newname # rename slug via positionalwh org rename acme --slug acme-co # rename slug via flagwh org rename acme --display-name "Acme Inc" # update display name onlyrepo — Repository management
Section titled “repo — Repository management”| Command | Description |
|---|---|
wh repo create <org/name> [--display-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] [--limit n] [--cursor c] [--all] | List repos in an org (archived hidden by default). Results are paginated: by default the first page is returned with a notice when more exist. Use --all to fetch every page, or --limit / --cursor to page manually. |
wh repo search <query> [--limit n] [--cursor c] [--all] | Search repos across all orgs visible to you (public, plus private repos you can read) by name, display name, description, shape names/descriptions, and README content. Without pagination flags, only the first page of results is returned and the CLI prints a notice if more pages exist — pass --all to fetch every page automatically, or use --limit / --cursor to step through pages manually. Cross-org discovery (not org-scoped like list). |
wh repo view [org/repo] | Show repo details |
wh repo describe [org/repo] [--indexed-fields] | Describe repo schema: shapes, fields, inline descriptions, and per-shape counts. For composite Shapes, inherited fields from composed shapes are expanded in the describe output. Shapes from installed component manifests appear with count 0 when no things have been written yet. Pass --indexed-fields to append additional sections to the normal schema output: a Declared Fields section sourced from the declaration registry (one row per declared field, including declaredByShape and carrying-shape readiness), a per-marker Indexed Fields section showing typed field index state (building, ready, failed, or other) with both the shape name and field path reported for each entry, and an Index consistency section when undeclared markers exist. JSON output also includes declarations and consistency when --indexed-fields is passed. |
wh repo rename <org/repoName> [<newName>] [--slug <newSlug>] [--display-name "..."] | Rename a repo and/or update its display name. The positional <newName> updates the slug; --slug is the named equivalent. Use --display-name alone to update only the display name without changing the slug. |
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 export [org/repo] [--output <path|->] [--mode heads|ops] [--since-repo-seq n] [--at-repo-seq n] [--async] [--wait] [--resume] [--token <token>] | Export the repository as verified NDJSON at an exact sequence. A bare --async prepares the export and prints a redeemable token; --output PATH|- is required only when the command will download bytes (pass a file path or - for stdout). Pass --async together with --wait to start the export and block until it completes, then download the result. Pass --token with --output to download a previously prepared export. See repository export. |
wh repo verify <file> | Verify a saved repository export locally: framing, canonicality, record count, sequence, and digest. Contacts no server. |
wh repo delete <org/repo> [--yes] | Delete a repo. Hidden immediately and purged after a 30-day grace window. Other repos do not block deletion: subscriptions in other repos that watch this repo are retired when it is purged. |
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.
For checkpoint operations on a repo, see Repository Checkpoints.
wh repo create myorg/world -d "Game world"wh repo create myorg/world -d "Game world" --visibility publicwh repo create myorg/world --display-name "My World" -d "Game world"wh repo list myorgwh repo search userswh repo rename myorg/world newworld # rename slug via positionalwh repo rename myorg/world --slug world-v2 # rename slug via flagwh repo rename myorg/world --display-name "My World" # update display name onlywh repo describe myorg/world # schema viewwh repo describe myorg/world --indexed-fields # schema view plus declared fields, indexed fields, and consistency sectionsrepo index — Typed field index declarations
Section titled “repo index — Typed field index declarations”| Command | Description |
|---|---|
wh repo index pin <Shape> <field.path> [org/repo] | Declare a typed field index for a field path. The declaration is repository-wide: every shape in the repo that carries that field path is backfilled, not only the named shape. Adds the field to the declaration registry, which is reflected in the Declared Fields section of wh repo describe --indexed-fields. The index begins building immediately and transitions to ready when complete. |
wh repo index unpin <Shape> <field.path> [org/repo] | Withdraw a typed field index declaration for a field path. The withdrawal is repository-wide: the index stops covering every shape in the repo that carries that field path, not only the named shape. Removes the entry from the declaration registry. |
wh repo index pin Player scorewh repo index unpin Player scorerepo 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: Readme (human-facing) and Agents (agent-facing). A third, 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>] [--event-request-id <uuid>] | Set Content markdown (file, inline, or piped stdin). llms-txt is read-only — set attempts are rejected. Pass --event-request-id for safe resubmission — see the note above. |
wh repo content prompt [org/repo] --kind readme|agents|llms-txt | Print an agent-ready prompt (bounded repo context + the follow-up set command) so your own agent can draft Content locally — no hosted LLM. llms-txt is read-only and cannot be drafted or set; passing --kind llms-txt errors immediately. |
wh repo content generate <org/repo> [--kind readme|agents|llms-txt] | Deprecated — always errors. WarmHub does not host README/AGENTS generation. Use wh repo content prompt instead: it prints a prompt your own agent drafts from, and you save the result with wh repo content set. |
set reads from --file <path>, --content "...", or stdin when neither flag is passed. Use --file - to force stdin.
The [org/repo] argument is optional for all three verbs — when omitted, the command falls back to the repo set via wh use for the current directory. You can also override the target repo for any single invocation with --repo.
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"
# Draft locally with your own agent, then save the result:wh repo content prompt myorg/world --kind readmewh repo content set myorg/world --kind readme --file README.md
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 <org/name>] [--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>' | --file <path>) [--description '...'] [--compose <shape> ...] [--event-request-id <uuid>] | Create a new shape. --file reads the same fields object from disk and avoids shell-specific JSON quoting. Use --compose (repeatable) to declare that this shape composes one or more existing shapes — each value is a shape name and the flag maps to the composes array in the shape body. In pretty mode, the request ID is written to stderr; pass --json to receive the full shape-change response on stdout, which carries the receipt under receipt. Pass --event-request-id for safe resubmission — see the note above. |
wh shape revise <name> [--fields '<json>'] [--file <path>] [--description '...'] [--compose <shape> ...] [--clear-composition] [--show-diff] [--event-request-id <uuid>] | Revise a shape. At least one of --fields, --file, --description, --compose, or --clear-composition is required — invoking wh shape revise <name> with none of these flags is a usage error. Supply --fields or --file (not both), --description, --compose, or --clear-composition (not both) — you may pass any subset of these, but --fields and --file are mutually exclusive, and --compose and --clear-composition are mutually exclusive. When --fields or --file is supplied, the fields object is a full replacement, not a merge — omitting a field removes it from the shape; include all fields you want to keep. A description-only revise (--description "..." with no --fields/--file) updates the description without touching fields. A composition-only revise (--compose or --clear-composition with no --fields/--file) updates composition without touching fields. Creates a new version when the definition changes; produces a noop when the supplied values are identical to the current version. Revising a composite shape (any of these flags) also re-captures its composed shapes at their current versions, so it creates a new version when a composed shape has a newer version, and is refused when that version conflicts with the composite — see Composite Shapes. Use --compose (repeatable) to replace the shape’s composition list with the supplied shapes. Use --clear-composition to remove all composed shapes from this shape. Pass --show-diff to print the field additions, removals, and type changes the revision applied. When --json is used together with --show-diff, the result includes a diff object describing those changes. Pass --event-request-id for safe resubmission — see the note above. |
wh shape retract <name> [--reason '<text>'] [-m msg] [--committer <wref>] [--expected-version <n>] | Retract a shape, marking it inactive. --expected-version retracts only if the shape is still at that version. |
wh shape history <name> [--include-retracted] [--limit n] [--cursor c] [--all] | Show shape version history |
wh shape rename <oldName> <newName> [--event-request-id <uuid>] | Rename a shape. Pass --event-request-id for safe resubmission — see the note above. |
wh shape create Location --fields '{"x":"number","y":"number"}'wh shape create Location --file location-fields.jsonwh shape create Location --fields '{"x":"number","y":"number"}' --compose BaseEntitywh shape listwh shape view Locationwh shape revise Location --fields '{"x":"number","y":"number","label":"string"}'wh shape revise Location --description "Updated description" # description-only revisewh shape revise Location --file location-fields.json --show-diff # print added/removed/changed fieldswh shape revise Location --fields '{"x":"number"}' --compose BaseEntity --compose Taggablewh shape revise Location --compose BaseEntity # composition-only revisewh shape revise Location --fields '{"x":"number"}' --clear-compositionwh shape revise Location --clear-composition # remove composition without changing fieldswh shape retract OldShape -m "Withdraw old shape"thing — Thing operations
Section titled “thing — Thing operations”A thing, assertion, collection, or View is addressed by its bare name (cave, cave-safe). A shape as a target is written hub/shapes/<Name>; flags that only take a shape (--shape, --declared-shape) use the bare <Name>. See Wrefs.
| Command | Description |
|---|---|
wh thing list [--shape s] [--declared-shape s] [--shapeless] [--kind shape|thing|assertion] [--limit n] [--cursor c] [--all] [--match glob] [--where "field=val"] [--include-retracted] [--count] [--since-repo-seq <n>] [--component <org/name>] [--exclude-components] [--include-validated-shapes] | Show current HEAD state. Results are paginated with a default page cap of 50 rows; use --all to auto-fetch every page, or use --limit / --cursor to page manually. Without --all, pretty output prints a notice when additional rows exist; either --json or --format jsonl suppresses that notice, but combining them is rejected. Use --count to return only the count of matching items. --where is a repeatable typed field-value predicate (field=val, field>=val, field~prefix, field in:[a,b], field?) — see Field-Value Predicates. Use --since-repo-seq <n> to return only identities changed after that repository sequence number (-1 or any nonnegative safe integer; cannot be combined with --live). When no --shape is given, system-managed component infrastructure records are hidden by default. Use --exclude-components to also hide component-owned data things. Use --declared-shape to filter results to things whose declared shape matches the given shape name (composite-read filter). --shapeless returns only things no shape certifies; it cannot be combined with --shape or --declared-shape. Pass --include-validated-shapes to expand each result with the full certified shape closure in the validatedShapes field; this is an opt-in expansion and is not included in the default payload. A collection is a thing: it is returned by --kind thing and identifies itself through its collections field. --kind collection is still accepted for a deprecation window and narrows to the built-in collection shape names — see Filtering by kind. |
wh thing create <name> (--shape s [--shape s ...] | --shapeless) (--data '<json>' | --file <path>) [-m msg] [--committer <wref>] | Create a thing from inline JSON or a JSON object file. Pass --shape (repeatable), or --shapeless for a thing no shape certifies. |
wh thing view [<wref>...] [--file <path>] [--version n] [--depth n] [--include-retracted] [--data-mode auto|full] | Show a shape or shaped thing’s details. Variadic: a single wref routes to the single-target path; multiple wrefs or --file routes to batch fetch (max 500 wrefs per call). --depth returns an embedded graph view and cannot be combined with --live, --version, or --include-retracted — graph reads are active-only and version-pinned graph reads are not supported on this command. Pretty output includes a by: row with the committer wref when the version was written with --committer. --data-mode controls collection data output for built-in collection shapes (Arc, Bond, Set, and List): auto (default) summarizes large bodies; full forces the complete collection body into the output. --data-mode cannot be combined with --depth. |
wh thing history [wref] [--shape] [--about] [--declared-shape s] [--resolve-collections] [--limit] [--cursor] [--all] [--include-retracted] | Show version history for a shape or shaped thing named by the positional wref. To see the history of assertions about a target instead, omit the positional and pass --about <wref>, optionally narrowed with --shape (e.g. wh thing history --about alice --shape Observation returns the version history of Observation assertions whose about target is alice). --resolve-collections only applies with --about. Each version line ends with by <committerWref> when attribution is recorded, followed by born: <createdOn> when creation metadata is present. Use --declared-shape to filter history to things whose declared shape matches the given shape name (composite-read filter). |
wh thing resolve <wref> | Resolve wref to a shape or shaped-thing identity. Accepts canonical wh:org/repo/... inputs and self-routes them without requiring --repo. |
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> (--shape s [--shape s ...] | --shapeless | --keep-shapes) --data '<json>' [-m msg] [--committer <wref>] [--expected-version <n>] [--lease-id <id>] | Revise a thing. One of --shape, --shapeless, or --keep-shapes is required. Pass --shape (repeatable) to certify the revised thing against one or more shapes, --shapeless to certify it against no shape, or --keep-shapes to leave the existing shape certification unchanged. --committer is optional; when omitted, the write is attributed to your token’s bound committer identity (set via wh token create --committer-identity) if one is configured, otherwise to your authenticated user identity. If the token’s bound identity has since been removed or become unavailable, the write fails with NOT_FOUND and a message to reconfigure the PAT; it does not fall back to your user identity. 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 <name> [--reason '<text>'] [-m msg] [--committer <wref>] [--expected-version <n>] [--lease-id <id>] | Retract a thing, marking it inactive. --committer is optional; when omitted, the write is attributed to your token’s bound committer identity (set via wh token create --committer-identity) if one is configured, otherwise to your authenticated user identity. If the token’s bound identity has since been removed or become unavailable, the write fails with NOT_FOUND and a message to reconfigure the PAT; it does not fall back to your user identity. --expected-version retracts only if the thing is still at that version; --lease-id writes under a read lease. |
wh thing restore <durableId> [--name <newName>] [--expected-version <n>] [--lease-id <id>] [-m msg] [--committer <wref>] | Make a retracted thing (including an assertion or a collection) or Shape active again, by its durable ID. The target must currently be retracted; restoring an active one is an error. Pass --name to restore it under a different name; a Shape takes hub/shapes/<Name>. Pass --expected-version to restore only if the thing is still at that version (optimistic concurrency). Pass --lease-id to write under a read lease. To find the durable ID of a retracted thing, run wh thing history <name> --include-retracted. |
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] [--declared-shape s] [--shapeless] [--kind shape|thing|assertion|collection] [--about wref] [--affirmed-about <pinnedTargetWref>] [--resolve-collections] [--role from|to|ends] [--limit n] [--cursor c] [--all] [--match glob] [--where "field=val"] [--include-retracted] [--count] [--since-repo-seq <n>] [--component <org/name>] [--exclude-components] [--include-validated-shapes] | Query by filters. Results are paginated with a default page cap of 50 rows; use --all to auto-fetch every page, or use --limit / --cursor to page manually. Without --all, pretty output prints a notice when additional rows exist; either --json or --format jsonl suppresses that notice, but combining them is rejected. Use --count to return only the count of matching items — --count cannot be combined with --role, --cursor, --all, --limit, or --live. --where is a repeatable typed field-value predicate (field=val, field>=val, field~prefix, field in:[a,b], field?) — see Field-Value Predicates. Use --since-repo-seq <n> to return only identities changed after that repository sequence number (-1 or any nonnegative safe integer; cannot be combined with --live). When no --shape is given, system-managed component infrastructure records are hidden by default. --role filters collection membership by direction and is only valid together with --about and --resolve-collections. from returns collections where the target is the source end, to returns collections where the target is the destination end, and ends returns collections where the target appears at either end. --affirmed-about accepts a pinned target wref (e.g. cave@v3) and narrows results to active assertions whose current version affirms that pinned target. The wref must be pinned; any pinned target wref is accepted, not only shape versions. Use --declared-shape to filter results to things whose declared shape matches the given shape name (composite-read filter). --shapeless returns only things no shape certifies, to find unstructured data; it cannot be combined with --shape or --declared-shape. Pass --include-validated-shapes to expand each result with the full certified shape closure in the validatedShapes field; this is an opt-in expansion and is not included in the default payload. |
wh thing search <query> [--shape s] [--kind shape|thing|assertion|collection] [--about wref] [--affirmed-about <pinnedTargetWref>] [--mode text|vector|hybrid] [--resolve-collections] [--include-retracted] [--limit n] [--cursor c] [--all] [--component <org/name>] [--exclude-components] | Search repository content by text, vector, or hybrid mode. --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; --mode hybrid warns and ignores it. --about is not supported with --mode vector — combining them is an error. --affirmed-about accepts a pinned target wref (e.g. cave@v3) and narrows results to active assertions whose current version affirms that pinned target; the wref must be pinned, and any pinned target wref is accepted, not only shape versions. See search indexing behavior below. |
wh thing refs <wref> [--inbound|--outbound] [--binding identity|versioned] [--field path] [--limit n --cursor c] [--all] | Show things that store this shape or shaped-thing wref in a field (inbound, default), or wrefs stored in this target’s fields (outbound). Use wh thing about for assertions targeting it. --field filters inbound refs by field path. --binding keeps only edges written under one binding mode: identity for unversioned wref fields, versioned for pinned wref fields. --cursor requires --limit (cursors are scoped to a specific page size). |
wh thing about <wref> [--shape s] [--match glob] [--depth n] [--resolve-collections] [--role from|to|ends] [--limit n --cursor c] [--all] [--include-retracted] | Show assertions whose about target is this shape or shaped 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. --role filters which collections are considered when --resolve-collections is active: from includes only collections where the target is the source end, to includes only collections where the target is the destination end, and ends includes collections where the target appears at either end. --role requires --resolve-collections. --cursor requires --limit (cursors are scoped to a specific page size). Accepts both thing wrefs and shape wrefs as the target. No --where: to filter by field values, pass where to the SDK’s client.thing.about or the MCP warmhub_thing_about tool. |
wh thing rename <name> <newName> [--event-request-id <uuid>] | Rename a thing. Pass --event-request-id for safe resubmission — see the note above. |
wh thing rewrite <name@vN|durableId@vN> --shape <Shape> [--shape <Shape> ...] (--data '<json>' | --file <path>) [--yes|-y] [--event-request-id <uuid>] [--dry-run] | Correct bad data by replacing the body of one exact version in place. The version number does not change and no new version is created; to record a new state, use wh thing revise instead. The target must name a pinned version (for example cave@v2). Pass every shape the version declares plus CorrectedThing, the correction marker, as repeated --shape flags. --data (or --file) is the complete replacement body; keep every earlier correctionLog entry and append one { "reason": ... }. The current body is not kept. Requires an unrestricted things:rewrite grant, which maps from repository admin access (repo:admin); an admin token limited to particular refs is rejected with FORBIDDEN, and write access does not grant rewrite. A redacted thing cannot be rewritten. Pass --dry-run to validate the rewrite without applying it, --yes / -y to skip the confirmation prompt, and --event-request-id for safe resubmission after an unclear result. |
wh thing redact <durableId> [--expected-version <n>] [--yes|-y] [--event-request-id <uuid>] | Redact a thing by its durable ID. Redaction irreversibly erases one thing’s canonical content and former name while preserving its durable identity, version history topology, and existing inbound references. Every version’s body becomes {} and every current and historical name becomes hub/redacted/<durableId>; the thing itself is not deleted (use wh thing remove for that). This cannot be undone. Requires an unrestricted things:redact grant, which maps from repository admin access (repo:admin); an admin token limited to particular refs is rejected with FORBIDDEN, and write access does not grant redaction. The target must be a bare durable ID — a wref is rejected; use wh thing resolve <wref> to look it up. Pass --expected-version to redact only if the thing is still at that version (a stale version fails with CONFLICT); when omitted, the current version is used. Pass --yes / -y to skip the confirmation prompt. Pass --event-request-id for safe resubmission after an unclear result. Core redaction does not erase backups, completed exports, logs, or copies already delivered downstream. See Redaction. |
wh thing remove <wref|durableId> [--expected-version <n>] [--yes|-y] [--event-request-id <uuid>] [--dry-run] | Permanently remove a thing. This is a destructive operation — the thing and its version history are deleted. Requires unrestricted repository admin access (repo:admin); an admin token limited to particular refs is rejected with FORBIDDEN. Write access does not grant removal — see Access reference. Pass --expected-version to remove only if the thing is still at that version; when --expected-version is omitted, the CLI reads the thing first and removes only the version it just saw (optimistic concurrency by default). Pass --yes / -y to skip the confirmation prompt. Pass --event-request-id for safe resubmission after an unclear result. Pass --dry-run to preview the operation without applying it. Note: when the target Thing has been retracted, you must supply its durable ID — a wref is rejected because retracted names are not unique. Use wh thing resolve <wref> to look up the durable ID before retraction; it prints on the durableId: line. |
wh thing list --shape Locationwh thing create cave --shape Location --file cave.jsonwh thing create note --shapeless --data '{"text":"unsorted"}'wh thing view cave --version 3wh thing view cave --depth 2wh thing query --kind assertion --about cavewh thing search "safe location" --shape Observation --mode hybridwh thing history cave --limit 10wh thing history --about alice --shape Observation # history of Observation assertions whose about target is alicewh thing refs cave # inbound refs (default)wh thing refs cave --field target # filter by field pathwh thing refs cave --binding identity # only identity-bound edgeswh thing refs obs1 --outbound # outbound refswh thing about cave # assertions about this thingwh thing about cave --shape Observation # filter assertions by shapewh thing about cave --depth 2 # include child assertionswh thing about hub/shapes/Location # assertions about a shape wrefwh thing list --count # count all active itemswh thing list --shape Location --count # count by shapewh thing query --about cave --count # count matching query results (cannot combine --count with --role)
# Filter to active assertions whose current version affirms a pinned target wrefwh thing query --affirmed-about cave@v2wh thing search "cave" --affirmed-about cave@v2
# Incremental reads — return only identities changed after a repo sequence numberwh thing list --shape Location --since-repo-seq 42wh thing query --kind assertion --since-repo-seq 0wh thing list --count --since-repo-seq 100 # count items changed after seq 100
# Composite-read filterswh thing list --declared-shape Locationwh thing query --declared-shape Location --include-validated-shapeswh thing history cave --declared-shape Location
# wh thing view is variadic — batch fetch up to 500 wrefs in one callwh thing view alice bob 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 alice@v1 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`)
# Force full collection body output for a large built-in collectionwh thing view large-set --data-mode full
# --data-mode also works in batch readswh thing view large-set other-set --data-mode full
# Remove a thing permanentlywh thing remove old-cave --yeswh thing remove old-cave --expected-version 3 --yeswh thing remove old-cave --dry-run# Remove a retracted thing — must use durable ID, not wrefwh thing remove <durableId> --yes
# Redact a thing by durable ID (irreversible; erases its content and former name)wh thing redact <durableId> --yeswh thing redact <durableId> --expected-version 3 --yes
# Correct version 2 of a thing in place (admin correction; no new version)wh thing rewrite cave@v2 --shape Location --shape CorrectedThing --file corrected.json --dry-runwh thing rewrite cave@v2 --shape Location --shape CorrectedThing --file corrected.json --yes
# Reactivate a retracted thing by its durable IDwh thing restore <durableId>wh thing restore <durableId> --name new-cave-namewh thing restore <durableId> --expected-version 3Batch 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, ...}. The fields below distinguish two wref shapes — the local form (the bare name, e.g.cave, repo-relative) and the canonical form (wh:org/repo/cave, fully qualified for cross-repo reads):requested— the original input string verbatim, so canonical inputs likewh:org/repo/caveare still identifiable downstream after the backend normalizes them to local form.wref— present on hit rows (the local form, e.g.caveeven when the input was canonical) and on miss rows where the backend returned an entry inresult.missing(the version-qualified form from that entry). A small number of miss rows — those where the requested wref was absent from bothresult.itemsandresult.missing— emit only{"requested": ..., "found": false}with nowreffield. Consumers must treatwrefas optional and usefoundas the authoritative hit/miss signal.found— boolean. Always present; use this to distinguish hits from misses rather than checking for the presence ofwref.
Correlate input lines to result lines by
requestedand filter byfoundwithout reconstructing the envelope.
--data-mode controls how built-in collection shapes (Arc, Bond, Set, and List) render their body: auto (default) summarizes large bodies; full forces the complete body into the output. It is accepted on both single-target and batch reads. Passing --data-mode together with --depth is an error: --data-mode cannot be combined with --depth on view.
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 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 alice --keep-shapes --data '{"score":2}' --lease-id <id>
# Or hand the lease back early if you decide not to write.wh thing release-lease 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 requires exactly one--retracttarget — a multi-target--retractwith--lease-idis rejected. 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 search indexing
Section titled “Thing search indexing”wh thing search behavior varies by mode:
Text mode (default when --mode is omitted or --mode text)
- Every field a thing’s shape declares is indexed when it holds a value, regardless of type. Strings, numbers, booleans, arrays, and nested object values are all converted to searchable text. Only fields described by the shape contribute — values not covered by a shape field are not indexed.
- New things and revisions are indexed asynchronously after the write completes. There is a short backfill delay before a newly written thing appears in text search results.
Vector mode (--mode vector) and hybrid mode (--mode hybrid)
- Vector and hybrid search depend on embeddings being enabled for the WarmHub deployment you connect to. When embeddings aren’t available, these modes return an error; text mode always works. See search modes for what each mode does.
--cursorand--allare not supported in vector or hybrid mode.
Durable ids on thing reads
Section titled “Durable ids on thing reads”A durable id is accepted anywhere a wref is. Because a bare durable id self-routes, repo context is not required on wh thing view, history, resolve, refs, and about when the target is a bare durable id:
wh thing view <durable-id> # no repo context neededwh thing history <durable-id>wh thing about <durable-id>wh thing resolve also self-routes canonical wh:org/repo/... inputs — no repo context is required when the wref is in canonical form.
wh assertion view and wh assertion history accept bare durable ids without repo context.
wh thing graph and wh thing view --depth also self-route when the root is a bare durable id — no repo context is required in that case. When the root is a canonical wh:-prefixed wref (e.g. wh:org/repo/cave), the command still needs repo context for graph expansion — supply it via --repo, WARMHUB_REPO, or a .wh file.
thing graph — Embedded graph reads
Section titled “thing graph — Embedded graph reads”wh thing graph is a sub-command of the thing domain. Starting from the requested thing or shape wref, it traverses outbound wref fields and assertion backlinks — specifically, assertion about targets and outbound wref fields — and returns them embedded as objects. Depth defaults to 2 and is capped at 5. The traversal does not follow inbound wref-field backlinks (things that happen to reference the target in a field); use wh thing refs <wref> --inbound for that case. 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 accepts --version <n> to pin the root thing to a specific version. It does not accept --include-retracted — retracted graph reads are not supported.
wh thing graph cavewh thing graph cave --depth 3wh thing graph cave --depth 2 --jsonwh thing graph cave --version 2assertion — 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] [--field <data.path=value>] | Browse assertions in HEAD, optionally scoped to assertions about a thing or shape wref. Use --count to return only the count. Use --field to filter by an exact data-field value (data.path=value). |
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. --depth is active-only: it cannot be combined with --version or --include-retracted. If you need a version-pinned graph read, use wh thing graph. Accepts a bare durable id without repo context; when the root is a bare durable id, --depth also self-routes without repo context. When the root is a canonical wh:-prefixed wref, repo context is still required for graph expansion — supply it via --repo, WARMHUB_REPO, or a .wh file. |
wh assertion create --shape <s> --name <n> --about <wref> [--data '<json>'] [--affirm <pinnedWref>] [-m msg] [--committer <wref>] | Create a named assertion. --about accepts an untyped shape or shaped-thing wref. To assert about a collection, create a named collection first and point --about at that collection’s wref. --affirm is repeatable; each value is a pinned wref (e.g. cave@v3) that the assertion affirms. Pass --affirm multiple times to affirm more than one target. |
wh assertion revise <wref> --data '<json>' [--affirm <pinnedWref>] [-m msg] [--committer <wref>] | Revise an assertion. --affirm supplies the complete affirmation set for the revised assertion (repeatable); omitting --affirm entirely clears the existing affirmation snapshot. Pass --affirm once per pinned wref you want the revised assertion to affirm — include every target you want to keep, not just new ones. See Assertion operations for the full affirmation contract. |
wh assertion reaffirm <wref> (--add <pinnedWref> | --remove <pinnedWref>) [--add <pinnedWref> ...] [--remove <pinnedWref> ...] [--expected-version <n>] [-m msg] [--committer <wref>] | Update an assertion’s affirmed targets without changing its data, using delta flags. --add adds a pinned wref to the affirmed set; --remove removes one. Both flags are repeatable and can be combined in a single call. At least one --add or --remove is required — omitting both is an error. Pass --expected-version to apply the reaffirm only if the assertion is still at that version. See Assertion operations for the full reaffirm contract. |
wh assertion retract <wref> [--reason '<text>'] [-m msg] [--committer <wref>] [--expected-version <n>] | Retract an assertion, marking it inactive. --expected-version retracts only if the assertion is still at that version. |
wh assertion history <wref> [--include-retracted] [--limit n] [--cursor c] [--all] | Show assertion version history. Accepts a bare durable id without repo context. |
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”. --about accepts both thing wrefs and shape wrefs.
wh thing view <name> also works since assertions are things.
wh assertion create --shape Observation --name cave-safe --about cave --data '{"safe":true}'wh assertion create --shape Observation --name cave-safe --about cave --data '{"safe":true}' --affirm cave@v1wh collection create --type bond --name cave-lake --members cave,lakewh assertion create --shape Connected --name cave-lake-connected --about cave-lake --data '{"connected":true}' # about the cave-lake bond abovewh assertion list --about cave --shape Observationwh assertion list --about hub/shapes/Location # assertions about a shape wrefwh assertion view cave-safe --depth 2wh assertion revise cave-safe --data '{"safe":true,"confidence":0.9}'# --affirm is a complete replacement — repeat every pinned target you want to keepwh assertion revise cave-safe --data '{"safe":true}' --affirm cave@v2wh assertion reaffirm cave-safe --add cave@v2wh assertion reaffirm cave-safe --add cave@v2 --add cave@v3wh assertion reaffirm cave-safe --remove cave@v1 --add cave@v2wh thing view cave-safewh assertion list --shape Observationwh assertion list --countwh assertion history cave-safe --include-retractedwh assertion list --about run-001 --shape HypothesisCandidate --field data.round=1collection — Collection operations
Section titled “collection — Collection operations”Collections group wrefs into typed containers. --type picks arc (directed), bond (symmetric), set, list, or pair (deprecated — accepted for compatibility but prefer the others for new models). The collection is addressed by its --name, like any other thing. See Collections for the full data model.
| Command | Description |
|---|---|
wh collection create --type <type> --name <name> [--members <wref,...>] [--file <path>] [--stdin] [--wref-field <field>] [--shape <shape>] [--where "field=val"] [--match glob] [--kind <kind>] [--about <wref>] [--component <org/name>] [--exclude-components] [--source-repo org/repo] [--from <wref>] [--add <wref,...>] [--remove <wref,...>] [--replace-file <path>] [--replace-stdin] [--skip-existing] [--event-request-id <uuid>] [-m msg] [--committer <wref>] | Create a named collection. For new models, use arc, bond, set, or list. pair is also accepted for compatibility but is deprecated. Supports three creation flows — explicit members, selector-backed set, and copy/delta — described below the table. Pass --skip-existing (valid only with --name) to make the create a no-op when a collection with that name already exists, instead of erroring. Pass --event-request-id for safe resubmission — see the note above. |
wh collection revise <named-wref> [--members <wref,...>] [--file <path>] [--stdin] [--wref-field <field>] [--add <wref,...>] [--remove <wref,...>] [--shape <shape>] [--where "field=val"] [--match glob] [--kind <kind>] [--about <wref>] [--component <org/name>] [--exclude-components] [--source-repo org/repo] [--event-request-id <uuid>] [-m msg] [--committer <wref>] | Update an existing collection. Pass --members, --file, or --stdin to replace the full member list, --add / --remove for set deltas, or --shape / --where / --match (and the selector filters --kind, --about, --component, --exclude-components) to refresh a selector-backed set. Pass --event-request-id for safe resubmission — see the note above. |
wh collection members <wref> [--version n] [--limit n] [--cursor c] [--all] | List the members of a collection. Paginated. Pass --version to read a specific historical version. |
wh collection contains <wref> <member...> [--members <wref,...>] [--file <path>] [--stdin] [--position <n>] [--version n] [--wref-field <field>] | Check whether a collection contains one or more member wrefs. Pass members as positional arguments, via --members (comma-separated), --file, or --stdin. Use --position <n> to constrain the check to a specific zero-based list index, --version to check against a specific version, and --wref-field to specify which field to match on. Pretty output prints hit positions without requiring --position. |
wh collection diff <wref> <wref> [--mode <mode>] [--left-version n] [--right-version n] | Show the member-level diff between two collections (or two versions of the same collection). --mode controls diff output format. --left-version and --right-version pin each side to a specific version. |
wh collection stats <wref> [--version n] | Show summary statistics for a collection (member count, type, version). Pass --version to read stats for a specific historical version. |
wh collection create supports three creation flows:
- Explicit members — pass
--members,--file,--stdin, or--wref-fieldto supply members directly. - Selector-backed set — pass
--shape,--where,--match, and/or the selector filters--kind,--about,--component,--exclude-components(optionally--source-repo) to define a dynamic query that populates the set. - Copy or delta — pass
--from <wref>to base the new collection on an existing one, optionally combined with--add,--remove,--replace-file, or--replace-stdinto apply changes on top.
# Explicit-members creationwh collection create --type set --name cave-set --members cave,lakewh collection create --type bond --name cave-lake --members cave,lakewh collection create --type list --name route --members start,end
# Selector-backed set creationwh collection create --type set --name all-caves --shape Location --where "kind=cave"
# Copy or delta creationwh collection create --type set --name cave-set-v2 --from cave-set
# Revise: full replacementwh collection revise cave-set --members cave,lake,river
# Revise: set deltawh collection revise cave-set --add river --remove lake
# Revise: refresh selector-backed setwh collection revise all-caves --shape Location --where "kind=cave"
# Read commandswh collection members cave-setwh collection members cave-set --version 2wh collection contains cave-set cavewh collection contains cave-set cave --position 0 --version 3wh collection diff cave-set other-setwh collection diff cave-set cave-set --left-version 1 --right-version 3wh collection stats cave-setwh collection stats cave-set --version 2commit — Write operations
Section titled “commit — Write operations”| Command | Description |
|---|---|
wh commit submit [--ops json] [-f file] [--stream] [--stream-id id] [--server-stream] [--group-size n] [--skip-existing] [--add name] [--revise wref] [--retract wref] [--expected-version n] [--reason text] [--affirm <pinnedWref>] [flags] | Submit a stream of operations. Bare wh commit is equivalent. --expected-version requires --revise or exactly one --retract and is shorthand for the short-form flags only; for --ops inline JSON arrays, --file, and --stream batches, carry expectedVersion inside each operation object instead. --affirm is repeatable; each value is a pinned wref (e.g. cave@v3) that the submitted assertion affirms — see Affirming a target with --affirm for where it can be used. |
wh commit receipt <event-request-id> [--metadata] | Look up the outcome of a previously submitted write by its event-request ID. Without --metadata, returns the full receipt, including per-operation results. A receipt whose data a later Thing removal or rewrite deleted is marked pruned: true and no longer carries per-operation results. Pass --metadata to fetch only the receipt header (status, timestamps, and write metadata) without the full operation payload — useful for a lightweight status check when you don’t need the per-operation detail. |
wh commit submit is the write entrypoint. Use wh thing history <wref> for
the per-thing version trail.
Affirming a target with --affirm
Section titled “Affirming a target with --affirm”--affirm works only when the command writes exactly one assertion:
- Use it with
--addor--revise. It is rejected with--ops,--file, or--stream— for those, putaffirmedTargetson each operation object instead. - With
--add, affirm one add at a time. Repeating--addalongside--affirmis an error, because the repeated--affirmvalues form a single affirmation set. - The write must be an assertion.
--addbecomes one as soon as you pass--about;--revisedoes not, so add--kind assertionwhen you affirm on a revise.
See Commit Submit Deep-Dive for the full breakdown of commit submit flags, including:
- JSONL client-streaming with
--stream/--file *.jsonl(both require--stream-idand--skip-existing), plus--progressand--chunk-size. - JSONL server-streaming with
--server-streamand--stream-id. In server-stream mode the server applies each operation and emits a versioned result row as it goes; use--format jsonlor default pretty output to receive that row stream —--server-streamrejects only--json.--server-streamrequires--stream-idand accepts--group-size <n>to control how many operations are grouped per server-side batch.
wh commit submit --add cave --shape Location --data '{"x":3,"y":7}' -m "Add cave"wh commit submit --retract 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 --stream-id bulk-import --skip-existing --progress -m "Bulk import"wh commit submit --file dataset.jsonl --stream-id bulk-import --skip-existing -m "Rerun seed"cat ops.jsonl | wh commit submit --stream --stream-id pipe-stream --skip-existing -m "Pipe stream"wh commit submit --file dataset.jsonl --server-stream --stream-id server-import --format jsonl --group-size 50 -m "Server-stream import"
# Look up a write outcome by event-request IDwh commit receipt 019d90f0-0000-7000-8000-000000000001
# Header-only receipt lookup — status and metadata without the full operation payloadwh commit receipt 019d90f0-0000-7000-8000-000000000001 --metadata--add is repeatable up to 20 operations; pair each with its own --data. See Commit Submit Deep-Dive for cardinality rules.
view — Stored-View read operations
Section titled “view — Stored-View read operations”A View is a saved query stored in the repo as a versioned object. wh view evaluate runs the query and returns the resulting rows. Views are addressed by name (e.g. my-view).
| Command | Description |
|---|---|
wh view evaluate <wref> [--limit n] [--cursor c] [--all] | Evaluate a stored View by its wref and return the resulting rows. Results are paginated: by default the first page is returned with a notice when more exist. Use --all to fetch every page automatically, or use --limit together with --cursor to page manually. Note: --cursor requires --limit — passing --cursor without --limit is rejected. |
wh view evaluate my-viewwh view evaluate my-view --limit 25wh view evaluate my-view --cursor <cursor> --limit 25wh view evaluate my-view --allgrant — Grant management
Section titled “grant — Grant management”Grants control which members, personal access tokens, and components can perform specific operations on a repo’s data. Use wh grant create to issue a grant, wh grant get or wh grant list to inspect grants, and wh grant revoke to remove one. Revoked grants stay queryable: get returns a grant whether it is active or revoked, and list returns both, so revocation history remains available for audit.
The target repo for a grant comes from the active repo context — set via wh use or --repo. --key is an issuer-scoped idempotency key: passing the same --key value a second time returns the original grant instead of creating a duplicate.
The grantable operations are: things:read, commits:read, shapes:read, notifications:read, subscriptions:read, and subscriptions:create.
Only a signed-in member can create a grant. Personal access tokens and components can receive grants but cannot issue them, so a request authenticated with a PAT or as a component is rejected. Automation that issues grants cannot run under a PAT.
The recipient you name has to resolve to something live at issuance time:
member <email>— a member of the repo’s org.pat <token-name>— one of your own active tokens. Another user’s token name is rejected, and so is one of yours that has been revoked or expired.component <org/name>— a component with an active installation in the target repo.
Once issued, a grant keeps replaying for the same request key even if its recipient’s token is later revoked or its component is uninstalled.
| Command | Description |
|---|---|
wh grant create <member|pat|component> <email|token-name|org/name> --key <idempotency-key> --op <operation> [--op <operation> ...] (--coverage JSON | --view NAME[@vN]) | Create a new grant against the active repo. The first positional is the grantee type (member, pat, or component); the second is the grantee identifier (email address, token name, or org/name). At least one --op is required and specifies the operation being permitted (repeatable). Exactly one of --coverage or --view must be supplied to scope what the grant covers. --coverage accepts a JSON object in the canonical shape-aware form { "paths": { "include": [...] }, "shapes"?: [...], "shapesAll"?: [...], "shapesNot"?: [...], "shapeless"?: true|false }, where paths is a required object with an include array of glob strings, shapes is an optional array of durable Shape ids to restrict coverage to (not shape names — a bare name is rejected as an invalid Shape id), shapesAll is an optional array of durable Shape ids that restricts coverage to things certified by all of the listed shapes, shapesNot is an optional array of durable Shape ids that excludes things certified by any of the listed shapes, and shapeless is an optional boolean to include things with no shape. The legacy compatibility form { "include": [...], "exclude"?: [...] } is also accepted. Note: field-scoped coverage is not available yet. --key is required. It is an issuer-scoped idempotency key — reuse the same value to retrieve the original grant instead of creating a duplicate. |
wh grant get <id> | Get details for a specific grant by its grant ID. Returns the grant whether it is active or revoked. |
wh grant list [--limit n] [--cursor c] | List active and revoked grants. Use --limit and --cursor to page through results manually. |
wh grant revoke <id> [--reason <text>] | Revoke a grant by its grant ID. Pass --reason to record why the grant was removed. |
Grant IDs are returned by wh grant create and appear in wh grant list output. Pass the ID to wh grant get or wh grant revoke.
# Grant a member things:read access covering all paths in the active repo (canonical form)wh grant create member alice@example.com --key grant-alice-read --op things:read \ --coverage '{"paths":{"include":["**"]}}'
# Grant a member things:read access scoped to a specific shape.# The shapes axis takes durable Shape ids, never the bare shape name.# Read one with `wh thing resolve hub/shapes/Location` and copy the value on its `durableId:` line.wh grant create member alice@example.com --key grant-alice-shape-read --op things:read \ --coverage '{"paths":{"include":["**"]},"shapes":["040SXA5RYM7Q4QXAGM8N6MXYVH5G37TDS2P16YQAG6D6M3VKT6YE8ZCW52H0"]}'
# Grant a PAT things:read and commits:read access scoped to a specific viewwh grant create pat my-ci-token --key grant-ci-read --op things:read --op commits:read --view my-view
# Grant a component shapes:read and subscriptions:read accesswh grant create component myorg/my-component --op shapes:read --op subscriptions:read \ --coverage '{"paths":{"include":["**"]}}' --key my-idempotency-key
# Inspect and manage grantswh grant listwh grant list --limit 20 --cursor <cursor>wh grant get <id>wh grant revoke <id>wh grant revoke <id> --reason "role change"For the full repo and org permission model — role to capability, scope to capability, and the minimum scope for a given task — see the Access reference.
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 accepts thing or assertion for add and revise operations. When --operation retract is used, --kind additionally accepts shape or collection. Passing one of the built-in collection shape names (Arc, Bond, Set, or List) as the <shape> argument produces the same ordinary write scaffold (name, shapes, data) as any other shape — no collection-specific payload is emitted. Pass --kind assertion to scaffold an assertion instead. --about supplies the assertion-target placeholder (used with --kind assertion) and accepts a shape or shaped-thing wref. |
wh shape template Hypothesiswh shape template Hypothesis --operation retractwh shape template Hypothesis Evidence --count 3 -o experiment.jsonwh shape template Hypothesis --kind assertion --about example# Scaffold a thing add/revise operation for a built-in collection shapewh shape template Arcwh shape template Bond --operation revisewh shape template Set --count 2 -o set-ops.jsonwh shape template Listinit — 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 |
wh doctor runs a set of environment and backend checks and reports their results. The checks cover:
- Environment — HTTP timeout configuration and API URL
- Repo and profile — default repo, active profile (including whether it came from
--profile,WH_PROFILE, or.wh), and repo accessibility. The profile check runs before any network probes, so it appears in the output even when the backend is down. - Backend connectivity and auth — authenticated identity (email plus server-validated scopes and token name for PATs). If the backend does not recognize your token — for example, when it was issued for a different API URL — the
authcheck reportsBackend returned unauthenticatedand includes guidance to verify the API URL. TheToken validation failed: <reason>message appears whenever the backend returns an error for your credentials (including component tokens and admin tokens, which are not valid for this context); the profile-specific orWH_TOKENreplacement hint is added only when the backend actively rejects the token as invalid. The auth check is skipped when the backend is unreachable. - Claude settings — validates that Claude settings files parse as valid JSON and reports any malformed files
- Claude hooks — checks whether the WarmHub hooks are installed
- AGENTS.md — checks whether AGENTS.md includes the WarmHub block
- Compatibility — CLI and backend version compatibility
Use --fix to auto-install missing hooks and to create or update AGENTS.md with the WarmHub harness block — this includes repairing the harness block in an existing AGENTS.md while preserving the rest of the file you have written.
wh doctorwh doctor --fixsub — Subscription management
Section titled “sub — Subscription management”See Subscriptions for concepts (filters, retry behavior) and Creating Subscriptions for detailed setup guides.
| Command | Description |
|---|---|
wh sub create <name> [--event commit] --filter '<json>' --webhook-url <url> [--on <shape>] [flags] | Create a commit-event webhook subscription. --event defaults to commit when omitted. --filter is required for commit subscriptions. Pair with --on to scope to a specific shape. |
wh sub create <name> --event <repo.renamed|thing.renamed|shape.renamed> --webhook-url <url> [flags] | Create a repo-scoped rename-event webhook subscription. |
wh sub create <name> --event <org.renamed|org.member_added|org.repo_created|org.repo_published|org.service_account_created|org.service_account_role_changed|org.service_account_disabled|org.service_account_enabled|org.service_account_deleted> --webhook-url <url> --org <org> [flags] | Create an org-scoped event subscription. --org is required for every org-scoped event type (org.renamed, org.member_added, org.repo_created, org.repo_published, and the five org.service_account_* events); --repo is not accepted for these events. |
wh sub create <name> --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 update <name> [--org <org>] [flags] | Update an existing subscription |
wh sub view <name> [--show-secrets] [--org <org>] | View subscription details. The webhook URL and fallback URL are redacted to <origin>/*** by default; --show-secrets reveals the raw URLs (that read is audit-logged). |
wh sub list [--limit n] [--org <org>] | List all subscriptions |
wh sub log <name> [--limit n] | Tail the delivery feed for a repo-scoped subscription (shows run status and, when your token has full-repository repo:read, failure details). wh sub log does not accept --org; org-scoped subscriptions cannot be tailed with it. |
wh sub attempts <runId> | Show attempt history for a delivery; free-form diagnostics (error message and response snippet) require full-repository read. |
wh sub pause <name> [--org <org>] | Pause a subscription |
wh sub resume <name> [--org <org>] | Resume a paused subscription |
wh sub delete <name> [--org <org>] | Remove a subscription. Delivery stops immediately. For operation subscriptions, the record is retired (name remains reserved and history is preserved) rather than deleted outright. See Managing Subscriptions for the full lifecycle, including how deletion behavior differs across subscription types. |
wh sub bind <name> --credentials <credentialSet> [--org <org>] | Bind a credential set for webhook auth |
wh sub unbind <name> [--org <org>] | Remove credential binding |
Subscription flags
| Flag | Description |
|---|---|
--event | Event type to subscribe to: commit, repo.renamed, org.renamed, org.member_added, org.repo_created, org.repo_published, org.service_account_created, org.service_account_role_changed, org.service_account_disabled, org.service_account_enabled, org.service_account_deleted, thing.renamed, or shape.renamed. Defaults to commit when omitted. |
--on | Target shape — required for --event commit webhook subscriptions; omit for rename-event subscriptions and shape lifecycle subscriptions whose filter is {"kind":"shape", ...} |
--filter | JSON filter expression. Required for --event commit subscriptions. |
--webhook-url | Destination URL for webhook deliveries |
--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 |
--org | Create or manage an org-scoped subscription instead of a repo-scoped one. Required when creating any org-scoped event subscription (org.renamed, org.member_added, org.repo_created, org.repo_published, and the five org.service_account_* events). --repo is not accepted for org-scoped event types. |
--allow-trace-reentry | Allow same-chain reentry (write-triggered only) |
Source flag
| Flag | Description |
|---|---|
--source | Watch a different repo in the same org; accepts repoName or orgName/repoName. |
--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.
--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.
# Create a commit-event webhook subscription (--filter is required for commit subscriptions)wh sub create sensor-hook --event commit --on Sensor --kind webhook \ --filter '{"shape":"Sensor"}' --webhook-url https://example.com/hook \ --fallback-webhook-url https://example.com/fallback
# Create a rename-event subscription — shown here for repo.renamed;# the same pattern applies to thing.renamed and shape.renamedwh sub create repo-rename-hook --event repo.renamed --kind webhook \ --webhook-url https://example.com/hook
# Create an org-scoped event subscription (--org is required for all org-scoped events).# The same shape applies to org.member_added, org.repo_created, org.repo_published,# and the org.service_account_* events.wh sub create org-rename-hook --event org.renamed --kind webhook \ --webhook-url https://example.com/hook --org myorg
# 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 sensors in another repo)wh sub create cross-hook --event commit --on Sensor --kind webhook \ --filter '{"shape":"Sensor"}' --source myorg/other-repo \ --webhook-url https://example.com/hook
# Allow same-trace reentry for a self-chaining webhook subscriptionwh sub create sensor-loop --event commit --on Echo --kind webhook \ --filter '{"shape":"Echo"}' --webhook-url https://example.com/hook \ --allow-trace-reentry
# Update a webhook subscriptionwh sub update sensor-hook --on Sensor \ --filter '{"shape":"Sensor"}' \ --webhook-url https://example.com/new-hook
# Clear a fallback webhook URLwh sub update sensor-hook --clear-fallback-webhook-url
# List and inspect (append --org myorg to any of these for org-scoped subscriptions)wh sub listwh sub view sensor-hook
# Tail delivery feed (shows run status, attempt count, and failure details when your token has full-repository repo:read)wh sub log sensor-hookwh sub log sensor-hook --live
# Drill into a specific run's attemptswh sub attempts 019d90f0-0000-7000-8000-000000000000
# Credential binding, pause/resume, and delete# Append --org <org> to manage org-scoped subscriptions with any of these commandswh sub bind sensor-hook --credentials webhook-keyswh sub unbind sensor-hookwh sub pause sensor-hookwh sub resume sensor-hookwh sub delete sensor-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 notifications. |
wh notifications returns repo-scoped notification records for terminal action-delivery failures (failed_terminal and dead_letter). Successful and suppressed runs do not produce a notification record and will not appear in the feed.
These commands are distinct from the web app’s cross-repo user notification feed.
Some personal access tokens are scoped to specific subscriptions — if yours is, results are filtered to only those subscriptions and you may see an empty result set if no notifications exist for them. See Personal Access Tokens for details.
--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 | Alias for wh auth status |
wh auth whoami --profile <name> | Alias for wh auth status --profile <name> |
The --profile (-P) flag selects which named auth profile to use. When it is omitted, the CLI resolves the profile in priority order — the --profile / -P flag, then the WH_PROFILE env var, then the profile recorded in the directory’s .wh file (written by wh use ... --profile ...) — and falls back to the default profile only when none of those is set. 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] [--committer-identity wref] | Create a new PAT |
wh token list [--all] | List your active tokens (interactive: all of them; PAT: only its own descendant subtree). Add --all to include expired and revoked tokens. |
wh token get --name <name> | View a token’s details |
wh token revoke --name <name> | Revoke a token |
Each token verb also takes --service-account <org>/<name> to act on a service account’s tokens instead of your own. That takes org:service-accounts-mint (granted by org:configure) on that org. create then requires --scope or --scopes-json with every scope naming an org or repository, and labels its output with the service account; get and list show who minted each token.
token commands run from an interactive session (which manages all your tokens) or under a PAT (which manages only the tokens it created, and tokens those created in turn); component (action) tokens are rejected. --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
# Role shorthand — expands to the role's permissions (role:owner|admin|editor|viewer)wh token create --name ci-bot --scope myorg/myrepo=role:editor
# 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
# Bind a default committer identity (stamped on writes unless --committer overrides per call)# Note: if this identity is later removed or becomes unavailable, writes under this token# will fail with NOT_FOUND — reconfigure the PAT before writing in that case.wh token create --name ci-bot --committer-identity wh:warmhub/users/<id>
# Full access (no --scope flag)wh token create --name full-access
# Restricted scopes: (paths, Shapes) pairs in grants (--scopes-json, mutually exclusive with --scope)wh token create --name scoped-bot --scopes-json '[ {"resource":"myorg/myrepo","permissions":["repo:read"],"grants":[{"allowedMatches":["sensors/**"],"allowedShapes":{"shapes":["Sensor"]}}]}]'
wh token listwh token get --name ci-botwh token revoke --name ci-bot
# A service account's token (needs org:service-accounts-mint on acme)wh token create --service-account acme/hub-x --name hub-x-toolset \ --scope acme/markets=repo:read --expires 90dwh token list --service-account acme/hub-xwh token revoke --service-account acme/hub-x --name hub-x-toolsetSee 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, but values are write-only — once set, they can’t be read back through the CLI.
| 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 stdin (recommended) or --value. Warning: --value exposes the secret in your shell history — prefer piping via stdin. When the value is piped via stdin, the CLI strips one final newline and otherwise preserves all whitespace. |
wh credential set <name> [--repo org/repo | --org org] | Bulk add or update keys. Reads a JSON object from stdin, e.g. {"KEY":"val"}. When the JSON is piped via stdin, the CLI strips one final newline and otherwise preserves all whitespace. |
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 stops bound webhook deliveries entirely. |
wh credential delete <name> [--repo org/repo | --org org] | Delete a credential set. Also removes any subscription credential bindings for this set — subscriptions that were using it will lose their auth binding on future deliveries. |
component — Component management
Section titled “component — Component management”| Command | Description |
|---|---|
wh component init <name> | Scaffold a new component repository |
wh component install warmhub/identity --repo org/repo | Install WarmHub’s first-party Identity component. First-party components are installed by <org>/<name> like any other registered component — there is no separate install-by-id path. |
wh component install <org/name> [--submission-id <uuid>] | Install a registered component identity. Installs the latest published version of the component and applies its declared resources and setup steps. Only registered <org>/<name> refs are installable; register your own component first with wh component register. Pass --submission-id to supply a caller-managed UUID. Reuse it to recover a run whose outcome you could not tell; it does not make re-running setup safe. A fresh resource-phase failure rolls back and throws CONFLICT (CLI exit 2, including JSON/JSONL error output). A preflight ownership denial exits with auth code 5 before resource writes. A replay of a historical degraded result still exits 1 and returns its errors[]. |
wh component list [--limit n] [--cursor c] [--all] | List installed components. Paginated: pass --all to auto-fetch every page, or --limit / --cursor to page manually. |
wh component search <query> [--limit n] [--cursor c] [--all] | Search registered components across all orgs visible to you (public, plus private components you can read) by name, description, and manifest vocabulary (shape names, CLI method names, and subscriptions). Without pagination flags, only the first page of results is returned and the CLI prints a notice if more pages exist — pass --all to fetch every page automatically, or use --limit / --cursor to step through pages manually. Always cross-org — to list components installed in a repo, use wh component list --repo. |
wh component view <org/name> | Show component details |
wh component exec <component> [<method>] [method flags] --repo org/repo | Invoke a CLI method an installed component exposes (the methods declared in its manifest cli block). Method-specific flags are validated against the installed manifest snapshot. Shorthand: wh <component> <method>. Omit <method> and pass --help to list the methods a component has installed without invoking any method: wh component exec <component> --help --repo org/repo. |
wh component validate <path> | Validate a component package without installing |
wh component update <org/name> [--submission-id <uuid>] | Re-resolve a registered component’s latest published manifest and replay setup. Any WarmHub-issued tokens the component uses are reissued: a setup token if the component was registered to receive one, and a runtime token if its manifest declares runtimeAccess. A component that uses neither has no tokens to rotate. Shape names that the new manifest no longer declares are released so those names become available for reuse. --submission-id carries the same replay behavior described under wh component install. Fresh resource-phase CONFLICT errors roll back and exit 2; preflight ownership denials exit with auth code 5, and historical degraded replays exit 1. |
wh component doctor <org/name> | Run health checks on an installed component. Exits 1 when the computed health is degraded, error, or uninstalled, and 0 for ready or paused, in every output format. Authentication failures resolving the install or probing non-credential resources use auth exit 5; inaccessible credential sets remain non-gating warnings. |
wh component teardown <org/name> [--submission-id <uuid>] | Pause subscriptions, revoke the install’s tokens, release every shape name owned by the component so those names become available for reuse, and mark the component uninstalled (terminal but non-destructive — data is preserved). The CLI prints a released N shape(s) line when shape names are freed. --submission-id carries the same repository-mutation replay guarantee described under wh component install. |
wh component register <name> --org <org> --manifest <path> [--show-secrets] [flags] | Register a component identity in an org from a local manifest file so it can be installed via install <org/name>. --manifest is required; --source-url is optional documentation metadata. --show-secrets reveals the raw lifecycle URLs in the echoed entry (redacted by default). |
wh component unregister <org/name> | Remove a registered component identity |
wh component registry list --org <org> [--show-secrets] | List registered components in an org. Pretty output shows org/name, visibility, and description only — lifecycle URLs do not appear in the human-readable list display. --show-secrets reveals raw lifecycle URLs in JSON/JSONL output (--format json or --format jsonl). |
wh component registry view <org/name> [--show-secrets] | View one registered component |
wh component registry update <org/name> [--show-secrets] [flags] | Update a registered component. Lifecycle URLs are redacted to <origin>/*** in the output by default; pass --show-secrets to reveal the raw URLs. |
Registered-component lifecycle URLs (setupUrl, uninstallUrl) are redacted to <origin>/*** in register, registry view, and registry update output by default; pass --show-secrets to reveal the raw URLs. For registry list, lifecycle URLs are not included in the human-readable display at all; --show-secrets applies only when using structured output (--format json or --format jsonl).
Components are declarative packages that bundle shapes, subscriptions, credentials, and seed data into a single installable unit. Each component is addressed by its registered <org>/<name> ref and owns the resources it creates — external writes to component-owned shapes, things, and assertions are blocked.
component exec — Flag disambiguation with --
Section titled “component exec — Flag disambiguation with --”wh component exec accepts method flags anywhere after the method name. The observable rule is straightforward: global flags (--repo, --profile, --json, and similar) are read before --. If a method flag shares a name with a global flag, place it after -- so the CLI reads it as a method argument rather than a global flag. Method flags with names that don’t collide with any global flag can be passed anywhere after the method name — no -- needed.
To list the methods a component has installed without invoking any method, omit <method> and pass --help:
# Discover installed methods for a component without invoking any methodwh component exec veritas --help --repo acme/world# The first positional is the installed component name (not a repo); the# install repo is supplied separately via --repo.# --repo before -- sets the install repo (global flag).# --repo after -- is passed as a method arg.wh component exec veritas sync --repo acme/world -- --repo @upstream
# Without --, the second --repo is parsed as a global flag, not as a method arg.# Whether this produces an error depends on the colliding method arg: a required# arg raises "Missing required argument"; an optional arg with a declared default# resolves to that default; an optional arg with no default stays unset.wh component exec veritas sync --repo acme/world --repo @upstreamThe -- terminator is only required when a method arg name collides with a global flag. Method args with unique names — such as --name or --limit — can be passed anywhere after the method name without --.
wh component init my-componentwh component register veritas --org warmhub --manifest ./warmhub/manifest.jsonwh component install warmhub/veritas --repo myorg/worldwh component registry list --org warmhubwh component search reputationwh component list --repo myorg/worldwh component view myorg/my-component --repo myorg/worldwh component validate ./local-componentwh component doctor myorg/my-component --repo myorg/worldwh component teardown myorg/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.
onboard — Agentic onboarding script
Section titled “onboard — Agentic onboarding script”| Command | Description |
|---|---|
wh onboard [--agent <name>] [--json] | Print an agentic onboarding script that walks a new user through connecting to WarmHub. Omit --agent for the model-agnostic script, or target a coding agent (claude, codex, cursor, gemini) to prepend that agent’s kickoff + MCP-config header. |
wh onboardwh onboard --agent claudewh onboard --agent gemini --jsonupdate — Update the CLI
Section titled “update — Update the CLI”| Command | Description |
|---|---|
wh update [--check] [-y|--yes] | Check for and install the latest @warmhub/cli. --check prints status without installing; -y / --yes skips the confirmation prompt. Rejects --format json/jsonl (install is mutating and interactive — use --check for read-only status). |
wh updatewh update --checkwh update --yeschannel — 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. At most 16 unique repositories may be watched per channel — passing more than 16 distinct repos causes the channel to exit at startup with a USER_INPUT error: Channel can watch at most 16 unique repositories. 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 alice, round-1 (shapes: Player, Score)When an action run status changes:
Action updated in acme/world — affected 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