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

@claylo/scrat

v0.1.3

Published

Release management tooling focused on sanity retention

Readme

scrat

CI Crates.io docs.rs MSRV

Release management tooling focused on sanity retention.

scrat is a batteries-included release pipeline for projects that use git tags and GitHub releases. It detects your ecosystem, diffs your dependencies, collects stats, renders release notes, bumps versions, commits, tags, pushes, and creates a GitHub release—all in one command. Every step is on by default, every step is skippable, and hooks let you bolt on anything custom.

Think of it as np for any ecosystem, with built-in release notes via git-cliff.

Table of Contents

Why

Every release I've ever done by hand has the same steps: run tests, bump versions, update the changelog, commit, tag, push, create a GitHub release, attach assets, and try not to forget something. Most tools automate one or two of those steps. scrat automates all of them—and the ones it doesn't know about, you can wire up with hooks.

Design principles:

  • Batteries included, everything optional. Every built-in step is on by default. Pass --no-<step> to skip it. Set a value in config to override the built-in behavior. Or disable it entirely.
  • Thin CLI, fat core. The binary is a thin UI layer. All orchestration lives in scrat-core so other tools can embed it.
  • Hooks over built-ins for custom stuff. scrat doesn't know about your postcard generator or your quote corpus. Declare shell commands as hooks—they run at every phase boundary with variable interpolation.
  • Zero config works. Override what you want. Auto-detection handles ecosystem, version strategy, test commands, and publish commands. Config files only exist to override the defaults.

Quick Start

# Install
cargo install scrat

# See what scrat detects about your project
scrat info

# Check if you're ready to release
scrat preflight

# Preview release notes without shipping
scrat notes

# Dry run the full pipeline
scrat ship --dry-run

# Ship it
scrat ship

scrat ship is the main event. It runs every stage below, shows you the plan, asks for confirmation, and executes.

The Pipeline

scrat ship runs these stages in order. Each stage feeds structured data into a PipelineContext that flows through the whole pipeline. Hooks can read and mutate this context at every phase boundary.

1. Preflight

Checks release readiness before anything else runs.

  • Clean working directory (no uncommitted changes)
  • On the correct release branch (main or master, configurable)
  • git-cliff installed (required for release notes)

If any check fails, the pipeline stops. Run scrat preflight standalone to diagnose issues.

2. Version Resolution

Determines the next version. Three strategies, auto-detected:

| Strategy | When | How | |----------|------|-----| | Conventional Commits | git-cliff installed | Analyzes commit messages to determine major/minor/patch | | Explicit | --version 1.2.3 passed | Uses exactly what you give it | | Interactive | Fallback | Shows recent commits, offers version candidates, you pick |

scrat reads the current version from your project files (Cargo.toml, package.json, composer.json, pyproject.toml) and computes candidates from there. Go, Swift, and Ruby don't have a standard version file, so they use the interactive or explicit strategy.

3. Test

Runs your test suite. The command is auto-detected per ecosystem by probing PATH for the relevant binaries — the table shows the preferred command, with the fallback in parentheses where applicable.

| Ecosystem | Detected Via | Detected Command | |-----------|-------------|------------------| | Rust | Cargo.toml | cargo nextest run (falls back to cargo test) | | Node | package.json | pnpm test > yarn test > npm test | | Go | go.mod | go test ./... | | PHP | composer.json | composer test | | Python | pyproject.toml | uv run pytest (falls back to pytest) | | Ruby | Gemfile | bundle exec rake test (or rake test without Bundler) | | Swift | Package.swift | swift test | | Generic | (manual selection) | (none — set commands.test) |

Override with commands.test in config. Skip with --no-test.

4. Bump

Updates version numbers in project files and generates the changelog.

| Ecosystem | Files written | |-----------|---------------| | Rust | Cargo.toml (always — via cargo set-version) | | Node | package.json (direct serde_json edit; lockfile sync is your package manager's job) | | PHP | composer.json (only if a version field already exists) | | Python | pyproject.toml (only if a [project] version field already exists) | | Ruby | lib/**/version.rb (VERSION = "..." literals) and *.gemspec (literal <spec>.version = "..." assignments — constant references like MyGem::VERSION are left alone) | | Go, Swift | (none — version lives in the git tag) | | Generic | (none) |

For any ecosystem you can also declare additional [[version_files]] in config to update arbitrary JSON, TOML, YAML, frontmatter, or plain-text files that hold a version string.

After file writes, scrat runs git-cliff to regenerate CHANGELOG.md.

Skip changelog generation with --no-changelog. Run scrat bump standalone to bump without shipping.

5. Dependency Diff

Diffs lockfiles between the previous tag and HEAD to find what changed.

| Ecosystem | Lockfile | Parser | |-----------|----------|--------| | Rust | Cargo.lock | State machine over [[package]] blocks | | Python | uv.lock | Same as Cargo.lock (identical TOML [[package]] format) | | Go | go.mod | Line-oriented collect-and-merge (not go.sum — cleaner, no checksums) | | PHP | composer.lock | State machine over JSON "name"/"version" pairs | | Ruby | Gemfile.lock | Collect-and-merge on 4-space-indent gem lines | | Swift | Package.resolved | JSON state machine on "identity"/"version" | | Node | package-lock.json | JSON state machine on lockfile v2/v3 (top-level deps only — nested dedup entries are skipped) | | Generic | (none) | Skipped |

All parsers work on git diff output—not the full lockfile—so they're fast and don't need heavyweight format-specific dependencies. Results feed into release notes automatically.

Skip with --no-deps.

6. Stats Collection

Gathers release statistics from git:

  • Commit count
  • Files changed, insertions, deletions
  • Contributors and their commit counts

Uses git diff --shortstat and git shortlog. Results feed into release notes.

Skip with --no-stats.

7. Release Notes

Renders release notes using a two-pass git-cliff pattern:

  1. git-cliff --unreleased --context produces JSON with commits grouped by type
  2. scrat injects extra data (deps, stats, metadata) into the context's extra field
  3. git-cliff --from-context - --body <template> renders the final markdown

scrat ships a built-in template with: breaking changes, grouped commits with emoji, dependency changes (updated/added/removed), a stats table, and a "nerd drawer" with contributor details.

Point to your own template with release.notes_template in config or --template on scrat notes.

Skip with --no-notes. Falls back to --generate-notes (GitHub's auto-generated notes) if rendering fails.

8. Git

Commits, tags, and pushes.

  • git add . && git commit -m "chore: release {version}"
  • git tag -a v{version} -m "Release {version}"
  • git push origin {branch} && git push origin --tags

Fine-grained control:

| Flag | Effect | |------|--------| | --no-git | Skip entire phase (commit, tag, push) | | --no-tag | Commit and push, but don't create a tag | | --no-push | Commit and tag locally, don't push |

9. GitHub Release

Creates (or updates) a GitHub release using gh.

  • Auto-detects edit vs. create: if a release already exists for the tag, it edits and re-uploads assets instead of failing. This makes scrat ship safe to re-run after a partial failure.
  • Draft by default: releases are created as drafts so you can review before publishing. Publish with gh release edit <tag> --draft=false.
  • Configurable title: release.title = "{repo} {tag}" with hook-style variable interpolation.
  • Assets: declare release.assets = ["dist/app.tar.gz", "checksums.txt"] in config. Hook commands produce these files; scrat attaches them.

Skip with --no-release. Override draft behavior with --draft / --no-draft.

10. Publish

Publishes to a package registry. Auto-detected:

| Ecosystem | Detected Command | |-----------|------------------| | Rust | cargo publish | | Node | pnpm publish > yarn publish > npm publish | | Python | uv publish (falls back to twine upload dist/*) | | Ruby | gem push | | Go | (none — Go modules publish via git push) | | Swift | (none — Swift packages distribute via git URLs) | | PHP | (none — set commands.publish if needed) |

Skip with --no-publish. Override with commands.publish in config.

Commands

scrat ship

The full release pipeline. Runs all stages above, with confirmation prompt.

scrat ship                    # interactive — asks for confirmation
scrat ship --dry-run          # preview without changes
scrat ship --version 2.0.0    # explicit version
scrat ship --no-publish -y    # skip publish, skip confirmation
scrat ship --draft            # force draft mode (overrides config)

scrat notes

Renders release notes without shipping. Useful for previewing what the notes will look like.

scrat notes                          # preview notes for current version
scrat notes --from v1.0.0            # diff against specific tag
scrat notes --version 2.0.0          # render as if releasing 2.0.0
scrat notes --template my-notes.tera # use custom template
scrat notes --json                   # output raw context as JSON

scrat bump

Bumps version and generates changelog without shipping.

scrat bump                    # interactive version selection
scrat bump --version 1.2.3    # explicit version
scrat bump --dry-run          # preview without changes
scrat bump --no-changelog     # skip changelog generation

scrat preflight

Checks release readiness.

scrat preflight               # run all checks
scrat preflight --json        # machine-readable output

scrat info

Shows project information: detected ecosystem, version, tools, config paths.

scrat info                    # human-readable
scrat info --json             # machine-readable

scrat init

Generates a scrat config file for a project.

scrat init                    # interactive — asks for ecosystem, version strategy

scrat doctor

Diagnoses configuration and environment issues.

Configuration

Config files are discovered automatically. Precedence (highest first):

  1. Explicit file via --config <path>
  2. .config/scrat.toml in current directory (walks up to .git boundary)
  3. .scrat.toml / scrat.toml in current directory (walks up to .git boundary)
  4. ~/.config/scrat/config.toml (user config)
  5. Built-in defaults

Supported formats: TOML, YAML, JSON.

Zero config works. Everything below is optional—only set what you want to override.

Full Reference

# Log level: debug, info, warn, error
log_level = "info"

# Directory for JSONL log files (default: platform-specific)
# log_dir = "/var/log/scrat"

[project]
# Override detected ecosystem: rust, node, go, php, python, ruby, swift, generic
# type = "rust"
# Override release branch (default: auto-detect main/master)
# release_branch = "main"

[version]
# Override version strategy: conventional-commits, interactive, explicit
# strategy = "conventional-commits"
# Use your own cliff.toml for version computation instead of scrat's built-in
# cliff_config = "cliff.toml"

[commands]
# Override per-phase commands (default: auto-detected per ecosystem)
# test = "just test"
# publish = "cargo publish"

[release]
# Create GitHub releases (default: true)
# github_release = true

# Create as draft — review before publishing (default: true)
# draft = true

# Title format with variable interpolation (default: tag name)
# title = "{repo} {tag}"

# GitHub Discussions category (only for new releases)
# discussion_category = "releases"

# Custom git-cliff template for release notes
# notes_template = "templates/my-notes.tera"

# Files to attach to the GitHub release
# assets = ["dist/release-card.png", "dist/checksums.txt"]

[hooks]
# Shell commands at each phase boundary.
# See the Hooks section for details.
# post_bump = ["ll-graphics generate --version {version} --output dist/release-card.png"]

[ship]
# Prompt for confirmation before executing (default: true)
# Set to false for CI/scripted use. --yes/-y flag also skips.
# confirm = true

# Skip pipeline phases permanently (equivalent to --no-* CLI flags).
# CLI flags override these — passing --no-publish on a run where
# no_publish is already true in config is harmless.
# no_changelog = false
# no_publish = false
# no_push = false
# no_release = false
# no_deps = false
# no_stats = false
# no_notes = false
# no_test = false
# no_tag = false
# no_git = false
# no_fetch = false

Hooks

Hooks are shell commands that run at phase boundaries during the ship workflow. Declare them in config as lists of strings.

Hook Points

12 hook points across 6 phases:

| Hook | When | |------|------| | pre_ship / post_ship | Before/after the entire workflow | | pre_test / post_test | Before/after the test phase | | pre_bump / post_bump | Before/after version bump + changelog | | pre_tag / post_tag | Before/after git commit + tag + push | | pre_release / post_release | Before/after GitHub release creation | | pre_publish / post_publish | Before/after registry publish |

Variable Interpolation

Commands support {var} placeholders:

| Variable | Example Value | |----------|---------------| | {version} | 1.2.3 | | {prev_version} | 1.1.0 | | {tag} | v1.2.3 | | {changelog_path} | CHANGELOG.md | | {owner} | claylo | | {repo} | scrat |

Execution Model

Commands run in parallel by default. Two prefixes alter execution:

sync: — barrier. All prior commands finish, the sync command runs alone, then subsequent commands resume in parallel.

[hooks]
post_bump = [
    "generate-image --version {version}",
    "generate-checksums",
    "sync: validate-artifacts",  # waits for both above, runs alone
    "upload-to-cdn",             # resumes parallel
]

filter: — barrier + JSON piping. Like sync:, but the command also receives the full PipelineContext as JSON on stdin and must return valid JSON on stdout. The output replaces the pipeline context. This lets you mutate built-in step output without replacing the whole step.

[hooks]
post_bump = [
    "filter: jq '[.dependencies[] | select(.name != \"dev-dep\")]'",
]

Example: Full Hook Setup

[hooks]
post_bump = [
    "ll-graphics release-postcard --tag {tag} --output dist/release-card.png",
]
pre_release = [
    "sync: test -f dist/release-card.png",
]

[release]
assets = ["dist/release-card.png"]

CLI Reference

Global Options

| Flag | Description | |------|-------------| | -c, --config <FILE> | Explicit config file path | | -C, --chdir <DIR> | Run as if started in DIR | | -v, --verbose | More detail (repeatable: -vv) | | -q, --quiet | Only print errors | | --json | Machine-readable JSON output | | --color <auto\|always\|never> | Colorize output |

scrat ship Flags

Step control — every pipeline step is skippable:

| Flag | Skips | |------|-------| | --no-test | Test phase | | --no-changelog | Changelog generation (during bump) | | --no-publish | Registry publish | | --no-deps | Dependency diff | | --no-stats | Stats collection | | --no-notes | Release notes rendering | | --no-tag | Git tag (still commits and pushes) | | --no-push | Git push (still commits and tags locally) | | --no-git | Entire git phase (commit, tag, push) | | --no-release | GitHub release creation | | --no-fetch | git fetch during preflight (faster startup, may miss recent remote changes) |

Other options:

| Flag | Description | |------|-------------| | --version <VERSION> | Set version explicitly | | --draft | Force draft mode (overrides config) | | --no-draft | Force published mode (overrides config) | | --dry-run | Preview without making changes | | -y, --yes | Skip confirmation prompt |

Crate Organization

  • scrat - The CLI binary. Handles argument parsing, command dispatch, and user interaction.
  • scrat-core - The core library. Contains configuration loading, error types, and shared functionality. Library consumers get zero clap dependency by default — clap is gated behind an opt-in cli feature that adds clap::ValueEnum derives to public enums (ConfigFormat, ConfigStyle). The scrat binary enables the feature automatically; embed scrat-core without it to skip clap and its transitive crates entirely.

Installation

Homebrew (macOS and Linux)

brew install claylo/brew/scrat

Pre-built Binaries

Download from the releases page.

Binaries available for:

  • macOS (Apple Silicon and Intel)
  • Linux (x86_64 and ARM64, glibc and musl)
  • Windows (x86_64 and ARM64)

From Source

cargo install scrat

Shell Completions

Shell completions for Bash, Zsh, and Fish are included in release archives and Homebrew installs. They are generated at build time via xtask, not a runtime subcommand.

Prerequisites

scrat shells out to these tools when their features are used:

| Tool | Required For | Install | |------|-------------|---------| | git-cliff | Changelog + release notes | cargo install git-cliff | | gh | GitHub release creation | brew install gh |

Development

Workspace layout:

crates/
  scrat/       # CLI binary — thin UI layer
  scrat-core/  # Core library — all orchestration and logic
xtask/         # Build automation (man pages, completions, install)

Requirements

Build Tasks

| Command | Description | |---------|-------------| | just check | Format + clippy + deny + test + doc-test | | just test | Run tests with nextest | | just clippy | Run clippy lints | | just fmt | Format with rustfmt | | just cov | Coverage report | | just deny | Security/license audit |

Architecture

  • Thin CLI, fat core: the binary parses args, calls core, maybe prompts the user, displays results. If you're importing deps, stats, detect, git, or pipeline in the CLI crate, it belongs in core.
  • Plan/execute pattern: plan_ship() returns Ready or NeedsInteraction. The CLI only prompts on the latter. ReadyShip::execute() runs the pipeline with event callbacks for progress display.
  • Error handling: thiserror in the library, anyhow in the binary.
  • Safe Rust only: #![deny(unsafe_code)] workspace-wide.

License

Licensed under either of:

at your option.