Configuring kasetto.yaml
Declare skills, commands, MCP servers, and instructions in one kasetto.yaml - sources, agents, scope, and composing configs with extends.
When --config is omitted, Kasetto looks for config in this order:
$KASETTO_CONFIGenv var./kasetto.yamlsource:key in$XDG_CONFIG_HOME/kasetto/config.yaml$XDG_CONFIG_HOME/kasetto/kasetto.yaml(or~/.config/kasetto/kasetto.yaml)
Point it at a specific file or URL with --config, or run kst init for local ./kasetto.yaml (kst init --global writes the global config file).
To persist a remote URL as your default, add a source: key to ~/.config/kasetto/config.yaml (see Persisting a Default Remote Config).
Two similarly named files live in ~/.config/kasetto/, and they do different jobs:
kasetto.yamlis a config. It holds your agents, skills, MCPs, commands, and instructions.kst init --globalwrites this one.config.yamlis a preferences file. Its only job is asource:key naming the config to use, and it never contains skills itself.
Because ./kasetto.yaml is checked first, a project that has its own config always wins over a persisted source:.
Example
Reference
Top-Level Fields
| Key | Required | Description |
|---|---|---|
agent | no | One or more supported agent presets - string or list |
destination | no | Explicit install path - overrides agent if both are set |
scope | no | "global" (default) or "project" - where to install |
skills | yes | List of skill sources |
mcps | no | List of MCP server sources |
commands | no | List of slash-command sources |
instructions | no | List of instruction sources (CLAUDE.md / .cursor/rules / AGENTS.md ...) |
secrets | no | Secret-injection policy for ${kst_...} placeholders (see Secret Injection) |
extends | no | Path or URL of a parent config to inherit from (string or list) |
Skill Source Fields
| Key | Required | Description |
|---|---|---|
source | yes | Git host URL or local path (GitHub, GitLab, Bitbucket, Codeberg/Gitea) |
branch | no | Branch for remote sources (default: main, falls back to master) |
ref | no | Git tag, commit SHA, or ref - takes priority over branch |
sub-dir | no | Relative subdirectory within the source used as the discovery root (sub_dir alias supported) |
skills | yes | "*" for all, or a list of names / { name, path } objects |
Skill Entry Fields
Each entry in the skills list can be a string (the skill name) or an object:
| Key | Required | Description |
|---|---|---|
name | yes | Name of the skill directory to install |
path | no | Parent directory containing <name>/SKILL.md, resolved relative to the source root (or sub-dir if set). Absolute paths are honored as-is. |
| Form | Resolves to |
|---|---|
- code-reviewer | discovered (root or skills/) |
- { name: x } | discovered (root or skills/) |
- { name: x, path: dir } | dir/x/SKILL.md |
- { name: x, path: nested/dir } | nested/dir/x/SKILL.md |
MCP Source Fields
| Key | Required | Description |
|---|---|---|
source | yes | Git host URL or local path containing MCP server config |
branch | no | Branch for remote sources (default: main, falls back to master) |
ref | no | Git tag, commit SHA, or ref - takes priority over branch |
mcps | yes | "*" to discover all, or a list of names / { name, path } objects |
When mcps: "*", Kasetto auto-discovers MCP config files in this order:
.mcp.jsonat the source rootmcp.jsonat the source root- Any
.jsonfile inside themcps/subdirectory
MCP Entry Fields
Each entry in the mcps list can be a plain string (name) or an object, the same as skill entries:
| Form | Resolves to |
|---|---|
- github | mcps/github.json |
- github.json | mcps/github.json |
- { name: x } | mcps/x.json |
- { name: x, path: dir } | dir/x.json |
- { name: x, path: nested/dir } | nested/dir/x.json |
Paths are resolved relative to the source root; absolute paths are honored as-is. .json is appended automatically when the name has no extension.
MCP config files must contain a mcpServers object with server definitions. Servers are merged
into each agent's native settings file (e.g., .claude.json for Claude Code, .cursor/mcp.json
for Cursor). See how sync works for merge behavior details.
A pack can reference secrets with ${kst_<name>} placeholders (tokens, passwords, API keys) instead
of committing them. They're resolved at sync time from the environment or a credentials file. See
Secret Injection.
Instruction Source Fields
Instructions wire each agent's instruction file (CLAUDE.md, .cursor/rules/*.mdc, AGENTS.md,
.github/copilot-instructions.md, and so on) from a single source. An instruction is Markdown with
optional YAML frontmatter, discovered in the source's instructions/ directory.
| Key | Required | Description |
|---|---|---|
source | yes | Git host URL or local path containing a instructions/ directory |
branch | no | Branch for remote sources (default: main, falls back to master) |
ref | no | Git tag, commit SHA, or ref - takes priority over branch |
sub-dir | no | Relative subdirectory within the source used as the discovery root (sub_dir alias supported) |
instructions | yes | "*" to discover all, or a list of names / { name, path } objects |
When instructions: "*", Kasetto walks instructions/**/*.{md,mdc} (nested directories namespace with :,
e.g. house:security).
Instruction Entry Fields
Each entry in the instructions list can be a plain string (name) or an object, the same as command entries:
| Form | Resolves to |
|---|---|
- style | instructions/style.{md,mdc} |
- house:security | instructions/house/security.{md,mdc} |
- { name: x, path: dir } | dir/x.{md,mdc} |
How Instructions Land per Agent
Each instruction is transformed into the target agent's native format. Two destination shapes exist:
- Aggregate file: many instructions merge into one shared file (
CLAUDE.md,AGENTS.md,GEMINI.md,.github/copilot-instructions.md, ...). Each instruction is wrapped in a managed<!-- kasetto:instruction:ID ... -->comment block, so your own hand-written content and other instructions in the same file are preserved.remove/cleanstrip only kasetto's blocks and never delete the file. - Per-instruction directory: one file per instruction. Cursor (
.cursor/rules/<name>.mdc) gets reconstructed MDC frontmatter (description,globs,alwaysApply); other directory agents (.windsurf/rules,.clinerules,.continue/rules, ...) get the Markdown body only.
globs/alwaysApply are only meaningful for Cursor; they are dropped for agents that don't scope
instructions. See supported agents for the per-agent destinations.
Extending Another Config
Use extends to inherit from a parent config. Local relative paths resolve against the extending file's directory; HTTPS URLs are fetched with the same auth env vars as --config.
extends accepts a single string or a list. With a list, parents merge left-to-right; the child overrides them all.
Merge Rules
| Field | Rule |
|---|---|
destination | Replace - child wins |
scope | Replace - child wins |
agent | Replace - child wins |
skills | Merge by (source, ref-or-branch, sub-dir) identity. Same identity replaces; new entries append. |
mcps | Same as skills (sub-dir is always empty for MCP sources, so identity is (source, ref-or-branch)). |
commands | Same as skills. |
instructions | Same as skills. |
Identity-based merging lets a child narrow a parent's skills: "*" to a specific list, or pin a different ref, while still adding new sources.
Cycles are detected and rejected. Maximum chain depth is 8.
Remote Configs
Kasetto can fetch configs from any HTTPS URL:
Great for sharing a single config across a team without checking it into every repository.
Real-World Example
pivoshenko/pivoshenko.ai is a public config that pulls skills from several community packs for Claude Code and OpenCode:
Kasetto recognises browser URLs from GitHub, GitLab, and Gitea / Codeberg / Forgejo, and auto-rewrites them to the matching raw-content endpoint. You can paste any of these directly:
https://github.com/owner/repo/blob/main/kasetto.yamlhttps://gitlab.com/group/repo/-/blob/main/kasetto.yamlhttps://codeberg.org/owner/repo/src/branch/main/kasetto.yaml
Kasetto prints a short note: rewrote browser URL to raw content: ... line so you can see what was fetched. Authentication is resolved against the rewritten host, so the same tokens that work for raw URLs apply here too.
If the URL points to a private repository, Kasetto uses the same token-based authentication as skill sources. See authentication for the full list of supported environment variables.
Persisting a Default Remote Config
To stop passing --config on every run, put the URL in the source: key of ~/.config/kasetto/config.yaml:
Bare kst sync now resolves that URL on its own, on every machine where you drop the same two-line file. Browser URLs are rewritten here exactly as they are for --config, and private repositories authenticate the same way.
Three things still take precedence, in this order: $KASETTO_CONFIG, an explicit --config, and a ./kasetto.yaml in the current directory. That last one matters most in practice. Inside a repository that ships its own config, the persisted source: is ignored, so a global default and a project config coexist without either fighting the other.
The value does not have to be a URL. An absolute local path works too, which is useful for keeping a config outside the repositories that consume it.
Multiple Agents
The agent field accepts a single value or a list. With a list, Kasetto installs skills, commands, and instructions to every agent's directory and merges MCPs into every agent's settings file:
Handy when you juggle multiple agents and want them all to share the same skill set.
Agent vs Destination
If you set both, destination wins. Use agent for convenience with supported presets, or destination when you need full control over the install path.
Use destination when targeting an agent that isn't in the supported list.
Scope: Global vs Project
By default, skills are installed globally into the agent's home-directory path. Add scope: project to your config, or pass --project on the command line, to install into the current project directory instead.
The --project / --global flags always override whatever scope is set in the config file.
Environment Variables
These environment variables affect Kasetto's behavior:
| Variable | Effect |
|---|---|
NO_COLOR | Disables colored output. Set to any value. |
CLICOLOR_FORCE | Forces color even when stdout is not a TTY. Set automatically by --color always. |
KASETTO_CONFIG | Overrides config discovery to use the exact path or URL (see Configuration). |
KASETTO_NO_CACHE | Disables the on-disk source cache, forcing a fresh download + extract on every fetch. Set to any value. |