Files
ai-agent/sidecar/test_shared_skills.py
Gabriel Vidal a76cce0c71 feat(users): guest defaults — starting shortcuts + skills shared with guest runs
The owner sets, for every guest at once (users.json guestDefaults):
- promptShortcuts: what a guest's composer starts with. A guest who never
  saved settings gets them as the seed of their ai-agent-settings ui-state.
- sharedSkills: lab/project skills handed to every guest run. The scope
  carries them to the host sidecar, which symlinks them into a generated
  plugin and passes --plugin-dir: they show up as shared:<name>.

GET/PUT /api/guest-defaults (owner), GET /api/guest-skills (the composer's
mentions: shared + granted projects' skills), shareRef/sharedWithGuests on
the skills catalog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 20:20:10 +02:00

51 lines
2.0 KiB
Python

"""shared_skills: the owner's shared skills as a generated `shared` plugin."""
import json
import launch
import shared_skills
def _skill(root, name):
d = root / name
d.mkdir(parents=True)
(d / "SKILL.md").write_text(f"---\nname: {name}\n---\n")
return d
def _roots(tmp_path, monkeypatch):
repo = tmp_path / "homelab" / ".claude" / "skills"
projects = tmp_path / "projects"
monkeypatch.setattr(shared_skills, "SHARED_SKILLS_REPO", repo)
monkeypatch.setattr(shared_skills, "SHARED_SKILLS_PROJECTS", projects)
monkeypatch.setattr(shared_skills, "SHARED_SKILLS_CACHE", tmp_path / "cache")
return repo, projects
def test_plugin_links_repo_and_project_skills(tmp_path, monkeypatch):
repo, projects = _roots(tmp_path, monkeypatch)
ask = _skill(repo, "ask-form")
cut = _skill(projects / "film" / ".claude" / "skills", "cut")
_skill(repo, "cut") # a later ref with a taken name is skipped
root = shared_skills.plugin_dir(["ask-form", "film/cut", "cut", "missing", "../x"])
assert json.loads((root / ".claude-plugin" / "plugin.json").read_text())["name"] == "shared"
links = {p.name: p.resolve() for p in (root / "skills").iterdir()}
assert links == {"ask-form": ask.resolve(), "cut": cut.resolve()}
# same set → same dir, reused as is
assert shared_skills.plugin_dir(["ask-form", "film/cut"]) == root
def test_nothing_to_share_adds_no_flag(tmp_path, monkeypatch):
_roots(tmp_path, monkeypatch)
assert shared_skills.plugin_dir(["nope"]) is None
assert "--plugin-dir" not in launch.scope_args({"skills": ["nope"]})
def test_scope_args_hand_the_plugin_to_the_run(tmp_path, monkeypatch):
repo, _ = _roots(tmp_path, monkeypatch)
_skill(repo, "ask-form")
args = launch.scope_args({"skills": ["ask-form"], "systemPrompt": "hi"})
i = args.index("--plugin-dir")
assert (tmp_path / "cache") in __import__("pathlib").Path(args[i + 1]).parents
assert launch.valid_scope({"skills": ["ask-form"]})