Symptoms
- DCP
3.1.14 installs cleanly and the debug log shows DCP initialized with no errors.
/dcp (TUI panel, registered via tui.json) works.
/dcp-compress never appears in the slash-command autocomplete list, and typing it manually sends it as a plain user message instead of triggering compression.
Root cause
Recent OpenCode versions (Effect-based refactor) build the slash-command list as a one-shot snapshot:
-
packages/opencode/src/command/index.ts — the Command service's init() runs once on first access, expands cfg.command into a static record, and never re-reads config afterwards. There is no subscription to config changes.
-
DCP registers the command inside its plugin config hook (index.ts):
opencodeConfig.command["dcp-compress"] = { template: "", description: "..." }
But that hook is invoked only after all plugins finish loading (packages/opencode/src/plugin/index.ts, "Notify plugins of current config" loop).
-
The TUI requests the command list almost immediately at startup, so the Command snapshot is built before plugin config hooks run. The injected dcp-compress entry lands in cfg.command after the snapshot was taken and is never picked up.
The same race affects any plugin that registers commands via the config hook; DCP is just the most visible one.
Workaround
Manually declare the command in opencode.json so it exists at config-load time:
DCP's "command.execute.before" handler matches by name (input.command === "dcp-compress"), so it intercepts the manually-declared command identically.
Proposed fix
Add a "chat.message" hook as a fallback: when a user message's text starts with /dcp-compress, run the same manual-trigger path (handleManualTriggerCommand -> state.pendingManualTrigger) and normalize the stored text to the canonical marker. This makes the command work even when the host never registered it. I have a PR ready.
Environment
- DCP: 3.1.14 (npm)
- OpenCode: 1.14.x (post Effect-refactor)
- OS: Windows 11
Symptoms
3.1.14installs cleanly and the debug log showsDCP initializedwith no errors./dcp(TUI panel, registered viatui.json) works./dcp-compressnever appears in the slash-command autocomplete list, and typing it manually sends it as a plain user message instead of triggering compression.Root cause
Recent OpenCode versions (Effect-based refactor) build the slash-command list as a one-shot snapshot:
packages/opencode/src/command/index.ts— the Command service'sinit()runs once on first access, expandscfg.commandinto a static record, and never re-reads config afterwards. There is no subscription to config changes.DCP registers the command inside its plugin
confighook (index.ts):But that hook is invoked only after all plugins finish loading (
packages/opencode/src/plugin/index.ts, "Notify plugins of current config" loop).The TUI requests the command list almost immediately at startup, so the Command snapshot is built before plugin
confighooks run. The injecteddcp-compressentry lands incfg.commandafter the snapshot was taken and is never picked up.The same race affects any plugin that registers commands via the
confighook; DCP is just the most visible one.Workaround
Manually declare the command in
opencode.jsonso it exists at config-load time:{ "command": { "dcp-compress": { "template": "", "description": "Trigger DCP manual compression with: /dcp-compress [focus]" } } }DCP's
"command.execute.before"handler matches by name (input.command === "dcp-compress"), so it intercepts the manually-declared command identically.Proposed fix
Add a
"chat.message"hook as a fallback: when a user message's text starts with/dcp-compress, run the same manual-trigger path (handleManualTriggerCommand->state.pendingManualTrigger) and normalize the stored text to the canonical marker. This makes the command work even when the host never registered it. I have a PR ready.Environment