Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 30 additions & 27 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,67 +292,70 @@ you to run `ug <agent>` (existing agent sessions need a restart before the MCP t

#### Add skill scopes without replacing existing ones

`ug skill add` registers skills additively, keeping anything already configured. With `--mcp` it
`ug skills add` registers skills additively, keeping anything already configured. With `--mcp` it
adds the schemas to the connection's scope, otherwise it downloads their skills to disk. `--location`
downloads whole schemas; `--skills` downloads a named set of fully-qualified skills that may span
downloads whole schemas; `--skill` downloads a named set of fully-qualified skills that may span
schemas.

```bash
# Add schemas to the skills MCP scope, keeping any already configured.
ug skill add --location main.default,ml.prod --mcp
ug skills add --location main.default,ml.prod --mcp

# Scope the schemas to specific agents. Any not set up yet are configured first.
ug skill add --location main.default --mcp --agents claude,codex
ug skills add --location main.default --mcp --agents claude,codex

# Download a schema's skills to disk, keeping existing downloads.
ug skill add --location main.default
ug skills add --location main.default

# Download into a specific project directory instead of your home dir.
ug skills add --location main.default --path /abs/project/dir

# Download a named set of skills by fully-qualified name (may span schemas).
ug skill add --skills main.default.my-skill,ml.prod.other-skill
ug skills add --skill main.default.my-skill,ml.prod.other-skill

# No --location (or --skills) launches an interactive picker of the workspace's
# No --location (or --skill) launches an interactive picker of the workspace's
# skills to download; it opens immediately and streams skills in as they're found.
ug skill add
ug skills add
```

With `--mcp`, `--agents` limits the change to the named agents; without it the schemas go to every
configured agent. It applies only to `--mcp`, since downloaded skills are shared across agents.

#### Remove skill scopes

Remove schemas from the skills MCP connection with `ug skill remove --mcp`. `--location` drops the
Remove schemas from the skills MCP connection with `ug skills remove --mcp`. `--location` drops the
named schemas; with no `--location` on an interactive terminal a picker lists the scoped schemas.

```bash
# Remove specific schemas from the MCP scope; each is removed from every agent it's on.
ug skill remove --location main.default,ml.prod --mcp
ug skills remove --location main.default,ml.prod --mcp

# Remove from specific agents only. A schema scoped to several agents is
# removed from the named ones and kept on the rest.
ug skill remove --location main.default --mcp --agents claude
ug skills remove --location main.default --mcp --agents claude

# No --location launches a picker of the scoped schemas to remove.
ug skill remove --mcp
ug skills remove --mcp
```

#### Remove downloaded skills

Without `--mcp`, `ug skill remove` deletes downloaded skill directories. Only skills
Without `--mcp`, `ug skills remove` deletes downloaded skill directories. Only skills
`ug` downloaded are removed, so a same-named skill you authored is left alone.

```bash
# Pick from every skill downloaded to disk, across all download bases.
ug skill remove
ug skills remove

# Remove every skill downloaded from a schema (all bases, or one with --path).
ug skill remove --location main.default
ug skill remove --location main.default --path /abs/project/dir
ug skills remove --location main.default
ug skills remove --location main.default --path /abs/project/dir

# Remove named skills by fully-qualified name (may span schemas).
ug skill remove --skills main.default.my-skill,ml.prod.other-skill
ug skills remove --skill main.default.my-skill,ml.prod.other-skill
```

`--location` and `--skills` each accept `--path` to limit removal to one download base, and are
`--location` and `--skill` each accept `--path` to limit removal to one download base, and are
mutually exclusive with each other.

### Exporting the config
Expand Down Expand Up @@ -417,15 +420,15 @@ The output looks like:
| `ug configure skills --location main.default [--path <dir>]` | Download a schema's skills to disk (under `<dir>`, or your home dir) and register a schema-less skills MCP connection |
| `ug configure skills --skill main.default.my-skill` | Download named skills by fully-qualified name (comma-separated; may span schemas) |
| `ug configure skills --location main.default --mcp` | Expose a schema's skills as MCP tools (override-only) instead of downloading |
| `ug skill add --location main.default --mcp` | Add schemas to the skills MCP scope, keeping any already configured (additive; never replaces) |
| `ug skill add --location main.default --mcp --agents claude,codex` | Add schemas to specific agents' skills MCP scope (sets up any not yet configured) |
| `ug skill add --location main.default` | Download a schema's skills to disk without removing existing downloads |
| `ug skill add --skills main.default.my-skill` | Download named skills by fully-qualified name (comma-separated; may span schemas) |
| `ug skill remove --location main.default --mcp` | Remove specific schemas from the skills MCP scope, or omit `--location` on a TTY for a picker (every agent) |
| `ug skill remove --location main.default --mcp --agents claude` | Remove schemas from specific agents' skills MCP scope, keeping them on the rest |
| `ug skill remove` | Pick from every downloaded skill (across all bases) and delete it from disk |
| `ug skill remove --location main.default [--path <dir>]` | Delete every skill downloaded from a schema (all bases, or one under `<dir>`) |
| `ug skill remove --skills main.default.my-skill [--path <dir>]` | Delete named downloaded skills by fully-qualified name (comma-separated; may span schemas; `--path` limits to one base) |
| `ug skills add --location main.default --mcp` | Add schemas to the skills MCP scope, keeping any already configured (additive; never replaces) |
| `ug skills add --location main.default --mcp --agents claude,codex` | Add schemas to specific agents' skills MCP scope (sets up any not yet configured) |
| `ug skills add --location main.default [--path <dir>]` | Download a schema's skills to disk (under `<dir>`, or your home dir) without removing existing downloads |
| `ug skills add --skill main.default.my-skill` | Download named skills by fully-qualified name (comma-separated; may span schemas) |
| `ug skills remove --location main.default --mcp` | Remove specific schemas from the skills MCP scope, or omit `--location` on a TTY for a picker (every agent) |
| `ug skills remove --location main.default --mcp --agents claude` | Remove schemas from specific agents' skills MCP scope, keeping them on the rest |
| `ug skills remove` | Pick from every downloaded skill (across all bases) and delete it from disk |
| `ug skills remove --location main.default [--path <dir>]` | Delete every skill downloaded from a schema (all bases, or one under `<dir>`) |
| `ug skills remove --skill main.default.my-skill [--path <dir>]` | Delete named downloaded skills by fully-qualified name (comma-separated; may span schemas; `--path` limits to one base) |

Databricks AI Tools are installed only by `ug configure`, never by `ug <agent>` launches.
Use `--enable-databricks-ai-tools` or `--disable-databricks-ai-tools` with `ug configure` to
Expand Down
36 changes: 18 additions & 18 deletions src/ucode/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1000,7 +1000,7 @@ def status() -> int:
print_note(
"Use `ug configure skills` to set up Unity Catalog Skills for configured coding tools."
)
print_note("Use `ug skill add` and `ug skill remove --mcp` to manage UC Skills.")
print_note("Use `ug skills add` and `ug skills remove --mcp` to manage UC Skills.")
print_note("Use `ug revert` to clear managed configs and restore prior files.")
return 0

Expand Down Expand Up @@ -1059,7 +1059,7 @@ def revert() -> int:
mcp_app = typer.Typer(add_completion=False, no_args_is_help=True)
app.add_typer(mcp_app, name="mcp", help="MCP servers exposed by ug.")
skill_app = typer.Typer(add_completion=False, no_args_is_help=True)
app.add_typer(skill_app, name="skill", help="Databricks Skills for your coding tools.")
app.add_typer(skill_app, name="skills", help="Databricks Skills for your coding tools.")


def _version_callback(value: bool) -> None:
Expand All @@ -1072,7 +1072,7 @@ def _version_callback(value: bool) -> None:

def _configure_agents_for_mcp(requested: list[str]) -> set[str]:
"""Ensure the named coding agents are set up (workspace + models) so a
subsequent `ug mcp add` / `ug skill add --mcp` has them as targets, and
subsequent `ug mcp add` / `ug skills add --mcp` has them as targets, and
return the full canonical name set. Agents already configured are left as-is;
only the rest are bootstrapped. Model agents go through
configure_workspace_command (which installs binaries and configures models);
Expand Down Expand Up @@ -1234,7 +1234,7 @@ def skills_add(
skills: Annotated[
str | None,
typer.Option(
"--skills",
"--skill",
help="(download) Download exactly these comma-separated fully-qualified "
"`<catalog>.<schema>.<name>` skills, spanning any number of schemas. Not valid "
"with --mcp or --location.",
Expand All @@ -1255,9 +1255,9 @@ def skills_add(
With ``--mcp``, adds the given schemas to the skills MCP connection's scope.
Otherwise downloads skills to project-level skill directories under ``--path``, or
to user-level skill directories when omitted, keeping already-downloaded skills.
``--location`` downloads whole ``<catalog>.<schema>`` schemas; ``--skills``
``--location`` downloads whole ``<catalog>.<schema>`` schemas; ``--skill``
downloads a named set of fully-qualified skills that may span schemas (and takes
no ``--location``). With no ``--location``/``--skills`` on an interactive terminal,
no ``--location``). With no ``--location``/``--skill`` on an interactive terminal,
opens a picker of the workspace's schemas to scope (``--mcp``) or skills to download.
"""
try:
Expand All @@ -1273,17 +1273,17 @@ def skills_add(
if mcp and path is not None:
raise RuntimeError("--path is not supported when using --mcp")
if mcp and requested_skills is not None:
raise RuntimeError("--skills is not supported when using --mcp")
raise RuntimeError("--skill is not supported when using --mcp")
if requested_skills is not None and location is not None:
raise RuntimeError("--skills takes fully-qualified names; drop --location.")
raise RuntimeError("--skill takes fully-qualified names; drop --location.")
# Downloaded skills use shared directory families, so only MCP scopes can be agent-scoped.
if not mcp and agents is not None:
raise RuntimeError("--agents is only supported when using --mcp")
if requested_skills is not None:
invalid = sorted(s for s in requested_skills if not _is_qualified_skill_name(s))
if invalid:
raise RuntimeError(
"--skills entries must be fully-qualified `<catalog>.<schema>.<name>` names "
"--skill entries must be fully-qualified `<catalog>.<schema>.<name>` names "
f"(invalid: {', '.join(invalid)})."
)
configure_selected_skills_download_command(sorted(requested_skills), path)
Expand All @@ -1301,7 +1301,7 @@ def skills_add(
else:
configure_skills_download_picker_command(path=path)
return
raise RuntimeError("--location is required for `ucode skill add`.")
raise RuntimeError("--location is required for `ucode skills add`.")
if mcp:
configured_agents = (
_configure_agents_for_mcp(sorted(requested_agents)) if requested_agents else None
Expand Down Expand Up @@ -1345,7 +1345,7 @@ def skills_remove(
skills: Annotated[
str | None,
typer.Option(
"--skills",
"--skill",
help="(download) Remove exactly these comma-separated fully-qualified "
"`<catalog>.<schema>.<name>` skills, spanning any number of schemas. Not valid "
"with --mcp or --location.",
Expand All @@ -1366,7 +1366,7 @@ def skills_remove(
With ``--mcp``, drops skill schemas from the skills MCP connection: ``--location`` removes the
named ``<catalog>.<schema>`` schemas, and with none on an interactive terminal a picker lists
the scoped schemas. Otherwise removes downloaded skill directories: ``--location`` removes every
skill downloaded from a ``<catalog>.<schema>``, ``--skills`` removes named fully-qualified skills
skill downloaded from a ``<catalog>.<schema>``, ``--skill`` removes named fully-qualified skills
that may span schemas, and with none of them a picker lists every downloaded skill. ``--path``
limits either to one download base. Only skills ucode downloaded are removed; a same-named skill
you authored is left alone.
Expand All @@ -1378,7 +1378,7 @@ def skills_remove(
)
if mcp:
if path is not None or requested_skills is not None:
raise RuntimeError("--path and --skills are not supported with --mcp.")
raise RuntimeError("--path and --skill are not supported with --mcp.")
requested_agents = (
None
if agents is None
Expand All @@ -1390,26 +1390,26 @@ def skills_remove(
elif _stdin_is_interactive():
remove_skills_command(agents=requested_agents)
else:
raise RuntimeError("--location is required for `ug skill remove --mcp`.")
raise RuntimeError("--location is required for `ug skills remove --mcp`.")
return
if agents is not None:
raise RuntimeError("--agents is only supported when using --mcp.")
if requested_skills is not None and location is not None:
raise RuntimeError("--skills takes fully-qualified names; drop --location.")
raise RuntimeError("--skill takes fully-qualified names; drop --location.")
if requested_skills is not None:
invalid = sorted(s for s in requested_skills if not _is_qualified_skill_name(s))
if invalid:
raise RuntimeError(
"--skills entries must be fully-qualified `<catalog>.<schema>.<name>` names "
"--skill entries must be fully-qualified `<catalog>.<schema>.<name>` names "
f"(invalid: {', '.join(invalid)})."
)
remove_downloaded_skills_command([], sorted(requested_skills), path=path)
return
locations = _parse_skill_locations(location)
if path is not None and not locations:
raise RuntimeError("--path is only supported with --location or --skills.")
raise RuntimeError("--path is only supported with --location or --skill.")
if not locations and not _stdin_is_interactive():
raise RuntimeError("--location or --skills is required for `ug skill remove`.")
raise RuntimeError("--location or --skill is required for `ug skills remove`.")
remove_downloaded_skills_command(locations, path=path)
except (RuntimeError, ValueError) as exc:
print_err(str(exc))
Expand Down
6 changes: 3 additions & 3 deletions src/ucode/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -2036,7 +2036,7 @@ def _skill_schema_choice(location: str, skill_count: int, in_scope: bool) -> que
"""Picker row for one schema: value is ``<catalog>.<schema>``, title carries the skill count.

An already-scoped schema is flagged and stays selectable; re-selecting it is a no-op, since
adding to the MCP scope is additive (removal is ``ug skill remove --mcp``).
adding to the MCP scope is additive (removal is ``ug skills remove --mcp``).
"""
noun = "skill" if skill_count == 1 else "skills"
scope_flag = " (already in skill MCP)" if in_scope else ""
Expand Down Expand Up @@ -2149,7 +2149,7 @@ def _removed_schemas_summary(count: int) -> str:


def remove_skills_command(agents: set[str] | None = None) -> int:
"""`ucode skill remove --mcp`: interactively drop skill schemas from clients' skills scopes.
"""`ucode skills remove --mcp`: interactively drop skill schemas from clients' skills scopes.

Shows the schemas in each targeted client's skills scope and removes the ones you select from
those clients. Without ``agents`` a selected schema is removed from every configured client;
Expand Down Expand Up @@ -2183,7 +2183,7 @@ def remove_skills_command(agents: set[str] | None = None) -> int:


def remove_skills_locations_command(locations: list[str], agents: set[str] | None = None) -> int:
"""`ucode skill remove --mcp --location`: drop the named schemas from clients' skills scopes.
"""`ucode skills remove --mcp --location`: drop the named schemas from clients' skills scopes.

Non-interactive counterpart to ``remove_skills_command``. ``agents`` (from ``--agents``) scopes
removal to that subset of configured clients; omitting it targets every configured client. A
Expand Down
2 changes: 1 addition & 1 deletion src/ucode/skills_download.py
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ def _prompt_for_downloaded_skill_removal(records: list[dict]) -> list[dict] | No
def remove_downloaded_skills_command(
locations: list[str], fqns: list[str] | None = None, *, path: str | None
) -> int:
"""`ug skill remove` (download side): delete downloaded skills and forget them.
"""`ug skills remove` (download side): delete downloaded skills and forget them.

With ``fqns``, removes those fully-qualified skills; with ``locations``, every skill
downloaded from those ``<catalog>.<schema>`` schemas; with neither, opens a picker over
Expand Down
Loading
Loading