On this page
App class -- TUI event loop for claudewheel.
#claudewheel.app
#claudewheel.app
TUI event loop, keyboard dispatch, and segment interaction.
#KeyContext
Ephemeral snapshot of state relevant to key dispatch decisions.
#Binding
A single keybinding entry in the registry.
#App
TUI application managing the event loop, keyboard handling, and segment interaction.
#_refresh_orphan_findings
def _refresh_orphan_findings(self) -> NoneRecompute orphan-token findings and the persistent status notice.
Read-only audit of the token store. A corrupt tokens.json (surfaced as a FAILED health check elsewhere) must not crash the TUI, so its :class:TokenStoreError is swallowed here into an empty finding set.
#run_tui
def run_tui(self) -> dict[str, str | None] | NoneEnter the TUI loop. Returns selections on launch, None on quit.
#_select_client
def _select_client(self) -> boolRun the Client selection step and drop claude-only segments.
Explicit --client wins and skips the picker (see :func:claudewheel.clients.resolve_client); otherwise the picker fans out the :data:~claudewheel.clients.CLIENT_ADAPTERS registry with the configured default pre-focused. Returns False when the user cancels the picker (Esc/Ctrl-C) so the caller quits cleanly; True otherwise.
When the resolved client is not claude, the version segment is removed from the bar: it selects a claudewheel-managed claude binary, so it is inapplicable to any other client and is skipped entirely.
#_promote_ephemeral
def _promote_ephemeral(self) -> NonePromote ephemeral selections to pinned on disk before launch.
#_run_slow_discovery_thread
def _run_slow_discovery_thread(self) -> NoneBackground thread: run slow discovery and store results.
#_apply_slow_discovery
def _apply_slow_discovery(self) -> NoneMerge slow discovery results into the live segment bar.
Results for the focused segment are deferred to avoid disrupting the user's current interaction. Unfocused segments are updated immediately.
Known limitation: while results are buffered, evaluate_requires() uses the focused segment's pre-discovery options for cross-segment constraint checks. Constraint-based dimming may be briefly stale until defocus.
#_apply_pending_for_segment
def _apply_pending_for_segment(self, seg: Segment) -> NoneApply any deferred discovery results for seg and clear pending state.
#_defocus
def _defocus(self) -> NoneRun deferred-apply housekeeping on the segment about to lose focus.
#_build_context
def _build_context(self) -> KeyContextBuild an ephemeral KeyContext from current app state.
#_compute_hints
def _compute_hints(self) -> list[str]Compute visible hint labels from the binding registry for the current state.
#_handle_key
def _handle_key(self, key: str) -> str | NoneProcess a keypress via the binding registry.
#_h_main_left
def _h_main_left(self, key: str) -> str | NoneHandle left arrow: defocus current segment and move focus left.
#_h_main_right
def _h_main_right(self, key: str) -> str | NoneHandle right arrow: defocus current segment and move focus right.
#_h_main_up
def _h_main_up(self, key: str) -> str | NoneHandle up arrow: clear search buffer and cycle selection up.
#_h_main_down
def _h_main_down(self, key: str) -> str | NoneHandle down arrow: clear search buffer and cycle selection down.
#_h_main_enter
def _h_main_enter(self, key: str) -> str | NoneHandle enter: launch if valid, or enter creation/install/auth flow.
#_h_main_tab
def _h_main_tab(self, key: str) -> str | NoneHandle tab: accept search match or advance focus to next segment.
#_h_main_backspace
def _h_main_backspace(self, key: str) -> str | NoneHandle backspace: enter freeform editing or trim search buffer.
#_h_main_esc
def _h_main_esc(self, key: str) -> str | NoneHandle escape: clear search buffer and exit freeform editing.
#_h_main_ctrl_c
def _h_main_ctrl_c(self, key: str) -> str | NoneHandle Ctrl-C: quit the TUI.
#_h_main_delete
def _h_main_delete(self, key: str) -> str | NoneHandle delete key: initiate profile deletion flow if on profile segment.
#_h_main_question
def _h_main_question(self, key: str) -> str | NoneHandle '?': toggle provenance overlay visibility.
#_h_main_inspect
def _h_main_inspect(self, key: str) -> str | NoneHandle 'i': open the profile inspect page for the focused profile.
#_h_main_review
def _h_main_review(self, key: str) -> str | NoneHandle 'T': open the stale token review page.
#_h_main_freeform_seed
def _h_main_freeform_seed(self, key: str) -> str | NoneHandle first printable key on freeform segment: seed editing from current value.
#_h_main_search
def _h_main_search(self, key: str) -> str | NoneHandle printable key on searchable segment: append to search buffer.
#_h_main_quit
def _h_main_quit(self, key: str) -> str | NoneHandle 'q' on non-searchable segment: quit the TUI.
#_h_theme_switch
def _h_theme_switch(self, key: str) -> str | NoneHandle Mode 2031 theme-change notification.
#_h_freeform_enter
def _h_freeform_enter(self, key: str) -> str | NoneHandle enter in freeform mode: submit the typed text as a new value.
#_h_freeform_tab
def _h_freeform_tab(self, key: str) -> str | NoneHandle tab in freeform mode: accept first matching option.
#_h_freeform_backspace
def _h_freeform_backspace(self, key: str) -> str | NoneHandle backspace in freeform mode: remove last character from buffer.
#_h_freeform_left
def _h_freeform_left(self, key: str) -> str | NoneHandle left arrow in freeform mode: cancel editing and move focus left.
#_h_freeform_right
def _h_freeform_right(self, key: str) -> str | NoneHandle right arrow in freeform mode: cancel editing and move focus right.
#_h_freeform_esc
def _h_freeform_esc(self, key: str) -> str | NoneHandle escape in freeform mode: cancel editing and clear buffer.
#_h_freeform_ctrl_c
def _h_freeform_ctrl_c(self, key: str) -> str | NoneHandle Ctrl-C in freeform mode: cancel editing and quit.
#_h_freeform_printable
def _h_freeform_printable(self, key: str) -> str | NoneHandle printable key in freeform mode: append to search buffer.
#_h_create_enter
def _h_create_enter(self, key: str) -> str | NoneHandle enter in creating mode: confirm creation of the new option.
#_h_create_esc
def _h_create_esc(self, key: str) -> str | NoneHandle escape in creating mode: cancel and clear the create buffer.
#_h_create_backspace
def _h_create_backspace(self, key: str) -> str | NoneHandle backspace in creating mode: remove last character from create buffer.
#_h_create_ctrl_c
def _h_create_ctrl_c(self, key: str) -> str | NoneHandle Ctrl-C in creating mode: cancel creation and quit.
#_h_create_printable
def _h_create_printable(self, key: str) -> str | NoneHandle printable key in creating mode: append to create buffer.
#_run_install_flow
def _run_install_flow(self, seg: Segment, version: str) -> NoneConfirm install via run_selection, download in cooked, show result page.
#_build_bindings
def _build_bindings(self) -> list[Binding]Build the full binding registry from handler methods.
#_h_main_search_or_quit
def _h_main_search_or_quit(self, key: str) -> str | NoneHandle printable key on searchable segment: search or quit.
#_intercept_unauth
def _intercept_unauth(self, seg: Segment) -> strPrompt auth for an unauthenticated profile before launch.
The app's terminal stays raw: the auth forms render borrowed as pages in the existing alt screen, and the subprocess steps inside the auth flow open their own cooked windows. On "authenticated", "unverified" (a token was saved without validation), and "failed" (credentials may be partially written), re-runs profile discovery and updates auth status. Returns the auth flow outcome: "authenticated", "unverified", "skip", "cancel", or "failed".
#_show_profile_inspect
def _show_profile_inspect(self, seg: Segment) -> NoneShow a fullscreen inspect page for the focused profile option.
The app's terminal stays raw: show_page renders borrowed in the existing alt screen and the main TUI repaints on return. If the profile has an auth shadow, the hint offers 'f' to fix it.
#_show_stale_token_review
def _show_stale_token_review(self) -> NoneReview and remove stale token entries (keys with no profile dir).
Lists each orphan tokens.json entry and offers per-entry or bulk removal via a selection list. Each removal goes through remove_orphan_token_entry (which rewrites tokens.json atomically), then findings are refreshed so the persistent notice clears once none remain. Cancelling leaves the store untouched.
#_delete_profile_flow
def _delete_profile_flow(self, seg: Segment) -> NoneConfirm and delete the focused profile from the TUI.
Profiles holding REAL data at shared-dir names are hard-blocked with a fullscreen page pointing at the CLI escape hatch -- the TUI offers no override. Otherwise an informed two-option confirm runs (Cancel default-focused). The running check stays CLI/TUI policy; the actual deletion goes through ProfileStore.delete (no force flags). Store refusals raise ValueError, surfaced as a flash.
#_refresh_profile_segment
def _refresh_profile_segment(self, seg: Segment) -> NoneRe-run profile discovery and update the segment's auth status.
#_launch_profile_wizard
def _launch_profile_wizard(self, seg: Segment) -> str | NoneRun the create-profile flow as one continuous alt-screen session.
The app's terminal stays raw throughout: the wizard form, the auth forms, and the creation summary page all render borrowed in the existing alt screen (subprocess steps open cooked windows inside the auth flow). The main TUI repaints on return.
#_confirm_create
def _confirm_create(self, seg: Segment) -> NoneConfirm creation of a new option.