Contributing
June 1, 2026 ยท View on GitHub
Thanks for your interest in contributing! This guide covers local development, testing, and the release process.
Setting up
Prerequisites
- Node.js 20 or newer (CI runs against 20 and 22).
- A local Fence build on your
PATH. The plugin spawnsfence --opencode-pre-tool-useon eachbashtool call, so an out-of-date or missing fence binary will surface as a runner error.
First-time setup
git clone https://github.com/fencesandbox/opencode-fence.git
cd opencode-fence
npm install
Workflow
npm run lint # biome check src
npm run typecheck # tsc --noEmit (catches editor-only type errors)
npm test # vitest run
npm run build # tsc -p tsconfig.build.json (writes dist/)
CI (.github/workflows/ci.yml) runs all four on every push and PR. prepublishOnly runs lint + typecheck + test + build before npm sees the package.
For watch-mode iteration:
npm run test:watch # vitest in watch mode
npx tsc -p tsconfig.build.json --watch # rebuild dist/ on save
Testing the plugin against a real OpenCode session
Three install modes, in order of how often you'll want them during development:
1. file:// URL (recommended for local dev)
Point OpenCode at your dist directly. OpenCode skips its npm-install step for file:// paths:
// ~/.config/opencode/opencode.jsonc
{
"$schema": "https://opencode.ai/config.json",
"plugin": [
"file:///absolute/path/to/opencode-fence/dist/index.js"
]
}
Rebuild dist (npm run build), restart OpenCode, you're testing the new code.
2. Local plugin file with the npm-published package
Once the package is on npm, you can write a local plugin shim that consumes the published version while still letting you pass createFencePlugin options:
// ~/.config/opencode/plugins/fence.ts
import { createFencePlugin } from "@fencesandbox/opencode-fence/factory";
export const Fence = createFencePlugin({
fenceBinary: "/Users/me/go/bin/fence",
// settingsPath, template, failOpenOnRunnerError, etc.
});
You'll need a package.json in ~/.config/opencode/ with @fencesandbox/opencode-fence as a dependency for OpenCode/Bun to resolve the import.
3. Tarball install
Useful for verifying the published package contents look right before actually publishing:
npm pack
# produces fencesandbox-opencode-fence-X.Y.Z.tgz
cd ~/.config/opencode
npm install /path/to/opencode-fence/fencesandbox-opencode-fence-X.Y.Z.tgz
OpenCode's plugin: ["@fencesandbox/opencode-fence"] then resolves to the tarball-installed copy.
Headless verification
Faster than driving the OpenCode TUI:
cd /path/to/some/repo/with/fence.json
opencode run --print-logs "run ls"
Watch the log for loading plugin path=... and any tool.execute.before activity. A working plugin denying ls shows up as Fence denied this command: ... in the output.
Release process
Releases are driven by scripts/release.sh. It bumps the package version, tags the commit, pushes, and creates a GitHub release. The publish workflow (.github/workflows/publish.yml) takes over from there: it runs lint + typecheck + test + build, then npm publish with the NPM_TOKEN secret.
./scripts/release.sh patch # 0.1.0 -> 0.1.1
./scripts/release.sh minor # 0.1.0 -> 0.2.0
./scripts/release.sh major # 0.1.0 -> 1.0.0
The script will:
- Verify you're on
mainand the working tree is clean. - Pull latest, confirm there are commits since the last tag.
- Compute the next version and ask for confirmation.
- Run
npm version <type>to bump + commit + tag. - Push the commit and tag to origin.
- Create a GitHub release with auto-generated notes.
The publish workflow then runs npm publish from a fresh checkout. Don't npm publish locally โ the workflow is the source of truth.
Prerequisites for releasing
ghCLI authenticated (gh auth status).NPM_TOKENsecret set in the GitHub repo settings, with publish rights for@fencesandbox/opencode-fence.- You have push access to
mainand tag-creation rights.
If a release fails mid-flight
If release.sh fails after npm version but before pushing, the local commit and tag exist but origin hasn't seen them. To recover:
git tag -d vX.Y.Z # remove the local tag
git reset --hard HEAD^ # undo the version-bump commit
Fix whatever broke, then re-run ./scripts/release.sh <type>.
If the publish workflow fails after the release was created on GitHub, you can re-run it from the Actions tab. The package isn't on npm yet, so npm publish is safe to retry. If you need to abandon the release entirely, delete the GitHub release and the tag (git push --delete origin vX.Y.Z); the next release.sh run will skip over the missing tag.