Skills
A skill is a self-contained capability directory that a supporting AI coding tool reads at startup. Skills are stored once in a canonical location and symlinked — not copied or merged — into each supporting provider's skills folder.
What a skill is
A skill is a directory containing a SKILL.md marker file plus any supporting assets. The marker file is what makes a directory a skill; directories without SKILL.md are ignored.
.agents/skills/<name>/
├─ SKILL.md # required marker; body of the skill
└─ <assets>/ # optional supporting files
Canonical store
The canonical store lives at .agents/skills/<name>/ inside your workspace root. The plural .agents is the agentskills.io vendor-neutral convention; codex and opencode read this location natively.
<workspace-root>/
└─ .agents/
└─ skills/
├─ my-skill/
│ └─ SKILL.md
└─ another-skill/
└─ SKILL.md
There is no database entry and no git involvement. Every link state is computed live from the filesystem (lstat/readlink) each time you run a skill command.
Reconciliation by symlink
Unlike agents — which are transformed, merged, and tracked in sqlite — skills are reconciled purely by the filesystem. graft creates one symlink per (provider, skill) pair pointing at the canonical directory. It never copies or merges skill content between providers.
.claude/skills/my-skill → ../../.agents/skills/my-skill
.opencode/skills/my-skill → ../../.agents/skills/my-skill
Link state for each (provider, skill) pair is one of:
| State | Meaning |
|---|---|
linked | Symlink exists and points at the canonical skill dir. |
missing | No entry at the provider path. |
wrong-link | A symlink exists but points somewhere else. |
conflict | A real (non-symlink) directory or file is present; use --override to replace it. |
Supporting providers
Only four of the eight active graft providers support skills. The others are silently skipped.
| Provider id | Tool | Project skills dir |
|---|---|---|
claude-code | Claude Code | .claude/skills/ |
opencode | OpenCode | .opencode/skills/ |
codex | Codex | native (.agents/skills/, no symlink) |
grok-cli | Grok CLI | native (.agents/skills/, no symlink) |
:::note Claude Code and the vendor-neutral store
Claude Code does not read .agents/skills directly, so it always gets a symlink under .claude/skills/. The symlink is what makes skills available to Claude Code in a project.
:::
:::note gemini-cli deprecated
gemini-cli previously supported skills (.gemini/skills/) but was deprecated 2026-06-15 and removed from the active skills engine.
:::
Home-scope detection
graft also scans each supporting provider's personal (home) skill directories when looking for install candidates. Home-scope directories are read-only sources — graft surfaces their contents as skills you can install into the canonical store, but it never symlinks anything into them.
| Provider | Personal skill directories scanned |
|---|---|
claude-code | ~/.claude/skills |
codex | ~/.codex/skills, ~/.agents/skills |
grok-cli | ~/.grok/skills, ~/.agents/skills |
opencode | ~/.config/opencode/skills, ~/.claude/skills, ~/.agents/skills |
A skill found in any of these locations appears as an install candidate in graft skill status and can be installed by bare name: graft skill install <name>.
Legacy migration
The legacy location .agent/skills (singular) from earlier graft versions is automatically migrated to .agents/skills (plural) the first time any skill command runs. The migration is idempotent: skills already present canonically are not overwritten, and the legacy directory is removed once it has been drained.
Init and sync hook
After a successful graft init or graft sync agents, graft automatically runs a skill apply pass (the init/sync hook). This hook symlinks every canonical skill into all supporting providers without requiring a separate graft skill sync. Hook behavior is controlled by config keys under skills.*:
skills.enabled— master switch;trueby default.skills.autoInstall— whentrue, install missing referenced skills without prompting (equivalent to--yes).skills.providers[]— restrict the hook to specific providers (empty = all supporting providers).
A skill problem in the hook never fails the agent operation. Errors are logged to stderr and the hook result is swallowed.
Associating skills with an agent
The skills: field in agent.yaml lets you declare which skills an agent should preload. This is different from discovery and symlink reconciliation: discovery makes skills available to the tool at a workspace level; per-agent association tells the tool which specific skills to activate for a given agent.
graft writes per-agent skill associations into the provider file for two providers only:
claude-code— graft writes askills:YAML frontmatter array in.claude/agents/<name>.md. Each entry is a skill name string.codex— graft writes[[skills.config]]TOML tables in.codex/agents/<name>.toml, where each table hasname = "<skill>"andenabled = true. User-authoredenabled = falseentries are preserved on round-trip (lossless).
The other providers do not have a per-agent skills field in their format, so graft does not write one for them. Skills for those providers still work via discovery and symlink.
| Provider | Per-agent association written | Format |
|---|---|---|
claude-code | Yes | skills: array in YAML frontmatter |
codex | Yes | [[skills.config]] TOML tables |
opencode | No (skills via discovery) | — |
grok-cli | No (skills via discovery) | — |
cursor, github-copilot, goose, roo-code | No | — |