Repository navigation
statusline: show the Shift+Tab permission mode - #2
Draft
broisnischal wants to merge 2 commits into
Draft
broisnischal wants to merge 2 commits into
broisnischal wants to merge 2 commits into
Conversation
The status line had no permission-mode indicator, because the statusLine
payload does not carry one — `permission_mode` is absent from the documented
schema (code.claude.com/docs/en/statusline), so nothing on stdin exposes it.
The transcript does: Claude Code appends a `permission-mode` entry the moment
the mode changes, so the badge tracks Shift+Tab live.
Reading the transcript on every render would break this file's cheapness
contract, so the resolved mode is cached against the transcript size:
- transcript unchanged -> one `wc -c` (fstat, ~1ms on a multi-MB file)
- transcript grew -> scan only the appended bytes
- no cache / rotated -> bootstrap from the last 1MiB, not the whole file
(a full scan of a 7.9MB transcript measured 231ms; bounded it is 66ms, and
the entry is re-emitted every turn so an older one is superseded shortly)
`default` renders nothing, matching Claude Code showing no indicator for it.
Set PHOBOS_STATUSLINE_MODE=0 to opt out.
|
An always-on `[⏵⏵ auto]` is noise: with permissions.defaultMode=auto you are in auto essentially always, and Claude Code already prints its own `⏵⏵ auto mode on` indicator right below the prompt. The badge only earns its space once you have switched away from the mode you configured. Resolve permissions.defaultMode from settings (local > project > user, with a PHOBOS_STATUSLINE_MODE_DEFAULT override) and render nothing while the current mode matches it. Resolved once per session and cached alongside the mode: defaultMode applies at startup, so editing it mid-session cannot change the mode the session is actually in — caching is correct here, not just cheap. Cache is now `|`-delimited. Space is IFS whitespace, so bash collapses runs of it and an empty permission-mode field would have silently shifted defmode into its place. Legacy space-delimited cache files fail the numeric size check and trigger one harmless re-bootstrap.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The status line showed no permission mode. Root cause: the statusLine payload does not carry one.
permission_modeis absent from the documented schema and from a live capture on Claude Code 2.1.220 — so nothing on stdin exposes it.(Same reason
claude-sudo's statusline renderer reads.permission_modeand its[SUDO ✗]branch can never fire — not fixed here, noted for a follow-up in that repo.)What
The transcript is a live source: Claude Code appends a
{"type":"permission-mode","permissionMode":"…"}entry the moment the mode changes, so the badge follows Shift+Tab immediately.Reading the transcript every render would break this file's "must stay cheap" contract, so the resolved mode is cached against transcript size:
wc -c— fstat, ~1 ms on a multi-MB fileA full scan of a real 7.9 MB transcript measured 231 ms; bounded to 1 MiB it is 66 ms, one time per session. Warm renders are +5 ms over baseline (21 ms vs 16 ms).
Rendering:
defaultplan[⏸ plan]blueacceptEdits[⏵ accept edits]greenauto[⏵⏵ auto]yellowbypassPermissions[⏵⏵ bypass]reddontAsk[⏵⏵ dont ask]redPHOBOS_STATUSLINE_MODE=0opts out.Testing
bash -nandshellcheck -S warningclean. 16/16 cases pass: all six modes, missing/empty/mode-less transcript, bootstrap, growth with mode retained, switch detection, rotation rescan, opt-out, and an entry pushed beyond the 1 MiB window (degrades to no badge, recovers on the next emit). Verified against a real captured payload — renders[⏵⏵ auto].Care taken with
set -euo pipefail: every test-in-a-list is anif, not&&, and thegreppipeline that legitimately fails when no match is guarded.