diff --git a/src/ucode/agents/__init__.py b/src/ucode/agents/__init__.py index b54e136..0785342 100644 --- a/src/ucode/agents/__init__.py +++ b/src/ucode/agents/__init__.py @@ -89,7 +89,7 @@ } -def install_ai_tools_for_agents(tools: list[str], state: dict) -> None: +def install_databricks_ai_tools_for_agents(tools: list[str], state: dict) -> None: """Install Databricks AI Tools for the coding agents that support them (gemini/pi have no ``aitools`` support and are dropped).""" if state.get("databricks_ai_tools_enabled", True) is False: @@ -429,7 +429,6 @@ def configure_single_tool(tool: str, state: dict) -> dict: available_tools = list(set((state.get("available_tools") or []) + [tool])) state["available_tools"] = available_tools save_state(state) - install_ai_tools_for_agents([tool], state) return state @@ -462,7 +461,7 @@ def configure_selected_tools(state: dict, tools: list[str]) -> dict: existing = state.get("available_tools") or [] state["available_tools"] = sorted(set(existing) | set(tools)) save_state(state) - install_ai_tools_for_agents(tools, state) + install_databricks_ai_tools_for_agents(tools, state) return state diff --git a/src/ucode/cli.py b/src/ucode/cli.py index c2e38f4..35b6a24 100644 --- a/src/ucode/cli.py +++ b/src/ucode/cli.py @@ -18,6 +18,7 @@ configure_tool, ensure_bootstrap_dependencies, ensure_provider_state, + install_databricks_ai_tools_for_agents, install_tool_binary, normalize_tool, provider_permission_error, @@ -769,6 +770,7 @@ def configure_workspace_command( ) state = states[0] state = configure_single_tool(tool, state) + install_databricks_ai_tools_for_agents([tool], state) spec = TOOL_SPECS[tool] console.print( Panel( diff --git a/tests/test_agents_init.py b/tests/test_agents_init.py index 993c967..f346af9 100644 --- a/tests/test_agents_init.py +++ b/tests/test_agents_init.py @@ -14,7 +14,7 @@ configure_selected_tools, default_model_for_tool, ensure_tool_binary_available, - install_ai_tools_for_agents, + install_databricks_ai_tools_for_agents, install_tool_binary, normalize_tool, provider_permission_error, @@ -74,26 +74,30 @@ def _capture(self, monkeypatch): def test_maps_supported_tools_and_drops_others(self, monkeypatch): captured = self._capture(monkeypatch) # gemini and pi aren't supported by `databricks aitools`, so they drop. - install_ai_tools_for_agents(["claude", "codex", "gemini", "pi"], {"profile": "prof"}) + install_databricks_ai_tools_for_agents( + ["claude", "codex", "gemini", "pi"], {"profile": "prof"} + ) assert captured == {"agents": ["claude-code", "codex"], "profile": "prof"} def test_installed_by_default(self, monkeypatch): # Opt-out: absent flag means install. captured = self._capture(monkeypatch) - install_ai_tools_for_agents(["claude"], {"profile": "p"}) + install_databricks_ai_tools_for_agents(["claude"], {"profile": "p"}) assert captured == {"agents": ["claude-code"], "profile": "p"} def test_skipped_when_disabled(self, monkeypatch): # `configure --disable-databricks-ai-tools` persists this False. captured = self._capture(monkeypatch) - install_ai_tools_for_agents( + install_databricks_ai_tools_for_agents( ["claude"], {"profile": "p", "databricks_ai_tools_enabled": False} ) assert captured == {} # install_ai_tools never called class TestConfigureWiresAiToolsInstall: - """Both configure chokepoints must trigger AI Tools install.""" + """AI Tools install is a `ucode configure`-only step. `configure_selected_tools` + (a configure-only chokepoint) triggers it; `configure_single_tool` does NOT, + because the launch path auto-configures through it and must never install.""" def _stub_configure(self, monkeypatch): captured = {} @@ -106,24 +110,18 @@ def _stub_configure(self, monkeypatch): ) return captured - def test_configure_single_tool_triggers_install(self, monkeypatch): + def test_configure_single_tool_does_not_install(self, monkeypatch): + # Launch auto-configures through configure_single_tool, so it must not + # install skills — that would put skill installation on the launch path. captured = self._stub_configure(monkeypatch) agents_mod.configure_single_tool("codex", {"codex_models": ["m"], "profile": "myprof"}) - assert captured == {"agents": ["codex"], "profile": "myprof"} + assert captured == {} def test_configure_selected_tools_triggers_install(self, monkeypatch): captured = self._stub_configure(monkeypatch) agents_mod.configure_selected_tools({"profile": "myprof"}, ["codex"]) assert captured == {"agents": ["codex"], "profile": "myprof"} - def test_configure_single_tool_respects_disable(self, monkeypatch): - captured = self._stub_configure(monkeypatch) - agents_mod.configure_single_tool( - "codex", - {"codex_models": ["m"], "profile": "myprof", "databricks_ai_tools_enabled": False}, - ) - assert captured == {} - class TestNormalizeTool: @pytest.mark.parametrize( diff --git a/tests/test_cli.py b/tests/test_cli.py index 55ab879..cc75427 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -2181,6 +2181,12 @@ def test_skip_validate_skips_single_tool_validation(self, monkeypatch): state = {**MINIMAL_STATE, "workspace": "https://first.com"} monkeypatch.setattr(cli_mod, "configure_shared_state", lambda *a, **k: state) monkeypatch.setattr(cli_mod, "configure_single_tool", lambda t, s: s) + installed: list = [] + monkeypatch.setattr( + cli_mod, + "install_databricks_ai_tools_for_agents", + lambda tools, s: installed.append(tools), + ) validated: list = [] monkeypatch.setattr(cli_mod, "validate_tool", lambda t: validated.append(t) or (True, "")) @@ -2192,6 +2198,9 @@ def test_skip_validate_skips_single_tool_validation(self, monkeypatch): assert result == 0 assert validated == [] + # `ucode configure` (single-agent) still installs AI Tools — it's the + # configure path, unlike launch which auto-configures without installing. + assert installed == [["claude"]] class TestConfigureSharedStateMcpCleanup: