You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
opskit go launches opencode without checking whether the installed opencode needs an upgrade. A stale/broken opencode build silently breaks the launched session (free-model access stops working; commands error), and the operator has to notice and repair it by hand.
Also, after selecting a project there is a long gap before the opencode UI appears (opencode starts its background server), with no message telling the operator the handoff is happening.
Desired behaviour
Before launching: show the installed opencode version and its status.
If there is internet access, check whether opencode needs an upgrade and offer to upgrade first.
Print a clear handoff message immediately before the final opencode exec.
Findings (installed opencode 2.0.28)
Installed binary: ~/.opencode/bin/opencode -> opencode v2.0.28 (~/.opencode/bin/opencode2 is a 47-byte shim).
This tracks the anomalyco/opencodedev channel: the repo's dev branch package.json is 2.0.28, tagged v2.0.28.
The published latest is NOT this channel: GitHub releases and npm opencode-ai both report v1.19.0 (lower than installed). Comparing against the published latest would wrongly report 'up to date' or push a downgrade.
opencode upgrade/update has no --check/--dry-run; it self-reports ('already on the latest version' vs 'Upgraded.'). No cached latest-version file and no plaintext update endpoint in the binary.
Decisions
Detect 'needed' by querying the dev channel (highest vX.Y.Z tag via git ls-remote --tags) -- for display and the offer.
Act by running opencode upgrade (idempotent; opencode is its own oracle).
Cache the check in ~/.opskit/ and re-check at most ~once/24h.
Only prompt when interactive (tty); never auto-upgrade non-interactively.
Make opskit go (a) tell the operator when it hands off to opencode, and
(b) if online, check whether the installed opencode needs an upgrade and offer
to run it before launching — so a stale/broken opencode build can't silently
break the launched session.
Scope
Two bundled changes, one PR:
Launch handoff message — one flushed line immediately before the final os.execvpe("opencode", ...) in launch_project.
Out of scope: auto-upgrading non-interactively; touching the published-vs-dev
release feeds beyond reading the dev channel; changing opencode itself.
Research
Installed: ~/.opencode/bin/opencode → opencode v2.0.28; ~/.opencode/bin/opencode2 is a 47-byte shim.
Channel = anomalyco/opencodedev: repo dev branch package.json = 2.0.28, tag v2.0.28. (Repo fork:false, default branch dev.)
Published latest = v1.19.0 (GitHub releases and npm opencode-ai) — LOWER
than installed. Comparing to it would wrongly say "up to date"/downgrade.
opencode upgrade/update has no --check/--dry-run; it self-reports
("already on the latest version" vs "Upgraded."). No cached latest-version
file; no plaintext update endpoint in the binary.
The multi-minute gap after selection is opencode's own background-server
start (after exec), not opskit's pre-exec steps (skills setup + BW session are
each ≤30s).
Decisions
Detect "needed": query the dev channel (highest vX.Y.Z tag via git ls-remote --tags) → used for display and the offer.
Act: run opencode upgrade (idempotent; opencode is its own oracle).
Cache: store in ~/.opskit/, re-check at most ~once/24h.
Interactive only: prompt only on a tty; never auto-upgrade otherwise.
SECURITY — upgrading can restart the running opencode service/session.
A nested opskit go from inside opencode could kill the caller's session. Resolution: prompt names the restart; tty-only; decline default ([y/N]);
non-tty never upgrades.
SECURITY — network/secret exposure:git ls-remote is read-only,
unauthenticated, touches no secrets; nothing client-identifying is printed.
APPROPRIATENESS — source drift: our tag query may not exactly match the
channel opencode upgrade uses. Resolution: the action is opencode upgrade (authoritative); a wrong query can at worst mis-offer or
miss an offer, never apply a wrong action.
APPROPRIATENESS — channel mismatch: max-semver could offer a major jump. Resolution: operator wants standard and major upgrades; prompt is explicit
and declinable; repo is env-overridable.
Activity
growlf commentedon Oct 13, 2026
#444 — opskit go: pre-launch opencode upgrade check + launch handoff message
Goal
Make
opskit go(a) tell the operator when it hands off to opencode, and(b) if online, check whether the installed opencode needs an upgrade and offer
to run it before launching — so a stale/broken opencode build can't silently
break the launched session.
Scope
Two bundled changes, one PR:
os.execvpe("opencode", ...)inlaunch_project.Out of scope: auto-upgrading non-interactively; touching the published-vs-dev
release feeds beyond reading the dev channel; changing opencode itself.
Research
~/.opencode/bin/opencode→opencode v2.0.28;~/.opencode/bin/opencode2is a 47-byte shim.anomalyco/opencodedev: repodevbranchpackage.json=2.0.28, tagv2.0.28. (Repofork:false, default branchdev.)v1.19.0(GitHub releases and npmopencode-ai) — LOWERthan installed. Comparing to it would wrongly say "up to date"/downgrade.
opencode upgrade/updatehas no--check/--dry-run; it self-reports("already on the latest version" vs "Upgraded."). No cached latest-version
file; no plaintext update endpoint in the binary.
start (after exec), not opskit's pre-exec steps (skills setup + BW session are
each ≤30s).
Decisions
vX.Y.Ztag viagit ls-remote --tags) → used for display and the offer.opencode upgrade(idempotent; opencode is its own oracle).~/.opskit/, re-check at most ~once/24h.Design
New
opskit/_opencode.py(pure, testable helpers):parse_version(text) -> (int,int,int) | None— regexv?(\d+)\.(\d+)\.(\d+).is_newer(latest, installed) -> bool— tuple compare; ignore-suffix/^{}.parse_latest_tag(ls_remote_output) -> str | None— accept only^refs/tags/v(\d+\.\d+\.\d+)$(ignorevscode-*, peeled^{}); return max.get_installed_version(runner=subprocess.run) -> str | None.get_latest_dev_version(repo, runner=subprocess.run) -> str | None—git ls-remote --tags https://github.com/<repo>, timeout=10s.DEFAULT_OPENCODE_REPO = "anomalyco/opencode"; env overrideOPSKIT_OPENCODE_REPO.Edit
opskit/_cache.py:OPENCODE_CHECK_FILE = CACHE_DIR / "opencode-version-check.json".read_opencode_check()/write_opencode_check(data)(atomic, existing style).opencode_check_is_stale(data, installed)— stale if missing, older thanCACHE_STALE_HOURS, ordata["installed"] != installed.Edit
bin/opskit-go.py:maybe_offer_opencode_upgrade()(tty-only; cache; fetch; compare; prompt;run
opencode upgrade; re-read version).main().launch_project, printNow starting interactive OpenCode shell...(flushed) beforeexecvpe.New
tests/test_opencode_upgrade.py(TDD, written first).Task list
tests/test_opencode_upgrade.py(failing) — commit first.opskit/_opencode.pyhelpers.opskit/_cache.py.maybe_offer_opencode_upgrade()+ handoff message intobin/opskit-go.py.make testgreen.plans/444-...md+ issue opskit go: pre-launch opencode upgrade check + launch handoff message #444; PR; review; merge.Critique
A nested
opskit gofrom inside opencode could kill the caller's session.Resolution: prompt names the restart; tty-only; decline default ([y/N]);
non-tty never upgrades.
git ls-remoteis read-only,unauthenticated, touches no secrets; nothing client-identifying is printed.
channel
opencode upgradeuses. Resolution: the action isopencode upgrade(authoritative); a wrong query can at worst mis-offer ormiss an offer, never apply a wrong action.
Resolution: operator wants standard and major upgrades; prompt is explicit
and declinable; repo is env-overridable.
timeout=10, exceptions swallowed, 24h cache.differs; version re-read after upgrade.
_cache.py.unexplained silent gap at handoff.
opskit go: pre-launch opencode upgrade check + launch handoff message (…