Repository navigation
Releases
v1.2.0
v1.2.0
skill_management gives every installed OpenCode skill a switch: list what is installed, find out
why one will not load, hide it from the model, and put it back. Disabling writes one global
skill permission rule — no skill file is ever moved, edited or deleted.
No breaking changes in 1.2.0. The four actions, their inputs, the JSON envelope, the written
rule shape, the same-line marker and the single-file layout are unchanged since 1.1.0.
Fixed since v1.1.0
listno longer reports "N skills differ across other agents" on every call.
summary.divergedcounted hidden internal agents (title,summary, …), which carry
{*,*,deny}; on a real machine that made every skill look divergent (33/33) and added a noise
warning to eachlist. Only visible agents are compared now;inspect.agents[]still lists
every agent, so the detail is not lost.- Frontmatter booleans no longer contradict each other.
disable-model-invocation: "false"is a
string, andBoolean("false") === true, soinspectreported it astruewhilelistreported
autoinvoke: falsefor the same file.true/yes/on/1andfalse/no/off/0(quoted or not) are
normalized now, and both fields go through the same rule. - A failed sidecar write no longer turns a successful config change into an error. Once the
config was written and verified, a sidecar write failure used to surface asinternal_error: the
file really changed, but the result had nochanged/diff. It now returnsok: true,
changed: true, keepsdiff, setssidecar: null, and explains itself inwarnings. The config
change is not rolled back. enableis no longer blocked by an unrelated file's syntax error. It reads sidecar-recorded
config files alongside the target; before, any of them failing to parse plus "no candidate rule"
producedconfig_unparsable. The hard refusal now applies only to the target config file;
other files are reported inwarningsby name.
Also since v1.1.0
- The installer checks before writing. If
opencode.json(c)already lists this package in
plugins, or a second copy sits inplugins/, it refuses and exits with code2, leaving every
file untouched — two sources make the host reportDuplicate plugin IDand fail one of them. - New installer flags:
--check/--dry-run,--uninstall(deletes only this plugin's file),
--npm(asserts the npm channel, never writes),--force,--json.<config>resolves the way
the host resolves it ($OPENCODE_CONFIG_DIRfirst). - The plugin exports
PLUGIN_VERSION, asserted againstpackage.jsonby the harness. - The acceptance harness grew from 260 to 326 assertions, and now validates every action's
outputagainst the declared JSON Schema (required fields, property types,error.code/
error.message) and asserts thatcontentis the same data asoutput. - README is down to 51 lines; field tables, installer flags, package contents, usage and the test
workflow moved to the documentation site. - The documentation site gained Design decisions (why permission rules instead of policies, why
there is noallow, why global-only, why text edits plus a same-line marker, why the sidecar is
only an audit trail — each with its cost) and Development & verification (harness sections,
CI, the performance and release gates, npm package contents, why two copies cannot coexist).
The Permissions page now expands Effects into adenyvsaskcomparison with three real
skill examples. English and Chinese stay mirrored.
What it does
| Action | What you get | Writes config |
|---|---|---|
list |
Every registered skill and its effective state for the calling agent (available / disabled / ask) |
No |
inspect |
Why one skill is (not) usable: the matched rule, per-agent differences, how the id was derived | No |
disable |
Hidden from the model-visible list and rejected at load (effect: "ask" keeps it listed with confirmation) |
Yes |
enable |
Deletes the one blocking rule, whoever wrote it | Yes |
Permissions are evaluated for the calling agent only: last match wins (findLast, with *
and ? globs), and no match falls back to ask, not allow.
The rule disable writes is exactly this line (the trailing comment is the provenance marker
enable uses):
Writes are atomic, preserve indentation, key order, line endings (CRLF/LF) and BOM, keep one
backup, and are verified by reloading the host and polling the caller's rule set; a write the host
did not pick up is rolled back.
Usage
skill_management({ action: "list" })
skill_management({ action: "inspect", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell", dryRun: true })
skill_management({ action: "disable", id: "windows-shell", effect: "ask" })
skill_management({ action: "enable", id: "windows-shell" })
Full field tables and error codes are on the four action pages of the docs site:
https://sogeisetsu.github.io/opencode-skill-management/
Install
| Channel | Command / asset | Notes |
|---|---|---|
| npm (recommended) | opencode plugin add opencode-skill-management@1.2.0 |
Installs the package and registers it in the global config; no token needed |
| git spec | opencode plugin add github:sogeisetsu/opencode-skill-management |
Bypasses the npm registry |
| Single file | asset skill_management.js |
Drop it into <config>/plugins/; best for offline use |
| npm tarball | asset opencode-skill-management-1.2.0.tgz |
The same tarball npm publishes; for auditing or internal mirrors |
Verify it loaded in a new process:
opencode plugin list --builtin
# skill_management 1.2.0 opencode-skill-management@1.2.0Compatibility and verification
- Targets OpenCode V2. 1.1.0 was accepted on host 2.0.32 on Windows and Linux.
- 1.2.0 was re-checked on host 2.2.0 (Windows) in a new process:
list,inspect,disable
andenablecalled in sequence, the config restored byte for byte afterwards, and every
result accepted by the host'soutputvalidation. node tools/verify.mjs: 326 assertions pass (sections A–J), entirely against a temporary
config directory.- The same harness runs on
ubuntu-latestandwindows-latestin CI, with no secrets. - One file, pure Node built-ins, no runtime dependencies, no shell calls.
Full diff: v1.1.0...v1.2.0
v1.1.0
v1.1.0
skill_management gives every installed OpenCode skill a switch: list what is installed, find out
why one will not load, hide it from the model, and put it back. Disabling writes one global
skill permission rule — no skill file is ever moved, edited or deleted.
Breaking change in 1.1.0
All four actions now return one JSON object instead of Markdown. The tool also declares an
output JSON Schema, so the host validates every result; this is a real structured tool result,
not Markdown wrapped in a JSON string.
- Envelope:
schemaVersion(currently1),action,ok,warnings. - Failures use
ok: falsepluserror: { code, message }; success is no longer inferred from
✅ / ❌ in the text. listreturnscallerAgent,summary,skills[],config,sidecar,consistency.
descriptionis still the first line, no longer truncated to 60 characters.inspectreturnsskill,idDerivation,agents[],caller,conclusion. Missing and
ambiguous targets useskill_not_found/name_ambiguous.disable/enablereturnchanged,reason,before,diff,verification,backup,
sidecar;enablealso returnslocation,rule,candidates,relatedRules.enablenow returnsok: falsewithconfig_unparsablewhen the target config cannot be
parsed and no candidate rule exists, instead of claiming "nothing to change".
Nothing else broke: actions, input fields, the written rule shape, the same-line marker format
and the single-file layout are unchanged.
Also since v1.0.0
- New bilingual documentation site (Astro + Starlight; English at the root, Chinese under
/zh-cn): https://sogeisetsu.github.io/opencode-skill-management/ - README now documents the minimal npm install (
opencode plugin add) and the package contents. - Theme/language menus, card alignment and mobile spacing fixes on the docs site.
What it does
| Action | What you get | Writes config |
|---|---|---|
list |
Every registered skill and its effective state for the calling agent (available / disabled / ask) |
No |
inspect |
Why one skill is (not) usable: the matched rule, per-agent differences, how the id was derived | No |
disable |
Hidden from the model-visible list and rejected at load (effect: "ask" keeps it listed with confirmation) |
Yes |
enable |
Deletes the one blocking rule, whoever wrote it | Yes |
Permissions are evaluated for the calling agent only: last match wins (findLast, with *
and ? globs), and no match falls back to ask, not allow.
The rule disable writes is exactly this line (the trailing comment is the provenance marker
enable uses):
{ "action": "skill", "resource": "windows-shell", "effect": "deny" } // skill_managementWrites are atomic, preserve indentation, key order, line endings (CRLF/LF) and BOM, keep one
backup, and are verified by reloading the host and polling the caller's rule set; a write the
host did not pick up is rolled back.
Usage
skill_management({ action: "list" })
skill_management({ action: "inspect", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell", dryRun: true })
skill_management({ action: "disable", id: "windows-shell", effect: "ask" })
skill_management({ action: "enable", id: "windows-shell" })
A list result looks like this (abridged):
{
"schemaVersion": 1,
"action": "list",
"ok": true,
"warnings": [],
"callerAgent": { "id": "build", "verified": true },
"summary": { "total": 29, "disabled": 1, "ask": 0, "diverged": 2, "derivedIds": 0 },
"skills": [
{ "id": "windows-shell", "state": "available", "source": "config", "path": "..." },
{ "id": "pdf-tools", "state": "disabled", "source": "catalog", "path": "..." }
],
"config": { "file": ".../opencode.jsonc", "hadPermissions": true, "notes": [] },
"sidecar": { "file": ".../skill_management.state.json", "ok": true, "error": null },
"consistency": { "staleSidecarIds": [], "unrecordedRules": [], "missingFromRegistry": [] }
}Full field tables and error codes are on the four action pages of the docs site.
Install
| Channel | Command / asset | Notes |
|---|---|---|
| npm (recommended) | opencode plugin add opencode-skill-management@1.1.0 |
Installs the package and registers it in the global config; no token needed |
| git spec | opencode plugin add github:sogeisetsu/opencode-skill-management |
Bypasses the npm registry |
| Single file | asset skill_management.js |
Drop it into <config>/plugins/; best for offline use |
| npm tarball | asset opencode-skill-management-1.1.0.tgz |
The same tarball npm publishes; for auditing or internal mirrors |
Verify it loaded in a new process:
opencode plugin list --builtin
# skill_management 1.1.0 opencode-skill-management@1.1.0Compatibility and verification
- Targets OpenCode V2; 1.1.0 was accepted on host 2.0.32 on Windows and Linux.
node tools/verify.mjs: 260 assertions pass, entirely against a temporary config directory.- New-process runs against the real host returned parseable JSON for both
listand the
"skill not found" error path, and both passed the host'soutputvalidation. - One file, pure Node built-ins, no runtime dependencies, no shell calls.
Full diff: v1.0.0...v1.1.0
v1.0.0
What this is
skill_management adds a switchboard for the skills installed in your OpenCode setup: see what is installed, find out why one will not load, take one out of the model's view, and put it back.
Disabling never touches skill files. It writes one skill permission rule into your global config (effect: "deny", or "ask" for a soft disable). Nothing is deleted, moved or rewritten, and removing that rule restores the skill exactly as it was.
This is the first stable release: the surface described below is frozen for 1.x.
What it does
| Action | What it does | Writes config |
|---|---|---|
list |
Every registered skill with its effective state for the calling agent (available / disabled / ask) |
no |
inspect |
Why one skill is (un)available: the matching rule, per-agent differences, how its id was derived | no |
disable |
Hide it from the model and reject loading it (effect: "ask" keeps it listed and asks on load) |
yes |
enable |
Remove the blocking rule again, whoever wrote it | yes |
How a permission is resolved: the host reads the rule set of the agent that is calling, takes the last match (findLast) with * / ? globs, and — when nothing matches — falls back to ask, not allow. That is why list and inspect evaluate per agent and name the agent they used.
disable writes exactly this line (the trailing comment is the origin marker that enable uses):
{ "action": "skill", "resource": "windows-shell", "effect": "deny" } // skill_managementWith deny the skill disappears from the model-visible skill list, and loading it through the built-in skill tool is rejected (permission.rejected).
How to use
skill_management({ action: "list" })
skill_management({ action: "inspect", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell" })
skill_management({ action: "disable", id: "windows-shell", dryRun: true })
skill_management({ action: "disable", id: "windows-shell", effect: "ask" })
skill_management({ action: "enable", id: "windows-shell" })
idis the primary key from the host registry;nameworks as an alias when it is unambiguous (an ambiguous name returns candidates, never a guess).dryRun: trueprints the exact lines that would be written and writes nothing.- Writes are atomic (
.tmp+ rename) and preserve indentation, key order, line endings (CRLF/LF) and BOM. One backup is kept under<config>/skill_management.backups/— it is a full copy of the config, which may contain credentials, so delete it if you keep secrets there. - After writing, the tool reloads the host and polls the agent rule sets to confirm the change actually landed; if it did not, the file is rolled back.
enableremoves the one blocking rule for that id — it does not have to be a rule this tool wrote. It refuses, and shows you the rule, when it cannot uniquely identify it, or when the blocker is a wildcard rule, an agent-levelpermissionsentry, or a policy (those are only reported, never edited).
What's changed since v0.2.0
Fixed — disable with effect: "ask" could report success without doing anything. The read-back check asked "is the caller's effective effect not deny?". The host falls back to ask when no rule matches, and an agent's own rule can sit after the global one and turn it into allow — neither is deny, so the check passed even when the host had never loaded the rule. It now verifies that the number of matching { skill, id, ask } rules in the caller's rule set actually went up (0 → 1 appears in the output). If the rule was loaded but a later agent rule overrides it, the output says so instead of claiming it took effect.
Fixed — a single-line permissions array produced invalid JSON. With "permissions": [{ … }] all on one line, adding a rule put the comma after the new entry, leaving a config the host could not parse. Insertion now handles the one-line case.
Fixed — a damaged state file could be trusted, and a broken config threw. <config>/skill_management.state.json is now rejected as a whole when it is not valid JSON, has no rules array, or carries an unknown version; list reports it instead of silently ignoring it. enable now reports a syntactically broken config file instead of throwing.
Faster. list no longer parses the target config twice, glob matching skips the regex when the pattern has no wildcard, and strict JSONC validation no longer spreads the whole file into a character array. Measured on 29 skills × 12 agents × 10 rules over a 13.6 KB config: 3.72 ms → 2.24 ms median (60 runs).
Frozen for 1.x: the four actions' inputs and output semantics, the rule shape above, the same-line marker comment, and the single-file plugin layout. Breaking any of them requires 2.0.0.
Maintenance. Publishing from CI is idempotent: re-running the publish workflow for an already published version exits successfully instead of failing.
Install or upgrade
Pick one. Recommended: the git spec — no registry lookup and no token.
opencode plugin add github:sogeisetsu/opencode-skill-management# npm
opencode plugin add opencode-skill-management@1.0.0Offline / single file (no registry, no npm): download skill_management.js from the assets below and drop it into <config>/plugins/ — %USERPROFILE%\.config\opencode\plugins\ on Windows, ~/.config/opencode/plugins/ elsewhere. The host scans that directory at startup; no config entry is needed.
opencode plugin list --builtin
# skill_management local <config>/plugins/skill_management.js
--builtinis required: without it, directory plugins are not listed and you will seeNo plugins found.
Which asset is which
| Asset | What it is | When to use it |
|---|---|---|
skill_management.js |
the whole plugin, one file | drop into <config>/plugins/ — no npm, no network |
opencode-skill-management-1.0.0.tgz |
the exact npm tarball (npm pack output) |
unpack it yourself, mirror it internally, or check what npm is actually serving |
Source code (zip) / Source code (tar.gz) are GitHub's automatic snapshots of the repository; the two assets above are the installable artifacts.
Requirements and what was verified
- OpenCode V2, tested on 2.0.32 (Windows and Linux). Other V2 builds are not claimed — the host APIs used here are exercised end-to-end by the acceptance suite, so an upgrade is easy to re-check.
- Node 18+; only
node:built-ins, no dependencies, no shell calls. - 248 assertions pass on Windows and on Linux (
node tools/verify.mjs). Thedisable → rejected load → enableround trip was re-run on a real host in a fresh process: the skill disappears from the model-visible list, loading it returnspermission.rejected, andenablerestores the config byte for byte.
Limits. Only the global config's skill permission rules are written — not project config, not skill files, not policies. ask is a soft disable: the skill stays listed and each load asks. list's state column is evaluated for the calling agent, so sub-agents that deny everything by default will show different states; inspect prints the per-agent table.
Full changelog: v0.2.0...v1.0.0
v0.2.0
What this is
skill_management puts a control panel on the skills installed in your OpenCode setup. It can list them, explain why one is unavailable, hide one from the model, and bring it back.
Disabling never touches skill files: it writes a single skill permission rule into your global config (effect: "deny", or "ask" for a soft disable). Nothing is deleted, moved or rewritten, and removing the rule restores the skill exactly as it was.
| Action | What it does | Writes config |
|---|---|---|
list |
Every registered skill with its effective state for the calling agent (available / disabled / ask) |
no |
inspect |
Why one skill is (un)available: the matching rule, per-agent differences, how its id was derived | no |
disable |
Hide it from the model and reject loading it (effect: "ask" keeps it listed and asks on load) |
yes |
enable |
Remove the blocking rule again, whoever wrote it | yes |
Install in one command: opencode plugin add github:sogeisetsu/opencode-skill-management (details below).
What's changed since v0.1.0
Fixed — list reported the wrong state. It merged every agent's permission rules before evaluating them, so deny rules owned by sub-agents (explorer, title, …) leaked into the caller's result: on a stock setup 28 of 29 skills showed up as disabled while inspect said they were available. States are now evaluated against the calling agent only — the output names that agent, and adds a one-line hint when other agents disagree (which is normal for sub-agents that deny everything).
Added — one-command verification on Linux / WSL. tools/linux-verify.sh puts Node and the OpenCode CLI in a temporary directory under $HOME, runs the full acceptance suite (175 assertions), confirms a real OpenCode host loads the plugin, then cleans up after itself. No sudo, no writes to your real config.
Docs — README rewritten for users. It now leads with the problem the tool solves and explains the four actions (list / inspect / disable / enable): what each one does, when to reach for it, and whether it writes configuration.
Verified. 175 assertions pass on Windows and on Linux (Debian 13.5, OpenCode 2.0.32). The disable → rejected load → enable round trip was re-run on a Linux host: the skill disappears from the model-visible list, loading it is rejected, and enable restores it.
Install or upgrade
# via git (no registry, no token)
opencode plugin add github:sogeisetsu/opencode-skill-management
# via npm
opencode plugin add opencode-skill-management@0.2.0Offline / single file: download skill_management.js from the assets below and drop it into <config>/plugins/. The opencode-skill-management-0.2.0.tgz asset is the exact npm tarball if you prefer to unpack it yourself.
Confirm it is loaded:
opencode plugin list --builtin
# skill_management local <config>/plugins/skill_management.jsFull changelog: v0.1.0...v0.2.0
{ "action": "skill", "resource": "windows-shell", "effect": "deny" } // skill_management