claudewheel v0.24.2 /claudewheel.clients
Edit
On this page

Adapter registry mapping resolved launch selections to a client-specific argv, targeting the official claude binary or the miniclaude REPL client.

#claudewheel.clients

#claudewheel.clients

Client adapters: map resolved launch inputs to a client-specific argv.

claudewheel can launch different Claude-compatible clients. Each client is an "adapter" -- a function that turns the shared launch context (resolved binary inputs, model id, selections, session flags) into the concrete argv handed to os.execvpe. The seam lets claudewheel target the official claude binary or an alternative client like miniclaude without special-casing launch.py.

Adapters:

  • claude: the official Claude Code CLI. Preserves claudewheel's historical

argv exactly (default_flags + strict-mcp + permission + model + --disallowedTools + session/passthrough flags), with the binary chosen by the :class:~claudewheel.binaries.BinaryLocator.

  • miniclaude: the miniclaude REPL client. Builds miniclaude repl with a

mapped permission mode and mapped session flags. By-definition claude-only inputs that simply do not apply are ignored without error: the version selection (it names a claudewheel-managed claude binary, which a non-claude client never execs), the mcp selection ("strict" maps to claude's --strict-mcp-config; miniclaude has no equivalent), config.default_flags (raw claude CLI flags), and the DISALLOWED_TOOLS constant. Ignoring version/mcp here is what lets a plain --client miniclaude succeed even when a claude-only value is remembered in last_config or set as a config default; a contradictory, same-invocation explicit override (-s version=... / -s mcp=strict together with a non-claude --client) is rejected upstream in the CLI, not here.

Every hard error raised here is a :class:ValueError so the CLI launch sequence catches it and prints a clean, actionable message instead of a traceback.

#ClientContext

Everything an adapter needs to build a client argv.

:func:claudewheel.launch.resolve_launch_config assembles the shared pieces (profile env, cwd, gh token, resolved model id) and hands the rest to the selected adapter.

extra_flags is the claude-form session flags followed by the passthrough tail (session flags + passthrough); passthrough is that same tail on its own. The claude adapter appends extra_flags verbatim; the miniclaude adapter uses :meth:session_flags (the prefix with the passthrough tail removed) and rejects any passthrough outright.

#session_flags

python
def session_flags(self) -> list[str]

The session-flag prefix of extra_flags with the passthrough tail removed.

#build_claude_argv

python
def build_claude_argv(ctx: ClientContext) -> list[str]

Build the argv for the official claude CLI.

Byte-for-byte identical to claudewheel's historical argv assembly: [binary] + default_flags + strict-mcp + permission + model + --disallowedTools + extra_flags. The binary is the selected version's on-disk path (a missing version is an :class:OSError) or the locator's fallback symlink when no version is selected.

#build_miniclaude_argv

python
def build_miniclaude_argv(ctx: ClientContext) -> list[str]

Build the argv for the miniclaude REPL client.

Shape: [binary, "repl", "--profile", <profile>, "--model", <model id>, "--permission-mode", <mapped>] + <session flags>. --model and --permission-mode are included only when the corresponding selection is present. A missing profile, an unsupported session flag, and passthrough args are all HARD ERRORS -- never silent drops.

version and mcp selections are IGNORED, not rejected: a version names a claudewheel-managed claude binary and mcp "strict" maps to claude's --strict-mcp-config, so for a non-claude client they are by-definition-inapplicable inputs on the same footing as default_flags and DISALLOWED_TOOLS. This is what lets --client miniclaude succeed when such a value is merely remembered/configured. A contradictory explicit override passed in the same invocation is rejected upstream in the CLI (claudewheel.cli), where the selection's provenance is known.

#_miniclaude_session_flags

python
def _miniclaude_session_flags(ctx: ClientContext) -> list[str]

Translate claude-form session flags into miniclaude equivalents.

--continue -> --continue-session; --resume <id> -> --resume <id>. A bare --resume (claude's session picker), --print/-p, and any passthrough args are HARD ERRORS: miniclaude has no session picker, no print mode, and no generic passthrough.

#resolve_default_client

python
def resolve_default_client(config: dict[str, Any]) -> str

Return the configured default_client, validated against the registry.

Reads config["default_client"] (falling back to :data:DEFAULT_CLIENT when the key is absent). An unknown value is a HARD ERROR (:class:ValueError) -- never a silent fallback to "claude" -- so a typo in config.json fails loudly instead of quietly launching the wrong client.

#client_available

python
def client_available(name: str, locator: BinaryLocator, clients_config: dict[str, Any]) -> bool

Report whether name's launch binary is resolvable right now.

Mirrors each adapter's own binary resolution so the picker's availability marking matches what an actual launch would find:

  • claude: the :class:~claudewheel.binaries.BinaryLocator fallback

symlink (what build_claude_argv execs when no version is selected).

  • miniclaude: the configured clients.miniclaude.binary or a PATH

miniclaude (what build_miniclaude_argv resolves).

A client with no known probe is reported available (True): we cannot prove it missing, so we never mislabel a freshly added adapter as "not installed".

#build_client_choices

python
def build_client_choices(locator: BinaryLocator, clients_config: dict[str, Any], default_client: str) -> tuple[list[tuple[str, str]], str]

Build the (options, initial_key) pair for the client-selection step.

Options are the :data:CLIENT_ADAPTERS registry entries in registry order ("claude" first), as (key, label) pairs for :func:claudewheel.ui.run_selection. The key is always the bare client name; unavailable clients get a " (not installed)" label suffix rather than being hidden -- selecting one still launches and fails with the adapter's own hard-error message. initial_key is default_client, so the cursor starts on the configured default.

#resolve_client

python
def resolve_client(explicit_client: str | None, prompt: Callable[[], str | None]) -> str | None

Resolve the launch client: explicit CLI flag wins, else prompt.

explicit_client is the --client value when the user passed it, or None when they did not. When it is set, it is returned verbatim and prompt is NOT called (explicit wins, the TUI step is skipped). Otherwise prompt (a zero-arg callable that runs the interactive picker) is invoked and its result returned -- a client name, or None if the user cancelled.

Search