Skip to main content

🛠️ skeel

Declarative agent skill management.

skeel reads a desired-state manifest and applies it through gh skill.

For Nix users, the flake also provides the CLI package and a native Home Manager module that builds skills from pinned sources without activation-time installers. See Nix and Home Manager.

✨ Features

  • Desired state: declare skill sources in one YAML file
  • Inventory, dry run, and diff: list selected skill inventory, preview commands, and compare managed skills against what's installed locally
  • Add, apply, and update: edit desired state, reconcile installed skills with live progress, and update declared installed skills
  • Target flags: choose project or user scope, a specific agent's skill directory, or any explicit directory from the CLI
  • JSON output: pass --json for one machine-readable object on stdout

🚀 Quickstart

Run skeel directly with uvx:

uvx skeel

⚙️ Manifest

Default path: .agents/skills.yaml in project scope, ~/.agents/skills.yaml in user scope.

sources:
  anthropics/skills:
    - skill-creator
  mavam/quarto-brief: all
  openclaw/gogcli:
    - gog
  acme/skills:
    pin: main
    skills:
      - code-review
      - name: deploy
        frontmatter:
          disable-model-invocation: true
          compatibility: Requires Git
  elevenlabs/skills:
    pin: main
    skills:
      - name: elevenlabs-agents
        spec: agents

Use all to install every skill discovered in a source. A list selects individual skills; [all] selects a skill literally named all. With source options, use skills: all:

sources:
  mavam/skills:
    pin: main
    skills: all

Empty values and options mappings without skills still select all skills for backward compatibility. The CLI writes explicit all selectors when adding or selecting an entire source. Custom install commands require an explicit skill list rather than all. Put frontmatter overrides on an individual skill entry. Skeel applies configured top-level fields to the installed SKILL.md, reapplies them after updates, and reports drift through diff.

Frontmatter overrides are shallow: scalar and list values replace upstream values, while metadata entries merge with the upstream map. The skill name and skeel's github-* provenance metadata cannot be overridden. To install a skill under another name, use an explicit entry with spec as the upstream selector and name as the local install name. Skeel renames the directory and frontmatter together while preserving the upstream path in its provenance. Renamed skills require the built-in GitHub installer and cannot use custom install commands.

Removing a frontmatter field stops skeel from managing it; use apply --reinstall to restore upstream content immediately. Applying overrides normalizes the YAML frontmatter and may discard comments or scalar formatting. Overrides cannot target symlinked files or skill directories that resolve outside the managed skills directory.

During update, selected skills refresh independently, while an install-all source refreshes once and discovers newly added upstream skills. Frontmatter overrides require an explicit skill entry and don't apply to install-all sources.

By default, skeel uses project scope: .agents/skills.yaml and .agents/skills in the current working directory. Use -g or --scope user for global installs into ~/.agents/skills:

uvx skeel -g apply

Use --manifest (-m) for a non-default desired-state manifest:

uvx skeel --manifest ./skills.yaml apply --dry-run

Scope selects the base directory for the implicit manifest and managed skill directory: project scope uses the current working directory and user scope uses $HOME. Use -a or --all with commands that can operate on both scopes. If the implicit manifest does not exist, apply, diff, list, and update are no-ops; add creates the manifest. Use --manifest or SKEEL_MANIFEST to use a manifest from another path. Because an explicit manifest path is not scoped, -a and --all are rejected when --manifest or SKEEL_MANIFEST is set.

🎯 Agent Targets

By default, skeel manages the universal .agents/skills directory. Set agents in the manifest to manage several targets with one command:

agents:
  - universal
  - claude-code

sources:
  anthropics/skills:
    - skill-creator

With this user manifest, skeel -g apply and skeel -g update manage both ~/.agents/skills and ~/.claude/skills. list, diff, and add/remove with --apply use the same defaults. agents must be a non-empty list of supported agent IDs; omit it to keep the universal default. Targets resolving to the same directory, including through symlinks, are processed only once.

Each scope uses its own manifest's defaults. Manifest discovery stays unchanged: project scope reads .agents/skills.yaml in the current directory unless an explicit agent selects a different base. The manifest's defaults select target directories, not another manifest.

An explicit --agent or --dir overrides the entire default list. Use --agent to manage a specific agent's skill directory instead, or --dir for an explicit directory:

uvx skeel --agent claude-code apply
uvx skeel --agent codex apply
uvx skeel --agent pi -g list
uvx skeel --dir ./custom/skills list

Agent names and directories mirror the GitHub CLI host registry. List them with:

uvx skeel agents

For an agent-specific target, project scope anchors at the enclosing git repository root so skills land where the agent discovers them, falling back to the working directory outside a repository. The universal target retains the default current-directory anchoring and project-over-user shadowing behavior. The same .agents/skills.yaml supplies skill selections for every target. Agent-specific targets reconcile project and user scope independently. Valid skill directory symlinks count as installed, so links to universal skills remain idempotent. When the same skill appears in both scopes, skeel warns and lets the agent decide runtime precedence. --dir is a complete target on its own and cannot be combined with --agent or scope selectors.

Custom install: commands receive SKEEL_AGENT, SKEEL_SCOPE, SKEEL_SKILLS_DIR, and SKEEL_MANIFEST in their environment. SKEEL_AGENT is universal for the default target, the selected agent ID for --agent, and empty for --dir. Portable installers must honor SKEEL_SKILLS_DIR; skeel verifies that declared skills appear there and fails the step otherwise.

✨ Commands

By default, every command operates on project scope. Use -g, --user, --global, or --scope user to operate on user scope. Use -a or --all to operate on both project and user scopes for diff, list, apply, remove, and update when using the implicit manifests.

Human output is consistent across commands: the first column is the action marker, the second column is a muted scope glyph (★ for project, ⌂ for user), followed by the skill name, the source, and a muted suffix for versions, paths, or diagnostic details.

For scripts, pass --json to add, apply, diff, list, path, remove, or update to emit one machine-readable object on stdout.

list

Show installed skills together with manifest status. Missing manifest skills are marked with ✘; installed skills that are not declared in the manifest still appear in the inventory and include "managed": false in JSON output. Rows are tagged with their scope glyph. Sources declared without a skill list expand to the installed skills from that source instead of showing *.

uvx skeel list -a
✔︎ ★ tenzir-docs tenzir/skills main@a5d04ab
✘ ★ gog openclaw/gogcli
✔︎ ⌂ skill-creator anthropics/skills main@3cf9a8d
✔︎ ⌂ wrangler cloudflare/skills main@45cc198
✔︎ ⌂ clacks
✔︎ ⌂ quarto-brief mavam/quarto-brief main@e89c555

diff

Compare desired state with installed skills. + rows would be installed by apply; - rows would be removed by apply --prune; ↑ rows have pending frontmatter overrides.

uvx skeel diff
+ ★ wrangler cloudflare/skills
+ ★ vectorize cloudflare/skills
↑ ★ deploy acme/skills frontmatter
- ★ obsolete-skill installed
- ★ old-experiment installed

JSON output includes missing skills in missing, undeclared skills in extra, and frontmatter drift in changed. Any non-empty array sets in_sync to false and makes diff exit with status 1.

apply

Reconcile installed skills with the manifest. Missing skills are installed; skills not declared in the manifest are preserved by default. Pass --prune to also remove undeclared extras. Pruning applies only to a full reconciliation; combine neither --reinstall nor a source selector with --prune. Use --reinstall to run every manifest installer without diffing first, or apply <source> [skill] to target one source. A selector that does not match the manifest exits with an error.

uvx skeel apply --dry-run --prune
↳ gh skill install cloudflare/skills wrangler --allow-hidden-dirs --dir .agents/skills --force
↳ gh skill install cloudflare/skills vectorize --allow-hidden-dirs --dir .agents/skills --force
↳ rm -rf .agents/skills/obsolete-skill
uvx skeel apply --prune
+ ★ wrangler cloudflare/skills
+ ★ vectorize cloudflare/skills
- ★ obsolete-skill

Immediately before deleting anything, skeel verifies that the planned target and skill entries have not been replaced, requires a regular SKILL.md, refuses paths outside the target, and never removes the target root. Pruning a symlinked skill removes only the link, not its destination. A target directory itself may be reached through a symlink; skeel pins its resolved destination while planning. These safeguards also cover skills pruned from pinned install-all sources during update.

update

Update installed skills that are represented by the manifest. Explicit skill entries update independently, and remote update checks run in parallel. An install-all entry refreshes once at source level, including when it uses a branch pin such as main. This refresh also installs skills added to the upstream source since the previous update.

Pass a source, or a source and skill, to update only that manifest selection. A source-and-skill selector stays targeted to that skill, even when the manifest entry normally installs all skills. A selector that does not match the manifest exits with an error.

uvx skeel update
uvx skeel update tenzir/skills
uvx skeel update tenzir/skills tenzir-docs
uvx skeel update -a
✔︎ ★ teach mattpocock/skills main@975430f
✔︎ ★ tenzir-docs tenzir/skills main@f3842c1
✔︎ ★ clacks downstairs-dawgs/clacks
✘ ★ broken-skill broken/source

Pinned GitHub entries are updated by resolving the configured pin and refreshing installed files when the source changes. Branch pins are checked for new content. Immutable tag and commit pins report current when their recorded tree is unchanged, without downloading the same archive again. Skills installed by gh skill include provenance in SKILL.md frontmatter, so future updates can track them directly.

Updates for pinned install-all sources also prune skills removed upstream. Skeel deletes a directory only when its github-repo matches the source exactly and its github-path is absent from the source's current inventory. Metadata-less, malformed, and differently owned directories stay untouched. The source-level result names removed skills, and JSON output includes their paths.

A dry run resolves a pinned source and previews each removal without changing local files. Unpinned install-all sources still add and refresh skills without pruning because gh skill install --all doesn't expose the remote inventory. Use apply --reinstall when you need to force-refresh an install-all entry without pruning it.

add

Upsert a source or source/skill entry into the manifest. Omit the skill to select all skills from the source. Pass --apply to reconcile immediately. Use --name to install an explicitly selected skill under a different local name:

uvx skeel -g add elevenlabs/skills agents --name elevenlabs-agents

This writes spec: agents as the upstream selector and name: elevenlabs-agents as the installed directory and frontmatter name. Updates continue to use the upstream selector.

uvx skeel add tenzir/skills tenzir-docs@main
✔︎ ★ tenzir-docs tenzir/skills .agents/skills.yaml
uvx skeel add mavam/quarto-brief --dry-run
↳ mavam/quarto-brief .agents/skills.yaml

remove

Remove an unambiguous skill name from the selected manifest. Pass --apply to reconcile immediately; this deletes exactly the deselected skill and leaves other undeclared skills alone. A selector that does not match the manifest exits with an error.

add and remove are intentionally asymmetric: adding starts from a source because skeel needs to know where to install from, while removing starts from a skill because that is the common user intent. Use --source only to disambiguate or remove a whole source.

uvx skeel remove tenzir-docs
✔︎ ★ tenzir-docs tenzir/skills .agents/skills.yaml

Local skill names must be unique within a manifest. Use the local name when removing a renamed skill:

uvx skeel remove elevenlabs-agents

Omit the skill to remove the whole source selected by --source. For custom installers, list every produced skill under skills: so --apply can identify the directories safely:

uvx skeel remove --source mavam/quarto-brief --dry-run
↳ mavam/quarto-brief .agents/skills.yaml

agents

List supported agents with their project and user skill directories. JSON output uses absolute user paths. Claude Code's user directory reflects CLAUDE_CONFIG_DIR when the variable is set; ~ expands to the home directory, and relative values are anchored there.

uvx skeel agents
github-copilot   .agents/skills   ~/.copilot/skills
claude-code      .claude/skills   ~/.claude/skills
codex            .agents/skills   ~/.codex/skills
cursor           .agents/skills   ~/.cursor/skills
pi               .pi/skills       ~/.pi/agent/skills
...

path

Print the manifest path that skeel would use for the selected scope.

uvx skeel path
.agents/skills.yaml

Use -a to print both implicit paths:

uvx skeel path -a
project .agents/skills.yaml
user    /Users/alice/.agents/skills.yaml

🧰 GitHub Skill Policy

When applying, skeel delegates placement to gh skill with:

gh skill install <repo> <skill> --dir .agents/skills --force

The target directory is derived from scope: project scope uses the current working directory and user scope uses $HOME. A bare GitHub source installs all skills from that repository:

sources:
  mavam/quarto-brief: all

which runs:

gh skill install mavam/quarto-brief --all --dir .agents/skills --force

For installers that are not backed by gh skill, provide source-level install commands under the source key. Skeel runs those commands as the complete install command set:

sources:
  slack-clacks/clacks:
    skills:
      - clacks
    install:
      - uvx --from slack-clacks clacks skill --mode universal --force

To add a GitHub source from the CLI, use the same positional shape as gh skill install:

uvx skeel add tenzir/skills tenzir-docs@main
uvx skeel remove tenzir/skills tenzir-docs
uvx skeel add mavam/quarto-brief --apply

📄 License

MIT

Release files for skeel 2.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for skeel 2.5.1
File Size Uploaded
skeel-2.5.1.tar.gz 82.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for skeel 2.5.1
File Interpreter ABI Platform
skeel-2.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 138.4 kB

Release files / skeel-2.5.1.tar.gz

Download URL skeel-2.5.1.tar.gz
Size 82.4 kB
Tags Source
SHA-256 checksum
How to use checksums
1a18b080c246055602bb1c1ff3eb3ec800e1e004cd4aacc0a5612ae572835be6
BLAKE2b-256 checksum
How to use checksums
4212190589a7ea4ad6ee9910ead9cb4e423282f03340f2d1a8193ba0ab53667a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / skeel-2.5.1-py3-none-any.whl

Download URL skeel-2.5.1-py3-none-any.whl
Size 56.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5296bbd00d40eb298ac7026724ac67be56c219edd469a5e1507ef17eb79ec460
BLAKE2b-256 checksum
How to use checksums
220790588d077fcc3f46d55527db1ecb433c8eb00403edb7e5dc17f4fdb89cef
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

2.5.1 This release

2 release files

2.5.0

2 release files

2.4.0

2 release files

2.3.0

2 release files

2.2.0

2 release files

2.1.0

2 release files

2.0.2

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.1.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.10.0

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page