@rockysoft/opencode-hybrid-workflow 0.3.2-beta.1 → 0.3.2-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,252 +1,167 @@
1
1
  # OpenCode Hybrid Workflow
2
2
 
3
- One OpenCode 2 package for premium planning/design/reviews, implementation
4
- checkpoints, specialist instructions and reviewer usage/cost cards in the sidebar.
5
- No dashboard process, trial launcher or project-local plugin folder is required.
3
+ Let your strongest models plan, design and review. Let your implementation model build.
6
4
 
7
- **Public beta:** Windows and Linux checks pass. macOS support is implemented,
8
- but a real Mac installation/UI run is still pending. Requires **OpenCode 2**
9
- (tested with 2.2.5), Node.js 22+, Git and Python 3. OpenCode 1 is not supported.
5
+ One plugin. One or two reviewers. Configure everything inside OpenCode.
10
6
 
11
- ## Install once
7
+ **Requirements:** OpenCode 2, Node.js 22+, Git and Python 3. Windows and Linux; experimental macOS support.
12
8
 
13
- In your terminal, with OpenCode 2 installed:
9
+ ## 1. Install
14
10
 
15
11
  ```sh
16
12
  opencode plugin add @rockysoft/opencode-hybrid-workflow@beta
17
13
  ```
18
14
 
19
- Then open OpenCode in any project and complete **Hybrid setup**. Configure
20
- providers, reviewers and optional tools inside OpenCode. Existing running
21
- sessions are not restarted; reopen OpenCode when ready to load the plugin.
15
+ ![Install the plugin from npm](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/01-install.png)
22
16
 
23
- To update the beta later:
17
+ Open `opencode` in your project. The setup wizard opens automatically.
24
18
 
25
- ```sh
26
- opencode plugin update @rockysoft/opencode-hybrid-workflow@beta
27
- ```
19
+ ## 2. Choose your reviewers
28
20
 
29
- These commands use OpenCode's global package plugin manager. No separate npm
30
- global install or `opencode-hybrid init` is needed for a new installation.
31
- If you used a previous local/archive build, remove its plugin entries from
32
- both `opencode.json` and `cli.json` before adding the npm package, preserving
33
- `hybrid/config.json`, managed instructions and runtime history. Do not load the
34
- local and npm builds together.
35
-
36
- ## Set up inside OpenCode
37
-
38
- Once the plugin is installed, open OpenCode normally in any project. On first
39
- launch, **Hybrid setup** opens automatically. Choose one or two reviewers, their
40
- OpenCode-connected providers or external CLIs, models, reasoning effort and the
41
- planning/design lead, then save. The plugin installs its managed agents, rules
42
- and design skills globally. Canceling the wizard does not save reviewer choices.
43
-
44
- Later, click **Hybrid Workflow settings** on the home screen or session sidebar,
45
- or select it in OpenCode's command palette. `/hybrid` opens the same menu.
46
- There is no separate configuration terminal or dashboard to launch.
47
-
48
- The menu includes:
49
-
50
- - **Reviewers and design lead**: one or two reviewers; any enabled tool-capable
51
- OpenCode provider/model, or the optional Codex/Claude CLI adapters.
52
- - **Connect provider** and **Implementation model**: open OpenCode's own menus.
53
- - **Reviewer browser**: enable an existing browser, or install managed Playwright
54
- MCP and Firefox with one confirmation. Each reviewer gets an isolated browser.
55
- - **Tools and dependencies**: install managed pnpm for verification; enable the
56
- official Context7 documentation MCP while preserving an existing configuration.
57
- - **Run diagnostics**: inspect prerequisites, reviewer configuration/logins,
58
- design skills and an actual browser launch without spending model tokens.
59
-
60
- Settings apply globally. New tasks pick up saved reviewers; active tasks retain
61
- their assignments and history. Changing the installed plugin version still needs
62
- a normal OpenCode restart. Installing an implementation MCP may also require a
63
- restart if the host does not reload its MCP configuration.
64
-
65
- The same npm installation works on Windows, macOS and Linux. `configure`,
66
- `doctor` and `browser` CLI commands are optional automation/support tools,
67
- not the routine setup workflow. Publishing on npm does not imply inclusion
68
- in a curated OpenCode marketplace.
69
-
70
- OpenCode's plugin manager does not put `opencode-hybrid` on your terminal PATH.
71
- For the optional CLI examples below, replace `opencode-hybrid` with
72
- `npx --package @rockysoft/opencode-hybrid-workflow@beta opencode-hybrid`, or
73
- install the package globally if you specifically want its standalone CLI.
74
- Routine setup and diagnostics remain available inside OpenCode.
75
-
76
- Machine prerequisites are OpenCode 2, Node 22+, Git and Python 3 for design
77
- research. pnpm and browser support have installers in the menu. External CLI
78
- choices require that CLI and its official account sign-in. Provider credentials
79
- remain with OpenCode or the official CLI; the plugin never copies them.
80
-
81
- Ask normally. The package supplies workflow instructions and tool gates. Specialist
82
- agents inherit the implementation model. New UI/UX decisions go to the configured
83
- design lead and reviewers; existing/supplied patterns can be implemented faithfully. Existing-app
84
- visual redesigns skip unrelated architecture planning. New applications and
85
- substantive architecture changes receive premium planning first. Premium planners
86
- own material structural choices; native fullstack-architect gathers facts/options,
87
- and implementation specialists follow the approved plan. New material choices
88
- return to planning before dependent edits. Subsequent
89
- reviews focus on actual corrections and affected dependencies. Approval requires
90
- all configured current-artifact reports, at least one pass, and no open medium/high/critical
91
- findings. There are no artificial line, screenshot, correction or call quotas.
92
-
93
- The sidebar follows the current project, shows input/output/cache tokens and USD
94
- API-equivalent costs when a known model rate is available. Unknown pricing is
95
- unavailable, not zero. Clear totals changes only the display baseline; reports,
96
- findings and approvals remain intact. No fixed local port or HTTP service is used.
97
-
98
- ## Configuration
99
-
100
- Both CLI and OpenCode-provider reviewers can independently inspect the running
101
- app with Playwright. Configure this through **Hybrid Workflow settings > Reviewer
102
- browser**. Installing browser support downloads a pinned Playwright MCP package
103
- and Firefox into plugin-managed tooling, then enables isolated headless sessions.
104
- The existing implementation MCP entry is preserved. Existing browser commands can
105
- also be enabled without downloading replacements.
106
-
107
- Advanced settings live in `reviewBrowser` in `hybrid/config.json`: `command`,
108
- `browser` (chrome/msedge/firefox/webkit), and `headless`. The CLI browser commands
109
- remain available for scripted administration.
110
-
111
- All reviewer adapters can navigate, inspect DOM, resize, click,
112
- fill test forms, evaluate page JavaScript, inspect console/network information,
113
- and capture screenshots. Output goes to that review's `browser-artifacts`; tool
114
- names are logged in `actions.jsonl`. This is not desktop control or the user's
115
- signed-in browser. Source edits, server startup, local-file upload and arbitrary
116
- host-code execution remain unavailable; Codex retains read-only shell inspection
117
- of evidence, while Claude uses its read tools. Browser interactions can still mutate application
118
- data: use a disposable test environment and synthetic data. Reviewers are told
119
- to inspect relevant states directly and distinguish live observations from the
120
- frozen source artifact. Supply the actual app URL in checkpoint checks and keep
121
- the development/test server running. Browser access does not authorize a reviewer
122
- to start/restart the app server.
123
-
124
- OpenCode-provider reviewers use the internal `hybrid_browser` tool for the same
125
- isolated Playwright access. Each call has its own browser process, started only
126
- when needed and closed before the review completes. Browser sessions are never
127
- shared between reviewers or inherited from the implementation agent. Vision-capable
128
- models receive actual image pixels; text-only models cannot approve visual evidence.
129
-
130
- `opencode-hybrid config` prints the configuration location, normally
131
- `~/.config/opencode/hybrid/config.json`. `OPENCODE_CONFIG_DIR`,
132
- `OPENCODE_HYBRID_CONFIG` can choose another user-owned location from your launcher.
133
- Plugin `options.configFile` must match that trusted location; a project cannot
134
- redirect reviewer executable settings to its own file. The installer supports
135
- `--config-dir PATH` for a separate profile.
136
- Do not put credentials in this file; OpenCode or the selected official CLI owns authentication.
137
-
138
- `opencode-hybrid configure` opens an interactive selector: choose one or two
139
- reviewers, OpenCode provider or external CLI for each, model, supported variant/CLI effort, and the
140
- planning/design lead. Either slot can use any supported adapter, including two
141
- OpenCode models, two CLI models, or one of each. `opencode-hybrid models` lists the
142
- enabled tool-capable models from your OpenCode provider catalog. Any enabled
143
- provider is eligible; Copilot is an ordinary catalog provider, not a special adapter. No local model
144
- server, GPU setup, Strata launcher or model download is managed by this package.
145
-
146
- Noninteractive examples (use actual IDs returned by `models`):
21
+ Select **one** reviewer or **two** for independent planning and review.
147
22
 
148
- ```sh
149
- opencode-hybrid configure --reviewer1 codex-cli:gpt-6-astra --reviewer2 claude-cli:opus --variant1 high --variant2 high
150
- opencode-hybrid configure --reviewer1 opencode:PROVIDER/MODEL --reviewer2 none
151
- opencode-hybrid configure --reviewer1 opencode:PROVIDER/MODEL --reviewer2 claude-cli:opus --variant1 SUPPORTED_VARIANT
152
- ```
23
+ ![Choose one or two reviewers](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/02-reviewers.png)
153
24
 
154
- ```json
155
- {
156
- "version": 1,
157
- "reviewers": {
158
- "astra": { "adapter": "codex-cli", "model": "gpt-6-astra", "effort": "high", "executable": "codex" },
159
- "opus": { "adapter": "claude-cli", "model": "opus", "effort": "high", "executable": "claude" }
160
- },
161
- "verification": { "executable": "pnpm", "timeoutMs": 0 },
162
- "reviewTimeoutMs": 0
163
- }
164
- ```
25
+ ## 3. Pick a connection, model and effort
26
+
27
+ Use any connected OpenCode provider, or your Codex/Claude CLI login. Each reviewer can use a different connection.
28
+
29
+ ![Choose a reviewer connection](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/03-connection.png)
30
+
31
+ <details>
32
+ <summary><strong>OpenCode provider — see every step</strong></summary>
33
+
34
+ **Choose your provider.**
35
+
36
+ ![Choose a connected provider](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/04-provider.png)
37
+
38
+ **Choose a model.** Type to search. Models shown depend on your connections.
39
+
40
+ ![Choose a reviewer model](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/05-model.png)
41
+
42
+ **Choose reasoning effort.** Options come from the selected model.
43
+
44
+ ![Choose reasoning effort](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/06-effort.png)
45
+
46
+ Repeat for reviewer 2 if enabled.
47
+
48
+ </details>
49
+
50
+ <details>
51
+ <summary><strong>External CLI — see every step</strong></summary>
52
+
53
+ **Choose Codex CLI or Claude Code CLI.** Install and sign into that CLI first.
54
+
55
+ ![Choose the external CLI](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/18-cli.png)
56
+
57
+ **Select a model by name.** Models and effort levels come from your installed CLI. Custom IDs are an advanced option.
58
+
59
+ ![Select a CLI model by name](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/19-cli-model.png)
60
+
61
+ Claude Code shows its own model list:
62
+
63
+ ![Select a Claude model by name](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/21-claude-model.png)
64
+
65
+ **Choose effort.** Use a level supported by that CLI and model.
66
+
67
+ ![Choose CLI reasoning effort](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/20-cli-effort.png)
68
+
69
+ </details>
70
+
71
+ <details>
72
+ <summary><strong>Need to connect a provider?</strong></summary>
73
+
74
+ Cancel the wizard, choose **Connect provider**, and complete OpenCode's sign-in flow. Then reopen **Complete setup**.
75
+
76
+ ![Connect a provider through OpenCode](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/15-connect-provider.png)
77
+
78
+ </details>
79
+
80
+ ## 4. Choose the lead and save
81
+
82
+ With two reviewers, choose who leads planning and design.
83
+
84
+ ![Choose the planning and design lead](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/07-design-lead.png)
85
+
86
+ Confirm your choices. They apply across projects; active tasks keep their assigned reviewers.
165
87
 
166
- `astra` and `opus` are stable storage-slot IDs, not provider restrictions. Set the
167
- second slot to `null` for one reviewer. An OpenCode slot has
168
- `{"adapter":"opencode","providerID":"...","model":"...","variant":"..."}`;
169
- omit `variant` for the provider default. There is no invented universal effort
170
- mapping. `designAuthor` selects `astra` or `opus`; default is the last enabled slot.
171
- Legacy defaults remain Codex/Astra and Claude/Opus, both high effort; first-run
172
- setup asks for explicit choices and initially highlights OpenCode providers.
173
-
174
- Native reviewers run in read-only OpenCode child sessions with only frozen
175
- evidence, public-documentation tools and the optional isolated browser. They cannot edit source, delegate or
176
- recursively start checkpoints. Image reviews require an image-capable model.
177
- Unknown/disabled models, unsupported variants and invalid reports fail explicitly;
178
- there is no provider fallback. Provider context limits still apply.
179
-
180
- Reviewer assignments are pinned for an active task; changing defaults affects
181
- the next task without restarting OpenCode. Existing findings are retained. One
182
- reviewer must pass with no medium-or-higher findings; it does not claim independent
183
- cross-model agreement. With two, both reports must complete and neither may have
184
- an unresolved medium-or-higher finding. Low/info suggestions do not block a pass.
185
-
186
- Installation merges plugin entries into `opencode.json` and `cli.json`, installs
187
- nine specialist definitions and the three design skills, and adds a managed rules
188
- file. Existing custom agent/skill files are preserved and reported. Other settings,
189
- permissions and implementation-model choices are retained. JSONC is accepted;
190
- comments are removed from the rewritten file, with an exact original backup.
191
- Updates replace only unchanged package-managed instruction files. It does not
192
- import personal project memories or modify Claude/Codex global rules.
193
-
194
- ## Storage and existing sessions
195
-
196
- New state lives outside both the package and project: Windows uses
197
- `%LOCALAPPDATA%/opencode-hybrid/runtime`, other platforms use
198
- `$XDG_DATA_HOME/opencode-hybrid/runtime` or `~/.local/share/opencode-hybrid/runtime`.
199
- Projects are isolated by canonical path. Checkpoints are project-scoped; concurrent
200
- conversations editing the same project must coordinate against the same gate.
201
- Opening a subfolder of a Git repository asks you to open its root.
202
-
203
- To retain a trial project's exact existing history without copying/resetting it:
88
+ ![Save the reviewer setup](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/08-save.png)
89
+
90
+ ## 5. Choose who builds
91
+
92
+ Open **Implementation model** and select your coding model. It follows the approved plan and design.
93
+
94
+ ![Choose the implementation model](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/17-implementation.png)
95
+
96
+ ## 6. Start working
97
+
98
+ Ask OpenCode for your feature or redesign. Reviewers handle planning and new design decisions; the implementation model builds and addresses review findings.
99
+
100
+ Continue when at least one reviewer passes and no reviewer has unresolved medium, high or critical findings.
101
+
102
+ Type **`/hybrid`** or click **Hybrid Workflow settings** whenever you want to change the setup.
103
+
104
+ ![Hybrid Workflow settings](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/09-settings.png)
105
+
106
+ <details>
107
+ <summary><strong>Reopen settings with /hybrid</strong></summary>
108
+
109
+ ![Open settings from the command prompt](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/16-reopen.png)
110
+
111
+ </details>
112
+
113
+ ## Optional tools
114
+
115
+ <details>
116
+ <summary><strong>Let reviewers inspect your app in a browser</strong></summary>
117
+
118
+ Choose **Reviewer browser → Install browser support**.
119
+
120
+ ![Reviewer browser options](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/10-browser.png)
121
+
122
+ Confirm installation of Playwright and Firefox.
123
+
124
+ ![Confirm browser installation](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/11-browser-confirm.png)
125
+
126
+ Each reviewer gets an isolated browser. Run your app and provide its URL; use test data.
127
+
128
+ ![Browser support installed](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/12-browser-ready.png)
129
+
130
+ </details>
131
+
132
+ <details>
133
+ <summary><strong>Install pnpm or enable Context7 documentation</strong></summary>
134
+
135
+ Open **Tools and dependencies** and select what you need.
136
+
137
+ ![Tools and dependencies](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/13-tools.png)
138
+
139
+ </details>
140
+
141
+ <details>
142
+ <summary><strong>Check your setup</strong></summary>
143
+
144
+ Choose **Run diagnostics**. Select any row for details. This does not call a model.
145
+
146
+ ![Setup diagnostics](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/images/14-diagnostics.png)
147
+
148
+ </details>
149
+
150
+ ## Usage and updates
151
+
152
+ The sidebar shows reviewer input, output, cache tokens and costs at known API rates. These are not subscription charges. **Clear totals** resets the display, not review history.
204
153
 
205
154
  ```sh
206
- opencode-hybrid init --legacy-config /path/to/old/config.json --project /path/to/project
155
+ opencode plugin update @rockysoft/opencode-hybrid-workflow@beta
207
156
  ```
208
157
 
209
- Only that project receives a runtime override; new projects use portable storage.
210
- OpenCode conversation history is separate. Sessions created under an isolated
211
- `XDG_DATA_HOME` remain in that profile; installation does not move them. Existing
212
- trial profiles stay usable. Restart OpenCode to load changed modules; installation
213
- does not interrupt running sessions or restart model servers.
214
-
215
- ## Validation and current limits
216
-
217
- The package targets Windows, macOS (Apple Silicon and Intel), and Linux. The same
218
- in-app setup is used on each platform; do not copy Windows executable paths or
219
- account credentials onto a Mac. On macOS it detects standard Homebrew locations,
220
- uses `python3`, and stores settings under `~/.config/opencode` and runtime data
221
- under `~/.local/share/opencode-hybrid` (or your XDG overrides). Browser and pnpm
222
- installers download the appropriate native platform binaries.
223
-
224
- macOS checkpoint exclusion uses a kernel-held advisory lock through Python 3,
225
- which is already a prerequisite for design research. The lock is released when
226
- the parent exits; the persistent `.mutex` file is normal and must not be deleted.
227
- Interrupted operation history and live-worker guards remain intact. This avoids
228
- stale Unix socket cleanup and macOS socket pathname-length problems.
229
-
230
- Validation: Windows and Linux execute the suite locally, including real POSIX
231
- crash/concurrency tests for the macOS lock adapter. Actual macOS execution is not
232
- yet verified here. `.github/workflows/compatibility.yml` is ready to run both
233
- Apple Silicon (`macos-15`) and Intel (`macos-15-intel`) tests plus actual OpenCode
234
- host integration once this package is placed in its own GitHub repository.
235
- It has not been pushed or run on hosted Mac runners. A Mac installation can also
236
- use **Hybrid Workflow settings > Run diagnostics** for prerequisite/browser checks.
237
-
238
- OpenCode 2 plugin hooks are checked at activation; patch versions are not pinned.
239
-
240
- Run `npm test` in the source checkout for the controller, installer, isolation,
241
- evidence, process lifecycle and sidebar tests. Installation/host tests make no
242
- model calls. Passing these tests does not guarantee model compliance or visual
243
- quality. Third-party attribution is in `THIRD_PARTY_NOTICES.md`.
244
-
245
- Package development uses `npm install --legacy-peer-deps`, then `npm run build`.
246
- `npm pack` builds automatically. The archive contains a compiled Solid/OpenTUI
247
- sidebar because the host does not compile JSX inside `node_modules`; host runtime
248
- imports stay external. End users do not need Babel or a separate sidebar process.
249
-
250
- The original plugin code is MIT-licensed; bundled skills retain their own
251
- licenses as listed in `THIRD_PARTY_NOTICES.md`. Authenticate providers on each
252
- machine rather than copying account credentials.
158
+ Reopen OpenCode after updating.
159
+
160
+ <details>
161
+ <summary><strong>Moving from a local/archive installation</strong></summary>
162
+
163
+ Remove the old plugin entries from `opencode.json` and `cli.json` before adding the npm package. Keep `hybrid/config.json` and runtime data to preserve settings and history.
164
+
165
+ </details>
166
+
167
+ [Advanced configuration](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/docs/configuration.md) · [MIT license](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/LICENSE) · [Third-party notices](https://cdn.jsdelivr.net/npm/@rockysoft/opencode-hybrid-workflow@0.3.2-beta.3/THIRD_PARTY_NOTICES.md)
package/core/workers.mjs CHANGED
@@ -29,7 +29,7 @@ export function workerArgs(who, config, folder, images = [], browser = null) {
29
29
  // implementation must be explicit too. Without it shell reads are denied.
30
30
  ...(process.platform === 'win32' ? ['-c', 'windows.sandbox="elevated"'] : []),
31
31
  '-c', 'approval_policy="never"',
32
- '-c', `model_reasoning_effort="${lane.effort??'high'}"`, '-c', 'web_search="live"',
32
+ ...(lane.effort==='default'?[]:['-c', `model_reasoning_effort="${lane.effort??'high'}"`]), '-c', 'web_search="live"',
33
33
  ...(browser ? ['-c',`mcp_servers.review_browser.command=${JSON.stringify(browser.command)}`,
34
34
  '-c',`mcp_servers.review_browser.args=${JSON.stringify(browser.args)}`,
35
35
  '-c','mcp_servers.review_browser.startup_timeout_sec=60',
@@ -41,7 +41,7 @@ export function workerArgs(who, config, folder, images = [], browser = null) {
41
41
  return ['-p', ...(browser?['--setting-sources','','--disable-slash-commands','--settings','{"disableAllHooks":true,"enabledPlugins":{}}']:['--safe-mode']), '--restricted', '--strict-mcp-config', '--mcp-config', JSON.stringify({mcpServers:browser?{review_browser:browser}:{}}),
42
42
  '--no-chrome', '--no-session-persistence', '--permission-mode', 'dontAsk', '--permission-prompts', 'none',
43
43
  '--tools', 'Read,Glob,Grep,WebSearch,WebFetch', '--allowedTools', ['Read','Glob','Grep','WebSearch','WebFetch',...(browser?browserTools.map(name=>'mcp__review_browser__'+name):[])].join(','),
44
- '--model', lane.model, '--effort', lane.effort??'high', '--output-format', 'stream-json', '--verbose',
44
+ '--model', lane.model, ...(lane.effort==='default'?[]:['--effort',lane.effort??'high']), '--output-format', 'stream-json', '--verbose',
45
45
  '--json-schema', JSON.stringify(resultSchema)];
46
46
  }
47
47
 
@@ -0,0 +1,134 @@
1
+ # Configuration reference
2
+
3
+ Use Hybrid Workflow settings inside OpenCode for routine setup. These details are for advanced configuration and migration.
4
+
5
+ For terminal commands below, use `npx --package @rockysoft/opencode-hybrid-workflow@beta opencode-hybrid` in place of `opencode-hybrid` unless you installed the standalone CLI globally.
6
+
7
+ ## Configuration
8
+
9
+ Both CLI and OpenCode-provider reviewers can independently inspect the running
10
+ app with Playwright. Configure this through **Hybrid Workflow settings > Reviewer
11
+ browser**. Installing browser support downloads a pinned Playwright MCP package
12
+ and Firefox into plugin-managed tooling, then enables isolated headless sessions.
13
+ The existing implementation MCP entry is preserved. Existing browser commands can
14
+ also be enabled without downloading replacements.
15
+
16
+ Advanced settings live in `reviewBrowser` in `hybrid/config.json`: `command`,
17
+ `browser` (chrome/msedge/firefox/webkit), and `headless`. The CLI browser commands
18
+ remain available for scripted administration.
19
+
20
+ All reviewer adapters can navigate, inspect DOM, resize, click,
21
+ fill test forms, evaluate page JavaScript, inspect console/network information,
22
+ and capture screenshots. Output goes to that review's `browser-artifacts`; tool
23
+ names are logged in `actions.jsonl`. This is not desktop control or the user's
24
+ signed-in browser. Source edits, server startup, local-file upload and arbitrary
25
+ host-code execution remain unavailable; Codex retains read-only shell inspection
26
+ of evidence, while Claude uses its read tools. Browser interactions can still mutate application
27
+ data: use a disposable test environment and synthetic data. Reviewers are told
28
+ to inspect relevant states directly and distinguish live observations from the
29
+ frozen source artifact. Supply the actual app URL in checkpoint checks and keep
30
+ the development/test server running. Browser access does not authorize a reviewer
31
+ to start/restart the app server.
32
+
33
+ OpenCode-provider reviewers use the internal `hybrid_browser` tool for the same
34
+ isolated Playwright access. Each call has its own browser process, started only
35
+ when needed and closed before the review completes. Browser sessions are never
36
+ shared between reviewers or inherited from the implementation agent. Vision-capable
37
+ models receive actual image pixels; text-only models cannot approve visual evidence.
38
+
39
+ `opencode-hybrid config` prints the configuration location, normally
40
+ `~/.config/opencode/hybrid/config.json`. `OPENCODE_CONFIG_DIR`,
41
+ `OPENCODE_HYBRID_CONFIG` can choose another user-owned location from your launcher.
42
+ Plugin `options.configFile` must match that trusted location; a project cannot
43
+ redirect reviewer executable settings to its own file. The installer supports
44
+ `--config-dir PATH` for a separate profile.
45
+ Do not put credentials in this file; OpenCode or the selected official CLI owns authentication.
46
+
47
+ `opencode-hybrid configure` opens an interactive selector: choose one or two
48
+ reviewers, OpenCode provider or external CLI for each, model, supported variant/CLI effort, and the
49
+ planning/design lead. Either slot can use any supported adapter, including two
50
+ OpenCode models, two CLI models, or one of each. `opencode-hybrid models` lists the
51
+ enabled tool-capable models from your OpenCode provider catalog. Any enabled
52
+ provider is eligible; Copilot is an ordinary catalog provider, not a special adapter. No local model
53
+ server, GPU setup, Strata launcher or model download is managed by this package.
54
+
55
+ Noninteractive examples (use actual IDs returned by `models`):
56
+
57
+ ```sh
58
+ opencode-hybrid configure --reviewer1 codex-cli:gpt-6-astra --reviewer2 claude-cli:opus --variant1 high --variant2 high
59
+ opencode-hybrid configure --reviewer1 opencode:PROVIDER/MODEL --reviewer2 none
60
+ opencode-hybrid configure --reviewer1 opencode:PROVIDER/MODEL --reviewer2 claude-cli:opus --variant1 SUPPORTED_VARIANT
61
+ ```
62
+
63
+ ```json
64
+ {
65
+ "version": 1,
66
+ "reviewers": {
67
+ "astra": { "adapter": "codex-cli", "model": "gpt-6-astra", "effort": "high", "executable": "codex" },
68
+ "opus": { "adapter": "claude-cli", "model": "opus", "effort": "high", "executable": "claude" }
69
+ },
70
+ "verification": { "executable": "pnpm", "timeoutMs": 0 },
71
+ "reviewTimeoutMs": 0
72
+ }
73
+ ```
74
+
75
+ `astra` and `opus` are stable storage-slot IDs, not provider restrictions. Set the
76
+ second slot to `null` for one reviewer. An OpenCode slot has
77
+ `{"adapter":"opencode","providerID":"...","model":"...","variant":"..."}`;
78
+ omit `variant` for the provider default. There is no invented universal effort
79
+ mapping. `designAuthor` selects `astra` or `opus`; default is the last enabled slot.
80
+ Legacy defaults remain Codex/Astra and Claude/Opus, both high effort; first-run
81
+ setup asks for explicit choices and initially highlights OpenCode providers.
82
+
83
+ Native reviewers run in read-only OpenCode child sessions with only frozen
84
+ evidence, public-documentation tools and the optional isolated browser. They cannot edit source, delegate or
85
+ recursively start checkpoints. Image reviews require an image-capable model.
86
+ Unknown/disabled models, unsupported variants and invalid reports fail explicitly;
87
+ there is no provider fallback. Provider context limits still apply.
88
+
89
+ Reviewer assignments are pinned for an active task; changing defaults affects
90
+ the next task without restarting OpenCode. Existing findings are retained. One
91
+ reviewer must pass with no medium-or-higher findings; it does not claim independent
92
+ cross-model agreement. With two, both reports must complete and neither may have
93
+ an unresolved medium-or-higher finding. Low/info suggestions do not block a pass.
94
+
95
+ Installation merges plugin entries into `opencode.json` and `cli.json`, installs
96
+ nine specialist definitions and the three design skills, and adds a managed rules
97
+ file. Existing custom agent/skill files are preserved and reported. Other settings,
98
+ permissions and implementation-model choices are retained. JSONC is accepted;
99
+ comments are removed from the rewritten file, with an exact original backup.
100
+ Updates replace only unchanged package-managed instruction files. It does not
101
+ import personal project memories or modify Claude/Codex global rules.
102
+
103
+ ## Storage and existing sessions
104
+
105
+ New state lives outside both the package and project: Windows uses
106
+ `%LOCALAPPDATA%/opencode-hybrid/runtime`, other platforms use
107
+ `$XDG_DATA_HOME/opencode-hybrid/runtime` or `~/.local/share/opencode-hybrid/runtime`.
108
+ Projects are isolated by canonical path. Checkpoints are project-scoped; concurrent
109
+ conversations editing the same project must coordinate against the same gate.
110
+ Opening a subfolder of a Git repository asks you to open its root.
111
+
112
+ To retain a trial project's exact existing history without copying/resetting it:
113
+
114
+ ```sh
115
+ opencode-hybrid init --legacy-config /path/to/old/config.json --project /path/to/project
116
+ ```
117
+
118
+ Only that project receives a runtime override; new projects use portable storage.
119
+ OpenCode conversation history is separate. Sessions created under an isolated
120
+ `XDG_DATA_HOME` remain in that profile; installation does not move them. Existing
121
+ trial profiles stay usable. Restart OpenCode to load changed modules; installation
122
+ does not interrupt running sessions or restart model servers.
123
+
124
+ ## Troubleshooting
125
+
126
+ Use **Hybrid Workflow settings > Run diagnostics** to check prerequisites,
127
+ reviewer connections, design skills and browser access. Sign in to each provider
128
+ on the machine where you use OpenCode. Reopen OpenCode after a plugin update.
129
+
130
+ ## License
131
+
132
+ The original plugin code is MIT-licensed; bundled skills retain their own
133
+ licenses as listed in `THIRD_PARTY_NOTICES.md`. Authenticate providers on each
134
+ machine rather than copying account credentials.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,77 @@
1
+ import {spawn,spawnSync} from 'node:child_process';
2
+ import {mkdtempSync,rmSync} from 'node:fs';
3
+ import {tmpdir} from 'node:os';
4
+ import path from 'node:path';
5
+ import {commandSpec,nativeCodex} from './commands.mjs';
6
+ import {workerEnvironment} from '../core/workers.mjs';
7
+
8
+ // Only initialize and inspect catalogs. Never start a thread, send a user
9
+ // message, read credentials, or return account metadata from CLI responses.
10
+ export function normalizeCLIModels(adapter,rows){
11
+ if(!Array.isArray(rows))throw Error('The CLI did not return a model catalog. Update the CLI and retry.');
12
+ const seen=new Set();
13
+ return rows.filter(m=>m&&!m.hidden).map(m=>{
14
+ const codex=adapter==='codex-cli';
15
+ const id=codex?(m.model??m.id):(m.value==='default'?m.resolvedModel:m.value);
16
+ const efforts=codex?m.supportedReasoningEfforts?.map(e=>e.reasoningEffort):m.supportedEffortLevels;
17
+ return {id,name:codex?(m.displayName??id):(m.displayName??id),description:typeof m.description==='string'?m.description:'',
18
+ efforts:[...new Set((efforts??[]).filter(e=>typeof e==='string'&&/^[a-zA-Z0-9_-]+$/.test(e)))],
19
+ ...(codex&&typeof m.defaultReasoningEffort==='string'?{defaultEffort:m.defaultReasoningEffort}:{}),isDefault:codex?!!m.isDefault:m.value==='default'};
20
+ }).filter(m=>typeof m.id==='string'&&m.id.trim()&&!seen.has(m.id)&&seen.add(m.id));
21
+ }
22
+
23
+ export async function discoverCLIModels(adapter,{executable,signal,timeoutMs=30000}={}){
24
+ if(!['codex-cli','claude-cli'].includes(adapter))throw Error('Unknown CLI connection.');
25
+ if(signal?.aborted)throw Error('Model discovery cancelled.');
26
+ const codex=adapter==='codex-cli';
27
+ const spec=commandSpec(codex?nativeCodex(executable??'codex'):(executable??'claude'));
28
+ const folder=mkdtempSync(path.join(tmpdir(),'hybrid-models-'));
29
+ const args=codex?['app-server','-c','model_provider="openai"','--listen','stdio://']:['-p','--safe-mode','--restricted','--strict-mcp-config','--mcp-config','{"mcpServers":{}}','--no-chrome','--no-session-persistence','--tools','','--input-format','stream-json','--output-format','stream-json','--verbose'];
30
+ try{return await new Promise((resolve,reject)=>{
31
+ const child=spawn(spec.executable,[...spec.args,...args],{cwd:folder,env:workerEnvironment(),shell:false,windowsHide:true,detached:process.platform!=='win32',stdio:['pipe','pipe','pipe']});
32
+ let done=false,buffer='',rows=[],nextId=2,expected=1,timer;const cursors=new Set();
33
+ const finish=(error,value)=>{
34
+ if(done)return;done=true;clearTimeout(timer);signal?.removeEventListener('abort',abort);
35
+ child.stdin.destroy();child.stdout.destroy();child.stderr.destroy();
36
+ if(child.pid&&child.exitCode===null){
37
+ if(process.platform==='win32')spawnSync(path.join(process.env.SystemRoot??'C:/Windows','System32/taskkill.exe'),['/PID',String(child.pid),'/T','/F'],{windowsHide:true,timeout:5000,stdio:'ignore'});
38
+ else try{process.kill(-child.pid,'SIGKILL');}catch{child.kill('SIGKILL');}
39
+ }
40
+ child.unref();error?reject(error):resolve(value);
41
+ };
42
+ const abort=()=>finish(Error('Model discovery cancelled.'));
43
+ const send=value=>{if(!done)child.stdin.write(JSON.stringify(value)+'\n');};
44
+ const list=cursor=>{expected=nextId++;send({id:expected,method:'model/list',params:{includeHidden:false,...(cursor?{cursor}:{})}});};
45
+ child.on('error',()=>finish(Error('Could not start the CLI. Check its installation and retry.')));
46
+ child.stdin.on('error',()=>finish(Error('The CLI closed model discovery. Update the CLI and retry.')));
47
+ child.on('close',()=>finish(Error('The CLI exited without a model catalog. Check its installation and login.')));
48
+ child.stderr.resume();
49
+ child.stdout.setEncoding('utf8');child.stdout.on('data',chunk=>{
50
+ buffer+=chunk;
51
+ if(buffer.length>8*1024*1024)return finish(Error('The CLI returned an invalid model response.'));
52
+ let pos;while(!done&&(pos=buffer.indexOf('\n'))>=0){
53
+ const line=buffer.slice(0,pos);buffer=buffer.slice(pos+1);let message;try{message=JSON.parse(line);}catch{continue;}
54
+ try{
55
+ if(codex){
56
+ if(message.id!==expected)continue;
57
+ if(message.error)throw Error('Codex could not list models. Check its login/version and retry.');
58
+ if(expected===1){send({method:'initialized',params:{}});list();continue;}
59
+ if(!Array.isArray(message.result?.data))throw Error('Codex returned an invalid model catalog.');
60
+ rows.push(...message.result.data);
61
+ const cursor=message.result.nextCursor;
62
+ if(cursor){if(cursors.has(cursor))throw Error('Codex repeated a model catalog page.');cursors.add(cursor);list(cursor);continue;}
63
+ }else{
64
+ if(message.type!=='control_response'||message.response?.request_id!=='hybrid-models')continue;
65
+ if(message.response.subtype!=='success')throw Error('Claude could not list models. Check its login/version and retry.');
66
+ rows=message.response.response?.models;
67
+ }
68
+ const models=normalizeCLIModels(adapter,rows);if(!models.length)throw Error('No models were returned. Check the CLI login and retry.');
69
+ finish(null,{models,source:codex?'Codex CLI catalog':'Claude Code CLI catalog'});
70
+ }catch(error){finish(error);}
71
+ }
72
+ });
73
+ timer=setTimeout(()=>finish(Error('Loading CLI models timed out. Check the CLI login and retry.')),timeoutMs);
74
+ signal?.addEventListener('abort',abort,{once:true});if(signal?.aborted){abort();return;}
75
+ send(codex?{id:1,method:'initialize',params:{clientInfo:{name:'opencode_hybrid',title:'Hybrid model picker',version:'1.0.0'}}}:{type:'control_request',request_id:'hybrid-models',request:{subtype:'initialize',hooks:{}}});
76
+ });}finally{rmSync(folder,{recursive:true,force:true});}
77
+ }
@@ -1,6 +1,7 @@
1
1
  import {spawnSync} from 'node:child_process';
2
2
  import {createInterface} from 'node:readline/promises';
3
3
  import {commandSpec,nativeCodex} from './commands.mjs';
4
+ import {discoverCLIModels} from './cli-models.mjs';
4
5
 
5
6
  export function modelCatalog(directory=process.cwd()) {
6
7
  const spec=commandSpec('opencode');
@@ -45,10 +46,16 @@ export async function selectInteractively(config) {
45
46
  const variant=await menu('Reasoning variant',[{label:'Provider default',value:undefined},...(model.variants??[]).map(v=>({label:v.id,value:v.id}))]);
46
47
  reviewers[who]=selection(`opencode:${provider}/${model.id}`,{catalog,variant});
47
48
  }else {
48
- const old=config.reviewers?.[who],fallback=adapter==='codex-cli'?'gpt-6-astra':'opus';
49
- const model=(await rl.question(`Model [${old?.adapter===adapter?old.model:fallback}]: `)).trim()||(old?.adapter===adapter?old.model:fallback);
50
- const effort=(await rl.question('Effort [high]: ')).trim()||'high';
51
- if(!/^[a-zA-Z0-9_-]+$/.test(effort))throw Error('Invalid effort.');
49
+ const old=config.reviewers?.[who];let models=[];
50
+ try{({models}=await discoverCLIModels(adapter,{executable:old?.adapter===adapter?old.executable:undefined}));}
51
+ catch(error){console.log(error.message);}
52
+ const items=models.map(m=>({label:m.name,value:m.id}));
53
+ if(old?.adapter===adapter&&!models.some(m=>m.id===old.model))items.push({label:`Keep current model (${old.model})`,value:old.model});
54
+ items.push({label:'Enter custom model ID (advanced)',value:'__custom'});
55
+ let model=await menu('CLI model',items);
56
+ if(model==='__custom'){model=(await rl.question('Exact model ID or CLI alias: ')).trim();if(!model)throw Error('Model is required.');}
57
+ const chosen=models.find(m=>m.id===model);
58
+ const effort=await menu('Reasoning effort',[{label:'CLI default',value:'default'},...(chosen?.efforts??['low','medium','high','xhigh','max']).map(v=>({label:v,value:v}))]);
52
59
  reviewers[who]=selection(`${adapter}:${model}`,{previous:old??{},variant:effort});
53
60
  }
54
61
  }
@@ -2,6 +2,7 @@
2
2
  const object={type:'object',additionalProperties:true};
3
3
  export const SettingsRPC={id:'hybrid.settings',methods:{
4
4
  get:{input:{type:'object',additionalProperties:false},output:object},
5
+ cliModels:{input:{type:'object',additionalProperties:false,properties:{adapter:{enum:['codex-cli','claude-cli']},who:{enum:['astra','opus']}},required:['adapter','who']},output:object},
5
6
  save:{input:{type:'object',additionalProperties:false,properties:{revision:{type:'string'},reviewers:object,designAuthor:{enum:['astra','opus']}},required:['revision','reviewers','designAuthor']},output:object},
6
7
  browser:{input:{type:'object',additionalProperties:false,properties:{action:{enum:['enable','disable','install']}},required:['action']},output:object},
7
8
  diagnostics:{input:{type:'object',additionalProperties:false},output:object},
@@ -8,6 +8,7 @@ import {commandSpec} from './commands.mjs';
8
8
  import {runProcess,requireSubscription} from '../core/workers.mjs';
9
9
  import {NativeBrowser} from './native-browser.mjs';
10
10
  import {SettingsRPC} from './settings-rpc.mjs';
11
+ import {discoverCLIModels} from './cli-models.mjs';
11
12
  const writes=new Set();
12
13
  const unwrap=x=>x?.data??x;
13
14
  export function revision(file){return createHash('sha256').update(existsSync(file)?readFileSync(file):'missing').digest('hex');}
@@ -73,6 +74,11 @@ export function createSettingsService(ctx,options={}){
73
74
  return folder;
74
75
  }
75
76
  return {get,diagnostics,
77
+ cliModels:(input,context={})=>{
78
+ if(!['astra','opus'].includes(input.who)||!['codex-cli','claude-cli'].includes(input.adapter))throw Error('Unknown reviewer connection.');
79
+ const previous=load().reviewers?.[input.who];
80
+ return discoverCLIModels(input.adapter,{executable:previous?.adapter===input.adapter?previous.executable:undefined,signal:context.signal});
81
+ },
76
82
  save:input=>exclusive(async()=>{
77
83
  if(input.revision!==revision(file))throw Error('Settings changed in another window. Reopen settings and try again.');
78
84
  const patch=validateChoices(input,await catalog(),load());
package/package.json CHANGED
@@ -1,12 +1,21 @@
1
1
  {
2
2
  "name": "@rockysoft/opencode-hybrid-workflow",
3
- "version": "0.3.2-beta.1",
3
+ "version": "0.3.2-beta.3",
4
4
  "description": "Portable configurable-reviewer planning, design, implementation checkpoints and usage sidebar for OpenCode 2",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": "RockySoft",
8
- "keywords": ["opencode", "opencode-plugin", "agents", "code-review", "workflow"],
9
- "publishConfig": { "access": "public", "tag": "beta" },
8
+ "keywords": [
9
+ "opencode",
10
+ "opencode-plugin",
11
+ "agents",
12
+ "code-review",
13
+ "workflow"
14
+ ],
15
+ "publishConfig": {
16
+ "access": "public",
17
+ "tag": "beta"
18
+ },
10
19
  "exports": {
11
20
  ".": "./index.js",
12
21
  "./tui": "./tui.js"
@@ -24,7 +33,8 @@
24
33
  "assets/",
25
34
  "README.md",
26
35
  "THIRD_PARTY_NOTICES.md",
27
- "LICENSE"
36
+ "LICENSE",
37
+ "docs/"
28
38
  ],
29
39
  "engines": {
30
40
  "node": ">=22"
@@ -25,8 +25,25 @@ export function createSettingsUI(context,onSaved=()=>{}){
25
25
  return {adapter:'opencode',providerID:provider,model,...(variant==='__default'?{}:{variant})};
26
26
  }
27
27
  const adapter=await select('External CLI',[{title:'Codex CLI',value:'codex-cli'},{title:'Claude Code CLI',value:'claude-cli'}],current?.adapter);if(!adapter)return;
28
- const model=await context.ui.dialog.prompt({title:'CLI model',placeholder:current?.adapter===adapter?current.model:adapter==='codex-cli'?'gpt-6-astra':'opus',value:current?.adapter===adapter?current.model:adapter==='codex-cli'?'gpt-6-astra':'opus'});if(!model?.trim())return;
29
- const effort=await select('CLI reasoning effort',['low','medium','high','xhigh','max'].map(v=>({title:v,value:v})),current?.effort??'high');if(!effort)return;
28
+ let catalog;
29
+ while(!catalog){
30
+ context.ui.toast.show({message:'Loading models from '+(adapter==='codex-cli'?'Codex':'Claude Code')+'…',variant:'info'});
31
+ try{catalog=await invoke('cliModels',{adapter,who});}
32
+ catch(error){
33
+ const action=await select('Could not load CLI models',[{title:'Retry',value:'retry',description:error.message},{title:'Enter custom model ID (advanced)',value:'custom'},{title:'Back to connections',value:'back'}]);
34
+ if(action==='retry')continue;if(action==='back')return reviewer(who,state,current);if(action!=='custom')return;catalog={models:[]};
35
+ }
36
+ }
37
+ const existing=current?.adapter===adapter?current.model:undefined;
38
+ const options=catalog.models.map(m=>({title:m.name,value:m.id,description:m.description||m.id}));
39
+ if(existing&&!catalog.models.some(m=>m.id===existing))options.push({title:'Keep current model',value:existing,description:existing+' · not in current CLI catalog'});
40
+ options.push({title:'Enter custom model ID (advanced)',value:'__custom'});
41
+ let model=await select('CLI model',options,existing??catalog.models.find(m=>m.isDefault)?.id);if(!model)return;
42
+ if(model==='__custom')model=await context.ui.dialog.prompt({title:'Custom model ID',placeholder:'Exact model ID or CLI alias',value:existing??''});if(!model?.trim())return;
43
+ const chosen=catalog.models.find(m=>m.id===model);
44
+ const efforts=chosen?.efforts??['low','medium','high','xhigh','max'];
45
+ const preferred=current?.adapter===adapter&&(current.effort==='default'||efforts.includes(current.effort))?current.effort:efforts.includes('high')?'high':'default';
46
+ const effort=await select('CLI reasoning effort',[{title:'CLI default',value:'default'},...efforts.map(v=>({title:v,value:v}))],preferred);if(!effort)return;
30
47
  return {adapter,model:model.trim(),effort};
31
48
  }
32
49
  async function wizard(state){