From 25a496a14e52aca79b9a2a6a554012beab217afd Mon Sep 17 00:00:00 2001 From: Lilly Luo Date: Fri, 11 Sep 2026 14:26:13 +0000 Subject: [PATCH] Skip validation prompts during harness autoconfiguration --- src/ucode/cli.py | 19 +++++-------------- tests/test_cli.py | 44 +++++++++++++++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 19 deletions(-) diff --git a/src/ucode/cli.py b/src/ucode/cli.py index 61d60592..fdf4e3e1 100644 --- a/src/ucode/cli.py +++ b/src/ucode/cli.py @@ -1690,7 +1690,11 @@ def claude_router_hook_cmd( def _auto_configure_tool(tool: str, custom_oauth: CustomOAuthConfig | None = None) -> None: - """First-time setup for a single tool — mirrors configure_workspace_command.""" + """Configure a tool for launch without sending a separate validation prompt. + + The real agent session follows immediately; explicit configure retains the + test-prompt validation. + """ existing = load_state() workspace = existing.get("workspace") profile = existing.get("profile") @@ -1713,19 +1717,6 @@ def _auto_configure_tool(tool: str, custom_oauth: CustomOAuthConfig | None = Non ) ) - with spinner(f"Validating {spec['display']}..."): - ok, err = validate_tool(tool) - if ok: - print_success(f"{spec['display']} is working") - else: - print_err(f"{spec['display']}: {provider_permission_error(tool, state, err)}") - managed = bool(state.get("managed_configs", {}).get(tool)) - restore_file(spec["config_path"], spec["backup_path"], managed) - available_tools = [t for t in (state.get("available_tools") or []) if t != tool] - state["available_tools"] = available_tools - save_state(state) - raise RuntimeError(f"{spec['display']} validation failed — config reverted.") - CAN_USE_CACHED_CONFIG_AGENTS = frozenset({"claude", "codex"}) diff --git a/tests/test_cli.py b/tests/test_cli.py index d026961d..f2bc970c 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1731,6 +1731,38 @@ def test_reports_runtime_error(self): class TestAutoConfigureOnFirstRun: + @pytest.mark.parametrize("tool", list(cli_mod.TOOL_SPECS)) + @pytest.mark.parametrize("has_workspace", [False, True]) + def test_launch_autoconfigures_without_test_prompt(self, tool, has_workspace): + initial_state = {**MINIMAL_STATE, "available_tools": []} if has_workspace else {} + configured_state = {**MINIMAL_STATE, "available_tools": [tool]} + with ( + patch("ucode.cli.ensure_bootstrap_dependencies"), + patch("ucode.cli.load_state", return_value=initial_state), + patch( + "ucode.cli._prompt_for_configuration", + return_value=(MINIMAL_STATE["workspace"], None), + ), + patch("ucode.cli.configure_shared_state", return_value=configured_state), + patch( + "ucode.cli.configure_single_tool", return_value=configured_state + ) as mock_configure, + patch("ucode.cli.ensure_provider_state", return_value=configured_state), + patch("ucode.cli._fetch_managed_config", return_value=(None, False)), + patch("ucode.cli.configure_tool", return_value=configured_state), + patch("ucode.cli.validate_tool", return_value=(False, "timed out")) as mock_validate, + patch("ucode.cli.restore_file") as mock_restore, + patch("ucode.cli.launch_agent") as mock_launch, + ): + result = runner.invoke(app, [tool]) + + assert result.exit_code == 0, result.output + mock_configure.assert_called_once_with(tool, configured_state) + mock_validate.assert_not_called() + mock_restore.assert_not_called() + mock_launch.assert_called_once() + assert mock_launch.call_args.args[:2] == (tool, configured_state) + def test_triggers_when_no_workspace(self): """Auto-configure runs when state has no workspace.""" empty_state = {} @@ -3091,7 +3123,9 @@ def test_skip_validate_skips_agent_validation(self, monkeypatch): assert result == 0 assert validated == [] - def test_skip_validate_skips_single_tool_validation(self, monkeypatch): + @pytest.mark.parametrize("skip_validate", [False, True]) + @pytest.mark.parametrize("tool", list(cli_mod.TOOL_SPECS)) + def test_single_tool_validation_is_optional(self, monkeypatch, skip_validate, tool): import ucode.cli as cli_mod state = {**MINIMAL_STATE, "workspace": "https://first.com"} @@ -3107,16 +3141,16 @@ def test_skip_validate_skips_single_tool_validation(self, monkeypatch): monkeypatch.setattr(cli_mod, "validate_tool", lambda t: validated.append(t) or (True, "")) result = cli_mod.configure_workspace_command( - "claude", + tool, workspaces=[("https://first.com", None)], - skip_validate=True, + skip_validate=skip_validate, ) assert result == 0 - assert validated == [] + assert validated == ([] if skip_validate else [tool]) # `ucode configure` (single-agent) still installs AI Tools — it's the # configure path, unlike launch which auto-configures without installing. - assert installed == [["claude"]] + assert installed == [[tool]] class TestConfigureSharedStateMcpCleanup: