Thank you for your interest in contributing!
# Clone the monorepo
git clone https://github.com/capybearista/opencode-plugins
cd opencode-plugins
# Install dependencies
bun install --frozen-lockfileThe repo pins Bun 1.3.12 (packageManager). The release workflow also pins Node 24.11.1 and npm 11.19.0. Match these versions when testing publishing behavior.
When using linked Git worktrees, check the working directory before installing or building. Husky can update the Git configuration shared by those worktrees. For isolated validation, use HUSKY=0 bun install --ignore-scripts --frozen-lockfile; do not clean another checkout's build output or change shared Git configuration as a workaround.
The canonical command is bun run test: it runs the release-guard tests, then Turbo's per-package test processes. Do not substitute bare bun test at the repo root; it bypasses package isolation and can share mocks between unrelated packages. Turbo's package tests depend on build because some tests inspect built artifacts.
Before opening a pull request, run the full gate:
bun run build && bun run typecheck && bun run lint && bun run testbun run check runs biome check --write in every package, so it edits files. bun run lint checks release tooling and packages without writing fixes. bun run typecheck likewise covers both surfaces.
Unit tests and smoke scripts don't run a real OpenCode. To try a change in your own setup before it is released:
-
Run
bun run build. -
In your config, replace the package's npm entry with the absolute path to its directory. Server plugins go in
opencode.json(c); TUI-only plugins go incli.json.{ "plugins": ["/path/to/opencode-plugins/packages/opencode-output-styles"] } -
Run
opencode service restart, then reopen the TUI. Do the same after every rebuild. -
When you're done, put the npm entry back and run
opencode plugin update.
On OpenCode 2.2.0 and later, a directory entry resolves through the package's exports, the same way the npm package does. Older versions load the root server.js / tui.js files instead, which re-export dist/. A combined server + TUI package such as output-styles loads both halves from the one entry.
For changes intended for the next package release, add a changeset with bun run changeset and select the bump. Packages are independently versioned.
Plugin majors track OpenCode majors, not strict semver. A package in release class vN (see tools/release-channels.json) only ever publishes major N, so:
- Use
patchfor fixes andminorfor features and for breaking changes within the same OpenCode major. Say "Breaking" in the changeset text so it lands in the changelog. - Use
majoronly to move a package to a new OpenCode major. Change itsreleaseClassintools/release-channels.jsonin the same PR (the guard rejects a class above the current OpenCode major,CURRENT_OPENCODE_MAJORintools/release-channel-guard.ts). - A new package needs one entry in
tools/release-channels.json; the guard rejects a workspace package without one. Its first release is a first publish (npm has never seen it), which CI'sRelease planstep shows before merge; CI can't publish it, so see First release of a new package.
bun run release:changesets checks pending changesets and package manifests against this rule without touching the registry. The Changesets workflow runs it on pull requests.
- The V2 packages (five ports, plus auto-advisor, which is V2-only) are published as
2.xonlatest. - The one V1-only package (
opencode-agent-prompt-inheritance) continues to publish underlatest. The output-styles V1 line is frozen at1.0.2. The loader and timeline V1 releases stay available at1.0.0and1.0.1by exact pin. - Keep V1 and V2 version/release batches separate. The guard checks all unpublished public workspace versions, not just the packages named in a changeset or Git diff. A mixed batch is rejected before publishing.
Publishing is CI-only, except a package's first release (see First release of a new package). The release workflow uses the npm-publish environment and npm trusted publishing, and runs bun run changeset:publish through the pinned Changesets Action. Version application and publication require maintainer authorization; do not run a real-registry publish locally.
The release flow on main:
- Merge a PR that carries a changeset. The Release workflow opens (or updates) a Version Packages PR with the version bumps and changelog entries.
- That PR is opened by
github-actions[bot], so its checks wait at "action_required". Click Approve and run workflows on the PR, then merge it once they pass. - The Release workflow publishes the unpublished versions with provenance, then pushes
name@versiontags and creates a GitHub release per package. - Verify on the registry with a direct curl (for example
curl -s https://registry.npmjs.org/@capybearista%2F<name>);npm viewcan lag. Use the registry API, not the npmjs.com website, which is behind a bot check.
The toolchain is Changesets CLI 3.x with changesets/action v2. They are upgraded together: action v2 rejects CLI v2. The guard pins the CLI version as well (see AGENTS.md).
Run bun run release:check to inspect the complete unpublished set and selected channel. It queries public registry metadata but does not publish or create Git tags. It rejects unsupported package/version combinations (including a version outside its class major), ambiguous batches, and failed or malformed registry responses.
npm trusted publishing can only be configured for a package that already exists, so CI cannot publish a package's first version: the Release job fails with ENEEDAUTH. bun run release:check (and CI's Release plan step) lists the package as first publish -> latest with a pointer here. A maintainer bootstraps it once:
- From a clean checkout of
main, runbun install --frozen-lockfile, then in the package directorybun run build,npm pack --dry-run(check the file list) andnpm publish --access public.npm whoamimust printcapybearista; log in withnpm loginfirst if it doesn't. A publish without a valid login fails with 404 Not Found, not 401. - On npmjs.com, add the package's trusted publisher: GitHub Actions,
capyBearista/opencode-plugins, workflowrelease.yml, environmentnpm-publish. Tick Allow npm publish (the Release job runsnpm publish); leave Allow npm dist-tag off. These fields cannot be edited later. A new connection expires unless it completes a publish within 2 days, so set it up shortly before the next release. - Create the
@capybearista/<name>@<version>tag on the commit you published from, and its GitHub release, which the Release job would otherwise have created.
npm may add a 0.0.0-stage version ("temporary package placeholder for staged publishing"). latest is unaffected; leave it. The manually published version has no provenance attestation; later CI releases do.
npm publication is not atomic across packages. If a job fails, inspect registry versions, dist-tags, and CI output before retrying. Changesets v3 reports failures per package and still creates Git tags for the packages that did publish, so a partial job leaves tags for what reached the registry. Re-running the Release workflow publishes only the versions still missing from the registry. Check that each published version also has its GitHub release; create any missing one from its tag. Do not bypass the guard or move a latest tag to recover a failed release.
We use conventional commits. Your commit messages must follow this format:
type(scope): description
Where type is one of:
feat— new featurefix— bug fixdocs— documentation changesstyle— formatting, no logic changesrefactor— code restructuringperf— performance improvementstest— test changesbuild— build system changesci— CI/CD changeschore— maintenance, dependenciesrevert— reverting a previous commit
Examples:
feat(output-styles): add /style clear command
fix(agents-loader): resolve path discovery bug
docs(readme): update install instructions
A commit-msg hook blocks commits that don't follow this format. That hook is the only Husky hook installed. There is no pre-commit hook.
- Create a branch:
git checkout -b feat/my-feature - Make your changes
- Add a changeset if the change should ship in a package
- Add tests if applicable
- Run quality gates
- Commit with conventional commit format
- Push and open a PR
Install and update instructions belong to the root README and the package READMEs. Do not duplicate per-plugin specs in a PR description. Background notes live in docs/README.md; V1 compatibility questions go to docs/v1-plugins.md.
Husky installs a single commit-msg hook that enforces conventional commits via commitlint.
CI runs typecheck, lint, test and the release plan (bun run release:check, read-only; it needs npm reachable) on pushes and pull requests. Markdown, docs, asset, and changeset-only changes skip CI through path filters. The Changesets workflow runs bun run release:changesets on pull requests that touch changesets, package manifests, or the release policy, so changeset-only PRs are still checked. Releases run separately in the release workflow, only from main.
packages/— published OpenCode pluginstools/— release-channel policy, guard and tests, plus the total-downloads badge workerdocs/— contributor notes and upstream reference snapshots (start at docs/README.md)
Each package in packages/ is independently versioned and published via Changesets.
- TypeScript strict mode is required
- Biome handles linting and formatting (
bun run checkto auto-fix,bun run lintto inspect) - Zero comments by default — only add when code isn't self-explanatory
- No
console.log— use structured approaches for logging - Colocate tests with source files (
src/index.test.ts)
All contributions are released under the MPL-2.0 license.