feat(plugin): publish kbagent through keboola/ai-kit, deprecate this repo's marketplace - #627
Conversation
The next-steps hint hardcoded the Claude Code marketplace and plugin names (`keboola/cli` / `kbagent@keboola-agent-cli`). #627 moves the marketplace listing to keboola/ai-kit and does not touch install.sh, so the hardcoded pair would merge silently and then advertise deprecated names. Point at `kbagent doctor` instead, which already prints the current `/plugin` commands and is updated by #627 -- correct under either merge order.
8179e1c to
13851cb
Compare
…repo's marketplace Keboola had two Claude Code plugin marketplaces. This leaves one. The plugin SOURCE stays here in plugins/kbagent/ -- four CI gates in this repo generate and validate it against the live command tree (make skill-gen / skill-check, scripts/sync_version.py, scripts/check_command_sync.py, make version-check) and none of them exist in ai-kit. What moves is PUBLICATION: keboola/ai-kit's keboola-claude-kit marketplace carries an external git-subdir entry pointing at plugins/kbagent here, pinned to a release tag. - New `ai-kit-marketplace` release job (release-kbagent.yml), gated exactly like `homebrew` (stable tag + environment: release): jq-rewrites the kbagent entry's version + source.ref in ai-kit and opens a PR there with `gh pr create`. A PR, not a push, so ai-kit's evals see the change. No-op safe -- an entry already at this version opens nothing, and a re-run of the same tag reuses its branch. Requires a new write-scoped PAT, secrets.AI_KIT_TOKEN. - .claude-plugin/marketplace.json stays as a deprecated shim so existing installs keep resolving updates; its entry description now leads with the migration notice, which is the only in-product channel that reaches those users. Mechanically untouched otherwise (sync_version.py still round-trips it). - doctor's claude_plugin check probes both cache dirs, keboola-claude-kit first. A legacy-only install still passes, with reinstall instructions appended. - Install commands updated in AGENT_CONTEXT, README, TUTORIAL, use-cases, CLAUDE.md, CONTRIBUTING (sync-map rows + a post-tag release step), SKILL.md and the plugin's CLAUDE.md. Before: /plugin marketplace add keboola/cli + /plugin install kbagent@keboola-agent-cli After: /plugin marketplace add keboola/ai-kit + /plugin install kbagent@keboola-claude-kit
13851cb to
061a093
Compare
soustruh
left a comment
There was a problem hiding this comment.
Approve. I read the code. Two points the description does not cover:
-
doctorwith both cache dirs present and the legacy one newer._check_claude_pluginstops at the first directory that holds any version —keboola-claude-kit— and never reads the legacy directory (doctor_service.py:577).latestis then the max within that one directory only (:602). While ai-kit trails a cli release — the state this PR creates — a user with both installs, and a newer copy underkeboola-agent-cli, sees the olderkeboola-claude-kitversion, gets no migration note, and gets a drift hint aimed at the wrong copy. The new tests cover claude-kit-only, legacy-only, and claude-kit-newer. They do not cover legacy-newer. Is that report intended? -
The sync job has no automated test.
ai-kit-marketplaceis the only new code that writes to another repo, and only manual checks cover it. A shell test of the jq rewrite and the value-compare would catch a future change to ai-kit's marketplace schema before a release does.
Neither blocks approval. The sequencing and AI_KIT_TOKEN caveats in the description hold.
…che dirs Review found a real bug in the claude_plugin check, not just a gap in the tests. `_check_claude_plugin` probed the two cache dirs in order and broke out of the loop at the first one holding any version dir, so `latest` was the max WITHIN that dir only. While ai-kit trails a cli release -- the exact state publishing through ai-kit creates -- a user with both installs and a newer copy under the legacy `keboola-agent-cli` dir was told the OLDER `keboola-claude-kit` version, got no migration note, and got a drift hint aimed at a copy they were not running. Reporting a stale version as current is worse than reporting nothing. The probe now collects every version dir under every marketplace dir that exists and picks the single newest, so version, path, marketplace name, drift hint and migration note all describe the same copy: - a newest-copy-under-the-shim install gets the migration note it was missing; - the drift hint names the marketplace of the reported copy (`/plugin update kbagent@<marketplace>`), unambiguous when both are installed; - the message and a new `plugin_marketplace` result key name where the copy came from; - ties (same version cached under both) resolve to the current marketplace -- same code, nothing to migrate off. Ordering is PEP 440 via `packaging.version`, the idiom version_service already uses, rather than dir-name string compare: a string sort puts "0.100.0" below "0.90.0" and would report a stale copy as newest once the minor rolls past 99, which this repo is ten releases from. Unparseable dir names sort below every real version but stay eligible, so a hand-made dir never hides an install. Single-cache-dir behaviour is unchanged; all 11 pre-existing tests pass as-is. Also adds the sync job's missing automated test. `ai-kit-marketplace` is the only job here that writes to another repo, and its entry shape is owned by ai-kit, so the failure worth pinning is a shape change on the far side. tests/test_release_kbagent_ai_kit_sync.py extracts the step's real `run:` block from the workflow and executes it with bash against a throwaway git repo -- same "load the real artifact by path, then exercise it" approach as test_sync_version_script.py -- covering the at-target no-op (byte-identical file, changed=false), a genuine bump rewriting both `version` and `source.ref` while leaving the git-subdir coordinates and sibling plugins alone, either field stale alone still counting as a bump, a missing `source.ref` getting added, a missing `kbagent` entry failing loudly, and a `source` that is a plain string (the shape this repo's own deprecated marketplace.json still uses) aborting with the file untouched instead of publishing an unpinned version. No version bump and no changelog entry, per #648. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiYtKRNF8XDx3dDZAAeBGV
|
Both points were right, and both are fixed in 1.
|
ci.yml's `build-windows` leg runs the FULL suite, and the new harness drives the release step through `bash` + `jq` with a deliberately minimal env. The job under test runs on `ubuntu-latest` and is POSIX shell, so there is no coverage to gain on Windows -- only a portability failure that reads as a product problem. Expressed as an explicit module-level POSIX-only skip, the idiom the suite already uses for this (tests/test_file_locking.py), plus a tool-presence skip for bash/jq. Per ci.yml's own note: a red suite nobody can act on is worse than no suite, because it reads as coverage while hiding things. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RiYtKRNF8XDx3dDZAAeBGV
Resolves the conflicts introduced by #627 (kbagent published through keboola/ai-kit as kbagent@keboola-claude-kit). - README.md, docs/TUTORIAL.md, commands/context.py: the /plugin fences now carry main's marketplace and plugin names plus this branch's /kbagent:setup line. - CLAUDE.md: keeps main's rewritten "source here, publication there" section; its stale surfaces sentence is replaced with this branch's corrected list, made exact against the tree (3 skills, 3 slash commands, 2 subagents). Its install fence gains /kbagent:setup. - plugins/kbagent/commands/setup.md: the claude_plugin bullet no longer paraphrases doctor's drift hint (main qualifies it with the marketplace) and now tells the agent to quote doctor's own /plugin lines as the single source of truth for the names. install.sh already defers to `kbagent doctor` and hardcodes no names, so main's rename does not reach it. No version file and no CI-gated figure is touched.
Requested by Jordan Burger · Slack thread
What it does
Keboola had two Claude Code plugin marketplaces; this leaves one —
keboola-claude-kitinkeboola/ai-kit— and turns this repo's marketplace into a deprecated shim that still serves existing installs.Before / After
Before. Installing the kbagent plugin meant adding this repo as a marketplace. Keboola therefore advertised two competing marketplaces, and a user had no way to tell which was the real one.
After. One marketplace,
keboola-claude-kit, published fromkeboola/ai-kit. The old commands keep working for now; the new ones are what every doc,kbagent context, andkbagent doctortell you.How
The plugin source stays here, in
plugins/kbagent/. Four CI gates in this repo generate and validate those files against the live command tree —make skill-gen/make skill-check,scripts/sync_version.py,scripts/check_command_sync.py,make version-check— and none of them exist in ai-kit, so moving the files would trade one marketplace for a permanent drift risk. What moves is publication: ai-kit's marketplace carries an externalgit-subdirentry pointing atplugins/kbagentin this repo, pinned to a release tag, and a new release job repoints that entry on every stable tag.The syncing mechanism
.github/workflows/release-kbagent.ymlgains anai-kit-marketplacejob, gated exactly like the existinghomebrewjob (needs: [version, freeze, publish-s3], non-prerelease,refs/tags/,environment: release). It checks outkeboola/ai-kitwith a new write-scoped PAT,jq-rewrites thekbagententry'sversionandsource.ref, and opens a PR withgh pr createrather than pushing tomain, so ai-kit's Tier 0 / Tier 1 evals see the change before it ships.Properties worth knowing:
version+source.refvalues before touching the file (jq re-emits the whole document with its own indentation, so agit diffalone would open empty PRs), and exits 0 when they already match. Re-running a tag is free.kbagententry is missing in ai-kit — that means the entry was never registered or was reverted, and silently publishing nothing would be worse.CONTRIBUTING.md's release checklist grows a step 13 saying so, and the sync map grows a row.The deprecation shim, and how long it lives
.claude-plugin/marketplace.jsonis kept, mechanically intact, so existing installs keep resolving updates. The only change is that thekbagententry'sdescriptionnow leads with the migration notice:That description string is what
/pluginlistings show, which makes it the only in-product channel that reaches people already installed from here.scripts/sync_version.pystill round-trips the file (it only writesversion; verified), somake version-checkstays green and the file is untouched in thegit diff --exit-codepath lists in theMakefileand this workflow.Intended lifetime: about three releases. Then drop the entry and leave a
renames: {"kbagent": null}tombstone. Note thatrenamescannot redirect across marketplaces — it only renames within one — so there is no mechanism that migrates an installed user automatically. The migration is necessarily a manual two-line user action, which is exactly why the notice lives in the description and whydoctornags.doctorand the docsdoctor'sclaude_plugincheck now probes both cache dirs, new first:~/.claude/plugins/cache/keboola-claude-kit/kbagent, then the legacy~/.claude/plugins/cache/keboola-agent-cli/kbagent. A plugin found only under the legacy dir still passes — it works and it updates — but the message appends reinstall-from-ai-kit instructions. Thewarnmessage for a missing plugin prints the two new/pluginlines. Three new tests cover the legacy-dir pass, the both-dirs-present preference, and an empty legacy root.AGENT_CONTEXT(kbagent context),README.md,docs/TUTORIAL.md(including the cache path),docs/use-cases.md,plugins/kbagent/skills/kbagent/SKILL.md,plugins/kbagent/.claude-plugin/CLAUDE.md.CLAUDE.md's "This repo doubles as a Claude Code plugin marketplace" framing is rewritten to "the plugin lives here and is published throughkeboola/ai-kit", including why the source stays and why the shim must not be deleted.Version and changelog — deliberately none
This PR no longer bumps the version and no longer adds a
changelog.pyentry. An earlier revision bumped0.86.0→0.87.0(later0.88.0); both were dropped when this branch was rebased onto0.89.0. If you were expecting0.90.0here, this is why you will not find it.maincommit 5313801 (#648, "version bumps move out of feature PRs into dedicated release PRs") now forbids precisely that: a feature PR merges with no change topyproject.toml'sversionand nochangelog.pyentry, and a dedicated release PR batches everything merged since the last release into one bump plus one entry. Its stated rationale is this PR's own failure mode — parallel PRs each bumping the version, colliding on every merge and silently renumbering releases. Accordingly:pyproject.tomlstays atmain's 0.89.0.plugin.json,.claude-plugin/marketplace.jsonanduv.lockare all in lock-step —make version-syncandmake version-checkare both clean no-ops.src/keboola_agent_cli/changelog.pyis byte-identical toorigin/main. Writing the release notes for this change is the next release PR's job; docs: version bumps move out of feature PRs into dedicated release PRs #648 step 1 collects merged PRs fromgit log, so this one lands in its scope automatically.vNEXTplaceholder rather than a guessed number, per the same convention — the release PR rewrites it to the version that actually ships it.CONTRIBUTING.mdwas added to the release checklist'svNEXTgrep (step 4) so that placeholder gets resolved instead of quietly rotting, since the file was not previously in that grep's path list.Dropping the changelog entry costs nothing user-facing: the deprecation notice still ships in the shim entry's
description(the one channel/pluginlistings surface), indoctor's legacy-marketplace message, and inCLAUDE.md,SKILL.mdand the plugin'sCLAUDE.md.Coordination
kbagententry inkeboola-claude-kit. Until it lands, the commands this PR documents do not resolve, and theai-kit-marketplacejob would fail its missing-entry guard.secrets.AI_KIT_TOKENmust be provisioned in thereleaseenvironment before the next release, scoped to contents + pull-requests onkeboola/ai-kitonly. Without it the new job fails (the rest of the release still ships, and no ai-kit PR appears — which is the symptom to look for).Overlap with #625
Draft PR #625 (
feat/kbagent-setup-slash-command, adds/kbagent:setup) touches six of the same files in the same hunks:README.md,docs/TUTORIAL.md,src/keboola_agent_cli/commands/context.py,install.sh,plugins/kbagent/skills/kbagent/SKILL.md,plugins/kbagent/.claude-plugin/CLAUDE.md. I read its diff and kept the edits here surgical — in those six files this PR changes the marketplace/install command lines and nothing adjacent, and the two additions that are not command-line swaps (SKILL.md, pluginCLAUDE.md) are appended clear of #625's hunks.#625 landing first is fine and preferred. Two notes for whoever rebases second:
install.shon currentmaindoes not mention the marketplace, so this PR leaves it alone. feat(plugin): add /kbagent:setup one-command first-run setup #625 adds a/plugin marketplace add keboola/cli, /plugin install kbagent@keboola-agent-cliline to its "Next steps" output — that line needs the new marketplace/plugin names, whether it is fixed in feat(plugin): add /kbagent:setup one-command first-run setup #625 or in a follow-up here.CLAUDE.mdwill conflict: feat(plugin): add /kbagent:setup one-command first-run setup #625 edits the surfaces sentence in the section this PR rewrites wholesale. Keep both — the rewritten framing plus feat(plugin): add /kbagent:setup one-command first-run setup #625's updated surface list.Validation
Re-run after the rebase onto
0.89.0.make checkminuschangelog-check(that target shells out togh, which is not installed in the environment this was authored in): lint, format-check, typecheck,skill-check,version-check,command-sync-check,check-error-codes,check-sentinel-guardsandloc-checkall green. Unit suite: 6009 passed, 13 skipped; the only two failures aretest_config_store.py::TestPermissionDenied::{test_save_to_readonly_directory,test_load_unreadable_config_file}, confirmed to fail identically on a pristineorigin/mainworktree because the authoring environment runs as uid 0 and root ignores thechmodthose tests rely on.tyreports its one pre-existinghatchlingunresolved-import warning (downgraded inpyproject.toml, non-blocking) and exits 0 — also confirmed on pristineorigin/main.make version-syncandmake skill-genboth produce an empty diff. Workflow YAML parses; the new job'sneeds/if/environmentgating is byte-identical to the existinghomebrewandchocolateyjobs, and nothingneeds:it, so a failure there cannot block a release.jq . .claude-plugin/marketplace.jsonis valid, and a fullgrep -rn "marketplace add"over*.md/*.py/*.shshows every live surface onkeboola/ai-kit— the onlykeboola/clihits left are two immutable historicalchangelog.pyentries.Not in this PR
.claude-plugin/marketplace.jsonor itskbagententry, and therenamestombstone — deliberately deferred ~3 releases.keboola/ai-kit(companion PR) or to feat(plugin): add /kbagent:setup one-command first-run setup #625.doctor's JSON output; the signal is prose inmessageonly. Worth adding if anything wants to act on it programmatically.