Repository navigation
Description
Description
OpenCode manages plugins through two paths that are poorly synchronized:
Path 1: opencode plugin install --global — Uses @npmcli/arborist reify with save: true to append the plugin to package.json. This is append-only: it never removes entries for plugins that were previously installed but are no longer in config.
Path 2: Direct config edit + restart — Users edit opencode.json/tui.json directly and restart. On startup, Loader.resolve() calls Npm.add() for each plugin individually, which calls reify() per-plugin (no batch). If node_modules is missing or stale, each plugin triggers a separate npm resolve+install.
Over time, package.json drifts from the actual plugin declarations in config:
- Stale deps remain — e.g. replacing
@tarquinen/opencode-dcpwithopen-memin config leaves the old entry inpackage.jsonforever - Missing deps — plugins added directly to config JSON are never added to
package.json; startup must resolve them from scratch each time - Slow startup — each plugin calls
Npm.add()→ individualreify(), N plugins = N sequential npm operations instead of one batch install - No
plugin removecommand — there is no way to cleanly uninstall a plugin from both config andpackage.json
Reproduction
Scenario A (CLI path — stale entries):
opencode plugin install --global some-plugin@1.0.0→package.jsongets{"some-plugin": "1.0.0"}opencode plugin install --global replacement@2.0.0→ config is updated, butsome-pluginstays inpackage.json- Repeat →
package.jsonaccumulates orphaned deps
Scenario B (config edit path — missing entries + slow startup):
- Edit
~/.config/opencode/opencode.json: add"new-plugin@^1.0.0"topluginarray - Delete
node_modules(or start fresh) - Restart opencode → startup takes seconds as each plugin is resolved individually via
Npm.add()→reify() - After startup,
package.jsonmay still not reflect all declared plugins (only the ones that needed install)
Root Cause (source analysis)
| File | Behavior |
|---|---|
packages/core/src/npm.ts L78-108 |
reify() uses save: true, saveType: "prod" — append-only, no prune |
packages/core/src/npm.ts L138-186 |
install() checks declared vs locked but never removes stale entries |
packages/opencode/src/plugin/install.ts |
patchPluginList() only modifies config JSON (opencode.json/tui.json), never package.json |
packages/opencode/src/plugin/loader.ts L94 |
Each plugin calls resolvePluginTarget() → Npm.add() → individual reify() |
packages/opencode/src/cli/cmd/plug.ts |
No remove/uninstall subcommand for plugins |
Suggested Fix
A. Sync package.json from config on startup — Before the per-plugin resolve loop, rebuild package.json dependencies to match the union of all plugin specifiers in opencode.json + tui.json, then run a single batch reify(). This would:
- Remove stale entries
- Add missing entries
- Replace N individual reify calls with one batch install
B. Add opencode plugin remove <module> command — Removes plugin from config JSON and prunes package.json entry. Gives users explicit control.
Workaround
Users experiencing long "loading plugins" delays on startup can manually clean up:
# Regenerate package.json from config, then batch install (eliminates the slow per-plugin resolve)
cd ~/.config/opencode
rm -rf node_modules package.json package-lock.json
# Restart opencode — it will regenerate from config declarationsEnvironment
- OpenCode: v1.15.13
- OS: macOS
- Config: global (opencode.json + tui.json)
- 8 plugins across opencode.json + tui.json
Activity
github-actions commentedon Aug 3, 2026
To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.
niStee commentedon Aug 13, 2026
Reopened for reassessment: the linked contributor PR was closed by automated stale-PR cleanup rather than merged. The issue should be re-triaged against current
dev; a refreshed PR should follow the current contribution template and include current verification.niStee commentedon Aug 13, 2026
Correction: GitHub rejected my reopen request because this account lacks repository permission to reopen issues. The issue remains closed. The recommendation is a fresh issue or maintainer-assisted reopen only if the behavior is still reproducible on current
dev; the old PR should not be reopened unchanged.