Motivation
Each integration (lua/code-preview/backends/{claudecode,copilot,codex,opencode}.lua) answers "is this installed?" with a different shape:
- claudecode — inline string search in
init.lua's status() for "code-preview" / "claude-preview" in .claude/settings.local.json. No module function.
- copilot —
is_our_config(path) boolean.
- codex —
is_installed() plus feature_flag_state() ("enabled" / "disabled" / nil), composed in status().
- opencode — file-existence check inlined in
status().
Adding a fifth agent means inventing a fifth pattern. status() is the only consumer and is unnecessarily aware of each integration's internals.
Proposed direction
A shared shape on every integration module:
--- @return { state: "installed" | "missing", warnings: string[]? }
function M.install_state() ... end
Where warnings is for cases like Codex's missing codex_hooks flag — installed but degraded. init.lua's status() becomes a loop over a registry of integrations.
Scope
- Add
M.install_state() to all four integration modules.
- Define a registry (probably in
init.lua or a new backends/init.lua) so status() and future code can iterate.
- Move the inline claudecode/opencode detection out of
init.lua into their respective modules.
- No behaviour change for the user;
:CodePreviewStatus output should look identical.
Out of scope
- Adding new agents.
- Changing the install / uninstall command shape.
Motivation
Each integration (
lua/code-preview/backends/{claudecode,copilot,codex,opencode}.lua) answers "is this installed?" with a different shape:init.lua'sstatus()for"code-preview"/"claude-preview"in.claude/settings.local.json. No module function.is_our_config(path)boolean.is_installed()plusfeature_flag_state()("enabled"/"disabled"/nil), composed instatus().status().Adding a fifth agent means inventing a fifth pattern.
status()is the only consumer and is unnecessarily aware of each integration's internals.Proposed direction
A shared shape on every integration module:
Where
warningsis for cases like Codex's missingcodex_hooksflag — installed but degraded.init.lua'sstatus()becomes a loop over a registry of integrations.Scope
M.install_state()to all four integration modules.init.luaor a newbackends/init.lua) sostatus()and future code can iterate.init.luainto their respective modules.:CodePreviewStatusoutput should look identical.Out of scope