Skip to content

feat(skills): per-skill install/uninstall + approve/reject + list --filter#56

Merged
scotthavird merged 1 commit into
mainfrom
feat/skills-install-uninstall
May 20, 2026
Merged

feat(skills): per-skill install/uninstall + approve/reject + list --filter#56
scotthavird merged 1 commit into
mainfrom
feat/skills-install-uninstall

Conversation

@scotthavird
Copy link
Copy Markdown
Contributor

Summary

Four new subcommands and a new --filter flag bring promptconduit skills to parity with the web UI's per-skill controls. Targets the skill-bundle path (.claude/skills/<name>/SKILL.md) which auto-loads via the Skill tool on trigger phrases — distinct from the slash-command path that bulk sync writes.

promptconduit skills install <name> [--scope project|global] [--force]
promptconduit skills install --all
promptconduit skills uninstall <name> [--force]
promptconduit skills uninstall --all
promptconduit skills approve <name>
promptconduit skills reject  <name>
promptconduit skills list --filter new|ready|removed|all

Backed by:

  • New internal/skills package (Manifest, ResolveScope, ValidateName) — atomic file writes (CreateTemp + Rename), single source of truth at ~/.config/promptconduit/skills-installed.json.
  • Re-uses existing client.GetSkills / client.ApproveSkill / client.GetSkillCommandFile — no API client surface change.
  • 11 cmd-level integration tests + 18 unit tests across the new package. 100% green.

Why a separate skill-bundle path

The web UI explicitly says "Save each download to .claude/skills/<name>/SKILL.md" and the API returns X-Skill-Suggested-Path matching that. Existing sync writes the slash-command form at .claude/commands/<name>.md. Both are valid Claude Code features but address different UX (auto-trigger vs typed /foo). This PR adds the bundle form without changing sync.

Behavior contract

Scenario Outcome
install foo when not on server exit 1, not found
install foo fresh atomic write + manifest entry
install foo at same sha no-op, prints already up to date
install foo after hand-edit, no --force prompts (or skips on non-TTY)
install foo --force over edits overwrites
install --all iterates approved set; per-file atomic
uninstall foo clean rm file + cleanup empty parent + manifest remove
uninstall foo after hand-edit, no --force refuses, prints local modifications
uninstall foo --force always removes
uninstall foo, not in manifest refuses, prints not tracked
uninstall --all iterates manifest only; safety rules per file
list --filter ready/removed/new/all mirrors web tabs (new=is_approved null)

Out of scope (deferred)

  • Multi-file bundles (reference/*.md, scripts/*) — needs platform changes to bundle generation + R2 storage. Manifest array shape is forward-compatible.
  • SessionStart hook for inline suggestions — Phase 3.
  • Update-detection ("the skill you have is out of date on the server") — Phase 4.

Test plan

  • make test — all packages green (29 new tests)
  • make lint — no new issues introduced by this PR
  • make build — clean
  • Smoke against prod: approveinstall → file landed at correct path with manifest entry → uninstall → file gone, manifest cleared → reject to revert state

After merge: tag v0.3.5 (patch bump, additive). GoReleaser + homebrew-tap will auto-update.

Adds four new subcommands plus a --filter flag on `skills list`:

  promptconduit skills install <name> [--scope project|global] [--force]
  promptconduit skills install --all
  promptconduit skills uninstall <name> [--force]
  promptconduit skills uninstall --all
  promptconduit skills approve <name>
  promptconduit skills reject  <name>
  promptconduit skills list --filter new|ready|removed|all

Targets the Claude Code skill-bundle path (.claude/skills/<name>/SKILL.md),
not the slash-command path (.claude/commands/<name>.md) that bulk `sync`
writes. Skills installed this way auto-load via the Skill tool on trigger
phrases instead of needing /<name> at the prompt.

New internal/skills package provides:
- Manifest (~/.config/promptconduit/skills-installed.json) — source of
  truth for what we wrote. Records id, name, scope, install time, sha256
  per file. Atomic writes via CreateTemp + Rename.
- ResolveScope — project (in current git repo) vs global routing, with
  auto-detect when --scope is unset.
- ValidateName — enforces the Anthropic ^[a-z0-9-]{1,64}$ rule before
  any filesystem operation.

Safety guarantees:
- Re-install at the same sha is a no-op
- Re-install over a hand-edited file prompts (or fails on non-TTY) unless
  --force is given
- Uninstall refuses to delete a file whose on-disk sha doesn't match the
  manifest record, unless --force is given
- Skills not in the manifest are never touched by uninstall, even
  with --force
- Manifest writes are atomic; --all batches save once at end so a
  crash mid-batch leaves a consistent record
@scotthavird scotthavird merged commit 8d25137 into main May 20, 2026
1 check passed
@scotthavird scotthavird deleted the feat/skills-install-uninstall branch May 20, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant