knack
knack is an open-source Rust CLI and self-hostable registry project for teams to package, validate, version, publish, discover, install, and govern Agent Skills.
The current v0 focuses on local skill authoring and distribution primitives. See PROJECT.md for the north-star product direction.
Install
Install the CLI from crates.io:
Install the registry server:
Or build both from source:
# Binaries land at target/release/knack and target/release/knack-registry
Verify the install:
Usage
Create a skill:
Initialize a project manifest:
This seeds the public knack registry (public → https://knack.ajac-zero.com) into the new manifest so you can knack find <term> and knack add public:<name> immediately. Pass --no-public-registry to skip if you want a bare manifest or only intend to use an internal registry. Either way, you can remove the bootstrapped entry later with knack registry remove public.
By default, commands use project scope:
.agents/knack.toml
.agents/knack.lock
.agents/skills/
Pass -g (or --global) on any command to operate on user-wide skills instead of the current project. knack add creates the manifest on first use, so an explicit knack init is only needed if you want to customize the install target:
Global scope uses:
~/.agents/knack.toml
~/.agents/knack.lock
~/.agents/skills/
System-wide defaults are configured at:
/etc/knack/knack.toml
/usr/local/share/knack/skills/
These have no dedicated CLI flag because they are an administrator concern. Edit /etc/knack/knack.toml directly (with sudo) to seed registry aliases that every user on the machine should inherit.
Registry aliases are inherited in this order, with later layers overriding earlier layers:
system (/etc/knack/knack.toml)
global (~/.agents/knack.toml)
project (./.agents/knack.toml)
That lets administrators inject aliases such as tea: for all users while still allowing user or project-level overrides.
Read-only commands (knack find, knack registry list) search the merged set of registries from all three layers automatically. They work from a directory with no project manifest, so a globally-registered company: alias is reachable without an explicit -g.
Add and install a skill source into the manifest target:
This updates .agents/knack.toml and writes .agents/knack.lock with the resolved source and a deterministic checksum of the installed skill contents.
Install all skills declared in .agents/knack.toml (uses the lockfile,
reproducible):
For CI: verify the install and lockfile are consistent with the manifest without modifying anything. Exits non-zero with an actionable message on missing lockfile entries, missing installs, or checksum drift:
Pull upstream changes for skills tracking a moving ref (branch, tag).
Sources pinned to a SHA-shaped ref are skipped — pass -f / --force
to retry them anyway. Pass -n / --dry-run to preview what would
change without touching the install dir or lockfile (still fetches
from the network so the preview is accurate):
The lockfile records each skill's resolved source as a
content-addressed commit SHA — gh: and git+ URLs embed @<sha>,
http+knack: URLs append #sha=<sha>. That makes knack sync truly
reproducible: a teammate cloning the repo six months later installs
the same commits, regardless of where main has moved since.
Validate a skill:
Package a skill:
Install a skill directory or package archive:
Install directly from a public GitHub repository:
For example:
Install from any Git host supported by your local git command:
Register a Git host alias in knack.toml:
For HTTP knack registries the alias can be omitted — the CLI fetches
GET <url>/info and adopts whatever name the registry advertises so
every client of the same registry uses the same alias:
Then add skills through the alias:
Alias syntax is:
alias:owner/repo[@ref]/path/to/skill
Generate a searchable registry index from a local tree of skills:
Serve the index with knack-registry:
Build and run the registry container:
The image defaults to:
Override arguments as needed:
To make the HTTP registry the only thing users need to interact with, serve skill archives too:
With --name company, search results return proxy install sources such as company:deploy-container. The CLI resolves those through the HTTP registry and downloads the skill archive from the registry server. The registry also advertises the name at GET /info so clients can knack registry add <url> without supplying an alias.
When a skill's backing source is a git repository, the registry includes the resolved commit SHA in the archive response via the X-Knack-Resolved-Sha header. The CLI captures it into the lockfile so HTTP-registry installs are pinned the same way gh:/git+ installs are.
Register and search that registry from the CLI:
Search results are installable sources:
local pdf Work with PDF files... gh:owner/repo/skills/pdf
In proxy mode, results look like this:
company deploy-container Deploy containers to Kubernetes. company:deploy-container
Users can install without knowing the backing Git repo:
For skills scattered across one or more Git repositories, prefer dynamic source entries. The registry fetches the backing source, scans for SKILL.md, and derives each skill's name and description from the skill itself:
[[]]
= "tea:platform/agent-skills"
= ["deploy", "kubernetes"]
At startup, that materializes skills such as tea:platform/agent-skills/deploy-container from platform/agent-skills/deploy-container/SKILL.md. This avoids duplicating fragile metadata in knack.index.toml.
Dynamic sources must refresh successfully on startup before the registry serves traffic. After startup, the registry refreshes dynamic sources every 300 seconds by default and keeps serving the last good index if a later refresh fails. Disable background refresh with --refresh-interval-seconds 0 or tune it with another interval.
Pass --cache-dir <path> to keep the cloned backing repos across restarts. When set, refreshes do git fetch + git reset --hard against the existing clone instead of re-cloning, and archive requests read straight from the cache directory — no per-request git operations. Point it at a persistent volume (Fly.io Volume, EFS mount, GCP Cloud Run volume, anything that survives container restart) for the full benefit. When omitted, the registry uses a per-process tempdir that's rebuilt on every cold start — that's the right shape for ephemeral-filesystem platforms like Cloudflare Containers.
Static deployment (Cloudflare R2 + Worker)
For a public, read-mostly registry, the cheapest shape is to skip running a live registry process entirely. Run knack-registry build-static from a CI cron job, upload the output to an object store (R2, S3, GCS, anything), and serve it via a small edge worker:
This produces info.json, index.json, sha-map.json, and one skills/<name>.skill.tar.gz per indexed skill. The output is everything a knack CLI client needs; an edge function in front of the bucket maps the four CLI endpoints (/info, /index, /search, /skills/<name>/archive) onto these files. See examples/cloudflare-worker/ for a working Worker + R2 setup with a daily GitHub Actions cron, free-tier-friendly at the scale of the public registry (~200 skills, single-digit thousands of requests/day).
Tradeoff: refresh granularity drops from --refresh-interval-seconds (default 300s) to whatever your cron interval is (daily for the public registry). Static loses dynamic queries, auth, and the ability to index private sources — keep knack-registry serve for internal team registries where any of those matter.
Static entries are still supported for hand-curated overrides:
[[]]
= "deploy-container"
= "Deploy containers into the internal Kubernetes cluster."
= "tea:payments/api/.agents/skills/deploy-container"
= ["deploy", "kubernetes"]
Start the registry with a source alias so it can fetch those backing sources server-side:
Users still only need the HTTP registry:
gh: sources resolve directly against github.com and need no --source-alias. The curated source list used by the project's public registry instance lives at registries/public.toml — mirror it for your own registry, or open a PR with a new [[source]] entry to propose adding a source to the public one. The file's header documents the curation criteria and PR process.
Publish a local skill into a git-backed team skills repository:
Publishing currently supports git-host registries. It clones the target repository, copies the skill into skills/<skill-name>, regenerates knack.index.toml, commits the change, and pushes it. Use --no-push to leave the commit local in the temporary checkout for debugging.
After the registry server is serving the updated knack.index.toml, teammates can discover and install the skill:
List installed skills:
v0 Scope
Implemented:
- Skill scaffolding.
- Agent Skills frontmatter validation.
- Deterministic
.skill.tar.gzpackaging. - Local installation from directories and package archives.
- GitHub installation with
gh:owner/repo[@ref]/path/to/skill. - Generic Git installation with
git+<url>[@ref]//path/to/skill. - Git-host registry aliases with
alias:owner/repo[@ref]/path/to/skill. - Searchable HTTP registries served by
knack-registry. - Proxied HTTP registry installs with
registry:skill-name. - Registry-side proxying from indexed Git backing sources.
- Registry index generation from local skill directories.
- Publishing skills to git-backed team repositories.
- Project manifests with
knack.toml. - Lockfiles with
knack.lock. - Project and global scoped config/install paths.
- System scoped config at
/etc/knack/knack.toml. - Layered registry alias inheritance from system to global to project.
add,sync, andupdateworkflows for reproducible project installs.- Content-addressed lockfile entries (commit SHA captured for
gh:,git+, andhttp+knack:sources). knack sync --checkfor CI: assert install + lockfile consistency.knack update --dry-runfor previewing upstream changes.- Versioned lockfile schema; future-incompatible lockfiles refuse to load rather than silently round-tripping.
- Listing installed skills.
Not implemented yet:
- Static registries.
- Signing, provenance, and policy checks.