opencode-skill-autodiscovery 1.6.1 → 2.1.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 +345 -45
- package/dist/agents.d.ts +3 -1
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +111 -27
- package/dist/agents.js.map +1 -1
- package/dist/cache.d.ts +5 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +94 -0
- package/dist/cache.js.map +1 -0
- package/dist/discovery-cache.d.ts +9 -0
- package/dist/discovery-cache.d.ts.map +1 -0
- package/dist/discovery-cache.js +161 -0
- package/dist/discovery-cache.js.map +1 -0
- package/dist/discovery.d.ts +31 -9
- package/dist/discovery.d.ts.map +1 -1
- package/dist/discovery.js +531 -178
- package/dist/discovery.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +64 -10
- package/dist/index.js.map +1 -1
- package/dist/mcp.d.ts +6 -2
- package/dist/mcp.d.ts.map +1 -1
- package/dist/mcp.js +104 -17
- package/dist/mcp.js.map +1 -1
- package/dist/schema.d.ts +1 -0
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +10 -0
- package/dist/schema.js.map +1 -1
- package/dist/schemas/mcp-schema-1.0.0.d.ts +117 -0
- package/dist/schemas/mcp-schema-1.0.0.d.ts.map +1 -0
- package/dist/schemas/mcp-schema-1.0.0.js +139 -0
- package/dist/schemas/mcp-schema-1.0.0.js.map +1 -0
- package/dist/schemas/plugin-schema-1.0.0.d.ts +66 -0
- package/dist/schemas/plugin-schema-1.0.0.d.ts.map +1 -0
- package/dist/schemas/plugin-schema-1.0.0.js +72 -0
- package/dist/schemas/plugin-schema-1.0.0.js.map +1 -0
- package/dist/spec-schema.d.ts +12 -0
- package/dist/spec-schema.d.ts.map +1 -0
- package/dist/spec-schema.js +71 -0
- package/dist/spec-schema.js.map +1 -0
- package/package.json +50 -47
package/README.md
CHANGED
|
@@ -1,22 +1,53 @@
|
|
|
1
1
|
# opencode-skill-autodiscovery
|
|
2
2
|
|
|
3
|
-
An [opencode](https://opencode.ai) plugin that auto-discovers skills
|
|
3
|
+
An [opencode](https://opencode.ai) plugin that auto-discovers skills installed by
|
|
4
|
+
VS Code agent plugins, Claude Code plugins, and [Agent Plugins
|
|
5
|
+
1.0.0](https://agent-plugins.org) conformant packages on the current machine,
|
|
6
|
+
and registers them with opencode's `skills.paths` so they appear in every
|
|
7
|
+
session. Each discovered skill is also exposed as a `/skill-name` slash command
|
|
8
|
+
that loads the skill and routes the rest of your message through it.
|
|
4
9
|
|
|
5
10
|
## Why
|
|
6
11
|
|
|
7
|
-
Skills live in different places depending on the tool, OS, and local vs remote
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
- VS Code agent plugins
|
|
12
|
+
Skills live in different places depending on the tool, OS, and local vs remote
|
|
13
|
+
setup:
|
|
14
|
+
|
|
15
|
+
- VS Code agent plugins, local client: `~/.vscode/agent-plugins` (Windows /
|
|
16
|
+
older builds), `~/.config/Code/agentPlugins` (Linux),
|
|
17
|
+
`~/Library/Application Support/Code/agentPlugins` (macOS), and
|
|
18
|
+
`%APPDATA%\Code\agentPlugins` (Windows), plus `Code - Insiders` variants.
|
|
19
|
+
The install layout is `{host}/{org}/{repo}`, with optional `installed.json`
|
|
20
|
+
and `cache.json` manifests alongside.
|
|
21
|
+
- VS Code agent plugins on a remote host (Remote-SSH, Codespaces, Dev
|
|
22
|
+
Containers, WSL): `~/.vscode-server/data/agentPlugins`. VS Code syncs the
|
|
23
|
+
enabled skills from your local client into a synthetic "VS Code Synced
|
|
24
|
+
Data" plugin materialized at
|
|
25
|
+
`~/.vscode-server/data/agentPlugins/{sanitizedUri}/{nonce}/skills/...`, and
|
|
26
|
+
records each synced bundle in `~/.vscode-server/data/agentPlugins/cache.json`.
|
|
11
27
|
- Claude Code plugins: `~/.claude/plugins/installed_plugins.json`
|
|
12
|
-
- Claude Code plugins on a remote host (SSH/remote sessions):
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
28
|
+
- Claude Code plugins on a remote host (SSH/remote sessions):
|
|
29
|
+
`~/.claude/remote/plugins/installed_plugins.json`
|
|
30
|
+
- **Agent Plugins 1.0.0 packages** distributed via npm. opencode installs npm
|
|
31
|
+
plugins into its own cache (`~/.cache/opencode/packages/...`), not the
|
|
32
|
+
project's `node_modules`, and a `skills.paths` entry relative to the project
|
|
33
|
+
dir silently resolves to nothing. This plugin scans both the opencode plugin
|
|
34
|
+
cache and the project's `node_modules` for packages carrying a root
|
|
35
|
+
`plugin.json` whose `$schema` is `https://agent-plugins.org/schemas/...`,
|
|
36
|
+
and registers their `skills/` with **absolute** paths — so an npm-distributed
|
|
37
|
+
Agent Plugins package (e.g. `@dodopayments/opencode-plugin`) just works with
|
|
38
|
+
`"plugin": ["opencode-skill-autodiscovery"]` and nothing else.
|
|
39
|
+
|
|
40
|
+
Each points at plugin directories that contain `SKILL.md` files. This plugin
|
|
41
|
+
reads the manifests (including the remote `cache.json` LRU), resolves each
|
|
42
|
+
entry to its on-disk skill directories, and registers them with opencode.
|
|
43
|
+
When a discovered package carries a conformant root `plugin.json`, that manifest
|
|
44
|
+
is preferred (its `skills/` children are registered as-is); otherwise the plugin
|
|
45
|
+
falls back to a tree walk so legacy layouts keep working.
|
|
16
46
|
|
|
17
47
|
### MCP servers (opt-in)
|
|
18
48
|
|
|
19
|
-
With the `mcp` option, the plugin also reads each discovered package's `mcp.json`
|
|
49
|
+
With the `mcp` option, the plugin also reads each discovered package's `mcp.json`
|
|
50
|
+
and maps it onto opencode's `config.mcp`:
|
|
20
51
|
|
|
21
52
|
| Agent Plugins server | opencode entry |
|
|
22
53
|
|---|---|
|
|
@@ -24,39 +55,97 @@ With the `mcp` option, the plugin also reads each discovered package's `mcp.json
|
|
|
24
55
|
| `{ "type": "streamable-http", "url" }` | `{ "type": "remote", "url" }` |
|
|
25
56
|
| `{ "type": "sse" }` | skipped (opencode has no SSE transport) |
|
|
26
57
|
|
|
27
|
-
`${PLUGIN_ROOT}` and `${PLUGIN_DATA}` placeholders are expanded in `args`/`env`,
|
|
58
|
+
`${PLUGIN_ROOT}` and `${PLUGIN_DATA}` placeholders are expanded in `args`/`env`,
|
|
59
|
+
and both variables are injected into each stdio server's environment
|
|
60
|
+
(`PLUGIN_DATA` points at
|
|
61
|
+
`{opencode state}/plugin-data/{packageName}`). Invalid entries are skipped
|
|
62
|
+
per-entry, never fatally. `sse` servers and stdio `cwd` (which opencode cannot
|
|
63
|
+
represent) are dropped with a log line.
|
|
64
|
+
|
|
65
|
+
### MCP credentials
|
|
66
|
+
|
|
67
|
+
Package-declared `mcp.json` servers are mirrored **verbatim** into your
|
|
68
|
+
opencode config: `headers`, `env` values, and server `url`s are copied exactly
|
|
69
|
+
as written (modulo `${PLUGIN_ROOT}` / `${PLUGIN_DATA}` expansion) into
|
|
70
|
+
`config.mcp.<server>`. opencode stores `config.mcp` in plaintext, so any
|
|
71
|
+
credential in those fields — an `Authorization` header, a token in `env`, an
|
|
72
|
+
`userinfo@` URL — is plaintext-at-rest and may appear in host config backups,
|
|
73
|
+
logs, or terminal output. Treat a server's headers/env/URL as a static
|
|
74
|
+
snapshot: once mirrored, the value has no rotation or revocation linkage back
|
|
75
|
+
to the package — updating the package does not rotate a copied token.
|
|
76
|
+
|
|
77
|
+
Guidance:
|
|
78
|
+
|
|
79
|
+
- **Use https-only remotes.** `streamable-http` servers must already be
|
|
80
|
+
`https://` to register (a plain `http://` URL would ship any `headers` in
|
|
81
|
+
cleartext), and that is the transport you want when a server carries
|
|
82
|
+
credentials.
|
|
83
|
+
- **Audit `config.mcp`** after discovery to confirm which servers and
|
|
84
|
+
credential fields entered your config, and remove anything you did not
|
|
85
|
+
intend to persist.
|
|
86
|
+
- **Use `consent` / `exclude`** to control admission: servers from untrusted
|
|
87
|
+
packages are skipped until the package is listed under `consent.mcp`, and
|
|
88
|
+
`exclude` drops a package entirely.
|
|
89
|
+
- The planning-time `mkdir` for a package stdio server's `PLUGIN_DATA`
|
|
90
|
+
directory is deferred: the directory is created only when the server is
|
|
91
|
+
actually applied to the config, never at plan time — so a
|
|
92
|
+
discovered-but-unapplied server leaves no filesystem trace.
|
|
28
93
|
|
|
29
94
|
### Agents (opt-in)
|
|
30
95
|
|
|
31
|
-
Agent Plugins 1.0.0 has no portable "agents" component type (only skills and
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
96
|
+
Agent Plugins 1.0.0 has no portable "agents" component type (only skills and
|
|
97
|
+
MCP servers), so agents are contributed through the spec's client-extension
|
|
98
|
+
mechanism. With the `agents` option, the plugin reads agents from four
|
|
99
|
+
sources, in order:
|
|
100
|
+
|
|
101
|
+
1. `plugin.json` → `extensions["dev.opencode"].agents` (a map of agent name →
|
|
102
|
+
opencode `agent` config).
|
|
103
|
+
2. A `dev.opencode/agents/<name>.json` extension directory (one opencode agent
|
|
104
|
+
config per file).
|
|
105
|
+
3. A legacy Claude Code plugin shim: `.claude-plugin/plugin.json` `agents`,
|
|
106
|
+
mapping `description` and `systemPrompt` (or the `agents/<name>/AGENTS.md`
|
|
107
|
+
body) onto an opencode agent.
|
|
108
|
+
4. Flat `agents/<name>.md` files (the agency-agents layout) — one agent per
|
|
109
|
+
file, keyed by filename, with `description`/`color` from frontmatter and the
|
|
110
|
+
markdown body as the prompt.
|
|
111
|
+
|
|
112
|
+
Agents discovered without an explicit `mode` default to opencode's `all`, so
|
|
113
|
+
they are both Tab-selectable and spawnable as subagents.
|
|
114
|
+
|
|
115
|
+
Discovered agents are registered as `config.agent.<name>` with the same rules
|
|
116
|
+
as commands: user-defined agents are never overwritten, same-source mirrors are
|
|
117
|
+
collapsed, and cross-source collisions become `<package>-<agent>`.
|
|
118
|
+
|
|
119
|
+
**Trust note:** package-supplied capability is clamped to the conservative
|
|
120
|
+
default: `permission` blocks and `tools` grants are always dropped, and any
|
|
121
|
+
declared `mode` other than `subagent` clamps to `subagent`. Each drop is logged
|
|
122
|
+
naming the package and the agent. If you need more, define the agent yourself
|
|
123
|
+
in `opencode.json` (which always wins).
|
|
45
124
|
|
|
46
125
|
### Slash commands
|
|
47
126
|
|
|
48
|
-
opencode treats skills and slash commands as separate mechanisms: skills are
|
|
127
|
+
opencode treats skills and slash commands as separate mechanisms: skills are
|
|
128
|
+
only loaded on demand via the `skill` tool. To make a discovered skill
|
|
129
|
+
invocable as `/name`, the plugin also registers a command for it (via
|
|
130
|
+
`config.command`) whose template loads the skill and forwards your arguments:
|
|
49
131
|
|
|
50
132
|
```markdown
|
|
51
|
-
Load the
|
|
133
|
+
Load the "spec" skill and follow its instructions.
|
|
52
134
|
Context: $ARGUMENTS
|
|
53
135
|
```
|
|
54
136
|
|
|
55
|
-
|
|
137
|
+
The skill name is rendered only as a quoted data value in the template — never
|
|
138
|
+
inside backticks — so a hostile name cannot break out into surrounding
|
|
139
|
+
instruction text.
|
|
140
|
+
|
|
141
|
+
So `/spec plan the migration` loads the `spec` skill and runs it against
|
|
142
|
+
`plan the migration`. The command's description is taken from the skill's
|
|
143
|
+
frontmatter; existing commands with the same name are never overwritten (a
|
|
144
|
+
colliding skill from a different source is registered as `/package-skill`).
|
|
56
145
|
|
|
57
146
|
## Install
|
|
58
147
|
|
|
59
|
-
Add the package name to the `plugin` array in your `opencode.json
|
|
148
|
+
Add the package name to the `plugin` array in your `opencode.json`:
|
|
60
149
|
|
|
61
150
|
```json
|
|
62
151
|
{
|
|
@@ -75,6 +164,7 @@ Use the tuple form to configure options:
|
|
|
75
164
|
"extraRoots": ["/home/user/.vscode-server"],
|
|
76
165
|
"scanCache": true,
|
|
77
166
|
"scanNodeModules": true,
|
|
167
|
+
"exclude": ["unwanted-plugin"],
|
|
78
168
|
"mcp": false,
|
|
79
169
|
"agents": false
|
|
80
170
|
}
|
|
@@ -86,14 +176,195 @@ Use the tuple form to configure options:
|
|
|
86
176
|
| Option | Default | Meaning |
|
|
87
177
|
|---|---|---|
|
|
88
178
|
| `extraRoots` | `[]` | Extra root directories to scan (e.g. a non-standard VS Code data location on a remote host). |
|
|
89
|
-
| `scanCache` | `
|
|
90
|
-
| `scanNodeModules` | `
|
|
179
|
+
| `scanCache` | `false` | Scan opencode's plugin cache (`~/.cache/opencode/packages/*`) for Agent Plugins packages. |
|
|
180
|
+
| `scanNodeModules` | `false` | Scan the project's `node_modules` (incl. `@scope/*`) for Agent Plugins packages. |
|
|
181
|
+
| `exclude` | `[]` | Package names to skip during discovery, regardless of trust tier. Matches the conformant package's `plugin.json` name, or the directory basename when there is no manifest. |
|
|
91
182
|
| `mcp` | `false` | Also register MCP servers from discovered packages' `mcp.json`. |
|
|
92
183
|
| `agents` | `false` | Also register agents from packages (see "Agents" above). |
|
|
184
|
+
| `consent` | `{}` | Per-package consent map: `consent.mcp` / `consent.agents` list package names whose MCP servers / agents are wanted even when the package is discovered as untrusted. Refines the `mcp` / `agents` switches; `exclude` remains the deny side. |
|
|
185
|
+
|
|
186
|
+
Both scan flags default to `false` for supply-chain reasons: a discovered
|
|
187
|
+
skill's `SKILL.md` becomes prompt material in your sessions, so anything that
|
|
188
|
+
plants a skill plants model-facing instructions — and neither transitive npm
|
|
189
|
+
dependencies (which land in `node_modules` without any install script running)
|
|
190
|
+
nor the shared `~/.cache/opencode/packages` directory (populated by every
|
|
191
|
+
project on the machine) requires a manifest you ever reviewed. This is the
|
|
192
|
+
OWASP LLM01 risk arriving via the software supply chain; both sources are
|
|
193
|
+
therefore opt-in, while manifest-mediated sources (Claude Code plugins, VS
|
|
194
|
+
Code agent plugins) stay default-on because their manifests record deliberate,
|
|
195
|
+
host-vouched installs.
|
|
196
|
+
|
|
197
|
+
## Threat model
|
|
198
|
+
|
|
199
|
+
Discovery reads manifests from well-known locations and registers what it finds
|
|
200
|
+
into your session config. Not all sources are equally trustworthy, so they are
|
|
201
|
+
split into two tiers:
|
|
202
|
+
|
|
203
|
+
**Trusted by default** — content a host tool or opencode itself installed
|
|
204
|
+
deliberately, vouched for by a manifest:
|
|
205
|
+
|
|
206
|
+
- Claude Code plugins (`installed_plugins.json`, local and remote)
|
|
207
|
+
- VS Code agent plugins recorded in `installed.json` / `cache.json`
|
|
208
|
+
- packages in opencode's own plugin cache (`~/.cache/opencode/packages/*`),
|
|
209
|
+
which exist because your config asked opencode to fetch them
|
|
210
|
+
|
|
211
|
+
**Untrusted by default** — content present merely as a side effect:
|
|
212
|
+
|
|
213
|
+
- the project's `node_modules`: dependencies install transitively, so anything
|
|
214
|
+
in the tree can ship skills, MCP servers, or agents. Scanning it is **off by
|
|
215
|
+
default**; restore it explicitly with `"scanNodeModules": true`.
|
|
216
|
+
- user-supplied `extraRoots`: the plugin cannot vouch for whatever you point
|
|
217
|
+
it at. A manifest discovered under one of these roots is untrusted and may
|
|
218
|
+
only reference a package inside that root (a trusted VS Code home root may
|
|
219
|
+
still reference a global extension directory).
|
|
220
|
+
- manifest-less directory walks (e.g. cloned-but-uninstalled marketplace folders).
|
|
221
|
+
|
|
222
|
+
### Graduated default
|
|
223
|
+
|
|
224
|
+
Trust gates the two component tiers differently:
|
|
225
|
+
|
|
226
|
+
- **Skills and slash commands register for every tier.** Both are read-only
|
|
227
|
+
content registration — surfacing a package's skills is the plugin's job, so
|
|
228
|
+
trust never blocks them. An untrusted package's skills and commands are
|
|
229
|
+
registered exactly like a trusted one's, but each untrusted package emits a
|
|
230
|
+
one-line info log naming the package and its source, so side-effect content
|
|
231
|
+
entering the session stays visible. `exclude` remains the deny side.
|
|
232
|
+
- **MCP servers and agents are default-off for untrusted packages.** The
|
|
233
|
+
`mcp` / `agents` switches admit servers and agents from trusted packages
|
|
234
|
+
directly; an untrusted package contributes them only when the package is
|
|
235
|
+
listed under `consent.mcp` / `consent.agents`.
|
|
236
|
+
|
|
237
|
+
Trust never decides whether content is *safe*: a registered skill's
|
|
238
|
+
`SKILL.md` becomes prompt material in your sessions. The `$schema` check
|
|
239
|
+
identifies format only — never provenance or safety. Any package can copy the
|
|
240
|
+
literal schema URL, so a conformant manifest proves nothing about who wrote it.
|
|
241
|
+
|
|
242
|
+
### `mcp` and `agents` register package content
|
|
243
|
+
|
|
244
|
+
Both flags are global opt-in switches, narrowed by `exclude` and refined by
|
|
245
|
+
per-package `consent` (below). Trust decides admission: `mcp: true` /
|
|
246
|
+
`agents: true` admit servers or agents from trusted packages, and from
|
|
247
|
+
untrusted packages only when the package is listed under `consent.mcp` /
|
|
248
|
+
`consent.agents`.
|
|
249
|
+
|
|
250
|
+
- `mcp: true` admits MCP servers from packages that are trusted or listed in
|
|
251
|
+
`consent.mcp`. Every registered package-supplied server starts with
|
|
252
|
+
`enabled: false`: opencode will not spawn a package-declared binary or
|
|
253
|
+
connect to a package-chosen endpoint at startup on discovery alone.
|
|
254
|
+
Admittance is opt-in; the safe default is off.
|
|
255
|
+
- `agents: true` admits agents from packages that are trusted or listed in
|
|
256
|
+
`consent.agents`, then clamps each to the conservative default: a declared
|
|
257
|
+
`mode` other than `subagent` is dropped (a package cannot make itself a
|
|
258
|
+
primary agent), `tools` booleans such as `"write": true` are never
|
|
259
|
+
inherited, and `permission` blocks are dropped — each drop is logged naming
|
|
260
|
+
the package and the agent. An agent without an explicit `mode` still falls
|
|
261
|
+
through to opencode's `all` default.
|
|
262
|
+
|
|
263
|
+
Pair these flags with `exclude` to carve out packages you do not want
|
|
264
|
+
registered:
|
|
265
|
+
|
|
266
|
+
```json
|
|
267
|
+
{
|
|
268
|
+
"plugin": [
|
|
269
|
+
["opencode-skill-autodiscovery", { "mcp": true, "agents": true, "exclude": ["unwanted-package"] }]
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Per-package consent (`consent`)
|
|
275
|
+
|
|
276
|
+
Consent is the per-package allow side for packages discovered as **untrusted**
|
|
277
|
+
(the project's `node_modules`, user-supplied `extraRoots`, manifest-less
|
|
278
|
+
walks). List a package's name under `consent.mcp` to admit its MCP servers, or
|
|
279
|
+
under `consent.agents` to admit its agents, even though the package is not
|
|
280
|
+
trusted by default:
|
|
281
|
+
|
|
282
|
+
```json
|
|
283
|
+
{
|
|
284
|
+
"plugin": [
|
|
285
|
+
[
|
|
286
|
+
"opencode-skill-autodiscovery",
|
|
287
|
+
{
|
|
288
|
+
"scanNodeModules": true,
|
|
289
|
+
"mcp": true,
|
|
290
|
+
"agents": true,
|
|
291
|
+
"consent": {
|
|
292
|
+
"mcp": ["community-tools"],
|
|
293
|
+
"agents": ["community-tools"]
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
]
|
|
297
|
+
]
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- `consent` only refines the coarse switches: `mcp: true` / `agents: true`
|
|
302
|
+
stay the on-switch, and `consent.mcp` / `consent.agents` list which
|
|
303
|
+
untrusted packages are admitted when a switch is on.
|
|
304
|
+
- `exclude` remains the deny side and wins: a package listed in both
|
|
305
|
+
`exclude` and `consent` is never discovered.
|
|
306
|
+
- Trusted packages need no consent entry.
|
|
307
|
+
- The default — no `consent` option — grants nothing extra: untrusted
|
|
308
|
+
packages contribute no MCP servers or agents until admitted by name.
|
|
309
|
+
|
|
310
|
+
Consent is declarative (package names in `opencode.json`), so it works inside
|
|
311
|
+
opencode's synchronous `config` hook — no interactive prompt required.
|
|
312
|
+
|
|
313
|
+
### Identifier rules
|
|
314
|
+
|
|
315
|
+
Every package-supplied identifier that becomes a config key must match the
|
|
316
|
+
same pattern: lowercase letters, digits, `-`, and `.` only (`[a-z0-9.-]`),
|
|
317
|
+
starting and ending with an alphanumeric character, with no `--` or `..`
|
|
318
|
+
runs. The prototype-chain keys `__proto__` and `constructor` are rejected
|
|
319
|
+
outright even though they satisfy the character pattern. This applies to:
|
|
320
|
+
|
|
321
|
+
- the `plugin.json` manifest `name` (collision namespaces and `exclude`
|
|
322
|
+
matching)
|
|
323
|
+
- `SKILL.md` frontmatter `name` (slash-command keys)
|
|
324
|
+
- `mcp.json` server keys (`config.mcp` keys)
|
|
325
|
+
- agent manifest keys, `dev.opencode/agents/*.json` filenames, legacy shim
|
|
326
|
+
agent names, and flat `agents/<name>.md` filenames (`config.agent` keys)
|
|
327
|
+
|
|
328
|
+
An invalid identifier never aborts discovery: the offending entry is skipped
|
|
329
|
+
with a log line naming the package, its source, and a reason, and legitimate
|
|
330
|
+
entries from the same package still register.
|
|
331
|
+
|
|
332
|
+
Defense-in-depth at the write sites holds even if a future call site skips
|
|
333
|
+
those checks:
|
|
334
|
+
|
|
335
|
+
- The `config.command`, `config.mcp`, and `config.agent` containers are built
|
|
336
|
+
prototype-free (`Object.create(null)`), so no key can ever resolve to an
|
|
337
|
+
inherited member such as `toString`, and `__proto__`/`constructor` can
|
|
338
|
+
never take effect through inheritance.
|
|
339
|
+
- The skill name inside a slash-command template is rendered as a quoted data
|
|
340
|
+
value (JSON string encoding), never wrapped in backticks, so it cannot
|
|
341
|
+
break out into surrounding instruction text.
|
|
342
|
+
- Frontmatter `description` strings pass through the same control-character
|
|
343
|
+
sanitizer used for log lines before they enter config.
|
|
344
|
+
|
|
345
|
+
### Migrating from 1.x
|
|
346
|
+
|
|
347
|
+
`scanNodeModules` used to default to `true`; it now defaults to `false`. If you
|
|
348
|
+
distribute an Agent Plugins package via npm and consumers relied on it being
|
|
349
|
+
picked up from the project's `node_modules`, they must now opt in explicitly:
|
|
350
|
+
|
|
351
|
+
```json
|
|
352
|
+
{
|
|
353
|
+
"plugin": [["opencode-skill-autodiscovery", { "scanNodeModules": true }]]
|
|
354
|
+
}
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
Prefer shipping your package as a regular opencode plugin
|
|
358
|
+
(`"plugin": ["your-package"]`) instead: opencode installs it into its own cache,
|
|
359
|
+
where you can pick it up explicitly with `"scanCache": true` — no
|
|
360
|
+
scan of the dependency tree required.
|
|
93
361
|
|
|
94
362
|
## VPS / remote hosts (SSH sessions)
|
|
95
363
|
|
|
96
|
-
Discovery is **machine-local**: a remote session only sees the skills, agents,
|
|
364
|
+
Discovery is **machine-local**: a remote session only sees the skills, agents,
|
|
365
|
+
and packages installed **on that host**. So opencode (and this plugin) must be
|
|
366
|
+
installed on each remote machine, and you run opencode inside the SSH session —
|
|
367
|
+
not from a local client terminal.
|
|
97
368
|
|
|
98
369
|
**On each remote host:**
|
|
99
370
|
|
|
@@ -120,27 +391,57 @@ Or hand-edit the remote's global config (`~/.config/opencode/opencode.json`):
|
|
|
120
391
|
```
|
|
121
392
|
|
|
122
393
|
**What a remote session discovers** (that host's own installs):
|
|
123
|
-
- VS Code Remote-SSH synced skills from `~/.vscode-server/data/agentPlugins/`
|
|
394
|
+
- VS Code Remote-SSH synced skills from `~/.vscode-server/data/agentPlugins/`
|
|
395
|
+
(read via the `cache.json` LRU).
|
|
124
396
|
- Claude Code remote plugins from `~/.claude/remote/plugins/`.
|
|
125
|
-
- Agent Plugins packages in the remote's opencode cache
|
|
397
|
+
- Agent Plugins packages in the remote's opencode cache
|
|
398
|
+
(`~/.cache/opencode/packages/*`) and the project's `node_modules`.
|
|
126
399
|
|
|
127
400
|
**Caveats:**
|
|
128
|
-
- Run opencode **on the remote**. A local client terminal reads the local
|
|
129
|
-
|
|
130
|
-
-
|
|
401
|
+
- Run opencode **on the remote**. A local client terminal reads the local
|
|
402
|
+
machine's manifests, not the remote's.
|
|
403
|
+
- VS Code only syncs **enabled** skills to the server as a flattened "VS Code
|
|
404
|
+
Synced Data" bundle; marketplace clones (including their `agents/*.md`
|
|
405
|
+
files) are generally not copied. Install the pack on the remote too if you
|
|
406
|
+
want its agents there.
|
|
407
|
+
- To force a re-fetch of a new release on a remote, clear the cached copy and
|
|
408
|
+
restart (opencode re-downloads the latest):
|
|
131
409
|
```sh
|
|
132
410
|
rm -rf ~/.cache/opencode/packages/opencode-skill-autodiscovery*
|
|
133
411
|
```
|
|
134
412
|
|
|
135
413
|
## Notes
|
|
136
414
|
|
|
137
|
-
- Discovery is inherently **machine-local**: it reads manifests from the home
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
- VS Code
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
-
|
|
415
|
+
- Discovery is inherently **machine-local**: it reads manifests from the home
|
|
416
|
+
directory of the machine opencode is running on. Remote sessions on a
|
|
417
|
+
different machine will discover that machine's skills.
|
|
418
|
+
- On a remote host, VS Code does **not** copy your marketplace plugins over.
|
|
419
|
+
It syncs only the enabled skills/agents/etc. from your client as a single
|
|
420
|
+
flattened "VS Code Synced Data" bundle under
|
|
421
|
+
`~/.vscode-server/data/agentPlugins/`. That is why you won't see the
|
|
422
|
+
original `{org}/{repo}` layout on the remote — the skill directories are
|
|
423
|
+
named after the skills instead. This plugin reads `cache.json` to locate
|
|
424
|
+
those materialized bundles.
|
|
425
|
+
- Newer VS Code layouts have no `installed.json` at all; marketplaces are
|
|
426
|
+
cloned directly under the agent plugin dir. The plugin falls back to a tree
|
|
427
|
+
walk only when no manifest is present, so it never surfaces skills from
|
|
428
|
+
cloned-but-uninstalled marketplaces on setups that do have a manifest.
|
|
429
|
+
- VS Code account sync only syncs your marketplace extension list; each machine
|
|
430
|
+
still has its own `installed.json`/`cache.json` that this plugin reads.
|
|
431
|
+
- A package is only treated as an Agent Plugins package when its root
|
|
432
|
+
`plugin.json` declares a `$schema` under `https://agent-plugins.org/schemas/`.
|
|
433
|
+
Everything else is ignored by the cache/node_modules scanners and falls back
|
|
434
|
+
to the tree walk elsewhere. This check identifies format only — never
|
|
435
|
+
provenance or safety: any package can copy the literal schema URL, so a
|
|
436
|
+
conformant manifest does not make scanning an untrusted directory safe.
|
|
437
|
+
Content is trusted based on where it came from (host-installed manifests vs.
|
|
438
|
+
side-effect locations), not on its shape.
|
|
439
|
+
- The same plugin can be materialised in several VS Code layouts at once
|
|
440
|
+
(local clone + synced bundle + marketplace clone). `skills.paths` keeps all
|
|
441
|
+
paths; slash commands and MCP servers are de-duplicated so mirrors don't
|
|
442
|
+
create a wall of `/mirror-of-...` junk.
|
|
443
|
+
- The plugin is a no-op when no manifests or conformant packages exist, or when
|
|
444
|
+
they contain no skills.
|
|
144
445
|
|
|
145
446
|
## Development
|
|
146
447
|
|
|
@@ -153,6 +454,5 @@ npm test # build + node --test (fixture-based unit tests)
|
|
|
153
454
|
Publish:
|
|
154
455
|
|
|
155
456
|
```sh
|
|
156
|
-
npm pack # local tarball (no publish needed for testing)
|
|
157
457
|
npm publish --access public
|
|
158
458
|
```
|
package/dist/agents.d.ts
CHANGED
|
@@ -11,8 +11,10 @@ export type AgentConfig = {
|
|
|
11
11
|
color?: string;
|
|
12
12
|
maxSteps?: number;
|
|
13
13
|
};
|
|
14
|
-
|
|
14
|
+
type ReadAgentsResult = Array<{
|
|
15
15
|
name: string;
|
|
16
16
|
agent: AgentConfig;
|
|
17
17
|
}>;
|
|
18
|
+
export declare function readAgents(pkg: PluginPackage): ReadAgentsResult;
|
|
19
|
+
export {};
|
|
18
20
|
//# sourceMappingURL=agents.d.ts.map
|
package/dist/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAIpD,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,KAAK,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAwKF,KAAK,gBAAgB,GAAG,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAAC;AAkBpE,wBAAgB,UAAU,CAAC,GAAG,EAAE,aAAa,GAAG,gBAAgB,CAQ/D"}
|