Everything you need to change the plugin, run it from a checkout and test it.
| Tool | Why |
|---|---|
| Bun 1.3+ | Installs dependencies, runs tests, builds and scripts. |
| OpenCode 2.0.16+ | Runs the plugin; needed only for the live tests. |
| A model provider signed in to OpenCode | Live tests and real Runs. Unit tests and the UI need none. |
| Path | What |
|---|---|
packages/plugin |
The published package: engine, protocol, service, Gateway, TUI plugin, skill. |
packages/plugin/src/*.ts |
The engine. Host-independent; talks to OpenCode only through src/host.ts. |
packages/plugin/src/host/ |
The OpenCode V2 adapter: tools, hooks, events, RPC, commands, the skill. |
packages/plugin/src/service/ |
The service boundary every surface calls, and the plugin-RPC contract. |
packages/plugin/src/gateway/ |
HTTP + SSE Gateway and token auth. |
packages/plugin/src/tui/ |
The TUI plugin, precompiled to dist/tui.js. |
packages/plugin/skill/ |
The dynamic-workflows authoring skill the plugin registers. |
packages/web |
The web app (Solid + Vite), built into packages/plugin/dist/web. |
packages/plugin/test/fake-host.ts |
An in-memory OpenCode: most engine behaviour is tested against it. |
packages/plugin/test/live/ |
Live tests against a real OpenCode server. |
git clone https://github.com/malhashemi/opencode-dynamic-workflows
cd opencode-dynamic-workflows
bun install
bun run build # dist/tui.js and dist/webPoint a test project at the package directory (OpenCode resolves a directory plugin's server, tui and rpc
entries by path):
{
"$schema": "https://opencode.ai/config.json",
"plugins": [{ "package": "/absolute/path/to/opencode-dynamic-workflows/packages/plugin" }]
}- Server changes (engine, host, service, Gateway) need an OpenCode restart: Bun caches modules for the life of the process, so a location reload does not pick them up.
- TUI changes need
bun run buildand a restart. - Web app changes:
bun run --cwd packages/web devserves it with hot reload and forwards/v1toWF_GATEWAY(defaulthttp://127.0.0.1:4320). - Worktree Units (
isolation: "worktree") only work when the plugin is configured in a committedopencode.json(or globally): worktrees check out the committed tree. - Do not load the checkout where the published package is installed. A checkout (plugin id = its path) and the
npm package (id
@malhashemi/opencode-dynamic-workflows) are two plugins to OpenCode: both load, both register the same tools and RPC methods, and either may answer, with its own options (a globalinline: "allow"would bypass a project's approval). Test the checkout on a server with its own config directory, as the live tests do:XDG_CONFIG_HOME=/tmp/wf-dev-config opencode serve …with that directory'sopencode/opencode.jsonholding your providers and no plugins. To change the published package's options for one project, name the same package in the project config: its options override the global ones.
bun run check # oxfmt --check, oxlint, tsc, bun test
bun run format # format with oxfmt
bun run lint:fix # apply oxlint's automatic fixes
bun run schemas # regenerate docs/protocol/schemas after a protocol changeA few rules are off in .oxlintrc.json on purpose: sequential await in loops (Unit turns and repair loops are
sequential by design), closures in factories, and the Solid ref pattern in .tsx files.
bun run verify:liveEach suite starts a private opencode serve with its own database and a throwaway project under
$TMPDIR/opencode, loads the plugin from your checkout, and never touches your configuration. WF_LIVE_MODEL picks
the model (default claude-work/claude-opus-5-5; any provider/model you have works). The acceptance suite packs
the package, installs it through a git+file spec in a clean project and runs it end to end.
Releases are automated with release-please:
- Conventional commits land on
mainthrough pull requests. - release-please keeps a release pull request open with the next version and the changelog
(
packages/plugin/CHANGELOG.md). - Merging it tags the release; the Release workflow runs
bun run checkandbun run build, then publishes@malhashemi/opencode-dynamic-workflowsto npm through trusted publishing (OIDC) with provenance.
A release that failed part-way can be finished by running the Release workflow manually with its tag.