Skip to content

feat(skill-install): repo-scoped install so curated skills don't leak across repos#127

Merged
aktasbatuhan merged 2 commits into
mainfrom
feat/repo-scoped-skill-install
Jun 9, 2026
Merged

feat(skill-install): repo-scoped install so curated skills don't leak across repos#127
aktasbatuhan merged 2 commits into
mainfrom
feat/repo-scoped-skill-install

Conversation

@aktasbatuhan

Copy link
Copy Markdown
Member

Closes #125.

Why

Curated skills are curated per-project but were installed globally (~/.claude/skills, ~/.codex/skills), so every project's skills were visible in every repo and the agent's own skill matching could suggest a skill from an unrelated repo. watchmen's statusline hint is already project-scoped (verified: clean index + correct resolve_project_key); the leak is the global install feeding the agent's native suggestions.

What changed

  • scope (project | global) on the install path, with skill_base() resolving the target. Project scope → <source_repo>/.claude/skills. The low-level install_skill keeps a global default (simple primitive); install_project, the curator auto-install, and the CLI default to project so curated skills land in their origin repo.
  • Manifest records scope; uninstall_skill is now manifest-driven (finds links in either location) with project_key to disambiguate.
  • migrate_to_project_scope() relocates existing global links into their repos. Robust to manifest loss: it also sweeps the global dirs on disk and infers project/slug from the bundle target path. Links with no resolvable repo are left global (reported, never dropped); user-made dirs are never touched.
  • CLI: watchmen install --scope project|global (default project) and watchmen install --migrate. Prints a one-line .gitignore heads-up for repo-local links but never writes .gitignore (the user's call, per the issue).
  • Viewer install/uninstall endpoints go through the same project-scoped path.
  • Two-project same-slug collisions are no longer possible (each lives in its own repo).
  • Drive-by: fixed a pre-existing SyntaxWarning in skill_install (raw docstring).

Migrating an existing install

watchmen install --migrate moves your current global links into their repos. Then per repo you only see that repo's curated skills; new installs are repo-scoped by default.

Testing

  • New/updated tests: project-scope target, no-repo skip, collision-free, manifest-driven + filesystem-sweep migration, repo-unresolvable-left-global, CLI default + --migrate dispatch, hermetic viewer endpoints.
  • Full suite green (561 passed), ruff clean. No plugin/ payload touched, so no plugin version bump needed.

Follow-up

Shared-skill distribution across related repos (e.g. the kai-* family) is tracked separately in #126.

… across repos (#125)

Curated skills are per-project but were installed into the global
~/.claude/skills / ~/.codex/skills, so every project's skills were visible in
every repo and the agent's own skill matching could suggest a skill from an
unrelated repo. (watchmen's statusline hint is already project-scoped; the leak
is the global install feeding the agent's native suggestions.)

- skill_install: add a `scope` (project|global) + `skill_base()`. Project scope
  targets <source_repo>/.claude/skills (resolved from the tracked repo, or a
  passed `repo`). The low-level install_skill defaults to global (keeps the
  primitive simple); install_project / auto-install / the CLI default to
  project so curated skills land in their origin repo.
- Manifest records `scope`; uninstall is now manifest-driven so it finds links
  in either location, with project_key to disambiguate.
- migrate_to_project_scope(): relocate existing global links into their repos.
  Robust to manifest loss — also sweeps the global dirs on disk and infers
  project/slug from the bundle target path. Links with no resolvable repo are
  left global, never dropped; user-made dirs never touched.
- CLI: `watchmen install --scope project|global` (default project) and
  `watchmen install --migrate`. Prints a one-line .gitignore heads-up for
  repo-local links but never writes .gitignore (user's call).
- viewer install/uninstall endpoints go through the same project-scoped path.
- Two-project same-slug collision is no longer possible under project scope.

Tests: project-scope target, no-repo skip, collision-free, manifest-driven and
filesystem-sweep migration, repo-unresolvable left-global, CLI default + migrate
dispatch, hermetic viewer endpoints. Full suite green (561). Also fixes a
pre-existing SyntaxWarning in skill_install (raw docstring).

Follow-up: shared-skill distribution across related repos (#126).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aktasbatuhan aktasbatuhan added the enhancement New feature or request label Jun 9, 2026
…dex review)

Codex review of #127 flagged two paths where the file-moving operations could
touch content watchmen doesn't own.

- blocker: _is_managed trusted a manifest entry unconditionally, so a stale row
  could let uninstall/migrate rmtree user content that had replaced our symlink
  at the same path. Ownership is now decided from disk only: the live object
  must be a symlink resolving into BUNDLES_DIR (this already survived manifest
  loss; the manifest claim was the unsafe part). uninstall now reports
  skipped_conflict and preserves user content at a stale path.
- should-fix: the migration filesystem sweep inferred slug from parts[-1], so a
  symlink to a skill SUB-path could migrate as a bogus slug pointing at the
  wrong source. Now requires exactly <key>/skills/<slug> (len(parts) == 3).

Tests: user content preserved at a stale manifest path under both uninstall and
migrate; sub-path symlink ignored by the sweep. Full suite green (564).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@aktasbatuhan aktasbatuhan merged commit 1612be3 into main Jun 9, 2026
8 checks passed
@aktasbatuhan aktasbatuhan deleted the feat/repo-scoped-skill-install branch June 9, 2026 08:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Repo-scoped skill install: curated skills leak across repos as agent suggestions

1 participant