Skip to content

aitools: report plugin state in list and version#5741

Merged
simonfaltum merged 7 commits into
mainfrom
simonfaltum/aitools-list-version
Jun 29, 2026
Merged

aitools: report plugin state in list and version#5741
simonfaltum merged 7 commits into
mainfrom
simonfaltum/aitools-list-version

Conversation

@simonfaltum

@simonfaltum simonfaltum commented Jun 26, 2026

Copy link
Copy Markdown
Member

Stack

Part of the aitools plugin-first redesign. Open PRs, merge bottom to top:

Merged: #5734, #5736, #5737, #5738, #5739.

Why

list and version only knew about raw skills. Now that the CLI installs plugins, they should report the real per-agent plugin state so users (and the plugin's SessionStart nudge) can see what's installed and whether it's current.

Stacked on #5740 (uninstall).

Changes

  • list --output json gains an additive agents[] array: each plugin agent with a recorded install (its version and an up_to_date / update_available status, managed: true), plus Cursor as manual_add_plugin (managed: false) when it's present. The existing release / skills / summary keys and shapes are byte-identical (additive omitempty field). The text view adds an AGENT / STATUS block only when there are agent entries, leaving the skills table and summary line unchanged.
  • version prints a Plugin (<Agent>): vX line per recorded plugin, and only prints the skills line when skills are actually installed, so a plugin-only install no longer reports "0 skills". Existing skills output is unchanged.

Test plan

  • Unit tests: list JSON includes agents[] with the right statuses/managed flags while keeping release/skills/summary intact; buildAgentEntries maps recorded plugins to up_to_date/update_available and surfaces Cursor as manual; version prints the plugin line for a plugin-only install.
  • Existing list/version tests pass unchanged (JSON round-trip, both-scopes skills output).
  • go test ./libs/aitools/... ./cmd/aitools/... passes.
  • ./task fmt-q, ./task lint-q (0 issues), ./task checks (no dead code) clean.

This pull request and its description were written by Isaac.

@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-uninstall branch from 4c63508 to 97b3066 Compare June 26, 2026 12:46
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-list-version branch from 73479ff to 09d6577 Compare June 26, 2026 12:47
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 26, 2026
…atabricks#5734)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Merge bottom to top:

- **databricks#5734 Detection & registry (plugin metadata + capability detection)
← this PR**
- databricks#5736 State schema v2 (plugin + file provenance records)
- databricks#5737 Plugin engine, checksums, `--path` dump (library)
- databricks#5738 Plugin-first install command
- databricks#5739 Update + prune of vanished skills
- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
<!-- /aitools-stack -->

## Why

`databricks aitools` is being redesigned from a raw-skills installer
into a plugin-first installer. The first thing that redesign needs is
better agent detection: today an agent is "detected" only if its config
dir exists on disk, which misses Codex and Copilot (often used through
an IDE, or installed-but-not-yet-run), and the registry has no notion of
which agents have a plugin or what their CLI binary is.

This PR lays that foundation. It is deliberately behavior-neutral: no
command output changes, and `DetectInstalled` still uses the same
config-dir signal it does today. The picker and install rewrites that
consume this come in later PRs in the stack.

## Changes

Before: the agent registry only knew each agent's config dir; detection
was a single `os.Stat` on that dir.

Now: the registry also describes each agent's plugin and CLI binary, and
there's a capability detector that can tell the difference between
"binary on PATH" and "config dir exists."

- `Agent.Binary` — the CLI binary name on PATH (`claude`, `codex`,
`copilot`, `cursor-agent`, `opencode`; empty for IDE-only Antigravity).
Cursor's binary is `cursor-agent`, not `cursor` (the latter is an IDE
shim that isn't on PATH).
- `Agent.Plugin *PluginSpec` — describes the databricks plugin. `Plugin
!= nil` means the agent has a plugin (Claude, Codex, Copilot, Cursor);
`nil` means raw skills are the only delivery (OpenCode, Antigravity).
`ManualOnly` marks Cursor (plugin exists, but no headless install path).
- OpenCode config dir is now per-OS: `%APPDATA%\opencode` on Windows,
and honors `XDG_CONFIG_HOME` otherwise (defaulting to
`~/.config/opencode`). The previous hardcoded path made OpenCode
undetectable on Windows and ignored `XDG_CONFIG_HOME` on Linux.
- New `detect.go`: `HasBinary` (via `exec.LookPath`), `DisplayState` (a
5-state enum for the picker, computed from the two cheap signals without
running the agent), `Preselect`, and `ProbePlugin` (runs `<agent> plugin
--help` with a 5s timeout through `libs/process`; refuses a cwd-relative
binary via `exec.ErrDot` so a malicious `./claude` is never executed).

## Test plan

- [x] Unit tests (`detect_test.go`): `HasBinary`, `DisplayState` across
all five states, `Preselect` rules, `ProbePlugin` (supported / CLI
reports unsupported / binary not on PATH / refuses dot-relative binary
with zero executions), and `openCodeConfigDir` (XDG honored /
`~/.config` default; Windows APPDATA branch on Windows hosts).
- [x] `go test ./libs/aitools/... ./cmd/aitools/...` passes (no behavior
change to existing commands).
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) all clean.

This pull request and its description were written by Isaac.
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-uninstall branch from 97b3066 to 07b7afa Compare June 26, 2026 15:11
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-list-version branch from 09d6577 to 821ac03 Compare June 26, 2026 15:12
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-uninstall branch from 07b7afa to e656bad Compare June 26, 2026 15:54
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-list-version branch from 821ac03 to 2ac9769 Compare June 26, 2026 15:54
…install

- uninstall now tears down plugins: for each agent in state.Plugins (full
  uninstall only), it removes the plugin through the agent's own CLI and
  de-registers the marketplace, but only when this CLI registered it and
  --keep-marketplace wasn't passed. Skills teardown is unchanged for file
  agents; the state file is removed only when no skills and no plugins remain.
- install now cleans up legacy raw skills on a plugin agent
  (RemoveLegacyRawSkills): after installing the plugin we remove skill dirs we
  previously dropped there (a symlink into our canonical dir, or a copy whose
  files all match recorded checksums), so the plugin and leftover files don't
  surface the same skills twice. User-modified or third-party dirs, and copies
  with no recorded provenance, are left untouched.

Co-authored-by: Isaac
The merge of the install-command review fix (which removed log from selectAgents)
crossed with this branch's cleanup log.Debugf usage; re-add the import.

Co-authored-by: Isaac
…s, marketplace)

Address cursor review of uninstall + cleanup:

- RemoveLegacyRawSkills now reuses the exact-match exposureRemovable/
  dirMatchesRecords helpers from update, so a copied skill dir with an extra
  user-added file is no longer deleted (and the duplicated checksum logic is
  removed). Test covers the extra-file keep case.
- Uninstall no longer drops the state record for an unknown agent without tearing
  it down; it warns and keeps the record (and the state file).
- UninstallPluginForAgent now only returns an error when the plugin itself can't
  be removed. A marketplace de-register failure after a successful plugin
  uninstall is warned about (not fatal), so the plugin record is cleared and a
  retry isn't stuck repeating the plugin uninstall.

Co-authored-by: Isaac
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-uninstall branch from e656bad to 7014345 Compare June 26, 2026 17:53
- list --output json gains an additive agents[] array: each plugin agent with a
  recorded install (version + up_to_date/update_available status, managed=true)
  plus Cursor as manual_add_plugin (managed=false) when present. The existing
  release/skills/summary keys and shapes are byte-identical. The text view adds
  an AGENT/STATUS block only when there are agent entries, leaving the skills
  table and summary line unchanged.
- version prints a "Plugin (<Agent>): vX" line per recorded plugin and only
  prints the skills line when skills are installed, so a plugin-only install no
  longer reports "0 skills". Existing skills output is unchanged.

Co-authored-by: Isaac
Address cursor review: buildAgentEntries returned the first scope's record, so a
stale project-scoped plugin could be hidden behind an up-to-date global one. Now
status aggregates across scopes (any out-of-date scope reports update_available)
and version reflects the record that set the status. Test covers the dual-scope
case.

Co-authored-by: Isaac
@simonfaltum simonfaltum force-pushed the simonfaltum/aitools-list-version branch from 2ac9769 to 837a7ad Compare June 26, 2026 18:04
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 27, 2026
…ks#5736)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Merge bottom to top:

- databricks#5734 Detection & registry (plugin metadata + capability detection)
- **databricks#5736 State schema v2 (plugin + file provenance records) ← this PR**
- databricks#5737 Plugin engine, checksums, `--path` dump (library)
- databricks#5738 Plugin-first install command
- databricks#5739 Update + prune of vanished skills
- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
<!-- /aitools-stack -->

## Why

The plugin-first `aitools` redesign needs `.state.json` to record two
new things: which plugins we installed through each agent's own CLI (so
`update`/`uninstall`/`list` act on exactly where we installed), and
provenance for the skill files we wrote (so a later `update` can prune a
skill that vanished from the release only when the user hasn't modified
it). This PR adds that schema, additively, ahead of the
install/update/uninstall changes that consume it.

Stacked on databricks#5734 (detection foundation).

## Changes

Before: `InstallState` tracked only `skills`/`repo_dirs` (schema v1).

Now: schema v2 adds two optional maps, and existing state migrates
forward transparently.

- `PluginRecord` (in `InstallState.Plugins`, keyed by agent name):
marketplace, plugin id, agent-native scope, last-seen version, and
`installed_marketplace` (whether this CLI registered the marketplace, so
uninstall knows if it may de-register it).
- `FileRecord` (in `InstallState.Files`, keyed by canonical-relative
path like `databricks/SKILL.md`): per-file `sha256` + `origin` ref.
- Both maps are `json:",omitempty"`, so a files-only install serializes
byte-identically to today.
- `LoadState` runs `migrateState`: forward-only and idempotent. v1 → v2
needs no data transformation (the maps are additive and optional), so it
only stamps the version; writers lazily initialize the maps the same way
`RepoDirs` is handled.
- The fresh-install writer now stamps v2 so the on-disk and in-memory
versions agree.

## Test plan

- [x] Unit tests: v1→v2 migration is additive (existing skills
untouched, new maps nil), migration is idempotent, and a state with
populated `Plugins`/`Files` round-trips through save/load. Existing
round-trip fixtures updated to v2.
- [x] `go test ./libs/aitools/... ./cmd/aitools/...` passes.
- [x] `go test ./acceptance -run TestAccept/experimental/aitools` passes
(`.state.json` is in `Ignore`, so the version bump doesn't touch
goldens).
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 27, 2026
…tabricks#5737)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Merge bottom to top:

- databricks#5734 Detection & registry (plugin metadata + capability detection)
- databricks#5736 State schema v2 (plugin + file provenance records)
- **databricks#5737 Plugin engine, checksums, `--path` dump (library)  ← this PR**
- databricks#5738 Plugin-first install command
- databricks#5739 Update + prune of vanished skills
- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
<!-- /aitools-stack -->

## Why

The plugin-first redesign installs the databricks plugin by driving each
agent's own plugin CLI (`claude plugin install`, etc.), and on `update`
it needs to be able to prune a skill that vanished from a release
without clobbering files the user edited. This PR adds the library layer
for both, with no command wiring yet, so the engine can be reviewed and
unit-tested on its own. The install/update/uninstall commands call into
it in later PRs.

Stacked on databricks#5736 (state schema v2).

## Changes

- `plugin.go` — the plugin engine. It drives each agent's CLI through
`libs/process` (so tests mock it with `process.WithStub`):
- `InstallPluginForAgent` registers the marketplace and installs the
plugin, recording whether *we* added the marketplace.
- `UpdatePluginForAgent` runs the per-agent update (Codex's two-step
`marketplace upgrade` then `plugin add` is encoded).
- `UninstallPluginForAgent` removes the plugin and de-registers the
marketplace **only** when this CLI registered it and
`--keep-marketplace` wasn't set — never a marketplace another plugin may
share.
- Argv is built per agent (Codex uses `plugin add`; Claude is the only
`--scope` agent) and run by absolute path; a cwd-relative binary
(`exec.ErrDot`) is refused, never executed.
- A blocked operation returns a typed `*BlockedError` (`CLINotOnPath` /
`InstallFailed` / `ManualOnly`), so the command layer can report it and
decide skip-vs-fail — it never silently falls back to skills. The
agent's own stderr is surfaced via `errors.AsType`, never
string-matched.
- `installer.go` — `installSkillToDir` now records a sha256 `FileRecord`
per file it writes (origin = ref); `InstallSkillsForAgents` and
`UpdateSkills` persist these to `state.Files` for the prune safeguard.
The symlink-vs-copy rules are unchanged.
- `dump.go` — `DumpSkillsToPath`, a dumb `--path` dump (no agents, no
`.state.json`, no lifecycle) that reuses the existing
manifest/resolve/fetch path.

## Test plan

- [x] `plugin_test.go` via `process.WithStub` + injected `lookPath`:
Claude success (marketplace add + `--scope user` install), Codex uses
`plugin add` with no `--scope`, manual-only (Cursor) → `ManualOnly`,
CLI-not-on-path → `CLINotOnPath` with zero executions, install failure →
`InstallFailed` surfacing stderr verbatim, marketplace-already-present
leaves `InstalledMarketplace=false`, Codex two-step update, marketplace
de-register only when we installed it and `--keep-marketplace` honored.
- [x] `dump_test.go`: writes files, writes no state, honors `--skills`
cherry-pick.
- [x] `installer_test.go`: install captures file checksums into
`state.Files`.
- [x] `go test ./libs/aitools/... ./cmd/aitools/...` and `go test
./acceptance -run TestAccept/experimental/aitools` pass.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code — engine funcs are reachable via tests until the command layer
lands) clean.

This pull request and its description were written by Isaac.
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 27, 2026
…-only) (databricks#5738)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Merge bottom to top:

- databricks#5734 Detection & registry (plugin metadata + capability detection)
- databricks#5736 State schema v2 (plugin + file provenance records)
- databricks#5737 Plugin engine, checksums, `--path` dump (library)
- **databricks#5738 Plugin-first install command  ← this PR**
- databricks#5739 Update + prune of vanished skills
- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
<!-- /aitools-stack -->

## Why

This is the user-visible flip of the `aitools` redesign: `databricks
aitools install` now installs the Databricks plugin through each coding
agent's own CLI instead of copying raw skill files into every agent. Raw
skills alongside a plugin produce duplicate, hard-to-update content; the
plugin is how those agents expect third-party content to arrive.

Stacked on databricks#5737 (plugin engine).

## Changes

Before: install copied skill files into each detected agent's skills
dir.

Now: install is plugin-first, with explicit escape hatches.

- **Delivery per agent:** plugin agents (Claude Code, Codex, Copilot)
get the databricks plugin; agents with no plugin (OpenCode, Antigravity)
get skill files; Cursor (plugin, but no headless install) prints the
`/add-plugin databricks` step and copies nothing.
- **Escape hatches:** `--skills-only` forces raw skill files for every
agent; `--path <dir>` is a dumb dump (no agents, no state).
`--skills-only` + `--path` is rejected.
- **Scope mapping** (`mapAgentScope`): CLI `global` → agent user scope;
CLI `project` → agent project scope only where supported (Claude),
otherwise the agent is skipped with a reason. No silent fallback to user
scope.
- **No silent fallback:** a blocked plugin install (CLI not on PATH,
install failure) is reported and skipped (exit 0), never swapped for
skills. It errors only when the agent was explicitly named via
`--agents`.
- **Interactive picker** lists every known agent with a detection-state
label (detected ones pre-checked), then shows a plan summary and a
single confirm.
- **Detection:** non-interactive selection now also detects plugin
agents by their CLI binary on PATH (fixing the Codex/Copilot config-dir
miss); `--skills-only` selection stays config-dir based and
PATH-independent.
- The legacy `experimental skills install` alias pins `--skills-only` to
preserve its behavior.

## Test plan

- [x] Unit tests: `buildPlan` deliveries (plugin/skills/manual/skip) and
`--skills-only` forcing skills; `mapAgentScope` matrix; `executePlan`
skip-with-warning vs error-on-explicit; plugin-first default (with a
controlled PATH), `--agents` for an undetected agent, no-agents no-op,
`--skills-only`/`--path` conflict, scope flags, interactive picker +
confirm.
- [x] Acceptance: the 3 existing `experimental aitools install` goldens
switch to `--skills-only` and keep their behavior strings byte-identical
(only the `>>>` echo gains the flag); a new `path-dump` test covers
`--path`.
- [x] `go test ./libs/aitools/... ./cmd/aitools/... ./acceptance -run
TestAccept/experimental/aitools` passes.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 27, 2026
)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Merge bottom to top:

- databricks#5734 Detection & registry (plugin metadata + capability detection)
- databricks#5736 State schema v2 (plugin + file provenance records)
- databricks#5737 Plugin engine, checksums, `--path` dump (library)
- databricks#5738 Plugin-first install command
- **databricks#5739 Update + prune of vanished skills  ← this PR**
- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
<!-- /aitools-stack -->

## Why

With install now plugin-first, `update` has to know about both worlds:
update the plugin for plugin agents, and reconcile raw skills for the
rest. This PR also makes the one intended behavior change from the
redesign: a skill that disappears from a release is now removed, not
warned-about-and-kept, so users don't accumulate dead skills forever.

Stacked on databricks#5738 (install command).

## Changes

Before: `update` only reconciled skill files, and a skill that vanished
from the manifest was kept with a warning.

Now:

- **Plugin agents:** every agent recorded in `state.Plugins` is updated
through its own plugin CLI (`UpdateInstalledPlugins`). The plugin's own
update handles content the release dropped, so plugin agents have no
per-skill prune.
- **Prune vanished skills:** a skill that disappeared from the manifest
is pruned, but **only** when the CLI installed it and the on-disk files
still match the recorded sha256. A user-modified skill, or one with no
recorded provenance (legacy v1 state), is kept with a warning.
`--no-prune` keeps vanished skills; `--check` previews the removals
without deleting.
- **No duplicate skills:** the file-skills reconcile excludes agents
managed as plugins in this scope (so a plugin agent never gets duplicate
raw skill files), and is skipped entirely for a pure-plugin install.
- `FormatUpdateResult` gains "removed" lines and a "Removed N skills."
summary; the existing updated/added lines and summary are
byte-identical.

## Test plan

- [x] Unit tests: prune of a vanished unmodified skill (state +
canonical dir cleaned), `--no-prune` keeps it, a user-modified vanished
skill is kept, `--check` shows the prune without deleting,
`UpdateInstalledPlugins` runs the plugin update and bumps the recorded
version, and the `--no-prune` flag wiring.
- [x] Acceptance: new `update-prune` test installs two skills then
updates against a release missing one — alpha updates, beta is pruned
("Removed 1 skill.").
- [x] `go test ./libs/aitools/... ./cmd/aitools/... ./acceptance -run
TestAccept/experimental/aitools` passes.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
Comment thread cmd/aitools/list.go Outdated
Comment thread cmd/aitools/list.go Outdated
Comment thread cmd/aitools/list.go Outdated
Comment thread cmd/aitools/list.go
Base automatically changed from simonfaltum/aitools-uninstall to main June 29, 2026 07:22
Address review on #5741:

- agentEntry now mirrors skillEntry: Installed maps scope -> {version}
  with a top-level Managed flag. buildAgentEntries records each scope's
  version as-is and the text renderer collapses them, surfacing a stale
  scope over an up-to-date one. Up-to-date-ness is derived from version
  vs release like the skills view, so the cross-scope merge is gone.
- buildAgentEntries takes a scope->state map containing only non-nil
  states; nil filtering moved to the call site.
- agents.Registry is now []*Agent, dropping the &Registry[i] idiom in
  list/install/update/uninstall and simplifying ByName/DetectInstalled.
@simonfaltum simonfaltum added this pull request to the merge queue Jun 29, 2026
Merged via the queue into main with commit 979871f Jun 29, 2026
21 checks passed
@simonfaltum simonfaltum deleted the simonfaltum/aitools-list-version branch June 29, 2026 07:59
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 29, 2026
…all (databricks#5740)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Open PRs, merge bottom to
top:

- **databricks#5740 Uninstall teardown + legacy skills cleanup  ← this PR**
- databricks#5741 list + version plugin state
- databricks#5745 Bug bash (wording, latest-by-default skills, project-scope plan,
version reporting)
- databricks#5746 Bug bash round 2 (uninstall confirm, picker, Cursor, official
marketplace, version scope)

Merged: databricks#5734, databricks#5736, databricks#5737, databricks#5738, databricks#5739.
<!-- /aitools-stack -->

## Why

To finish the lifecycle: `uninstall` has to remove the plugin we
installed (not just skills), and `install` has to clean up any raw
skills a user had from the old skills-everywhere model, so a plugin
agent doesn't end up with the plugin *and* leftover loose skill files
showing the same skills twice.

Stacked on databricks#5739 (update).

## Changes

- **Uninstall tears down plugins.** On a full uninstall (no `--skills`
filter), each agent recorded in `state.Plugins` has its plugin removed
through the agent's own CLI, and the marketplace is de-registered, but
only when this CLI registered it and `--keep-marketplace` wasn't passed,
so we never remove a marketplace another plugin may share. Skills
teardown is unchanged for file agents; the state file is removed only
when no skills and no plugins remain in the scope.
- **Install cleans up legacy raw skills.** After installing the plugin
for an agent, `RemoveLegacyRawSkills` removes skill dirs the CLI
previously dropped there: a symlink pointing into our canonical dir, or
a copy whose files all match the recorded checksums. User-modified dirs,
third-party dirs, and copies with no recorded provenance are left
untouched.
- New `--keep-marketplace` flag on `uninstall`.

## Test plan

- [x] Unit tests: uninstall tears down the plugin and de-registers the
marketplace, removing the state file when nothing remains;
`--keep-marketplace` skips the de-register; `RemoveLegacyRawSkills`
removes our symlink and a checksum-matched copy while keeping a
user-modified copy and a third-party dir.
- [x] Existing skills-uninstall tests still pass byte-for-byte
("Uninstalled N skills.", selective removal, orphan cleanup, "no skills
installed").
- [x] `go test ./libs/aitools/... ./cmd/aitools/...` passes.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 29, 2026
…lan, version reporting) (databricks#5745)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Open PRs, merge bottom to
top:

- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
- **databricks#5745 Bug bash (wording, latest-by-default skills, project-scope
plan, version reporting) ← this PR**
- databricks#5746 Bug bash round 2 (uninstall confirm, picker, Cursor, official
marketplace, version scope)

Merged: databricks#5734, databricks#5736, databricks#5737, databricks#5738, databricks#5739.
<!-- /aitools-stack -->

## Why

Testing the built CLI surfaced several issues in `databricks aitools`:

1. Inconsistent, plugin-blind copy ("AI Tools" / "AI skills") even
though most agents now get a plugin.
2. Skills pinned to a cli-compat version (e.g. `0.2.5`) while plugin
agents installed latest (e.g. `0.2.7`). Two different versions for the
same content, and list/version misreported the plugin version.
3. Project-scope install offered files-only agents (OpenCode,
Antigravity) that can't do project scope, then failed after the fact
without installing anything.

## Changes

**Wording.** Everything under `databricks aitools` now reads "skills and
plugins" (help, version output, install log); empty-state errors say "no
skills or plugins installed". The legacy skills alias and `experimental
aitools` are untouched.

**Skills track latest by default.** Plugin agents' CLIs have no version
flag, so they only ever install the marketplace's latest. Skills now
match that:

- `GetSkillsRef` precedence: `DATABRICKS_SKILLS_REF` (exact ref, for
evals), then cli-compat.json, then default latest (`main`).
- cli-compat.json is the remote safety valve: its `skills` field is now
`"latest"` (track latest), but it can be edited to a concrete version to
pin older CLIs after a breaking change, with no CLI release needed.
AppKit version resolution is unchanged (cli-compat still pins it via its
own field).
- Plugins (and unpinned skills) report `latest` instead of a stale
concrete version, so list/version are honest.
- `update` reconciles against latest when unpinned instead of falsely
reporting "already up to date".

**Project-scope plan is honest.** `buildPlan` skips files-only agents
that don't support project scope (the way plugin agents already were),
and the picker is scope-aware, so incompatible agents are labeled and
not pre-checked. No more "offer then fail with nothing installed".

## Test plan

- [x] Unit: `GetSkillsRef` (default latest / env pin / cli-compat pin),
`DisplaySkillsVersion`, `buildPlan` project-scope skip for files-only
agents; clicompat allows the `latest` sentinel.
- [x] Acceptance `experimental/aitools` unchanged (tests pin the ref via
env).
- [x] `go test ./libs/aitools/... ./cmd/aitools/...
./libs/clicompat/...` passes.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
bradleyjamrozik-origindigital pushed a commit to bradleyjamrozik-origindigital/databricks-cli that referenced this pull request Jun 29, 2026
… marketplace) (databricks#5746)

<!-- aitools-stack -->
### Stack

Part of the `aitools` plugin-first redesign. Open PRs, merge bottom to
top:

- databricks#5740 Uninstall teardown + legacy skills cleanup
- databricks#5741 list + version plugin state
- databricks#5745 Bug bash (wording, latest-by-default skills, project-scope plan,
version reporting)
- **databricks#5746 Bug bash round 2 (uninstall confirm, picker, Cursor, official
marketplace, version scope) ← this PR**

Merged: databricks#5734, databricks#5736, databricks#5737, databricks#5738, databricks#5739.
<!-- /aitools-stack -->

## Why

A second round of testing the plugin-first `databricks aitools` CLI.
Findings are
tracked in `documents/proposals/aitools-bugbash-findings.md`.

## Changes

- **uninstall confirms in interactive mode.** It removed skills and
de-registered
plugins immediately; now (on a TTY) it summarizes what will be removed
and asks
  "Proceed?". Non-interactive runs are unaffected.
- **version labels the scope.** Skills and plugins are now always
labeled
  global/project (e.g. `Plugin (Claude Code, global): latest`).
- **Claude installs from the official marketplace.** The databricks
plugin is in
Claude's built-in `claude-plugins-official` marketplace, so Claude
installs
  `databricks@claude-plugins-official`. A built-in marketplace (empty
PluginSpec.Source) is never added or de-registered. Codex/Copilot keep
our own
  marketplace.
- **Picker only offers actionable agents.** Every detected agent still
shows in the
detection list with its state/reason, but only agents that will do
something
(plugin or skills) are selectable; agents skipped in the scope are no
longer
  checkboxes.
- **Cursor is a plain skills agent.** Cursor was a confusing "manual,
run
/add-plugin" option that did nothing. Since the Cursor plugin can't be
installed
or interacted with headlessly, all references to it are removed: Cursor
now
installs raw skills like OpenCode/Antigravity, labeled simply "skills".
This
removed the whole manual-only concept
(PluginSpec.ManualOnly/ManualInstructions,
StateManualOnly, ReasonManualOnly, the manual_add_plugin list status,
and the
  deliveryManualCursor path).
- **Help links the source repo.** `databricks aitools` help points at
https://github.com/databricks/databricks-agent-skills so users know
where skills
  and plugins come from.

## Test plan

- [x] Unit: uninstall confirm; version scope labels; built-in
marketplace install
(no `marketplace add`) and uninstall (no de-register); picker offers
only
  actionable agents; Cursor plan = skills; no-plugin install guard.
- [x] `go test ./libs/aitools/... ./cmd/aitools/...` and acceptance
`experimental/aitools` pass.
- [x] `./task fmt-q`, `./task lint-q` (0 issues), `./task checks` (no
dead code) clean.

This pull request and its description were written by Isaac.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants