You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In the V2 TUI, /plugins currently mutates only the running registration. Editing any plugin source triggers reconciliation from config, which can re-enable unrelated plugins that the user manually disabled. The choice also does not survive a TUI restart.
Desired Behavior
A plugin disabled through /plugins remains disabled when:
another plugin source reloads;
its own source reloads;
the TUI restarts.
Enabling it again should remove or replace that durable preference without disturbing its package declaration or options.
Design Question
Choose a durable config representation. The simplest candidate for declared external plugins is:
Another trigger for this: switching to a different session. On 2.0.29 (macOS), I disabled several plugins in the /plugins dialog, and they were all re-enabled after I switched to another session. cli.json was unchanged throughout (no -<id> entries written).
Reading the binary, reconciliation reruns when JSON.stringify([serverTuiPlugins, config.plugins]) changes. The server plugin list is requested per location, so switching to a session in a different directory can change it and reactivate everything, not only an unrelated plugin source reload. Regression coverage for this fix could include a session or location switch.
I filed this separately as #55506 and am closing it as a duplicate of this issue.
Thanks for the report. I reproduced this on 2.2.0, and it still happens on the latest dev (a97dc23e).
Steps: I added two discovered TUI plugins, .opencode/plugins/alpha/tui.ts (repro.alpha) and .opencode/plugins/beta/tui.ts (repro.beta).
In /plugins, I disabled repro.alpha. It showed inactive, local.
I edited only beta/tui.ts and reopened /plugins. repro.alpha was active again.
I disabled it again and started a fresh TUI. It was active again.
Nothing was written to cli.json. The toggle only changes the running registration, and the reconcile step in packages/tui/src/plugin/context.tsx takes enabled from config alone, so any reconcile turns the plugin back on.
The recording and screenshots below show the whole sequence, and the script below reproduces it. I didn't test the session/location-switch trigger mentioned in the comments.
recording.mp4
Reproduction script (drive.ts)
// Repro for anomalyco/opencode#42417: a TUI plugin disabled via /plugins is// re-enabled when an unrelated plugin source reloads, and after a TUI restart.import{Effect}from"effect"import{join}from"node:path"import{writeFileSync}from"node:fs"import{OpenCodeDriver}from"opencode-drive"constplugin=(id: string,gen: number)=>`export default { id: "${id}", setup() { /* generation ${gen} */ } }\n`exportdefaultOpenCodeDriver.use({
...(process.env.OC_DEV ? {opencode: {dev: process.env.OC_DEV}} : {}),tui: {recording: true,keypressOverlay: true},project: {git: true,files: {".opencode/plugins/alpha/tui.ts": plugin("repro.alpha",1),".opencode/plugins/beta/tui.ts": plugin("repro.beta",1),},},},(oc)=>Effect.gen(function*(){const{ ui }=occonsttext=(u: typeofui)=>u.capture().pipe(Effect.map((f)=>f.lines.map((l)=>l.spans.map((s)=>s.text).join("")).join("\n")),)constuntil=(u: typeofui,re: RegExp)=>Effect.gen(function*(){for(leti=0;i<120;i++){if(re.test(yield*text(u)))returnyield*Effect.sleep("250 millis")}returnyield*Effect.die(newError("timeout waiting for "+re+"\n"+(yield*text(u))))})constalphaInactive=(u: typeofui)=>Effect.gen(function*(){for(leti=0;i<40;i++){if(/repro\.alpha[^\n]*inactive/.test(yield*text(u)))returntrueyield*Effect.sleep("250 millis")}returnfalse})constopenPlugins=Effect.gen(function*(){yield*ui.type("/plugins")yield*Effect.sleep("1 second")yield*ui.enter()yield*Effect.sleep("2 seconds")yield*until(ui,/repro\.beta/)yield*Effect.sleep("500 millis")})yield*until(ui,/commands/)yield*openPluginsyield*ui.screenshot("1-both-active")// Disable repro.alpha (first TUI entry is focused) with Enter.yield*ui.enter()yield*alphaInactive(ui)yield*ui.screenshot("2-alpha-disabled")yield*ui.press("\u001b")// Edit the UNRELATED plugin (beta) to trigger hot reload.writeFileSync(join(oc.artifacts,"files/.opencode/plugins/beta/tui.ts"),plugin("repro.beta",2))yield*Effect.sleep("4 seconds")yield*openPluginsyield*ui.screenshot("3-after-beta-reload")constnow=yield*text(ui)yield*Effect.log(now)yield*Effect.log(/repro\.alpha[^\n]*inactive/.test(now)
? "alpha still inactive after beta reload (OK)"
: "BUG: alpha re-enabled after unrelated beta reload",)yield*ui.press("\u001b")// Disable alpha again, then simulate a TUI restart with a fresh TUI.yield*openPluginsyield*ui.enter()yield*alphaInactive(ui)yield*ui.press("\u001b")constsecond=yield*oc.tuis.launch({viewport: {cols: 120,rows: 40}})yield*until(second.ui,/commands/)yield*second.ui.type("/plugins")yield*Effect.sleep("1 second")yield*second.ui.enter()yield*until(second.ui,/repro\.beta/)yield*Effect.sleep("500 millis")yield*second.ui.screenshot("4-after-restart")constrestarted=yield*text(second.ui)yield*Effect.log(restarted)yield*Effect.log(/repro\.alpha[^\n]*inactive/.test(restarted)
? "alpha still inactive after restart (OK)"
: "BUG: alpha re-enabled after TUI restart",)}),)
Run it with opencode-drive: opencode-drive run ./drive.ts. It drives opencode from your PATH; set OC_DEV to the path of an OpenCode checkout to run that from source instead.
Activity
ls-pavel-makhov commentedon Oct 14, 2026
Another trigger for this: switching to a different session. On 2.0.29 (macOS), I disabled several plugins in the
/pluginsdialog, and they were all re-enabled after I switched to another session.cli.jsonwas unchanged throughout (no-<id>entries written).Reading the binary, reconciliation reruns when
JSON.stringify([serverTuiPlugins, config.plugins])changes. The server plugin list is requested per location, so switching to a session in a different directory can change it and reactivate everything, not only an unrelated plugin source reload. Regression coverage for this fix could include a session or location switch.I filed this separately as #55506 and am closing it as a duplicate of this issue.
opencodejr commentedon Oct 28, 2026
Thanks for the report. I reproduced this on 2.2.0, and it still happens on the latest
dev(a97dc23e).Steps: I added two discovered TUI plugins,
.opencode/plugins/alpha/tui.ts(repro.alpha) and.opencode/plugins/beta/tui.ts(repro.beta)./plugins, I disabledrepro.alpha. It showedinactive, local.beta/tui.tsand reopened/plugins.repro.alphawas active again.Nothing was written to
cli.json. The toggle only changes the running registration, and the reconcile step inpackages/tui/src/plugin/context.tsxtakesenabledfrom config alone, so any reconcile turns the plugin back on.The recording and screenshots below show the whole sequence, and the script below reproduces it. I didn't test the session/location-switch trigger mentioned in the comments.
recording.mp4
Reproduction script (drive.ts)
Run it with opencode-drive:
opencode-drive run ./drive.ts. It drivesopencodefrom yourPATH; setOC_DEVto the path of an OpenCode checkout to run that from source instead.