How the Craft Agents Skills System Works: Implementation and Custom Instruction Storage
Custom instructions in Craft Agents are stored in SKILL.md files within skill directories, and the system implements a three-tier loading hierarchy (project > workspace > global) with 5-minute caching.
The skills system in lukilabs/craft-agents-oss provides a modular way to extend agent capabilities through declarative configuration files. Each skill encapsulates custom instructions, metadata, and tool permissions that agents load dynamically at runtime.
Skill Definition and Directory Structure
The SKILL.md File Format
Every skill requires a SKILL.md file located at …/skills/{slug}/SKILL.md. This file uses a dual-section format:
- YAML front-matter containing structured metadata (name, description, globs, always-allow tools, icon, required sources)
- Plain-text body containing the custom instructions that agents execute
The parseSkillFile function in packages/shared/src/skills/storage.ts handles this parsing, stripping the front-matter and returning the body as the instruction text【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L68-L90】.
Directory Locations: Global, Workspace, and Project Levels
The system searches for skills in three distinct locations, merged in priority order:
- Global:
~/.agents/skills/{slug}/SKILL.md(defined byGLOBAL_AGENT_SKILLS_DIR) - Workspace:
<root>/skills/{slug}/SKILL.md(returned bygetWorkspaceSkillsPath) - Project:
<projectRoot>/.agents/skills/{slug}/SKILL.md
The loadAllSkills function implements the merge logic, giving priority to project over workspace over global configurations【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L16-L30】.
Metadata Model and Type Definitions
The SkillMetadata interface in packages/shared/src/skills/types.ts defines the structured fields available in the YAML front-matter:
interface SkillMetadata {
name: string;
description?: string;
globs?: string[]; // File patterns the skill applies to
alwaysAllow?: string[]; // Tools always permitted for this skill
icon?: string;
requiredSources?: string[]; // Required data sources
}
This metadata drives the LoadedSkill type, which combines the metadata with the instruction body and file system path【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/types.ts#L9-L28】.
Loading and Caching Skills
loadSkill and loadAllSkills Functions
The storage layer exposes two primary loading functions in packages/shared/src/skills/storage.ts:
-
loadSkill(workspaceRoot, slug): Loads a single skill from the workspace directory, returning aLoadedSkillobject orundefinedif not found【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L78-L81】. -
loadAllSkills(workspaceRoot, projectRoot): Loads and merges skills from all three tiers (global, workspace, project), implementing the priority hierarchy【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L16-L30】.
Priority and Merge Strategy
When loading all skills, the system resolves conflicts using a cascading priority:
- Project-level skills override workspace and global definitions
- Workspace-level skills override global definitions
- Global skills serve as the base layer
This merge strategy ensures that project-specific customizations take precedence while maintaining shared defaults at the workspace or user level.
To optimize performance, results are cached for 5 minutes to avoid repeated disk scans across the three directory locations【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L92-L104】.
Runtime Skill Discovery and Prerequisite Enforcement
Parsing Skill Mentions in Chat
When users reference skills in messages using the [skill:slug] syntax, the BaseAgent class in packages/shared/src/agent/base-agent.ts handles detection and resolution:
The extractSkillPaths method parses the user message, identifies all skill mentions, resolves them against loaded skills, and returns:
- A
Map<string, string>mapping skill slugs to absoluteSKILL.mdfile paths - The cleaned message with mentions removed【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/agent/base-agent.ts#L82-L106】
The PrerequisiteManager and Read Directives
Before executing any tools, the system enforces that the agent has read the required skill instructions. The formatSkillDirective method generates a directive instructing the model to read specific SKILL.md files:
// From base-agent.ts
private formatSkillDirective(skillPaths: Map<string, string>): string {
// Generates: "Please read the following files before proceeding: ..."
}
The PrerequisiteManager registers these files as dependencies that must be satisfied before tool execution, ensuring the model has context from all referenced skills【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/agent/base-agent.ts#L42-L48】.
Where Custom Instructions Are Stored
Custom instructions reside in the body section of SKILL.md files, located after the YAML front-matter separator (---).
The parseSkillFile function in packages/shared/src/skills/storage.ts handles the extraction:
- Parses the file content to separate metadata from instructions
- Returns the front-matter as structured
SkillMetadata - Returns everything below the second
---as thebodystring containing the raw instructions【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L68-L90】
The agent does not store these instructions in memory or copy them to other locations; it reads them on-demand via the Read tool or cat command when the prerequisite directive instructs the model to do so.
Implementation Example: Loading Skills in Practice
Loading a Single Workspace Skill
import { loadSkill } from '@craft-agent/shared/skills/storage';
// workspaceRoot is the absolute path to your workspace
const slug = 'my-awesome-skill';
const skill = loadSkill(workspaceRoot, slug);
if (skill) {
console.log('Skill metadata:', skill.metadata);
console.log('Instruction body:', skill.content);
}
Source: loadSkill in storage.ts【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L78-L81】
Loading All Skills with Merge Priority
import { loadAllSkills } from '@craft-agent/shared/skills/storage';
const all = loadAllSkills(workspaceRoot, projectRoot);
all.forEach(s => console.log(`${s.slug}: ${s.metadata.name}`));
Source: loadAllSkills implementation【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/skills/storage.ts#L16-L30】
Agent Message Processing with Skill Directives
// Inside a subclass of BaseAgent, the chat() entry point runs:
const { skillPaths, cleanMessage } = this.extractSkillPaths(userMessage);
// skillPaths is a Map<string, string>(slug → absolute SKILL.md path)
const directive = this.formatSkillDirective(skillPaths);
const effectiveMessage = `${directive}\n\n${cleanMessage}`;
// The backend then sends `effectiveMessage` to the LLM.
Source: extractSkillPaths and formatSkillDirective in base-agent.ts【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/agent/base-agent.ts#L82-L106】【/cache/repos/github.com/lukilabs/craft-agents-oss/main/packages/shared/src/agent/base-agent.ts#L42-L48】
Sample SKILL.md File Structure
---
name: "Git Helper"
description: "Provides git commit, branch, and diff utilities."
globs: ["src/**/*.ts"]
alwaysAllow: ["Read", "Write"]
icon: "🔧"
requiredSources: ["git"]
---
You are a git assistant. When invoked, you should:
1. List the current branch.
2. Show the diff of staged changes.
3. Offer to commit with a conventional commit message.
The front-matter populates SkillMetadata; everything below the --- separator becomes the instruction body that the model reads at runtime.
Summary
- Custom instructions reside in the body of
SKILL.mdfiles, parsed byparseSkillFileinpackages/shared/src/skills/storage.ts. - Storage locations follow a three-tier hierarchy: project (
.<project>/.agents/skills), workspace (<root>/skills), and global (~/.agents/skills), merged byloadAllSkillswith project taking precedence. - Runtime integration occurs through
BaseAgentinpackages/shared/src/agent/base-agent.ts, which detects[skill:slug]mentions viaextractSkillPathsand enforces prerequisites usingformatSkillDirective. - Caching prevents redundant disk operations by caching skill loads for 5 minutes.
- Metadata structure is defined by
SkillMetadatainpackages/shared/src/skills/types.ts, supporting fields likealwaysAllow,globs, andrequiredSources.
Frequently Asked Questions
Where are custom instructions stored in the Craft Agents skills system?
Custom instructions are stored in the body section of SKILL.md files, located after the YAML front-matter. The parseSkillFile function in packages/shared/src/skills/storage.ts extracts this content by separating the metadata from the instruction text, returning the body as a raw string that agents read at runtime.
How does the skill loading priority work when multiple locations define the same skill?
The loadAllSkills function implements a cascading merge strategy where project-level skills override workspace and global definitions, and workspace-level skills override global ones. Specifically, the system loads from ~/.agents/skills (global), <root>/skills (workspace), and .<project>/.agents/skills (project), merging them in that order with later sources taking precedence.
What is the SKILL.md file format and what metadata does it support?
SKILL.md uses a dual-section format with YAML front-matter followed by free-form instructions. The front-matter conforms to the SkillMetadata interface defined in packages/shared/src/skills/types.ts, supporting fields including name, description, globs (file patterns), alwaysAllow (permitted tools), icon, and requiredSources. Everything below the second --- separator becomes the instruction body.
How do I reference a skill in a chat message and how does the agent process it?
Users reference skills using the [skill:slug] syntax in chat messages. The BaseAgent class in packages/shared/src/agent/base-agent.ts processes these mentions through the extractSkillPaths method, which returns a map of slugs to absolute SKILL.md paths. The agent then uses formatSkillDirective to generate a prerequisite instruction forcing the model to read those files before executing any tools, ensuring the custom instructions are available in context.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →