KASETTO

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:

  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)

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.yaml is a config. It holds your agents, skills, MCPs, commands, and instructions. kst init --global writes this one.
  • config.yaml is a preferences file. Its only job is a source: 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

# 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 → <path>/<name>/, 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/<name>.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/<name>.{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/<name>.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

Reference

Top-Level Fields

KeyRequiredDescription
agentnoOne or more supported agent presets - string or list
destinationnoExplicit install path - overrides agent if both are set
scopeno"global" (default) or "project" - where to install
skillsyesList of skill sources
mcpsnoList of MCP server sources
commandsnoList of slash-command sources
instructionsnoList of instruction sources (CLAUDE.md / .cursor/rules / AGENTS.md ...)
secretsnoSecret-injection policy for ${kst_...} placeholders (see Secret Injection)
extendsnoPath or URL of a parent config to inherit from (string or list)

Skill Source Fields

KeyRequiredDescription
sourceyesGit host URL or local path (GitHub, GitLab, Bitbucket, Codeberg/Gitea)
branchnoBranch for remote sources (default: main, falls back to master)
refnoGit tag, commit SHA, or ref - takes priority over branch
sub-dirnoRelative subdirectory within the source used as the discovery root (sub_dir alias supported)
skillsyes"*" 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:

KeyRequiredDescription
nameyesName of the skill directory to install
pathnoParent directory containing <name>/SKILL.md, resolved relative to the source root (or sub-dir if set). Absolute paths are honored as-is.
FormResolves to
- code-reviewerdiscovered (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

KeyRequiredDescription
sourceyesGit host URL or local path containing MCP server config
branchnoBranch for remote sources (default: main, falls back to master)
refnoGit tag, commit SHA, or ref - takes priority over branch
mcpsyes"*" to discover all, or a list of names / { name, path } objects

When mcps: "*", Kasetto auto-discovers MCP config files in this order:

  1. .mcp.json at the source root
  2. mcp.json at the source root
  3. Any .json file inside the mcps/ subdirectory

MCP Entry Fields

Each entry in the mcps list can be a plain string (name) or an object, the same as skill entries:

FormResolves to
- githubmcps/github.json
- github.jsonmcps/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.

KeyRequiredDescription
sourceyesGit host URL or local path containing a instructions/ directory
branchnoBranch for remote sources (default: main, falls back to master)
refnoGit tag, commit SHA, or ref - takes priority over branch
sub-dirnoRelative subdirectory within the source used as the discovery root (sub_dir alias supported)
instructionsyes"*" 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:

FormResolves to
- styleinstructions/style.{md,mdc}
- house:securityinstructions/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/clean strip 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.

# child.yaml
extends: ./team-base.yaml
scope: project
skills:
  - source: https://github.com/example/extra-pack
    skills: "*"

extends accepts a single string or a list. With a list, parents merge left-to-right; the child overrides them all.

extends:
  - ./org-base.yaml
  - https://example.com/team-overlay.yaml

Merge Rules

FieldRule
destinationReplace - child wins
scopeReplace - child wins
agentReplace - child wins
skillsMerge by (source, ref-or-branch, sub-dir) identity. Same identity replaces; new entries append.
mcpsSame as skills (sub-dir is always empty for MCP sources, so identity is (source, ref-or-branch)).
commandsSame as skills.
instructionsSame 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:

kst sync --config https://example.com/team-skills.yaml

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:

kst sync --config https://github.com/pivoshenko/pivoshenko.ai/blob/main/kasetto.yaml

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.yaml
  • https://gitlab.com/group/repo/-/blob/main/kasetto.yaml
  • https://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:

# ~/.config/kasetto/config.yaml
source: https://github.com/pivoshenko/pivoshenko.ai/blob/main/kasetto.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:

agent:
  - claude-code
  - cursor
  - codex
 
skills:
  - source: https://github.com/org/skill-pack
    skills: "*"

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:

VariableEffect
NO_COLORDisables colored output. Set to any value.
CLICOLOR_FORCEForces color even when stdout is not a TTY. Set automatically by --color always.
KASETTO_CONFIGOverrides config discovery to use the exact path or URL (see Configuration).
KASETTO_NO_CACHEDisables the on-disk source cache, forcing a fresh download + extract on every fetch. Set to any value.