npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dustindu/opencode-cabinet

v0.0.2

Published

A shareable agent cabinet for opencode: registers agents, skills, commands, MCP servers and providers from one plugin.

Readme

opencode-cabinet

A single opencode plugin that installs a whole agent cabinet — 8 specialist agents, 40 skills, 12 commands, MCP servers, model providers, the shared L1 norms, and a hard guard that keeps multi-step work state on disk — and wires in its supporting tools for you: the codebase-memory-mcp code graph and the opencode-mem persistent memory.

Instead of copying a pile of files into ~/.config/opencode/, you install one package. On first load it assembles the rest.

receive → triage → dispatch → collect → review → accept → report

What you get

| Piece | Count | Notes | |---|---|---| | Agents | 8 | pmo (lead) · scout · researcher · planner · builder-lite · builder · designer · critic | | Skills | 40 | engineering domains, design, research, planning, review | | Commands | 12 | slash commands | | MCP servers | 2 by default | context7, gh_grep; MCP_DOCKER opt-in (the codebase-memory-mcp entry is owned by cbm itself) | | Providers | 4 | the cch* relay providers + their models | | Norms | L1 | injected into every agent's system prompt | | Guard | — | .pmo/ single-writer enforcement + plan-format validation |

pmo becomes the default agent. It dispatches; it doesn't write product code.

Install

  1. Add the plugin to your opencode config:
opencode plugin add @dustindu/opencode-cabinet
  1. Configure it with the wizard:
cabinet init        # or: npx @dustindu/opencode-cabinet init

It asks for your relay key, lets you pick a model per agent (prefilled from your current opencode.json), and stores the embedding key — see Configure. Prefer environment variables instead?

export CABINET_API_KEY=sk-...
  1. Reload. That's it — the first load runs the assembly in the background.

The bundled providers point at a private relay (cch.tianyou.space). You need a key for that relay, or edit content/providers.json to point at your own OpenAI-compatible endpoint. Without a key the cabinet still installs — agents just fall back to opencode's default model.

Install from a local checkout

{
  "plugin": ["/absolute/path/to/opencode-cabinet"]
}

(opencode resolves a local directory via <dir>/server then <dir>/index; this repo ships a root index.ts for exactly that.)

The assembly (cbm + memory)

On first load the plugin runs a one-time assembly, in this order:

  1. cbm — if codebase-memory-mcp is missing it runs the official installer (curl … install.sh | bash); then lets cbm configure its own opencode surface (MCP entry, skill, agents, AGENTS.md block). cbm owns that surface and keeps it up to date.
  2. repair — cbm writes a V1-shaped plugins/cbm-augment.ts that opencode v2 cannot load. The assembly strips cbm's managed block (or writes the V2 module when cbm wrote one that is V1-only, or when the file is absent).
  3. memory — adds the upstream opencode-mem plugin at a pinned spec (see memorySpec), replacing any other memory entry in the config's plugin list. A fork placed in the plugins/ directory is loaded by opencode itself and is invisible to that list — cabinet doctor reports it instead.
  4. memory config — writes ~/.config/opencode/opencode-mem.jsonc from the bundled template only if you don't have one, preconfigured for remote embeddings (no local model download) and an opencode provider for extraction.
  5. guard plugins — writes the bundled templates/plugin/*.ts (model fallback, comment / read-before-write / tool-output / empty-task-retry guards) into ~/.config/opencode/plugins/, only where one is not already present — your edits in those files are never taken back. Their tuning lives in cabinet.jsonc: model-fallback.ts reads each agent's fallback from the agents roster and the thresholds / error signatures / last-resort model from the fallback block, preferring cabinet.jsonc over a leftover model-fallback-config.json (read for compatibility only). A fresh machine then assembles into the same posture with no files copied by hand. Turn off with "guardPlugins": false.

A marker at ~/.config/opencode/.cabinet/state.json records the result.

cabinet init     # interactive configuration wizard
cabinet models   # resolve every agent's primary + fallback and show which file set them
cabinet status   # last assembly result
cabinet doctor   # read-only probe of cbm / memory / augment / guard-plugin state
cabinet setup    # reset the marker and reload to re-run the assembly
cabinet sync     # update the live guard plugins to what this package ships (backs up)

Set "assemble": false in cabinet.jsonc to manage cbm/memory yourself — cabinet then only registers its own content.

Configure — cabinet init

cabinet init is an interactive wizard that writes ~/.config/opencode/cabinet.jsonc:

  1. Relay API key — one key, shared by the bundled cch* providers.
  2. A model per agent — for the 8 cabinet agents and opencode's built-in general / explore subagents. Defaults are prefilled from your current cabinet.jsonc, else each agent's own frontmatter (and, for the two built-ins, the shipped content/roster.json), so pressing Enter keeps everything as-is. Pick by number from the printed catalog, or type any provider/model; if the chosen model has reasoning variants you're asked which one. Each agent is then asked for a fallback model — what the model-fallback guard plugin switches to when the primary errors. The whole roster is written out in full, so one block holds every agent's primary, variant and fallback.
  3. Embedding key — for llm.tianyou.space (memory). The model is fixed at BAAI/bge-m3 (1024-dim); only the key is asked.

Keys are stored in plaintext in cabinet.jsonc (and the embedding key in opencode-mem.jsonc) — the same way opencode.json stores its own provider keys. Anything overwritten is backed up to <file>.bak-<timestamp> first, and nothing is written until you confirm.

cabinet init                  # interactive
cabinet init --yes            # accept all defaults, no prompts
cabinet init --print          # print the generated config to stdout, write nothing
cabinet init --key sk-...     # supply the relay key non-interactively
cabinet init --embed-key sk-.. # supply the embedding key non-interactively
cabinet init --no-neutralize  # don't touch opencode.json's agent block
cabinet init --project        # write <cwd>/.opencode/cabinet.jsonc instead
cabinet init --reload         # run `cabinet setup` when done

Because cabinet registers after opencode's config loader, its model choices win over an agent block in opencode.json. The wizard therefore offers to remove the now-redundant agent.general / agent.explore entries for you.

Why not on install? opencode installs plugins with npm's engine but forces ignoreScripts, so a package postinstall never runs. The wizard is therefore an explicit command — and the plugin prints a reminder on load while no key is configured.

Customize

You can also edit ~/.config/opencode/cabinet.jsonc (or <project>/.opencode/cabinet.jsonc) by hand — see cabinet.example.jsonc. Highlights:

{
  "assemble": true,          // run the cbm/memory assembly
  "cbm": "auto",             // "auto" | "hint" | "off"
  "memory": true,            // install/keep opencode-mem
  "memorySpec": "[email protected]",

  // The roster: one entry per agent, primary and fallback together.
  //   "model"    primary, "provider/model"
  //   "variant"  reasoning level, one of that model's variants
  //   "fallback" model to switch to when the primary errors, "provider/model#variant"
  // Keys may be cabinet agents OR opencode built-ins (general/explore). This is
  // the single place to maintain the model system: an entry wins over the agent's
  // frontmatter default, so re-routing a model never needs a package release.
  // Drop an entry to fall back to that agent's shipped default; drop only its
  // "fallback" to use "fallback.defaultFallback" instead.
  // `cabinet models` prints every agent's primary + fallback and which source
  // won (env > agents > models > frontmatter), and flags a dangling reference.
  "agents": {
    "pmo":     { "model": "cch-codex/gpt-6.1-sol", "variant": "medium", "fallback": "cch-response/deepseek-v4.1-flash#high" },
    "builder": { "model": "cch-response/deepseek-v4.1-flash", "variant": "high" },
    "general": { "model": "cch-response/muse-spark-1.3-contributor", "variant": "xhigh" },
    "explore": { "model": "cch-open/gemini-3.8-flash" }
  },

  // Tuning for the bundled model-fallback guard plugin (thresholds, error
  // signatures, last-resort model). Per-agent chains live in "agents" above.
  // A fallback naming the very model that is failing is not a fallback: the
  // plugin then tries "defaultFallback", and if that is the same model too it
  // logs `no-distinct-fallback` and leaves the session alone instead of switching
  // in a circle. `cabinet doctor` and `cabinet models` flag that shape up front.
  "fallback": {
    "defaultFallback": { "providerID": "cch-response", "modelID": "deepseek-v4.1-flash", "variant": "high" },
    "retryThreshold": 3, "cooldownMs": 120000
  },

  "mcp": { "context7": true, "gh_grep": true, "MCP_DOCKER": false }
}

~/.config/opencode/cabinet.jsonc and a project's .opencode/cabinet.jsonc are merged: scalars from the project layer win outright, and the maps (agents, models, mcp, embedding) merge key by key — so a project file that names one agent does not erase the other nine. Everything is read when the plugin loads: editing any of these files takes effect on the next opencode restart.

The provider catalog is additive

content/providers.json describes the cch* relay: its providers, models, context limits and reasoning variants. A fresh install self-registers that whole system. Each agent's default primary lives in its own frontmatter (content/agent/<id>.md); content/roster.json holds only what has no other home — every agent's default fallback, plus the primaries of opencode's built-in general / explore. cabinet init reads both and writes the whole roster into cabinet.jsonc.

Your own declarations always win. Cabinet reads the provider block of your opencode.json / opencode.jsonc (global and project) and only supplies what you left out:

| You write | Result | |---|---| | nothing | the catalog's provider, models, limits and variants are used | | provider.cch-open.options.apiKey | your key is used, the catalog's baseURL still applies | | provider.cch-open.models.glm-5.3.limit | your limit wins, the model's other fields stay catalog-supplied | | provider.cch-open.models.glm-5.3.variants.turbo | your variant is added next to the catalog's | | provider.my-own-provider | untouched — cabinet never writes providers it doesn't ship |

opencode drops a whole provider block that fails its schema check, with only a warning in the log. Declare a limit with both context and output.

Environment overrides

| Var | Effect | |---|---| | CABINET_API_KEY | relay key for the bundled providers (a literal apiKey in cabinet.jsonc wins over it) | | CABINET_MODEL_<AGENT> | override an agent's model id (e.g. CABINET_MODEL_PMO=cch-open/glm-5.3) — wins over cabinet.jsonc and frontmatter | | CABINET_EMBED_URL / CABINET_EMBED_MODEL / CABINET_EMBED_DIM | memory embeddings (applied at setup time) | | CABINET_MEM_PROVIDER / CABINET_MEM_MODEL | memory extraction model (applied at setup time) | | CABINET_EMBED_KEY | memory embedding key (kept live via env:// in the generated config) | | CBM_BIN | path to the codebase-memory-mcp binary | | CABINET_WORKSPACE | value for {env:CABINET_WORKSPACE} (docker MCP gateway) |

Optional dependencies

The cabinet degrades gracefully when these are missing:

| Feature | Needs | Without it | |---|---|---| | Code graph tools | codebase-memory-mcp binary (auto-installed) | graph nudges are skipped, L1 code-graph block is pruned | | Docker MCP gateway | Docker + docker mcp | enable MCP_DOCKER in config | | Persistent memory | the opencode-mem plugin (auto-added) | the L1 "Memory" line is pruned automatically | | Browser | the ego-browser skill | not bundled — install it separately; web open / screenshot falls back to opencode's built-in tools |

How it works

opencode v2 loads its own agent / mcp / skill / command / provider config through plugins — the built-ins are opencode.config.agent, opencode.config.mcp, etc. A user plugin gets the same context, so this package registers everything programmatically from the bundled content/ directory:

src/index.ts        orchestrator (registers content, triggers the assembly)
src/setup.ts        the assembly: ensureCbm / repairCbmAugment / ensureMemory / writeMemoryConfig
src/content.ts      reads content/ (frontmatter, {env:VAR} substitution)
src/config.ts       reads cabinet.jsonc
src/guard.ts        .pmo/ single-writer guard (hard block via execute.before)
src/instruction.ts  injects content/AGENTS.md into every session
bin/cabinet.mjs     control CLI (init / setup / doctor / models / sync / status)
bin/init.mjs        the configuration wizard
bin/lib.mjs         shared CLI helpers
templates/          opencode-mem.jsonc + the repaired cbm-augment.ts + the guard plugins
content/            the single source of truth (agents, skills, commands, AGENTS.md,
                    providers.json = the model catalog, roster.json = default fallbacks
                    + the built-ins' primaries, mcp.json)

Compatibility

Built and verified against opencode v2.0.23–v2.0.24. The ctx.*.transform API used here is real but not formally documented, and opencode is moving fast — a future release may change it. If the cabinet stops loading, check the opencode log for loading plugin and any [opencode-cabinet] errors.

License

MIT