Skip to content

feat: automatically prepare compatible updates - #14

Merged
aorumbayev merged 1 commit into
mainfrom
feat/automatic-updates
Jul 9, 2026
Merged

feat: automatically prepare compatible updates#14
aorumbayev merged 1 commit into
mainfrom
feat/automatic-updates

Conversation

@aorumbayev

Copy link
Copy Markdown
Member

Prepares the latest npm release for the next restart when it is compatible with the running OpenCode, while leaving incompatible, pinned, and local installs untouched.

What it does

  • src/tui/updates.ts checks npm latest once per hour for bare/@latest installs, fetches the candidate's engines.opencode, and classifies it as ready (compatible) or blocked (needs a newer OpenCode).
  • src/tui/update-manager.ts prepares a compatible exact release via api.plugins.add, writes a marker, and promotes it into place only on api.lifecycle.onDispose so the next restart loads it. Failed promotion restores the backup.
  • src/tui/update-paths.ts holds the cache-path resolution and validation: only non-symlinked @kagan-sh/kagan wrappers are accepted, and cleanup removes only its own marker and single backup.
  • The board footer shows persistent update status; a host toast appears only on home/session routes.

Notes

  • Exact pins, file: installs, and development builds are never touched.
  • Rebased onto the verifyx reorg: source lives under src/tui/, and the update code passes the new complexity gate.

Verification

bun run verify passes (all built-in verifyx checks, format, lint, types, 602 tests, package validation).

Prepare npm latest releases for restart while preserving incompatible, pinned, and local installs.

Co-authored-by: Cursor <cursoragent@cursor.com>
@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Updated@​opencode-ai/​plugin@​1.17.13 ⏵ 1.17.181001007097100
Added@​types/​semver@​7.7.11001007581100
Updated@​opencode-ai/​sdk@​1.17.13 ⏵ 1.17.18931007597100

View full report

@greptile-apps

greptile-apps Bot commented Jul 9, 2026

Copy link
Copy Markdown

Greptile Summary

This PR adds automatic update handling for npm-installed Kagan plugins.

  • Checks npm latest and reads engines.opencode compatibility.
  • Prepares compatible exact releases through the TUI plugin API.
  • Defers cache promotion until TUI disposal and cleans up markers on the next load.
  • Shows ready or blocked update status in the board footer and route-aware toasts.
  • Updates docs, specs, tests, package validation, and OpenCode dependencies.

Confidence Score: 4/5

The update flow has one cache-promotion issue to fix before merging.

Registry checks and wrapper validation are conservative, and pinned, local, development, failed, and incompatible update paths leave the install unchanged.

src/tui/update-manager.ts needs attention because shutdown-time promotion can leave the active cache path missing if disposal does not wait for the async rename sequence.

T-Rex T-Rex Logs

What T-Rex did

  • Reproduced the Async Shutdown Promotion test by running a Bun TypeScript harness that calls prepareUpdate with a fake TUI lifecycle and disposes without awaiting the returned Promise.
  • Used real temporary cache directories and a FileSystem wrapper to perform the first rename from current to backup and to hold the second rename from prepared to current.
  • Observed runtime checks immediately after non-awaited disposal showing kagan@latest was absent while kagan@0.2.0 and kagan@latest.kagan-backup remained.
  • Executed the Bun verify workflow and confirmed a passing result with exit code 0.
  • Reviewed the auxiliary logs, noting the initial capture wrapper failure was superseded by a clean bash capture.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
src/tui/updates.ts Adds strict release parsing, npm manifest caching, and OpenCode compatibility classification.
src/tui/update-paths.ts Adds cache path derivation and wrapper validation for non-symlinked Kagan package wrappers.
src/tui/update-manager.ts Adds update preparation, marker writing, cleanup, and shutdown-time promotion, with a risk around async disposal promotion.
src/tui.tsx Wires update cleanup, checking, preparation, status storage, and route-aware toasts into TUI startup.
src/tui/board/store.tsx Replaces the old update-available signal with structured ready or blocked update status.
src/tui/board/board.tsx Renders persistent update status in the board footer.
scripts/package-check.ts Extends package validation across the pinned and minimum supported OpenCode versions.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
  participant TUI as Kagan TUI
  participant NPM as npm registry
  participant API as OpenCode plugin API
  participant FS as package cache
  participant UI as board/toast UI

  TUI->>FS: cleanup marker and backup from prior update
  TUI->>NPM: fetch latest tag and manifest
  NPM-->>TUI: version and engines.opencode
  alt compatible latest
    TUI->>API: add exact Kagan release
    API->>FS: prepare package wrapper
    TUI->>FS: validate wrappers and write marker
    TUI->>UI: show ready status
    TUI->>FS: promote prepared wrapper on disposal
  else incompatible latest
    TUI->>UI: show blocked status
  else pinned, local, dev, or failure
    TUI-->>TUI: leave install unchanged
  end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
  participant TUI as Kagan TUI
  participant NPM as npm registry
  participant API as OpenCode plugin API
  participant FS as package cache
  participant UI as board/toast UI

  TUI->>FS: cleanup marker and backup from prior update
  TUI->>NPM: fetch latest tag and manifest
  NPM-->>TUI: version and engines.opencode
  alt compatible latest
    TUI->>API: add exact Kagan release
    API->>FS: prepare package wrapper
    TUI->>FS: validate wrappers and write marker
    TUI->>UI: show ready status
    TUI->>FS: promote prepared wrapper on disposal
  else incompatible latest
    TUI->>UI: show blocked status
  else pinned, local, dev, or failure
    TUI-->>TUI: leave install unchanged
  end
Loading

Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
src/tui/update-manager.ts:99
**Async Shutdown Promotion**

When a compatible update is prepared, this registers an async cache rename for TUI disposal. If the host does not wait for returned disposal promises, shutdown can cut off the sequence after `current` is moved to `backup` and before `prepared` is moved into place or restored, so the next restart can find `kagan@latest` missing.

Reviews (1): Last reviewed commit: "feat: automatically prepare compatible u..." | Re-trigger Greptile

Comment thread src/tui/update-manager.ts
}
await fs.writeFile(paths.marker, JSON.stringify(marker))
if (api.lifecycle.signal.aborted) return false
api.lifecycle.onDispose(() => promotePreparedUpdate(paths, currentVersion, status.version, fs))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Async Shutdown Promotion

When a compatible update is prepared, this registers an async cache rename for TUI disposal. If the host does not wait for returned disposal promises, shutdown can cut off the sequence after current is moved to backup and before prepared is moved into place or restored, so the next restart can find kagan@latest missing.

Context Used: AGENTS.md (source)

Artifacts

Repro: Bun TypeScript harness for non-awaited async disposal promotion

  • Contains supporting evidence from the run (text/typescript; charset=utf-8).

Repro: harness output showing rename order and missing current cache path

  • Keeps the command output available without making the summary code-heavy.

View artifacts

T-Rex Ran code and verified through T-Rex

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/tui/update-manager.ts
Line: 99

Comment:
**Async Shutdown Promotion**

When a compatible update is prepared, this registers an async cache rename for TUI disposal. If the host does not wait for returned disposal promises, shutdown can cut off the sequence after `current` is moved to `backup` and before `prepared` is moved into place or restored, so the next restart can find `kagan@latest` missing.

**Context Used:** AGENTS.md ([source](https://app.greptile.com/kagan/github/kagan-sh/kagan/-/custom-context?memory=88a14340-9a15-4297-925d-3656d144ad2a))

How can I resolve this? If you propose a fix, please make it concise.

Fix in Claude Code Fix in Codex

@aorumbayev
aorumbayev merged commit eb3b846 into main Jul 9, 2026
4 checks passed
@aorumbayev
aorumbayev deleted the feat/automatic-updates branch July 9, 2026 23:36
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 0.3.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant