Expand description
§adk-skill
AgentSkills parser, index, matcher, and runtime injection helpers for ADK-Rust.
§Overview
adk-skill provides the core building blocks to load markdown instruction files, select relevant skills for a user query, and inject selected instructions into user prompts.
This crate is provider-agnostic and can be used through:
adk-agent(LlmAgentBuilder::with_skills*)adk-runner(Runner::with_auto_skills)- Direct API calls from custom runtimes
§Supported File Conventions
§Frontmatter Skill Files (.skills/**/*.md)
Each skill is a markdown file with YAML frontmatter:
---
name: code_search
description: Search source code quickly
tags: [code, search]
---
Use `rg --files` first, then `rg <pattern>`.Rules enforced by parser:
- Opening and closing
---delimiters are required. nameis required and must be non-empty after trim.descriptionis required and must be non-empty after trim.tagsis optional and empty/whitespace tags are dropped.
§Instruction Convention Files
The index loader also discovers and ingests these markdown files:
AGENTS.mdandAGENT.mdCLAUDE.mdGEMINI.mdCOPILOT.mdSKILLS.mdSOUL.md(root-level)
For these files, frontmatter is optional:
- If valid frontmatter is present, it is used.
- Otherwise the file is parsed as plain markdown instructions and converted into a skill document with convention tags (for example
agents-md,claude-md).
§What The Crate Does
§1. Discovery
- Scans
<root>/.skills/recursively for frontmatter skills. - Scans
<root>recursively for supported convention files. - Skips common heavy directories (
.git,target,node_modules, etc.). - Returns deterministic sorted file paths with de-duplication.
API: discover_skill_files(root)
API: discover_instruction_files(root)
§2. Parsing + Validation
- Strict path (
.skills/**): parses required frontmatter as YAML with validation. - Convention path (
AGENTS.md,CLAUDE.md, etc.): parses plain markdown (or frontmatter if provided). - Returns actionable errors with file path context for strict frontmatter paths.
API: parse_skill_markdown(path, content)
API: parse_instruction_markdown(path, content)
§3. Indexing
- Builds
SkillIndexfrom discovered files. - Computes:
- content hash (
SHA-256) last_modified(Unix timestamp seconds when available)- stable document id:
normalized-name + first-12-hash-chars
- content hash (
- Sorts documents deterministically by
(name, path).
API: load_skill_index(root)
§4. Selection
Selection is lexical and deterministic (no embeddings yet).
Scoring weights:
name:+4.0per token hitdescription:+2.5tags:+2.0body:+1.0
Score is normalized by sqrt(body_token_count) to reduce long-body bias.
Tie-break order:
- Higher score first
- Lexicographically smaller
name - Lexicographically smaller
path
API: select_skills(index, query, policy)
SelectionPolicy defaults:
top_k = 1min_score = 1.0include_tags = []exclude_tags = []
§5. Injection
Injection helpers prepend the selected skill body to user content using:
[skill:<name>]
<skill body>
[/skill]Then original user text follows.
Behavior:
- Injection runs only when
Content.role == "user". - Query text is extracted from text parts and joined with newlines.
- Only the top match is injected.
- Injected body is truncated to
max_injected_chars.
APIs:
select_skill_prompt_block(...)apply_skill_injection(...)SkillInjector/SkillInjectorConfigSkillInjector::build_plugin(...)SkillInjector::build_plugin_manager(...)
§Quick Start
§Load and Match Skills
use adk_skill::{SelectionPolicy, load_skill_index, select_skills};
let index = load_skill_index(".")?;
let policy = SelectionPolicy {
top_k: 1,
min_score: 0.1,
include_tags: vec![],
exclude_tags: vec![],
};
let matches = select_skills(&index, "find TODO markers in code", &policy);
for m in matches {
println!("{} ({:.2})", m.skill.name, m.score);
}§Inject Into User Content
use adk_core::Content;
use adk_skill::{SelectionPolicy, apply_skill_injection, load_skill_index};
let index = load_skill_index(".")?;
let policy = SelectionPolicy { min_score: 0.1, ..SelectionPolicy::default() };
let mut content = Content::new("user").with_text("Search this repository for TODO markers");
let matched = apply_skill_injection(&mut content, &index, &policy, 1500);
if let Some(m) = matched {
println!("Injected skill: {}", m.skill.name);
}§Build A Plugin Manager
use adk_skill::{SkillInjector, SkillInjectorConfig};
let injector = SkillInjector::from_root(".", SkillInjectorConfig::default())?;
let plugin_manager = injector.build_plugin_manager("skills");§Error Model
Main error type: SkillError
IoYamlInvalidFrontmatter { path, message }MissingField { path, field }InvalidSkillsRoot(path)
Type alias: SkillResult<T> = Result<T, SkillError>
§Current Limits
- No embedding/vector retrieval (lexical matching only).
- No incremental file reload API yet.
- No remote catalog (
skills-ref/MCP) in this crate yet. - No script/file reference execution layer in this crate (selection + injection only).
§Related Examples
From this repository:
examples/skills_llm_minimalexamples/skills_auto_discoveryexamples/skills_policy_filtersexamples/skills_runner_injectorexamples/skills_workflow_minimal
§Development
cargo test -p adk-skill§License
Apache-2.0
Structs§
- Parsed
Skill - Selection
Policy - Skill
Document - Skill
Frontmatter - Skill
Index - Skill
Injector - Skill
Injector Config - Skill
Match - Skill
Summary
Enums§
Functions§
- apply_
skill_ injection - discover_
instruction_ files - discover_
skill_ files - load_
skill_ index - parse_
instruction_ markdown - parse_
skill_ markdown - select_
skill_ prompt_ block - select_
skills