npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lacymorrow/shipx

v0.1.14

Published

Interactive release CLI — bump, tag, publish, and ship your packages with a beautiful terminal UI

Readme


[!NOTE] shipx is a thin, opinionated wrapper around npm publish, git tag, gh release, cargo set-version, and a Homebrew formula update. It's a beautiful pipeline for the release ritual you already know — not a magic black box.

Why shipx

Releasing a package is the same nine commands every time, in the same order, and you don't want to forget any of them or run them out of order.

  • Beautiful interactive UI built on @clack/prompts — spinners, prompts, and confirms that you actually enjoy looking at.
  • One config, every channel. npm, GitHub releases, Cargo workspaces (Tauri-friendly), and Homebrew tap formula — all from a single shipx.config.ts.
  • Stop on red. Preflight refuses to run on a dirty tree or the wrong branch. Each step is a single shell-out — no hidden state, no surprises.
  • Beta path. shipx --beta increments -beta.N and publishes with the beta dist-tag. Homebrew is skipped automatically.
  • Recoverable. If npm publish fails (auth, OTP, network), shipx drops into an interactive retry loop instead of aborting the whole pipeline.
  • Zero install. npx @lacymorrow/shipx runs against the project in your current directory.

Install

# Global
npm install -g @lacymorrow/shipx
shipx

# One-off
npx @lacymorrow/shipx

# In your repo
npm install --save-dev @lacymorrow/shipx

Requires Node ≥20. gh CLI is required for githubRelease. cargo-edit is required if you bump Cargo workspaces.

Usage

# Interactive — prompts for patch / minor / major
shipx

# Skip the prompt
shipx patch
shipx minor
shipx major

# Explicit version
shipx 2.0.0

# Beta release (publishes with --tag beta, skips Homebrew)
shipx --beta

# Multi-project deploy — scan parent dir, batch-publish with one OTP
cd ~/repo && shipx --multi

[!TIP] Set SHIPX_ROOT=/path/to/project to run shipx against a project other than the current directory — useful in monorepo automation.

Pipeline

flowchart LR
    A[preflight] --> B[bump version]
    B --> C[changelog]
    C --> D[commit + tag]
    D --> E[push]
    E --> F[GitHub release]
    F --> G[npm publish]
    G --> H[Homebrew]

    style A fill:#c026d3,stroke:#c026d3,color:#fff
    style H fill:#c026d3,stroke:#c026d3,color:#fff

Each step is independently toggleable. Set steps.<name>: false to skip it.

Configuration

shipx looks for config in this order, first hit wins:

  1. shipx.config.ts / shipx.config.js
  2. .shipxrc.json / .shipxrc
  3. "shipx" key in package.json
  4. Defaults (auto-detects package.json, src-tauri/Cargo.toml, and sibling ../homebrew-tap)

Example shipx.config.ts

import type { ShipConfig } from "@lacymorrow/shipx";

export default {
  packageJsonPaths: ["package.json", "packages/core/package.json"],
  bumpFiles: [
    {
      path: "bin/cli",
      pattern: /^VERSION="[^"]*"/m,
      replacement: (v) => `VERSION="${v}"`,
    },
  ],
  cargoWorkspaces: ["src-tauri"],
  steps: {
    homebrew: true,
    githubRelease: true,
  },
  git: {
    releaseBranch: "main",
    tagPrefix: "v",
    extraTags: ["cua-{tag}"],
    commitMessage: "release: {tag}",
  },
  npm: {
    access: "public",
  },
  homebrew: {
    tapPath: "../homebrew-tap",
    formulaFile: "Formula/mytool.rb",
    repoSlug: "user/repo",
  },
} satisfies ShipConfig;

All options

| Option | Type | Default | What it does | |---|---|---|---| | packageJsonPaths | string[] | Auto (["package.json"]) | Paths to package.json files to bump | | bumpFiles | BumpFileConfig[] | [] | Additional files with regex-based version bumping | | cargoWorkspaces | string[] | Auto (["src-tauri"] if exists) | Cargo workspace dirs to bump via cargo set-version --workspace. Use [] to opt out. | | steps.preflight | boolean | true | Require clean tree + correct branch | | steps.bumpVersion | boolean | true | Update version in package.json + bump files | | steps.changelog | boolean | true | Generate changelog from git log since last tag | | steps.commit | boolean | true | Single commit for all bumped files | | steps.tag | boolean | true | Create git tag (plus any extraTags) | | steps.push | boolean | true | Push commit + tag(s) to origin | | steps.githubRelease | boolean | true | Create GitHub release via gh CLI | | steps.npm | boolean | true | Publish to npm (with interactive retry) | | steps.homebrew | boolean | true | Update Homebrew formula SHA + URL | | git.releaseBranch | string | "main" | Branch required for stable releases | | git.tagPrefix | string | "v" | Prefix prepended to the version | | git.extraTags | string[] | [] | Additional tags. Templates support {tag} (full, e.g. v0.5.3) and {version} (bare) | | git.commitMessage | string | "release: {tag}" | Commit message template | | git.commitFlags | string | "--no-verify" | Flags passed to git commit | | git.pushFlags | string | "--no-verify" | Flags passed to git push | | npm.cwd | string | Project root | Working directory for npm publish | | npm.access | "public" \| "restricted" | "public" | npm publish access | | homebrew.tapPath | string | Auto (sibling ../homebrew-tap) | Path to your tap repo | | homebrew.formulaFile | string | Auto-derived | Formula file, relative to tapPath | | homebrew.repoSlug | string | Auto-derived from origin | owner/repo for the tarball URL | | homebrew.commitMessage | string | "{formula}: update to {tag}" | Tap commit message template |

Recipes

Tauri / Cargo workspace

shipx auto-detects src-tauri/Cargo.toml and adds it to cargoWorkspaces. Requires cargo install cargo-edit.

// shipx.config.ts
export default {
  cargoWorkspaces: ["src-tauri"], // explicit; or omit for auto-detection
  steps: { npm: false }, // Tauri apps usually don't publish to npm
} satisfies ShipConfig;

Monorepo with coupled versioning

All packages bump to the same version:

export default {
  packageJsonPaths: [
    "package.json",
    "packages/core/package.json",
    "packages/cli/package.json",
  ],
} satisfies ShipConfig;

For independently-versioned monorepos, use changesets instead — shipx isn't built for that.

Homebrew tap

Drop your tap repo next to your project as a sibling (../homebrew-tap) and shipx finds it. Or configure it explicitly:

export default {
  homebrew: {
    tapPath: "../homebrew-tap",
    formulaFile: "Formula/mytool.rb",
    repoSlug: "lacymorrow/mytool",
  },
} satisfies ShipConfig;

shipx downloads the tarball, computes SHA256, updates url/sha256 in the formula, commits, and pushes from the tap.

Multi-project deploy

Got a bunch of repos in ~/repo/? Deploy them all at once:

cd ~/repo
shipx --multi

shipx scans for subdirectories with a package.json, detects which have unreleased commits, and lets you pick which to release. The killer feature: npm publishes are batched — enter your OTP once and it's reused across all packages, so your 2FA code doesn't expire mid-deploy.

The flow:

  1. Select projects — sorted by change count, with dirty/private indicators
  2. Pick versions — individually, or apply the same bump type to all
  3. Prepare — each project gets its own bump → commit → tag → push → GitHub release
  4. Batch publish — all npm publishes happen back-to-back with a shared OTP
  5. Homebrew — formulas updated for non-beta releases

Combine with --beta for beta batch releases: shipx --multi --beta.

Beta release

shipx --beta
  • If current version is 1.0.0, becomes 1.0.0-beta.0
  • If current version is 1.0.0-beta.0, becomes 1.0.0-beta.1
  • Publishes to npm with --tag beta (your latest dist-tag is untouched)
  • Skips the branch check in preflight (release from any branch)
  • Skips Homebrew automatically

Comparison

| | shipx | np | release-it | changesets | |---|:-:|:-:|:-:|:-:| | Interactive UI | ✅ (@clack) | ✅ (Listr) | partial | ❌ | | npm publish | ✅ | ✅ | ✅ | ✅ | | GitHub release | ✅ | ✅ | ✅ | via Action | | Cargo workspaces | ✅ | ❌ | via plugin | ❌ | | Homebrew formula | ✅ | ❌ | via plugin | ❌ | | Beta / pre-release | ✅ | ✅ | ✅ | ✅ | | Multi-project batch deploy | ✅ | ❌ | ❌ | ❌ | | Multi-package monorepo (coupled) | ✅ | ❌ | ✅ | ✅ | | Multi-package monorepo (independent) | ❌ | ❌ | ✅ | ✅ | | Changelog from PR labels | ❌ | ❌ | via plugin | ✅ | | Zero plugins required | ✅ | ✅ | ❌ | ✅ |

TL;DR — Use shipx for single-package or coupled-version projects that ship to multiple registries (especially Cargo + Homebrew). Use changesets for independently-versioned monorepos. Use np if you want a smaller, npm-only tool.

FAQ

np is excellent and the spiritual predecessor of shipx. The differences:

  • shipx uses @clack/prompts instead of Listr — the UI feels more modern.
  • shipx bumps Cargo workspaces and updates Homebrew formulas out of the box. np is npm-only.
  • shipx is intentionally tiny (~600 lines of TS, two runtime deps). np is more battle-tested with more options.

shipx drops into an interactive retry loop with four options:

  1. Enter OTP — for 2FA accounts (validates that you typed 6 digits)
  2. Log in to npm — runs npm login, then retries
  3. Retry — just try again (good for transient errors)
  4. Skip — abandon npm publish and continue to remaining steps

Everything before npm publish (the bump, commit, tag, push, GitHub release) is already done — you can always re-publish manually.

Yes. Every step in the pipeline has a steps.<name> boolean flag. Set it to false to skip:

export default {
  steps: { homebrew: false, githubRelease: false },
} satisfies ShipConfig;

shipx delegates to your local git config. Set commit.gpgsign=true / tag.gpgsign=true and your tags will be signed. The default commitFlags / pushFlags of --no-verify is overrideable via config if you have hooks you actually want to run.

shipx generates a changelog from git log <last-tag>..HEAD --pretty=format:"- %s (%h)" and uses it as the GitHub release body. It's printed to the terminal during the release. If you need a CHANGELOG.md file, write your own bumpFile entry — or use git-cliff before invoking shipx.

Related

Other projects by the author:

  • album-art — Fetch an album or artist image URL.
  • crossover — A crosshair overlay for any screen.
  • cinematic — Gorgeous desktop movie collections.

Acknowledgments

shipx stands on the shoulders of:

Contributing

Bug reports and pull requests welcome. See CONTRIBUTING.md and the security policy. For a high-level architecture overview, see CLAUDE.md.

License

MIT © Lacy Morrow