Skip to content

feat: show update-available indicator in TUI footer#175

Merged
anandgupta42 merged 6 commits intomainfrom
feat/upgrade-available-banner
Mar 20, 2026
Merged

feat: show update-available indicator in TUI footer#175
anandgupta42 merged 6 commits intomainfrom
feat/upgrade-available-banner

Conversation

@anandgupta42
Copy link
Contributor

@anandgupta42 anandgupta42 commented Mar 16, 2026

What does this PR do?

Adds a persistent, subtle upgrade indicator in the TUI footer that shows when a newer version of Altimate Code is available. Instead of relying only on a 10-second toast notification, users now see a persistent inline hint in both the home page and session footers:

local → 0.5.0 · altimate upgrade

When no update is available, the footer shows the current version as before.

Changes:

  • New UpgradeIndicator Solid.js component with muted/accent styling
  • New getAvailableVersion utility (pure function, easily testable)
  • Store available version in KV store on UpdateAvailable event for persistence across route changes
  • Show indicator in both home page footer and session footer
  • Fix toast message to say altimate upgrade instead of opencode upgrade
  • 24 new tests covering version comparison, KV integration, event schema, and end-to-end flow

Type of change

  • New feature (non-breaking change which adds functionality)

Issue for this PR

Closes #173

How did you verify your code works?

  • All 2637 tests pass (24 new + 2613 existing), 0 failures
  • TypeScript typecheck passes
  • Manually verified by seeding kv.json with {"update_available_version":"0.5.0"} and launching the TUI
  • Tested that indicator hides when stored version matches Installation.VERSION

Note: A real screenshot of the TUI showing the indicator needs to be manually attached — automated screencapture was blocked by macOS Screen Recording permissions.

Checklist

  • My code follows the guidelines of this project
  • I have performed a self-review of my own code
  • New and existing unit tests pass locally with my changes

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an upgrade indicator in app footers that shows an arrow, the available version (themed accent), and a muted "available · ultimate upgrade" label; falls back to displaying the current version when no update is available.
    • The app now remembers detected update version state so the indicator reflects recent update events.
  • Tests

    • Added comprehensive tests covering version detection, indicator behavior, and update/refresh flows.

@dev-punia-altimate
Copy link

🤖 Behavioral Analysis — 3 Finding(s)

🟡 Warnings (2)

  • F1 packages/opencode/src/cli/cmd/tui/app.tsx:732 [Stale indicator after autoupgrade]
    When autoupdate is enabled (not 'notify'), upgrade.ts:23 publishes Installation.Event.Updated after a successful in-app upgrade. The TUI handles Installation.Event.UpdateAvailable (sets the KV key) but has no handler for Installation.Event.Updated. So after an automatic upgrade finishes, the KV key update_available_version is never cleared. The banner keeps showing '→ 0.5.0 · altimate upgrade' until the app is restarted, because getAvailableVersion only hides the indicator when kvValue === Installation.VERSION — which is still the OLD version while the process is running. The upgrade already succeeded but the UI says otherwise.

  • F2 packages/opencode/src/cli/cmd/tui/component/upgrade-indicator-utils.ts:7 [Stale KV version causes downgrade arrow in indicator]
    getAvailableVersion only suppresses the indicator when the stored version exactly matches the current version. It does not verify that the stored version is actually newer. Concrete scenario: (1) user on 0.4.0, autoupdate=notify fires UpdateAvailable for 0.5.0, KV set to '0.5.0'. (2) User upgrades externally (brew/npm) directly to 0.6.0 — the in-app Updated event never fires. (3) App restarts with VERSION='0.6.0', KV still holds '0.5.0'. (4) getAvailableVersion('0.5.0') returns '0.5.0' because '0.5.0' !== '0.6.0'. (5) Indicator renders '0.6.0 → 0.5.0 · altimate upgrade' — a downgrade arrow on a version the user is already past.

🔵 Nits (1)

  • F3 packages/opencode/src/cli/cmd/tui/component/upgrade-indicator-utils.ts:8 — If the KV value is an empty string, getAvailableVersion('') returns '' rather than undefined. The return type string | undefined implies any returned string is a valid version. Behaviour is accidentally correct — treats '' as falsy so the indicator stays hidden, and !getAvailableVersion(...) is true so the fallback version shows — but the function leaks an invalid value that callers checking typeof result === 'string' would accept as a real version. The test file acknowledges this gap explicitly (upgrade-indicator.test.ts:47-56) but accepts the behaviour.

Analysis run | Powered by QA Autopilot

@dev-punia-altimate
Copy link

✅ Tests — All Passed

TypeScript — passed

Python — passed

Tested at e18c645e | Run log | Powered by QA Autopilot

anandgupta42 and others added 2 commits March 20, 2026 11:57
Add a persistent, subtle upgrade indicator in both the home page and
session footers. When a newer version of Altimate Code is available,
the footer shows `current → latest · altimate upgrade` in muted/accent
colors instead of just the version number.

- Add `UpgradeIndicator` component and `getAvailableVersion` utility
- Store available version in KV on `UpdateAvailable` event for persistence
- Show indicator in both home and session footers
- Fix toast message to say `altimate upgrade` instead of `opencode upgrade`
- Add 24 tests covering version comparison, KV integration, and event flow

Closes #173

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- F1: Clear KV on `Updated` event — set `update_available_version` to
  current `VERSION` after autoupgrade so indicator hides immediately
- F2: Add semver comparison via `isNewer()` — prevents downgrade arrow
  when user upgrades externally past the stored version
- F3: Reject empty string as invalid version in `getAvailableVersion()`
- Update tests to cover all three fixes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anandgupta42 anandgupta42 force-pushed the feat/upgrade-available-banner branch from e18c645 to 1b86aff Compare March 20, 2026 19:14
@coderabbitai
Copy link

coderabbitai bot commented Mar 20, 2026

Warning

Rate limit exceeded

@anandgupta42 has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 10 minutes and 55 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 6ff4431b-d4a0-4e8d-9c7a-5a7148109148

📥 Commits

Reviewing files that changed from the base of the PR and between 52eaacb and 46e9599.

📒 Files selected for processing (2)
  • packages/opencode/src/cli/cmd/tui/component/upgrade-indicator.tsx
  • packages/opencode/test/cli/tui/upgrade-indicator-e2e.test.ts
📝 Walkthrough

Walkthrough

Persist upgrade-related version info to the KV store on update events, add utilities to resolve whether a stored version is newer, introduce an UpgradeIndicator SolidJS component that reads KV to conditionally show an inline footer hint, and add tests covering utilities, KV integration, and event-driven flows.

Changes

Cohort / File(s) Summary
Event persistence
packages/opencode/src/cli/cmd/tui/app.tsx
On Installation.Event.UpdateAvailable write UPGRADE_KV_KEY = reported version to KV; register Installation.Event.Updated listener to set KV to Installation.VERSION when it differs.
Indicator utilities
packages/opencode/src/cli/cmd/tui/component/upgrade-indicator-utils.ts
New exports: UPGRADE_KV_KEY ("update_available_version") and getAvailableVersion(kvValue) which validates/compares semver (special-cases "local") and returns newer version or undefined.
UI component
packages/opencode/src/cli/cmd/tui/component/upgrade-indicator.tsx
New UpgradeIndicator SolidJS component: reads KV via useKV(), derives latestVersion via getAvailableVersion(...), and conditionally renders a single-line upgrade hint (with props.fallback).
Footer integration
packages/opencode/src/cli/cmd/tui/routes/home.tsx, packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx
Replace static version text with <UpgradeIndicator /> in Home footer; add UpgradeIndicator to session footer right-side container.
Tests
packages/opencode/test/cli/tui/upgrade-indicator.test.ts, packages/opencode/test/cli/upgrade-notify.test.ts
Add Bun test suites validating UPGRADE_KV_KEY, getAvailableVersion behavior across edge cases (types, invalid semver, prereleases, dev-mode), event schemas, Installation.latest(...) normalization, KV read/write workflow, and an end-to-end event→indicator→clear flow.

Sequence Diagram(s)

sequenceDiagram
    participant Installation as Installation Events
    participant KV as KV Store
    participant Indicator as UpgradeIndicator
    participant UI as Footer UI

    Installation->>KV: UpdateAvailable event\nkv.set(UPGRADE_KV_KEY, version)
    Note over KV: Stored version for indicator

    Indicator->>KV: mount / read\nkv.get(UPGRADE_KV_KEY)
    KV-->>Indicator: stored version

    Indicator->>Indicator: getAvailableVersion(stored, Installation.VERSION)
    alt stored is newer
        Indicator->>UI: render inline hint (available version)
    else not newer or invalid
        Indicator->>UI: render fallback / nothing
    end

    Installation->>KV: Updated event\nkv.set(UPGRADE_KV_KEY, Installation.VERSION)
    Indicator->>KV: re-read value
    KV-->>Indicator: current version
    Indicator->>UI: clear hint
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I found a version in the grass so neat,

Stored it in KV, a tiny upgrade treat.
A gentle arrow nudges from the footer's light,
No fuss, just a whisper: hop to the new sight. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: show update-available indicator in TUI footer' is clear, concise, and directly describes the main feature added—a persistent upgrade indicator in the TUI footer.
Description check ✅ Passed The PR description is complete and well-structured, covering what changed and why, detailed testing verification, and confirming test additions. All template sections are adequately addressed.
Linked Issues check ✅ Passed The PR fully implements all acceptance criteria from issue #173: asynchronous version checks, persistent KV-based caching, a subtle single-line footer indicator, non-intrusive styling, proper indicator hiding when current, and no modal/dismissal requirements.
Out of Scope Changes check ✅ Passed All changes directly support the upgrade indicator feature. New components, utilities, and tests are tightly scoped to version checking, KV persistence, and indicator rendering with no extraneous modifications.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/upgrade-available-banner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/tui/app.tsx (1)

845-856: Use UPGRADE_KV_KEY here to avoid key drift.

At Line 845 and Line 855, the raw key string is duplicated. Reusing the shared constant prevents subtle regressions if the key ever changes.

Proposed fix
+import { UPGRADE_KV_KEY } from "./component/upgrade-indicator-utils"
...
-    kv.set("update_available_version", evt.properties.version)
+    kv.set(UPGRADE_KV_KEY, evt.properties.version)
...
-    kv.set("update_available_version", Installation.VERSION)
+    kv.set(UPGRADE_KV_KEY, Installation.VERSION)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/src/cli/cmd/tui/app.tsx` around lines 845 - 856, Replace
the hard-coded key "update_available_version" with the shared constant
UPGRADE_KV_KEY in both kv.set calls so the key cannot drift; update the two
occurrences inside the event handlers (the handler that uses
evt.properties.version and the sdk.event.on(Installation.Event.Updated.type,
...) handler that sets Installation.VERSION) to call kv.set(UPGRADE_KV_KEY, ...)
and ensure UPGRADE_KV_KEY is imported or defined in this module.
packages/opencode/test/cli/tui/upgrade-indicator.test.ts (1)

41-45: This assertion is non-informative and always passes.

At Line 44, the condition matches the function’s declared return type, so it doesn’t verify behavior.

Proposed fix
-    test("returns version when stored version is newer or unparseable (dev mode)", () => {
-      // In dev mode VERSION="local", semver parsing falls back to showing indicator
-      const result = getAvailableVersion("999.0.0")
-      expect(typeof result === "string" || result === undefined).toBe(true)
-    })
+    test("returns version for high semver unless current version is non-comparable policy override", () => {
+      const result = getAvailableVersion("999.0.0")
+      if (Installation.VERSION === "local") {
+        expect(result).toBe("999.0.0")
+      } else {
+        expect(result).toBe("999.0.0")
+      }
+    })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/opencode/test/cli/tui/upgrade-indicator.test.ts` around lines 41 -
45, The test's assertion is too broad and always passes; update the assertion to
assert the specific expected behavior of getAvailableVersion in dev mode by
checking it returns the stored version string. Replace the current expect with a
concrete check such as expect(result).toBe("999.0.0") (or at minimum
expect(result).toBeDefined() and expect(typeof result).toBe("string")) so the
test for getAvailableVersion("999.0.0") actually validates the function returns
the version string in dev mode.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/opencode/src/cli/cmd/tui/component/upgrade-indicator-utils.ts`:
- Line 10: The isNewer function currently treats any parse failure as "newer"
(c.some(isNaN) || cur.some(isNaN) => true); change this so unparseable candidate
versions are rejected: if the parsed candidate parts contain NaN, return false
(not newer); similarly ensure the parsed current version is valid before
comparing—only perform numeric comparisons when both c and cur arrays have no
NaN. Apply the same validation/early-return logic to the KV parsing block
referenced around lines 20-24 so malformed KV strings do not result in a bogus
upgrade indicator.

---

Nitpick comments:
In `@packages/opencode/src/cli/cmd/tui/app.tsx`:
- Around line 845-856: Replace the hard-coded key "update_available_version"
with the shared constant UPGRADE_KV_KEY in both kv.set calls so the key cannot
drift; update the two occurrences inside the event handlers (the handler that
uses evt.properties.version and the
sdk.event.on(Installation.Event.Updated.type, ...) handler that sets
Installation.VERSION) to call kv.set(UPGRADE_KV_KEY, ...) and ensure
UPGRADE_KV_KEY is imported or defined in this module.

In `@packages/opencode/test/cli/tui/upgrade-indicator.test.ts`:
- Around line 41-45: The test's assertion is too broad and always passes; update
the assertion to assert the specific expected behavior of getAvailableVersion in
dev mode by checking it returns the stored version string. Replace the current
expect with a concrete check such as expect(result).toBe("999.0.0") (or at
minimum expect(result).toBeDefined() and expect(typeof result).toBe("string"))
so the test for getAvailableVersion("999.0.0") actually validates the function
returns the version string in dev mode.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 1e2c75b9-953f-44f5-b1c2-f47f00061b8e

📥 Commits

Reviewing files that changed from the base of the PR and between df24e73 and 1b86aff.

📒 Files selected for processing (7)
  • packages/opencode/src/cli/cmd/tui/app.tsx
  • packages/opencode/src/cli/cmd/tui/component/upgrade-indicator-utils.ts
  • packages/opencode/src/cli/cmd/tui/component/upgrade-indicator.tsx
  • packages/opencode/src/cli/cmd/tui/routes/home.tsx
  • packages/opencode/src/cli/cmd/tui/routes/session/footer.tsx
  • packages/opencode/test/cli/tui/upgrade-indicator.test.ts
  • packages/opencode/test/cli/upgrade-notify.test.ts

anandgupta42 and others added 4 commits March 20, 2026 12:31
- Replace custom `isNewer` with `semver.gt()` — `semver` is already a
  dependency; handles prerelease tags and rejects corrupted KV values
- Use `UPGRADE_KV_KEY` constant in `app.tsx` instead of magic string
- Add `fallback` prop to `UpgradeIndicator` — eliminates duplicate
  `getAvailableVersion` call in `home.tsx` (Gemini design suggestion)
- Remove unused `UPGRADE_KV_KEY` re-export from `upgrade-indicator.tsx`
- Add conditional check before `kv.set` on `Updated` event to avoid
  unnecessary file writes
- Fix tautological test, add tests for corrupted/prerelease versions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 21 end-to-end tests covering the full upgrade indicator flow:
- Full lifecycle: fresh install → UpdateAvailable → indicator shown →
  Updated → indicator hidden
- F1 regression: stale indicator after autoupgrade is cleared
- F2 regression: downgrade arrow prevention with `semver.gt()`
- F3 regression: empty/corrupted/non-string KV values rejected
- Semver integration: prerelease, build metadata, v-prefix handling
- Edge cases: rapid events, Updated without UpdateAvailable, same
  version available as current

Uses mock KV store pattern consistent with codebase test conventions
(algorithm extraction, no Solid.js context required).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Change "available" to "update available" so users clearly understand
a new version upgrade is pending, not just that a version exists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On narrow terminals (<100 cols), hide "update available ·" text and
show only the essential: ↑ 0.6.0 altimate upgrade

Wide (100+): ↑ 0.6.0 update available · altimate upgrade
Narrow (<100): ↑ 0.6.0 altimate upgrade

Uses useTerminalDimensions() for reactive width detection.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@anandgupta42 anandgupta42 merged commit 22651a6 into main Mar 20, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: show update-available banner in TUI when a new version is released

2 participants