Release Checklist (npm + macOS)
Use pnpm (Node 22+) from the repo root. Keep the working tree clean before tagging/publishing.
Operator trigger
When the operator says “release”, immediately do this preflight (no extra questions unless blocked):
- Read this doc and
docs/platforms/mac/release.md. - Load env from
~/.profileand confirmSPARKLE_PRIVATE_KEY_FILE+ App Store Connect vars are set (SPARKLE_PRIVATE_KEY_FILE should live in~/.profile). - Use Sparkle keys from
~/Library/CloudStorage/Dropbox/Backup/Sparkleif needed.
- Version & metadata
- [ ] Bump
package.jsonversion (e.g.,2026.1.29). - [ ] Run
pnpm plugins:syncto align extension package versions + changelogs. - [ ] Update CLI/version strings:
src/cli/program.tsand the Baileys user agent insrc/provider-web.ts. - [ ] Confirm package metadata (name, description, repository, keywords, license) and
binmap points toedwin.mjsforedwin. - [ ] If dependencies changed, run
pnpm installsopnpm-lock.yamlis current.
- Build & artifacts
- [ ] If A2UI inputs changed, run
pnpm canvas:a2ui:bundleand commit any updatedsrc/canvas-host/a2ui/a2ui.bundle.js. - [ ]
pnpm run build(regeneratesdist/). - [ ] Verify npm package
filesincludes all requireddist/*folders (notablydist/node-host/**anddist/acp/**for headless node + ACP CLI). - [ ] Confirm
dist/build-info.jsonexists and includes the expectedcommithash (CLI banner uses this for npm installs). - [ ] Optional:
npm pack --pack-destination /tmpafter the build; inspect the tarball contents and keep it handy for the GitHub release (do not commit it).
- Changelog & docs
- [ ] Update
CHANGELOG.mdwith user-facing highlights (create the file if missing); keep entries strictly descending by version. - [ ] Ensure README examples/flags match current CLI behavior (notably new commands or options).
- Validation
- [ ]
pnpm build - [ ]
pnpm check - [ ]
pnpm test(orpnpm test:coverageif you need coverage output) - [ ]
pnpm release:check(verifies npm pack contents) - [ ]
EDWIN_INSTALL_SMOKE_SKIP_NONROOT=1 pnpm test:install:smoke(Docker install smoke test, fast path; required before release)- If the immediate previous npm release is known broken, set
EDWIN_INSTALL_SMOKE_PREVIOUS=<last-good-version>orEDWIN_INSTALL_SMOKE_SKIP_PREVIOUS=1for the preinstall step.
- If the immediate previous npm release is known broken, set
- [ ] (Optional) Full installer smoke (adds non-root + CLI coverage):
pnpm test:install:smoke - [ ] (Optional) Installer E2E (Docker, runs
curl -fsSL https://edwinpai.com/install.sh | bash, onboards, then runs real tool calls):pnpm test:install:e2e:openai(requiresOPENAI_API_KEY)pnpm test:install:e2e:anthropic(requiresANTHROPIC_API_KEY)pnpm test:install:e2e(requires both keys; runs both providers)
- [ ] (Optional) Spot-check the web gateway if your changes affect send/receive paths.
- macOS app (Sparkle)
- [ ] Build + sign the macOS app, then zip it for distribution.
- [ ] Generate the Sparkle appcast (HTML notes via
scripts/make_appcast.sh) and updateappcast.xml. - [ ] Keep the app zip (and optional dSYM zip) ready to attach to the GitHub release.
- [ ] Follow macOS release for the exact commands and required env vars.
APP_BUILDmust be numeric + monotonic (no-beta) so Sparkle compares versions correctly.- If notarizing, use the
edwin-notarykeychain profile created from App Store Connect API env vars (see macOS release).
- Publish (npm)
- [ ] Confirm git status is clean; commit and push as needed.
- [ ]
npm login(verify 2FA) if needed. - [ ]
npm publish --access public(use--tag betafor pre-releases). - [ ] Verify the registry:
npm view edwin version,npm view edwin dist-tags, andnpx -y edwin@X.Y.Z --version(or--help).
Troubleshooting (notes from 2.0.0-beta2 release)
- npm pack/publish hangs or produces huge tarball: the macOS app bundle in
dist/Edwin.app(and release zips) get swept into the package. Fix by whitelisting publish contents viapackage.jsonfiles(include dist subdirs, docs, skills; exclude app bundles). Confirm withnpm pack --dry-runthatdist/Edwin.appis not listed. - npm auth web loop for dist-tags: use legacy auth to get an OTP prompt:
NPM_CONFIG_AUTH_TYPE=legacy npm dist-tag add edwin@X.Y.Z latest
npxverification fails withECOMPROMISED: Lock compromised: retry with a fresh cache:NPM_CONFIG_CACHE=/tmp/npm-cache-$(date +%s) npx -y edwin@X.Y.Z --version
- Tag needs repointing after a late fix: force-update and push the tag, then ensure the GitHub release assets still match:
git tag -f vX.Y.Z && git push -f origin vX.Y.Z
- GitHub release + appcast
- [ ] Tag and push:
git tag vX.Y.Z && git push origin vX.Y.Z(orgit push --tags). - [ ] Create/refresh the GitHub release for
vX.Y.Zwith titleedwin X.Y.Z(not just the tag); body should include the full changelog section for that version (Highlights + Changes + Fixes), inline (no bare links), and must not repeat the title inside the body. - [ ] Attach artifacts:
npm packtarball (optional),Edwin-X.Y.Z.zip, andEdwin-X.Y.Z.dSYM.zip(if generated). - [ ] Commit the updated
appcast.xmland push it (Sparkle feeds from main). - [ ] From a clean temp directory (no
package.json), runnpx -y edwin@X.Y.Z send --helpto confirm install/CLI entrypoints work. - [ ] Announce/share release notes.
Plugin publish scope (npm)
We only publish existing npm plugins under the @edwin/* scope. Bundled plugins that are not on npm stay disk-tree only (still shipped in extensions/**).
Process to derive the list:
npm search @edwin --jsonand capture the package names.- Compare with
extensions/*/package.jsonnames. - Publish only the intersection (already on npm).
Current npm plugin list (update as needed):
- @edwin/bluebubbles
- @edwin/diagnostics-otel
- @edwin/discord
- @edwin/feishu
- @edwin/pipelines
- @edwin/matrix
- @edwin/msteams
- @edwin/nextcloud-talk
- @edwin/nostr
- @edwin/voice-call
- @edwin/zalo
- @edwin/zalouser
Release notes must also call out new optional bundled plugins that are not on by default (example: tlon).
