decimalai skills installs registry skills into your project as ordinary folders, .agents/skills/<install-name>/, the layout every native skill loader reads. It works like a package manager. .decimal/skills.toml lists the skills the project wants, .decimal/skills.lock records the exact bytes that were written, and decimalai skills install --frozen reproduces those bytes in CI or Docker, or refuses with an exit code that says why.
Added in SDK 0.15.0. A project set up by 0.14 or older has a version 1 lock; run decimalai skills migrate once.
This is the disk channel: your framework reads the files itself. The Skill Router is the other channel: it puts skills into the prompt at run time and needs no files. Pick one per agent. A skill that is both routed and on disk reaches the prompt twice, and the adapters log a warning when they see the router’s loader inside a runtime that loads
SKILL.md from disk.Quick start
1
Install the SDK
2
Add a skill
DECIMAL_API_KEY, only public skills are available, fetched anonymously.3
Point your framework at the folder
decimalai.skills_dir() returns the absolute path of .agents/skills. Each framework has a one-line entry point; see the table below and Native skill loaders. Claude Code loads the whole root as a plugin:4
Commit, and install frozen in CI
Commit
.decimal/skills.toml, .decimal/skills.lock and .agents/. CI and Docker run decimalai skills install --frozen.What skills add writes
.git entry and never goes above your home directory; within that range the project is the nearest folder holding .decimal/skills.toml or .decimal/skills.lock, else the nearest holding any .decimal/, else the repository root. Every command takes --directory DIR to name it instead.
The root is always .agents/skills. The manifest and the lock record it, and any other value is refused.
.agents/skills/<install-name>/
The registry’s own files for one version of the skill, written byte for byte: SKILL.md plus its bundled files under scripts/, references/, templates/ and assets/. Bundled scripts that the skill marks executable are written with mode 0755, everything else 0644. A version’s eval.yaml is not part of the folder; skills pull writes it for a loose copy.
SKILL.md carries the skill’s identity under metadata, where every loader keeps it:
nameis the install name, which is also the folder name.metadata.decimalai-nameis the registry name.decimalai-body-sha256is the sha256 of the body with surrounding whitespace stripped, so you can tell an edited copy from the original.- The top-level keys are only ever
name,description,license,compatibility,allowed-tools,disable-model-invocationandmetadata.allowed-toolsandcompatibilityare single strings, never lists.decimalai-sourceis written for public skills only. disable-model-invocation: trueis written for a skill whose owner made it user-only. Only Claude Code honours it; the other seven loaders offer it to the model.
allowed-tools depends on who wrote the skill. Claude Code pre-approves every tool a skill lists, so a skill your organization did not author (a public skill, a skill you use, a fork, a GitHub import) keeps only Read, Grep and Glob; its scripts still run, with your permission. A skill your organization authored keeps its list, except that a bare Bash becomes one scoped entry per bundled script, such as Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/compute_refund.py *). Every dropped or rewritten tool is printed when the skill is installed.
Binary attachments are not installed. The registry stores text attachments only. A file it cannot serve is named in a warning and recorded in the lock as unavailable, so verify does not report it missing. If you put such a file there yourself, updates carry it over.
.decimal/skills.toml
skills add given a url_slug, an install name or a skill id records the name the registry answers with. A value is "latest" or a version number written as a string; there are no ranges. A skill added by its id is written as "name" = { version = "latest", id = "…" }. The engine rewrites only the lines whose entry changed, so your comments and other lines stay as you wrote them.
.decimal/skills.lock
files holds the sha256 of every file exactly as written, and render names the exact rendering, so the same bytes can be fetched again later. lock_hash covers every entry except installed_at, so two machines that install the same lock agree. Never edit the lock by hand: a frozen install refuses a lock whose lock_hash does not recompute (exit 6), and skills install recomputes it with a warning.
source.base_url records where the skill came from and is informational only. The API key is only ever sent to the registry you configure (--base-url or DECIMAL_BASE_URL), never to a URL read from a committed lock.
.agents/.claude-plugin/plugin.json
.agents a Claude Code plugin named decimalai-skills, so Claude Code lists the skills as decimalai-skills:<install-name>. It is not named decimalai, which is the DecimalAI marketplace plugin; two plugins with one name replace each other. The engine refuses to overwrite a plugin.json it did not write, unless you pass --force, which backs it up first.
.decimal/.gitignore
Lists the engine’s working files so they are never committed: install.json, staging/, backup/, migrated-v1/, skills.lock.journal and .skills.lck. Missing lines are appended; your own lines stay.
Install names
The install name is the folder name and thename: in SKILL.md. The registry chooses it for your project, from the names the project already uses:
- The base form lowercases the name, drops accents, and turns every other character run into one
-. - The hashed form, up to 57 characters of the base plus
-and 6 hex of the skill’s id, is used when the base is taken in this project, or when the base would be unusable: empty, over 64 characters, reserved (install,search,check,decimalai-skills), a name that lost characters other than Latin letters, only theownerpart ofowner/name, or a word YAML reads as something other than text (404,yes, a date). - Names compare case-insensitively, because
Acme/andacme/are one folder on macOS and Windows. - An installed skill keeps its folder name for good, through upstream renames. Only
removefollowed byaddpicks up a new one.
Versions: latest or exact
cargo add does. skills install never moves a version: it installs what the manifest and lock say. Moving forward is skills update:
Each changed folder is replaced whole, so a file the new version dropped disappears. If one skill cannot be updated, none is.
--dry-run says what would change and changes nothing; --dry-run --json gives the plan to a script.
The owner can also edit a version’s description, license, compatibility, allowed tools or invocation setting without publishing a new version. skills status --remote reports that as description changed upstream (same version) or rendered differently upstream (same version), and skills update delivers it. A frozen install keeps reproducing the bytes the lock names until someone runs update.
For another organization’s skill, latest skips a version whose safety scan failed and installs the newest one that passed, and says so. Asking for that failed version exactly is refused (exit 7).
Commands
Every flag and output is in the CLI reference. The same engine is a Python API:
Result with the same changes, problems and exit_code the command prints; a refusal is returned, not raised.
The trust gate
Every skill passes the registry’s safety review before it is written:
Skills your organization owns are never asked about. The question comes once per version: after you accept a version,
install of the same version does not ask again, and install --frozen never asks, because the lock records that the version was accepted.
If the registry later blocks or withholds a locked version, a frozen install that has to fetch it refuses:
skills status --remote reports the locked version as revoked.
What the bands mean, and what scanning cannot catch: How skills are vetted.
Commit the root, or gitignore it
Commit it (the default). Commit.decimal/skills.toml, .decimal/skills.lock and .agents/. A fresh clone works with no install step, and code review shows exactly which skill text changed. In CI, install --frozen re-checks every committed byte against the lock and fetches nothing.
Or gitignore the root. Add .agents/skills/ and .agents/.claude-plugin/ to your .gitignore and commit only the two .decimal/ files. Then every checkout runs install --frozen, which fetches each missing folder by its exact lock entry and checks every byte against it. Skills you wrote yourself in .agents/skills/ would be ignored too, so keep those elsewhere if you choose this.
skills status warns when git ignores .decimal/skills.lock or .decimal/skills.toml.
CI
install --frozen exits 4 when someone edited .decimal/skills.toml without running skills install, and 5 when a committed folder no longer matches the lock. With a committed root, add --offline to make the job fail (exit 8) rather than fetch anything. A scheduled job can run decimalai skills status --remote --check, which exits 1 when a skill is outdated, re-described upstream or revoked.
Docker
With the root committed, the image needs no network and no key:Local edits, --force and backups
A managed folder is the registry’s bytes. When a file in it differs from the lock, status shows the folder as modified, and every command that would replace it stops with exit 5 instead:
--force. --force first copies the folder as it was to .decimal/backup/<txid>/<install-name>/, where <txid> is the time of the run plus a random suffix, and prints that path:
On every one of them,
--force also replaces a .agents/.claude-plugin/plugin.json the engine did not write, after backing it up. It never overrides a symlink, or a file where a skill folder belongs (exit 5): the engine never writes through a symlink inside the project. Backups are gitignored and never deleted by the engine; remove them when you no longer need them.
Without --force, skills install leaves a modified folder alone and says so; only the commands above that must replace it stop.
A crash is recoverable. Every command fetches and checks everything before it changes a folder, then swaps whole folders one at a time and writes the lock after each one. A run that stops halfway leaves each folder either as it was or as it should be, never half written. The next add, install, update or remove finishes or rolls back the interrupted change; until then status shows it as interrupted, and install --frozen, which never writes the lock, refuses with exit 1. Two decimalai skills commands in one project never run at once: the second waits up to 30 seconds for .decimal/.skills.lck, then exits 1.
status and verify
skills status reads the project and changes nothing. Each folder is clean, modified (with the files), missing (locked, not on disk), unmanaged (a folder the lock does not list) or illegal (a symlink or a file), and it reports a manifest that disagrees with the lock, an interrupted run and a version 1 lock:
./skills holds skills you wrote and an API key is set, a second section compares them with the platform, as decimalai skills sync sees them. --check makes status exit 1 when the installed skills have drifted, for CI.
skills verify checks folders the way the eight native loaders read them, and catches what a loader would drop silently. Seven of the eight skip or rename a folder that breaks the format without raising an error, so a bad folder in production usually shows up as a skill that is simply never used.
- Managed folders (in the lock) must hold exactly their locked files, hashes and exec bits; every rule is an error.
- Your own folders in the root, and any tree you name with
skills verify PATH, are checked by the format rules every loader needs (errors) and the format’s own rules (warnings, or errors with--strict).
decimalai skills verify ./skills --strict is a CI check for a repository of skills you author. The rule ids are listed in the CLI reference.
Exit codes
Stable across releases, so CI and Dockerfiles can branch on them. Every refusal names the skill, the reason and the command that fixes it.Point your framework at it
decimalai.skills_dir() finds the project from the working directory and returns .agents/skills as an absolute path. It raises SkillsNotInstalledError when there is no lock or no root yet (run decimalai skills install), and LegacyLockError for a version 1 lock (run decimalai skills migrate).
project is the project directory (decimalai.skills_dir().parents[1]), and run_script is a script runner you provide. The full snippet for each, what each one reads and the gaps each has upstream: Native skill loaders.
After skills update, restart the agent process. A loader that has already read a skill can keep serving the old version: Microsoft Agent Framework caches skills by default, and a running Claude Code session can keep the skills it already loaded. Each loader is checked in a fresh process, so a reload is the safe assumption.
How usage is counted
- Fetching a skill for disk is an install, not a delivery. Each fetch is counted on its own, by organization, day and command (frozen CI installs included), and never as the skill being served to a model.
- Traces name installed skills by registry name. A loader reports a skill by its folder name, by
decimalai-skills:<install-name>(Claude Code) or by its path. Before the SDK sends a trace, it looks those names up in the lock of the project the process runs in and adds the registry name, skill id, version id and body hash, so the activation counts for that exact version. A folder with local edits is reported with the hash of its edited body and no version id, so it is never credited to the registry version. - For a framework with no DecimalAI adapter, report a load yourself inside a trace with
decimalai.log_skill_activation(name="acme-refund-policy"); the same lookup adds the ids. See Skills in the SDK.
Upgrading from an older SDK
SDK 0.14 and older wrote a version 1.decimal/skills.lock (keyed by registry name, with no record of the bytes) and per-agent folders such as .claude/skills/. 0.15 refuses that lock and asks you to migrate:
migrate resolves every entry again, installs each skill fresh from the registry, and writes the manifest, a version 2 lock and plugin.json. Old folders inside .agents/skills move to .decimal/migrated-v1/; old folders anywhere else stay where they are; nothing is deleted. What moves where, step by step: Migrations.
What changed in the files
Limits
- Windows is not tested in this release: folder renames, exec bits and long paths.
- One root per project, inside it. For a copy in your home directory (personal Claude Code skills), use
decimalai skills pull <ref> --out ~/.claude/skills, a loose copy that nothing updates. - A 0.15 client needs the current registry. Against a registry without the install routes, every command that reaches the registry exits 7 naming the missing route; it never falls back to the routes 0.14 used.