A declarative AI agent environment manager, written in Rust.
**About the name**
Name comes from the Japanese word **カセット** (*kasetto*) - cassette. Think of Skills, MCPs, commands, and instructions as cassettes you plug in, swap out, and share across machines.
## Why Kasetto
There are good tools in this space already - [Vercel Skills](https://github.com/vercel-labs/skills) installs skills from a curated catalog, and [Claude Plugins](https://claude.com/plugins) offer runtime integrations. Both work well for one-off installs, but neither gives you a declarative, version-controlled config.
Kasetto is a **community-first** project that solves a different problem: **declarative, reproducible AI environment management across projects, machines, and agents.**
- **Declarative** — one YAML file, your whole setup: skills, commands, MCPs, instructions, and agents. Apply globally or scope to a project; configs compose with `extends`, so org, team, and project stay in sync
- **Enterprise & private repositories** — pull from anywhere: GitHub, GitLab, Bitbucket, Codeberg, Gitea, and self-hosted instances, public or private. Onboard a new engineer with one command; everyone gets the same environment, zero drift
- **Multi-agent** — write once, ship everywhere. Claude Code, Cursor, Codex, Windsurf, Copilot, Gemini CLI, and [many more](#supported-agents) — one sync keeps them all current
- **Skills, Commands, MCPs & Instructions** — four asset kinds, one source: skills, commands, MCPs, and instructions (`CLAUDE.md`, `.cursor/rules`, `AGENTS.md`, ...). Everything is transformed into each agent's native format, and auto-merged. Distribute instructions, tools, and prompts as easily as sharing a repository link
- **Secrets management** — specify `${kst_...}` placeholders into your configs instead of real tokens. At sync time Kasetto pulls the value from your environment, a credentials file, or a secret manager you already use (1Password, Vault, AWS, GCP, Azure, KeePassXC, pass, macOS Keychain) and injects it into the agent's settings only
- **Speed** — instant by design. Built in Rust, it hashes content and diffs a lock file so only what changed gets touched — full syncs finish in seconds
- **Universal** — one static binary for macOS, Linux, and Windows. Install as `kasetto`, run as `kst`. CI-friendly with `--json` output and real exit codes
> Inspired by [cargo](https://github.com/rust-lang/cargo) and [uv](https://github.com/astral-sh/uv) — the same lock-first, declarative, CLI-only ergonomics, applied to AI agent skills.
## Install
### Standalone Installer
**macOS and Linux:**
```bash
curl -fsSL kasetto.dev/install | sh
```
**Windows:**
```powershell
powershell -ExecutionPolicy Bypass -c "irm kasetto.dev/install.ps1 | iex"
```
### Homebrew
```bash
brew tap pivoshenko/tap
brew trust pivoshenko/tap
brew install kasetto
```
### Scoop (Windows)
```bash
scoop bucket add kasetto https://github.com/pivoshenko/scoop-bucket
scoop install kasetto
```
### Cargo
```bash
cargo install kasetto
```
## Getting Started
**1. Scaffold a config:**
```bash
kst init # creates ./kasetto.yaml in the current directory
kst init --global # or a global one at ~/.config/kasetto/kasetto.yaml
```
Edit the generated `kasetto.yaml` — pick an `agent`, add a `skills:` source, and you're ready to sync. Or let Kasetto edit the config for you:
```bash
kst add https://github.com/anthropics/skills # add every skill in the pack
kst add https://github.com/anthropics/skills@v1.2.0 # `@` shorthand (cargo/uv-style)
kst add https://github.com/anthropics/skills --skill pptx # or just named ones
kst add https://github.com/example/repo --skill find --mcp github --command review
```
`kst add` appends the source (keeping your comments) and syncs it in one step; `kst remove ` reverses it. See [cargo/uv-style editing](#commands) below.
**2. Sync skills into your agents:**
```bash
# uses ./kasetto.yaml in the current directory
kst sync
# or point at a shared team config over HTTPS
kst sync --config https://example.com/team-skills.yaml
```
Want bare `kst sync` to always pull from a remote URL? Persist it in `~/.config/kasetto/config.yaml`:
```yaml
source: https://github.com/pivoshenko/pivoshenko.ai/blob/main/kasetto.yaml
```
After that, `kst sync` resolves the URL automatically — no `--config` flag needed.
That's it. Kasetto pulls the skills, installs them into the right agent directory, and records exactly what it installed in `kasetto.lock`. Commit `kasetto.yaml` and `kasetto.lock` together (like `Cargo.lock` or `package-lock.json`) and every teammate gets identical versions. A plain `kst sync` honors the lock without re-resolving moving refs; `kst sync --update` rolls versions forward; `kst sync --locked` enforces the lock in CI.
See [pivoshenko/pivoshenko.ai](https://github.com/pivoshenko/pivoshenko.ai) for a community example pulling skills from multiple packs for Claude Code and OpenCode.
**3. See what's installed:**
```bash
kst list # table of installed skills, MCPs, commands, instructions
kst list --type skills # filter to one asset kind
kst doctor # version, paths, last sync status
```
## Commands
One-line synopsis below. Full flags and examples in the [commands reference](https://kasetto.dev/docs/commands).
- **`kst init`** — generate a starter `kasetto.yaml` (local or `--global`)
- **`kst add `** — append a source to the config (comments preserved) and sync it in. Kind-tagged repeatable flags `--skill`/`--mcp`/`--command`/`--instruction` name entries (a lone `*` is a wildcard; no flags ⇒ `skills: "*"`), so one `add` can touch several lists. Accepts a cargo/uv-style `@` shorthand and deep `blob`/`tree` browse URLs — the latter decomposed into source + `ref`/`branch` + `sub-dir` (+ skill name for a `SKILL.md` link); `--ref`/`--branch`/`--sub-dir` override. `--dry-run` previews the edit; `--no-sync` edits without installing; `--locked` keeps the follow-up sync offline; `--json` for scripting
- **`kst remove `** (alias `rm`) — drop entries from the config and prune the now-unconfigured assets. Mirrors `add`: `--skill`/`--mcp`/`--command`/`--instruction` (repeatable) subtract named entries (last one drops the whole entry; a lone `*` drops it outright); no kind flags removes the source from every list. `--ref`/`--branch` (or the `@` shorthand) disambiguate a repeated URL. `--dry-run` previews; `--no-sync` edits only; `--locked` and `--json` mirror `add`
- **`kst lock`** — re-resolve every source and pin it into `kasetto.lock` without installing; skills become offline-ready for `sync --locked`, MCP/command/instruction revision pins refresh. `--check` (alias `--locked`/`--frozen`) verifies the lock matches the config without writing (CI-friendly); `-P`/`--upgrade-package ...` re-resolves only the named skills' sources
- **`kst sync`** — read config, install skills + MCPs + commands + instructions into agent dirs honoring `kasetto.lock`; `--update` rolls pins forward, `--locked`/`--frozen` enforce the lock without fetching
- **`kst list`** — print a uv-style table of installed skills, MCPs, commands, and instructions from the lock file; `--type skills|mcps|commands|instructions` filters; `--json` for scripting
- **`kst doctor`** — local diagnostics: version, paths, last sync status, broken skills
- **`kst clean`** — remove tracked skills, commands, MCP configs, and instructions for the given scope
- **`kst self update`** — fetch latest release, verify SHA256, replace binary in place
- **`kst self uninstall`** — remove installed assets, data, and the binary
- **`kst completions `** — emit shell completion script (`bash`/`zsh`/`fish`/`powershell`)
Most commands accept `--json`, `--color `, `-q`/`--quiet` (repeat for stricter silence), and `--project | --global`. `--plain` is still accepted as a deprecated alias for `--color never`.
## Configuration
When `--config` is omitted, Kasetto looks for config in this order:
1. `$KASETTO_CONFIG` env var
2. `./kasetto.yaml`
3. `source:` key in `$XDG_CONFIG_HOME/kasetto/config.yaml`
4. `$XDG_CONFIG_HOME/kasetto/kasetto.yaml` (or `~/.config/kasetto/kasetto.yaml`)
Run `kst init` to scaffold a local config, or `kst init --global` for the global one.
```yaml
# Option A: preset destination by agent (see README for supported agent values)
agent:
- codex
- claude-code
# Option B: manual destination (takes precedence if both are set)
# destination: ./.agents/skills
skills:
# "*" syncs every skill in the source — each is a directory with a SKILL.md,
# discovered in the source root or its skills/ subdirectory
- source: https://github.com/vercel-labs/next-skills
# ref: v1.0.0 # pin to a tag or commit; omit to track the default branch
skills: "*"
# or list skills by name
- source: https://github.com/anthropics/skills
skills:
- doc-coauthoring
- frontend-design
- pptx
# sub-dir: resolve the named skills under this path, e.g. skills/productivity/grill-me/
- source: https://github.com/mattpocock/skills
sub-dir: skills/productivity
skills:
- grill-me
- caveman
# path: a skill in a non-standard location → //, here skills/engineering/improve-codebase-architecture/
- source: https://github.com/mattpocock/skills
skills:
- name: improve-codebase-architecture
path: skills/engineering
commands:
# names resolve to commands/.md in the source (nested dirs namespace, e.g. git:commit)
- source: https://github.com/gsd-build/get-shit-done
commands:
- gsd:explore
- gsd:fast
instructions:
# instructions wire CLAUDE.md / .cursor/rules / AGENTS.md etc. from instructions/.{md,mdc}
# "*" syncs every instruction; aggregate files (CLAUDE.md, AGENTS.md) get managed blocks
- source: https://github.com/pivoshenko/pivoshenko.ai
instructions:
- docs-autoupdate
- multi-agent-dispatch
mcps:
# names resolve to mcps/.json in the source
- source: https://github.com/pivoshenko/pivoshenko.ai
branch: main # track a specific branch (use ref: to pin a tag or commit)
mcps:
- github
- vercel
- kaggle
```
Full key reference, merge instructions, and `extends:` inheritance live in the [configuration docs](https://kasetto.dev/docs/configuration).
### Secrets
MCP packs often need a token or password. Reference one with a `${kst_}` placeholder instead of committing it — Kasetto resolves it at sync time and writes the value into the agent's settings file only:
```json
{
"mcpServers": {
"vercel": {
"url": "https://mcp.vercel.com",
"type": "http",
"headers": { "Authorization": "Bearer ${kst_vercel_token}" }
}
}
}
```
Values come from environment variables first (the name as written, then uppercased — `${kst_vercel_token}` reads `KST_VERCEL_TOKEN`), then `~/.config/kasetto/credentials.yaml` (`__` in a name descends nested keys, e.g. `${kst_vercel__token}` → `vercel.token`).
To pin exactly one source, use the tagged form `${kst::}`:
- **`env`** — Specific environment variable
- **`crd`** — Credentials file
- **`op`** — 1Password
- **`vault`** — HashiCorp Vault
- **`kp`** — KeePassXC
- **`aws`** — AWS Secrets Manager
- **`gcp`** — Google Secret Manager
- **`az`** — Azure Key Vault
- **`pass`** — Pass / GoPass
- **`keychain`** — MacOS Keychain
Each external manager inherits your existing CLI session, so kasetto stores no tokens of its own. A missing secret fails the sync (exit non-zero) unless you pass `--allow-missing-secrets`. The resolved value never lands in `kasetto.lock`, so the lock stays commit-safe. Rotated a secret? A plain `sync` won't touch the live entry — run `kst sync --update` to push it. Full details in the [secret-injection docs](https://kasetto.dev/docs/secrets).
## Supported Agents
Set the `agent` field and Kasetto figures out where to put things.
Full list of supported agents
| Agent | Config value | Install path |
| -------------- | ---------------- | ------------------------------- |
| Amp | `amp` | `~/.config/agents/skills/` |
| Antigravity | `antigravity` | `~/.gemini/antigravity/skills/` |
| Augment | `augment` | `~/.augment/skills/` |
| Claude Code | `claude-code` | `~/.claude/skills/` |
| Cline | `cline` | `~/.agents/skills/` |
| Codex | `codex` | `~/.codex/skills/` |
| Continue | `continue` | `~/.continue/skills/` |
| Cursor | `cursor` | `~/.cursor/skills/` |
| Gemini CLI | `gemini-cli` | `~/.gemini/skills/` |
| GitHub Copilot | `github-copilot` | `~/.copilot/skills/` |
| Goose | `goose` | `~/.config/goose/skills/` |
| Junie | `junie` | `~/.junie/skills/` |
| Kiro CLI | `kiro-cli` | `~/.kiro/skills/` |
| OpenClaw | `openclaw` | `~/.openclaw/skills/` |
| OpenCode | `opencode` | `~/.config/opencode/skills/` |
| OpenHands | `openhands` | `~/.openhands/skills/` |
| Replit | `replit` | `~/.config/agents/skills/` |
| Roo Code | `roo` | `~/.roo/skills/` |
| Trae | `trae` | `~/.trae/skills/` |
| Warp | `warp` | `~/.agents/skills/` |
| Windsurf | `windsurf` | `~/.codeium/windsurf/skills/` |
| ZCode | `zcode` | `~/.zcode/skills/` |
Don't see your agent? Use the `destination` field to point at any path.
## Private Repositories & Enterprise
Private GitHub, GitLab, Bitbucket, Codeberg, Gitea, and self-hosted instances work via env-var tokens (`GITHUB_TOKEN`, `GITLAB_TOKEN`, `BITBUCKET_TOKEN`, `GITEA_TOKEN`, etc.) — no login command, no credentials file. The same tokens apply to remote `--config` URLs.
Full host table and auth resolution instructions in the [authentication docs](https://kasetto.dev/docs/authentication).
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.
## License
Licensed under either [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your option.