diff --git a/.agent/skills/pr-checklist/SKILL.md b/.agent/skills/pr-checklist/SKILL.md index 3eae258d6e..6b4f920f7d 100644 --- a/.agent/skills/pr-checklist/SKILL.md +++ b/.agent/skills/pr-checklist/SKILL.md @@ -22,7 +22,7 @@ Before submitting a PR, run these commands to match what CI checks. CI uses the # 5. If you changed files in python/: ./task pydabs-codegen pydabs-test pydabs-lint pydabs-docs -# 6. If you changed experimental/aitools or experimental/ssh: +# 6. If you changed aitools or experimental/ssh: ./task test-exp-aitools # only if aitools code changed ./task test-exp-ssh # only if ssh code changed ``` diff --git a/.github/OWNERS b/.github/OWNERS index 7cae525465..592bd57e13 100644 --- a/.github/OWNERS +++ b/.github/OWNERS @@ -59,5 +59,5 @@ # Internal /internal/ team:platform -# Experimental -/experimental/aitools/ team:eng-apps-devex @lennartkats-db +# AI tools +/aitools/ team:eng-apps-devex @lennartkats-db diff --git a/NEXT_CHANGELOG.md b/NEXT_CHANGELOG.md index 00152d550e..7d575bb10f 100644 --- a/NEXT_CHANGELOG.md +++ b/NEXT_CHANGELOG.md @@ -4,6 +4,8 @@ ### CLI +* Promote the aitools skills-management surface (`install`, `update`, `uninstall`, `list`, `version`, `skills`) from `databricks experimental aitools` to top-level `databricks aitools`. The old `experimental aitools` path remains as a hidden, deprecated alias for these commands and prints a deprecation warning. The `tools` subtree (`query`, `discover-schema`, `get-default-warehouse`, `statement …`) stays under `databricks experimental aitools tools` for now and does not warn. + ### Bundles ### Dependency updates diff --git a/Taskfile.yml b/Taskfile.yml index 912b3f666a..fde36b2f64 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -606,9 +606,9 @@ tasks: # generic `test` target (the catch-all) instead. test-exp-aitools: - desc: Run experimental aitools unit and acceptance tests + desc: Run aitools unit and acceptance tests sources: - - experimental/aitools/** + - aitools/** - acceptance/apps/** - "{{.EMBED_SOURCES}}" cmds: @@ -616,7 +616,7 @@ tasks: {{.GO_TOOL}} gotestsum \ --format ${GOTESTSUM_FORMAT:-pkgname-and-test-fails} \ --no-summary=skipped \ - --packages ./experimental/aitools/... \ + --packages ./aitools/... \ -- -timeout=${LOCAL_TIMEOUT:-30m} - | {{.GO_TOOL}} gotestsum \ diff --git a/acceptance/help/output.txt b/acceptance/help/output.txt index d9f379f5bb..159eca916b 100644 --- a/acceptance/help/output.txt +++ b/acceptance/help/output.txt @@ -167,6 +167,7 @@ Developer Tools Additional Commands: account Databricks Account Commands + aitools Databricks AI Tools for coding agents api Perform Databricks API call auth Authentication related commands cache Local cache related commands diff --git a/aitools/README.md b/aitools/README.md new file mode 100644 index 0000000000..404a44823c --- /dev/null +++ b/aitools/README.md @@ -0,0 +1,72 @@ +# AI Tools + +`databricks aitools` exposes commands for coding-agent workflows. + +Two surfaces with different stability tiers: + +- **`databricks aitools …`** (top-level) — skills management. Stable. +- **`databricks experimental aitools tools …`** — workspace helpers used by + AI agents. Still experimental; commands and output may change. + +## Skills management (stable) + +Top-level commands for installing and managing Databricks skills in detected +coding agents: + +- `databricks aitools install [skill-name]` +- `databricks aitools update` +- `databricks aitools uninstall` +- `databricks aitools list` +- `databricks aitools version` + +The hidden `databricks aitools skills {list,install}` subgroup exists as a +backward-compatibility alias for the flat top-level commands. + +## Tools (experimental) + +These commands live under `databricks experimental aitools tools` and are +optimized for AI coding agents like Claude Code and Cursor. Names and output +shapes can change. + +- `databricks experimental aitools tools query` +- `databricks experimental aitools tools discover-schema` +- `databricks experimental aitools tools get-default-warehouse` +- `databricks experimental aitools tools statement submit` +- `databricks experimental aitools tools statement get` +- `databricks experimental aitools tools statement status` +- `databricks experimental aitools tools statement cancel` + +### `tools query` + +Accepts a single SQL or multiple SQLs in one invocation. Pass several +positional arguments and/or repeat `--file` to run them in parallel against the +warehouse. Multi-query output is always JSON; control parallelism with +`--concurrency` (default 8). + +```bash +databricks experimental aitools tools query \ + --warehouse --output json \ + "SELECT count(*) FROM samples.nyctaxi.trips" \ + "SELECT min(tpep_pickup_datetime), max(tpep_pickup_datetime) FROM samples.nyctaxi.trips" \ + "SELECT vendor_id, count(*) FROM samples.nyctaxi.trips GROUP BY 1" +``` + +### `tools statement` + +Low-level lifecycle for asynchronous statements. `submit` returns a +`statement_id` immediately, `get` polls until terminal and emits rows, `status` +peeks without blocking, and `cancel` requests termination. Ctrl+C on `get` +stops polling but does NOT cancel the server-side statement; use `cancel` for +that. + +```bash +SID=$(databricks experimental aitools tools statement submit \ + --warehouse "SELECT pg_sleep(5)" | jq -r '.statement_id') +databricks experimental aitools tools statement status "$SID" +databricks experimental aitools tools statement get "$SID" +``` + +## Removed behavior + +- there is no MCP server under `aitools` +- the old `deploy` and `validate` flows were removed diff --git a/aitools/cmd/aitools.go b/aitools/cmd/aitools.go new file mode 100644 index 0000000000..0d99dcd986 --- /dev/null +++ b/aitools/cmd/aitools.go @@ -0,0 +1,38 @@ +package aitools + +import ( + "github.com/spf13/cobra" +) + +// NewAitoolsCmd builds the skills-management surface — install, update, +// uninstall, list, version, and the (hidden) skills alias group. The `tools` +// subtree is intentionally not attached here: it remains under +// `databricks experimental aitools tools` until it graduates separately. See +// NewToolsCmd. +func NewAitoolsCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "aitools", + Short: "Databricks AI Tools for coding agents", + Long: `Manage Databricks AI Tools. + +Provides commands to: +- Install the AI tools in coding agents (install) +- Manage skills (skills)`, + } + + cmd.AddCommand(newInstallCmd()) + cmd.AddCommand(newUpdateCmd()) + cmd.AddCommand(newUninstallCmd()) + cmd.AddCommand(newListCmd()) + cmd.AddCommand(newVersionCmd()) + cmd.AddCommand(newSkillsCmd()) + + return cmd +} + +// NewToolsCmd exposes the experimental tools subtree (query, discover-schema, +// get-default-warehouse, statement) so wrappers can attach it under their own +// parent. Currently only `databricks experimental aitools tools`. +func NewToolsCmd() *cobra.Command { + return newToolsCmd() +} diff --git a/experimental/aitools/cmd/batch.go b/aitools/cmd/batch.go similarity index 100% rename from experimental/aitools/cmd/batch.go rename to aitools/cmd/batch.go diff --git a/experimental/aitools/cmd/batch_test.go b/aitools/cmd/batch_test.go similarity index 100% rename from experimental/aitools/cmd/batch_test.go rename to aitools/cmd/batch_test.go diff --git a/experimental/aitools/cmd/discover_schema.go b/aitools/cmd/discover_schema.go similarity index 98% rename from experimental/aitools/cmd/discover_schema.go rename to aitools/cmd/discover_schema.go index 091222368d..6110505ccc 100644 --- a/experimental/aitools/cmd/discover_schema.go +++ b/aitools/cmd/discover_schema.go @@ -12,9 +12,9 @@ import ( "sync" "syscall" + "github.com/databricks/cli/aitools/lib/middlewares" + "github.com/databricks/cli/aitools/lib/session" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/experimental/aitools/lib/middlewares" - "github.com/databricks/cli/experimental/aitools/lib/session" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" diff --git a/experimental/aitools/cmd/discover_schema_test.go b/aitools/cmd/discover_schema_test.go similarity index 100% rename from experimental/aitools/cmd/discover_schema_test.go rename to aitools/cmd/discover_schema_test.go diff --git a/experimental/aitools/cmd/flags.go b/aitools/cmd/flags.go similarity index 100% rename from experimental/aitools/cmd/flags.go rename to aitools/cmd/flags.go diff --git a/experimental/aitools/cmd/flags_test.go b/aitools/cmd/flags_test.go similarity index 100% rename from experimental/aitools/cmd/flags_test.go rename to aitools/cmd/flags_test.go diff --git a/experimental/aitools/cmd/get_default_warehouse.go b/aitools/cmd/get_default_warehouse.go similarity index 93% rename from experimental/aitools/cmd/get_default_warehouse.go rename to aitools/cmd/get_default_warehouse.go index 446541e9d9..e2c0440b87 100644 --- a/experimental/aitools/cmd/get_default_warehouse.go +++ b/aitools/cmd/get_default_warehouse.go @@ -1,9 +1,9 @@ package aitools import ( + "github.com/databricks/cli/aitools/lib/middlewares" + "github.com/databricks/cli/aitools/lib/session" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/experimental/aitools/lib/middlewares" - "github.com/databricks/cli/experimental/aitools/lib/session" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/databricks-sdk-go/service/sql" diff --git a/experimental/aitools/cmd/install.go b/aitools/cmd/install.go similarity index 97% rename from experimental/aitools/cmd/install.go rename to aitools/cmd/install.go index b6e87d68b1..3012ef7c9e 100644 --- a/experimental/aitools/cmd/install.go +++ b/aitools/cmd/install.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "github.com/databricks/cli/experimental/aitools/lib/agents" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/fatih/color" "github.com/spf13/cobra" diff --git a/experimental/aitools/cmd/install_test.go b/aitools/cmd/install_test.go similarity index 99% rename from experimental/aitools/cmd/install_test.go rename to aitools/cmd/install_test.go index 38639705ea..37e469c816 100644 --- a/experimental/aitools/cmd/install_test.go +++ b/aitools/cmd/install_test.go @@ -7,8 +7,8 @@ import ( "path/filepath" "testing" - "github.com/databricks/cli/experimental/aitools/lib/agents" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/experimental/aitools/cmd/list.go b/aitools/cmd/list.go similarity index 98% rename from experimental/aitools/cmd/list.go rename to aitools/cmd/list.go index 1be1538c9a..f0fe48f07b 100644 --- a/experimental/aitools/cmd/list.go +++ b/aitools/cmd/list.go @@ -8,7 +8,7 @@ import ( "strings" "text/tabwriter" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" "github.com/spf13/cobra" diff --git a/experimental/aitools/cmd/list_test.go b/aitools/cmd/list_test.go similarity index 100% rename from experimental/aitools/cmd/list_test.go rename to aitools/cmd/list_test.go diff --git a/experimental/aitools/cmd/query.go b/aitools/cmd/query.go similarity index 99% rename from experimental/aitools/cmd/query.go rename to aitools/cmd/query.go index 7e9ae1d030..8b9504d4ab 100644 --- a/experimental/aitools/cmd/query.go +++ b/aitools/cmd/query.go @@ -11,9 +11,9 @@ import ( "syscall" "time" + "github.com/databricks/cli/aitools/lib/middlewares" + "github.com/databricks/cli/aitools/lib/session" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/experimental/aitools/lib/middlewares" - "github.com/databricks/cli/experimental/aitools/lib/session" "github.com/databricks/cli/libs/cmdctx" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/env" diff --git a/experimental/aitools/cmd/query_test.go b/aitools/cmd/query_test.go similarity index 100% rename from experimental/aitools/cmd/query_test.go rename to aitools/cmd/query_test.go diff --git a/experimental/aitools/cmd/render.go b/aitools/cmd/render.go similarity index 100% rename from experimental/aitools/cmd/render.go rename to aitools/cmd/render.go diff --git a/experimental/aitools/cmd/render_test.go b/aitools/cmd/render_test.go similarity index 100% rename from experimental/aitools/cmd/render_test.go rename to aitools/cmd/render_test.go diff --git a/experimental/aitools/cmd/scope.go b/aitools/cmd/scope.go similarity index 97% rename from experimental/aitools/cmd/scope.go rename to aitools/cmd/scope.go index 8c6ce0f013..acd012135a 100644 --- a/experimental/aitools/cmd/scope.go +++ b/aitools/cmd/scope.go @@ -8,7 +8,7 @@ import ( "path/filepath" "github.com/charmbracelet/huh" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/env" ) @@ -230,10 +230,10 @@ func scopeNotInstalledError(scope, verb, projectDir string, hasGlobal, hasProjec "no project-scoped skills found in the current directory.\n\n"+ "Project-scoped skills are detected based on your working directory.\n"+ "Make sure you are in the project root where you originally ran\n"+ - "'databricks experimental aitools install --project'.\n\n"+ + "'databricks aitools install --project'.\n\n"+ "Expected location: %s/", expectedPath) } else { - msg = "no globally-scoped skills installed. Run 'databricks experimental aitools install --global' to install" + msg = "no globally-scoped skills installed. Run 'databricks aitools install --global' to install" } hint := crossScopeHint(scope, verb, hasGlobal, hasProject) diff --git a/experimental/aitools/cmd/scope_test.go b/aitools/cmd/scope_test.go similarity index 99% rename from experimental/aitools/cmd/scope_test.go rename to aitools/cmd/scope_test.go index ecda25faad..80e5a976a9 100644 --- a/experimental/aitools/cmd/scope_test.go +++ b/aitools/cmd/scope_test.go @@ -7,7 +7,7 @@ import ( "path/filepath" "testing" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/experimental/aitools/cmd/skills.go b/aitools/cmd/skills.go similarity index 95% rename from experimental/aitools/cmd/skills.go rename to aitools/cmd/skills.go index 9995ff72a0..4f338adc15 100644 --- a/experimental/aitools/cmd/skills.go +++ b/aitools/cmd/skills.go @@ -5,8 +5,8 @@ import ( "errors" "github.com/charmbracelet/huh" - "github.com/databricks/cli/experimental/aitools/lib/agents" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/installer" "github.com/spf13/cobra" ) diff --git a/experimental/aitools/cmd/statement.go b/aitools/cmd/statement.go similarity index 100% rename from experimental/aitools/cmd/statement.go rename to aitools/cmd/statement.go diff --git a/experimental/aitools/cmd/statement_cancel.go b/aitools/cmd/statement_cancel.go similarity index 100% rename from experimental/aitools/cmd/statement_cancel.go rename to aitools/cmd/statement_cancel.go diff --git a/experimental/aitools/cmd/statement_get.go b/aitools/cmd/statement_get.go similarity index 100% rename from experimental/aitools/cmd/statement_get.go rename to aitools/cmd/statement_get.go diff --git a/experimental/aitools/cmd/statement_status.go b/aitools/cmd/statement_status.go similarity index 100% rename from experimental/aitools/cmd/statement_status.go rename to aitools/cmd/statement_status.go diff --git a/experimental/aitools/cmd/statement_submit.go b/aitools/cmd/statement_submit.go similarity index 100% rename from experimental/aitools/cmd/statement_submit.go rename to aitools/cmd/statement_submit.go diff --git a/experimental/aitools/cmd/statement_test.go b/aitools/cmd/statement_test.go similarity index 100% rename from experimental/aitools/cmd/statement_test.go rename to aitools/cmd/statement_test.go diff --git a/experimental/aitools/cmd/tools.go b/aitools/cmd/tools.go similarity index 100% rename from experimental/aitools/cmd/tools.go rename to aitools/cmd/tools.go diff --git a/experimental/aitools/cmd/uninstall.go b/aitools/cmd/uninstall.go similarity index 95% rename from experimental/aitools/cmd/uninstall.go rename to aitools/cmd/uninstall.go index 3eda84cfbc..8ebcecb462 100644 --- a/experimental/aitools/cmd/uninstall.go +++ b/aitools/cmd/uninstall.go @@ -1,7 +1,7 @@ package aitools import ( - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/spf13/cobra" ) diff --git a/experimental/aitools/cmd/update.go b/aitools/cmd/update.go similarity index 94% rename from experimental/aitools/cmd/update.go rename to aitools/cmd/update.go index c5072d1fb1..32e7ff7745 100644 --- a/experimental/aitools/cmd/update.go +++ b/aitools/cmd/update.go @@ -3,8 +3,8 @@ package aitools import ( "fmt" - "github.com/databricks/cli/experimental/aitools/lib/agents" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/spf13/cobra" ) diff --git a/experimental/aitools/cmd/version.go b/aitools/cmd/version.go similarity index 91% rename from experimental/aitools/cmd/version.go rename to aitools/cmd/version.go index 67c38fec42..7984682c73 100644 --- a/experimental/aitools/cmd/version.go +++ b/aitools/cmd/version.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/spf13/cobra" ) @@ -40,7 +40,7 @@ func newVersionCmd() *cobra.Command { if globalState == nil && projectState == nil { cmdio.LogString(ctx, "No Databricks AI Tools components installed.") cmdio.LogString(ctx, "") - cmdio.LogString(ctx, "Run 'databricks experimental aitools install' to get started.") + cmdio.LogString(ctx, "Run 'databricks aitools install' to get started.") return nil } @@ -89,6 +89,6 @@ func printVersionLine(ctx context.Context, label string, state *installer.Instal cmdio.LogString(ctx, " Update available: v"+latestVersion) cmdio.LogString(ctx, " Last updated: "+state.LastUpdated.Format("2006-01-02")) cmdio.LogString(ctx, "") - cmdio.LogString(ctx, "Run 'databricks experimental aitools update' to update.") + cmdio.LogString(ctx, "Run 'databricks aitools update' to update.") } } diff --git a/experimental/aitools/cmd/version_test.go b/aitools/cmd/version_test.go similarity index 97% rename from experimental/aitools/cmd/version_test.go rename to aitools/cmd/version_test.go index d24f7e99f8..e74c411b80 100644 --- a/experimental/aitools/cmd/version_test.go +++ b/aitools/cmd/version_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - "github.com/databricks/cli/experimental/aitools/lib/installer" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/libs/cmdio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/experimental/aitools/lib/agents/agents.go b/aitools/lib/agents/agents.go similarity index 100% rename from experimental/aitools/lib/agents/agents.go rename to aitools/lib/agents/agents.go diff --git a/experimental/aitools/lib/agents/recommend.go b/aitools/lib/agents/recommend.go similarity index 90% rename from experimental/aitools/lib/agents/recommend.go rename to aitools/lib/agents/recommend.go index bf10c67bfd..30ca09f02d 100644 --- a/experimental/aitools/lib/agents/recommend.go +++ b/aitools/lib/agents/recommend.go @@ -15,7 +15,7 @@ func RecommendSkillsInstall(ctx context.Context, installFn func(context.Context) } if !cmdio.IsPromptSupported(ctx) { - cmdio.LogString(ctx, "Tip: coding agents detected without Databricks skills. Run 'databricks experimental aitools skills install' to install them.") + cmdio.LogString(ctx, "Tip: coding agents detected without Databricks skills. Run 'databricks aitools skills install' to install them.") return nil } diff --git a/experimental/aitools/lib/agents/recommend_test.go b/aitools/lib/agents/recommend_test.go similarity index 96% rename from experimental/aitools/lib/agents/recommend_test.go rename to aitools/lib/agents/recommend_test.go index c2c2769921..faebba6e05 100644 --- a/experimental/aitools/lib/agents/recommend_test.go +++ b/aitools/lib/agents/recommend_test.go @@ -67,7 +67,7 @@ func TestRecommendSkillsInstallNonInteractive(t *testing.T) { ctx, stderr := cmdio.NewTestContextWithStderr(t.Context()) err := RecommendSkillsInstall(ctx, noopInstall) require.NoError(t, err) - assert.Contains(t, stderr.String(), "databricks experimental aitools skills install") + assert.Contains(t, stderr.String(), "databricks aitools skills install") } func TestRecommendSkillsInstallInteractiveDecline(t *testing.T) { diff --git a/experimental/aitools/lib/agents/skills.go b/aitools/lib/agents/skills.go similarity index 100% rename from experimental/aitools/lib/agents/skills.go rename to aitools/lib/agents/skills.go diff --git a/experimental/aitools/lib/agents/skills_test.go b/aitools/lib/agents/skills_test.go similarity index 100% rename from experimental/aitools/lib/agents/skills_test.go rename to aitools/lib/agents/skills_test.go diff --git a/experimental/aitools/lib/installer/SKILLS_VERSION b/aitools/lib/installer/SKILLS_VERSION similarity index 100% rename from experimental/aitools/lib/installer/SKILLS_VERSION rename to aitools/lib/installer/SKILLS_VERSION diff --git a/experimental/aitools/lib/installer/installer.go b/aitools/lib/installer/installer.go similarity index 98% rename from experimental/aitools/lib/installer/installer.go rename to aitools/lib/installer/installer.go index 982df0c163..5db3b151c7 100644 --- a/experimental/aitools/lib/installer/installer.go +++ b/aitools/lib/installer/installer.go @@ -14,7 +14,7 @@ import ( "strings" "time" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/internal/build" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/env" @@ -135,7 +135,7 @@ func InstallSkillsForAgents(ctx context.Context, src ManifestSource, targetAgent if state == nil && scope == ScopeGlobal { isLegacy := checkLegacyInstall(ctx, baseDir) if isLegacy && len(opts.SpecificSkills) > 0 { - return errors.New("legacy install detected without state tracking; run 'databricks experimental aitools install' (without a skill name) first to rebuild state") + return errors.New("legacy install detected without state tracking; run 'databricks aitools install' (without a skill name) first to rebuild state") } } @@ -314,7 +314,7 @@ func printNoAgentsDetected(ctx context.Context) { // Returns true if a legacy install was detected. func checkLegacyInstall(ctx context.Context, globalDir string) bool { if hasSkillsOnDisk(globalDir) { - cmdio.LogString(ctx, "Found skills installed before state tracking was added. Run 'databricks experimental aitools install' to refresh.") + cmdio.LogString(ctx, "Found skills installed before state tracking was added. Run 'databricks aitools install' to refresh.") return true } homeDir, err := env.UserHomeDir(ctx) @@ -323,7 +323,7 @@ func checkLegacyInstall(ctx context.Context, globalDir string) bool { } legacyDir := filepath.Join(homeDir, ".databricks", "agent-skills") if hasSkillsOnDisk(legacyDir) { - cmdio.LogString(ctx, "Found skills installed before state tracking was added. Run 'databricks experimental aitools install' to refresh.") + cmdio.LogString(ctx, "Found skills installed before state tracking was added. Run 'databricks aitools install' to refresh.") return true } return false diff --git a/experimental/aitools/lib/installer/installer_test.go b/aitools/lib/installer/installer_test.go similarity index 99% rename from experimental/aitools/lib/installer/installer_test.go rename to aitools/lib/installer/installer_test.go index b769143906..8bd926637e 100644 --- a/experimental/aitools/lib/installer/installer_test.go +++ b/aitools/lib/installer/installer_test.go @@ -11,7 +11,7 @@ import ( "strings" "testing" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/internal/build" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" diff --git a/experimental/aitools/lib/installer/source.go b/aitools/lib/installer/source.go similarity index 100% rename from experimental/aitools/lib/installer/source.go rename to aitools/lib/installer/source.go diff --git a/experimental/aitools/lib/installer/state.go b/aitools/lib/installer/state.go similarity index 100% rename from experimental/aitools/lib/installer/state.go rename to aitools/lib/installer/state.go diff --git a/experimental/aitools/lib/installer/state_test.go b/aitools/lib/installer/state_test.go similarity index 100% rename from experimental/aitools/lib/installer/state_test.go rename to aitools/lib/installer/state_test.go diff --git a/experimental/aitools/lib/installer/uninstall.go b/aitools/lib/installer/uninstall.go similarity index 97% rename from experimental/aitools/lib/installer/uninstall.go rename to aitools/lib/installer/uninstall.go index 1ad9f58511..92c69f7ec9 100644 --- a/experimental/aitools/lib/installer/uninstall.go +++ b/aitools/lib/installer/uninstall.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" ) @@ -54,7 +54,7 @@ func UninstallSkillsOpts(ctx context.Context, opts UninstallOptions) error { if state == nil { if scope == ScopeGlobal && hasLegacyInstall(ctx, baseDir) { - return errors.New("found skills from a previous install without state tracking; run 'databricks experimental aitools install' first, then uninstall") + return errors.New("found skills from a previous install without state tracking; run 'databricks aitools install' first, then uninstall") } return errors.New("no skills installed") } diff --git a/experimental/aitools/lib/installer/uninstall_test.go b/aitools/lib/installer/uninstall_test.go similarity index 99% rename from experimental/aitools/lib/installer/uninstall_test.go rename to aitools/lib/installer/uninstall_test.go index 6c7589f6f2..cf7ada3a9a 100644 --- a/experimental/aitools/lib/installer/uninstall_test.go +++ b/aitools/lib/installer/uninstall_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/libs/cmdio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/experimental/aitools/lib/installer/update.go b/aitools/lib/installer/update.go similarity index 96% rename from experimental/aitools/lib/installer/update.go rename to aitools/lib/installer/update.go index 663ad5e908..87b62e76ed 100644 --- a/experimental/aitools/lib/installer/update.go +++ b/aitools/lib/installer/update.go @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/internal/build" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/env" @@ -76,9 +76,9 @@ func UpdateSkills(ctx context.Context, src ManifestSource, targetAgents []*agent if state == nil { if scope == ScopeGlobal && hasLegacyInstall(ctx, baseDir) { - return nil, errors.New("found skills from a previous install without state tracking; run 'databricks experimental aitools install' to refresh before updating") + return nil, errors.New("found skills from a previous install without state tracking; run 'databricks aitools install' to refresh before updating") } - return nil, errors.New("no skills installed. Run 'databricks experimental aitools install' to install") + return nil, errors.New("no skills installed. Run 'databricks aitools install' to install") } latestTag := GetSkillsRef(ctx) diff --git a/experimental/aitools/lib/installer/update_test.go b/aitools/lib/installer/update_test.go similarity index 98% rename from experimental/aitools/lib/installer/update_test.go rename to aitools/lib/installer/update_test.go index 97e3014be6..4339ddead4 100644 --- a/experimental/aitools/lib/installer/update_test.go +++ b/aitools/lib/installer/update_test.go @@ -8,7 +8,7 @@ import ( "path/filepath" "testing" - "github.com/databricks/cli/experimental/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/agents" "github.com/databricks/cli/libs/cmdio" "github.com/databricks/cli/libs/log" "github.com/stretchr/testify/assert" @@ -24,7 +24,7 @@ func TestUpdateNoStateReturnsInstallHint(t *testing.T) { _, err := UpdateSkills(ctx, src, nil, UpdateOptions{}) require.Error(t, err) assert.Contains(t, err.Error(), "no skills installed") - assert.Contains(t, err.Error(), "databricks experimental aitools install") + assert.Contains(t, err.Error(), "databricks aitools install") } func TestUpdateLegacyInstallDetected(t *testing.T) { diff --git a/experimental/aitools/lib/installer/version.go b/aitools/lib/installer/version.go similarity index 100% rename from experimental/aitools/lib/installer/version.go rename to aitools/lib/installer/version.go diff --git a/experimental/aitools/lib/middlewares/databricks_client.go b/aitools/lib/middlewares/databricks_client.go similarity index 96% rename from experimental/aitools/lib/middlewares/databricks_client.go rename to aitools/lib/middlewares/databricks_client.go index 6abf360e03..738643f9d0 100644 --- a/experimental/aitools/lib/middlewares/databricks_client.go +++ b/aitools/lib/middlewares/databricks_client.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/databricks/cli/experimental/aitools/lib/session" + "github.com/databricks/cli/aitools/lib/session" "github.com/databricks/cli/libs/databrickscfg/profile" "github.com/databricks/databricks-sdk-go" ) diff --git a/experimental/aitools/lib/middlewares/databricks_client_test.go b/aitools/lib/middlewares/databricks_client_test.go similarity index 100% rename from experimental/aitools/lib/middlewares/databricks_client_test.go rename to aitools/lib/middlewares/databricks_client_test.go diff --git a/experimental/aitools/lib/middlewares/warehouse.go b/aitools/lib/middlewares/warehouse.go similarity index 98% rename from experimental/aitools/lib/middlewares/warehouse.go rename to aitools/lib/middlewares/warehouse.go index fc42c2d03c..a77b68e155 100644 --- a/experimental/aitools/lib/middlewares/warehouse.go +++ b/aitools/lib/middlewares/warehouse.go @@ -4,7 +4,7 @@ import ( "context" "fmt" - "github.com/databricks/cli/experimental/aitools/lib/session" + "github.com/databricks/cli/aitools/lib/session" "github.com/databricks/cli/libs/databrickscfg/cfgpickers" "github.com/databricks/cli/libs/env" "github.com/databricks/databricks-sdk-go" diff --git a/experimental/aitools/lib/session/session.go b/aitools/lib/session/session.go similarity index 100% rename from experimental/aitools/lib/session/session.go rename to aitools/lib/session/session.go diff --git a/experimental/aitools/lib/session/session_test.go b/aitools/lib/session/session_test.go similarity index 100% rename from experimental/aitools/lib/session/session_test.go rename to aitools/lib/session/session_test.go diff --git a/cmd/apps/init.go b/cmd/apps/init.go index 6e6ba2ccee..44d070360e 100644 --- a/cmd/apps/init.go +++ b/cmd/apps/init.go @@ -16,9 +16,9 @@ import ( "text/template" "github.com/charmbracelet/huh" + "github.com/databricks/cli/aitools/lib/agents" + "github.com/databricks/cli/aitools/lib/installer" "github.com/databricks/cli/cmd/root" - "github.com/databricks/cli/experimental/aitools/lib/agents" - "github.com/databricks/cli/experimental/aitools/lib/installer" "github.com/databricks/cli/libs/apps/generator" "github.com/databricks/cli/libs/apps/initializer" "github.com/databricks/cli/libs/apps/manifest" @@ -1135,7 +1135,7 @@ func runCreate(ctx context.Context, opts createOptions) error { // In flags mode, only print a hint — never prompt interactively. if flagsMode { if !agents.HasDatabricksSkillsInstalled(ctx) { - cmdio.LogString(ctx, "Tip: coding agents detected without Databricks skills. Run 'databricks experimental aitools skills install' to install them.") + cmdio.LogString(ctx, "Tip: coding agents detected without Databricks skills. Run 'databricks aitools skills install' to install them.") } } else if err := agents.RecommendSkillsInstall(ctx, installer.InstallAllSkills); err != nil { log.Warnf(ctx, "Skills recommendation failed: %v", err) diff --git a/cmd/cmd.go b/cmd/cmd.go index 014471f763..10209bd640 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -4,6 +4,7 @@ import ( "context" "strings" + aitoolscmd "github.com/databricks/cli/aitools/cmd" "github.com/databricks/cli/cmd/psql" ssh "github.com/databricks/cli/experimental/ssh/cmd" @@ -93,6 +94,7 @@ func New(ctx context.Context) *cobra.Command { } // Add other subcommands. + cli.AddCommand(aitoolscmd.NewAitoolsCmd()) cli.AddCommand(api.New()) cli.AddCommand(auth.New()) cli.AddCommand(completion.New()) diff --git a/cmd/experimental/experimental.go b/cmd/experimental/experimental.go index 36ad876589..c3b3b3e76e 100644 --- a/cmd/experimental/experimental.go +++ b/cmd/experimental/experimental.go @@ -1,7 +1,9 @@ package experimental import ( - aitoolscmd "github.com/databricks/cli/experimental/aitools/cmd" + "fmt" + + aitoolscmd "github.com/databricks/cli/aitools/cmd" "github.com/spf13/cobra" ) @@ -20,7 +22,46 @@ These commands provide early access to new features that are still under development. They may change or be removed in future versions without notice.`, } - cmd.AddCommand(aitoolscmd.NewAitoolsCmd()) + // Keep aitools under experimental as a hidden backward-compatibility alias. + // The skills-management surface (install/update/uninstall/list/version/skills) + // is now registered at the top level; the deprecation warning steers users + // to it. The `tools` subtree (query/discover-schema/get-default-warehouse/ + // statement) is NOT promoted yet and only lives here, so it must not warn. + // + // Cobra's `Deprecated` field only fires for the specific command it's set + // on, so running 'databricks experimental aitools install' would skip the + // warning — use a PersistentPreRunE that runs for every subcommand below + // the alias. + // + // Cobra walks up the parent chain and runs the FIRST PersistentPreRunE it + // finds, so defining one here would shadow the root's IO/log/user-agent + // initialization (cmd/root/root.go) and panic with "no cmdIO found in the + // context". Chain to the root hook explicitly to preserve setup. + aitoolsAlias := aitoolscmd.NewAitoolsCmd() + aitoolsAlias.Hidden = true + aitoolsAlias.Deprecated = "use 'databricks aitools' instead" + aitoolsAlias.PersistentPreRunE = func(c *cobra.Command, args []string) error { + fmt.Fprintln(c.ErrOrStderr(), "Warning: 'databricks experimental aitools' is deprecated; use 'databricks aitools' instead.") + if root := c.Root(); root.PersistentPreRunE != nil { + return root.PersistentPreRunE(c, args) + } + return nil + } + + // `tools` is the only home for the experimental tools subtree, so attach + // it here with its own PersistentPreRunE that chains to root WITHOUT + // printing the deprecation warning. Cobra picks the nearest ancestor's + // hook, so this overrides the alias's warning for tools subcommands. + toolsCmd := aitoolscmd.NewToolsCmd() + toolsCmd.PersistentPreRunE = func(c *cobra.Command, args []string) error { + if root := c.Root(); root.PersistentPreRunE != nil { + return root.PersistentPreRunE(c, args) + } + return nil + } + aitoolsAlias.AddCommand(toolsCmd) + + cmd.AddCommand(aitoolsAlias) cmd.AddCommand(newWorkspaceOpenCommand()) return cmd diff --git a/experimental/aitools/README.md b/experimental/aitools/README.md deleted file mode 100644 index ec12ed10f7..0000000000 --- a/experimental/aitools/README.md +++ /dev/null @@ -1,53 +0,0 @@ -# Experimental AI Tools - -`databricks experimental aitools` is the remaining experimental surface for coding-agent workflows. - -Current commands: - -- `databricks experimental aitools skills list` -- `databricks experimental aitools skills install [skill-name]` -- `databricks experimental aitools install [skill-name]` -- `databricks experimental aitools tools query` -- `databricks experimental aitools tools discover-schema` -- `databricks experimental aitools tools get-default-warehouse` -- `databricks experimental aitools tools statement submit` -- `databricks experimental aitools tools statement get` -- `databricks experimental aitools tools statement status` -- `databricks experimental aitools tools statement cancel` - -Current behavior: - -- `skills install` installs Databricks skills for detected coding agents. -- `install` is a compatibility alias for `skills install`. -- `tools` exposes a small set of AI-oriented workspace helpers. -- `tools query` accepts a single SQL or multiple SQLs in one invocation. Pass - several positional arguments and/or repeat `--file` to run them in parallel - against the warehouse. Multi-query output is always JSON; control parallelism - with `--concurrency` (default 8). - - ```bash - databricks experimental aitools tools query \ - --warehouse --output json \ - "SELECT count(*) FROM samples.nyctaxi.trips" \ - "SELECT min(tpep_pickup_datetime), max(tpep_pickup_datetime) FROM samples.nyctaxi.trips" \ - "SELECT vendor_id, count(*) FROM samples.nyctaxi.trips GROUP BY 1" - ``` - -- `tools statement` is a low-level lifecycle for asynchronous statements. - `submit` returns a `statement_id` immediately, `get` polls until terminal - and emits rows, `status` peeks without blocking, and `cancel` requests - termination. Ctrl+C on `get` stops polling but does NOT cancel the - server-side statement; use `cancel` for that. - - ```bash - SID=$(databricks experimental aitools tools statement submit \ - --warehouse "SELECT pg_sleep(5)" | jq -r '.statement_id') - databricks experimental aitools tools statement status "$SID" - databricks experimental aitools tools statement get "$SID" - ``` - -Removed behavior: - -- there is no MCP server under `experimental aitools` -- the old `deploy` and `validate` flows were removed -- command names and behavior in this area are still experimental and may change diff --git a/experimental/aitools/cmd/aitools.go b/experimental/aitools/cmd/aitools.go deleted file mode 100644 index f037ac1a22..0000000000 --- a/experimental/aitools/cmd/aitools.go +++ /dev/null @@ -1,29 +0,0 @@ -package aitools - -import ( - "github.com/spf13/cobra" -) - -func NewAitoolsCmd() *cobra.Command { - cmd := &cobra.Command{ - Use: "aitools", - Hidden: true, - Short: "Databricks AI Tools for coding agents", - Long: `Manage Databricks AI Tools. - -Provides commands to: -- Install the AI tools in coding agents (install) -- Manage skills (skills) -- Access tools directly (tools)`, - } - - cmd.AddCommand(newInstallCmd()) - cmd.AddCommand(newUpdateCmd()) - cmd.AddCommand(newUninstallCmd()) - cmd.AddCommand(newListCmd()) - cmd.AddCommand(newVersionCmd()) - cmd.AddCommand(newSkillsCmd()) - cmd.AddCommand(newToolsCmd()) - - return cmd -}