Install registry skills into your project
agent_config() first runs with an API key in a git repository, which writes .decimal/link.toml.
- Not connected: it writes the three things below, all meant to be committed. It creates no fork and no link on DecimalAI.
- Connected, or with
--to-agent <name>: it adds the skill to the agent on DecimalAI and writes no files. It needs an API key.
name: in its SKILL.md. CI and Docker builds run decimalai skills install --frozen, which writes the locked bytes or fails. With an API key you can install your own skills, skills you’ve linked, and public ones. With no key, public skills only. Skills on disk has the files, commands, exit codes and CI recipes.
The same engine is a Python API. It always writes files, connected or not:
Result with changes, problems and exit_code, and a refusal is returned, not raised. The key comes from api_key= or DECIMAL_API_KEY. A skill whose safety review is caution or unreviewed is refused when there’s no terminal to ask on, unless you pass yes=True.
To add a skill to an agent on DecimalAI from Python, use router.assign_skill().
Load installed skills with a framework loader
decimalai.skills_dir() returns the project’s root, .agents/skills, as an absolute path, from any subdirectory. Each framework with a skill loader of its own has a helper that points the loader at that root. Import its module first (import decimalai.adk), since import decimalai alone doesn’t load it:
skills_dir() raises SkillsNotInstalledError when the project has no .decimal/skills.lock or no .agents/skills, and LegacyLockError for a lock in an old format (run decimalai skills migrate).
DecimalAI’s own skill list stays out of an agent whose loader serves its skills, so the model sees one list. See Don’t deliver a skill two ways.
Record what a native loader loaded
With DecimalAI’s instrumentation on, each skill a framework’s loader hands the model is recorded on the run’s trace as an activation, the Loaded step. It carries the skill id and the version the model received, decided from the delivered bytes rather than the folder on disk. See What DecimalAI records. Anywhere else, such as a framework with no DecimalAI adapter, record the load yourself in a run you trace withdecimalai.start_trace():
decimalai-skills:acme-refund-policy, or the folder’s path. The trace carries acme/refund-policy, and the activation carries the skill id, version id and body hash from the lock. A folder you edited is credited to its skill, never to the locked version. The folder is hashed when the trace is sent, so after a skills update a loader that cached the old version is credited with the new one. The helpers above don’t have that problem.
When the process runs somewhere the lock isn’t, pass the identity yourself. skill_id, version_id and body_sha256 are keyword arguments of both log_skill_activation() and log_skill_loaded(). The values sit in the installed SKILL.md’s metadata, and decimalai.skills.discover_skills() returns them on each descriptor:
DecimalConfigError outside a trace. log_skill_loaded() says the body reached the model. With an id or a hash, it also records an activation carrying them, unless you declared one for that name already.
Pull a public skill
For a loose copy of a published skill,pull needs no API key:
--force. More: skills pull.
Sync skills from code to platform
Skills you wrote can be uploaded as your agent starts:skill_dirs is an argument of three instrument() calls, in decimalai.openai_agents, decimalai.langchain and decimalai.otel, and of decimalai.start_trace(). This upload is one-way: it sends the skills you wrote and writes nothing to disk. A folder decimalai skills installed is read but never uploaded, since that would make a private copy of a registry skill in your organization. To sync both ways from a terminal or CI, use decimalai skills sync.
To upload a folder from a script, with no agent running, use sync_to_platform():
conflict_policy="local_wins", which suits CI where the repository is the source of truth.
SkillRouter: manage skills from code
assign_skill()also takes a public registry skill’s id, which links it for that agent only. An agent DecimalAI hasn’t seen yet is created. Assigning again only updates the pin.unassign_skill()takes away a"restricted"skill. A"workspace"skill is still offered, as to every agent.delete_skill()deactivates the skill.update_skill()makes a new version only when the text changes. Every version keeps its text andcontent_hash, so you can read two withget_version()and compare them.- A skill is part of your agent’s manifest, so a changed skill shows up in a regression check’s report.
router.export()writes.agents/skills/and the lock, connected or not.export,update_skillsandstatustakeproject_root=.
On-demand bodies: the load_skill tool
agent_config() gives your agent a skill list and a load_skill tool, which How skills reach the model explains, along with what a load can reach and how to turn it off. This section is for a SkillRouter you drive yourself.
Body loads are budgeted per turn so they can’t blow the context window:
Loading a skill again in the same turn is free. Each load is recorded on the trace as
skills_loaded_by_agent.
load_skill() always returns a string and never raises. get_skill_body_record() returns the server’s whole record, or None when the skill isn’t served. routing_id="" means a run whose routing call returned no decision.
On Pydantic AI and the raw provider SDKs, recording a load needs a run to attach to, which agent_run() opens and agent_config() opens for you. Outside one, those traces still ship, with no routing_id and no skill names.
To put bodies in the prompt instead of using the tool, pass SkillRouter(inject_body=True). CrewAI’s adapter works this way, since it has no tool loop. A body is injected only when the platform ranked the result by this message, so a turn like “thanks!” gets the list alone. Offered priority_skills and an agent’s only skill get their body either way. A body that doesn’t fit the limits above is left out, not cut short.