opencode-dynamic-skills 1.0.0

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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +180 -0
  3. package/README.zh-CN.md +180 -0
  4. package/dist/api.d.ts +96 -0
  5. package/dist/commands/SlashCommand.d.ts +26 -0
  6. package/dist/commands/SlashCommand.test.d.ts +1 -0
  7. package/dist/config.d.ts +47 -0
  8. package/dist/config.test.d.ts +1 -0
  9. package/dist/index.d.ts +29 -0
  10. package/dist/index.js +23805 -0
  11. package/dist/lib/Identifiers.d.ts +17 -0
  12. package/dist/lib/Indentifiers.test.d.ts +1 -0
  13. package/dist/lib/OpenCodeChat.d.ts +5 -0
  14. package/dist/lib/OpenCodeChat.test.d.ts +1 -0
  15. package/dist/lib/ReadyStateMachine.d.ts +36 -0
  16. package/dist/lib/SkillFs.d.ts +30 -0
  17. package/dist/lib/createPromptRenderer.d.ts +52 -0
  18. package/dist/lib/createPromptRenderer.test.d.ts +9 -0
  19. package/dist/lib/getModelFormat.d.ts +35 -0
  20. package/dist/lib/renderers/JsonPromptRenderer.d.ts +8 -0
  21. package/dist/lib/renderers/JsonPromptRenderer.test.d.ts +10 -0
  22. package/dist/lib/renderers/MdPromptRenderer.d.ts +18 -0
  23. package/dist/lib/renderers/MdPromptRenderer.test.d.ts +11 -0
  24. package/dist/lib/renderers/XmlPromptRenderer.d.ts +9 -0
  25. package/dist/lib/renderers/XmlPromptRenderer.test.d.ts +11 -0
  26. package/dist/lib/renderers/resourceMapToArray.d.ts +2 -0
  27. package/dist/lib/xml.d.ts +1 -0
  28. package/dist/mocks.d.ts +32 -0
  29. package/dist/mocks.skillfs.d.ts +1 -0
  30. package/dist/services/MessageModelIdAccountant.d.ts +22 -0
  31. package/dist/services/SkillRegistry.d.ts +9 -0
  32. package/dist/services/SkillRegistry.test.d.ts +1 -0
  33. package/dist/services/SkillResourceResolver.d.ts +20 -0
  34. package/dist/services/SkillResourceResolver.test.d.ts +1 -0
  35. package/dist/services/SkillSearcher.d.ts +77 -0
  36. package/dist/services/SkillSearcher.functions.test.d.ts +1 -0
  37. package/dist/services/SkillSearcher.test.d.ts +1 -0
  38. package/dist/services/logger.d.ts +2 -0
  39. package/dist/tools/SkillFinder.d.ts +46 -0
  40. package/dist/tools/SkillRecommender.d.ts +25 -0
  41. package/dist/tools/SkillRecommender.test.d.ts +1 -0
  42. package/dist/tools/SkillResourceReader.d.ts +43 -0
  43. package/dist/tools/SkillUser.d.ts +5 -0
  44. package/dist/types.d.ts +211 -0
  45. package/package.json +56 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 zenobi.us
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,180 @@
1
+ # OpenCode Dynamic Skills
2
+
3
+ [中文文档](./README.zh-CN.md)
4
+
5
+ OpenCode plugin focused on three things:
6
+
7
+ 1. Dynamic skill loading
8
+ 2. Slash-style skill usage
9
+ 3. Skill-root-relative file references
10
+
11
+ ## Status
12
+
13
+ This project is a reboot of the archived `opencode-skillful` codebase.
14
+
15
+ - Package: `opencode-dynamic-skills`
16
+ - Version: `1.0.0`
17
+ - License: MIT
18
+
19
+ ## Attribution
20
+
21
+ This project is derived from and adapted from:
22
+
23
+ - https://github.com/zenobi-us/opencode-skillful
24
+
25
+ The current codebase restructures and extends that work for a fresh restart.
26
+
27
+ ## Goals
28
+
29
+ - Keep skills **lazy-loaded**, instead of injecting all skills into every session
30
+ - Support **slash commands** for skill invocation
31
+ - Resolve skill file references **relative to the skill directory root**
32
+ - Follow current OpenCode plugin / command / skill conventions
33
+
34
+ ## What ships in the current release
35
+
36
+ ### Dynamic skill tools
37
+
38
+ The plugin provides:
39
+
40
+ - `skill_find`
41
+ - `skill_recommend`
42
+ - `skill_use`
43
+ - `skill_resource`
44
+
45
+ ### Slash skill command
46
+
47
+ Default form:
48
+
49
+ ```txt
50
+ /skill <skill-id> [request]
51
+ ```
52
+
53
+ Example:
54
+
55
+ ```txt
56
+ /skill git_release draft a release checklist
57
+ ```
58
+
59
+ Recommendation form:
60
+
61
+ ```txt
62
+ /skill-recommend draft a release checklist
63
+ ```
64
+
65
+ Optional aliases are also supported:
66
+
67
+ ```txt
68
+ /git-release draft a release checklist
69
+ ```
70
+
71
+ Aliases are disabled by default to avoid collisions with native or official OpenCode commands.
72
+
73
+ ### Skill-root-relative resources
74
+
75
+ `skill_resource` now resolves paths from the skill root, for example:
76
+
77
+ ```txt
78
+ references/guide.md
79
+ scripts/setup.sh
80
+ assets/logo.svg
81
+ templates/pr.md
82
+ ```
83
+
84
+ Safety rules:
85
+
86
+ - accepts `./references/guide.md`
87
+ - accepts Windows `\` separators
88
+ - rejects absolute paths
89
+ - rejects `..` traversal
90
+
91
+ ## Installation
92
+
93
+ Register the plugin in `opencode.json`:
94
+
95
+ ```json
96
+ {
97
+ "$schema": "https://opencode.ai/config.json",
98
+ "plugin": ["opencode-dynamic-skills"]
99
+ }
100
+ ```
101
+
102
+ ## Configuration
103
+
104
+ Example:
105
+
106
+ ```json
107
+ {
108
+ "debug": false,
109
+ "promptRenderer": "xml",
110
+ "modelRenderers": {
111
+ "claude-3-5-sonnet": "xml",
112
+ "gpt-4": "json"
113
+ },
114
+ "slashCommandName": "skill",
115
+ "enableSkillAliases": false
116
+ }
117
+ ```
118
+
119
+ Fields:
120
+
121
+ - `debug`
122
+ - `basePaths`
123
+ - `promptRenderer`
124
+ - `modelRenderers`
125
+ - `slashCommandName`
126
+ - `enableSkillAliases`
127
+
128
+ ## Skill discovery
129
+
130
+ The rewrite supports current OpenCode-compatible locations:
131
+
132
+ - `.opencode/skills`
133
+ - `.claude/skills`
134
+ - `.agents/skills`
135
+ - `~/.config/opencode/skills`
136
+ - `~/.claude/skills`
137
+ - `~/.agents/skills`
138
+
139
+ Project-local paths are walked upward from the current working directory to the git worktree.
140
+
141
+ ## Skill format
142
+
143
+ Each skill must contain `SKILL.md` with valid frontmatter:
144
+
145
+ ```md
146
+ ---
147
+ name: git-release
148
+ description: Create consistent releases and changelogs
149
+ compatibility: opencode
150
+ ---
151
+
152
+ ## What I do
153
+
154
+ - Draft release notes
155
+ - Propose version bumps
156
+ ```
157
+
158
+ Rules:
159
+
160
+ - `name` is required
161
+ - `name` must match the directory name
162
+ - `name` must satisfy OpenCode naming rules
163
+ - `description` is required
164
+
165
+ ## Development
166
+
167
+ ```bash
168
+ bun install
169
+ bun run typecheck
170
+ bun run lint
171
+ bun run format:check
172
+ bun test
173
+ bun run build
174
+ ```
175
+
176
+ To format the repo:
177
+
178
+ ```bash
179
+ bun run format
180
+ ```
@@ -0,0 +1,180 @@
1
+ # OpenCode Dynamic Skills
2
+
3
+ [English README](./README.md)
4
+
5
+ 这是一个专注于三件事的 OpenCode 插件:
6
+
7
+ 1. 动态加载 skill
8
+ 2. 用 slash 方式使用 skill
9
+ 3. 让 skill 内文件引用统一基于 skill 根目录
10
+
11
+ ## 当前状态
12
+
13
+ 这是对已归档 `opencode-skillful` 仓库的重启版本。
14
+
15
+ - 包名:`opencode-dynamic-skills`
16
+ - 版本:`1.0.0`
17
+ - 协议:MIT
18
+
19
+ ## 来源说明
20
+
21
+ 本项目来自并改造于:
22
+
23
+ - https://github.com/zenobi-us/opencode-skillful
24
+
25
+ 当前版本在此基础上做了重新组织和重启开发。
26
+
27
+ ## 目标
28
+
29
+ - 保持 **按需加载**,不把所有 skill 预注入到每个会话
30
+ - 支持 **slash 方式** 调用 skill
31
+ - 让 skill 文件引用 **相对 skill 根目录** 解析
32
+ - 对齐当前 OpenCode 的 plugin / command / skill 约定
33
+
34
+ ## 当前版本已完成内容
35
+
36
+ ### 动态技能工具
37
+
38
+ 插件提供:
39
+
40
+ - `skill_find`
41
+ - `skill_recommend`
42
+ - `skill_use`
43
+ - `skill_resource`
44
+
45
+ ### Slash 技能命令
46
+
47
+ 默认形式:
48
+
49
+ ```txt
50
+ /skill <skill-id> [请求]
51
+ ```
52
+
53
+ 示例:
54
+
55
+ ```txt
56
+ /skill git_release 帮我起草 release checklist
57
+ ```
58
+
59
+ 推荐形式:
60
+
61
+ ```txt
62
+ /skill-recommend 帮我推荐适合 release checklist 的 skill
63
+ ```
64
+
65
+ 也支持可选 alias:
66
+
67
+ ```txt
68
+ /git-release 帮我起草 release checklist
69
+ ```
70
+
71
+ alias 默认关闭,避免与 OpenCode 内置命令或官方 commands 冲突。
72
+
73
+ ### Skill 根目录相对资源
74
+
75
+ `skill_resource` 现在按 skill 根目录解析路径,例如:
76
+
77
+ ```txt
78
+ references/guide.md
79
+ scripts/setup.sh
80
+ assets/logo.svg
81
+ templates/pr.md
82
+ ```
83
+
84
+ 安全规则:
85
+
86
+ - 支持 `./references/guide.md`
87
+ - 支持 Windows `\` 分隔符
88
+ - 拒绝绝对路径
89
+ - 拒绝 `..` 越界
90
+
91
+ ## 安装
92
+
93
+ 在 `opencode.json` 中注册插件:
94
+
95
+ ```json
96
+ {
97
+ "$schema": "https://opencode.ai/config.json",
98
+ "plugin": ["opencode-dynamic-skills"]
99
+ }
100
+ ```
101
+
102
+ ## 配置
103
+
104
+ 示例:
105
+
106
+ ```json
107
+ {
108
+ "debug": false,
109
+ "promptRenderer": "xml",
110
+ "modelRenderers": {
111
+ "claude-3-5-sonnet": "xml",
112
+ "gpt-4": "json"
113
+ },
114
+ "slashCommandName": "skill",
115
+ "enableSkillAliases": false
116
+ }
117
+ ```
118
+
119
+ 字段:
120
+
121
+ - `debug`
122
+ - `basePaths`
123
+ - `promptRenderer`
124
+ - `modelRenderers`
125
+ - `slashCommandName`
126
+ - `enableSkillAliases`
127
+
128
+ ## Skill 发现路径
129
+
130
+ 当前重构支持这些 OpenCode 兼容位置:
131
+
132
+ - `.opencode/skills`
133
+ - `.claude/skills`
134
+ - `.agents/skills`
135
+ - `~/.config/opencode/skills`
136
+ - `~/.claude/skills`
137
+ - `~/.agents/skills`
138
+
139
+ 项目本地路径会从当前目录向上遍历到 git worktree。
140
+
141
+ ## Skill 格式
142
+
143
+ 每个 skill 都必须包含 `SKILL.md`,frontmatter 示例:
144
+
145
+ ```md
146
+ ---
147
+ name: git-release
148
+ description: Create consistent releases and changelogs
149
+ compatibility: opencode
150
+ ---
151
+
152
+ ## What I do
153
+
154
+ - Draft release notes
155
+ - Propose version bumps
156
+ ```
157
+
158
+ 规则:
159
+
160
+ - `name` 必填
161
+ - `name` 必须和目录名一致
162
+ - `name` 必须符合 OpenCode 命名规则
163
+ - `description` 必填
164
+
165
+ ## 开发
166
+
167
+ ```bash
168
+ bun install
169
+ bun run typecheck
170
+ bun run lint
171
+ bun run format:check
172
+ bun test
173
+ bun run build
174
+ ```
175
+
176
+ 格式化:
177
+
178
+ ```bash
179
+ bun run format
180
+ ```
package/dist/api.d.ts ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * API Factory - Plugin Initialization and Tool Creation
3
+ *
4
+ * WHY: Centralizes the creation of all plugin components (logger, registry, tools, renderers)
5
+ * in one place. Makes it easy to test different configurations, mock components,
6
+ * and understand the complete initialization flow.
7
+ *
8
+ * RESPONSIBILITY: This is the only place that knows how to wire together:
9
+ * - Logger (for debug output)
10
+ * - SkillRegistry (for discovery and parsing)
11
+ * - Tool creators (functions that create skill_find, skill_use, skill_resource)
12
+ * - PromptRenderer (for format selection and rendering)
13
+ *
14
+ * INITIALIZATION TIMING (CRITICAL):
15
+ * - createLogger(): synchronous, immediate
16
+ * - createSkillRegistry(): synchronous factory call (returns a SkillRegistry object)
17
+ * - createPromptRenderer(): synchronous, immediate (format selection at runtime)
18
+ * - registry.initialise(): NOT called here, caller must do this separately
19
+ *
20
+ * WHY NOT CALL initialise(): The caller (index.ts) needs to await initialise()
21
+ * and handle errors. We can't do that in the factory without making it more complex.
22
+ *
23
+ * RETURN VALUE: Object with:
24
+ * - registry: SkillRegistry instance (must call .initialise() before use)
25
+ * - logger: PluginLogger for debug output
26
+ * - config: PluginConfig (needed for model-aware format selection)
27
+ * - findSkills: Tool creator function for skill search
28
+ * - readResource: Tool creator function for resource reading
29
+ * - loadSkill: Tool creator function for skill loading
30
+ *
31
+ * EXAMPLE:
32
+ * const api = await createApi(config);
33
+ * const { registry, config, findSkills, readResource, loadSkill } = api;
34
+ * // Note: registry is created but NOT yet initialized
35
+ * // Must be done by caller: await registry.initialise()
36
+ */
37
+ import type { PluginConfig } from './types';
38
+ export declare const createApi: (config: PluginConfig) => Promise<{
39
+ registry: import("./types").SkillRegistry;
40
+ logger: import("./types").PluginLogger;
41
+ config: PluginConfig;
42
+ findSkills: (args: {
43
+ query: string | string[];
44
+ }) => Promise<{
45
+ query: string | string[];
46
+ skills: {
47
+ name: string;
48
+ description: string;
49
+ }[];
50
+ summary: {
51
+ total: number;
52
+ matches: number;
53
+ feedback: string;
54
+ };
55
+ debug: import("./types").SkillRegistryDebugInfo | undefined;
56
+ }>;
57
+ recommendSkills: (args: {
58
+ task: string;
59
+ limit?: number;
60
+ }) => Promise<{
61
+ mode: "recommend";
62
+ query: string;
63
+ skills: {
64
+ name: string;
65
+ description: string;
66
+ }[];
67
+ recommendations: {
68
+ name: string;
69
+ description: string;
70
+ score: number;
71
+ reason: string;
72
+ }[];
73
+ guidance: string;
74
+ summary: {
75
+ total: number;
76
+ matches: number;
77
+ feedback: string;
78
+ };
79
+ debug: import("./types").SkillRegistryDebugInfo | undefined;
80
+ }>;
81
+ readResource: (args: {
82
+ skill_name: string;
83
+ relative_path: string;
84
+ }) => Promise<{
85
+ injection: {
86
+ skill_name: string;
87
+ resource_path: string;
88
+ resource_mimetype: string;
89
+ content: string;
90
+ };
91
+ }>;
92
+ loadSkill: (skillNames: string[]) => Promise<{
93
+ loaded: import("./types").Skill[];
94
+ notFound: string[];
95
+ }>;
96
+ }>;
@@ -0,0 +1,26 @@
1
+ import type { Skill, SkillRegistry } from '../types';
2
+ export declare const SLASH_COMMAND_SENTINEL = "<!-- opencode-dynamic-skills:slash-expanded -->";
3
+ export declare const RECOMMEND_COMMAND_SENTINEL = "<!-- opencode-dynamic-skills:skill-recommend-expanded -->";
4
+ type ParsedSlashCommand = {
5
+ invocationName: string;
6
+ skillSelector: string;
7
+ userPrompt: string;
8
+ };
9
+ export declare function parseSlashCommand(text: string, slashCommandName: string): ParsedSlashCommand | null;
10
+ export declare function findSkillBySelector(registry: SkillRegistry, selector: string): Skill | null;
11
+ export declare function renderSlashSkillPrompt(args: {
12
+ invocationName: string;
13
+ renderedSkill: string;
14
+ skill: Skill;
15
+ userPrompt: string;
16
+ }): string;
17
+ export declare function renderRecommendSlashPrompt(task: string): string;
18
+ export declare function rewriteRecommendSlashCommandText(text: string): Promise<string | null>;
19
+ export declare function rewriteSlashCommandText(args: {
20
+ text: string;
21
+ registry: SkillRegistry;
22
+ renderSkill: (skill: Skill) => string;
23
+ slashCommandName: string;
24
+ enableSkillAliases: boolean;
25
+ }): Promise<string | null>;
26
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,47 @@
1
+ import type { PluginInput } from '@opencode-ai/plugin';
2
+ import type { PluginConfig } from './types';
3
+ /**
4
+ * Gets OpenCode-compatible config paths for the current platform.
5
+ *
6
+ * Matches OpenCode's path resolution logic from internal/config/config.go:
7
+ * - Uses XDG_CONFIG_HOME if set
8
+ * - Falls back to LOCALAPPDATA on Windows, ~/.config on Unix
9
+ * - Also includes ~/.opencode/ as an alternative
10
+ *
11
+ * @returns Array of config directory paths in priority order (lowest to highest)
12
+ */
13
+ export declare function getOpenCodeConfigPaths(): string[];
14
+ /**
15
+ * Expands tilde (~) in a path to the user's home directory.
16
+ *
17
+ * WHY: When users configure paths in JSON config files, shell expansion
18
+ * doesn't happen automatically. Paths like "~/my-skills" remain literal
19
+ * strings, causing file operations to fail.
20
+ *
21
+ * HANDLES:
22
+ * - "~" alone → home directory
23
+ * - "~/path" → home directory + path
24
+ * - Other paths → unchanged
25
+ *
26
+ * @param path - The path string to expand
27
+ * @returns The expanded path with ~ resolved to home directory
28
+ */
29
+ export declare function expandTildePath(path: string): string;
30
+ /**
31
+ * Resolve a configured base path to an absolute path.
32
+ *
33
+ * Resolution rules:
34
+ * - "~" / "~/..." are expanded to the user home directory
35
+ * - Absolute paths are preserved
36
+ * - Relative paths are resolved from the project directory
37
+ */
38
+ export declare function resolveBasePath(basePath: string, projectDirectory: string): string;
39
+ /**
40
+ * Normalize configured base paths:
41
+ * - Resolve to absolute paths
42
+ * - Remove empty entries
43
+ * - Remove duplicates while preserving priority order
44
+ */
45
+ export declare function normalizeBasePaths(basePaths: string[], projectDirectory: string): string[];
46
+ export declare function getProjectSkillBasePaths(directory: string, worktree?: string): string[];
47
+ export declare function getPluginConfig(ctx: PluginInput): Promise<PluginConfig>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ /**
2
+ * OpenCode Dynamic Skills Plugin
3
+ *
4
+ * Implements Anthropic's Agent Skills Specification (v1.0) for OpenCode.
5
+ *
6
+ * Features:
7
+ * - Discovers SKILL.md files from .opencode/skills/, ~/.opencode/skills/, and ~/.config/opencode/skills/
8
+ * - Validates skills against Anthropic's spec (YAML frontmatter + Markdown)
9
+ * - Provides unified skill discovery and loading via two main tools:
10
+ * - use_skills(): Load one or more skills by name
11
+ * - find_skills(): Search for skills by free-text query
12
+ * - Delivers skill content via silent message insertion (noReply pattern)
13
+ * - Supports nested skills with proper naming
14
+ * - Supports multiple prompt formats (XML, JSON, Markdown) with model-aware selection
15
+ *
16
+ * Design Decisions:
17
+ * - Consolidates 50+ individual skill tools into 2 unified tools (cleaner namespace)
18
+ * - Skills are discovered resources, not always-on capabilities
19
+ * - Lazy loading: skills only inject when explicitly requested
20
+ * - Tool restrictions handled at agent level (not skill level)
21
+ * - Message insertion pattern ensures skill content persists (user messages not purged)
22
+ * - Base directory context enables relative path resolution
23
+ * - Skills require restart to reload (acceptable trade-off)
24
+ * - Prompt format selection: model-aware via modelRenderers config, default XML
25
+ *
26
+ * @see https://github.com/anthropics/skills
27
+ */
28
+ import { type Plugin } from '@opencode-ai/plugin';
29
+ export declare const SkillsPlugin: Plugin;