Metadata-Version: 2.5
Name: claudewheel
Version: 0.25.0
Summary: TUI launcher for Claude Code
Project-URL: Homepage, https://github.com/smm-h/claudewheel
Project-URL: Repository, https://github.com/smm-h/claudewheel
Author-email: smm-h <smmh72@gmail.com>
License-Expression: MIT
License-File: LICENSE
Keywords: claude,claude-code,config,launcher,rlsbl,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: User Interfaces
Requires-Python: >=3.11
Requires-Dist: strictcli>=0.39.0
Description-Content-Type: text/markdown

<!-- Auto-generated by selfdoc from docs/_README.md — do not edit -->

<p align="center">
  <img src="assets/banner.png" alt="claudewheel" width="700">
</p>

TUI launcher for Claude Code with profile, GitHub account, version, model, directory, MCP, and permission switching.

## Installation

Install from PyPI:

```bash
pipx install claudewheel
```

Or with `uv`:

```bash
uv tool install claudewheel
```

Requires Python 3.11+.

### Upgrading from the Node package

Early versions of claudewheel were distributed as an npm package (`npm install -g claudewheel`). The Node wrapper is deprecated -- it only exists as a thin shim that calls `python3 -m claudewheel`. Install the Python package directly instead:

```bash
npm uninstall -g claudewheel    # remove the old Node wrapper
pipx install claudewheel        # install the Python package
```

If you have the old Node binary at `/opt/homebrew/bin/claudewheel` or a similar npm global path, removing the npm package will clean it up.

## Quick start

```bash
claudewheel         # launch the TUI
claudewheel --help  # show all flags
```

The first run creates `~/.claudewheel/` populated with defaults (config, segments, options, themes).

## The segment bar

The TUI is a single horizontal "segment bar" rendered at the vertical centre of the terminal. Each segment is a labelled cell whose value can be cycled, searched, or freely edited. Above and below the focused segment, a vertical "fan-out" shows the other available options dimmed in the segment's accent colour. Pressing Enter on any segment launches Claude Code with the current selections.

Keys:

- Left / Right -- move focus between segments (also exits freeform edit mode)
- Up / Down -- cycle the focused segment's value (blank state `---` is part of the ring)
- Type characters -- start fuzzy search (on `searchable` segments) or freeform edit (on `freeform` segments)
- Tab -- accept the current fuzzy match and advance to the next segment
- Backspace -- delete a search/edit character (on a non-empty selected value, starts edit mode)
- Esc -- cancel the in-progress search or edit
- `S` (uppercase) -- open the sessions overview for the selected profile (see below)
- Enter -- launch
- q or Ctrl-C -- quit without launching

Search shows the matched characters in the search-match colour. The search buffer turns red when no option matches.

An uppercase `S` typed with nothing in the search buffer no longer seeds a fuzzy search -- it opens the sessions overview. Lowercase `s` still searches, and once a search is in progress `S` is an ordinary character again.

## The sessions overview

Press uppercase `S` from anywhere on the bar to list every Claude Code session registered under the *selected profile* -- name, working directory, uptime, resident memory, and the session you are sitting in marked. The focused row expands to show its pid, session id and Claude Code version.

It is a snapshot, not a live monitor: the registry is read when the screen opens and nothing refreshes under the cursor.

- Up / Down -- move the focus (clamped, never wrapping)
- `r` -- re-read the registry into a new snapshot
- `p` -- prune: delete the registry files of the sessions whose processes are provably gone. Liveness and file identity are both re-checked at that moment, so a session that started while the screen was open keeps its file
- `q` or Esc -- close and return to the segment bar

## Client selection

Before the segment bar, the interactive launcher shows a **Client** step: choose which client to launch --- `claude` (the official Claude Code CLI) or `miniclaude` (the miniclaude REPL). The cursor starts on the `default_client` configured in `config.json` (default: `claude`). A client whose binary is not installed is shown with a `(not installed)` suffix rather than hidden; selecting it still launches and fails with a clear message.

Pass `--client <name>` to skip the step and choose explicitly; non-interactive launches (e.g. `-p`) use `default_client` without prompting. When the selected client is not `claude`, the version step is skipped --- the version selects a claudewheel-managed *claude* binary, which does not apply to other clients.

## Narrow terminals

When the segment bar is wider than the terminal, the renderer switches to a scrolling viewport:

- The focused segment is centered horizontally
- Edge arrows (`<2`, `3>`) show how many segments are off-screen in each direction
- A minimap in the top-right corner shows all segments as small colored squares; the focused one has an opaque background highlight
- Partially visible segments at the viewport edges are clipped rather than wrapped

The viewport activates automatically and deactivates when the terminal is resized wider. All rendering is identical to the non-scrolling case when the bar fits.

## Segment types

| Key           | Label   | Controls                                                                       |
|---------------|---------|--------------------------------------------------------------------------------|
| `profile`     | Profile | Maps to `CLAUDE_CONFIG_DIR` (e.g. `~/.claude-personal`)                        |
| `github`      | GH      | Selects the GitHub account; `gh auth token --user <acct>` exported as `GH_TOKEN` |
| `version`     | Ver     | Picks the Claude Code binary in `~/.local/share/claude/versions/`              |
| `model`       | Model   | Passes the model id as `--model`; an Opus/Sonnet `[1m]` suffix selects 1M-context |
| `directory`   | Dir     | Working directory to `cd` into before launch                                   |
| `mcp`         | MCP     | MCP profile mode (`default`, `strict`)                                         |
| `permissions` | Perms   | Permission mode passed to Claude Code (`bypass`, `default`, `plan`, `auto`)    |

Profile, GitHub, and Model are *creatable*: their option lists end with a `+` sentinel that prompts for a new value and persists it to `options.json`. Directory is *freeform*: you can type any path. Version pulls a live npm listing merged with the locally installed binaries.

## Commands

| Command | Description |
| --- | --- |
| `health` | run diagnostic health checks on profiles, tokens, and hooks, then exit |
| `config` | open the ~/.claudewheel/ config directory in your $EDITOR |
| `versions` | list all installed Claude Code versions, marking the current symlink target |
| `install` | download and install a specific Claude Code version |
| `uninstall` | delete an installed Claude Code version binary from the versions directory |
| `reset-options` | delete options.json so it regenerates from defaults |
| `show` | print a summary of current segment selections, theme, and recent directories |
| `migrate` | move session data files from one profile to another, optionally filtered by UUID |
| `stats` | report shared-store stats and clean up legacy data |
| `mv` | rename a project directory and migrate session data |
| `import` | import session data from an external Claude Code directory |
| `deploy-hooks` | deploy built-in hook scripts to the ~/.claudewheel/scripts/ directory |
| `patch-profiles` | reconcile every managed profile and shared-settings.json to EXACTLY the canonical guardrail model (hooks, disallowedTools, permissions deny/ask); prunes drift and user-added extras -- the old additive, extras-preserving behavior is gone. Deploys any missing guardrail hook scripts. The 'default' profile (~/.claude) is never touched. Preview with --dry-run; writing needs a terminal or --approve-consequential. |
| `reconcile-permissions` | reconcile every managed profile and shared-settings.json to EXACTLY the canonical guardrail model (hooks, disallowedTools, permissions deny/ask made exact; allow keeps only its non-conflicting entries); prunes all drift and user-added extras. The 'default' profile (~/.claude) is never touched. Pass --dry-run to preview the per-target diff without writing; writing needs a terminal to confirm at, or --approve-consequential. |
| `purge-plugins` | remove the Claude Code plugin tree from the selected profiles: the official-marketplace clone and every plugin installed from it, six to ten megabytes per profile. Opt-in and separate from the canonical reconciliation, which is exact and would otherwise delete plugin state on every run. Names the marketplaces and plugins it finds before removing them; --dry-run reports the inventory without touching anything. New launches do not collect a new tree -- the launch environment suppresses the auto-install, one-way per profile. The 'default' profile (~/.claude) is never touched. |
| `launch` | start the interactive TUI launcher to select a profile, model, and directory |
| **profile** | create, inspect, rename, delete, and manage Claude Code profiles and their stored tokens |
| `profile create` | run the create-profile wizard in one continuous alt-screen session: prompt for the profile name, config directory and launch options, write the profile directory together with its symlinks into the shared store, then drive an interactive Claude Code OAuth login so the profile is authenticated before you leave. Requires a real terminal, and prints the summary and auth outcome afterwards |
| `profile delete` | remove a registered profile for good: unlink its shared-store symlinks, delete the real entries in its directory (its stored token among them), drop its options.json registration, and clear any last_config reference in state.json. Refuses a profile holding a live interactive Claude Code session unless --force-delete (background jobs and daemons do not block it), and takes conversation history only with --force-delete-data |
| `profile show` | print a detailed report for one profile: whether its directory exists on disk, whether it is registered or pinned in options.json, the state of its stored token, its resolved configuration and the session data it holds. Inspects default (~/.claude) like any other profile, and exits non-zero when the name matches no directory, registration or token |
| `profile rename` | move a profile to a new name, taking its directory (with the token stored inside it), its options.json registration and its session data with it. Validates that the old name exists, that the new one is free in both the directory tree and the options file, and that it fits the lowercase-letters-digits-hyphens charset. Refuses a profile holding a live interactive Claude Code session, and the reserved name default |
| `profile fix-auth` | repair one profile's authentication: strip the session credentials that shadow its stored long-lived token so the token is used again. Says so plainly when there is nothing to repair, and refuses a name with no profile directory behind it |
| `profile set-plan` | declare which plan a profile's Claude account is on, without a prompt. Claude Code resolves its subscription tier from the launch environment and only from there when auth is a stored setup token, so an undeclared profile launches with the tier null and tier-dependent features failing closed. Writes both plan fields into the profile's token entry, leaving the token itself alone; the interactive picker in the create flow and the pre-launch prompt write exactly the same thing |
| `profile check-tokens` | read every discovered profile's own stored OAuth token and validate each one against the Anthropic API, then print a table of profile name, status and a truncated token preview. The status distinguishes a valid token from an invalid one, an unreachable API and an indeterminate answer, and profiles holding no token are listed too |
| **permission** | add, remove, and list permission rules across Claude profiles |
| `permission add` | Add a permission rule to a profile's settings.json. Takes a category (allow, deny, or ask) and a rule string such as Bash or Read(//home/**). Writes the rule into the specified category array. Use --profile to target a single profile or --all-profiles to apply the rule across every registered profile. Skips duplicates if the rule already exists in the category. |
| `permission remove` | Remove a permission rule from a profile's settings.json. Takes a category (allow, deny, or ask) and the exact rule string to delete. The rule is removed from the specified category array and the file is saved. Use --profile to target a single profile or --all-profiles to remove the rule from every registered profile. Reports whether the rule was found. |
| `permission list` | List permission rules from a profile's settings.json. Displays rules in grouped, flat, or JSON format controlled by --format. Use --category to filter output to a single category (allow, deny, or ask). Use --profile to inspect a single profile or --all-profiles to show rules from every registered profile, with each profile's rules displayed under a header. |

### Segment overrides

Every enabled segment gets its own `--<key>` flag. These pre-fill the TUI:

```bash
c --profile myprofile --github myhandle
c --directory ~/Projects/foo --model claude-opus-4-7
```

If the override set covers every *required* segment, the TUI is skipped entirely and Claude Code launches directly.

### Session passthrough

Mutually exclusive flags forwarded to Claude Code:

```bash
c -c                       # --continue: resume the most recent session
c -r                       # --resume: open Claude Code's session picker
c -r 0123abcd              # --resume <id>: jump to a specific session
c -p "summarize this repo" # --print: non-interactive print mode
```

These compose with segment overrides: `c --profile personal -r` opens the picker against the personal profile.

Print mode (`-p`) skips the TUI and launches Claude Code non-interactively. Extra flags after `--` are passed through:

```bash
c -p "explain auth.py" -- --output-format json --allowedTools "Read,Bash"
```

## Config directory

`~/.claudewheel/` layout:

| Path             | Purpose                                                     | Auto-written?     |
|------------------|-------------------------------------------------------------|-------------------|
| `config.json`    | Theme, enabled segments, default flags, health-check switch, minimap mode | No (user-edited)  |
| `segments.json`  | Segment definitions (label, width, wrap, searchable, etc.) | No                |
| `options.json`   | Values, metadata, and discovery configs per segment         | Only via `+` UX   |
| `state.json`     | `last_config`, `recent_dirs`, `launch_count`, npm cache    | Yes, every launch |
| `themes/*.json`  | Colour schemes (`dark.json`, `light.json` ship by default)  | No                |
| `hooks/*`        | Executable scripts -- see below                             | No                |

Defaults are regenerated on first run if any file is missing.

On startup, missing keys from the current defaults are merged into existing files (config, segments, themes) without overwriting user values. Schema-versioned migrations handle value changes that must be applied once (e.g. correcting a default).

## Hooks

Drop an executable script into `~/.claudewheel/hooks/` whose name starts with `pre-launch` (e.g. `pre-launch-token-refresh`). It runs immediately before `exec`, with the chosen segment values exported as `CL_<KEY>` environment variables:

```bash
#!/usr/bin/env bash
# ~/.claudewheel/hooks/pre-launch-warn-work
if [[ "$CL_PROFILE" == "work" && "$CL_DIRECTORY" == "$HOME/Projects/personal-thing" ]]; then
    echo "Refusing to use the work profile on a personal project." >&2
    exit 1
fi
```

A nonzero exit aborts the launch (and prevents `launch_count` from being incremented). Hooks have a 10-second timeout.

## Adding new options

- **Profile / GitHub / Model**: cycle the segment to its `+` sentinel, press Enter, type the new value. It is appended to `options.json` under the segment's `values` list and selected.
- **Direct edit**: open `~/.claudewheel/options.json` and add to the relevant segment's `values` array. For profiles you also need a `metadata.<name>.config_dir` entry.
- **Install a Claude Code version**: run `c --install <version>` or pick a not-yet-installed version in the TUI and confirm the install prompt. Binaries land in `~/.local/share/claude/versions/<version>`.

## Themes

Two themes ship with the launcher: `dark.json` and `light.json` in `~/.claudewheel/themes/`. Switch by setting `theme` in `config.json` to the file's basename. Themes define per-segment foreground / focus / option / unavailable colours and the search highlight palette. Add a new theme by writing another `themes/<name>.json` and pointing `config.json` at it.

Themes also include an `overflow` section for viewport chrome:

| Key                | Controls                                                     |
|--------------------|--------------------------------------------------------------|
| `arrow_fg`         | Colour of the `<N` / `N>` edge scroll indicators             |
| `minimap_fg`       | Colour of unselected minimap squares                          |
| `minimap_focused_bg` | Background highlight on the focused minimap square          |
| `minimap_char`     | Character used for minimap squares (default `▪`)              |

## Tests

```bash
cd /home/m/Projects/claudewheel
python3 -m unittest discover tests/
```

240+ stdlib `unittest` tests covering segment cycling, fuzzy matching, requires-evaluation, install/manifest parsing, discovery merge logic, viewport scrolling, and config migration. Runs in under one second.
