Skip to content

[BUG] plugin package.json drifts from config: stale deps never cleaned, missing deps cause slow startup #30526

Closed as not planned

Description

@wjiuxing

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:

  1. Stale deps remain — e.g. replacing @tarquinen/opencode-dcp with open-mem in config leaves the old entry in package.json forever
  2. Missing deps — plugins added directly to config JSON are never added to package.json; startup must resolve them from scratch each time
  3. Slow startup — each plugin calls Npm.add() → individual reify(), N plugins = N sequential npm operations instead of one batch install
  4. No plugin remove command — there is no way to cleanly uninstall a plugin from both config and package.json

Reproduction

Scenario A (CLI path — stale entries):

  1. opencode plugin install --global some-plugin@1.0.0 → package.json gets {"some-plugin": "1.0.0"}
  2. opencode plugin install --global replacement@2.0.0 → config is updated, but some-plugin stays in package.json
  3. Repeat → package.json accumulates orphaned deps

Scenario B (config edit path — missing entries + slow startup):

  1. Edit ~/.config/opencode/opencode.json: add "new-plugin@^1.0.0" to plugin array
  2. Delete node_modules (or start fresh)
  3. Restart opencode → startup takes seconds as each plugin is resolved individually via Npm.add() → reify()
  4. After startup, package.json may 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 declarations

Environment

  • OpenCode: v1.15.13
  • OS: macOS
  • Config: global (opencode.json + tui.json)
  • 8 plugins across opencode.json + tui.json

Activity

  1. github-actions commented on Aug 3, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

  2. niStee commented on Aug 13, 2026

    @niStee

    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.

  3. niStee commented on Aug 13, 2026

    @niStee

    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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions