@zenobius/opencode-skillful 1.1.0 → 1.2.0-next.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +949 -78
- package/dist/index.js +11877 -5440
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,12 +2,55 @@
|
|
|
2
2
|
|
|
3
3
|
An interpretation of the [Anthropic Agent Skills Specification](https://github.com/anthropics/skills) for OpenCode, providing lazy-loaded skill discovery and injection.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Differentiators include:
|
|
6
6
|
|
|
7
7
|
- Conversationally the agent uses `skill_find words, words words` to discover skills
|
|
8
|
-
- The agent uses `skill_use
|
|
8
|
+
- The agent uses `skill_use "skill_name"` and,
|
|
9
9
|
- The agent can use `skill_resource skill_relative/resource/path` to read reference material
|
|
10
10
|
|
|
11
|
+
## Table of Contents
|
|
12
|
+
|
|
13
|
+
- [Quick Start](#quick-start) - Get started in 2 minutes
|
|
14
|
+
- [Installation](#installation) - Set up the plugin
|
|
15
|
+
- [Key Differences from Built-in OpenCode](#key-differences-from-built-in-opencode) - Why choose opencode-skillful
|
|
16
|
+
- [Three Core Tools](#three-core-tools) - Overview of skill_find, skill_use, skill_resource
|
|
17
|
+
- [Running Skill Scripts](#running-skill-scripts) - How agents execute skill scripts
|
|
18
|
+
- [Usage Examples](#usage-examples) - Real-world scenarios
|
|
19
|
+
- [Plugin Tools](#plugin-tools) - Detailed tool documentation
|
|
20
|
+
- [Configuration](#configuration) - Advanced setup
|
|
21
|
+
- [Architecture](#architecture) - How it works internally
|
|
22
|
+
- [Creating Skills](#creating-skills) - Build your own skills
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
Get up and running with three common tasks:
|
|
27
|
+
|
|
28
|
+
### 1. Find Skills by Keyword
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
skill_find "git commit"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Searches for skills related to writing git commits. Returns matching skills sorted by relevance.
|
|
35
|
+
|
|
36
|
+
### 2. Load a Skill into Your Chat
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
skill_use "experts_writing_git_commits"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Loads the skill into your chat context. The AI agent can now reference it when giving advice.
|
|
43
|
+
|
|
44
|
+
### 3. Read a Skill's Reference Document
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
skill_resource skill_name="experts_writing_git_commits" relative_path="references/guide.md"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Access specific documentation or templates from a skill without loading the entire skill.
|
|
51
|
+
|
|
52
|
+
**Next steps:** See [Usage Examples](#usage-examples) for real-world scenarios, or jump to [Plugin Tools](#plugin-tools) for detailed documentation.
|
|
53
|
+
|
|
11
54
|
## Installation
|
|
12
55
|
|
|
13
56
|
Create or edit your OpenCode configuration file (typically `~/.config/opencode/config.json`):
|
|
@@ -18,70 +61,275 @@ Create or edit your OpenCode configuration file (typically `~/.config/opencode/c
|
|
|
18
61
|
}
|
|
19
62
|
```
|
|
20
63
|
|
|
21
|
-
##
|
|
64
|
+
## Key Differences from Built-in OpenCode
|
|
22
65
|
|
|
23
|
-
|
|
66
|
+
This plugin takes a different approach than OpenCode's built-in skills implementation:
|
|
24
67
|
|
|
25
|
-
|
|
26
|
-
|
|
68
|
+
| Aspect | Built-in OpenCode | opencode-skillful |
|
|
69
|
+
| ------------------------ | ----------------------------------------------- | --------------------------------------------- |
|
|
70
|
+
| **Skill Loading** | All skills pre-loaded into context by default | Skills loaded on-demand only |
|
|
71
|
+
| **Memory Overhead** | All skills consume tokens in every conversation | Only loaded skills consume tokens |
|
|
72
|
+
| **Format Configuration** | Fixed format (usually markdown) | Per-model configuration (JSON, XML, Markdown) |
|
|
73
|
+
| **Skill Discovery** | Limited, built-in set | Extensible, custom skills in any directory |
|
|
74
|
+
| **Resource Access** | Direct filesystem (less secure) | Pre-indexed resources (security-first) |
|
|
27
75
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```
|
|
76
|
+
### On-Demand Skill Injection
|
|
77
|
+
|
|
78
|
+
Unlike built-in skills that load automatically, opencode-skillful uses **lazy loading**:
|
|
32
79
|
|
|
33
|
-
|
|
80
|
+
- Skills are discovered at initialization but not injected until you explicitly request them
|
|
81
|
+
- Only the skills you use consume tokens in your conversation
|
|
82
|
+
- Reduces context bloat and improves efficiency with large skill libraries
|
|
83
|
+
- Perfect for workflows with 50+ skills where you might use 2-3 per conversation
|
|
34
84
|
|
|
35
|
-
-
|
|
36
|
-
- Loading skills into the chat context
|
|
37
|
-
- Applying skill guidance to tasks
|
|
85
|
+
### Provider-Model Specific Format Configuration
|
|
38
86
|
|
|
39
|
-
|
|
87
|
+
Different LLM providers prefer different formats. Configure which format each model receives:
|
|
40
88
|
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"promptRenderer": "xml",
|
|
92
|
+
"modelRenderers": {
|
|
93
|
+
"claude-3-5-sonnet": "xml",
|
|
94
|
+
"gpt-4": "json",
|
|
95
|
+
"gpt-4-turbo": "json",
|
|
96
|
+
"llama-2-70b": "md"
|
|
97
|
+
}
|
|
98
|
+
}
|
|
41
99
|
```
|
|
42
|
-
What skills are available? Show me everything under the "experts" category.
|
|
43
100
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
101
|
+
This allows you to optimize skill injection for each model without creating duplicate skill documentation.
|
|
102
|
+
|
|
103
|
+
## Running Skill Scripts
|
|
104
|
+
|
|
105
|
+
Skills can include executable scripts in their `scripts/` directory. When a skill is loaded with `skill_use`, the agent receives the full inventory of available scripts and can be instructed to run them.
|
|
106
|
+
|
|
107
|
+
**How it works:**
|
|
108
|
+
|
|
109
|
+
1. Skills reference scripts in their SKILL.md like: `./scripts/setup.sh`
|
|
110
|
+
2. When you load a skill with `skill_use`, the agent sees all available scripts in the resource inventory
|
|
111
|
+
3. You instruct the agent: "Run the setup script from this skill"
|
|
112
|
+
4. The agent determines the script path and executes it
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
|
|
116
|
+
Your skill's SKILL.md includes: `./scripts/generate-changelog.sh`
|
|
117
|
+
|
|
118
|
+
You ask: "Can you run the changelog generator from the build-utils skill?"
|
|
119
|
+
|
|
120
|
+
The agent:
|
|
121
|
+
|
|
122
|
+
- Loads the skill with `skill_use`
|
|
123
|
+
- Sees the script in the resource inventory
|
|
124
|
+
- Determines the path: `scripts/generate-changelog.sh`
|
|
125
|
+
- Runs the script with appropriate context
|
|
126
|
+
|
|
127
|
+
**Why this approach?**
|
|
128
|
+
|
|
129
|
+
Rather than a dedicated script execution tool, agents have full visibility into all skill resources and can intelligently decide when and how to run them based on your instructions and the task context. Scripts are referenced naturally in skill documentation (e.g., "Run `./scripts/setup.sh` to initialize") and agents can work out the paths and execution from context.
|
|
130
|
+
|
|
131
|
+
## Three Core Tools
|
|
132
|
+
|
|
133
|
+
The plugin provides three simple but powerful tools:
|
|
134
|
+
|
|
135
|
+
| Tool | Purpose | When to Use |
|
|
136
|
+
| ------------------ | ------------------------------- | -------------------------------------------------- |
|
|
137
|
+
| **skill_find** | Discover skills by keyword | You want to search for relevant skills |
|
|
138
|
+
| **skill_use** | Load skills into chat | You want the AI to reference a skill |
|
|
139
|
+
| **skill_resource** | Read specific files from skills | You need a template, guide, or script from a skill |
|
|
140
|
+
|
|
141
|
+
See [Plugin Tools](#plugin-tools) for complete documentation.
|
|
142
|
+
|
|
143
|
+
## Usage Examples
|
|
144
|
+
|
|
145
|
+
These real-world scenarios show how to use the three tools together:
|
|
146
|
+
|
|
147
|
+
### Scenario 1: Writing Better Git Commits
|
|
148
|
+
|
|
149
|
+
**You want:** The AI to help you write a commit message following best practices.
|
|
150
|
+
|
|
151
|
+
**Steps:**
|
|
152
|
+
|
|
153
|
+
1. Search for relevant skills:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
skill_find "git commit"
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
2. Load the skill into your chat:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
skill_use "experts_writing_git_commits"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
3. Ask the AI: "Help me write a commit message for refactoring the auth module"
|
|
166
|
+
|
|
167
|
+
The AI now has the skill's guidance and can apply best practices to your request.
|
|
168
|
+
|
|
169
|
+
### Scenario 2: Finding and Exploring a Specific Skill's Resources
|
|
170
|
+
|
|
171
|
+
**You want:** Access a specific template or guide from a skill without loading the entire skill.
|
|
172
|
+
|
|
173
|
+
**Steps:**
|
|
174
|
+
|
|
175
|
+
1. Find skills in a category:
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
skill_find "testing"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
2. Once you've identified a skill, read its resources:
|
|
182
|
+
```
|
|
183
|
+
skill_resource skill_name="testing-skill" relative_path="references/test-template.md"
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
This is useful when you just need a template or specific document, not full AI guidance.
|
|
187
|
+
|
|
188
|
+
### Scenario 3: Browsing and Discovering Skills
|
|
189
|
+
|
|
190
|
+
**You want:** See what skills are available under a specific category.
|
|
191
|
+
|
|
192
|
+
**Steps:**
|
|
193
|
+
|
|
194
|
+
1. List all skills:
|
|
195
|
+
|
|
196
|
+
```
|
|
197
|
+
skill_find "*"
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
2. Filter by category:
|
|
201
|
+
|
|
202
|
+
```
|
|
203
|
+
skill_find "experts"
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
3. Search with exclusions:
|
|
207
|
+
```
|
|
208
|
+
skill_find "testing -performance"
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
This searches for testing-related skills but excludes performance testing.
|
|
212
|
+
|
|
213
|
+
### Query Syntax Quick Reference
|
|
214
|
+
|
|
215
|
+
- `*` or empty: List all skills
|
|
216
|
+
- `keyword1 keyword2`: AND logic (all terms must match)
|
|
217
|
+
- `-term`: Exclude results matching this term
|
|
218
|
+
- `"exact phrase"`: Match exact phrase
|
|
219
|
+
- `experts`, `superpowers/writing`: Path prefix matching
|
|
220
|
+
|
|
221
|
+
## Features
|
|
222
|
+
|
|
223
|
+
- Discover SKILL.md files from multiple locations
|
|
224
|
+
- Lazy loading - skills only inject when explicitly requested
|
|
225
|
+
- Path prefix matching for organized skill browsing
|
|
226
|
+
- Natural query syntax with negation and quoted phrases
|
|
227
|
+
- Skill ranking by relevance (name matches weighted higher)
|
|
228
|
+
- Silent message insertion (noReply pattern)
|
|
229
|
+
- **Pluggable prompt rendering** with model-aware format selection (XML, JSON, Markdown)
|
|
230
|
+
|
|
231
|
+
## Prompt Renderer Configuration
|
|
232
|
+
|
|
233
|
+
The plugin supports **multiple formats for prompt injection**, allowing you to optimize results for different LLM models and use cases.
|
|
234
|
+
|
|
235
|
+
> See the [Configuration](#configuration) section for complete configuration details, including bunfig setup and global/project-level overrides.
|
|
236
|
+
|
|
237
|
+
### Supported Formats
|
|
238
|
+
|
|
239
|
+
Choose the format that works best for your LLM:
|
|
240
|
+
|
|
241
|
+
| Format | Best For | Characteristics |
|
|
242
|
+
| ----------------- | ---------------------- | ---------------------------------------------------------------------- |
|
|
243
|
+
| **XML** (default) | Claude models | Human-readable, structured, XML-optimized for Claude |
|
|
244
|
+
| **JSON** | GPT and strict parsers | Machine-readable, strict JSON structure, strong parsing support |
|
|
245
|
+
| **Markdown** | All models | Readable prose, heading-based structure, easy to read in conversations |
|
|
246
|
+
|
|
247
|
+
### Configuration Syntax
|
|
248
|
+
|
|
249
|
+
Set your preferences in `.opencode-skillful.json`:
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"promptRenderer": "xml",
|
|
254
|
+
"modelRenderers": {
|
|
255
|
+
"claude-3-5-sonnet": "xml",
|
|
256
|
+
"gpt-4": "json",
|
|
257
|
+
"gpt-4-turbo": "json"
|
|
258
|
+
}
|
|
259
|
+
}
|
|
47
260
|
```
|
|
48
261
|
|
|
49
|
-
**
|
|
262
|
+
**How It Works:**
|
|
263
|
+
|
|
264
|
+
1. Every tool execution checks the current active LLM model
|
|
265
|
+
2. If `modelRenderers[modelID]` is configured, that format is used
|
|
266
|
+
3. Otherwise, the global `promptRenderer` default is used
|
|
267
|
+
4. Results are rendered in the selected format and injected into the prompt
|
|
50
268
|
|
|
51
|
-
|
|
52
|
-
- Path prefix filtering (e.g., "experts", "superpowers/writing")
|
|
53
|
-
- Hierarchical skill organization
|
|
269
|
+
### Format Output Examples
|
|
54
270
|
|
|
55
|
-
|
|
271
|
+
#### XML Format (Claude Optimized)
|
|
56
272
|
|
|
273
|
+
```xml
|
|
274
|
+
<Skill>
|
|
275
|
+
<name>git-commits</name>
|
|
276
|
+
<description>Guidelines for writing effective git commit messages</description>
|
|
277
|
+
<toolName>writing_git_commits</toolName>
|
|
278
|
+
</Skill>
|
|
57
279
|
```
|
|
58
|
-
Find testing-related skills but exclude anything about performance testing.
|
|
59
280
|
|
|
60
|
-
|
|
281
|
+
**Advantages:**
|
|
282
|
+
|
|
283
|
+
- Matches Claude's native instruction format
|
|
284
|
+
- Clear tag-based structure
|
|
285
|
+
- Excellent readability for complex nested data
|
|
286
|
+
|
|
287
|
+
#### JSON Format (GPT Optimized)
|
|
288
|
+
|
|
289
|
+
```json
|
|
290
|
+
{
|
|
291
|
+
"name": "git-commits",
|
|
292
|
+
"description": "Guidelines for writing effective git commit messages",
|
|
293
|
+
"toolName": "writing_git_commits"
|
|
294
|
+
}
|
|
61
295
|
```
|
|
62
296
|
|
|
63
|
-
**
|
|
297
|
+
**Advantages:**
|
|
64
298
|
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
299
|
+
- Strong parsing support across LLMs
|
|
300
|
+
- Strict, validated structure
|
|
301
|
+
- Familiar format for language models trained on JSON data
|
|
68
302
|
|
|
69
|
-
|
|
303
|
+
#### Markdown Format (Human Readable)
|
|
304
|
+
|
|
305
|
+
```markdown
|
|
306
|
+
# Skill
|
|
307
|
+
|
|
308
|
+
### name
|
|
309
|
+
|
|
310
|
+
- **name**: _git-commits_
|
|
311
|
+
|
|
312
|
+
### description
|
|
70
313
|
|
|
71
|
-
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
|
|
314
|
+
- **description**: _Guidelines for writing effective git commit messages_
|
|
315
|
+
|
|
316
|
+
### toolName
|
|
317
|
+
|
|
318
|
+
- **toolName**: _writing_git_commits_
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Advantages:**
|
|
322
|
+
|
|
323
|
+
- Most readable in conversations
|
|
324
|
+
- Natural language-friendly
|
|
325
|
+
- Works well for exploratory workflows
|
|
77
326
|
|
|
78
327
|
## Skill Discovery Paths
|
|
79
328
|
|
|
80
329
|
Skills are discovered from these locations (in priority order, last wins on duplicates):
|
|
81
330
|
|
|
82
|
-
1. `~/.opencode/skills/` -
|
|
83
|
-
2.
|
|
84
|
-
3. `.opencode/skills/` - Project-local skills (highest priority)
|
|
331
|
+
1. `~/.config/opencode/skills/` - Standard XDG config location or (`%APPDATA%/.opencode/skills` for windows users)
|
|
332
|
+
2. `.opencode/skills/` - Project-local skills (highest priority)
|
|
85
333
|
|
|
86
334
|
## Usage in OpenCode
|
|
87
335
|
|
|
@@ -89,48 +337,71 @@ Skills are discovered from these locations (in priority order, last wins on dupl
|
|
|
89
337
|
|
|
90
338
|
```
|
|
91
339
|
# List all available skills
|
|
92
|
-
skill_find
|
|
93
|
-
query="*"
|
|
340
|
+
skill_find query="*"
|
|
94
341
|
|
|
95
342
|
# Search by keyword
|
|
96
|
-
skill_find
|
|
97
|
-
query="git commit"
|
|
98
|
-
|
|
99
|
-
# Path prefix matching
|
|
100
|
-
skill_find
|
|
101
|
-
query="experts/data-ai"
|
|
343
|
+
skill_find query="git commit"
|
|
102
344
|
|
|
103
345
|
# Exclude terms
|
|
104
|
-
skill_find
|
|
105
|
-
query="testing -performance"
|
|
346
|
+
skill_find query="testing -performance"
|
|
106
347
|
```
|
|
107
348
|
|
|
108
349
|
### Loading Skills
|
|
109
350
|
|
|
351
|
+
Skills must be loaded by their fully-qualified identifier (generated from the directory path).
|
|
352
|
+
|
|
353
|
+
The skill identifier is created by converting the directory path to a slug: directory separators and hyphens become underscores.
|
|
354
|
+
|
|
110
355
|
```
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
356
|
+
skills/
|
|
357
|
+
experts/
|
|
358
|
+
ai/
|
|
359
|
+
agentic-engineer/ # Identifier: experts_ai_agentic_engineer
|
|
360
|
+
superpowers/
|
|
361
|
+
writing/
|
|
362
|
+
code-review/ # Identifier: superpowers_writing_code_review
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
When loading skills, use the full identifier:
|
|
366
|
+
|
|
367
|
+
```
|
|
368
|
+
# Load a skill by its identifier
|
|
369
|
+
skill_use "experts_ai_agentic_engineer"
|
|
114
370
|
|
|
115
371
|
# Load multiple skills
|
|
116
|
-
skill_use
|
|
117
|
-
skill_names=["writing-git-commits", "code-review"]
|
|
372
|
+
skill_use "experts_ai_agentic_engineer", "superpowers_writing_code_review"
|
|
118
373
|
```
|
|
119
374
|
|
|
120
375
|
### Reading Skill Resources
|
|
121
376
|
|
|
122
377
|
```
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
378
|
+
|
|
379
|
+
# Read a reference document
|
|
380
|
+
|
|
381
|
+
skill_resource skill_name="writing-git-commits" relative_path="references/style-guide.md"
|
|
382
|
+
|
|
383
|
+
# Read a template
|
|
384
|
+
|
|
385
|
+
skill_resource skill_name="brand-guidelines" relative_path="assets/logo-usage.html"
|
|
386
|
+
|
|
127
387
|
```
|
|
128
388
|
|
|
389
|
+
Normally you won't need to use `skill_resource` directly, since the llm will do it for you.
|
|
390
|
+
|
|
391
|
+
When skill_use is called, it will be wrapped with instructions to the llm on
|
|
392
|
+
how to load references, assets, and scripts from the skill.
|
|
393
|
+
|
|
394
|
+
But when writing your skills, there's nothing stopping you from using `skill_resource` to be explicit.
|
|
395
|
+
|
|
396
|
+
(Just be aware that exotic file types might need special tooling to handle them properly.)
|
|
397
|
+
|
|
129
398
|
## Plugin Tools
|
|
130
399
|
|
|
131
|
-
|
|
400
|
+
The plugin provides three core tools implemented in `src/tools/`:
|
|
401
|
+
|
|
402
|
+
### `skill_find` (SkillFinder.ts)
|
|
132
403
|
|
|
133
|
-
Search for skills using natural query syntax.
|
|
404
|
+
Search for skills using natural query syntax with intelligent ranking by relevance.
|
|
134
405
|
|
|
135
406
|
**Parameters:**
|
|
136
407
|
|
|
@@ -141,33 +412,604 @@ Search for skills using natural query syntax.
|
|
|
141
412
|
- Negation: `-term` (exclude results)
|
|
142
413
|
- Quoted phrases: `"exact match"` (phrase matching)
|
|
143
414
|
|
|
144
|
-
**Returns:**
|
|
415
|
+
**Returns:**
|
|
416
|
+
|
|
417
|
+
- List of matching skills with:
|
|
418
|
+
- `skill_name`: Skill identifier (e.g., `experts_writing_git_commits`)
|
|
419
|
+
- `skill_shortname`: Directory name of the skill (e.g., `writing-git-commits`)
|
|
420
|
+
- `description`: Human-readable description
|
|
421
|
+
- If config.debug is enabled:
|
|
422
|
+
- Debug information: discovered count, parsed results, rejections, duplicates, errors
|
|
423
|
+
|
|
424
|
+
**Example Response:**
|
|
425
|
+
|
|
426
|
+
```xml
|
|
427
|
+
<SkillSearchResults query="git commit">
|
|
428
|
+
<Skills>
|
|
429
|
+
<Skill skill_name="experts_writing_git_commits" skill_shortname="writing-git-commits">
|
|
430
|
+
Guidelines for writing effective git commit messages
|
|
431
|
+
</Skill>
|
|
432
|
+
</Skills>
|
|
433
|
+
<Summary>
|
|
434
|
+
<Total>42</Total>
|
|
435
|
+
<Matches>1</Matches>
|
|
436
|
+
<Feedback>Found 1 skill matching your query</Feedback>
|
|
437
|
+
</Summary>
|
|
438
|
+
</SkillSearchResults>
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Then load it with:
|
|
145
442
|
|
|
146
|
-
|
|
443
|
+
```
|
|
444
|
+
skill_use "experts_writing_git_commits"
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### `skill_use` (SkillUser.ts)
|
|
147
448
|
|
|
148
|
-
Load one or more skills into the chat context.
|
|
449
|
+
Load one or more skills into the chat context with full resource metadata.
|
|
149
450
|
|
|
150
451
|
**Parameters:**
|
|
151
452
|
|
|
152
|
-
- `skill_names`: Array of skill
|
|
453
|
+
- `skill_names`: Array of skill identifiers to load (e.g., `experts_ai_agentic_engineer`)
|
|
153
454
|
|
|
154
455
|
**Features:**
|
|
155
456
|
|
|
156
|
-
- Injects skill metadata and content
|
|
157
|
-
- Skill
|
|
158
|
-
-
|
|
457
|
+
- Injects skill metadata and content including:
|
|
458
|
+
- Skill name and description
|
|
459
|
+
- Resource inventory:
|
|
460
|
+
- `references`: Documentation and reference files
|
|
461
|
+
- `assets`: Binary files, templates, images
|
|
462
|
+
- `scripts`: Executable scripts with paths and MIME types
|
|
159
463
|
- Full skill content formatted as Markdown for easy reading
|
|
464
|
+
- Base directory context for relative path resolution
|
|
160
465
|
|
|
161
|
-
|
|
466
|
+
**Behavior:** Silently injects skills as user messages (persists in conversation history)
|
|
467
|
+
|
|
468
|
+
**Example Response:**
|
|
469
|
+
|
|
470
|
+
```json
|
|
471
|
+
{ "loaded": ["experts/writing-git-commits"], "notFound": [] }
|
|
472
|
+
```
|
|
162
473
|
|
|
163
|
-
|
|
474
|
+
### `skill_resource` (SkillResourceReader.ts)
|
|
475
|
+
|
|
476
|
+
Read a specific resource file from a skill's directory and inject silently into the chat.
|
|
477
|
+
|
|
478
|
+
**When to Use:**
|
|
479
|
+
|
|
480
|
+
- Load specific reference documents or templates from a skill
|
|
481
|
+
- Access supporting files without loading the entire skill
|
|
482
|
+
- Retrieve examples, guides, configuration templates, or scripts
|
|
483
|
+
- Most commonly used after loading a skill with `skill_use` to access its resources
|
|
164
484
|
|
|
165
485
|
**Parameters:**
|
|
166
486
|
|
|
167
487
|
- `skill_name`: The skill containing the resource (by toolName/FQDN or short name)
|
|
168
488
|
- `relative_path`: Path to the resource relative to the skill directory
|
|
169
489
|
|
|
170
|
-
**
|
|
490
|
+
**Resource Types:**
|
|
491
|
+
|
|
492
|
+
Can read any of the three resource types:
|
|
493
|
+
|
|
494
|
+
- `references/` - Documentation, guides, and reference materials
|
|
495
|
+
- `assets/` - Templates, images, binary files, and configuration files
|
|
496
|
+
- `scripts/` - Executable scripts (shell, Python, etc.) for viewing or analysis
|
|
497
|
+
|
|
498
|
+
**Returns:**
|
|
499
|
+
|
|
500
|
+
- MIME type of the resource
|
|
501
|
+
- Success confirmation with skill name, resource path, and type
|
|
502
|
+
|
|
503
|
+
**Behavior:**
|
|
504
|
+
|
|
505
|
+
- Silently injects resource content without triggering AI response (noReply pattern)
|
|
506
|
+
- Resolves relative paths within skill directory (e.g., `references/guide.md`, `assets/template.html`, `scripts/setup.sh`)
|
|
507
|
+
- Supports any text or binary file type
|
|
508
|
+
|
|
509
|
+
**Example Responses:**
|
|
510
|
+
|
|
511
|
+
Reference document:
|
|
512
|
+
|
|
513
|
+
```
|
|
514
|
+
Load Skill Resource
|
|
515
|
+
|
|
516
|
+
skill: experts/writing-git-commits
|
|
517
|
+
resource: references/commit-guide.md
|
|
518
|
+
type: text/markdown
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
Script file:
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
Load Skill Resource
|
|
525
|
+
|
|
526
|
+
skill: build-utils
|
|
527
|
+
resource: scripts/generate-changelog.sh
|
|
528
|
+
type: text/plain
|
|
529
|
+
```
|
|
530
|
+
|
|
531
|
+
Asset file:
|
|
532
|
+
|
|
533
|
+
```
|
|
534
|
+
Load Skill Resource
|
|
535
|
+
|
|
536
|
+
skill: brand-guidelines
|
|
537
|
+
resource: assets/logo-usage.html
|
|
538
|
+
type: text/html
|
|
539
|
+
```
|
|
540
|
+
|
|
541
|
+
## Error Handling
|
|
542
|
+
|
|
543
|
+
### skill_find Errors
|
|
544
|
+
|
|
545
|
+
When a search query doesn't match any skills, the response includes feedback:
|
|
546
|
+
|
|
547
|
+
```xml
|
|
548
|
+
<SkillSearchResults query="nonexistent">
|
|
549
|
+
<Skills></Skills>
|
|
550
|
+
<Summary>
|
|
551
|
+
<Total>42</Total>
|
|
552
|
+
<Matches>0</Matches>
|
|
553
|
+
<Feedback>No skills found matching your query</Feedback>
|
|
554
|
+
</Summary>
|
|
555
|
+
</SkillSearchResults>
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### skill_use Errors
|
|
559
|
+
|
|
560
|
+
When loading skills, if a skill name is not found, it's returned in the `notFound` array:
|
|
561
|
+
|
|
562
|
+
```json
|
|
563
|
+
{ "loaded": ["writing-git-commits"], "notFound": ["nonexistent-skill"] }
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
The loaded skills are still injected into the conversation, allowing you to use available skills even if some are not found.
|
|
567
|
+
|
|
568
|
+
### skill_resource Errors
|
|
569
|
+
|
|
570
|
+
Resource loading failures occur when:
|
|
571
|
+
|
|
572
|
+
- The skill doesn't exist
|
|
573
|
+
- The resource path doesn't exist within the skill
|
|
574
|
+
- The file cannot be read due to permissions
|
|
575
|
+
|
|
576
|
+
The tool returns an error message indicating which skill or resource path was problematic.
|
|
577
|
+
|
|
578
|
+
## Configuration
|
|
579
|
+
|
|
580
|
+
The plugin loads configuration from **bunfig**, supporting both project-local and global configuration files:
|
|
581
|
+
|
|
582
|
+
### Configuration Files
|
|
583
|
+
|
|
584
|
+
Configuration is loaded in this priority order (highest priority last):
|
|
585
|
+
|
|
586
|
+
1. **Global config** (standard platform locations):
|
|
587
|
+
- Linux/macOS: `~/.config/opencode-skillful/config.json`
|
|
588
|
+
- Windows: `%APPDATA%/opencode-skillful/config.json`
|
|
589
|
+
|
|
590
|
+
2. **Project config** (in your project root):
|
|
591
|
+
- `.opencode-skillful.json`
|
|
592
|
+
|
|
593
|
+
Later configuration files override earlier ones. Use project-local `.opencode-skillful.json` to override global settings for specific projects.
|
|
594
|
+
|
|
595
|
+
### Configuration Options
|
|
596
|
+
|
|
597
|
+
#### Plugin Installation
|
|
598
|
+
|
|
599
|
+
First, register the plugin in your OpenCode config (`~/.config/opencode/config.json`):
|
|
600
|
+
|
|
601
|
+
```json
|
|
602
|
+
{
|
|
603
|
+
"plugins": ["@zenobius/opencode-skillful"]
|
|
604
|
+
}
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
#### Skill Discovery Configuration
|
|
608
|
+
|
|
609
|
+
Create `.opencode-skillful.json` in your project root or global config directory:
|
|
610
|
+
|
|
611
|
+
```json
|
|
612
|
+
{
|
|
613
|
+
"debug": false,
|
|
614
|
+
"basePaths": ["~/.config/opencode/skills", ".opencode/skills"],
|
|
615
|
+
"promptRenderer": "xml",
|
|
616
|
+
"modelRenderers": {}
|
|
617
|
+
}
|
|
618
|
+
```
|
|
619
|
+
|
|
620
|
+
**Configuration Fields:**
|
|
621
|
+
|
|
622
|
+
- **debug** (boolean, default: `false`): Enable debug output showing skill discovery stats
|
|
623
|
+
- When enabled, `skill_find` responses include discovered, parsed, rejected, and error counts
|
|
624
|
+
- Useful for diagnosing skill loading and parsing issues
|
|
625
|
+
|
|
626
|
+
- **basePaths** (array, default: standard locations): Custom skill search directories
|
|
627
|
+
- Paths are searched in priority order; later paths override earlier ones for duplicate skill names
|
|
628
|
+
- Default: `[~/.config/opencode/skills, .opencode/skills]`
|
|
629
|
+
- Use project-local `.opencode/skills/` for project-specific skills
|
|
630
|
+
- Platform-aware paths: automatically resolves to XDG, macOS, or Windows standard locations
|
|
631
|
+
|
|
632
|
+
- **promptRenderer** (string, default: `'xml'`): Default format for prompt injection
|
|
633
|
+
- Options: `'xml'` | `'json'` | `'md'`
|
|
634
|
+
- XML (default): Claude-optimized, human-readable structured format
|
|
635
|
+
- JSON: GPT-optimized, strict JSON formatting for strong parsing models
|
|
636
|
+
- Markdown: Human-readable format with headings and nested lists
|
|
637
|
+
- Used when no model-specific renderer is configured
|
|
638
|
+
|
|
639
|
+
- **modelRenderers** (object, default: `{}`): Per-model format overrides
|
|
640
|
+
- Maps model IDs to preferred formats
|
|
641
|
+
- Overrides global `promptRenderer` for specific models
|
|
642
|
+
- Example: `{ "gpt-4": "json", "claude-3-5-sonnet": "xml" }`
|
|
643
|
+
|
|
644
|
+
### How Renderer Selection Works
|
|
645
|
+
|
|
646
|
+
When any tool executes (`skill_find`, `skill_use`, `skill_resource`):
|
|
647
|
+
|
|
648
|
+
1. The plugin queries the OpenCode session to determine the active LLM model
|
|
649
|
+
2. Builds a list of model candidates to check, from most to least specific:
|
|
650
|
+
- Full model ID (e.g., `"anthropic-claude-3-5-sonnet"`)
|
|
651
|
+
- Generic model pattern (e.g., `"claude-3-5-sonnet"`)
|
|
652
|
+
3. Checks if any candidate exists in `modelRenderers` configuration
|
|
653
|
+
- First match wins (most specific takes precedence)
|
|
654
|
+
- If found, uses that format
|
|
655
|
+
4. If no match in `modelRenderers`, falls back to `promptRenderer` default
|
|
656
|
+
5. Renders the results in the selected format and injects into the prompt
|
|
657
|
+
|
|
658
|
+
**Example**: If your config has `"claude-3-5-sonnet": "xml"` and the active model is `"anthropic-claude-3-5-sonnet"`, the plugin will:
|
|
659
|
+
|
|
660
|
+
- Try matching `"anthropic-claude-3-5-sonnet"` (no match)
|
|
661
|
+
- Try matching `"claude-3-5-sonnet"` (match found! Use XML)
|
|
662
|
+
- Return `"xml"` format
|
|
663
|
+
|
|
664
|
+
This allows different models to receive results in their preferred format without needing to specify every model variant. Configure the generic model name once and it works for all provider-prefixed variations.
|
|
665
|
+
|
|
666
|
+
### Example Configurations
|
|
667
|
+
|
|
668
|
+
#### Global Configuration for Multi-Model Setup
|
|
669
|
+
|
|
670
|
+
`~/.config/opencode-skillful/config.json`:
|
|
671
|
+
|
|
672
|
+
```json
|
|
673
|
+
{
|
|
674
|
+
"debug": false,
|
|
675
|
+
"promptRenderer": "xml",
|
|
676
|
+
"modelRenderers": {
|
|
677
|
+
"claude-3-5-sonnet": "xml",
|
|
678
|
+
"claude-3-opus": "xml",
|
|
679
|
+
"gpt-4": "json",
|
|
680
|
+
"gpt-4-turbo": "json",
|
|
681
|
+
"llama-2-70b": "md"
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
#### Project-Specific Override
|
|
687
|
+
|
|
688
|
+
`.opencode-skillful.json` (project root):
|
|
689
|
+
|
|
690
|
+
```json
|
|
691
|
+
{
|
|
692
|
+
"debug": true,
|
|
693
|
+
"basePaths": ["~/.config/opencode/skills", ".opencode/skills", "./vendor/skills"],
|
|
694
|
+
"promptRenderer": "xml",
|
|
695
|
+
"modelRenderers": {
|
|
696
|
+
"gpt-4": "json"
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
```
|
|
700
|
+
|
|
701
|
+
This project-local config:
|
|
702
|
+
|
|
703
|
+
- Enables debug output for troubleshooting
|
|
704
|
+
- Adds a custom vendor skills directory
|
|
705
|
+
- Uses JSON format specifically for GPT-4 when it's the active model
|
|
706
|
+
- Falls back to XML for all other models
|
|
707
|
+
|
|
708
|
+
## Architecture
|
|
709
|
+
|
|
710
|
+
### System Design Overview
|
|
711
|
+
|
|
712
|
+
The plugin uses a **layered, modular architecture** with clear separation of concerns and two-phase initialization.
|
|
713
|
+
|
|
714
|
+
#### Design Principles
|
|
715
|
+
|
|
716
|
+
- **Async Coordination**: ReadyStateMachine ensures tools don't execute before registry initialization completes
|
|
717
|
+
- **Security-First Resource Access**: All resources are pre-indexed at parse time; no path traversal possible
|
|
718
|
+
- **Factory Pattern**: Centralized API creation for easy testing and configuration management
|
|
719
|
+
- **Lazy Loading**: Skills only injected when explicitly requested, minimal memory overhead
|
|
720
|
+
|
|
721
|
+
### System Layers
|
|
722
|
+
|
|
723
|
+
```
|
|
724
|
+
┌──────────────────────────────────────────────────────┐
|
|
725
|
+
│ Plugin Entry Point (index.ts) │
|
|
726
|
+
│ - Defines 3 core tools: skill_find, skill_use, │
|
|
727
|
+
│ skill_resource │
|
|
728
|
+
│ - NOT including skill_exec (removed) │
|
|
729
|
+
│ - Initializes API factory and config │
|
|
730
|
+
│ - Manages message injection (XML serialization) │
|
|
731
|
+
└──────────────────────────────────────────────────────┘
|
|
732
|
+
↓
|
|
733
|
+
┌──────────────────────────────────────────────────────┐
|
|
734
|
+
│ API Factory Layer (api.ts, config.ts) │
|
|
735
|
+
│ - createApi(): initializes logger, registry, tools │
|
|
736
|
+
│ - getPluginConfig(): resolves base paths with proper │
|
|
737
|
+
│ precedence (project-local overrides global) │
|
|
738
|
+
└──────────────────────────────────────────────────────┘
|
|
739
|
+
↓
|
|
740
|
+
┌──────────────────────────────────────────────────────┐
|
|
741
|
+
│ Services Layer (src/services/) │
|
|
742
|
+
│ - SkillRegistry: discovery, parsing, resource │
|
|
743
|
+
│ mapping with ReadyStateMachine coordination │
|
|
744
|
+
│ - SkillSearcher: query parsing + intelligent ranking│
|
|
745
|
+
│ - SkillResourceResolver: safe path-based retrieval │
|
|
746
|
+
│ - SkillFs (via lib): filesystem abstraction (mockable)
|
|
747
|
+
└──────────────────────────────────────────────────────┘
|
|
748
|
+
↓
|
|
749
|
+
┌──────────────────────────────────────────────────────┐
|
|
750
|
+
│ Core Libraries (src/lib/) │
|
|
751
|
+
│ - ReadyStateMachine: async initialization sequencing │
|
|
752
|
+
│ - SkillFs: abstract filesystem operations │
|
|
753
|
+
│ - Identifiers: path ↔ tool name conversions │
|
|
754
|
+
│ - OpenCodeChat: message injection abstraction │
|
|
755
|
+
│ - xml.ts: JSON → XML serialization │
|
|
756
|
+
└──────────────────────────────────────────────────────┘
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Initialization Flow (Why This Matters)
|
|
760
|
+
|
|
761
|
+
The plugin uses a **two-phase initialization pattern** to coordinate async discovery with tool execution:
|
|
762
|
+
|
|
763
|
+
```
|
|
764
|
+
PHASE 1: SYNCHRONOUS CREATION
|
|
765
|
+
├─ Plugin loads configuration
|
|
766
|
+
├─ createApi() called:
|
|
767
|
+
│ ├─ Creates logger
|
|
768
|
+
│ ├─ Creates SkillRegistry (factory, NOT initialized yet)
|
|
769
|
+
│ └─ Returns registry + tool creators
|
|
770
|
+
└─ index.ts registers tools immediately
|
|
771
|
+
|
|
772
|
+
PHASE 2: ASYNCHRONOUS DISCOVERY (Background)
|
|
773
|
+
├─ registry.initialise() called
|
|
774
|
+
│ ├─ Sets ready state to "loading"
|
|
775
|
+
│ ├─ Scans all base paths for SKILL.md files
|
|
776
|
+
│ ├─ Parses each file (YAML frontmatter + resources)
|
|
777
|
+
│ ├─ Pre-indexes all resources (scripts/assets/references)
|
|
778
|
+
│ └─ Sets ready state to "ready"
|
|
779
|
+
│
|
|
780
|
+
└─ WHY PRE-INDEXING: Prevents path traversal attacks.
|
|
781
|
+
Tools can only retrieve pre-registered resources,
|
|
782
|
+
never arbitrary filesystem paths.
|
|
783
|
+
|
|
784
|
+
PHASE 3: TOOL EXECUTION (User Requested)
|
|
785
|
+
├─ User calls: skill_find("git commits")
|
|
786
|
+
├─ Tool executes:
|
|
787
|
+
│ ├─ await registry.controller.ready.whenReady()
|
|
788
|
+
│ │ (blocks until Phase 2 completes)
|
|
789
|
+
│ ├─ Search registry
|
|
790
|
+
│ └─ Return results
|
|
791
|
+
└─ WHY THIS PATTERN: Multiple tools can call whenReady()
|
|
792
|
+
at different times without race conditions.
|
|
793
|
+
Simple Promise would resolve once; this allows
|
|
794
|
+
concurrent waiters.
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
### Data Flow Diagram: skill_find Query
|
|
798
|
+
|
|
799
|
+
```
|
|
800
|
+
┌─────────────────────────────────────────┐
|
|
801
|
+
│ User Query: skill_find("git commit") │
|
|
802
|
+
└────────────┬────────────────────────────┘
|
|
803
|
+
↓
|
|
804
|
+
┌─────────────────────────────────────────┐
|
|
805
|
+
│ SkillFinder Tool (tools/SkillFinder.ts) │
|
|
806
|
+
│ - Validates query string │
|
|
807
|
+
│ - Awaits: controller.ready.whenReady() │
|
|
808
|
+
└────────────┬────────────────────────────┘
|
|
809
|
+
↓ (continues when registry ready)
|
|
810
|
+
┌─────────────────────────────────────────┐
|
|
811
|
+
│ SkillSearcher.search() │
|
|
812
|
+
│ - Parse query via search-string library │
|
|
813
|
+
│ ("git commit" → include: [git,commit])│
|
|
814
|
+
│ - Filter ALL include terms must match │
|
|
815
|
+
│ (name, description, or toolName) │
|
|
816
|
+
│ - Exclude results matching exclude │
|
|
817
|
+
│ terms │
|
|
818
|
+
│ - Rank results: │
|
|
819
|
+
│ * nameMatches × 3 (strong signal) │
|
|
820
|
+
│ * descMatches × 1 (weak signal) │
|
|
821
|
+
│ * exact match bonus +10 │
|
|
822
|
+
└────────────┬────────────────────────────┘
|
|
823
|
+
↓
|
|
824
|
+
┌─────────────────────────────────────────┐
|
|
825
|
+
│ Results with Feedback │
|
|
826
|
+
│ - Matched skills array │
|
|
827
|
+
│ - Sorted by relevance score │
|
|
828
|
+
│ - User-friendly feedback message │
|
|
829
|
+
│ "Searching for: **git commit** | │
|
|
830
|
+
│ Found 3 matches" │
|
|
831
|
+
└────────────┬────────────────────────────┘
|
|
832
|
+
↓
|
|
833
|
+
┌─────────────────────────────────────────┐
|
|
834
|
+
│ Format & Return (index.ts) │
|
|
835
|
+
│ - Convert to XML via jsonToXml() │
|
|
836
|
+
│ - Inject into message context │
|
|
837
|
+
│ - Return to user │
|
|
838
|
+
└─────────────────────────────────────────┘
|
|
839
|
+
```
|
|
840
|
+
|
|
841
|
+
### Data Flow Diagram: skill_use Loading
|
|
842
|
+
|
|
843
|
+
```
|
|
844
|
+
┌──────────────────────────────────┐
|
|
845
|
+
│ User: skill_use("git-commits") │
|
|
846
|
+
└────────────┬─────────────────────┘
|
|
847
|
+
↓
|
|
848
|
+
┌──────────────────────────────────┐
|
|
849
|
+
│ SkillUser Tool (tools/SkillUser) │
|
|
850
|
+
│ - Await ready state │
|
|
851
|
+
│ - Validate skill names │
|
|
852
|
+
└────────────┬─────────────────────┘
|
|
853
|
+
↓
|
|
854
|
+
┌──────────────────────────────────┐
|
|
855
|
+
│ Registry.controller.get(key) │
|
|
856
|
+
│ - Look up skill in Map │
|
|
857
|
+
│ - Return Skill object with: │
|
|
858
|
+
│ * Name, description │
|
|
859
|
+
│ * Content (markdown) │
|
|
860
|
+
│ * Resource maps (indexed) │
|
|
861
|
+
└────────────┬─────────────────────┘
|
|
862
|
+
↓
|
|
863
|
+
┌──────────────────────────────────┐
|
|
864
|
+
│ Format Skill for Injection │
|
|
865
|
+
│ - Skill metadata │
|
|
866
|
+
│ - Resource inventory (with MIME) │
|
|
867
|
+
│ - Full content as markdown │
|
|
868
|
+
│ - Base directory context │
|
|
869
|
+
└────────────┬─────────────────────┘
|
|
870
|
+
↓
|
|
871
|
+
┌──────────────────────────────────┐
|
|
872
|
+
│ Silent Injection Pattern │
|
|
873
|
+
│ - Inject as user message │
|
|
874
|
+
│ - Persists in conversation │
|
|
875
|
+
│ - Returns success summary │
|
|
876
|
+
│ { loaded: [...], notFound: []}│
|
|
877
|
+
└──────────────────────────────────┘
|
|
878
|
+
```
|
|
879
|
+
|
|
880
|
+
### Data Flow Diagram: skill_resource Access
|
|
881
|
+
|
|
882
|
+
```
|
|
883
|
+
┌──────────────────────────────────────────────┐
|
|
884
|
+
│ User: skill_resource("git-commits", │
|
|
885
|
+
│ "scripts/commit.sh") │
|
|
886
|
+
└──────────────┬───────────────────────────────┘
|
|
887
|
+
↓
|
|
888
|
+
┌──────────────────────────────────────────────┐
|
|
889
|
+
│ SkillResourceReader Tool │
|
|
890
|
+
│ (tools/SkillResourceReader.ts) │
|
|
891
|
+
│ - Validate skill_name │
|
|
892
|
+
│ - Parse path: "scripts/commit.sh" │
|
|
893
|
+
│ ├─ type = "scripts" │
|
|
894
|
+
│ └─ relative_path = "commit.sh" │
|
|
895
|
+
│ - Assert type is valid (scripts|assets|refs)│
|
|
896
|
+
└──────────────┬───────────────────────────────┘
|
|
897
|
+
↓
|
|
898
|
+
┌──────────────────────────────────────────────┐
|
|
899
|
+
│ SkillResourceResolver │
|
|
900
|
+
│ - Fetch skill object from registry │
|
|
901
|
+
│ - Look up in skill.scripts Map │
|
|
902
|
+
│ (pre-indexed at parse time) │
|
|
903
|
+
│ - Retrieve: { absolutePath, mimeType } │
|
|
904
|
+
│ │
|
|
905
|
+
│ ★ SECURITY: Only pre-indexed paths exist │
|
|
906
|
+
│ No way to request ../../../etc/passwd │
|
|
907
|
+
└──────────────┬───────────────────────────────┘
|
|
908
|
+
↓
|
|
909
|
+
┌──────────────────────────────────────────────┐
|
|
910
|
+
│ File I/O (SkillFs abstraction) │
|
|
911
|
+
│ - Read file content from absolutePath │
|
|
912
|
+
│ - Detect MIME type (e.g., text/plain) │
|
|
913
|
+
└──────────────┬───────────────────────────────┘
|
|
914
|
+
↓
|
|
915
|
+
┌──────────────────────────────────────────────┐
|
|
916
|
+
│ Return Injection Object │
|
|
917
|
+
│ { │
|
|
918
|
+
│ skill_name, │
|
|
919
|
+
│ resource_path, │
|
|
920
|
+
│ resource_mimetype, │
|
|
921
|
+
│ content │
|
|
922
|
+
│ } │
|
|
923
|
+
└──────────────┬───────────────────────────────┘
|
|
924
|
+
↓
|
|
925
|
+
┌──────────────────────────────────────────────┐
|
|
926
|
+
│ Silent Injection │
|
|
927
|
+
│ - Inject content into message │
|
|
928
|
+
│ - User sees resource inline │
|
|
929
|
+
└──────────────────────────────────────────────┘
|
|
930
|
+
```
|
|
931
|
+
|
|
932
|
+
### Key Design Decisions and Their Rationale
|
|
933
|
+
|
|
934
|
+
| Decision | Why | Impact |
|
|
935
|
+
| ---------------------------- | -------------------------------------------------- | ------------------------------------------------------------ |
|
|
936
|
+
| **ReadyStateMachine** | Ensures tools don't race with async registry setup | Tools are guaranteed registry is ready before execution |
|
|
937
|
+
| **Pre-indexed Resources** | Prevents path traversal attacks | Security: only pre-registered paths retrievable |
|
|
938
|
+
| **Factory Pattern (api.ts)** | Centralizes initialization | Easy to test, swap implementations, mock components |
|
|
939
|
+
| **Removed SkillProvider** | Eliminated unnecessary abstraction | Simpler code, direct registry access, easier debugging |
|
|
940
|
+
| **XML Serialization** | Human-readable message injection | Results display nicely formatted in chat context |
|
|
941
|
+
| **Two-phase Init** | Async discovery doesn't block tool registration | Tools available immediately, discovery happens in background |
|
|
942
|
+
|
|
943
|
+
### Services Layer Breakdown
|
|
944
|
+
|
|
945
|
+
#### SkillRegistry (`src/services/SkillRegistry.ts`)
|
|
946
|
+
|
|
947
|
+
- **Role**: Central skill catalog and discovery engine
|
|
948
|
+
- **Responsibilities**:
|
|
949
|
+
1. Scan multiple base paths for SKILL.md files (recursive)
|
|
950
|
+
2. Parse each file's YAML frontmatter (validates schema)
|
|
951
|
+
3. Index all resources (scripts/assets/references) for safe retrieval
|
|
952
|
+
4. Register skills in controller.skills Map by toolName
|
|
953
|
+
5. Coordinate initialization via ReadyStateMachine
|
|
954
|
+
- **Key Methods**:
|
|
955
|
+
- `initialise()`: Async discovery and parsing pipeline
|
|
956
|
+
- `register()`: Parse and store skill files
|
|
957
|
+
- `parseSkill()`: Extract metadata and resource paths
|
|
958
|
+
- **Error Handling**: Malformed skills logged but don't halt discovery
|
|
959
|
+
|
|
960
|
+
#### SkillSearcher (`src/services/SkillSearcher.ts`)
|
|
961
|
+
|
|
962
|
+
- **Role**: Natural language query interpretation and ranking
|
|
963
|
+
- **Responsibilities**:
|
|
964
|
+
1. Parse queries using search-string library (Gmail syntax)
|
|
965
|
+
2. Filter: ALL include terms must match (AND logic)
|
|
966
|
+
3. Exclude: Remove results matching exclude terms
|
|
967
|
+
4. Rank by relevance (name matches 3×, description 1×, exact +10)
|
|
968
|
+
5. Generate user-friendly feedback
|
|
969
|
+
- **Scoring Formula**: `(nameMatches × 3) + (descMatches × 1) + exactBonus`
|
|
970
|
+
- **Edge Cases**: Empty queries or "\*" list all skills
|
|
971
|
+
|
|
972
|
+
### Tools Layer Overview
|
|
973
|
+
|
|
974
|
+
#### SkillFinder (`src/tools/SkillFinder.ts`)
|
|
975
|
+
|
|
976
|
+
- Wraps SkillSearcher with ready-state synchronization
|
|
977
|
+
- Transforms results to SkillFinder schema
|
|
978
|
+
- Returns matched skills + summary metadata
|
|
979
|
+
|
|
980
|
+
#### SkillUser (`src/tools/SkillUser.ts`)
|
|
981
|
+
|
|
982
|
+
- Loads skills into chat context
|
|
983
|
+
- Injects as user message (persists in conversation)
|
|
984
|
+
- Returns { loaded: [...], notFound: [...] }
|
|
985
|
+
|
|
986
|
+
#### SkillResourceReader (`src/tools/SkillResourceReader.ts`)
|
|
987
|
+
|
|
988
|
+
- Safe resource path parsing and validation
|
|
989
|
+
- Pre-indexed path lookup (prevents traversal)
|
|
990
|
+
- Returns injection object with MIME type and content
|
|
991
|
+
|
|
992
|
+
### Configuration and Path Resolution
|
|
993
|
+
|
|
994
|
+
```
|
|
995
|
+
Configuration Priority (Last Wins):
|
|
996
|
+
1. Global: ~/.opencode/skills/ (lowest)
|
|
997
|
+
2. Project: ./.opencode/skills/ (highest)
|
|
998
|
+
|
|
999
|
+
Why This Order:
|
|
1000
|
+
- Users install global skills once
|
|
1001
|
+
- Projects override with local versions
|
|
1002
|
+
- Same skill name in both → project version wins
|
|
1003
|
+
```
|
|
1004
|
+
|
|
1005
|
+
## Contributing
|
|
1006
|
+
|
|
1007
|
+
Contributions are welcome! When adding features, follow these principles:
|
|
1008
|
+
|
|
1009
|
+
- **Explain the WHY, not the WHAT**: Code comments should document design decisions, not mechanics
|
|
1010
|
+
- **Keep modules focused**: Each file has one primary responsibility
|
|
1011
|
+
- **Test async behavior**: Ready state coordination is critical
|
|
1012
|
+
- **Document algorithms**: Ranking, parsing, and search logic should have detailed comments
|
|
171
1013
|
|
|
172
1014
|
## Creating Skills
|
|
173
1015
|
|
|
@@ -181,18 +1023,28 @@ skills/
|
|
|
181
1023
|
template.md
|
|
182
1024
|
```
|
|
183
1025
|
|
|
1026
|
+
### Skill Structure
|
|
1027
|
+
|
|
1028
|
+
```
|
|
1029
|
+
my-skill/
|
|
1030
|
+
SKILL.md # Required: Skill metadata and instructions
|
|
1031
|
+
references/ # Optional: Documentation and guides
|
|
1032
|
+
guide.md
|
|
1033
|
+
examples.md
|
|
1034
|
+
assets/ # Optional: Binary files and templates
|
|
1035
|
+
template.html
|
|
1036
|
+
logo.png
|
|
1037
|
+
scripts/ # Optional: Executable scripts
|
|
1038
|
+
setup.sh
|
|
1039
|
+
generate.py
|
|
1040
|
+
```
|
|
1041
|
+
|
|
184
1042
|
### SKILL.md Format
|
|
185
1043
|
|
|
186
1044
|
```yaml
|
|
187
1045
|
---
|
|
188
1046
|
name: my-skill
|
|
189
1047
|
description: A brief description of what this skill does (min 20 chars)
|
|
190
|
-
license: MIT
|
|
191
|
-
allowed-tools:
|
|
192
|
-
- bash
|
|
193
|
-
- read
|
|
194
|
-
metadata:
|
|
195
|
-
author: Your Name
|
|
196
1048
|
---
|
|
197
1049
|
# My Skill
|
|
198
1050
|
|
|
@@ -202,9 +1054,28 @@ Instructions for the AI agent when this skill is loaded...
|
|
|
202
1054
|
**Requirements:**
|
|
203
1055
|
|
|
204
1056
|
- `name` must match the directory name (lowercase, alphanumeric, hyphens only)
|
|
205
|
-
- `description` must be at least 20 characters
|
|
1057
|
+
- `description` must be at least 20 characters and should be concise (under 500 characters recommended)
|
|
1058
|
+
- Focus on when to use the skill, not what it does
|
|
1059
|
+
- Include specific triggering conditions and symptoms
|
|
1060
|
+
- Use third person for system prompt injection
|
|
1061
|
+
- Example: "Use when designing REST APIs to establish consistent patterns and improve developer experience"
|
|
206
1062
|
- Directory name and frontmatter `name` must match exactly
|
|
207
1063
|
|
|
1064
|
+
### Resource Types
|
|
1065
|
+
|
|
1066
|
+
Skill resources are automatically discovered and categorized:
|
|
1067
|
+
|
|
1068
|
+
- **References** (`references/` directory): Documentation, guides, and reference materials
|
|
1069
|
+
- Typically Markdown, plaintext, or HTML files
|
|
1070
|
+
- Accessed via `skill_resource` for reading documentation
|
|
1071
|
+
- **Assets** (`assets/` directory): Templates, images, and binary files
|
|
1072
|
+
- Can include HTML templates, images, configuration files
|
|
1073
|
+
- Useful for providing templates and examples to the AI
|
|
1074
|
+
- **Scripts** (`scripts/` directory): Executable scripts that perform actions
|
|
1075
|
+
- Shell scripts (.sh), Python scripts (.py), or other executables
|
|
1076
|
+
- Can be accessed via `skill_resource` for reading as files
|
|
1077
|
+
- Useful for providing automation scripts, code generation, or templates
|
|
1078
|
+
|
|
208
1079
|
## Considerations
|
|
209
1080
|
|
|
210
1081
|
- Skills are discovered at plugin initialization (requires restart to reload)
|