CLI and config

CLI

After install, yoke is on PATH and always runs the main checkout's installed packages (not a linked worktree's source), unless you re-point the editable install for source-dev.

Common operators:

yoke status
yoke onboard
yoke ui up
yoke items get PREFIX-N
yoke items get PREFIX-N body
yoke board rebuild --print-only
yoke doctor

Harness skills (/yoke do, /yoke idea, …) call the same function-call surface; CLI adapters are the operator/debug shape. Prefer yoke <subcommand> --help for flags.

Machine config

~/.yoke/config.json holds machine-local connections and tunables: which environment is active, API URLs, local paths. Secrets for capabilities live under ~/.yoke/secrets/ (not in the repo).

Launch defaults retain the scalar preferred_session_models map that the previous release can read. Context stays encoded in each native model selector, while effort lives in the additive preferred_session_reasoning_efforts map:

{
  "preferred_session_models": {
    "claude-cli": "claude-opus-4-8[1m]",
    "codex-cli": "gpt-5.6-sol"
  },
  "preferred_session_reasoning_efforts": {
    "claude-cli": "max",
    "codex-cli": "xhigh"
  }
}

Both maps are machine-local and travel on that machine's relay heartbeat. After placement, each knob resolves independently: its explicit launch flag > the chosen machine's advertised value > the vendor default. The caller's map never decides a launch running elsewhere. Preview shows the raw request and the effective selection with the machine setting that supplied each default; the launch record retains both, and the bound session shows the effective ask beside provider-attested served facts.

A fresh installer/onboard write seeds both keys with every launchable harness surface. Blank model or effort means unset. Validation rejects non-string entries, invalid selectors, unsupported effort values, and combinations the named CLI cannot encode. Existing machine files are not rewritten during rollout. yoke status and --list-models describe this machine's maps; preview a launch to see another machine's effective defaults.

session_model_routing is the separate, optional key that says which model each tier of work asks for on a surface. It is a policy about work rather than a fact about a provider account, so it is read from the machine composing the launch — unlike the two default maps above, which come from the machine that will run it:

{
  "session_model_routing": {
    "cursor-cli": {
      "tier1": "cursor-grok-4.6-high",
      "tier2": "cursor-grok-4.6-medium",
      "excluded": ["cursor-auto"],
      "fallbacks": ["claude-opus-5-thinking-high"]
    },
    "claude-cli": {
      "tier1": "claude-opus-5",
      "tier2": "claude-sonnet-5",
      "worker_tier": "tier2"
    },
    "codex-cli": {
      "tier1": "gpt-6-astra",
      "tier2": "gpt-5.6-terra",
      "worker_tier": "tier2"
    }
  }
}

tier1 is the model for demanding, ambiguous, or high-consequence work; tier2 is for bounded work a cheaper model already handles. excluded models are never launched. A fallbacks entry is reachable only when the preferred model's own billing pool is confirmed empty — an unreadable meter, a low headroom reading, or room in a different pool is not confirmation, so a fallback never starts spending a separate allowance by accident. Every key is optional and a surface with no entry keeps the defaults above.

worker_tier is how a surface reserves its tier-1 model: ordinary work placed on that surface routes to the named tier whatever the work kind asked for, while the steering seat still takes tier1, as does a launch that names its model explicitly. The example above reserves the Claude and Codex tier-1 models for steering or an explicit instruction and routes ordinary work to tier2, and leaves the Cursor surface unreserved so its tier-1 model is the ordinary worker there, with the Claude fallback reachable only on confirmed pool exhaustion. That split is a machine-local operator choice, not a Yoke default: a surface with no worker_tier routes every work kind to the tier that kind asks for.

yoke session-control launch preview --model M reports each machine's quota in the pool M actually bills to, under REQUESTED MODEL POOL, and ranks machines by that pool's meter rather than by whichever window reads lowest.

yoke session-control launch preview and create accept the three flags. --context-window accepts a token count or compact form such as 1m. --list-models --surface SURFACE prints this machine's configured defaults beside its observed native availability (below), plus accepted effort and context values. Claude maps context 1M to the model's [1m] selector and effort to --effort; Codex maps effort to -c model_reasoning_effort=... and refuses explicit context. Cursor passes an exact advertised selector, such as cursor-grok-4.6-high; a separate matching effort is encoded once, and a conflicting effort refuses. A base name plus effort resolves only to an advertised variant. Cursor context is model-specific: an explicit window requires that exact variant's native display label to name it. Grok has no advertised 1M window; omit the context flag. A label establishes a selectable option, never a served-session measurement, so unattested context stays unknown. Preview and create use the chosen machine's native observations for these checks. An unsupported combination is a preview refusal named for the harness and knob, with a recovery step. A combination the provider rejects at run time fails as model_combo_unsupported, retains a bounded vendor message in launch evidence, and never retries under defaults.

Selectable models are observed, not declared

Which models a surface will accept is a fact about the account and build on one machine, so Yoke reads it from the surface rather than shipping a list. Each relay poll refreshes a per-surface reading and carries it on the heartbeat, so a model an account gains becomes visible to the fleet within about a minute rather than within a release.

Discovery is per surface, and a surface with no adapter says so:

Surface Route
cursor-cli cursor-agent --list-models
codex-cli codex app-server model/list
every other surface none declared; the reading says so by name

Shipping one vendor's CLI adapter proves nothing about the same vendor's desktop app or editor extension, so every known surface gets its own reading rather than inheriting a sibling's.

Each reading names its own status:

Status Meaning
ok the surface answered just now; models is current
stale the last attempt failed; models is what it last published
unsupported Yoke declares no listing adapter for this surface
unknown the surface has never answered

Only ok and stale carry models. Neither unknown nor unsupported is evidence that a model is unavailable — a caller routing work reads the status before the list. A failed probe never empties a list that was populated: it keeps the models with their original observation time and flips the status to stale, because an unreachable surface has not withdrawn anything.

Each model entry carries what its vendor published: the selectable token, a display description, that model's own reasoning options and default, and any replacement target with its retirement time. Reasoning options are per model rather than per surface — one installed app-server offers ultra on its newest model and not on the one beside it.

Availability carries no dependency on researched pricing or tier data. A model this machine can select today is reported today, whether or not anything else is known about it.

Read it three ways:

yoke relay probe-models [--surface S] [--json]   # refresh this machine now
yoke session-control launch --list-models        # defaults beside availability
yoke steering report get                         # every machine in the scope

The relay refreshes on its own cadence during normal polling; yoke relay probe-models is the bounded refresh for when you need the answer before the next poll.

Launched sessions run unattended

A session the launch plane starts is an autonomous worker with nobody watching its terminal, so every launch and every wake engages the harness permission bypass: Claude Code is launched with --dangerously-skip-permissions, Codex CLI with approvals, sandbox, and hook trust bypassed (--dangerously-bypass-approvals-and-sandbox plus --dangerously-bypass-hook-trust; the app-server route uses equivalent thread parameters where available), and Cursor with --force alongside --trust. This is unconditional for launched sessions and changes nothing about a session you start yourself.

One native gate can still refuse: Claude Code declines a bypassed background launch until the machine has accepted the bypass disclaimer once. The launch reports permission_bypass_unaccepted with the recovery step — run claude --dangerously-skip-permissions interactively on that machine, accept the prompt, then retry.

Connections

A connection is how this machine reaches a universe:

  • local-postgres — in-process against a local DB
  • https — relay to Cloud or self-hosted API

yoke status shows which connection is active.

Local UI

yoke ui up       # start it detached, print the tokened URL
yoke ui          # same as `yoke ui status` — running or stopped, plus the URL
yoke ui down     # stop it

Serves the same universe workbench used on Cloud, against your active connection (typically http://127.0.0.1:8688). yoke ui up runs the server as a machine daemon rather than a terminal job: closing the window leaves it serving, and on macOS a launch agent brings it back after a reboot until you run yoke ui down.

The URL carries a session token — treat it like a password. The token is stable per machine, so the URL you bookmark keeps working across up/down cycles. The server binds loopback only and refuses remote-facing hosts.

Project-local .yoke/

Installed into each managed repo: skills, agent adapters, hooks, policy, and .yoke/docs (this public corpus). Board markdown under .yoke/BOARD.md is a generated view — do not hand-edit it as source of truth.

CLI and config

CLI and config · Yoke