# How the Craft Agents Skills System Works: Implementation and Custom Instruction Storage

> Learn how the Craft Agents skills system works. Discover custom instruction storage in SKILLmd files and its three tier loading hierarchy with 5-minute caching.

- Repository: [Craft Docs/craft-agents-oss](https://github.com/lukilabs/craft-agents-oss)
- Tags: internals
- Published: 2026-04-18

---

**Custom instructions in Craft Agents are stored in [`SKILL.md`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) file located at `…/skills/{slug}/SKILL.md`. This file uses a dual-section format:

1. **YAML front-matter** containing structured metadata (name, description, globs, always-allow tools, icon, required sources)
2. **Plain-text body** containing the custom instructions that agents execute

The `parseSkillFile` function in [`packages/shared/src/skills/storage.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/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 by `GLOBAL_AGENT_SKILLS_DIR`)
- **Workspace**: `<root>/skills/{slug}/SKILL.md` (returned by `getWorkspaceSkillsPath`)
- **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`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/skills/types.ts) defines the structured fields available in the YAML front-matter:

```typescript
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`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/skills/storage.ts):

- **`loadSkill(workspaceRoot, slug)`**: Loads a single skill from the workspace directory, returning a `LoadedSkill` object or `undefined` if 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:

1. **Project-level** skills override workspace and global definitions
2. **Workspace-level** skills override global definitions
3. **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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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 absolute [`SKILL.md`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) file 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) files:

```typescript
// 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) files, located after the YAML front-matter separator (`---`). 

The `parseSkillFile` function in [`packages/shared/src/skills/storage.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/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 the `body` string 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

```typescript
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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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

```typescript
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

```typescript
// 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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

```markdown
---
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.md`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) files, parsed by `parseSkillFile` in [`packages/shared/src/skills/storage.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/skills/storage.ts).
- **Storage locations** follow a three-tier hierarchy: project (`.<project>/.agents/skills`), workspace (`<root>/skills`), and global (`~/.agents/skills`), merged by `loadAllSkills` with project taking precedence.
- **Runtime integration** occurs through `BaseAgent` in [`packages/shared/src/agent/base-agent.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/agent/base-agent.ts), which detects `[skill:slug]` mentions via `extractSkillPaths` and enforces prerequisites using `formatSkillDirective`.
- **Caching** prevents redundant disk operations by caching skill loads for 5 minutes.
- **Metadata structure** is defined by `SkillMetadata` in [`packages/shared/src/skills/types.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/skills/types.ts), supporting fields like `alwaysAllow`, `globs`, and `requiredSources`.

## 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`](https://github.com/lukilabs/craft-agents-oss/blob/main/SKILL.md) files, located after the YAML front-matter. The `parseSkillFile` function in [`packages/shared/src/skills/storage.ts`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/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`](https://github.com/lukilabs/craft-agents-oss/blob/main/packages/shared/src/agent/base-agent.ts) processes these mentions through the `extractSkillPaths` method, which returns a map of slugs to absolute [`SKILL.md`](https://github.com/lukilabs/craft-agents-oss/blob/main/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.