Skip to content

Tell a session once when the plugin and basecamp command are from different releases - #949

Merged
robzolkos merged 4 commits into
mainfrom
plugin-version-session-notice
Oct 23, 2026
Merged

robzolkos merged 4 commits into
mainfrom
plugin-version-session-notice

Conversation

@robzolkos

@robzolkos robzolkos commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Pinning the marketplace to stable (basecamp/plugins#13, with #925) keeps fresh installs on a release. It doesn't reach someone whose plugin or binary is already old:

  • Updates aren't automatic. Claude Code leaves auto-update off for third-party marketplaces, and marketplace.json has no field to turn it on (docs). An installed plugin changes only when its version string does (docs). So a plugin installed once can sit still while Homebrew moves the CLI on.
  • The binary and the skills can disagree. Nothing in either agent's manifest can say "requires basecamp ≥ X". basecamp doctor reports a mismatch, but only to someone who runs it.

Other vendors handle this with a check on the user's machine and a hint in the skill. Vercel's plugin compares vercel --version at SessionStart (source). Railway's skill says an unrecognized command means the CLI may be out of date (source). The research behind this, and the landing order for stable, is in this comment on basecamp/plugins#13.

What

SessionStart version notice. The plugin's existing SessionStart hook, basecamp agent-hook pre-commit-snapshot, already runs the installed binary. It now:

  • reads the version stamped in this install's own manifest, .claude-plugin/plugin.json or .codex-plugin/plugin.json, and compares it with the CLI's;
  • once per pairing of versions, tells the user and the model which side to update:
    • CLI behind: basecamp upgrade.
    • Plugin behind, Claude Code: claude plugin update basecamp-cli@37signals, then restart, plus the auto-update hint basecamp setup already prints.
    • Plugin behind, Codex: basecamp setup codex, the same remedy doctor gives.

Updating either side creates a new pairing, so the notice speaks again only when there's something new to say.

It stays silent for:

  • dev builds;
  • prerelease and go install pseudo-versions ahead of the plugin, which have no newer plugin to update to (an rc left installed past its release is behind the plugin, and gets basecamp upgrade);
  • versions that aren't semver;
  • pre-rename installs, which already get the rename notice, and its basecamp setup also updates them.

This keeps the rule from #675 that the plugin doesn't inject context into every session; the manifest test's comment now names this as the third allowed case.

Skill line. An old binary can't run that check, so skills/basecamp/SKILL.md now says, under Errors and recovery: if the CLI rejects a command or flag as unknown, check the group's help first; if the skill names it and the help lacks it, the CLI and skill may be from different releases, and basecamp doctor --json compares the versions and names the side to update. An unknown command alone doesn't show which side is older, since the skill still names the removed basecamp campfire among its triggers.

Notice markers. Without a plugin data directory, the once-markers (this notice's, and the existing rename notice's) used to fall back to the commit-snapshot directory, whose cleanup removes every file older than an hour, so the notice came back. Both now fall back to agent-hook-notices in the CLI's cache, and the version marker names the agent, so Claude Code and Codex sharing that fallback each get their own notice.

Doctor's Codex hint. The Codex plugin version check said basecamp setup codex for every mismatch. When the CLI is the side behind, it now says basecamp upgrade, as the Claude Code check already did.

Not doing

A command plugin source (reference), where the binary prints its own plugin directory, would keep the two in step by construction. It's left out because:

  • it needs Claude Code v2.1.229+ and the user's acceptance;
  • Codex has no equivalent;
  • none of the 315 entries in Anthropic's marketplace uses it.

Worth revisiting if this notice isn't enough.

Tests

  • internal/commands/agent_hook_version_test.go covers:
    • plugin behind, which speaks once per pairing and again for a new pairing;
    • CLI behind;
    • the Codex remedy;
    • the silent cases (same release, v-spelled, dev, prerelease ahead, pseudo-version, non-semver, no version);
    • an rc behind its release getting basecamp upgrade;
    • a fallback marker surviving the snapshot cleanup;
    • deferring to the rename notice;
    • no notice on a tool-call hook.
  • The tests fail without the code. Disconnecting the notice from emitSessionStartNotices fails the tests that expect a notice. Dropping the prerelease guard fails the prerelease-ahead and pseudo-version cases; making it unconditional fails the rc-behind case; pointing the fallback back at the snapshot directory fails the cleanup case.
  • bin/ci passes on 1e8b29f7 (exit 0, no failures), in the main checkout.
  • Independent review: Pi gpt-6.1-sol (thinking high), four rounds. Findings fixed: notice markers lost to the snapshot cleanup, an rc behind its release staying silent, the skill line over-claiming what an unknown command means, doctor's Codex hint naming setup when the CLI is behind, and Claude Code and Codex sharing one fallback marker. Three findings declined; see the summary comment.
  • Real sessions: checked in Claude Code 2.1.308 and Codex 0.163.0, covering plugin behind, shown once, CLI behind, and matching versions. Results are in a comment below.

Copilot AI balanced review requested due to automatic review settings October 23, 2026 01:59
@github-actions github-actions Bot added commands CLI command implementations tests Tests (unit and e2e) skills Agent skills labels Oct 23, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Snapshot cleanup can delete fallback notice markers, causing repeated notices for unchanged version pairings.

1 open issue
What changed in this PR

Adds a SessionStart notice when the installed Basecamp plugin and CLI come from different releases, complementing the stable-release installation work without adding unconditional session context.

Changes:

  • Compares plugin and CLI versions and provides update instructions once per version pairing.
  • Adds tests for update guidance, notice suppression, and SessionStart-only behavior.
  • Updates skill recovery guidance and hook documentation.

[!TIP]
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.

File Description
skills/​basecamp/​SKILL.md Adds upgrade guidance for unknown commands or flags.
internal/​release/​manifests_test.go Updates comments and assertion messages to include version notices.
internal/​commands/​agent_hook.go Updates the SessionStart routing comment.
internal/​commands/​agent_hook_version.go Implements version comparison, notice markers, and update guidance.
internal/​commands/​agent_hook_version_test.go Tests notices, suppression, and hook routing.
internal/​commands/​agent_hook_rename.go Integrates version notices and shares agent detection.
hooks/​hooks.json Documents the version notice in the hook description.

Review effort: Balanced

Comment thread internal/commands/agent_hook_version.go Outdated
…ferent releases

Neither Claude Code nor Codex updates a third-party plugin unless the user
turns that on, and a plugin changes only when its version string does, so a
plugin installed once can stay put while Homebrew moves the CLI on. Doctor
reports the mismatch, but only to someone who runs it.

The plugin's SessionStart hook already runs the installed binary at every
session start. It now reads the version stamped in this install's manifest,
compares it with the CLI's, and once per pairing says which side to update:
`basecamp upgrade` when the CLI is behind, the agent's own plugin update when
the plugin is. Dev, prerelease and go-install builds, versions that aren't
semver, and pre-rename installs (which get the rename notice) stay silent.

An old binary can't run this check, so the skill also says that an unknown
command means the CLI is older than the skill: run `basecamp upgrade`.
… release; soften the skill line

Review findings:

- Without a plugin data directory the once-markers fell back to the
  commit-snapshot directory, whose cleanup removes every file older than an
  hour, so the version notice (and the rename notice, which shared the
  fallback) came back. Both now fall back to agent-hook-notices in the CLI's
  cache.
- A prerelease CLI behind the plugin (an rc left installed past its release)
  was silent; it now gets `basecamp upgrade`. Only a prerelease ahead of the
  plugin has nothing to update to.
- An unknown command doesn't show which side is older: the skill names a
  removed command in its own triggers. The skill now says to check help
  first, and to let `basecamp doctor --json`, which compares the versions,
  name the side to update.
Review finding: doctor's Codex version check said `basecamp setup codex` for
every mismatch, but setup can't bring forward a CLI that is behind the
plugin; only an upgrade can. It now says `basecamp upgrade` then, as the
Claude Code check already does.

The skill line no longer leans on doctor's remedy, which an older CLI still
gets wrong: it has the agent read both versions from doctor and name the
older side itself.
Review finding: without plugin data directories, Claude Code and Codex share
the fallback notice directory, so the first agent's notice silenced the
other's, whose remedy differs. The marker now names the agent.
Copilot AI balanced review requested due to automatic review settings October 23, 2026 06:40
@robzolkos
robzolkos force-pushed the plugin-version-session-notice branch from 924f40b to 1e8b29f Compare October 23, 2026 06:40
@robzolkos

Copy link
Copy Markdown
Collaborator Author

Review summary: declined findings

An independent review by Pi gpt-6.1-sol (thinking high) ran four rounds on this PR. Five findings were fixed; the PR description lists them. The three below were declined on merit. They're recorded here so they aren't raised again.

  1. A prerelease or go install CLI ahead of the installed plugin stays silent, even when that plugin is also behind the latest published plugin.

    • The hook can't know the latest published plugin version without a network call at every session start.
    • Prerelease builds embed their own matching skill, and only testers run them.
    • basecamp doctor still reports the mismatch.
    • An rc that is behind the plugin does get basecamp upgrade; that case was fixed in this PR.
  2. claude plugin update basecamp-cli@37signals uses the default scope, so it may miss a project- or local-scope install.

    • It's the same remedy basecamp doctor has always given (PluginVersionMismatchHint).
    • basecamp setup installs at user scope.
    • The session's plugin root doesn't reveal the scope, so getting it right means reading Claude Code's install records for a rare, deliberate setup.
  3. An install under the pre-rename id basecamp@37signals gets no version notice, even when the CLI is the side behind.

    • That id is a deprecated alias, and it goes away once the marketplace points basecamp at the hosted connector (see AGENTS.md).
    • Its users get the rename notice, and the CLI-behind case is still covered by the skill line and doctor.

About the loop. Findings across the four rounds were 3, 1, 2 and 2. The later ones were all remedy-text corner cases for particular install shapes: scope, alias, prerelease, which agent. I stopped there instead of chasing the next one. If one of these matters more than I've judged, say so and I'll pick it up.

bin/ci passes on 1e8b29f7.

@robzolkos
robzolkos marked this pull request as ready for review October 23, 2026 06:40
@robzolkos
robzolkos requested a review from a team as a code owner October 23, 2026 06:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approved

No unresolved blocking findings remain, and the questioned Codex test call is correct in both the supplied diff and the checkout.

No open issues 🎉

1 resolved since last review

Review effort: Balanced

@robzolkos

Copy link
Copy Markdown
Collaborator Author

Tested in real Claude Code and Codex sessions

I built 1e8b29f7 stamped with test versions, put it first on PATH for each session, and asked the model to quote any Basecamp notice it got at session start. The versions are made up so the leftover markers can't collide with a real release; I deleted the markers afterwards.

Claude Code 2.1.308. Real install: basecamp-cli@37signals 0.16.0, user scope.

CLI Session Result
0.99.0 1st "The Basecamp plugin (0.16.0) is older than the basecamp command (0.99.0)… Run claude plugin update basecamp-cli@37signals, then restart Claude Code…". systemMessage and additionalContext were both in the hook reply.
0.99.0 2nd NONE, so shown once
0.15.9 1st "…(0.16.0) is newer than the basecamp command (0.15.9)… Run basecamp upgrade."
0.16.0 1st NONE, since the versions match

Codex CLI 0.163.0. Throwaway CODEX_HOME. codex plugin add basecamp-cli@37signals installed 0.17.0 from stable, which also confirms basecamp/plugins#13 for Codex. Hooks were run with --dangerously-bypass-hook-trust, for this plugin's hooks only.

CLI Session Result
0.99.0 1st "The Basecamp plugin (0.17.0) is older than the basecamp command (0.99.0)… Run basecamp setup codex, then restart Codex."
0.99.0 2nd NONE. The marker was written as plugins/data/basecamp-cli-37signals/plugin-version-notice-codex-v0.17.0-v0.99.0.
0.15.9 1st "…(0.17.0) is newer than the basecamp command (0.15.9)… Run basecamp upgrade."

Not exercised: whether following each remedy actually clears the mismatch, i.e. claude plugin update and basecamp setup codex against a stale install. Those are the remedies basecamp doctor already gives.

@robzolkos
robzolkos merged commit d0c313d into main Oct 23, 2026
32 checks passed
@robzolkos
robzolkos deleted the plugin-version-session-notice branch October 23, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations skills Agent skills tests Tests (unit and e2e)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants