opencode-skill-autodiscovery 1.1.4 → 1.3.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.
- package/README.md +87 -10
- package/dist/agents.d.ts +18 -0
- package/dist/agents.d.ts.map +1 -0
- package/dist/agents.js +143 -0
- package/dist/agents.js.map +1 -0
- package/dist/discovery.d.ts +71 -0
- package/dist/discovery.d.ts.map +1 -0
- package/dist/discovery.js +629 -0
- package/dist/discovery.js.map +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -275
- package/dist/index.js.map +1 -1
- package/dist/log.d.ts +3 -0
- package/dist/log.d.ts.map +1 -0
- package/dist/log.js +12 -0
- package/dist/log.js.map +1 -0
- package/dist/mcp.d.ts +18 -0
- package/dist/mcp.d.ts.map +1 -0
- package/dist/mcp.js +169 -0
- package/dist/mcp.js.map +1 -0
- package/dist/schema.d.ts +5 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +9 -0
- package/dist/schema.js.map +1 -0
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -1,284 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { dirname, join } from "node:path";
|
|
4
|
-
// Parses the YAML frontmatter of a SKILL.md (name, description). Returns null
|
|
5
|
-
// when the file is unreadable or lacks a valid `name`.
|
|
6
|
-
function readSkillInfo(dir) {
|
|
7
|
-
const skillMd = join(dir, "SKILL.md");
|
|
8
|
-
let content;
|
|
9
|
-
try {
|
|
10
|
-
content = readFileSync(skillMd, "utf8");
|
|
11
|
-
}
|
|
12
|
-
catch {
|
|
13
|
-
return null;
|
|
14
|
-
}
|
|
15
|
-
const frontmatter = /^---\s*\n([\s\S]*?)\n---/.exec(content)?.[1];
|
|
16
|
-
if (!frontmatter)
|
|
17
|
-
return null;
|
|
18
|
-
const field = (key) => {
|
|
19
|
-
const m = new RegExp(`^${key}:[ \\t]*(.*)$`, "m").exec(frontmatter);
|
|
20
|
-
if (!m)
|
|
21
|
-
return undefined;
|
|
22
|
-
return m[1].trim().replace(/^["']|["']$/g, "");
|
|
23
|
-
};
|
|
24
|
-
const name = field("name");
|
|
25
|
-
if (!name)
|
|
26
|
-
return null;
|
|
27
|
-
return { dir, name, description: field("description") ?? "" };
|
|
28
|
-
}
|
|
29
|
-
// VS Code keeps agent plugins in a per-platform "data dir". The holding
|
|
30
|
-
// folder is named `agent-plugins` on older builds (e.g. ~/.vscode) and
|
|
31
|
-
// `agentPlugins` on newer builds; remote servers nest theirs under `data/`
|
|
32
|
-
// (~/.vscode-server/data/agentPlugins). We list every known candidate so the
|
|
33
|
-
// discovery works regardless of OS, build channel, or local/remote setup.
|
|
34
|
-
function vsCodeDataRoots(extra) {
|
|
35
|
-
const home = homedir();
|
|
36
|
-
const roots = new Set([
|
|
37
|
-
join(home, ".vscode"),
|
|
38
|
-
// Linux
|
|
39
|
-
join(home, ".config", "Code"),
|
|
40
|
-
join(home, ".config", "Code - Insiders"),
|
|
41
|
-
// macOS
|
|
42
|
-
join(home, "Library", "Application Support", "Code"),
|
|
43
|
-
join(home, "Library", "Application Support", "Code - Insiders"),
|
|
44
|
-
// Windows
|
|
45
|
-
...(process.env.APPDATA
|
|
46
|
-
? [
|
|
47
|
-
join(process.env.APPDATA, "Code"),
|
|
48
|
-
join(process.env.APPDATA, "Code - Insiders"),
|
|
49
|
-
]
|
|
50
|
-
: []),
|
|
51
|
-
// Remote hosts (Remote-SSH, Dev Containers, Codespaces, WSL)
|
|
52
|
-
join(home, ".vscode-server"),
|
|
53
|
-
join(home, ".vscode-server-insiders"),
|
|
54
|
-
join(home, ".vscode-remote"),
|
|
55
|
-
]);
|
|
56
|
-
for (const root of extra)
|
|
57
|
-
roots.add(root);
|
|
58
|
-
return [...roots];
|
|
59
|
-
}
|
|
60
|
-
function agentPluginDirs(roots) {
|
|
61
|
-
const dirs = new Set();
|
|
62
|
-
for (const root of roots) {
|
|
63
|
-
dirs.add(join(root, "agent-plugins"));
|
|
64
|
-
dirs.add(join(root, "agentPlugins"));
|
|
65
|
-
dirs.add(join(root, "data", "agent-plugins"));
|
|
66
|
-
dirs.add(join(root, "data", "agentPlugins"));
|
|
67
|
-
}
|
|
68
|
-
return [...dirs];
|
|
69
|
-
}
|
|
70
|
-
function findSkillDirs(root, out, seen) {
|
|
71
|
-
const resolved = join(root);
|
|
72
|
-
if (seen.has(resolved))
|
|
73
|
-
return;
|
|
74
|
-
seen.add(resolved);
|
|
75
|
-
let entries;
|
|
76
|
-
try {
|
|
77
|
-
entries = readdirSync(resolved);
|
|
78
|
-
}
|
|
79
|
-
catch {
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
for (const entry of entries) {
|
|
83
|
-
if (entry === ".git")
|
|
84
|
-
continue;
|
|
85
|
-
const full = join(resolved, entry);
|
|
86
|
-
let isDir = false;
|
|
87
|
-
try {
|
|
88
|
-
isDir = statSync(full).isDirectory();
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
if (isDir) {
|
|
94
|
-
if (existsSync(join(full, "SKILL.md"))) {
|
|
95
|
-
out.add(full);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
findSkillDirs(full, out, seen);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
function vscodePluginPath(pluginUri) {
|
|
104
|
-
const m = /^file:\/\/(.+)$/.exec(pluginUri);
|
|
105
|
-
if (!m)
|
|
106
|
-
return pluginUri;
|
|
107
|
-
let raw;
|
|
108
|
-
try {
|
|
109
|
-
raw = decodeURIComponent(m[1]);
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
raw = m[1];
|
|
113
|
-
}
|
|
114
|
-
if (raw.startsWith("/"))
|
|
115
|
-
raw = raw.slice(1);
|
|
116
|
-
return raw;
|
|
117
|
-
}
|
|
118
|
-
function collectVscodeManifest(out, installedJson) {
|
|
119
|
-
if (!existsSync(installedJson))
|
|
120
|
-
return;
|
|
121
|
-
let manifest;
|
|
122
|
-
try {
|
|
123
|
-
manifest = JSON.parse(readFileSync(installedJson, "utf8"));
|
|
124
|
-
}
|
|
125
|
-
catch {
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
for (const plugin of manifest.installed ?? []) {
|
|
129
|
-
if (!plugin.pluginUri)
|
|
130
|
-
continue;
|
|
131
|
-
const dir = vscodePluginPath(plugin.pluginUri);
|
|
132
|
-
if (dir)
|
|
133
|
-
findSkillDirs(dir, out, new Set());
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
// Remote hosts: VS Code syncs client skills into
|
|
137
|
-
// {data}/agentPlugins/{sanitizedUri}/{nonce}/ and records the LRU in
|
|
138
|
-
// cache.json. Resolve each entry so the materialized synced-customization
|
|
139
|
-
// bundle ("VS Code Synced Data" Open Plugin, skills/<name>/SKILL.md) is
|
|
140
|
-
// discovered.
|
|
141
|
-
function collectVscodeCache(out, cacheJson) {
|
|
142
|
-
if (!existsSync(cacheJson))
|
|
143
|
-
return;
|
|
144
|
-
let entries;
|
|
145
|
-
try {
|
|
146
|
-
entries = JSON.parse(readFileSync(cacheJson, "utf8"));
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
return;
|
|
150
|
-
}
|
|
151
|
-
if (!Array.isArray(entries))
|
|
152
|
-
return;
|
|
153
|
-
const parent = dirname(cacheJson);
|
|
154
|
-
for (const entry of entries) {
|
|
155
|
-
if (typeof entry?.uri !== "string")
|
|
156
|
-
continue;
|
|
157
|
-
const key = sanitizeKey(entry.uri) || "default";
|
|
158
|
-
const nonce = typeof entry.nonce === "string" && entry.nonce
|
|
159
|
-
? sanitizeKey(entry.nonce)
|
|
160
|
-
: "default";
|
|
161
|
-
findSkillDirs(join(parent, key, nonce), out, new Set());
|
|
162
|
-
// Some remote builds materialize the synced bundle directly as
|
|
163
|
-
// {parent}/{key}/skills/... without the {nonce} subdirectory.
|
|
164
|
-
findSkillDirs(join(parent, key), out, new Set());
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
// Mirrors the server-side AgentPluginManager sanitizer so we can resolve the
|
|
168
|
-
// cache.json entries to their on-disk directories.
|
|
169
|
-
function sanitizeKey(value) {
|
|
170
|
-
return value
|
|
171
|
-
.replace(/[^a-zA-Z0-9]/g, "-")
|
|
172
|
-
.replace(/-+/g, "-")
|
|
173
|
-
.replace(/^-|-$/g, "")
|
|
174
|
-
.substring(0, 128);
|
|
175
|
-
}
|
|
176
|
-
function collectAgentPluginRoot(out, root) {
|
|
177
|
-
const installedJson = join(root, "installed.json");
|
|
178
|
-
const cacheJson = join(root, "cache.json");
|
|
179
|
-
const hasManifest = existsSync(installedJson) || existsSync(cacheJson);
|
|
180
|
-
collectVscodeManifest(out, installedJson);
|
|
181
|
-
collectVscodeCache(out, cacheJson);
|
|
182
|
-
// Newer VS Code installs marketplaces directly as {host}/{org}/{repo}
|
|
183
|
-
// subdirectories with no manifest file at all. Fall back to a full tree walk
|
|
184
|
-
// only when no metadata exists, so we don't surface skills from cloned-but-
|
|
185
|
-
// not-installed marketplaces on setups that do have a manifest.
|
|
186
|
-
if (!hasManifest) {
|
|
187
|
-
findSkillDirs(root, out, new Set());
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
function collectVscode(out, extra) {
|
|
191
|
-
for (const dir of agentPluginDirs(vsCodeDataRoots(extra))) {
|
|
192
|
-
collectAgentPluginRoot(out, dir);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
function collectClaudeManifest(out, installedJson) {
|
|
196
|
-
if (!existsSync(installedJson))
|
|
197
|
-
return;
|
|
198
|
-
let manifest;
|
|
199
|
-
try {
|
|
200
|
-
manifest = JSON.parse(readFileSync(installedJson, "utf8"));
|
|
201
|
-
}
|
|
202
|
-
catch {
|
|
203
|
-
return;
|
|
204
|
-
}
|
|
205
|
-
for (const versions of Object.values(manifest.plugins ?? {})) {
|
|
206
|
-
for (const plugin of versions) {
|
|
207
|
-
if (plugin.installPath) {
|
|
208
|
-
findSkillDirs(plugin.installPath, out, new Set());
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function collectClaude(out) {
|
|
214
|
-
const home = homedir();
|
|
215
|
-
const installedJsons = [
|
|
216
|
-
join(home, ".claude", "plugins", "installed_plugins.json"),
|
|
217
|
-
join(home, ".claude", "remote", "plugins", "installed_plugins.json"),
|
|
218
|
-
];
|
|
219
|
-
let found = false;
|
|
220
|
-
for (const installedJson of installedJsons) {
|
|
221
|
-
if (existsSync(installedJson)) {
|
|
222
|
-
found = true;
|
|
223
|
-
collectClaudeManifest(out, installedJson);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
// Remote hosts sync Claude plugins as {nonce}/ dirs with per-plugin
|
|
227
|
-
// manifest.json but no installed_plugins.json; tree-walk the remote plugins
|
|
228
|
-
// dir as a fallback so those skills are discovered too.
|
|
229
|
-
if (!found) {
|
|
230
|
-
const remoteRoot = join(home, ".claude", "remote", "plugins");
|
|
231
|
-
if (existsSync(remoteRoot)) {
|
|
232
|
-
findSkillDirs(remoteRoot, out, new Set());
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
}
|
|
1
|
+
import { join } from "node:path";
|
|
2
|
+
import { applyConfigPatch, collectClaude, collectNodeModules, collectOpencodeCache, collectVscode, opencodeCacheRoot, planConfig, } from "./discovery.js";
|
|
236
3
|
export default (async (_input, options) => {
|
|
237
4
|
return {
|
|
238
5
|
config: async (cfg) => {
|
|
239
6
|
const config = cfg;
|
|
240
|
-
const extra = Array.isArray(options?.extraRoots)
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
seen.add(name);
|
|
253
|
-
unique.push(dir);
|
|
254
|
-
}
|
|
255
|
-
config.skills ??= {};
|
|
256
|
-
config.skills.paths ??= [];
|
|
257
|
-
for (const dir of unique) {
|
|
258
|
-
if (!config.skills.paths.includes(dir)) {
|
|
259
|
-
config.skills.paths.push(dir);
|
|
260
|
-
}
|
|
7
|
+
const extra = Array.isArray(options?.extraRoots)
|
|
8
|
+
? options.extraRoots
|
|
9
|
+
: [];
|
|
10
|
+
const scanCache = options?.scanCache !== false;
|
|
11
|
+
const scanNodeModules = options?.scanNodeModules !== false;
|
|
12
|
+
const mcpEnabled = options?.mcp === true;
|
|
13
|
+
const agentsEnabled = options?.agents === true;
|
|
14
|
+
const packages = [];
|
|
15
|
+
collectClaude(packages);
|
|
16
|
+
collectVscode(packages, extra);
|
|
17
|
+
if (scanCache) {
|
|
18
|
+
collectOpencodeCache(join(opencodeCacheRoot(), "packages"), packages);
|
|
261
19
|
}
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
// loaded via the `skill` tool, so without this a discovered skill is
|
|
265
|
-
// never invocable as `/name`. The command body tells the agent to load
|
|
266
|
-
// the skill and route the user's arguments through it.
|
|
267
|
-
config.command ??= {};
|
|
268
|
-
for (const dir of unique) {
|
|
269
|
-
const info = readSkillInfo(dir);
|
|
270
|
-
if (!info)
|
|
271
|
-
continue;
|
|
272
|
-
if (config.command[info.name])
|
|
273
|
-
continue;
|
|
274
|
-
config.command[info.name] = {
|
|
275
|
-
description: info.description || `Run the ${info.name} skill`,
|
|
276
|
-
template: [
|
|
277
|
-
`Load the \`${info.name}\` skill and follow its instructions.`,
|
|
278
|
-
`Context: $ARGUMENTS`,
|
|
279
|
-
].join("\n"),
|
|
280
|
-
};
|
|
20
|
+
if (scanNodeModules) {
|
|
21
|
+
collectNodeModules(join(process.cwd(), "node_modules"), packages);
|
|
281
22
|
}
|
|
23
|
+
const plan = planConfig(packages, {
|
|
24
|
+
commands: Object.keys(config.command ?? {}),
|
|
25
|
+
mcp: Object.keys(config.mcp ?? {}),
|
|
26
|
+
agents: Object.keys(config.agent ?? {}),
|
|
27
|
+
});
|
|
28
|
+
applyConfigPatch(config, plan, { mcp: mcpEnabled, agents: agentsEnabled });
|
|
282
29
|
},
|
|
283
30
|
};
|
|
284
31
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EACL,gBAAgB,EAChB,aAAa,EACb,kBAAkB,EAClB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,EACjB,UAAU,GACX,MAAM,gBAAgB,CAAC;AAUxB,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE;IACxC,OAAO;QACL,MAAM,EAAE,KAAK,EAAE,GAAW,EAAE,EAAE;YAC5B,MAAM,MAAM,GAAG,GAAuB,CAAC;YACvC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,UAAU,CAAC;gBAC9C,CAAC,CAAE,OAAO,CAAC,UAAuB;gBAClC,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,SAAS,GAAG,OAAO,EAAE,SAAS,KAAK,KAAK,CAAC;YAC/C,MAAM,eAAe,GAAG,OAAO,EAAE,eAAe,KAAK,KAAK,CAAC;YAC3D,MAAM,UAAU,GAAG,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;YACzC,MAAM,aAAa,GAAG,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;YAE/C,MAAM,QAAQ,GAAoB,EAAE,CAAC;YACrC,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxB,aAAa,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC/B,IAAI,SAAS,EAAE,CAAC;gBACd,oBAAoB,CAAC,IAAI,CAAC,iBAAiB,EAAE,EAAE,UAAU,CAAC,EAAE,QAAQ,CAAC,CAAC;YACxE,CAAC;YACD,IAAI,eAAe,EAAE,CAAC;gBACpB,kBAAkB,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;YACpE,CAAC;YAED,MAAM,IAAI,GAAG,UAAU,CAAC,QAAQ,EAAE;gBAChC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;gBAC3C,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;gBAClC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;aACxC,CAAC,CAAC;YAEH,gBAAgB,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC,CAAC;QAC7E,CAAC;KACF,CAAC;AACJ,CAAC,CAAkB,CAAC"}
|
package/dist/log.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9C;AAED,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEzC"}
|
package/dist/log.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Strips ANSI escape sequences and C0 control characters so values
|
|
2
|
+
// interpolated from package names can never inject terminal control codes
|
|
3
|
+
// into the log stream.
|
|
4
|
+
export function sanitize(value) {
|
|
5
|
+
return value
|
|
6
|
+
.replace(/\u001b\[[0-9;]*[A-Za-z]/g, "")
|
|
7
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/g, "");
|
|
8
|
+
}
|
|
9
|
+
export function log(message) {
|
|
10
|
+
console.error(`[opencode-skill-autodiscovery] ${sanitize(message)}`);
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=log.js.map
|
package/dist/log.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"log.js","sourceRoot":"","sources":["../src/log.ts"],"names":[],"mappings":"AAAA,mEAAmE;AACnE,0EAA0E;AAC1E,uBAAuB;AACvB,MAAM,UAAU,QAAQ,CAAC,KAAa;IACpC,OAAO,KAAK;SACT,OAAO,CAAC,0BAA0B,EAAE,EAAE,CAAC;SACvC,OAAO,CAAC,2CAA2C,EAAE,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,GAAG,CAAC,OAAe;IACjC,OAAO,CAAC,KAAK,CAAC,kCAAkC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AACvE,CAAC"}
|
package/dist/mcp.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PluginPackage } from "./discovery.js";
|
|
2
|
+
export type McpEntry = {
|
|
3
|
+
type: "local";
|
|
4
|
+
command: string[];
|
|
5
|
+
environment?: Record<string, string>;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
} | {
|
|
8
|
+
type: "remote";
|
|
9
|
+
url: string;
|
|
10
|
+
headers?: Record<string, string>;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
};
|
|
13
|
+
export declare function pluginDataRoot(name: string): string;
|
|
14
|
+
export declare function readMcp(pkg: PluginPackage, out: Array<{
|
|
15
|
+
key: string;
|
|
16
|
+
entry: McpEntry;
|
|
17
|
+
}>): void;
|
|
18
|
+
//# sourceMappingURL=mcp.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.d.ts","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,MAAM,QAAQ,GAChB;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAWN,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnD;AA8BD,wBAAgB,OAAO,CACrB,GAAG,EAAE,aAAa,EAClB,GAAG,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,QAAQ,CAAA;CAAE,CAAC,GAC3C,IAAI,CAiIN"}
|
package/dist/mcp.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { log } from "./log.js";
|
|
5
|
+
import { MCP_SCHEMA, VERSION } from "./schema.js";
|
|
6
|
+
const STDIO_KEYS = new Set(["type", "command", "args", "env", "cwd"]);
|
|
7
|
+
const HTTP_KEYS = new Set(["type", "url", "headers"]);
|
|
8
|
+
const HTTP_URL = /^https?:\/\//;
|
|
9
|
+
function opencodeStateRoot() {
|
|
10
|
+
const base = process.env.XDG_STATE_HOME || join(homedir(), ".local", "state");
|
|
11
|
+
return join(base, "opencode");
|
|
12
|
+
}
|
|
13
|
+
export function pluginDataRoot(name) {
|
|
14
|
+
return join(opencodeStateRoot(), "plugin-data", name);
|
|
15
|
+
}
|
|
16
|
+
function expandPluginVars(value, root, dataDir) {
|
|
17
|
+
return value
|
|
18
|
+
.split("${PLUGIN_ROOT}")
|
|
19
|
+
.join(root)
|
|
20
|
+
.split("${PLUGIN_DATA}")
|
|
21
|
+
.join(dataDir);
|
|
22
|
+
}
|
|
23
|
+
function collectHeaders(value) {
|
|
24
|
+
const headers = {};
|
|
25
|
+
if (typeof value === "object" && value !== null) {
|
|
26
|
+
for (const [k, v] of Object.entries(value)) {
|
|
27
|
+
if (typeof v === "string")
|
|
28
|
+
headers[k] = v;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return headers;
|
|
32
|
+
}
|
|
33
|
+
function hasUnknownKeys(server, allowed) {
|
|
34
|
+
return Object.keys(server).some((k) => !allowed.has(k));
|
|
35
|
+
}
|
|
36
|
+
// Maps the portable mcp.json shape onto opencode's native config.mcp. Per the
|
|
37
|
+
// Agent Plugins spec, failures are per-entry (and per-package for an invalid
|
|
38
|
+
// mcp.json): a bad server never blocks other servers or the package's skills.
|
|
39
|
+
export function readMcp(pkg, out) {
|
|
40
|
+
if (!pkg.mcpPath)
|
|
41
|
+
return;
|
|
42
|
+
let raw;
|
|
43
|
+
try {
|
|
44
|
+
raw = readFileSync(pkg.mcpPath, "utf8");
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let parsed;
|
|
50
|
+
try {
|
|
51
|
+
parsed = JSON.parse(raw);
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
log(`ignoring mcp.json for package "${pkg.name}": not valid JSON`);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
58
|
+
log(`ignoring mcp.json for package "${pkg.name}": not an object`);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (typeof parsed.$schema !== "string" || !MCP_SCHEMA.test(parsed.$schema)) {
|
|
62
|
+
log(`ignoring mcp.json for package "${pkg.name}": unsupported or missing $schema`);
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const mcpVersion = VERSION.exec(parsed.$schema)?.[1];
|
|
66
|
+
if (pkg.schemaVersion && mcpVersion && mcpVersion !== pkg.schemaVersion) {
|
|
67
|
+
log(`ignoring mcp.json for package "${pkg.name}": schema version ${mcpVersion} does not match plugin.json (${pkg.schemaVersion})`);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (typeof parsed.mcpServers !== "object" || parsed.mcpServers === null) {
|
|
71
|
+
log(`ignoring mcp.json for package "${pkg.name}": missing mcpServers`);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const dataDir = pluginDataRoot(pkg.name);
|
|
75
|
+
const servers = parsed.mcpServers;
|
|
76
|
+
for (const [name, serverValue] of Object.entries(servers)) {
|
|
77
|
+
if (typeof serverValue !== "object" || serverValue === null) {
|
|
78
|
+
log(`skipping MCP server "${pkg.name}/${name}": invalid entry`);
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
const server = serverValue;
|
|
82
|
+
if (server.type === "stdio") {
|
|
83
|
+
if (hasUnknownKeys(server, STDIO_KEYS)) {
|
|
84
|
+
log(`skipping MCP server "${pkg.name}/${name}": unknown fields in stdio entry`);
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
if (typeof server.command !== "string" || server.command.length === 0) {
|
|
88
|
+
log(`skipping MCP server "${pkg.name}/${name}": stdio requires a string command`);
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
const command = server.command.startsWith("./")
|
|
92
|
+
? join(pkg.root, server.command)
|
|
93
|
+
: server.command;
|
|
94
|
+
const args = Array.isArray(server.args)
|
|
95
|
+
? server.args
|
|
96
|
+
.filter((a) => typeof a === "string")
|
|
97
|
+
.map((a) => expandPluginVars(a, pkg.root, dataDir))
|
|
98
|
+
: [];
|
|
99
|
+
const environment = {};
|
|
100
|
+
if (typeof server.env === "object" && server.env !== null) {
|
|
101
|
+
for (const [k, v] of Object.entries(server.env)) {
|
|
102
|
+
if (k === "PLUGIN_ROOT" || k === "PLUGIN_DATA") {
|
|
103
|
+
log(`dropping reserved env key "${k}" for MCP server "${pkg.name}/${name}"`);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (typeof v === "string") {
|
|
107
|
+
environment[k] = expandPluginVars(v, pkg.root, dataDir);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
environment.PLUGIN_ROOT = pkg.root;
|
|
112
|
+
environment.PLUGIN_DATA = dataDir;
|
|
113
|
+
if (server.cwd !== undefined) {
|
|
114
|
+
const cwd = typeof server.cwd === "string"
|
|
115
|
+
? expandPluginVars(server.cwd, pkg.root, dataDir)
|
|
116
|
+
: String(server.cwd);
|
|
117
|
+
log(`dropping cwd "${cwd}" for MCP server "${pkg.name}/${name}": opencode has no cwd support`);
|
|
118
|
+
}
|
|
119
|
+
try {
|
|
120
|
+
mkdirSync(dataDir, { recursive: true });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Non-fatal: the subprocess env still points at the (uncreated) dir.
|
|
124
|
+
}
|
|
125
|
+
out.push({
|
|
126
|
+
key: name,
|
|
127
|
+
entry: { type: "local", command: [command, ...args], environment, enabled: true },
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (server.type === "streamable-http") {
|
|
131
|
+
if (hasUnknownKeys(server, HTTP_KEYS)) {
|
|
132
|
+
log(`skipping MCP server "${pkg.name}/${name}": unknown fields in streamable-http entry`);
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (typeof server.url !== "string" || server.url.length === 0) {
|
|
136
|
+
log(`skipping MCP server "${pkg.name}/${name}": streamable-http requires a string url`);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (!HTTP_URL.test(server.url)) {
|
|
140
|
+
log(`skipping MCP server "${pkg.name}/${name}": url must be an absolute http(s) URL`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
out.push({
|
|
144
|
+
key: name,
|
|
145
|
+
entry: {
|
|
146
|
+
type: "remote",
|
|
147
|
+
url: server.url,
|
|
148
|
+
headers: collectHeaders(server.headers),
|
|
149
|
+
enabled: true,
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else if (server.type === "sse") {
|
|
154
|
+
if (hasUnknownKeys(server, HTTP_KEYS)) {
|
|
155
|
+
log(`skipping MCP server "${pkg.name}/${name}": unknown fields in sse entry`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (typeof server.url !== "string" || !HTTP_URL.test(server.url)) {
|
|
159
|
+
log(`skipping MCP server "${pkg.name}/${name}": sse requires an absolute http(s) url`);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
log(`skipping MCP server "${pkg.name}/${name}": opencode does not support the sse transport`);
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
log(`skipping MCP server "${pkg.name}/${name}": unknown transport "${String(server.type)}"`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=mcp.js.map
|
package/dist/mcp.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
|
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa,QACoD,CAAC;AAE/E,eAAO,MAAM,UAAU,QACoD,CAAC;AAE5E,eAAO,MAAM,OAAO,QAA8D,CAAC;AAInF,eAAO,MAAM,YAAY,QAC8B,CAAC"}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Shared Agent Plugins schema identifiers and constraints. Kept in one module
|
|
2
|
+
// so the plugin manifest, MCP config, and version-match logic never drift.
|
|
3
|
+
export const PLUGIN_SCHEMA = /^https:\/\/agent-plugins\.org\/schemas\/1\.\d+\.\d+\/plugin\.schema\.json$/;
|
|
4
|
+
export const MCP_SCHEMA = /^https:\/\/agent-plugins\.org\/schemas\/1\.\d+\.\d+\/mcp\.schema\.json$/;
|
|
5
|
+
export const VERSION = /^https:\/\/agent-plugins\.org\/schemas\/(\d+\.\d+\.\d+)\//;
|
|
6
|
+
// Manifest `name` constraints (Agent Plugins spec 5.5): 1-64 chars, lowercase
|
|
7
|
+
// alphanumeric plus `-`/`.`, alphanumeric start/end, no `--` or `..`.
|
|
8
|
+
export const NAME_PATTERN = /^(?!.*(?:--|\.\.))[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/;
|
|
9
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,2EAA2E;AAE3E,MAAM,CAAC,MAAM,aAAa,GACxB,4EAA4E,CAAC;AAE/E,MAAM,CAAC,MAAM,UAAU,GACrB,yEAAyE,CAAC;AAE5E,MAAM,CAAC,MAAM,OAAO,GAAG,2DAA2D,CAAC;AAEnF,8EAA8E;AAC9E,sEAAsE;AACtE,MAAM,CAAC,MAAM,YAAY,GACvB,qDAAqD,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-skill-autodiscovery",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "OpenCode plugin that auto-discovers skills installed by VS Code agent plugins
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "OpenCode plugin that auto-discovers skills installed by VS Code agent plugins, Claude Code plugins, and Agent Plugins 1.0.0 packages, and registers them with the local opencode config.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
30
|
"build": "tsc -p tsconfig.json",
|
|
31
|
+
"test": "npm run build && node --test",
|
|
31
32
|
"prepublishOnly": "npm run build"
|
|
32
33
|
},
|
|
33
34
|
"keywords": [
|