File formats
This page lists the files SkillCatalog reads, their fields, and the rules and limits it enforces. skc validate --path <folder> checks a catalog folder against these rules, and skc validate --manifest checks the team manifest in the current folder. JSON schemas describe the same shapes for editors and other tools. See skillcatalog.dev/schemas/index.json.
Skill folders
Each skill is a folder, skills/<slug>/, named after the skill's slug. It holds a SKILL.md and any companion files: every other file in the folder, such as a template or a script.
skills/
review-checklist/
SKILL.md
templates/
review-comment.mdSKILL.md starts with YAML front matter between two --- lines, followed by the Markdown body. Its schema is skill-frontmatter.schema.json.
---
name: review-checklist
description: Checklist for reviewing a pull request at Acme. Use when reviewing code changes.
metadata:
skillcatalog/category: quality
skillcatalog/owner: platform
skillcatalog/tags: '["review"]'
---
# Review checklist
1. Read the pull request description.
2. Check that tests cover the change.
3. Use the comment template in @skill-dir/templates/review-comment.md.| Field | Required | Rules |
|---|---|---|
name | Yes | The folder name, at most 64 bytes. |
description | Yes | What the skill does and when to use it. At most 500 characters and 1,024 bytes. |
license | No | Free text. |
compatibility | No | Free text, at most 500 bytes. |
allowed-tools | No | Free text, delivered as written. |
metadata | No | A map of text values. Keys that start with skillcatalog/ are reserved and listed below. Other keys are free, and values such as true or 3 are kept as text. |
SkillCatalog ignores other top-level fields. It does not deliver them, and the desktop app removes them when it saves the skill.
| Metadata key | Value |
|---|---|
skillcatalog/display_name | The name SkillCatalog shows. Without it, SkillCatalog uses the first level-one heading of the body, then the slug in title case. |
skillcatalog/category | The slug of a category in catalog.yaml. |
skillcatalog/owner | The slug of an owner in catalog.yaml. |
skillcatalog/author | Free text, at most 500 characters. |
skillcatalog/tags | A JSON array of tag slugs written as a YAML string, such as '["review", "security"]'. Each tag must be defined in catalog.yaml. |
skillcatalog/created_at, skillcatalog/updated_at | RFC 3339 timestamps, such as 2026-09-23T09:00:00Z. updated_at cannot be earlier than created_at. Without them, SkillCatalog uses the file's times. |
skillcatalog/validation | Content checks to turn off (false) or on (true) for this skill: a JSON object written as a YAML string, such as '{"validation.body.too-long": false}'. It wins over the catalog's validation map. See Content checks. |
Any other key that starts with skillcatalog/ is an error. The body can be empty and is at most 1 MiB (1,048,576 bytes).
Each delivered SKILL.md gets new front matter with name, description, license, compatibility, allowed-tools, and metadata. In that copy only, SkillCatalog fills in skillcatalog/display_name, skillcatalog/created_at, and skillcatalog/updated_at when the source lacks them. Delivery also replaces two tokens in the body of SKILL.md, not in companion files:
| Token | Delivered as |
|---|---|
@skill:<slug> | The path of that skill's SKILL.md for the same tool and profile. In the Home profile's Claude Code copy, that is ~/.claude/skills/<slug>/SKILL.md; in a project profile, an absolute path. |
@skill-dir | The absolute path of this skill's delivered folder, such as /Users/dana/.claude/skills/review-checklist. |
SkillCatalog recognizes @skill:<slug> at the start of the body, after a space, tab, or line break, and right after one of (, [, {, ", ', <, code blocks included. After any other character, such as a backtick or the * of bold text, the text stays as written and is not checked. skc validate reports a reference to a skill that the catalog does not have, and a skill that refers to itself.
SkillCatalog replaces @skill-dir wherever no letter, digit, -, or _ touches it, even inside backticks, so @skill-directory and user@skill-dir stay as written. See Refer to other skills.
To write either token as text, put a backslash before it: \@skill-dir and \@skill:<slug>. Delivery removes the backslash and replaces nothing, so the delivered copy shows @skill-dir and @skill:<slug>, and skc validate does not check an escaped reference. A backslash before any other text stays as written.
Delivery copies companion files byte for byte and keeps their executable bit. It never copies a symbolic link: when a skill folder holds one anywhere, delivery withholds that skill, leaves its delivered files as they are, delivers the other skills, and reports the skill as failed. skc validate reports the link as an error. See A skill folder holds a symbolic link.
A special file, such as a named pipe, or a file over 10 MiB in a skill folder stops the whole delivery run until you remove it, and skc validate reports neither. Delivery skips these files:
| Skipped | Where |
|---|---|
SCORE.json, the skill's score history | At the top of the skill folder |
.DS_Store, and file names that end in .pyc, .pyo, .pyd, .swp, or ~ | Anywhere in the skill folder |
Everything inside a folder named .git, node_modules, or __pycache__ | Anywhere in the skill folder |
| Files and folders that Git ignores in the catalog clone | Anywhere in the skill folder |
If Git ignores the SKILL.md itself, delivery stops with an error. See Add scripts and other companion files.
Stack files
A stack is a file stacks/<slug>.yaml. SkillCatalog reads only files that end in .yaml and skips a .yml file without a message. The file holds front matter between --- lines, then a YAML body with the list of skills. Its schema is stack.schema.json.
---
name: Code review
description: Skills for reviewing Acme pull requests.
author: Dana Reyes
owner: platform
tags: [review]
created_at: 2026-09-23T09:00:00Z
updated_at: 2026-09-23T09:00:00Z
---
skills:
- review-checklist
- review-security| Field | Required | Rules |
|---|---|---|
name | No | The display name, at most 500 characters. Without it, the slug. |
description | Yes | At most 500 characters. |
author | Yes | Free text, at most 500 characters. |
created_at, updated_at | Yes | RFC 3339 timestamps. updated_at cannot be earlier than created_at. |
owner | No | The slug of an owner in catalog.yaml. |
tags | No | A YAML list of tag slugs defined in catalog.yaml. |
skills (in the body) | Yes | The skills, in order: slugs of skills in the same catalog, at least one, no duplicates. |
When a skill in the stack refers to another skill with @skill:, that skill must be in the stack too. Other fields are ignored.
Bundle files
A bundle is a file bundles/<slug>.yaml with the same layout as a stack. It lists stacks in its body and has no tags. Its schema is bundle.schema.json.
---
name: Reviewers
description: Everything an Acme reviewer needs.
author: Dana Reyes
created_at: 2026-09-23T09:00:00Z
updated_at: 2026-09-23T09:00:00Z
---
stacks:
- code-reviewname, description, author, owner, created_at, and updated_at follow the stack rules. stacks lists stacks of the same catalog: at least one, no duplicates. Each of those stacks must include, on its own, every skill that its skills refer to.
A skill, a stack, and a bundle in one catalog cannot share a slug. See Group skills into stacks and bundles.
catalog.yaml
catalog.yaml sits at the root of a catalog. Its schema is catalog.schema.json.
id: acme-skills
name: Acme Skills
description: Review and release skills for Acme engineers.
taxonomy:
categories:
- slug: quality
name: Quality
children:
- slug: security
name: Security
owners:
- slug: platform
name: Platform team
tags: [review, release]
validation:
validation.body.too-long: false
metrics_url: http://metrics.acme.internal:9090/api/v1/otlp/v1/metrics| Field | Required | Rules |
|---|---|---|
id | Yes | The catalog id: a slug of 2 to 64 characters. Profiles and manifests refer to the catalog by it. |
name | Yes | The display name, at most 500 characters. |
description | No | At most 500 characters. |
taxonomy | No | The categories, owners, and tags that skills, stacks, and bundles may use. |
validation | No | Content checks to turn off (false) or on (true) for every skill in the catalog. See Content checks. |
metrics_url | No | The metrics address that receives anonymous usage counts from everyone who has the catalog and has usage metrics on. The rules are below the table. |
In taxonomy, each category and owner has a slug and a name, and may have children one level deep. Slugs cannot repeat within categories or within owners.
tags is a list of names, and each name becomes a slug: Code Review becomes code-review. Two names cannot give the same slug, and skills and stacks refer to tags by slug. SkillCatalog ignores other keys, including categories, owners, or tags placed outside taxonomy.
A metrics_url is valid when it starts with http:// or https://, names a host, has a path that ends in /v1/metrics, has no user name, password, query, fragment, or surrounding spaces, and is at most 2,048 bytes. A blank value, null, or no key means the catalog has no address. SkillCatalog ignores any other value, such as a list or an address that breaks a rule: the catalog stays readable, no counts are sent for it, and skc validate --path warns about it. See Collect usage counts for your catalog.
A catalog also needs a skills/ folder next to catalog.yaml. Git does not keep empty folders, so keep a file such as .gitkeep in it. A registered catalog whose clone lacks skills/ cannot be read. stacks/ and bundles/ are optional: a missing one counts as empty. When present, none of the three can be a symbolic link or a file. README.md is optional.
Content checks
skc validate runs these four checks on every skill:
| Check | What it finds | Level |
|---|---|---|
validation.body.too-long | A SKILL.md body over 500 lines | Warning |
validation.reference.depth-too-deep | A link from SKILL.md to a file more than one folder down | Warning |
validation.reference.missing-toc | A linked Markdown file over 200 lines without a contents heading | Warning |
validation.time-sensitive.outside-old-patterns | A date, an "as of", or a version pin such as v1.2.3 in prose, outside sections headed Old patterns, History, Historical, or Deprecated | Info |
All four run by default. false turns a check off, and true turns it back on for a skill in a catalog that turned it off. A skill's skillcatalog/validation metadata wins over the catalog's validation map. An unknown check id makes catalog.yaml unreadable, and a skill with one fails skc validate.
skc validate also runs validation.conflict-markers, which cannot be turned off. It reports an error for a leftover Git conflict block in a SKILL.md: a <<<<<<< line, then a ======= line, then a >>>>>>> line, each marker at the start of its line. A <<<<<<< line inside a fenced code block starts no block, so marker examples belong in fenced code, and a real conflict inside a fenced code block goes unreported. The check reads only SKILL.md, not companion files. See A commit stops on leftover conflict markers.
See Check a skill before you share it.
Team manifest
The team manifest is .skillcatalog/skillcatalog.yml in a repository. It lists the entries that repository wants for everyone. Its schema is skillcatalog-yml.schema.json.
profile:
id: payments-api
display_name: Payments API
catalogs:
acme-skills:
source: git@github.com:acme/acme-skills.git
entries:
- catalog_id: acme-skills
kind: skill
slug: release-notes| Field | Required | Rules |
|---|---|---|
profile.id | Yes | The project profile id: a slug other than home. |
profile.display_name | Yes | At most 500 characters. |
catalogs | When entries is not empty | A map from catalog id to source. Each key must equal the id in that catalog's catalog.yaml. |
catalogs.<catalog-id>.source | Yes | The catalog's remote Git URL. Each URL can appear once. SkillCatalog compares it with the registered URL as text, so SSH and HTTPS forms of one repository do not match. See Names and limits. |
entries | Yes | The skills, stacks, and bundles to deliver, in order; the list can be empty. Each entry has catalog_id (a key of catalogs), kind (skill, stack, or bundle), and slug. The same entry cannot appear twice. |
Unknown keys make the manifest invalid. SkillCatalog also reads a skillcatalog.yml at the repository root when .skillcatalog/skillcatalog.yml does not exist. When both exist, skc install, skc update, and skc update --check fail. skc init refuses to write when either file exists.
skc init writes this file from its flags. It pairs each --source with the --catalog-id in the same position. A --catalog-id without a paired --source uses the URL registered for that catalog. See Add skills to a team manifest.
Local manifest
The local manifest is .skillcatalog/skillcatalog.local.yml: your own additions and exclusions for one checkout. SkillCatalog adds it to the checkout's .gitignore when it first writes it. Every key is optional. Its schema is skillcatalog-local-yml.schema.json.
catalogs:
acme-skills:
source: git@github.com:acme/acme-skills.git
entries:
- catalog_id: acme-skills
kind: stack
slug: code-review
excludes:
- catalog_id: acme-skills
kind: skill
slug: release-notes| Field | Rules |
|---|---|
catalogs | As in the team manifest. An entry here must use a catalog declared in this file, even when the team manifest declares it too. When both files declare a catalog id, the URLs must be the same. |
entries | As in the team manifest. |
excludes | Team manifest entries to leave out of delivery, with the same three keys. An exclusion needs no catalog declaration. |
profile | Optional. When present, it follows the team manifest's rules. |
Unknown keys make the file invalid. A skillcatalog.local.yml at the repository root is read when this file does not exist. Both at once is an error. See Make personal changes.
settings.toml
~/.skillcatalog/settings.toml holds your settings. SkillCatalog creates it when you first change a setting, and you may edit it by hand. SkillCatalog ignores keys it does not know and keeps them when it saves the file.
[targets.claude_code]
enabled = true
[targets.cursor]
enabled = false
[targets.codex]
enabled = false
[scoring.enabled_providers.claude-code-local]
enabled = true
model = "sonnet"
effort = "high"
[proposals]
archive_retention_days = 30
remote_only_discovery_cap = 200
[telemetry]
enabled = false| Key | Default | Meaning |
|---|---|---|
targets.claude_code.enabled, targets.cursor.enabled, targets.codex.enabled | false | Whether SkillCatalog delivers to Claude Code, Cursor, and Codex. skc settings enable <tool> and skc settings disable <tool> set them. |
scoring.enabled_providers.<provider-id>.enabled | false | Whether an AI provider may score skills. The ids are claude-code-local and codex-local. |
scoring.enabled_providers.<provider-id>.model | None | The model name passed to the provider's tool. |
scoring.enabled_providers.<provider-id>.effort | None | The effort level. Each provider accepts its own values. skc scoring providers set-effort lists them when you give another one. |
proposals.archive_retention_days | 30 | Days SkillCatalog keeps records of proposals that are merged, closed, or discarded. |
proposals.remote_only_discovery_cap | 200 | The most proposal branches without a record on this machine that one listing shows. |
telemetry.enabled | true | Whether SkillCatalog sends usage counts to the metrics addresses that your catalogs name. skc settings telemetry --enable and --disable set it. See Stop sending usage counts. |
onboarding_completed | false | Whether the desktop app's first-launch setup finished. |
A [proposals] table must hold both of its keys, because with only one, every command that reads settings fails. SkillCatalog ignores a theme key.
skc links
The desktop app opens two kinds of skc:// links. Values in a link are URL-encoded. A fragment, a repeated parameter, or a parameter not listed here makes a link invalid.
| Link | Parameters |
|---|---|
skc://catalog/add?source=<url>&name=<name> | source (required): the catalog's remote Git URL. name (optional): the name the confirmation shows, 2 to 100 characters, without line breaks, /, \, or null characters, and not . or ... Without it, the app shows the repository name. |
skc://share/install?v=1&kind=<kind>&catalog_id=<catalog-id>&slug=<slug>&source=<url> | All required. v: 1. kind: skill, stack, or bundle. catalog_id: the catalog id. slug: the item's slug. source: the catalog's remote Git URL. |
For example, these links add the Acme catalog and install its review checklist:
skc://catalog/add?source=git%40github.com%3Aacme%2Facme-skills.git&name=Acme%20Skills
skc://share/install?v=1&kind=skill&catalog_id=acme-skills&slug=review-checklist&source=git%40github.com%3Aacme%2Facme-skills.gitThe desktop app makes share links for skills, stacks, and bundles. No command makes catalog links, so write them yourself. See Share with a link.
Names and limits
| Name | Rule |
|---|---|
| Slug: skill, stack, bundle, category, owner, tag, profile id | Lowercase letters, digits, and hyphens, 1 to 128 characters. It starts with a letter or digit, and has no hyphen at the end and no two hyphens in a row. |
| Catalog id | A slug of 2 to 64 characters. |
Skill name | Equal to the skill's folder name; at most 64 bytes. |
| Names, display names, and descriptions | Not empty; at most 500 characters. |
Remote Git URL, in skc catalog add, manifests, and links | git@<host>:<path>, or a URL that starts with ssh://, http://, or https://. No spaces, passwords, tokens, query strings, or fragments, and no user name in an http:// or https:// URL. Local paths and file:// URLs are refused. |
Commit message for skc sync | Not empty; at most 1,000 bytes after trimming. |
| File limit | Value |
|---|---|
SKILL.md body | 1 MiB (1,048,576 bytes) |
| Any other file in a catalog, including companion files | 10 MiB |
| Path inside a catalog | 4,096 bytes, with at most 32 parts of up to 255 bytes each |
| Entries in one folder | 4,096 |
Reading a symbolic link, a special file, or a file with more than one hard link inside a catalog fails.