
Kasetto - a declarative AI agent environment manager
I've been building Kasetto: a single Rust binary that takes one YAML config and syncs Skills and MCP servers into every AI agent on your machine or your teammates' machines. Supported: Claude Code, Cursor, Codex, Windsurf, Copilot, Gemini CLI, and more.
Sources can be GitHub, GitLab, Bitbucket, Codeberg, Gitea, self-hosted instances, or local directories. MCP configs are auto-merged into the right format per agent so you don't have to hand-edit four different settings files every time you add a server.
The core idea: the YAML is the source of truth. Version it, share it, bootstrap a teammate's whole agent setup in one command. No registry, no boilerplate — any directory with a SKILL.md is a skill.
>Inspired by uv - what uv did for Python packages, Kasetto aims to do for AI skills.
What it gives you:
- Declarative - one YAML describes your entire setup. Version-controlled, readable, auditable.
- Multi-agent - Claude Code, Cursor, Codex, Windsurf, Copilot, Gemini CLI, and more. One config, every agent updated.
- Enterprise & private repos — GitHub, GitLab, Bitbucket, Codeberg, Gitea, and self-hosted instances out of the box.
- Skills & MCP - any directory with a
SKILL.mdis a skill. MCP server configs are auto-merged into every supported format (Cursor JSON, Claude JSON, Copilot VS Code, Codex TOML). - Fast - written in Rust. SHA-256 content hashing and lock file diffing mean only what changed gets touched.
- Universal - single static binary for macOS, Linux, and Windows. Install as
kasetto, run askst. CI-friendly with--jsonoutput and proper exit codes.
A kasetto.yaml looks like this - multiple agents, multiple sources, pinned refs/branches, per-skill paths, and an optional extends: for inheriting a shared team base:
# inherit a shared base config — overrides merge on top
# extends: github.com/acme/kasetto-base/raw/main/kasetto.yaml
agent:
- claude-code
- cursor
- opencode
scope: project # or global
# destination: ./.agents/skills # optional, override install path
skills:
- source: github.com/acme/frontend-pack
skills: "*"
- source: gitlab.com/team/internal-tools
branch: master
skills:
- react-patterns
- go-standards
- source: codeberg.org/oss/shared
ref: v2.1.0
skills:
- name: custom-lint
path: rules/custom-lint
- name: format-helpers
path: rules/format
mcps:
- source: github.com/acme/mcp-pack
mcps: "*"
- source: github.com/acme/monorepo
ref: v1.4.0
mcps:
- github
- linear
Running it:
# 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 once in ~/.config/kasetto/config.yaml:
source: https://github.com/pivoshenko/pivoshenko.ai/blob/main/kasetto.yaml
After that, kst sync resolves the URL automatically — no --config flag needed. Then to see what landed:
kst list # interactive browser with vim-style navigation
kst doctor # version, paths, last sync status
For a real, runnable example: pivoshenko/pivoshenko.ai is my public config — it pulls skills from Anthropic, Vercel Labs, Apollo, and a few independent authors into Claude Code and OpenCode. Fork it, point your own config at it with extends:, or use it as the source: above.
Install:
curl -fsSL kasetto.dev/install | sh
# or: brew install pivoshenko/tap/kasetto
# or: cargo install kasetto
Docs: https://kasetto.dev
Repository: https://github.com/pivoshenko/kasetto
Happy to hear feedback, especially from anyone juggling skills across multiple agents or sharing setups across a team.