Skills
Skills extend an agent with domain expertise using the AgentSkills open standard. A skill is a directory containing a SKILL.md file with instructions the agent can load on demand.
How it works
Skills use progressive disclosure to manage context efficiently:
- Metadata (~100 tokens/skill) — name + description, always in the system prompt
- Instructions (<5k tokens) — SKILL.md body, loaded when the agent decides the skill is relevant
- Resources (unlimited) — scripts, references, assets — loaded only when needed
The agent decides when to activate a skill based on the description alone. No trigger engine needed.
Skill format
my-skill/
├── SKILL.md # Required: YAML frontmatter + instructions
├── scripts/ # Optional: executable code
├── references/ # Optional: documentation loaded on demand
└── assets/ # Optional: templates, static resources
SKILL.md uses YAML frontmatter:
---
name: git
description: Git operations — commit, branch, merge, rebase. Use when the user mentions version control.
---
# Git Skill
## Workflow
1. Run `git status` first
2. Stage changes, write conventional commit messages
3. For merges, check for conflicts first
## Scripts
For complex diffs: `bash {baseDir}/scripts/diff_summary.sh`
Loading skills
#![allow(unused)] fn main() { use phi_core::SkillSet; use std::path::PathBuf; // Load from multiple directories (later dirs override earlier on name conflict) let skills = SkillSet::load(&[PathBuf::from("./skills"), PathBuf::from("~/.phi-core/skills")]); // Or load from a single directory with a label let workspace_skills = SkillSet::load_dir("./skills", "workspace"); }
Using with Agent
#![allow(unused)] fn main() { use phi_core::{BasicAgent, SkillSet}; use phi_core::provider::ModelConfig; use std::path::PathBuf; let api_key = std::env::var("ANTHROPIC_API_KEY").unwrap(); let skills = SkillSet::load(&[PathBuf::from("./skills")]); let agent = BasicAgent::new(ModelConfig::anthropic( "claude-sonnet-4-20250514", "Claude Sonnet 4", &api_key, )) .with_system_prompt("You are a coding assistant.") .with_skills(skills) // Appends skill index to system prompt .with_tools(tools); }
The agent's system prompt will include:
<available_skills>
<skill>
<name>git</name>
<description>Git operations — commit, branch, merge, rebase.</description>
<location>/path/to/skills/git/SKILL.md</location>
</skill>
</available_skills>
When the agent encounters a task matching a skill, it reads the SKILL.md using the read_file tool and follows the instructions. No special infrastructure needed.
Precedence
When loading from multiple directories, later directories take precedence. A skill in ./skills/ overrides the same-named skill in ~/.phi-core/skills/.
You can also merge skill sets explicitly:
#![allow(unused)] fn main() { let mut base = SkillSet::load_dir("/usr/share/phi-core/skills", "bundled")?; let user = SkillSet::load_dir("~/.phi-core/skills", "user")?; let workspace = SkillSet::load_dir("./skills", "workspace")?; base.merge(user); base.merge(workspace); // workspace wins on conflict }
Compatibility
By following the AgentSkills standard, skills written for phi-core work with Claude Code, Codex CLI, Gemini CLI, Cursor, OpenCode, Goose, and any other compatible agent. Write once, use everywhere.
Design philosophy
Skills are deliberately simple:
- No trigger engine — the LLM decides from descriptions
- No compile-time registration — skills use existing tools (read_file, bash)
- No plugin API — skills are just files
- No runtime loading — loaded at startup, that's it
If a skill needs a custom tool, it can provide an MCP server.