Language: English | 中文
Audience: developers and maintainers working on the CrewBee repository. If you only want to install or use CrewBee, start with the Installation Guide.
This guide covers local maintainer workflows, GitHub Actions CI/CD, official releases, npm publishing, and post-release verification. It is not required for normal CrewBee users.
CrewBee now supports two parallel flows:
- Keep the existing local development pack/install flow for day-to-day development.
- Add a registry publish flow so OpenCode can load CrewBee from the package name
crewbee.
This aligns CrewBee with the same package-name-first model used by oh-my-openagent:
{
"plugin": ["crewbee"]
}When OpenCode supports package-name plugin resolution, that config lets it pull CrewBee from the published npm package automatically.
For local development and verification:
npm install
npm run pack:local
npm run install:local:user
npm run doctorArtifacts:
- stable local tarball:
.artifacts/local/crewbee-local.tgz - OpenCode plugin config entry:
crewbee
Use this when you are iterating locally and want to test the package before publishing.
For official online distribution:
- npm package name:
crewbee - OpenCode config plugin entry:
crewbee - publish target: npm registry
- release automation: both local script and GitHub Actions are supported
This path is useful when you want to prepare or manually publish from your own machine.
npm run pack:release- builds a versioned tarball in
.artifacts/release/
- builds a versioned tarball in
node ./scripts/release-registry.mjs --dryRun- computes next version
- updates local package files
- runs
typecheck,test,doctor, simulators,pack:release, and package smoke - does not publish
node ./scripts/release-registry.mjs --publish- does the same validation
- then runs
npm publish --access public --provenance --tag <tag>
- Windows wrapper:
scripts\release-registry.bat
node ./scripts/release-registry.mjs --dryRunOr on Windows:
scripts\release-registry.batnode ./scripts/release-registry.mjs --publish --bump patchnode ./scripts/release-registry.mjs --publish --bump minornode ./scripts/release-registry.mjs --publish --version 0.2.0node ./scripts/release-registry.mjs --publish --version 0.2.0-beta.1 --tag beta- Reads current local version.
- Reads npm registry latest version when available.
- Computes next version.
- Updates
package.jsonandpackage-lock.json. - Runs:
npm run typechecknpm run testnpm run doctornpm run simulate:opencodenpm run simulate:compactnpm run pack:releasenpm run smoke:package
- Optionally publishes to npm.
Note: the local publish path requests npm provenance. Some local shells are not recognized by npm as supported provenance providers. If validation and
pack:releasehave already passed but npm fails withAutomatic provenance generation not supported for provider: null, publish the prepared package from the same checked version withnpm publish --access public --tag <tag>and record the provenance fallback in the release notes / handoff.
The local script currently focuses on package preparation and npm publish.
After a successful local publish, you should still do the Git steps explicitly:
git add package.json package-lock.json
git commit -m "Release vX.Y.Z"
git tag vX.Y.Z
git push origin main
git push origin vX.Y.Z
gh release create vX.Y.Z --generate-notesThis keeps the local manual path explicit and safe.
This is the recommended official release path. Humans merge normal feature/fix PRs into main, then manually run release-ci when a release should happen. After release-ci passes, the version bump, tag, npm publish, and GitHub Release happen automatically.
This flow requires CREWBEE_AUTOMATION_TOKEN to be configured as a repository secret. The token must be allowed to push to main and push tags; using the default GITHUB_TOKEN is not enough because GitHub suppresses downstream workflows triggered by GITHUB_TOKEN pushes.
File:
.github/workflows/ci.yml
It runs:
npm cinpm testnpm run typechecknpm run buildnpm run simulate:opencodenpm run simulate:compactnpm run doctornpm run pack:release- packed package smoke via
scripts/smoke-packed-package.mjs
| Workflow | Trigger | Role | Output |
|---|---|---|---|
.github/workflows/ci.yml |
push to main, any pull request |
Validate ordinary development changes | Required validation checks |
.github/workflows/release-ci.yml |
release PRs, or manual workflow_dispatch |
Validate release candidates; on manual release, bump version after gates pass | Release vX.Y.Z commit on main |
.github/workflows/release-tag.yml |
push to main |
Detect version-only release commits and create immutable tags | vX.Y.Z tag |
.github/workflows/publish.yml |
push of vX.Y.Z tag |
Publish npm package and create GitHub Release | npm release and GitHub Release |
Normal feature/fix PRs should not change package versions. Maintainers decide when to publish by manually running release-ci.
File:
.github/workflows/release-ci.yml
Triggers:
- pull requests targeting
mainwhose branch starts withrelease/v - or pull requests targeting
mainwith thereleaselabel - manual
workflow_dispatchto validatemain, bump the package version, and push the release commit
Release PR requirements:
- the PR must come from this repository, not a fork
package.json,package-lock.json, and the release branch version must match- the target version must not already exist on npm
- the PR may only change
package.jsonandpackage-lock.json
Release CI gates:
npm run typechecknpm testnpm run buildnpm run simulate:opencodenpm run simulate:compactnpm run doctornpm run pack:release- packed tarball smoke test
- install local tarball into the OpenCode user-level workspace
opencode models --print-logs --log-level DEBUGopencode agent list --print-logs --log-level DEBUG
Manual release behavior:
- runs the same release gates on the current
main - computes the next version from
patch,minor,major, or an explicit version - updates only
package.jsonandpackage-lock.json - commits
Release vX.Y.Zdirectly tomain - lets
release-tag.ymlcreate the release tag from that version bump
File:
.github/workflows/release-tag.yml
Trigger:
- push to
main
Behavior:
- if the push to
mainchangedpackage.jsonorpackage-lock.json, validate that both files contain the same release versionX.Y.Z - refuse to tag if
crewbee@X.Y.Zalready exists on npm - create and push tag
vX.Y.Zwhen it does not already exist - pushes that do not change package version files are ignored
File:
.github/workflows/publish.yml
Trigger:
- push of tag
vX.Y.Z
- Checks out the immutable release tag.
- Validates that tag,
package.json, andpackage-lock.jsonversions match. - If that npm version is already published, skips
npm publishand continues with registry smoke,latestpromotion, latest verification, and GitHub Release creation. This supports rerunning the same tag after a post-publish failure. - Runs the full release gates:
typecheck, tests, build, simulators, doctor, pack, package smoke, and real OpenCode loading smoke. - If that npm version is not already published, publishes the verified tarball to a staging dist-tag:
- stable releases first publish to
next - prereleases publish to their prerelease tag, for example
beta
- stable releases first publish to
- Fetches the just-published package back from npm and smoke-tests it.
- For stable releases, promotes
crewbee@X.Y.Ztolatest. - For stable releases, installs from the registry and reruns OpenCode smoke.
- Creates a GitHub release with generated notes.
- Merge normal feature or fix PRs into
mainas usual. - When you are ready to publish, manually run the
release-ciworkflow. - Choose
patch,minor,major, or provide an explicit version. release-ciruns the full release gates onmain.- If the gates pass,
release-cicommitsRelease vX.Y.Ztomainwith onlypackage.jsonandpackage-lock.jsonchanged. release-tag.ymldetects the package version bump onmainand createsvX.Y.Zautomatically.publish.ymlpublishes the tag automatically.- After success, verify:
- npm has
crewbee@<version> latestpoints to the released stable version- tag
v<version>exists - GitHub release exists
- npm has
The release commit is intentionally version-only. This keeps the release safety boundary while avoiding a hand-written second PR for every feature iteration.
If a release needs a specific version, do not edit package.json by hand, and do not bump to a new version after a publish failure. The standard entry point is still the manual release-ci workflow with the version input filled in.
GitHub UI steps:
- Open Actions ->
release-ci-> Run workflow. - Select branch
main. - Set
versionto the target version, for example0.2.0or0.2.0-beta.1. - Leave
bumpat the defaultpatch; it is ignored whenversionis provided. - Wait for
release-ci,release-tag, andpublishto complete.
If an Agent should execute the release, give it the version directly, for example:
Please release CrewBee 0.2.0
The Agent should run this procedure:
- Confirm the worktree has no uncommitted release-related changes; do not read or commit
.crewbee/,.crewbeectxt/, or.local/. - Trigger
release-ciwithworkflow_dispatch, usingversion=<target version>andbump=patch. - Wait for
release-cito create theRelease v<target version>commit. - Wait for
release-tag.ymlto create thev<target version>tag. - Wait for
publish.ymlto complete npm publish,latestpromotion, and GitHub Release creation. - Verify
npm view crewbee version,npm view crewbee dist-tags --json, and the GitHub Release.
Failure handling rules:
- If the failure happens before
Publish npm package to staging dist-tag, fix the workflow or environment and rerun the same tag / workflow. Do not increase the version. - If
npm publishsucceeded butlatestpromotion, latest verification, or GitHub Release creation failed, fix the workflow or environment and rerun the same tag / workflow.publish.ymldetects the existing version, skipsnpm publish, and resumes the remaining steps. - Only choose a new version when you intentionally want to publish a new product version.
After CrewBee is published, the target OpenCode config should reference the package name directly:
{
"plugin": ["crewbee"]
}CrewBee now writes and validates the canonical plugin entry as:
crewbee
instead of a file://.../opencode-plugin.mjs path.
This is the same model used by oh-my-openagent: the config references the package name, and the host/plugin workspace resolves the published npm package.
If you are doing development or the package is not yet published, use:
npm run install:local:userThat still installs a local tarball into the OpenCode user-level workspace, but the config entry remains the same canonical package name:
{
"plugin": ["crewbee"]
}Use the preserved local flow:
npm run install:local:userUse the local release preflight:
node ./scripts/release-registry.mjs --dryRunUse the manual release-ci flow:
workflow_dispatch release-ci -> Release vX.Y.Z commit -> automatic tag -> automatic publish
This keeps a single human entry point while preserving an immutable tag as the publish boundary.
Before release:
npm run typechecknpm run testnpm run doctornpm run simulate:opencodenpm run simulate:compactnpm run pack:releasenpm run smoke:package
After release:
- npm shows
crewbee@<version> - OpenCode config can use
"crewbee" npm run install:registry:usersucceedsnpm run doctoris healthy