Smart skill loading for OpenCode — automatic, contextual, and budget-aware
npm install 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.
---
| 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 maxTokens to set a budget, useSummaries for large skills, or useMinification to reduce token usage.
---
1. Add to opencode.json:
``json`
{
"plugin": ["opencode-plugin-preload-skills"]
}
2. Create .opencode/preload-skills.json:
`json`
{
"skills": ["coding-standards"],
"fileTypeSkills": {
".py": ["flask", "python-patterns"],
".ts,.tsx": ["typescript-patterns"]
}
}
3. Create skill files in .opencode/skills/
---
`json`
{
"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
}
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| skills | string[] | [] | Always load these skills |fileTypeSkills
| | Record | {} | Map file extensions to skills |agentSkills
| | Record | {} | Map agent names to skills |pathPatterns
| | Record | {} | Map glob patterns to skills |contentTriggers
| | Record | {} | Map keywords to skills |groups
| | Record | {} | Define skill bundles |conditionalSkills
| | ConditionalSkill[] | [] | Load if condition met |skillSettings
| | Record | {} | 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 |
---
Load skills when agent touches files with specific extensions:
`json`
{
"fileTypeSkills": {
".py": ["flask", "python-best-practices"],
".ts,.tsx": ["typescript-advanced-types"],
".go": ["golang-patterns"]
}
}
Triggers on: read, edit, write, glob, grep tools.
Load different skills for different OpenCode agents:
`json`
{
"agentSkills": {
"plan": ["architecture-planning", "task-breakdown"],
"code": ["coding-standards", "testing-patterns"],
"review": ["code-review-checklist"]
}
}
Use glob patterns to match file paths:
`json`
{
"pathPatterns": {
"src/api/**": ["api-design", "rest-patterns"],
"src/components/*/.tsx": ["react-component-patterns"],
"tests/**": ["testing-best-practices"]
}
}
Load skills when keywords appear in conversation:
`json`
{
"contentTriggers": {
"database": ["sql-patterns", "orm-usage"],
"authentication": ["auth-security", "jwt-patterns"],
"performance": ["optimization-tips"]
}
}
Bundle related skills and reference with @:
`json`
{
"groups": {
"frontend": ["react", "css", "accessibility"],
"backend": ["api-design", "database", "caching"]
},
"skills": ["@frontend"]
}
Use @frontend anywhere you'd use a skill name.
Load skills only when conditions are met:
`json`
{
"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
Limit total tokens to protect your context window:
`json`
{
"maxTokens": 8000,
"skills": ["skill-a", "skill-b", "skill-c"]
}
Skills load in order until budget is exhausted. Remaining skills are skipped.
Add a summary field to your skill frontmatter for compact loading:
`markdown`
---
name: my-skill
description: Full description
summary: Brief one-liner for summary mode
---
Enable with:
`json`
{
"useSummaries": true
}
If no summary field, auto-generates from first paragraph.
Reduce token usage by minifying skill content before injection:
`json`
{
"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:
`json`
{
"useMinification": "aggressive"
}
Transformations:
- All standard minification, plus:
- # Headers → [HEADERS] (uppercase, bracketed)bold
- and italic → plain text- item
- Code blocks → pipe-delimited single line
- Lists → pipe-delimited ( → |item)[SKILL:name]|content|[END]
- Links → text only (URLs removed)
- Skills wrapped as
Example output:
``
[SKILL:api-patterns]|[API RULES]|MANDATORY: Use REST conventions|Endpoints:|/users|/orders|[END]
Works with both systemPrompt and chatMessage injection methods.
Show a TUI toast notification whenever skills are loaded or triggered:
`json`
{
"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.
Registers a custom tool that LLM agents can call to query skill state:
`json`
{
"enableTools": true
}
Enabled by default. When the agent calls loaded_skills, it:showToasts: true
- 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 )
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.
Override global settings for specific skills:
`json`
{
"useSummaries": false,
"skillSettings": {
"large-reference": { "useSummary": true },
"critical-instructions": { "useSummary": false }
}
}
Available settings:
- useSummary — Override global useSummaries for this skill
Priority: skillSettings > useSummaries (global)
This lets you use full content for critical skills while summarizing large reference materials.
Choose where skills are injected:
`json`
{
"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
Track which skills are loaded and how often:
`json`
{
"analytics": true
}
Saves to .opencode/preload-skills-analytics.json.
---
`markdown
---
name: skill-name
description: Brief description for logs
summary: Optional one-liner for summary mode
---
Full instructions here...
`
1. .opencode/skills/ (project).claude/skills/
2. (project)~/.config/opencode/skills/
3. (global)~/.claude/skills/
4. (global)
---
`skills
┌─────────────────────────────────────────────────────────┐
│ SESSION START │
├─────────────────────────────────────────────────────────┤
│ 1. Load + 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) │
└─────────────────────────────────────────────────────────┘
---
1. Use fileTypeSkills over skills — Only load what's needed
2. Set maxTokens — Protect your context window
3. Use groups — Organize related skills
4. Enable analytics — Find unused skills
5. Write summary fields — For large skills, enable useSummariesuseMinification
6. Enable — Strip unnecessary whitespace and comments to save tokens
---
| Problem | Solution |
|---------|----------|
| Skills not loading | Check config path, skill file exists, frontmatter valid |
| Wrong skills loading | Check trigger conditions, enable debug: true |maxTokens
| Context too small | Reduce skills, set , enable useSummaries or useMinification |persistAfterCompaction: true` |
| Skills lost after compaction | Ensure
---
MIT