Autoship
Drive an npm release end to end: changeset, fix loop, push, CI watch, Version Packages PR merge, publish watch, npm verification.
- IS: the full release pipeline for an existing changesets-based npm package, from writing the changeset file to confirming the new version on the registry, plus diagnosing why a release run did not version or publish.
- IS NOT: opening a feature PR (use
pr-creator), monitoring a feature PR for reviews, conflicts, or CI (use pr-babysitter), general build or type fixes outside a release flow, or scaffolding a new package (use scaffold-cli, which hands off to autoship for the first release).
The Release Loop
One workflow, two successive runs. Misreading it as two workflows causes most autoship mistakes.
- Push a commit containing a pending
.changeset/*.md file to the default branch.
- The release workflow runs:
changesets/action sees pending changesets, runs changeset version in CI, and opens or updates a PR on branch changeset-release/<default-branch> (title "Version Packages", suffixed "(next)" in pre mode) carrying the package.json bump and CHANGELOG.md entry.
- Merge that PR once every check is green.
- The same workflow runs again. With no pending changesets left, the action runs its publish script (
changeset publish), which publishes to npm and, by default, pushes the git tag and creates a GitHub release.
The local job ends at "push the changeset file". CI owns versioning and publishing.
The action has two live majors with different input names: @v1 takes publish:, @v2 takes publish-script:. Read the uses: line before diagnosing a run that versioned but never published.
Reference Files
| File | Read when |
|---|
references/changeset-and-commit.md | Steps 1-3: writing the changeset file, discovering and running gates non-interactively, staging the release commit |
references/ci-polling.md | Step 4 and Step 5 watches: the Monitor tool, the commit watch script, failure classification, the Changeset Status check |
references/version-pr-and-publish.md | Once CI is green: workflow shape, finding and merging the Version Packages PR, the publish run, npm verification, publish failure diagnosis |
evals/evals.json | Only when changing this skill; never loads during a release |
Intent Map
| Intent | Steps | Notes |
|---|
| Full autoship (ship / release / publish) | 1-5 | Default entry point. End to end through publish, no intermediate prompts |
| Create changeset only | 1 | Stage a release without pushing |
| Fix gates and push | 1-3 | Changeset, fixes, commit, no CI watch |
| Watch CI only | 4-5 | Changeset already pushed |
| Merge Version Packages PR only | 4b-5 | CI already green; merges once preconditions hold |
| Fix gates only | 2 | Inside a release flow; no changeset needed |
| Diagnose a release that did not publish | read-only | references/version-pr-and-publish.md (PR-wait timeout checks, publish failure table) |
"Ship it" with no npm release context routes to pr-creator.
Safety Tiers
Invoking autoship is standing consent for the full release flow. Do not pause mid-flow to re-confirm; gate risky steps on objective preconditions instead.
- Green (execute directly): reads: CI and PR state,
npm view, pending changesets, package.json scripts, git history and status.
- Yellow (announce in one line, then execute): writing changeset files, running lint/type/test/format fixers,
git add/commit/push, starting watches, and merging the Version Packages PR once its identity is confirmed and every check passes.
- Red (explicit confirmation required): force-pushes, history rewrites, repository settings changes, any destructive git operation.
Workflow
Copy this checklist to track progress:
Autoship progress:
- [ ] Step 1: Create changeset (default patch)
- [ ] Step 2: Fix lint, types, tests, format
- [ ] Step 3: Commit and push the changeset
- [ ] Step 4a: Watch CI on the pushed commit
- [ ] Step 4b: Find and merge the Version Packages PR
- [ ] Step 5: Watch the publish run, verify on npm
Step 1: Create changeset (default patch)
- Load
references/changeset-and-commit.md.
- Inspect pending changesets and their package coverage. Reuse those covering the requested release; add one only for uncovered changes. Ask only if unrelated pending releases make the publish scope ambiguous.
- Default to
patch; minor or major only on explicit user instruction.
- Write the file directly (the interactive prompt needs a TTY); the summary is user-facing changelog text inferred from
git log --oneline -10.
pnpm changeset status validates the file: a misspelled package name fails here instead of in CI.
Step 2: Fix lint, types, tests, format
- Discover commands from
package.json scripts (check, lint, typecheck, test, format, fix); in non-Node repos check Makefile, Cargo.toml, pyproject.toml, go.mod.
- Run lint, typecheck, test, format. After any code change, re-run from the first gate: a type fix routinely breaks lint, and a lint autofix can break a test.
- Scope auto-fixers to changed files where supported, then check
git status: broad fix/format scripts reformat files outside the change (MDX is a frequent casualty). Undo only fixer changes introduced by this run, preserving pre-existing edits in the same files.
- Cap the loop at 5 fix iterations per gate, reporting the remaining error count each pass; then stop and report the gate, remaining error count, and last error output.
Step 3: Commit and push the changeset
- Stage the changeset file and in-scope fixes by explicit path; sweep
git status --porcelain for hook artifacts (a root schema.gql is a known one) before committing.
- Commit (
chore: add <type> changeset for <package>) and push.
- The pushed commit must still contain
.changeset/*.md (first Gotcha).
Step 4a: Watch CI on the pushed commit
- Load
references/ci-polling.md.
- Start the commit watch on the pushed SHA. It emits a line per state change and one
TERMINAL: line when every run for that commit (CI and the release workflow) completes. An idle first poll is normal; runs take time to queue.
- On failure, classify from
gh run view <id> --log-failed: flaky or infra gets gh run rerun <id> --failed (max 3); a real failure gets a fix, commit, push, and a fresh watch on the new SHA.
Step 4b: Find and merge the Version Packages PR
- Load
references/version-pr-and-publish.md.
- Find the open PR whose head is
changeset-release/<default-branch>. If absent, run the PR-wait watch (10-minute cap).
- Merge only when all three hold:
- Head branch is
changeset-release/<default-branch> (title normally "Version Packages", but pr-title can rename it). Never merge any other PR.
gh pr checks <n> --json name,bucket reports bucket: pass for every check.
gh pr view <n> --json mergeable reports MERGEABLE (on UNKNOWN, wait and re-query).
- Announce ("Merging Version Packages PR #N: <package>@<version>"), then
gh pr merge <n> --squash --delete-branch, or the merge method the repo allows. Any failed precondition: stop and report, never merge.
Step 5: Watch the publish run, verify on npm
- Merging triggers the same workflow again; with no pending changesets it publishes.
- Take the merge SHA (
gh pr view <n> --json mergeCommit --jq .mergeCommit.oid) and run the commit watch on it.
- On failure: read the log, match it against the publish failure table in
references/version-pr-and-publish.md, report, and stop. Publish failures are never retried blind; every cause on that table needs a config or settings change.
- On success:
npm view <package>@<version> version must print the merged version, and npm view <package> dist-tags must show latest pointing at it (unless pre mode). Stop remaining watches and report both outputs; they are the completion evidence.
Failure Recovery
Stop and report on a gate still failing after 5 iterations or any failed merge precondition; never override failing checks or resolve conflicts in the bot PR. Diagnosis lives with each stage: CI and Changeset Status failures in references/ci-polling.md, a missing Version Packages PR and publish failures in references/version-pr-and-publish.md.
Gotchas
- Never run
pnpm changeset version locally. It consumes .changeset/*.md, so the pushed commit has no pending changeset, "Changeset Status" fails, and no Version Packages PR opens. Recovery is reverting the bump, not rerunning CI.
- Never run
pnpm publish or npm publish directly. It bypasses changesets, skips the changelog and tag, and leaves the Version Packages PR describing an already-shipped version, which then fails with "You cannot publish over the previously published versions".
- Never hand-edit
CHANGELOG.md or the package.json version. CI generates both in the Version Packages PR; local edits make the bot PR CONFLICTING.
changesets/action@v2 renamed every input (publish to publish-script, version to version-script, title to pr-title, commit to commit-message, branch to pr-base-branch). The old names are ignored with only a warning, so a @v2 workflow still using publish: opens the Version PR and then completes green without publishing anything.
setup-node with node-version: 22 ships npm 10.9.x. Trusted publishing needs npm 11.5.1 or later, so changeset publish fails ENEEDAUTH even with id-token: write set (pnpm hands the upload to the npm CLI on PATH). Use Node 24 or add npm install -g npm@latest before publishing.
- A workflow authenticating with an
NPM_TOKEN secret that holds a classic token fails ENEEDAUTH: npm revoked every classic token on 9 December 2025. Move to trusted publishing rather than minting a new token; write-capable granular tokens expire within 90 days.
- npm does not validate a trusted publisher when you save it. A workflow filename that differs from
.github/workflows/<file>.yml by case or extension surfaces only at publish time, as ENEEDAUTH.
- New personal repos block Actions from opening PRs. The release run fails with "GitHub Actions is not permitted to create or approve pull requests" and no Version PR appears. Fix: Settings, Actions, General, "Allow GitHub Actions to create and approve pull requests" (an org-level setting can override it). Red tier: report it, do not change settings unasked.
gh pr list --json headBranch and gh pr checks --json conclusion are invalid fields and error. Use headRefName and bucket.
- The Monitor tool's default
timeout_ms is 300000 (5 minutes). A CI run that outlasts it kills the watch with a timeout that looks like silence. Pass timeout_ms: 3600000 for CI watches.
- Poll every 30 seconds or slower. Faster loops burn the GitHub API rate limit (
gh api rate_limit --jq .resources.core.remaining) and stall the flow mid-release.
git add -A commits pre-commit-hook artifacts and fixer churn into the release commit. Stage explicit paths.
major without explicit instruction signals breaking changes to every consumer. Default to patch.
Related Skills
scaffold-cli: scaffolds a new TypeScript package with changesets and OIDC publishing, then hands off to autoship for its first release.
pr-creator: opens feature PRs. Autoship merges only the bot-opened Version Packages PR.
pr-babysitter: watches feature PRs (reviews, conflicts, CI). Autoship watches release CI only; never babysit a Version Packages PR autoship is driving.