SkillsOps — governance CLI for agent skills. Validate, audit, version, publish, and distribute skills across any IDE.
Project description
The governance layer for agent skills.
Validate, audit, version, deploy, and self-host agent skills.
One CLI for the whole lifecycle — what kubectl does for Kubernetes, skillctl does for skills.
Why SkillsOps
A SKILL.md is a typed resource with a manifest, versioned content, capabilities,
and a lifecycle. Treating it that way — instead of as "just a markdown file
that lives in a folder" — is the difference between a hobby project and
production governance.
SkillsOps gives that resource one CLI:
skillctl validate ./my-skill # schema, semver, capabilities
skillctl eval audit ./my-skill # security audit -> A-F grade
skillctl apply ./my-skill # push to a content-addressed store
skillctl bump --minor # 1.2.0 -> 1.3.0
skillctl diff my-org/x@1.2.0 my-org/x@1.3.0
skillctl install ./my-skill --target all # deploy to every IDE
skillctl describe skill my-org/x@1.3.0
skillctl logs my-org/x # audit trail from the registry
Skills are written in the same SKILL.md format Anthropic uses —
SkillsOps adds the governance layer so your team can keep them
secure, private, and auditable on infrastructure you control. No
vendor lock-in, no requirement to host skills off-site.
What's in the box
| Capability | Status | Why it's here |
|---|---|---|
validate — schema, semver, capability checks |
stable | Bad manifests should never reach the store. |
eval audit — static security audit (9 categories, ~35 finding codes, ~70 regex patterns; --strict adds an AST pass for Python) |
stable | Block leaked secrets, prompt injection, exfil URLs, unsafe deserialization, encoded payloads in CI. |
apply / get / describe / delete / diff — content-addressed local store |
stable | SHA-256 hashing, integrity verification, structural version diffs. |
bump — semver version edits in skill.yaml |
stable | --major / --minor / --patch with breaking-change detection. |
install / uninstall — multi-IDE deploy (Claude Code, Cursor, Windsurf, Copilot, Kiro) |
stable | One source SKILL.md, native frontmatter on every IDE. |
serve — self-hosted FastAPI registry with token auth, hash-chained audit log |
stable | Run governance on infra you control. See SECURITY.md for the threat model. |
auth / rbac / namespace — role-based access control |
stable | Users, 4 roles, hierarchical namespaces, scoped tokens; every decision audited. See docs/rbac.md. |
policy / observe — runtime policy hooks + OpenTelemetry |
stable | Rate-limit, data-boundary, PII redaction, time-window, output-size; OPA/Cedar; traced + audited. See docs/runtime-policy.md. |
compliance — EU AI Act / ISO 42001 / NIST AI RMF evidence reports |
stable | Maps governance to controls; risk classification; attestations. See docs/compliance.md. |
deploy — canary / blue-green / staged rollouts |
stable | Consistent-hash routing, health checks, auto-rollback, audited. See docs/deployment.md. |
identity / ci / forensics — federation, ABAC, lineage, CI/CD |
stable | OIDC→RBAC, attribute policies, data lineage + forensics, pipeline templates. See docs/enterprise.md. |
eval report — deterministic governance score (80% security audit + 20% schema contract) |
stable | Reproducible: same inputs always yield the same score. |
| Claude Code MCP plugin (5 core tools: validate, audit, bump, diff, publish) | stable | Use SkillsOps from inside an agentic IDE. |
export / import — portable archives |
stable | Backup, share, migrate between hosts. |
Self-hosted, by design
Skills often encode private prompts, internal IP, and security-sensitive review rules. Most teams don't want to ship those to a vendor:
# Run the registry on your own host (or a private VPC).
skillctl serve --hmac-key "$SKILLCTL_HMAC_KEY"
# Issue narrowly-scoped tokens to CI / authors.
skillctl token create --name ci-bot --scope read --scope write:my-org
# Push and pull through your own URL.
skillctl config set registry.url https://skills.internal.example.com
skillctl apply ./my-skill
The registry is FastAPI + SQLite + FTS5, stores blobs content-addressed on the filesystem (or in a git repository), and signs every mutation into a hash-chained audit log. Tokens are SHA-256-hashed at rest; namespace-scoped permissions enforce tenant isolation; rate limiting, CORS, and TrustedHost middleware are on by default. See SECURITY.md for the full threat model and hardening checklist.
Audit in CI
# .github/workflows/skill-audit.yml
name: Skill audit
on:
pull_request:
paths: ['**/SKILL.md', '**/skill.yaml']
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/setup-python@v6
with: { python-version: '3.13' }
- run: pip install skillsops
- run: skillctl eval audit ./skills/ --fail-on-warning --format=github
A copy-paste-ready template lives at examples/workflows/skill-audit.yml. CRITICAL findings fail the build unconditionally.
--format=github emits Actions workflow commands so each finding shows
up as an inline annotation on the offending line of the SKILL.md in the
PR diff. GitHub caps inline annotations at 10 per level per run, so
quiet noisy categories with .skilleval.yaml if you hit it. Tune per-skill
suppressions with a .skilleval.yaml (docs).
The audit is static — an A grade means "no obvious issues against
~35 finding codes / ~70 regex patterns", not "safe to run untrusted".
skillctl eval report combines it with deterministic schema-contract
validation (80% audit + 20% contract) for a reproducible governance score.
Multi-IDE install — what it actually does
Each IDE has its own conventions:
| IDE | Project path | Frontmatter |
|---|---|---|
| Claude Code | .claude/skills/<name>/SKILL.md |
passthrough |
| Cursor | .cursor/rules/<name>.mdc |
description, globs, alwaysApply |
| Windsurf | .windsurf/rules/<name>.md |
trigger: always_on | glob | manual | model_decision |
| GitHub Copilot | .github/instructions/<name>.instructions.md |
applyTo: "<glob>" |
| Kiro | .kiro/steering/<name>.md |
inclusion, fileMatchPattern |
skillctl install reads one source SKILL.md and writes the right file
with the right frontmatter to every target you ask for — no copy-paste
drift.
skillctl install ./my-skill --target all # auto-detect
skillctl install ./my-skill --target cursor,windsurf,kiro # specific
skillctl install ./my-skill --target claude --global # user-level
skillctl uninstall ./my-skill --target all
Install
pip install skillsops # core CLI
pip install "skillsops[server]" # + the registry server
pip install "skillsops[plugin]" # + MCP server for Claude Code plugin
pip install "skillsops[observability]" # + OpenTelemetry tracing
pip install "skillsops[policy-opa]" # + OPA policy integration
pip install "skillsops[all]" # everything
# The LLM-driven optimizer is now a separate, optional package:
pip install skillsops-optimize # authoring-time optimizer (pulls in litellm)
Python 3.10+. The core CLI has only one dependency (pyyaml); the
server and plugin are optional extras. The optimizer ships separately
(skillsops-optimize) because authoring assistance is a different
concern from governance gatekeeping.
60-second tour
# Author from scratch.
skillctl create skill my-org/code-reviewer
# (edit SKILL.md)
# Validate and audit.
skillctl validate
skillctl eval audit .
# Push to your local content-addressed store.
skillctl apply
# Deploy to every IDE in the workspace.
skillctl install my-org/code-reviewer@0.1.0 --target all
# Or, working from an existing SKILL.md:
skillctl validate ~/.claude/skills/code-reviewer/SKILL.md
skillctl eval audit ~/.claude/skills/code-reviewer/
skillctl install ~/.claude/skills/code-reviewer/ --target cursor,windsurf,kiro
apply --local accepts a bare-name skill (no namespace) for the local
store. Only the remote registry requires a namespaced name like
my-org/code-reviewer, because that store is shared.
What this replaces
If you don't use SkillsOps, the typical alternative is a hand-rolled pipeline:
| Without SkillsOps | With SkillsOps |
|---|---|
Bash script that copies SKILL.md to .claude/skills/, .cursor/rules/, .windsurf/rules/, .github/instructions/, .kiro/steering/, each with different frontmatter |
skillctl install ./my-skill --target all |
gitleaks + a list of "things people said agents shouldn't do" + a CI script |
skillctl eval audit --fail-on-warning |
bumpversion config + a git tag script + ad-hoc changelog |
skillctl bump, skillctl diff, skillctl logs |
| Skills shipped to a vendor, or no central store at all | skillctl serve on your own host |
| Ad-hoc "does the schema look right?" review before merging | skillctl eval report (deterministic 80% audit + 20% contract) |
The wedge is the integration: one resource model, one error model, one config, one CLI, one audit trail. Each individual capability has mature standalone alternatives — what those don't give you is the shared lifecycle.
Documentation
| Document | Purpose |
|---|---|
| docs/0-architecture.md | System overview, module map, data flow diagrams |
| docs/1-skill-format.md | Full CLI reference, skill format, registry server, eval suite, optimizer flags, API endpoints |
| docs/3-security-audit.md | Audit categories, severities, suppression workflow |
| docs/rbac.md | RBAC: roles, permissions, namespaces, CLI, bootstrap, audit |
| docs/runtime-policy.md | Runtime policy hooks, built-ins, OPA/Cedar, OpenTelemetry |
| docs/compliance.md | Compliance frameworks, evidence, risk classification, attestations |
| docs/deployment.md | Progressive deployment: canary/blue-green/staged, health, rollback |
| docs/enterprise.md | Identity federation, ABAC, data lineage, forensics, federation, CI/CD |
| SECURITY.md | Threat model, controls, and how to report vulnerabilities |
| CONTRIBUTING.md | How to set up a dev environment and send a PR |
| CHANGELOG.md | Version history and release notes |
Verify your setup
skillctl doctor # Python, deps, store, registry, IDE targets
skillctl version # current version
Development
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev,plugin]"
pytest -m "not integration" # unit tests
pytest -m integration # e2e + real Bedrock tests
See CONTRIBUTING.md for project conventions.
Status
Beta (0.2.x). The core CLI surface (apply, install, validate,
eval audit, eval report, bump, diff, get, describe, delete,
serve, logs) is stable and covered by 640+ unit tests plus an
end-to-end and real-Bedrock integration suite. The registry's REST API
shape and the skillctl: frontmatter block may change before 1.0.0
based on user feedback. The optimizer now lives in the separate
skillsops-optimize package.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file skillsops-0.1.0b7.tar.gz.
File metadata
- Download URL: skillsops-0.1.0b7.tar.gz
- Upload date:
- Size: 298.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b07091035162857e4f2b530706f6013094a18f8d44a079dfec1cdde94c63085
|
|
| MD5 |
18442c3250ad318c60d1344750f45aed
|
|
| BLAKE2b-256 |
4ba29b9b8f0ad17d0a266b3a36e9e186deb4ce0db38158e2e52828b3983a36d9
|
Provenance
The following attestation bundles were made for skillsops-0.1.0b7.tar.gz:
Publisher:
publish.yml on dgallitelli/skillsops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillsops-0.1.0b7.tar.gz -
Subject digest:
2b07091035162857e4f2b530706f6013094a18f8d44a079dfec1cdde94c63085 - Sigstore transparency entry: 2033745703
- Sigstore integration time:
-
Permalink:
dgallitelli/skillsops@1ff29fd5ba95e17b1d7ca699744d66bb5ac8075c -
Branch / Tag:
refs/tags/v0.1.0b7 - Owner: https://github.com/dgallitelli
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1ff29fd5ba95e17b1d7ca699744d66bb5ac8075c -
Trigger Event:
release
-
Statement type:
File details
Details for the file skillsops-0.1.0b7-py3-none-any.whl.
File metadata
- Download URL: skillsops-0.1.0b7-py3-none-any.whl
- Upload date:
- Size: 249.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bcc513376c11cb17e5734ab5f8213ec08ea8788fd524e079f751aec7fb4130e2
|
|
| MD5 |
097c33a4aac06507f97a7fe0a0e4e1df
|
|
| BLAKE2b-256 |
e64c0bfb0b288d8ea207ec0e961d8afd6ed583c692157c116841048e7885fc82
|
Provenance
The following attestation bundles were made for skillsops-0.1.0b7-py3-none-any.whl:
Publisher:
publish.yml on dgallitelli/skillsops
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
skillsops-0.1.0b7-py3-none-any.whl -
Subject digest:
bcc513376c11cb17e5734ab5f8213ec08ea8788fd524e079f751aec7fb4130e2 - Sigstore transparency entry: 2033746123
- Sigstore integration time:
-
Permalink:
dgallitelli/skillsops@1ff29fd5ba95e17b1d7ca699744d66bb5ac8075c -
Branch / Tag:
refs/tags/v0.1.0b7 - Owner: https://github.com/dgallitelli
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@1ff29fd5ba95e17b1d7ca699744d66bb5ac8075c -
Trigger Event:
release
-
Statement type: