Skip to content

config: watcher.ignore is not applied to plugin entrypoints and the config-directory watcher #55934

Open

Description

@lookson111

Summary

The watcher.ignore config key is declared in the opencode config schema but is never consulted for plugin hot-reload. Its only consumer applies ignore to config documents only, while the config-directory watcher and each plugin entrypoint watcher start unconditionally with fixed ignores, so watcher.ignore cannot suppress watching of a plugin directory or plugin files.

Environment

  • opencode version: 2.0.28
  • OS: Linux 7.0.0-38-generic x86_64
  • Terminal: TERM_PROGRAM=vscode, TERM=xterm-256color, COLORTERM=truecolor
  • Shell: /bin/bash
  • Install/channel: release build at ~/.opencode/bin/opencode
  • Active plugins: 5 local plugins under .opencode/plugins/ (agentgate.js, instructions.js, limits.js, readonly.js, taskgate.js); no plugins from config entries

Reproduction

  1. Create a workspace with plugins under .opencode/plugins/ (e.g. taskgate.js importing a helper from .opencode/lib/).
  2. Add "watcher": { "ignore": [".opencode/plugins", "**/.opencode/plugins/**"] } to opencode.json (the bug also reproduces with the key absent).
  3. Start opencode and inspect ~/.local/share/opencode/log/opencode.log for watcher started / loading plugin lines.
  4. Edit a plugin file while opencode runs.

Expected Behavior

watcher.ignore should suppress watching of the listed paths, including the plugin directory and the plugin files, so no watcher is started for them and plugin HMR does not trigger on edits under ignored paths.

Actual Behavior

watcher.ignore is only applied to config documents. The single consumer of the key filters by document type:

if (type !== "document" || !info.watcher?.ignore) continue;
l.add(h.info.watcher.ignore)

Meanwhile the config-directory watcher and every plugin entrypoint watcher start regardless of watcher.ignore, using hard-coded defaults (["node_modules",".git","**/{node_modules,.git}/**"]). The log shows the directory watcher with 3 built-in ignores, then each plugin file watched with no ignores, immediately followed by plugin loading:

watcher started path=.../.opencode type=directory backend=inotify ignores=3
loading plugin id=.../.opencode/plugins/taskgate.js entrypoint=file://.../taskgate.js
watcher started path=.../.opencode/plugins/taskgate.js type=file backend=node ignores=0

So plugin HMR keeps working for paths that watcher.ignore is documented to ignore, and the config key has no effect on it.

Additional Context

  • In our project watcher.ignore is not set at all; the impact is reduced because plugin sources live in a separate directory and are published by an install step, but HMR still watches them with ignores=0.
  • Reproducible consistently on every start; not intermittent.
  • No workaround via config found; suppressing plugin reload currently requires not placing files under watched plugin paths.
  • Related code locations: config schema (watcher.ignore entry) and the opencode.config.location-watcher plugin.

Activity

  1. opencode-agent commented on Oct 16, 2026

    @opencode-agent
    Contributor

    Thanks for the detailed write-up, @lookson111!

    In the current code, watcher.ignore only applies to the workspace file watcher. The entry.type !== "document" check in opencode.config.location-watcher collects the ignore patterns from each config document (each opencode.json). It doesn't limit them to document files. Plugin hot-reload and the .opencode config-directory watcher are a separate mechanism with fixed ignores, and the docs don't say watcher.ignore covers them. So this works as currently designed.

    That makes this a feature request: a way to stop watching plugin files and the config directory, or to turn off plugin hot-reload, either through watcher.ignore or a dedicated option. The maintainers will consider it. A plugin can't do this, because those watchers belong to the core plugin/config loader.

    Related issues about config/plugin reload watching:

  2. Dante-dan commented on Oct 16, 2026

    @Dante-dan
    Contributor

    I’d propose an explicit opt-in watcher.reloadIgnore rather than changing watcher.ignore: existing workspace exclusions should not silently start disabling config/plugin reloads.

    Proposed contract:

    • reloadIgnore affects automatic reload watches for config directories and local plugin sources, including imported local dependencies. It does not affect discovery or initial loading. Unset means current behavior.
    • Relative paths/globs are rooted at the directory of the config document declaring them; absolute paths stay absolute. Rules are additive across documents, with no negation/override syntax. Matching a directory excludes its descendants. A project rule can name .opencode/plugins/**; out-of-tree sources require a matching absolute rule or a rule declared alongside that source.
    • Apply the same matching decision to both routes: recursive config-directory events and plugin-source subscriptions. Otherwise excluding the plugin’s direct watch still leaves the config-directory route able to trigger reload. Skip excluded source subscriptions; prune excluded subtrees from directory watches and filter events consistently. Keep config documents observable through targeted watches so removing an exclusion takes effect without restarting.

    The current split is in ConfigWatch.plan and PluginModule.make. The latter also watches missing dependencies via an existing ancestor, so exclusions must be evaluated against the requested source path before selecting that fallback target.

    Acceptance checks would cover initial loading of excluded plugins, edits through either watch route causing no reload, ordinary config/non-excluded plugin edits still reloading, removing an exclusion, imported/missing dependencies, and relative/absolute/glob rules across global and project configs. This is based on code inspection, not a runtime reproduction; it does not address debounce or plugin disposal in the related reports.

    Would a core maintainer approve this approach and config surface?

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

Metadata

Metadata

Assignees

No one assigned

    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