Skip to content

aitools list: emit JSON via --output json#5233

Draft
jamesbroadhead wants to merge 4 commits into
jbroadhead/aitools-publicfrom
jb/aitools-list-json
Draft

aitools list: emit JSON via --output json#5233
jamesbroadhead wants to merge 4 commits into
jbroadhead/aitools-publicfrom
jb/aitools-list-json

Conversation

@jamesbroadhead
Copy link
Copy Markdown

Summary

databricks aitools list learns --output json, emitting a structured document so coding agents and CI can consume the skill/version/installation matrix without scraping the tabwriter text output. Text rendering is unchanged.

Stacked on #4917 (uses --scope and the moved-to-top-level aitools/ package). Base will rebase to main once #4917 merges.

JSON shape

{
  "release": "0.1.0",
  "skills": [
    {
      "name": "databricks-jobs",
      "latest_version": "1.0.0",
      "experimental": false,
      "installed": { "global": "1.0.0", "project": "0.9.0" }
    }
  ],
  "summary": {
    "global":  { "installed": 5, "total": 10 },
    "project": { "installed": 3, "total": 10 }
  }
}
  • installed is keyed by scope; absent key = not installed in that scope; empty map = not installed anywhere.
  • summary only includes scopes that were queried, so --scope=global narrows it to one key.
  • release is the version string without the v prefix.

This is the documented public contract — field names and types should not change without a major version bump.

Why

aitools list is one of the surfaces an agent reaches for first ("what's installed, what's available, what's stale"). Scraping tabwriter columns from stderr is fragile; a stable JSON contract makes the command declarative for non-human callers. Matches the convention used by other CLI commands that already honor --output json (bundle validate, pipelines run, etc.).

Test plan

  • databricks aitools list --output json against a workspace with a mix of installed/uninstalled skills, both scopes — JSON validates against the shape above.
  • databricks aitools list --output json --scope=globalsummary only contains global.
  • databricks aitools list (no --output) — output is byte-for-byte unchanged from main.
  • Unit: TestRenderListJSON, TestRenderListJSONScopeFiltersSummary, TestInstalledStatusFromEntry cover the rendering paths.

This pull request was AI-assisted by Isaac.

Replace the two-boolean --project/--global pair on install/update/uninstall/list
with --scope=project|global (and --scope=both on update/list). The old booleans
keep working with a cobra deprecation warning so existing scripts continue to
run; they'll be removed in a later release.

Why now: aitools just left experimental in this PR, so this is the cheapest
moment to fix the interface before external scripts start to depend on the
two-boolean shape. An enum is friendlier for agent-driven invocations than a
pair of booleans with implicit precedence.

Co-authored-by: Isaac
Pin down the contract for non-interactive callers (CI, agents) by
documenting that an unset --agents flag means "install for every detected
agent" outside a TTY. The selected list is already logged to stderr via
PrintInstallingFor before the install runs, so callers can verify what
was picked.

Co-authored-by: Isaac
Refactor list rendering to build a structured listOutput first and dispatch
on root.OutputType(cmd) for text vs json. JSON shape:

  {
    "release": "0.1.0",
    "skills": [{ "name": "...", "latest_version": "...",
                 "experimental": false,
                 "installed": { "global": "...", "project": "..." } }],
    "summary": { "global": { "installed": N, "total": M },
                 "project": { "installed": N, "total": M } }
  }

The installed map omits scopes where the skill isn't present. The summary
only includes scopes that were queried, so --scope=global narrows it. The
text rendering path is byte-for-byte unchanged from the prior implementation.

Why: aitools list is one of the surfaces an agent reaches for first
(\"what's installed, what's available, what's stale\"). Scraping tabwriter
columns from stderr is fragile; a stable JSON contract makes the command
declarative for non-human callers.

Depends on #4917.

Co-authored-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.

1 participant