Conversation
Refresh a developer's UC-downloaded skills before `ug <agent>` launches, so a skill whose Unity Catalog source changed since download is re-downloaded in place without a manual `ug skills add`. The check reuses the download attribution manifest (`~/.ucode/skills.json`). Each install already records the skill's `uc_update_time` at download, and a new top-level `last_update_check` rate-limits the sweep to once every 24 hours, matching Isaac's plugin marketplace staleness window. Only the launching workspace's own non-managed downloads under the home or working directory are eligible; managed skills stay owned by `ug configure`. The sweep is best effort and fails open, so it never blocks a launch. Co-authored-by: Isaac <no-reply@databricks.com>
| working dir qualify. Managed skills are left to ``ug configure``, and other workspaces' | ||
| downloads are skipped because the launch token authenticates only this workspace. | ||
| """ | ||
| bases = {os.path.normpath(str(Path.home())), os.path.normpath(cwd)} |
There was a problem hiding this comment.
Is this true? I think for claude code at least, the entire every directory in the directory path can have valid skills dirs right? Do you think we can just drop the base check for now for simplicity?
| ] | ||
|
|
||
|
|
||
| def _stale_launch_refs( |
There was a problem hiding this comment.
nit: can you use a more intuitive name? Maybe "get_updated_refs" sth like that?
| return pairs | ||
|
|
||
|
|
||
| def _apply_launch_updates(workspace: str, token: str, pairs: list[tuple[dict, SkillRef]]) -> int: |
There was a problem hiding this comment.
nit: maybe call this update_stale_skills or something more intuitive and human understandable?
| updated = 0 | ||
| for base, refs in refs_by_base.items(): | ||
| path = None if base == home else base | ||
| roots = skill_dir_roots(path) |
There was a problem hiding this comment.
Is there an existing download and write util function, if not can we extract one? I think we've rewritten this in a few places?
| return updated | ||
|
|
||
|
|
||
| def refresh_downloaded_skills_on_launch(state: dict) -> None: |
There was a problem hiding this comment.
One edge case to consider, if a skill has been manually deleted by user but the record is still in manifest, should we still update this in this case? I think we should just remove the invalid record in that case, wdyt?
| print_warning(f"Skipping `{ref.fqn}`: {reason}.") | ||
| continue | ||
| try: | ||
| write_skill(roots, ref, files) |
There was a problem hiding this comment.
So this write_skill overwrite the existing files, right?
What did you change, and why?
Change:
ug <agent>now refreshes a developer's UC-downloaded skills before the agent starts. At launch it reads the download attribution manifest, and for each of the current workspace's own (non-managed) downloads under the home or working directory it calls GetSkill and compares the skill's currentuc_update_timeagainst the value recorded at download. Any skill whose Unity Catalog source is newer is re-downloaded in place, with no overwrite prompt, since only manifest-attributed directories are touched.Why: A downloaded skill previously drifted from its Unity Catalog source until the developer re-ran
ug skills add. This keeps downloads current automatically, the way Isaac keeps installed plugins current.The sweep is rate-limited to once every 24 hours through a new top-level
last_update_checkkey in~/.ucode/skills.json, matching Isaac's plugin marketplace staleness window, so back-to-back launches make no network calls. It is best effort and fails open, so a network, auth, or fetch error is reported and the launch proceeds on whatever is already on disk. It is skipped under--skip-preflight.How do you know it works?
Testing: Added unit tests covering the new manifest accessors and that recording downloads preserves the
last_update_checkstamp (test_skills_state.py), plus the launch path (test_skills_download.py): eligibility filtering (home and working dir, current workspace, managed excluded), stale detection byuc_update_time(newer, equal, older, missing, deleted), the silent in-place overwrite that refreshes both skill roots and the manifest record, and the entry point's rate-limit gate, fail-open behavior, and end-to-end update.uv run ruff check,ruff format --check,ty check src, anduv run pytestall pass locally.This pull request and its description were written by Isaac.