feat(opencode-workspaces-plugin): add Daytona workspace adaptor plugin for OpenCode - #38
feat(opencode-workspaces-plugin): add Daytona workspace adaptor plugin for OpenCode#38jamesmurdza wants to merge 26 commits into
Conversation
|
All contributors have signed the CLA. ✅ Thank you! |
|
I have read the CLA Document and I hereby sign the CLA |
…io/daytona@106ca0a3 Seeds apps/opencode-workspaces-plugin with libs/opencode-plugin as it stood in daytonaio/daytona at 106ca0a33 (tree 1e19ef2), so that the workspace- adaptor rewrite commits that follow apply as their authors wrote them. This tree is the pre-rewrite plugin; the next commit replaces most of it. It exists only to give the replayed history a faithful base. Signed-off-by: James Murdza <james@jamesmurdza.com>
Replaces the previous custom tools/session-manager implementation with a workspace adaptor that runs opencode inside a Daytona sandbox. Adds a system-prompt instructions file scoped to Daytona sessions, generates unique opencode-prefixed sandbox names, and forwards env vars through workspace create. Follow-up improvements folded in: - Drop workspace-API workarounds, import upstream types - Clean up Daytona sandbox when create() fails partway - Exclude host .opencode/ from sandbox repo upload - Clean up repo tarball on sandbox after extraction - Derive preview URL template from getPreviewLink - Warn/error clearly when DAYTONA_API_KEY is missing - Add integration and adapter-registration tests; fix flakiness - Update README (troubleshooting, migration guide, instructions, workspace creation steps, OpenCode 1.14.x requirement, test docs) - Add bun.lock Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
macOS bsdtar embeds Apple extended attributes (e.g. com.apple.provenance) as LIBARCHIVE.xattr.* pax headers and AppleDouble ._* entries when building the repo upload tarball. On extraction the sandbox's GNU tar floods stdout with "Ignoring unknown extended header keyword" warnings and litters the repo with ._* files. Pass --no-xattrs and COPYFILE_DISABLE=1 to suppress both. Both are no-ops on Linux hosts (COPYFILE_DISABLE is macOS-only; --no-xattrs is GNU tar's default), so the fix stays portable. Adds an optional env passthrough to spawnAsync to set COPYFILE_DISABLE for the tar invocation. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
Add a "Running against a local OpenCode build" subsection explaining how to test the plugin against a from-source OpenCode checkout: run `bun dev` from the OpenCode repo root with the test project as a trailing argument. Notes the JSX-config (jsxImportSource) reason it must run from packages/opencode, and the "Cannot find module 'react/jsx-dev-runtime'" failure that results from launching the entry point inside the test project instead. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…teps The source-parse and adaptor-registration snippets assumed different cwds without saying so. Add the cd lines (libs/opencode-plugin and /tmp/myproject) so each is unambiguous. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
The create() run() helper wrote every sandbox command's stdout to the host terminal, flooding it with the opencode installer's progress bar/banner, tar output, etc. during workspace creation. Surface command output only on failure (already captured in the thrown error) so the happy path stays quiet. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
The installer's "latest release" lookup hits api.github.com, which is rate-limited (HTTP 429) and fails with "Failed to fetch version information" when many sandboxes install in a short window — aborting workspace creation. Pass VERSION to skip that lookup. Also moves the env vars onto bash (right of the pipe) where the installer actually reads them, instead of curl. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
File logger to /tmp/daytona-plugin.log tracing each create()/remove()/target() step, so a stalled or failing step in the experimental-workspace flow is visible without streaming to the UI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
The sandbox stops servicing new commands while opencode runs its one-time DB migration on first start, so a health poll issued during that window hangs indefinitely and never returns — wedging the poll loop, and thus workspace creation, forever (the TUI shows "Creating…" with no progress) even after the server is actually healthy. Wrap each poll (and the final log fetch) in a 5s client-side timeout: a stuck poll is abandoned, treated as not-ready-yet, and the loop continues. Once the migration finishes a later poll returns and creation completes normally. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
Drives the real OpenCode TUI via tmux (open -> /warp -> select Daytona -> wait for sandbox -> send a message) against the released opencode binary. Hard-asserts the plugin's responsibilities: the workspace provisions (sandbox started, remote /global/health healthy, create() completes without hanging) and the remote answers (session contains an assistant reply echoing a magic token). Logs — but does not assert — whether the host TUI renders the reply, since that depends on OpenCode's experimental-workspace global sync (an intermittent upstream gap, not this plugin). Gated on DAYTONA_API_KEY + tmux + a released opencode binary; skipped otherwise. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…them as dead Idle Daytona sandboxes auto-stop after ~15 min. The workspace global-sync connects to the sandbox via target() (it dials <url>/global/event), so once a sandbox paused, that endpoint failed and the host logged a relentless "failed to connect to global sync" (HTTP 400) storm — which destabilized the host and broke chat rendering. The sandbox wasn't dead, just paused. - target(): if the sandbox isn't 'started', resume it (sandbox.start()) and relaunch opencode serve (the server process doesn't survive a stop/start), then return the preview link. Cache entries carry a short verification TTL so bursty target() calls stay fast. - create(): disable auto-delete (autoDeleteInterval: -1) so a timeout only pauses the sandbox (resumable) rather than deleting it (which orphans the OpenCode workspace entry and is what actually triggered the 400 storm). - Factor the server launch + health-wait into ensureServerRunning(), shared by create() and target(). Verified live: create -> stop (simulated timeout) -> access resumes the sandbox, relaunches the server, and /global/health is reachable through the proxy again. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…alone npm setup The plugin arrived from the Daytona monorepo carrying that repo's Nx wiring. None of it works here, so swap it for the same standalone layout the other Node projects in this repo use: - drop project.json (Nx build/test/set-version/publish targets) and tsconfig.lib.json (project references into ../../tsconfig.base.json, which does not exist here); fold the compiler options into a single tsconfig.json - drop bun.lock for package-lock.json — CI installs with `npm ci` - drop README.legacy.md and .npmignore, both artifacts of npm publishing - mark the package private: this is an app, not a published package - add scripts: build (tsc, emits in place), typecheck, test (bun) - add LICENSE, and ignore the in-place tsc output Also add `opencode-ai` as a devDependency. Every test file needs an `opencode` binary — @opencode-ai/sdk does not bundle one, it cross-spawns `opencode` from PATH — and only e2e-tui.test.ts degrades to a skip without it; the other two fail outright. The npm package ships the real binary as platform optionalDependencies, so `npm ci` now yields a runnable suite with nothing installed globally and no curl-pipe-bash anywhere. The `test` script points OPENCODE_BIN and PATH at node_modules/.bin so both CI and a fresh clone pick it up. Verified: `npm run typecheck` and `npm run build` pass with @opencode-ai/plugin resolving to 1.18.11 (the manifest floor is ^1.4.11), so the WorkspaceAdapter / experimental_workspace surface the plugin builds on is still intact upstream. `npm test` on a PATH stripped of any global opencode gives 1 pass / 10 skip / 0 fail — the skips are the live tests wanting DAYTONA_API_KEY. Signed-off-by: James Murdza <james@jamesmurdza.com>
The README arrived describing a package published to npm from the Daytona
monorepo. Neither is true here, so:
- installation now uses a `file://` plugin spec against a local checkout,
since this app is not published; the symlink recipe stays as the alternative
- development setup is `git clone integrations` + `npm ci`, replacing the
yarn + `npx nx run opencode-plugin:{test,build,publish}` commands
- the "Migrating from v0.167.0" section became "Relationship to
@daytona/opencode": the two plugins now coexist in this repo rather than
superseding each other by version, so the useful thing to document is how
they differ and which to reach for
- documented the /tmp/daytona-plugin.log debug file
- corrected paths (libs/opencode-plugin -> apps/opencode-workspaces-plugin)
and the project-structure tree
The testing section gets a per-file table of what each test needs and covers,
and states plainly that nothing runs the suite in CI. Missing prerequisites
make these tests skip rather than fail, so a green run proves very little on
its own — the table exists so the skip count can be read against something.
Single-file runs go through `npm test -- <file>` rather than bare `bun test`.
integration.test.ts and e2e-tui.test.ts launch the binary by absolute path
(OPENCODE_BIN, defaulting to ~/.opencode/bin/opencode), which only the `test`
script sets; calling bun directly makes them look for a global install a fresh
clone does not have, and both fail quietly when it is missing — one waits 60s
and blames the server, the other just skips.
Prose is unwrapped to one line per paragraph, matching the other three
READMEs in apps/.
Also lists the app in the root README's Apps table.
Signed-off-by: James Murdza <james@jamesmurdza.com>
…test.ts Despite its name, this test never touched the plugin. It inlined a ~115-line copy of the workspace adaptor into a temp project and ran OpenCode against that. The copy had drifted from the real plugin and was missing two fixes that matter: - the create() error path, so a failure leaked its sandbox instead of deleting it - withTimeout() on the health polls, so a command issued during opencode's first-start DB migration hung instead of failing Together those produced the observed behaviour: a ~300s hang ending in a client-side fetch timeout, with a running sandbox left behind. A copy of the thing under test proves nothing about the thing under test. Load the real plugin through a file:// spec in the temp project's opencode.json, the way plugin.test.ts already does. The temp-project `npm install` goes with it — the plugin's imports resolve from this package's node_modules, since the spec points inside it. Two further fixes the switch exposed: - the repo was built on whatever init.defaultBranch gives (main on current git) while the workspace asked for `master`. The real plugin passes that to `git clone --branch`, so it could never have cloned. Both sides now use TEST_BRANCH, forced with `git branch -M` so the host's git config cannot change the outcome. - afterAll only knew about a sandbox once the create request came back, so a hang left nothing recording what to delete. It now also parses `create: start name=` out of the plugin's debug log, which is written before the sandbox is provisioned. Same trick e2e-tui.test.ts uses. Verified live: 2 pass / 0 fail in 25s, down from a 300s hang. Step 5 now lists README.md, index.ts, package.json, src/ and .git inside the sandbox — the repo upload only exists in the real plugin, the copy just made an empty directory. The plugin log shows create -> "server healthy; done" and a matching "remove: deleted sandbox", so create() and remove() are both covered now. Sandbox count returned to its pre-run baseline. Signed-off-by: James Murdza <james@jamesmurdza.com>
…e_modules The tests defaulted OPENCODE_BIN to ~/.opencode/bin/opencode — a global install — and the npm `test` script papered over that by exporting OPENCODE_BIN and PATH. So the suite only worked through one invocation path. Calling `bun test` directly, which is the obvious thing to do, broke on a fresh clone, and broke quietly: integration.test.ts waited 60s and blamed the server, e2e-tui.test.ts skipped its whole file and reported success. Invert it. `opencode-ai` is already a devDependency shipping the binary as platform optionalDependencies, so default to that and drop the env wiring: process.env.OPENCODE_BIN || resolve(import.meta.dir, '../node_modules/.bin/opencode') OPENCODE_BIN still wins when set, so pointing at a local OpenCode build is unchanged. plugin.test.ts needs nothing — it goes through PATH, which bun and npm both populate from node_modules/.bin. The `test` script collapses to `bun test --timeout 180000`, and the README loses its caveat paragraph rather than having to explain a footgun. Verified with no global opencode reachable at all (PATH stripped, HOME pointing at an empty dir): bun test test/integration.test.ts 2 pass / 0 fail (was: "Server did not start") bun test test/e2e-tui.test.ts runs the test (was: 0 pass / 3 skip) e2e-tui still fails its final magic-token assertion, unchanged by this and tracked separately — the point here is that it now runs instead of silently skipping. Both runs cleaned up their sandboxes; the account is back to zero opencode-* sandboxes. Signed-off-by: James Murdza <james@jamesmurdza.com>
… in workspaces The e2e test provisioned a healthy sandbox but the remote never produced an assistant reply. Two root causes: 1. Pinned OPENCODE_VERSION (1.15.13) ships no built-in "opencode" Zen provider models (e.g. opencode/big-pickle), so a session created with one silently produces no reply — the server is healthy but has no usable model. Bump the pin to 1.18.11 to match recent host builds. 2. opencode proxies every remote session request with `?directory=<host worktree>` in the URL, and the server resolves the directory from that query param before the x-opencode-directory header (searchParams "directory" || header || cwd). The host path (/tmp/…, /Users/…) does not exist in the sandbox, so the remote session dies immediately with `FileSystem.realPath ENOENT`. Symlink the host worktree path to REPO_PATH during create() so that query param resolves to the extracted repo. With both fixes the e2e test passes: the remote generates the reply and it even renders back in the host TUI. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
… symlink Reference opencode PR anomalyco/opencode#40136, which strips the host `directory` query param before proxying to the remote — the upstream fix for the bug the worktree->REPO_PATH symlink works around. Once it ships in a release and OPENCODE_VERSION is bumped to include it, the symlink can be removed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…pencode to 1.18.15 opencode 1.18.15 ships the upstream fix (anomalyco/opencode#40136) that strips the `directory` query param before proxying to the remote, so the host worktree path no longer needs to be symlinked to REPO_PATH inside the sandbox. Remove the now-redundant `ln -sfn` workaround and pin the sandbox opencode version to 1.18.15 to match. Verified end-to-end with test/e2e-tui.test.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…DE_VERSION comment Drop the obsolete caveat about builds older than the free-models release; the pin is well past it now. Keep the rate-limit rationale for passing VERSION to the installer. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…ion waitForServer Under bun's test runner, a fetch to the child opencode server (spawned with piped stdio) can hang indefinitely even though the server is healthy — an external curl to /global/health returns 200 in ~8ms while the in-test fetch never resolves. Since waitForServer awaited fetch with no timeout, that single stuck request wedged the whole loop until the bun test hook timed out (180s+), failing the suite before create() was ever reached. Add AbortSignal.timeout(3000) so the stuck request rejects and the loop retries; the retry connects and the server is detected as ready. Verified: the suite went from hanging every run to 2/2 clean passes (~25s each), including the real create -> verify -> delete workspace cycle. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…ary from README - Collapse the two equivalent test commands to one: `npm test -- <file>` carries the required --timeout, whereas a bare `bun test <file>` uses bun's 5s default and times out — so it wasn't an alternative, just a broken variant. - Drop the duplicate `npm ci` blocks (Development > Setup was a verbatim copy of Installation; Running Tests repeated it) and the redundant Setup subsection. - Turn the test prerequisites prose into a scannable list. - Remove `npm run build` (output is gitignored and unused) and the duplicate "no build step required" note; keep the single statement in Installation. - Trim the CI paragraph to one line and tighten a couple of restated sentences. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…age wording - Lead with the test-file table, then prerequisites, then the run commands. - Describe each file's coverage in plain sentences; fold the billing caveat into the plugin.test.ts row (covers the removed "billable sandboxes" line). - Drop the redundant "Setup is the same as Installation" line. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…ts in Needs column plugin.test.ts listed a per-test breakdown (nothing / DAYTONA_API_KEY); show the minimum needed to run all its tests instead, consistent with the other rows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
- Open with a one-sentence summary of the three suites. - Replace the per-file table with a per-command table (run all + each file), folding Needs/Covers into each command row. - Drop the --timeout note (npm test already applies it) and the OPENCODE_BIN note (covered in "Running against a local OpenCode build"). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…ereqs, drop Needs column Give DAYTONA_API_KEY and tmux their own prerequisite bullets (noting tmux is only for the e2e test), and remove the now-redundant Needs column from the command table. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
…elopment Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Signed-off-by: James Murdza <james@jamesmurdza.com>
a256e9e to
19edfc0
Compare
There was a problem hiding this comment.
3 issues found across 13 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="apps/opencode-workspaces-plugin/.opencode/plugin/daytona/index.ts">
<violation number="1" location="apps/opencode-workspaces-plugin/.opencode/plugin/daytona/index.ts:208">
P2: Concurrent workspace creates in the same millisecond share this temp directory, causing clone/tar races and cleanup to fail or provision the wrong repository. Use a unique directory from `mkdtemp` before creating the sandbox files.</violation>
</file>
<file name="apps/opencode-workspaces-plugin/test/integration.test.ts">
<violation number="1" location="apps/opencode-workspaces-plugin/test/integration.test.ts:31">
P1: Hardcoded `/tmp/daytona-plugin.log` is shared between `integration.test.ts` and `e2e-tui.test.ts`. If both run concurrently (e.g., parallel CI jobs on the same host) they overwrite each other's logs, causing both tests to miss sandbox names in cleanup and potentially leave orphan sandboxes running. Use a unique path per test file.</violation>
<violation number="2" location="apps/opencode-workspaces-plugin/test/integration.test.ts:248">
P2: Workspace creation fetch is unbounded — if the plugin hangs during provisioning the test freezes until the runner timeout. Add `signal: AbortSignal.timeout(...)`, matching the pattern already used in `waitForServer` for the health check.</violation>
</file>
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
| // The plugin's own debug log. It records every workspace it starts building, | ||
| // which is the only reliable way to know what to clean up when a create call | ||
| // hangs rather than returning. Kept in sync with e2e-tui.test.ts. | ||
| const PLUGIN_LOG = '/tmp/daytona-plugin.log' |
There was a problem hiding this comment.
P1: Hardcoded /tmp/daytona-plugin.log is shared between integration.test.ts and e2e-tui.test.ts. If both run concurrently (e.g., parallel CI jobs on the same host) they overwrite each other's logs, causing both tests to miss sandbox names in cleanup and potentially leave orphan sandboxes running. Use a unique path per test file.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/opencode-workspaces-plugin/test/integration.test.ts, line 31:
<comment>Hardcoded `/tmp/daytona-plugin.log` is shared between `integration.test.ts` and `e2e-tui.test.ts`. If both run concurrently (e.g., parallel CI jobs on the same host) they overwrite each other's logs, causing both tests to miss sandbox names in cleanup and potentially leave orphan sandboxes running. Use a unique path per test file.</comment>
<file context>
@@ -0,0 +1,290 @@
+// The plugin's own debug log. It records every workspace it starts building,
+// which is the only reliable way to know what to clean up when a create call
+// hangs rather than returning. Kept in sync with e2e-tui.test.ts.
+const PLUGIN_LOG = '/tmp/daytona-plugin.log'
+async function readLog(): Promise<string> {
+ return await readFile(PLUGIN_LOG, 'utf8').catch(() => '')
</file context>
| throw new Error('DAYTONA_API_KEY environment variable is not set') | ||
| } | ||
|
|
||
| const temp = join(tmpdir(), `opencode-daytona-${Date.now()}`) |
There was a problem hiding this comment.
P2: Concurrent workspace creates in the same millisecond share this temp directory, causing clone/tar races and cleanup to fail or provision the wrong repository. Use a unique directory from mkdtemp before creating the sandbox files.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/opencode-workspaces-plugin/.opencode/plugin/daytona/index.ts, line 208:
<comment>Concurrent workspace creates in the same millisecond share this temp directory, causing clone/tar races and cleanup to fail or provision the wrong repository. Use a unique directory from `mkdtemp` before creating the sandbox files.</comment>
<file context>
@@ -0,0 +1,366 @@
+ throw new Error('DAYTONA_API_KEY environment variable is not set')
+ }
+
+ const temp = join(tmpdir(), `opencode-daytona-${Date.now()}`)
+ const d = getDaytona()
+ debug(
</file context>
|
|
||
| test('creates and deletes workspace via API', async () => { | ||
| console.log('\n=== Step 4: Create workspace via API ===') | ||
| const createRes = await fetch(`http://127.0.0.1:${serverPort}/experimental/workspace`, { |
There was a problem hiding this comment.
P2: Workspace creation fetch is unbounded — if the plugin hangs during provisioning the test freezes until the runner timeout. Add signal: AbortSignal.timeout(...), matching the pattern already used in waitForServer for the health check.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/opencode-workspaces-plugin/test/integration.test.ts, line 248:
<comment>Workspace creation fetch is unbounded — if the plugin hangs during provisioning the test freezes until the runner timeout. Add `signal: AbortSignal.timeout(...)`, matching the pattern already used in `waitForServer` for the health check.</comment>
<file context>
@@ -0,0 +1,290 @@
+
+ test('creates and deletes workspace via API', async () => {
+ console.log('\n=== Step 4: Create workspace via API ===')
+ const createRes = await fetch(`http://127.0.0.1:${serverPort}/experimental/workspace`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
</file context>
|
@jamesmurdza Reviewed the full source and live-tested everything against real Daytona sandboxes (5 A few requests before merge, all small: 1. Normalize "adaptor" → "adapter"The upstream API spells it adapter everywhere ( 2. README: "Running the e2e on any machine" sectionThe e2e drives the real TUI, so it inherits the developer's shell and global opencode Please add a short README subsection under Running Tests documenting this, with the why:
The custom-agent behavior itself looks like upstream workspace-API territory (remote can't 3. Stale comment: the TUI-render "sync gap"The e2e header (test/e2e-tui.test.ts:16–20) says OpenCode's global sync delivers state 4. Minor / documentation
On the cubic comments
|
Description
Adds
apps/opencode-workspaces-plugin: an OpenCode plugin that provisions Daytona sandboxes as remote workspaces via OpenCode's experimental workspace-adaptor API.This PR was originally ported from daytonaio/daytona#4504, with Nx build config replaced by the standalone npm layout used elsewhere in this repo, and fixing several issues in the original plugin. It is not published and does not change
packages/opencode-plugin.This app has extensive test coverage, including end-to-end tests that run a full user flow in the actual OpenCode TUI with the plugin installed.
Upstream bugs in OpenCode
To make this PR work, two PRs were raised in OpenCode:
These PRs have both been merged and shipped.
Related issue(s)
Scope
(e.g.
feat(pi-extension): ...,fix(adk-plugin): ...) — it becomes the squash-commitmessage that release-please reads.
Checks
Legal
DCO (
git commit -s); the sign-off matches thecommit author.
1 of 15 commits signed off — needs
git rebase --signoff main.Contributor License Agreement.
On my first PR, the CLA assistant will comment and I will reply to sign (once).