|
3 | 3 |
|
4 | 4 | from __future__ import annotations |
5 | 5 |
|
| 6 | +import shutil |
6 | 7 | from typing import Annotated |
7 | 8 |
|
8 | 9 | import typer |
@@ -1144,6 +1145,39 @@ def pi_cmd(ctx: typer.Context, skip_preflight: SkipPreflightOption = False) -> N |
1144 | 1145 | _launch_tool("pi", ctx, skip_preflight=skip_preflight) |
1145 | 1146 |
|
1146 | 1147 |
|
| 1148 | +@app.command("cursor", context_settings={"allow_extra_args": True, "ignore_unknown_options": True}) |
| 1149 | +def cursor_cmd(ctx: typer.Context) -> None: |
| 1150 | + """Launch Cursor Agent. |
| 1151 | +
|
| 1152 | + Cursor is MCP-only: `cursor-agent` runs models on your own Cursor account, so |
| 1153 | + ucode configures no models for it. Its Databricks MCP servers (added via |
| 1154 | + `ucode configure mcp`) run `ucode mcp-proxy`, which authenticates itself — so |
| 1155 | + this command is a thin convenience wrapper over `cursor-agent`, kept for |
| 1156 | + symmetry with the other `ucode <agent>` launchers. |
| 1157 | + """ |
| 1158 | + from ucode.agents import cursor |
| 1159 | + |
| 1160 | + try: |
| 1161 | + if not shutil.which(cursor.CURSOR_BINARY): |
| 1162 | + raise RuntimeError( |
| 1163 | + f"`{cursor.CURSOR_BINARY}` was not found on PATH. Install Cursor Agent " |
| 1164 | + "(https://cursor.com/cli), then re-run `ucode cursor`." |
| 1165 | + ) |
| 1166 | + print_section("ucode with Cursor") |
| 1167 | + print_note( |
| 1168 | + "Cursor runs models on your Cursor account; its Databricks MCP servers " |
| 1169 | + "authenticate through `ucode mcp-proxy`." |
| 1170 | + ) |
| 1171 | + print_success("Starting Cursor Agent") |
| 1172 | + cursor.launch(load_state(), ctx.args) |
| 1173 | + except RuntimeError as exc: |
| 1174 | + print_err(str(exc)) |
| 1175 | + raise typer.Exit(1) from None |
| 1176 | + except KeyboardInterrupt: |
| 1177 | + print_err("Interrupted.") |
| 1178 | + raise typer.Exit(130) from None |
| 1179 | + |
| 1180 | + |
1147 | 1181 | @configure_app.callback(invoke_without_command=True) |
1148 | 1182 | def configure( |
1149 | 1183 | ctx: typer.Context, |
@@ -1316,20 +1350,42 @@ def configure( |
1316 | 1350 | **skip_kwargs, |
1317 | 1351 | ) |
1318 | 1352 | elif agents is not None: |
1319 | | - selected_tools = _parse_agents_option(agents) |
1320 | | - if workspace_entries is None: |
1321 | | - configure_workspace_command( |
1322 | | - selected_tools=selected_tools, |
1323 | | - prompt_optional_updates=prompt_optional_updates, |
1324 | | - **skip_kwargs, |
| 1353 | + # Cursor is MCP-only (no model routing), so it can't go through the |
| 1354 | + # model-agent configure path. Split it out: model agents configure |
| 1355 | + # normally; cursor only needs workspace state established here, and |
| 1356 | + # its MCP servers are added separately via `ucode configure mcp` |
| 1357 | + # (which picks cursor up through MCP_ONLY_CLIENTS). If cursor is the |
| 1358 | + # only agent, do a workspace-only configure so that later `configure |
| 1359 | + # mcp` run has a current workspace to target. |
| 1360 | + requested = [a.strip().lower() for a in agents.split(",") if a.strip()] |
| 1361 | + wants_cursor = "cursor" in requested |
| 1362 | + model_agent_names = ",".join(a for a in requested if a != "cursor") |
| 1363 | + if model_agent_names: |
| 1364 | + selected_tools = _parse_agents_option(model_agent_names) |
| 1365 | + if workspace_entries is None: |
| 1366 | + configure_workspace_command( |
| 1367 | + selected_tools=selected_tools, |
| 1368 | + prompt_optional_updates=prompt_optional_updates, |
| 1369 | + **skip_kwargs, |
| 1370 | + ) |
| 1371 | + else: |
| 1372 | + configure_workspace_command( |
| 1373 | + selected_tools=selected_tools, |
| 1374 | + workspaces=workspace_entries, |
| 1375 | + prompt_optional_updates=prompt_optional_updates, |
| 1376 | + **skip_kwargs, |
| 1377 | + ) |
| 1378 | + elif wants_cursor: |
| 1379 | + # Cursor-only: establish workspace state without the model picker. |
| 1380 | + _configure_shared_workspace_states( |
| 1381 | + workspace_entries or [_prompt_for_configuration(None)], |
| 1382 | + tools=[], |
| 1383 | + force_login=not use_pat, |
| 1384 | + use_pat=use_pat, |
1325 | 1385 | ) |
1326 | 1386 | else: |
1327 | | - configure_workspace_command( |
1328 | | - selected_tools=selected_tools, |
1329 | | - workspaces=workspace_entries, |
1330 | | - prompt_optional_updates=prompt_optional_updates, |
1331 | | - **skip_kwargs, |
1332 | | - ) |
| 1387 | + # Neither model agents nor cursor -> empty/invalid --agents list. |
| 1388 | + _parse_agents_option(agents) |
1333 | 1389 | elif mcp is not None: |
1334 | 1390 | # MCP-only: `--mcp` without --agent(s) (e.g. Cursor, which isn't a |
1335 | 1391 | # model agent, or adding MCP servers to an already-configured setup). |
|
0 commit comments