@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.
Files changed (3) hide show
  1. package/README.md +949 -78
  2. package/dist/index.js +11877 -5440
  3. 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
- Differenator is :
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 fully_resolved_skill_name` and,
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
- ## Usage
64
+ ## Key Differences from Built-in OpenCode
22
65
 
23
- ### Example 1: Finding and Loading Skills
66
+ This plugin takes a different approach than OpenCode's built-in skills implementation:
24
67
 
25
- ```
26
- I need to write a commit message. Can you find any relevant skills and load them?
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
- 1. Use skill_find to search for "commit" or "git" related skills
29
- 2. Load the most relevant skill using skill_use
30
- 3. Apply the loaded skill guidance to write my commit message
31
- ```
76
+ ### On-Demand Skill Injection
77
+
78
+ Unlike built-in skills that load automatically, opencode-skillful uses **lazy loading**:
32
79
 
33
- **Demonstrates:**
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
- - Searching for skills by keyword
36
- - Loading skills into the chat context
37
- - Applying skill guidance to tasks
85
+ ### Provider-Model Specific Format Configuration
38
86
 
39
- ### Example 2: Browsing Available Skills
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
- 1. List all skills with skill_find "*"
45
- 2. Filter to a specific path with skill_find "experts"
46
- 3. Load a specific expert skill for deep guidance
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
- **Demonstrates:**
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
- - Listing all available skills
52
- - Path prefix filtering (e.g., "experts", "superpowers/writing")
53
- - Hierarchical skill organization
269
+ ### Format Output Examples
54
270
 
55
- ### Example 3: Advanced Search with Exclusions
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
- skill_find "testing -performance"
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
- **Demonstrates:**
297
+ **Advantages:**
64
298
 
65
- - Natural language query syntax
66
- - Negation with `-term`
67
- - AND logic for multiple terms
299
+ - Strong parsing support across LLMs
300
+ - Strict, validated structure
301
+ - Familiar format for language models trained on JSON data
68
302
 
69
- ## Features
303
+ #### Markdown Format (Human Readable)
304
+
305
+ ```markdown
306
+ # Skill
307
+
308
+ ### name
309
+
310
+ - **name**: _git-commits_
311
+
312
+ ### description
70
313
 
71
- - :mag: Discover SKILL.md files from multiple locations
72
- - :zap: Lazy loading - skills only inject when explicitly requested
73
- - :file_folder: Path prefix matching for organized skill browsing
74
- - :abc: Natural query syntax with negation and quoted phrases
75
- - :label: Skill ranking by relevance (name matches weighted higher)
76
- - :recycle: Silent message insertion (noReply pattern)
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/` - User global skills (lowest priority)
83
- 2. `~/.config/opencode/skills/` - Standard XDG config location
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
- # Load a single skill
112
- skill_use
113
- skill_names=["writing-git-commits"]
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
- # Read a resource file from a skill's directory
124
- skill_resource
125
- skill_name="brand-guidelines"
126
- relative_path="templates/logo-usage.md"
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
- ### `skill_find`
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:** Matching skills with name and fully-qualified toolName (FQDN).
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
- ### `skill_use`
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 names to load (by toolName/FQDN or short name)
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 with:
157
- - Skill resources available via `skill_resource(skillname, resourcename)`
158
- - Base directory context for relative path resolution
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
- ### `skill_resource`
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
- Read a resource file from a skill's directory and inject silently into the chat.
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
- **Behavior:** Silently injects resource content without triggering AI response (noReply pattern)
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)