# Publishing to npm Low-level reference for how Paperclip packages are prepared and published to npm. For the maintainer workflow, use [doc/RELEASING.md](RELEASING.md). This document focuses on packaging internals. ## Current Release Entry Points Use these scripts: - [`scripts/release.sh`](../scripts/release.sh) for canary and stable publish flows - [`scripts/create-github-release.sh`](../scripts/create-github-release.sh) after pushing a stable tag - [`scripts/rollback-latest.sh`](../scripts/rollback-latest.sh) to repoint `latest` - [`scripts/build-npm.sh`](../scripts/build-npm.sh) for the CLI packaging build Paperclip no longer uses release branches or Changesets for publishing. ## Why the CLI needs special packaging The CLI package, `paperclipai`, imports code from workspace packages such as: - `@paperclipai/server` - `@paperclipai/db` - `@paperclipai/shared` - adapter packages under `packages/adapters/` Those workspace references are valid in development but not in a publishable npm package. The release flow rewrites versions temporarily, then builds a publishable CLI bundle. ## `build-npm.sh` Run: ```bash ./scripts/build-npm.sh ``` This script: 1. runs the forbidden token check unless `--skip-checks` is supplied 2. runs `pnpm -r typecheck` 3. bundles the CLI entrypoint with esbuild into `cli/dist/index.js` 4. verifies the bundled entrypoint with `node --check` 5. rewrites `cli/package.json` into a publishable npm manifest and stores the dev copy as `cli/package.dev.json` 6. copies the repo `README.md` into `cli/README.md` for npm metadata After the release script exits, the dev manifest and temporary files are restored automatically. ## Package discovery and versioning Public packages are discovered from: - `packages/` - `server/` - `cli/` `ui/` is ignored because it is private. The version rewrite step now uses [`scripts/release-package-map.mjs`](../scripts/release-package-map.mjs), which: - finds all public packages - sorts them topologically by internal dependencies - rewrites each package version to the target release version - rewrites internal `workspace:*` dependency references to the exact target version - updates the CLI's displayed version string Those rewrites are temporary. The working tree is restored after publish or dry-run. ## Version formats Paperclip uses calendar versions: - stable: `YYYY.M.D` - canary: `YYYY.M.D-canary.N` Examples: - stable: `2026.3.17` - canary: `2026.3.17-canary.2` ## Publish model ### Canary Canaries publish under the npm dist-tag `canary`. Example: - `paperclipai@2026.3.17-canary.2` This keeps the default install path unchanged while allowing explicit installs with: ```bash npx paperclipai@canary onboard ``` ### Stable Stable publishes use the npm dist-tag `latest`. Example: - `paperclipai@2026.3.17` Stable publishes do not create a release commit. Instead: - package versions are rewritten temporarily - packages are published from the chosen source commit - git tag `vYYYY.M.D` points at that original commit ## Trusted publishing The intended CI model is npm trusted publishing through GitHub OIDC. That means: - no long-lived `NPM_TOKEN` in repository secrets - GitHub Actions obtains short-lived publish credentials - trusted publisher rules are configured per workflow file See [doc/RELEASE-AUTOMATION-SETUP.md](RELEASE-AUTOMATION-SETUP.md) for the GitHub/npm setup steps. ## Rollback model Rollback does not unpublish anything. It repoints the `latest` dist-tag to a prior stable version: ```bash ./scripts/rollback-latest.sh 2026.3.16 ``` This is the fastest way to restore the default install path if a stable release is bad. ## Related Files - [`scripts/build-npm.sh`](../scripts/build-npm.sh) - [`scripts/generate-npm-package-json.mjs`](../scripts/generate-npm-package-json.mjs) - [`scripts/release-package-map.mjs`](../scripts/release-package-map.mjs) - [`cli/esbuild.config.mjs`](../cli/esbuild.config.mjs) - [`doc/RELEASING.md`](RELEASING.md)