opencode-plugin-preload-skills
v1.8.0
Published
Smart skill loading for OpenCode — automatic, contextual, and budget-aware
Maintainers
Readme
opencode-plugin-preload-skills
Smart skill loading for OpenCode — automatic, contextual, and budget-aware
A powerful plugin for OpenCode that intelligently loads skills based on context — file types, directory patterns, agent type, conversation content, and more.
Features
| Feature | Description |
|---------|-------------|
| Always-On Skills | Load skills at session start |
| File-Type Triggers | Load skills when touching .py, .ts, etc. |
| Agent-Specific | Different skills for different agents |
| Path Patterns | Glob patterns like src/api/** |
| Content Triggers | Keywords in conversation trigger skills |
| Skill Groups | Bundle skills together with @group-name |
| Conditional Loading | Load only if dependency exists |
| Token Budget | Cap total skill tokens to protect context |
| Summaries Mode | Load compact summaries instead of full content |
| Content Minification | Minify skill content before injection to save tokens |
| System Prompt Injection | Inject skills into system prompt instead of messages |
| Toast Notifications | Show TUI toast when skills are loaded |
| loaded_skills Tool | LLM agent can query loaded skills (also shows toast to user) |
| Usage Analytics | Track which skills are actually used |
⚠️ Warning: Preloaded skills consume context window tokens. Use
maxTokensto set a budget,useSummariesfor large skills, oruseMinificationto reduce token usage.
Quick Start
1. Add to opencode.json:
{
"plugin": ["opencode-plugin-preload-skills"]
}2. Create .opencode/preload-skills.json:
{
"skills": ["coding-standards"],
"fileTypeSkills": {
".py": ["flask", "python-patterns"],
".ts,.tsx": ["typescript-patterns"]
}
}3. Create skill files in .opencode/skills/<name>/SKILL.md
Configuration Reference
All Options
{
"skills": ["always-loaded-skill"],
"fileTypeSkills": {
".py": ["flask"],
".ts,.tsx": ["typescript"]
},
"agentSkills": {
"plan": ["planning-skill"],
"code": ["coding-skill"]
},
"pathPatterns": {
"src/api/**": ["api-design"],
"src/components/**": ["react-patterns"]
},
"contentTriggers": {
"database": ["sql-patterns"],
"authentication": ["auth-security"]
},
"groups": {
"frontend": ["react", "css", "testing"],
"backend": ["api-design", "database"]
},
"conditionalSkills": [
{ "skill": "react", "if": { "packageHasDependency": "react" } },
{ "skill": "prisma", "if": { "fileExists": "prisma/schema.prisma" } }
],
"skillSettings": {
"large-skill": { "useSummary": true },
"critical-skill": { "useSummary": false }
},
"injectionMethod": "systemPrompt",
"maxTokens": 10000,
"useSummaries": false,
"useMinification": false,
"showToasts": false,
"enableTools": true,
"analytics": false,
"persistAfterCompaction": true,
"debug": false
}Options Table
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| skills | string[] | [] | Always load these skills |
| fileTypeSkills | Record<string, string[]> | {} | Map file extensions to skills |
| agentSkills | Record<string, string[]> | {} | Map agent names to skills |
| pathPatterns | Record<string, string[]> | {} | Map glob patterns to skills |
| contentTriggers | Record<string, string[]> | {} | Map keywords to skills |
| groups | Record<string, string[]> | {} | Define skill bundles |
| conditionalSkills | ConditionalSkill[] | [] | Load if condition met |
| skillSettings | Record<string, SkillSettings> | {} | Per-skill settings |
| injectionMethod | "chatMessage" \| "systemPrompt" | "systemPrompt" | Where to inject skills |
| maxTokens | number | undefined | Max tokens for all skills |
| useSummaries | boolean | false | Use skill summaries (global) |
| useMinification | boolean \| "standard" \| "aggressive" | false | Minify skill content (true/"standard" or "aggressive") |
| showToasts | boolean | false | Show TUI toast notifications when skills are loaded |
| enableTools | boolean | true | Register loaded_skills tool for LLM agents |
| analytics | boolean | false | Track skill usage |
| persistAfterCompaction | boolean | true | Keep skills after compaction |
| debug | boolean | false | Enable debug logs |
Feature Details
File-Type Skills
Load skills when agent touches files with specific extensions:
{
"fileTypeSkills": {
".py": ["flask", "python-best-practices"],
".ts,.tsx": ["typescript-advanced-types"],
".go": ["golang-patterns"]
}
}Triggers on: read, edit, write, glob, grep tools.
Agent-Specific Skills
Load different skills for different OpenCode agents:
{
"agentSkills": {
"plan": ["architecture-planning", "task-breakdown"],
"code": ["coding-standards", "testing-patterns"],
"review": ["code-review-checklist"]
}
}Path Patterns
Use glob patterns to match file paths:
{
"pathPatterns": {
"src/api/**": ["api-design", "rest-patterns"],
"src/components/**/*.tsx": ["react-component-patterns"],
"tests/**": ["testing-best-practices"]
}
}Content Triggers
Load skills when keywords appear in conversation:
{
"contentTriggers": {
"database": ["sql-patterns", "orm-usage"],
"authentication": ["auth-security", "jwt-patterns"],
"performance": ["optimization-tips"]
}
}Skill Groups
Bundle related skills and reference with @:
{
"groups": {
"frontend": ["react", "css", "accessibility"],
"backend": ["api-design", "database", "caching"]
},
"skills": ["@frontend"]
}Use @frontend anywhere you'd use a skill name.
Conditional Skills
Load skills only when conditions are met:
{
"conditionalSkills": [
{
"skill": "react-patterns",
"if": { "packageHasDependency": "react" }
},
{
"skill": "prisma-guide",
"if": { "fileExists": "prisma/schema.prisma" }
},
{
"skill": "ci-patterns",
"if": { "envVar": "CI" }
}
]
}Condition types:
packageHasDependency— Check package.json dependenciesfileExists— Check if file exists in projectenvVar— Check if environment variable is set
Token Budget
Limit total tokens to protect your context window:
{
"maxTokens": 8000,
"skills": ["skill-a", "skill-b", "skill-c"]
}Skills load in order until budget is exhausted. Remaining skills are skipped.
Skill Summaries
Add a summary field to your skill frontmatter for compact loading:
---
name: my-skill
description: Full description
summary: Brief one-liner for summary mode
---Enable with:
{
"useSummaries": true
}If no summary field, auto-generates from first paragraph.
Content Minification
Reduce token usage by minifying skill content before injection:
{
"useMinification": true
}Minification levels:
| Value | Description |
|-------|-------------|
| true or "standard" | Standard minification (safe, ~20% reduction) |
| "aggressive" | Vercel-style compression (~50%+ reduction) |
Standard minification (true or "standard"):
- HTML/markdown comments removed
- Frontmatter stripped
- Multiple blank lines collapsed
- Whitespace normalized
Aggressive minification ("aggressive"):
Inspired by Vercel's AGENTS.md research, this mode achieves maximum compression:
{
"useMinification": "aggressive"
}Transformations:
- All standard minification, plus:
# Headers→[HEADERS](uppercase, bracketed)**bold**and*italic*→ plain text- Code blocks → pipe-delimited single line
- Lists → pipe-delimited (
- item→|item) - Links → text only (URLs removed)
- Skills wrapped as
[SKILL:name]|content|[END]
Example output:
[SKILL:api-patterns]|[API RULES]|MANDATORY: Use REST conventions|Endpoints:|/users|/orders|[END]Works with both systemPrompt and chatMessage injection methods.
Toast Notifications
Show a TUI toast notification whenever skills are loaded or triggered:
{
"showToasts": true
}Toasts appear for:
- Initial skills — when session-start skills are first injected
- Triggered skills — when file-type, path, agent, or content triggers load new skills
Each toast displays the skill names and how many were loaded, e.g. Loaded 2 skills: react, typescript or Triggered skill: api-design.
loaded_skills Tool
Registers a custom tool that LLM agents can call to query skill state:
{
"enableTools": true
}Enabled by default. When the agent calls loaded_skills, it:
- Returns a list of all loaded skills with names, descriptions, and token counts
- Shows a toast notification to the user with the same info (requires
showToasts: true)
Ask the agent "what skills are loaded?" and it will use this tool — you'll see the answer both in the conversation and as a toast. Disable with "enableTools": false.
Per-Skill Settings
Override global settings for specific skills:
{
"useSummaries": false,
"skillSettings": {
"large-reference": { "useSummary": true },
"critical-instructions": { "useSummary": false }
}
}Available settings:
useSummary— Override globaluseSummariesfor this skill
Priority: skillSettings > useSummaries (global)
This lets you use full content for critical skills while summarizing large reference materials.
Injection Method
Choose where skills are injected:
{
"injectionMethod": "chatMessage"
}Methods:
| Method | Description | Use Case |
|--------|-------------|----------|
| systemPrompt (default) | Injects into system prompt via experimental.chat.system.transform hook | Persistent across all LLM calls, invisible to user |
| chatMessage | Injects skills into user messages | One-time injection, visible in conversation |
System prompt injection benefits (default):
- File-triggered skills available on next LLM step (same turn)
- Skills persist automatically (no need for
persistAfterCompaction) - Cleaner conversation history (skills not visible in messages)
Chat message injection benefits:
- Skills visible in conversation for debugging
- Works with older OpenCode versions
- More control over when skills appear
Usage Analytics
Track which skills are loaded and how often:
{
"analytics": true
}Saves to .opencode/preload-skills-analytics.json.
Skill File Format
---
name: skill-name
description: Brief description for logs
summary: Optional one-liner for summary mode
---
# Skill Content
Full instructions here...Locations (in priority order)
.opencode/skills/<name>/SKILL.md(project).claude/skills/<name>/SKILL.md(project)~/.config/opencode/skills/<name>/SKILL.md(global)~/.claude/skills/<name>/SKILL.md(global)
How It Works
┌─────────────────────────────────────────────────────────┐
│ SESSION START │
├─────────────────────────────────────────────────────────┤
│ 1. Load `skills` + `conditionalSkills` (if met) │
│ 2. Apply token budget if set │
│ 3. Inject on first message │
├─────────────────────────────────────────────────────────┤
│ DURING SESSION │
├─────────────────────────────────────────────────────────┤
│ On file access: │
│ → Check fileTypeSkills (by extension) │
│ → Check pathPatterns (by glob match) │
│ │
│ On message: │
│ → Check agentSkills (by agent name) │
│ → Check contentTriggers (by keyword) │
│ → Inject any pending skills │
├─────────────────────────────────────────────────────────┤
│ COMPACTION │
├─────────────────────────────────────────────────────────┤
│ All loaded skills added to compaction context │
│ (if persistAfterCompaction: true) │
└─────────────────────────────────────────────────────────┘Best Practices
- Use
fileTypeSkillsoverskills— Only load what's needed - Set
maxTokens— Protect your context window - Use
groups— Organize related skills - Enable
analytics— Find unused skills - Write
summaryfields — For large skills, enableuseSummaries - Enable
useMinification— Strip unnecessary whitespace and comments to save tokens
Troubleshooting
| Problem | Solution |
|---------|----------|
| Skills not loading | Check config path, skill file exists, frontmatter valid |
| Wrong skills loading | Check trigger conditions, enable debug: true |
| Context too small | Reduce skills, set maxTokens, enable useSummaries or useMinification |
| Skills lost after compaction | Ensure persistAfterCompaction: true |
License
MIT
