-
Notifications
You must be signed in to change notification settings - Fork 131
feat: Add OpenCode CLI provider support #193
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 23 commits
Commits
Show all changes
33 commits
Select commit
Hold shift + click to select a range
edf9957
feat(opencode): Phase 1 — foundation primitives for OpenCode CLI prov…
patricka3125 facd245
fix(opencode): Phase 1 review items — ANSI fixture, model cleanup, gu…
patricka3125 0b070b7
feat(install): add opencode_cli provider branch with --auto-approve flag
patricka3125 8f5be49
fix(install): apply Phase 2 review polish
patricka3125 1cac5b6
feat(providers): add OpenCodeCliProvider (Phase 3)
patricka3125 6ec7c5c
docs: add Phase 3 OpenCode provider runtime development walkthrough r…
patricka3125 f608f5d
fix(opencode): Phase 3 review polish — correct report line count, add…
patricka3125 d0cee13
feat(opencode): Phase 4 — e2e test, provider docs, README/CHANGELOG
patricka3125 c812e06
docs: add Phase 4 OpenCode e2e and docs development walkthrough report
patricka3125 4c30661
fix(opencode): translate CAO mcpServer format to OpenCode's opencode.…
patricka3125 9f08c9f
docs(opencode): add --yolo DANGEROUS caveat to permission troubleshoo…
patricka3125 92bfabf
docs: update Phase 4 report with e2e results and Phase 3 regression f…
patricka3125 3d70846
feat(opencode): native skill discovery via OPENCODE_CONFIG_DIR/skills…
patricka3125 7c0c224
fix(opencode): handle Nm Ns duration format and extend extraction buffer
patricka3125 8979d41
refactor(opencode): single capture-pane per get_output call; add wron…
patricka3125 079f4a9
refactor(opencode): rename on-disk config directory from opencode_cli…
patricka3125 b5c6078
Merge branch 'main' into feat/opencli-integration
patricka3125 bd92d87
fix(opencode): fall back to first agent-indented line when user messa…
patricka3125 89ad483
refactor(terminal_service): guard build_skill_catalog() call and upda…
patricka3125 b870f4f
docs: update Phase 6 dev report with alt-screen extraction fix and cl…
patricka3125 e3149a7
docs(opencode): correct extraction_tail_lines docstring + black forma…
patricka3125 23c56e0
docs(opencode): correct OPENCODE_DISABLE_MOUSE rationale and add UX c…
patricka3125 1b94c2f
untrack out of scope docs
patricka3125 38ded3e
fix(opencode): align auto-approve, agent ID, and MCP cleanup semantics
patricka3125 7358b78
docs(opencode): drop reference to removed design doc from changelog
patricka3125 b82db76
refactor(opencode): scope extraction_tail_lines to OpenCodeCliProvider
patricka3125 3f64824
test(opencode): close codecov gaps in cli provider and permission tra…
patricka3125 bc92a83
Merge branch 'main' into feat/opencli-integration
patricka3125 37d5510
Merge branch 'main' into feat/opencli-integration
patricka3125 f7ee124
Merge branch 'main' into feat/opencli-integration
haofeif 675d9f2
Merge branch 'main' into feat/opencli-integration
patricka3125 0478d08
Merge branch 'main' into feat/opencli-integration
patricka3125 41b0b40
docs(opencode): mark provider experimental — single-agent flows only
patricka3125 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,250 @@ | ||
| # OpenCode CLI Provider | ||
|
|
||
| ## Overview | ||
|
|
||
| The OpenCode CLI provider enables CLI Agent Orchestrator (CAO) to work with **OpenCode**, a terminal-based AI assistant with a native agent system. OpenCode uses Markdown files with YAML frontmatter as its agent format — nearly identical to CAO's own profile format — making this integration especially clean. | ||
|
|
||
| For the full design rationale, architecture decisions, and permission-mapping tables, see [`docs/feat-opencode-provider-design.md`](feat-opencode-provider-design.md). | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| 1. **OpenCode binary** — install from [opencode.ai](https://opencode.ai): | ||
| ```bash | ||
| npm install -g opencode-ai | ||
| # or | ||
| curl -fsSL https://opencode.ai/install | bash | ||
| ``` | ||
| 2. **Node.js 18+** — required by OpenCode for its plugin system | ||
| 3. **tmux 3.3+** — required by CAO for terminal management | ||
| 4. **API credentials** — configure whichever model provider you want OpenCode to use (Anthropic, OpenAI, etc.) per [OpenCode's auth docs](https://opencode.ai/docs/auth) | ||
|
|
||
| ### First-launch delay | ||
|
|
||
| On its **first ever launch** against a fresh CAO config directory (`~/.aws/opencode/`), OpenCode runs `npm install @opencode-ai/plugin` — roughly 57 MB of dependencies that take **5–30 seconds** to install. The TUI will appear blank until the install completes. This is expected; CAO's 120-second initialization timeout covers it automatically. | ||
|
|
||
| Subsequent launches complete in ~2 seconds. | ||
|
|
||
| ## Quick Start | ||
|
|
||
| ### 1. Install agent profiles | ||
|
|
||
| ```bash | ||
| # Built-in profiles | ||
| cao install code_supervisor --provider opencode_cli | ||
| cao install developer --provider opencode_cli | ||
| cao install reviewer --provider opencode_cli | ||
|
|
||
| # Custom or example profiles | ||
| cao install examples/assign/data_analyst.md --provider opencode_cli | ||
| cao install examples/assign/report_generator.md --provider opencode_cli | ||
| ``` | ||
|
|
||
| ### 2. Start the CAO server | ||
|
|
||
| ```bash | ||
| uv run cao-server | ||
| ``` | ||
|
|
||
| ### 3. Launch an agent | ||
|
|
||
| ```bash | ||
| # Standard launch — shows tool summary and asks for confirmation | ||
| cao launch --agents developer --provider opencode_cli | ||
|
|
||
| # Skip confirmation prompt (restrictions still enforced) | ||
| cao launch --agents developer --provider opencode_cli --auto-approve | ||
|
|
||
| # Specify model override | ||
| cao launch --agents developer --provider opencode_cli --model anthropic/claude-sonnet-4-6 | ||
|
|
||
| # Unrestricted (DANGEROUS) — agent can run any command | ||
| cao launch --agents developer --provider opencode_cli --yolo | ||
| ``` | ||
|
|
||
| Via HTTP API: | ||
|
|
||
| ```bash | ||
| curl -X POST "http://localhost:9889/sessions?provider=opencode_cli&agent_profile=developer" | ||
| ``` | ||
|
|
||
| ## Config Isolation | ||
|
|
||
| CAO runs OpenCode with `OPENCODE_CONFIG_DIR` and `OPENCODE_CONFIG` both pointing at `~/.aws/opencode/`, which is separate from the user's personal OpenCode config at `~/.config/opencode/`. This means: | ||
|
|
||
| - CAO-installed agents are visible in OpenCode's agent picker alongside the built-ins | ||
| - CAO's MCP wiring (`opencode.json`) never touches the user's personal setup | ||
| - Switching between `cao launch` and personal `opencode` usage is safe — they use independent config trees | ||
|
|
||
| Storage layout: | ||
|
|
||
| ``` | ||
| ~/.aws/opencode/ | ||
| ├── opencode.json # MCP servers + per-agent tool gating (written by cao install) | ||
| ├── package.json # written by opencode on first launch | ||
| ├── node_modules/ # ~57 MB, written by opencode on first launch | ||
| └── agents/ | ||
| ├── code_supervisor.md | ||
| ├── developer.md | ||
| └── ... | ||
| ``` | ||
|
|
||
| ## Permission and Tool Mapping | ||
|
|
||
| OpenCode enforces permissions natively via `permission:` YAML frontmatter in each agent file. CAO translates its `allowedTools` list to an OpenCode `permission:` dict at install time — **no entry in `utils/tool_mapping.py` is needed**. | ||
|
|
||
| For the complete CAO-category → OpenCode-tool mapping table and per-tool default policies, see [§9 of the design doc](feat-opencode-provider-design.md#9-tool-vocabulary). | ||
|
|
||
| ### Summary | ||
|
|
||
| | CAO category | OpenCode tools enabled | | ||
| |---|---| | ||
| | `execute_bash` | `bash` | | ||
| | `fs_read` | `read` | | ||
| | `fs_write` | `edit`, `write` | | ||
| | `fs_list` | `glob`, `grep` | | ||
| | `fs_*` | `read`, `edit`, `write`, `glob`, `grep` | | ||
| | `@<mcp-server-name>` | Handled in `opencode.json` (not frontmatter) | | ||
|
|
||
| Tools not in any enabled category default to `deny`. The following tools have hardcoded policies regardless of `allowedTools`: | ||
|
|
||
| | Tool | Policy | Reason | | ||
| |---|---|---| | ||
| | `task` | deny | Sub-agents escape CAO's terminal tracking | | ||
| | `question` | deny | Blocks unattended flows indefinitely | | ||
| | `webfetch`, `websearch`, `codesearch` | deny | Network egress — opt-in only | | ||
| | `todowrite`, `skill` | allow | In-memory / additive, no side-effects | | ||
|
|
||
| Pass `--yolo` (or set `allowedTools: ["*"]` in the profile) to allow all 13 tools including the above. | ||
|
|
||
| ### Auto-approve | ||
|
|
||
| ```bash | ||
| cao launch --agents developer --provider opencode_cli --auto-approve | ||
| ``` | ||
|
|
||
| With `--auto-approve`, CAO writes `permission: allow` for all enabled tools into the agent frontmatter at install time. Without it, permission-gated operations raise OpenCode's native `△ Permission required` dialog — CAO detects this as `WAITING_USER_ANSWER` and halts the polling loop until a human confirms. | ||
|
|
||
| ## Skills | ||
|
|
||
| CAO skills (e.g. `cao-supervisor-protocols`, `cao-worker-protocols`) are exposed to OpenCode agents through OpenCode's **native `skill` tool** with progressive loading — they are **not** baked into the agent's system prompt. | ||
|
|
||
| At `cao install --provider opencode_cli` time, CAO creates a symlink: | ||
|
|
||
| ``` | ||
| ~/.aws/opencode/skills → ~/.aws/cli-agent-orchestrator/skills/ | ||
| ``` | ||
|
|
||
| OpenCode auto-discovers `<OPENCODE_CONFIG_DIR>/skills/` and makes its contents available through the `skill` tool. Metadata (name, description) is listed up front; full skill bodies are loaded on demand. This means: | ||
|
|
||
| - Skill additions or removals under `~/.aws/cli-agent-orchestrator/skills/` take effect on the next OpenCode launch with no reinstall required. | ||
| - The agent's system prompt stays lean — only `profile.system_prompt`/`profile.prompt` is written to the `.md` body, with no catalog injection. | ||
| - CAO's `load_skill` MCP tool remains available as a second path to the same content (cross-provider parity). | ||
|
|
||
| For the full design rationale, see [§5.1 of docs/feat-opencode-provider-design.md](feat-opencode-provider-design.md#51-skill-delivery-native-discovery). | ||
|
|
||
| ## Status Detection | ||
|
|
||
| The provider detects terminal state from the tmux capture buffer (ANSI-stripped): | ||
|
|
||
| | State | Marker | | ||
| |---|---| | ||
| | `IDLE` | `ctrl+p commands` footer, no `esc interrupt` | | ||
| | `PROCESSING` | `esc interrupt` footer keybind | | ||
| | `COMPLETED` | `▣ <agent> · <model> · Ns` completion marker followed by idle footer | | ||
| | `WAITING_USER_ANSWER` | `△ Permission required` or `△ Always allow` heading | | ||
| | `ERROR` | Fallback — no state marker matched | | ||
|
|
||
| ## MCP Server Wiring | ||
|
|
||
| `cao install --provider opencode_cli` writes MCP server declarations into `~/.aws/opencode/opencode.json`: | ||
|
|
||
| - Each `mcpServers` entry from the agent profile is added under the top-level `mcp` key | ||
| - The server's tools are default-denied globally (`"<servername>*": false` under `tools`) | ||
| - Re-enabled per-agent under `agent.<profile_name>.tools` | ||
|
|
||
| `CAO_TERMINAL_ID` is **not** written to `opencode.json`. OpenCode spawns MCP subprocesses that inherit the tmux window's environment, so the terminal ID propagates naturally — the same mechanism Kiro uses. | ||
|
|
||
| ## End-to-End Testing | ||
|
|
||
| ```bash | ||
| # Install profiles first | ||
| cao install examples/assign/data_analyst.md --provider opencode_cli | ||
| cao install examples/assign/report_generator.md --provider opencode_cli | ||
| cao install developer --provider opencode_cli | ||
|
|
||
| # Start CAO server | ||
| uv run cao-server | ||
|
|
||
| # Run all OpenCode CLI e2e tests | ||
| uv run pytest -m e2e test/e2e/test_assign.py -k opencode -v | ||
|
|
||
| # Run a single test | ||
| uv run pytest -m e2e test/e2e/test_assign.py::TestOpenCodeCliAssign::test_assign_with_callback -v | ||
| ``` | ||
|
|
||
| The `test_assign_with_callback` test validates all four orchestration modes: | ||
| - **assign** (non-blocking): supervisor terminal created and stays IDLE | ||
| - **send_message** (inbox delivery): worker pushes result to supervisor inbox | ||
| - **status transitions**: IDLE → PROCESSING → COMPLETED across concurrent terminals | ||
| - **handoff** (blocking): inbox delivery triggers supervisor state transition | ||
|
|
||
| ## Known Limitations | ||
|
|
||
| ### Project-local `opencode.json` override | ||
|
|
||
| OpenCode's config merge precedence places a project-local `opencode.json` in the current working directory **above** `OPENCODE_CONFIG` (the CAO-managed file). If you `cao launch` in a directory that has its own `opencode.json` with conflicting `agent.<name>.tools` or `tools` entries, CAO's MCP wiring can be silently overridden for that agent. | ||
|
|
||
| **Workaround:** remove or rename the project-local `opencode.json` before launching CAO, or move it under `.opencode/` (a subdirectory OpenCode also searches but at a lower priority level). | ||
|
|
||
| ### Scrolling enters tmux copy mode | ||
|
|
||
| When you scroll (mouse wheel or trackpad) inside a CAO-managed OpenCode terminal, tmux enters copy mode instead of scrolling the TUI conversation history. This is intentional. | ||
|
|
||
| CAO launches OpenCode with `OPENCODE_DISABLE_MOUSE=1`, which prevents OpenCode from requesting application mouse-reporting mode (`\x1b[?1000h`). Without that request, tmux does not forward scroll events to the OpenCode process — it intercepts them and enters copy mode instead. | ||
|
|
||
| The reason for this trade-off: if OpenCode owned scroll events, scrolling the conversation history would move the completion marker (`▣ <agent> · <model> · Ns`) off screen. The footer (`ctrl+p commands`, `esc interrupt`) is pinned to the bottom of the TUI and remains visible regardless of scroll position, so IDLE and PROCESSING detection are unaffected. But COMPLETED detection requires both the completion marker and the idle footer to be present simultaneously in the captured frame — if the marker has scrolled away, CAO never detects COMPLETED even after the agent finishes. Disabling mouse keeps the frame locked to the most recent render. | ||
|
|
||
| Press `q` or `Escape` to exit copy mode. If you need to read earlier conversation history, use the `get_output` API endpoint or the `/terminals/<id>/output` endpoint to retrieve the full captured log. | ||
|
|
||
| ### `opencode.json` concurrent writes | ||
|
|
||
| Parallel `cao install --provider opencode_cli` invocations (e.g., from a batch script) can race on the shared `~/.aws/opencode/opencode.json` file. The second writer may clobber the first's agent entry. **Sequential installs are safe.** File locking is deferred to a future release. | ||
|
|
||
| ## Troubleshooting | ||
|
|
||
| ### First-launch blank TUI (5–30 seconds) | ||
|
|
||
| OpenCode installs `@opencode-ai/plugin` into `~/.aws/opencode/node_modules/` on the first launch. The terminal will appear blank until `npm install` completes. CAO's 120-second initialization timeout covers this automatically. | ||
|
|
||
| To pre-populate `node_modules/` before the first CAO launch (optional): | ||
| ```bash | ||
| OPENCODE_CONFIG_DIR=~/.aws/opencode opencode --help | ||
| ``` | ||
|
|
||
| ### "Unknown provider" error from the server | ||
|
|
||
| Ensure the CAO server running on port 9889 is the **dev version**, not the pre-installed binary: | ||
| ```bash | ||
| # Kill any stale installed binary | ||
| pkill -f 'cao-server' | ||
| # Start the dev server | ||
| uv run cao-server | ||
| ``` | ||
|
|
||
| ### Authentication / model errors | ||
|
|
||
| OpenCode itself handles model authentication. Verify your credentials are set for the model provider you want to use. Check `~/.config/opencode/opencode.json` (your personal config) for provider API keys, or set them via environment variables before launching. | ||
|
|
||
| ### Permission prompt blocking an automated flow | ||
|
|
||
| If OpenCode raises `△ Permission required` and your flow is blocking: | ||
| 1. Re-install the agent with `--auto-approve` to bake `permission: allow` into its frontmatter | ||
| 2. Or respond to the prompt manually in the tmux window | ||
| 3. Or use `--yolo` to disable all restrictions **(DANGEROUS — allows any command including `aws`, `rm`, `curl`)** | ||
|
|
||
| ### Status stuck as `PROCESSING` | ||
|
|
||
| This can happen if: | ||
| - OpenCode launched but the TUI hasn't painted yet (transient — the poller recovers) | ||
| - A `node_modules` install is still in progress (wait up to 120s) | ||
| - The `opencode` binary isn't on PATH in the tmux window's shell (check `echo $PATH` inside tmux) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -16,6 +16,7 @@ | |
| "gemini_cli", | ||
| "kimi_cli", | ||
| "kiro_cli", | ||
| "opencode_cli", | ||
| } | ||
|
|
||
|
|
||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.