Skip to content

Latest commit

 

History

History
267 lines (195 loc) · 8.52 KB

File metadata and controls

267 lines (195 loc) · 8.52 KB

skills-manager

Cross-platform CLI to centralize, deduplicate, and manage coding-agent skills for Claude Code and Codex across multiple repositories and machines.

Why

Coding-agent skills (prompt libraries, scripts, references, adapters) tend to scatter across repos and machines. Copies diverge, renames break identity, and there is no single source of truth. skills-manager solves this by:

  1. Collecting skills from configured locations on a machine
  2. Deduplicating them deterministically (newest wins)
  3. Importing winners into a single canonical repository
  4. Building tool-specific dist targets (Claude / Codex)
  5. Relinking project repos to point at canonical copies via symlinks

After migration, each repo's .claude/skills/ and .agents/skills/ directories contain symlinks to the central skills-manager/skills/dist/ — no more independent copies that drift apart.

Design decisions

No per-project manifest

There is no manifest file listing "enabled skills." The active set of skills for a repo is exactly what is linked inside that repo's .claude/skills/ and .agents/skills/ directories. Links are the truth.

Canonical registry (not a manifest)

The file registry/skills-registry.json tracks identity — canonical slugs, aliases, rename history, provenance, content hashes. It exists so that:

  • A skill renamed from auto-converger to autoconverger is recognized as the same thing
  • Re-importing the same skill from another machine doesn't create duplicates
  • You can query "where is this skill, what is its canonical name?"

The registry is not an enablement file. It does not control which repos use which skills.

Deduplication: newest wins

When the same skill slug appears in multiple locations:

  1. Same content hash → exact duplicate, keep either
  2. Different hash → newest modification time wins
  3. Timestamp tie → prefer project > personal > legacy, then richer file tree, then lexicographic path

Losing copies are archived, never silently discarded.

Installation

cd /path/to/skills-manager
pip install -e .

Or run directly:

python3 -m skills_manager.cli --help

Requires Python 3.11+. No external dependencies.

Configuration

Create a machine-specific config file (JSON):

{
  "machine_name": "linux-dev",
  "skills_manager_repo": "/home/user/skills-manager",
  "repo_roots": [
    "/home/user/projects"
  ],
  "global_skill_roots": [
    "/home/user/.claude/skills",
    "/home/user/.agents/skills"
  ],
  "legacy_roots": [],
  "quarantine_root": "/home/user/skills-manager/backups/quarantine"
}

See config/machine.example.json and config/machine.example-windows.json for templates.

Field Description
machine_name Human label for this machine
skills_manager_repo Absolute path to this repository
repo_roots Directories to scan for git repos (up to 3 levels deep)
global_skill_roots Personal/global skill directories to scan
legacy_roots Additional non-standard skill locations
quarantine_root Where to move duplicate global skills (optional)

Commands

Full migration (recommended first run)

skills-manager migrate --config config/machine.json

Runs: collect → build → relink → quarantine. Add --no-quarantine to skip the last step.

Individual steps

# Collect and import from configured roots
skills-manager collect --config config/machine.json

# Build dist targets from canonical src
skills-manager build

# Relink all discovered repos to canonical dist
skills-manager relink --config config/machine.json

# Quarantine duplicate global skills
skills-manager quarantine --config config/machine.json

Query commands

# List all skills in the registry
skills-manager list

# Show where a skill lives (accepts aliases)
skills-manager where firecrawl

Per-repo link management

# Add a skill link to a specific repo
skills-manager add-link --repo /path/to/repo --skill playwright --target both

# Remove a skill link
skills-manager remove-link --repo /path/to/repo --skill playwright --target claude

Target options: claude, codex, both (default).

How it works

Collection

The collector scans only configured roots:

  • Global roots: ~/.claude/skills, ~/.agents/skills (one level of subdirectories)
  • Repo roots: finds git repos (up to 3 levels deep), then scans .claude/skills/ and .agents/skills/ within each
  • Legacy roots: any additional configured directories

A directory is recognized as a skill if it contains SKILL.md.

Canonical store structure

skills-manager/
  skills/
    src/              # canonical editable source (one dir per skill)
      firecrawl/
        SKILL.md
        rules/
        .provenance.json    # import metadata (auto-generated)
      playwright/
        SKILL.md
        agents/
          openai.yaml       # Codex adapter
        scripts/
    dist/             # tool-specific emit targets (auto-generated)
      claude/
        firecrawl/    # SKILL.md + rules/ (no agents/)
        playwright/   # SKILL.md + scripts/ (no agents/)
      codex/
        firecrawl/    # full copy including agents/ if present
        playwright/   # full copy including agents/

Building dist

  • Claude dist: copies everything from src except agents/ (Codex-specific) and .provenance.json
  • Codex dist: copies everything from src except .provenance.json

Both tools read SKILL.md as the primary skill definition. The only difference is whether the Codex adapter (agents/openai.yaml) is included.

Relinking

When relinking a repo:

  1. For each canonical skill and each target (claude/codex):
  2. If a local (non-link) skill dir exists → back it up → remove it
  3. If a correct symlink already exists → skip (idempotent)
  4. Create symlink to the dist target

Backups go to backups/repo-originals/ with timestamps.

Quarantine

Global/personal skills that duplicate canonical ones can be moved to a quarantine directory. Only skills whose slug matches a registry entry are affected. Symlinks are left alone. Nothing is permanently deleted.

Cross-platform links

OS Strategy
Linux/macOS os.symlink() — standard symlinks
Windows Try os.symlink() first (works in Developer Mode). Fall back to mklink /J (NTFS junction, no elevation needed)

If both fail on Windows, a clear error is reported.

Registry format

registry/skills-registry.json:

{
  "version": 1,
  "updated_at": "2025-06-01T12:00:00+00:00",
  "skills": [
    {
      "canonical_slug": "firecrawl",
      "display_name": "firecrawl",
      "aliases": ["fire-crawl"],
      "current_source_relpath": "skills/src/firecrawl",
      "adapter_targets": ["claude", "codex"],
      "status": "active",
      "provenance": [
        {
          "action": "imported",
          "from_path": "/home/user/.agents/skills/firecrawl",
          "scope": "personal",
          "machine": "linux-dev",
          "hash": "abc123...",
          "at": "2025-06-01T12:00:00+00:00"
        }
      ],
      "latest_hash": "abc123...",
      "first_seen_at": "2025-06-01T12:00:00+00:00",
      "last_seen_at": "2025-06-01T12:00:00+00:00",
      "supersedes": [],
      "superseded_by": null
    }
  ]
}

Multi-machine workflow

  1. Set up skills-manager as a git repo on Machine A
  2. Run skills-manager migrate --config config/machine-a.json
  3. Commit and push
  4. Clone on Machine B
  5. Create config/machine-b.json with Machine B paths
  6. Run skills-manager migrate --config config/machine-b.json

The registry merges cleanly because slugs are deterministic and hashes prevent unnecessary overwrites.

Running tests

pip install pytest
python3 -m pytest tests/ -v

62 tests covering: models, collector, registry, deduplication, builder, linker, quarantine, Windows fallback (mocked), and a full end-to-end migration scenario.

Known limitations

  • Skill detection requires SKILL.md in the directory root. Directories without it are ignored.
  • Frontmatter parsing is minimal (key: value lines between --- fences). Complex YAML is not supported.
  • Windows junctions don't support relative paths — absolute paths are used.
  • Repo discovery stops at 3 levels of nesting by default. Deeply nested repos may need explicit repo_roots entries.
  • No conflict resolution UI — newest always wins. Manual curation requires editing skills/src/ directly.
  • No automatic sync — run migrate when you want to update. There is no daemon or watcher.