Skip to content

fix(tui): keep the TUI usable after updating the pinned install from it - #681

Open
eric-engberg wants to merge 2 commits into
sirmalloc:mainfrom
eric-engberg:fix/in-tui-update-mismatch
Open

eric-engberg wants to merge 2 commits into
sirmalloc:mainfrom
eric-engberg:fix/in-tui-update-mismatch

Conversation

@eric-engberg

Copy link
Copy Markdown
Contributor

What

After you run a global update from Check for Updates on a pinned install, the TUI stays usable: you're back on Check for Updates with ✓ Global package updated, and Ctrl+S and Save & Exit still save your edits. Before, it switched to the "Pinned Install Version Mismatch" screen, where Exit was the only option, and unsaved edits were lost.

Why

On a pinned npm install at 2.2.30 with 2.3.0 on the registry, the steps are: change Terminal Options → Color Level to Truecolor (unsaved), then Manage Installation → Check for Updates → Run npm install -g [email protected] → Yes. The update succeeds and the TUI shows:

CCStatusline Configuration | v2.2.30  ✓ Global package updated

Pinned Install Version Mismatch

Claude Code is pinned to ccstatusline v2.3.0, but this TUI is v2.2.30.
...
▶  Exit

Ctrl+S does nothing on that screen and there's no way back to the menus. settings.json still has "colorLevel": 2, so the Truecolor edit is gone once you exit.

The update stores the new version as the pinned install. The next render compares it with the running TUI (2.2.30), sees the pinned install is newer, and shows the mismatch screen. That screen is for a TUI that's a different version from the pinned runtime, so it doesn't write settings that runtime may not expect. It's the right guard when you launch an older TUI. Here, though, the settings being edited were loaded before the update. Saving them afterwards writes exactly what saving a moment before the update would have written.

How

App remembers the version its own update installed (set as soon as the package manager succeeds), and getPinnedVersionMismatch takes it as an optional fourth argument and doesn't treat that version as a mismatch. Both places that check for a mismatch pass it: the render that swaps in the mismatch screen, and the Ctrl+S handler that skips saving.

  • Launching an older TUI against a newer pinned install still gets the mismatch screen. The exemption lives only in that session's state.
  • Only the exact version this session installed is exempt. A different version (say the active binary on PATH reports something else) still shows the screen.
  • A failed update records nothing, so nothing changes there.

I picked this over asking you to save before running the update. A save prompt would still leave you on an Exit-only screen afterwards, and it would add a step to every update even though the pre-update settings are safe to save after it.

Separately, the Check for Updates screen still shows the result from before the update ("An update is available" with the same command). main already does this whenever no mismatch follows an update, because nothing refreshes that screen's result. Fixing that is a separate change.

Demo

No GIF: recording this flow would run a real npm install -g and a real registry lookup. Above is a tmux capture of main. This is the same flow on this branch (built CLI under Node, stub npm, registry lookup answered locally with 2.3.0); the render mode doesn't affect these screens:

CCStatusline Configuration | v2.2.30  ✓ Global package updated
...
Check for Updates
...

Then Ctrl+S:

CCStatusline Configuration | v2.2.30  ✓ Configuration saved

and settings.json has "colorLevel": 3 with "installation": { "method": "pinned", "installedVersion": "2.3.0" }.

Testing

  • New whole-App test in src/tui/__tests__/App.test.ts: on a pinned install at the running version, it makes an unsaved Color Level edit, runs the update to 99.0.0 from Check for Updates, and expects no mismatch screen, then Ctrl+S to save colorLevel: 3 with the new pinned version. On main it fails because the frame shows "Pinned Install Version Mismatch". It passed 10 of 10 runs under Bun and under Node.
  • New unit test: getPinnedVersionMismatch returns null for the version installed this session and still blocks for any other.
  • bun test: 2784 pass, 0 fail. bun run lint passes.
  • App.test.ts under Node (Vitest): 21 pass (19 existing, 2 new). main passes the same 19.
  • Built CLI under Node 26.10.0 in tmux, with a scratch HOME and CLAUDE_CONFIG_DIR, stub npm and claude, and the registry lookup answered locally: on main, the flow above ends on the Exit-only mismatch screen and Ctrl+S leaves colorLevel: 2 on disk. On this branch it stays on Check for Updates, Ctrl+S saves colorLevel: 3 and installedVersion: 2.3.0, and ESC returns to Manage Installation and the main menu.
  • Bun 1.4.2, Node 26.10.0.

Running a global update from Check for Updates records the new version as
the pinned install. On the next render the TUI, still the older version,
saw a pinned install newer than itself and replaced everything with the
"Pinned Install Version Mismatch" screen, whose only option is Exit. Ctrl+S
is a no-op on that screen, so any edits made before the update were lost.

That screen exists so a TUI doesn't write settings for a pinned runtime of
a different version. Here, though, the settings being edited were loaded
before the update, so saving them afterwards is the same as saving a moment
earlier. The TUI now remembers the version its own update installed and
doesn't treat that version as a mismatch for the rest of the session, so
the menus and both save paths stay available. A fresh launch of the older
TUI still gets the mismatch screen.
The App sets chalk's global color level from the settings it loads, and
the whole-App test helper didn't restore it. Test files that run later in
the same process then rendered at that level instead of their own: a
No Color check could see 256-color codes. The sandbox now restores the
level along with the mocks and the config path.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant