Skip to content

Commit 25aef26

Browse files
xsh310Isaac
andcommitted
[skills] Inline the by-name download guard; rename schema download command
Move the `--location` rejection into each command's guard block, next to the other mutual-exclusion checks, and inline the FQN validation and dispatch, dropping _download_selected_skills_route. _is_qualified_skill_name stays shared. Rename configure_skills_download_command to configure_location_skills_download_command so its name makes clear it downloads every skill in the selected `<catalog>.<schema>` locations, distinct from configure_selected_skills_download_command. Co-authored-by: Isaac <no-reply@databricks.com>
1 parent 56de037 commit 25aef26

4 files changed

Lines changed: 32 additions & 36 deletions

File tree

src/ucode/cli.py

Lines changed: 21 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@
109109
skill_locations_for_client,
110110
)
111111
from ucode.skills_download import (
112+
configure_location_skills_download_command,
112113
configure_selected_skills_download_command,
113-
configure_skills_download_command,
114114
configure_skills_download_picker_command,
115115
download_managed_skills_on_launch,
116116
)
@@ -359,26 +359,6 @@ def _is_qualified_skill_name(name: str) -> bool:
359359
return len(parts) == 3 and all(part and part == part.strip() for part in parts)
360360

361361

362-
def _download_selected_skills_route(
363-
requested_skills: set[str], location: str | None, path: str | None, *, flag: str
364-
) -> None:
365-
"""Download the fully-qualified `requested_skills` (cross-schema) and register.
366-
367-
The `--skills`/`--skill` download path: names must be fully qualified so a
368-
selection can span schemas without a `--location`, so passing one is an error
369-
and any bare/malformed name is rejected up front. `flag` names the option in
370-
those errors. Empty selections download nothing."""
371-
if location is not None:
372-
raise RuntimeError(f"{flag} takes fully-qualified names; drop --location.")
373-
invalid = sorted(name for name in requested_skills if not _is_qualified_skill_name(name))
374-
if invalid:
375-
raise RuntimeError(
376-
f"{flag} entries must be fully-qualified `<catalog>.<schema>.<name>` names "
377-
f"(invalid: {', '.join(invalid)})."
378-
)
379-
configure_selected_skills_download_command(sorted(requested_skills), path)
380-
381-
382362
def _parse_workspaces_option(workspaces: str) -> list[tuple[str, str | None]]:
383363
"""Parse `--workspaces` into [(url, profile_name | None), ...].
384364
@@ -1343,11 +1323,19 @@ def skills_add(
13431323
raise RuntimeError("--path is not supported when using --mcp")
13441324
if mcp and requested_skills is not None:
13451325
raise RuntimeError("--skills is not supported when using --mcp")
1326+
if requested_skills is not None and location is not None:
1327+
raise RuntimeError("--skills takes fully-qualified names; drop --location.")
13461328
# Downloaded skills use shared directory families, so only MCP scopes can be agent-scoped.
13471329
if not mcp and agents is not None:
13481330
raise RuntimeError("--agents is only supported when using --mcp")
13491331
if requested_skills is not None:
1350-
_download_selected_skills_route(requested_skills, location, path, flag="--skills")
1332+
invalid = sorted(s for s in requested_skills if not _is_qualified_skill_name(s))
1333+
if invalid:
1334+
raise RuntimeError(
1335+
"--skills entries must be fully-qualified `<catalog>.<schema>.<name>` names "
1336+
f"(invalid: {', '.join(invalid)})."
1337+
)
1338+
configure_selected_skills_download_command(sorted(requested_skills), path)
13511339
return
13521340
locations = _parse_skill_locations(location)
13531341
if not locations:
@@ -1361,7 +1349,7 @@ def skills_add(
13611349
)
13621350
add_skills_command(locations, agents=scope)
13631351
else:
1364-
configure_skills_download_command(locations, path=path)
1352+
configure_location_skills_download_command(locations, path=path)
13651353
except (RuntimeError, ValueError) as exc:
13661354
print_err(str(exc))
13671355
raise typer.Exit(1) from None
@@ -3237,16 +3225,24 @@ def configure_skills(
32373225
raise RuntimeError("--path is not valid with --mcp.")
32383226
if mcp and selected_skills is not None:
32393227
raise RuntimeError("--skill is not valid with --mcp; it only applies when downloading.")
3228+
if selected_skills is not None and location is not None:
3229+
raise RuntimeError("--skill takes fully-qualified names; drop --location.")
32403230
if selected_skills is not None:
3241-
_download_selected_skills_route(selected_skills, location, path, flag="--skill")
3231+
invalid = sorted(s for s in selected_skills if not _is_qualified_skill_name(s))
3232+
if invalid:
3233+
raise RuntimeError(
3234+
"--skill entries must be fully-qualified `<catalog>.<schema>.<name>` names "
3235+
f"(invalid: {', '.join(invalid)})."
3236+
)
3237+
configure_selected_skills_download_command(sorted(selected_skills), path)
32423238
return
32433239
locations = _parse_skill_locations(location)
32443240
if path is not None and not locations:
32453241
raise RuntimeError("--path only applies when downloading with --location.")
32463242
if mcp or not locations:
32473243
configure_skills_mcp_command(locations)
32483244
else:
3249-
configure_skills_download_command(locations, path=path)
3245+
configure_location_skills_download_command(locations, path=path)
32503246
except (RuntimeError, ValueError) as exc:
32513247
print_err(str(exc))
32523248
raise typer.Exit(1) from None

src/ucode/skills_download.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ def download_managed_skills_on_launch(
503503
return written
504504

505505

506-
def configure_skills_download_command(locations: list[str], *, path: str | None) -> int:
506+
def configure_location_skills_download_command(locations: list[str], *, path: str | None) -> int:
507507
"""Download every skill in each schema to disk and register the skills connection.
508508
509509
Downloads to ``path`` (or the home dir when None), then registers/keeps the

tests/test_cli.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1377,15 +1377,15 @@ def test_comma_location_yields_multiple_schemas(self):
13771377
mock_mcp.assert_called_once_with(["a.b", "c.d"])
13781378

13791379
def test_default_mode_dispatches_download_with_path(self):
1380-
with patch("ucode.cli.configure_skills_download_command") as mock_download:
1380+
with patch("ucode.cli.configure_location_skills_download_command") as mock_download:
13811381
result = runner.invoke(
13821382
app, ["configure", "skills", "--location", "a.b", "--path", "/tmp/skills"]
13831383
)
13841384
assert result.exit_code == 0, result.output
13851385
mock_download.assert_called_once_with(["a.b"], path="/tmp/skills")
13861386

13871387
def test_default_mode_without_path_dispatches_download(self):
1388-
with patch("ucode.cli.configure_skills_download_command") as mock_download:
1388+
with patch("ucode.cli.configure_location_skills_download_command") as mock_download:
13891389
result = runner.invoke(app, ["configure", "skills", "--location", "a.b"])
13901390
assert result.exit_code == 0, result.output
13911391
mock_download.assert_called_once_with(["a.b"], path=None)
@@ -1434,7 +1434,7 @@ def test_skill_with_mcp_exit_1(self):
14341434
def test_path_with_mcp_exit_1(self):
14351435
with (
14361436
patch("ucode.cli.configure_skills_mcp_command") as mock_mcp,
1437-
patch("ucode.cli.configure_skills_download_command") as mock_download,
1437+
patch("ucode.cli.configure_location_skills_download_command") as mock_download,
14381438
):
14391439
result = runner.invoke(
14401440
app, ["configure", "skills", "--location", "a.b", "--mcp", "--path", "/tmp/skills"]
@@ -1472,7 +1472,7 @@ def test_mcp_without_location_registers_schemaless_connection(self):
14721472
def test_path_without_location_exit_1(self):
14731473
with (
14741474
patch("ucode.cli.configure_skills_mcp_command") as mock_mcp,
1475-
patch("ucode.cli.configure_skills_download_command") as mock_download,
1475+
patch("ucode.cli.configure_location_skills_download_command") as mock_download,
14761476
):
14771477
result = runner.invoke(app, ["configure", "skills", "--path", "/tmp/skills"])
14781478
assert result.exit_code == 1
@@ -1498,7 +1498,7 @@ def test_comma_location_yields_multiple_schemas(self):
14981498
mock_add.assert_called_once_with(["a.b", "c.d"], agents=None)
14991499

15001500
def test_default_mode_dispatches_download(self):
1501-
with patch("ucode.cli.configure_skills_download_command") as mock_download:
1501+
with patch("ucode.cli.configure_location_skills_download_command") as mock_download:
15021502
result = runner.invoke(app, ["skill", "add", "--location", "a.b", "--path", "/tmp/s"])
15031503
assert result.exit_code == 0, result.output
15041504
mock_download.assert_called_once_with(["a.b"], path="/tmp/s")
@@ -1534,7 +1534,7 @@ def test_without_location_non_interactive_exit_1(self):
15341534
with (
15351535
patch("ucode.cli._stdin_is_interactive", return_value=False),
15361536
patch("ucode.cli.add_skills_command") as mock_add,
1537-
patch("ucode.cli.configure_skills_download_command") as mock_download,
1537+
patch("ucode.cli.configure_location_skills_download_command") as mock_download,
15381538
patch("ucode.cli.configure_skills_download_picker_command") as mock_picker,
15391539
):
15401540
result = runner.invoke(app, ["skill", "add"])
@@ -1631,7 +1631,7 @@ def test_empty_agents_folds_to_global_scope(self):
16311631
mock_add.assert_called_once_with(["a.b"], agents=None)
16321632

16331633
def test_agents_is_rejected_for_download_mode(self):
1634-
with patch("ucode.cli.configure_skills_download_command") as mock_download:
1634+
with patch("ucode.cli.configure_location_skills_download_command") as mock_download:
16351635
result = runner.invoke(app, ["skill", "add", "--location", "a.b", "--agents", "claude"])
16361636

16371637
assert result.exit_code == 1

tests/test_skills_download.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -842,7 +842,7 @@ def test_malformed_location_is_skipped(self, tmp_path, monkeypatch):
842842
)
843843

844844

845-
class TestConfigureSkillsDownloadCommand:
845+
class TestConfigureLocationSkillsDownloadCommand:
846846
def _stub(self, monkeypatch):
847847
calls: dict[str, object] = {}
848848
monkeypatch.setattr(sd, "load_state", lambda: {"state": True})
@@ -865,15 +865,15 @@ def _stub(self, monkeypatch):
865865
def test_downloads_then_registers_connection(self, monkeypatch):
866866
calls = self._stub(monkeypatch)
867867

868-
assert sd.configure_skills_download_command(["a.b"], path="/tmp/skills") == 0
868+
assert sd.configure_location_skills_download_command(["a.b"], path="/tmp/skills") == 0
869869

870870
assert calls["download"] == (WS, "token", ["a.b"], "/tmp/skills")
871871
assert calls["register"] == (WS, "profile", ["claude"])
872872

873873
def test_none_path_threads_through(self, monkeypatch):
874874
calls = self._stub(monkeypatch)
875875

876-
assert sd.configure_skills_download_command(["a.b"], path=None) == 0
876+
assert sd.configure_location_skills_download_command(["a.b"], path=None) == 0
877877

878878
assert calls["download"] == (WS, "token", ["a.b"], None)
879879
assert calls["register"] == (WS, "profile", ["claude"])

0 commit comments

Comments
 (0)