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

@williamthorsen/overlay

v0.2.0

Published

Idempotently overlay a canonical set of scaffolding files onto a target directory, backed by chezmoi

Readme

Release notes — v0.2.0 (2026-06-04)

🎉 Features

  • Add chezmoi-backed overlay CLI with verify/create/force modes (#96)

    Adds overlay, a command-line tool (installable as @williamthorsen/overlay) that brings a target directory into line with a directory of canonical scaffolding files — adding what's missing, removing what's been marked for deletion, and running any normalization steps. Three modes set how much it will change: --verify (the default) reports what's out of sync without touching anything; --create brings everything into line except files you've changed locally, which it will not overwrite; and --force brings everything into line and overwrites your local changes too. The same behavior is also available as a typed function for use from other TypeScript code.

@williamthorsen/overlay

Overlay a canonical set of scaffolding files onto a target directory you do not control, and idempotently converge it: create what is missing, delete what is declared for removal, run normalization scripts, and refuse to silently clobber content it does not own.

Overlay is built on chezmoi, whose source-state model fits exactly — dot_-encoded filenames, executable_/run_ attributes, and --source/--destination to drive any source tree into any target. Overlay adds three modes (--verify, --create, --force) computed from a parsed chezmoi status, plus one guarantee chezmoi lacks natively: --create never overwrites a differing managed file.

Installation

Overlay shells out to the chezmoi binary, which must be on your PATH:

brew install chezmoi

Install overlay for personal use via npm or a local link:

pnpm add -g @williamthorsen/overlay
# or, from a checkout:
pnpm link --global

chezmoi 2.46.0 or later is required; overlay preflights the version and exits with an actionable error otherwise.

Usage

overlay <source-dir> [target-dir] [--verify|--create|--force] [--json] [--help]
  • <source-dir> — a chezmoi source directory describing the files the target should have.
  • [target-dir] — the directory to converge (defaults to the current working directory).
  • Modes are mutually exclusive; the default is --verify.
  • --json prints the structured result instead of the text report.

Exit codes

| Code | Meaning | | ---- | ------------------------------------------------------------------------------------------------------- | | 0 | Converged / clean. | | 1 | Drift (--verify) or unresolved conflicts (--create). | | 2 | Hard error: chezmoi missing or below the minimum version, a run_ script failed, or invalid arguments. |

Exit 2 is deliberately distinct from drift so callers can tell a real failure apart from "the target has drifted".

Modes

Each mode is computed from the second (apply-side) column of chezmoi status, whose codes overlay reads as:

| Status code | Meaning | --verify | --create | --force | | ----------- | --------------------------------------------- | ------------------------------------------------------- | -------------------------------- | ----------------- | | A | addition (missing in target) | report drift | create | create | | D | native removal (.chezmoiremove / remove_) | report drift | delete | delete | | M | differing managed file | report drift | conflict (never overwritten) | overwrite | | R | run_ script will run | surfaced informationally; never affects the verdict | run (live output) | run (live output) |

--create and --force differ only in the differing-file (M) column: --create refuses to overwrite, --force overwrites.

--verify

Read-only. Drift is any A/M/D row; overlay exits 1 if any exists, 0 otherwise. Pending R scripts are reported as "N script(s) would run" but never make verify fail.

--verify confirms file convergence, not script execution. It cannot know what a run_ script would do to the target, so it reports the script as pending and moves on.

Verify-enforceability guideline: if you want --verify to enforce a deletion, express it as a chezmoi-native removal (.chezmoiremove or a remove_ entry), which surfaces as a D row that verify counts as drift. Reserve run_ scripts for imperative normalization that has no static target-state for verify to check.

Why overlay does not use chezmoi verify directly

Overlay runs every chezmoi invocation with a throwaway --persistent-state (a temp file discarded after each run). Because chezmoi keeps no memory across runs, it always believes run_once_ / run_onchange_ scripts are still pending — so chezmoi verify exits non-zero on a fully file-converged target. Overlay therefore parses chezmoi status itself and ignores the R rows for the verdict, which is the only way to get a clean verify on a converged target.

--create

Creates missing entries (A), performs native removals (D), runs run_ scripts (R), and reports differing files (M) as conflicts that are never written. The fix-it hint suggests re-running with --force to overwrite.

Mechanically, overlay applies only the A/D entries by absolute target path (chezmoi apply --include=files,dirs,remove -- <abs paths>), skipping that apply entirely when no A/D entries exist — a bare apply would converge every file and clobber the M entries the mode exists to protect. Scripts then run in a separate --include=scripts pass.

--force

A full chezmoi apply: overwrite differing files, perform removals, run scripts. conflicts is always 0.

How script results are surfaced

Under --create / --force, run_ script stdout and stderr stream live to overlay's stderr — keeping overlay's stdout clean for the text report or --json. OverlayResult.scripts records { ran, ok }. A script that exits non-zero aborts chezmoi's apply; overlay maps that to exit 2 and surfaces chezmoi's diagnostic. chezmoi provides no per-script structured output, so overlay does not invent any.

Idempotency contract

Because each invocation uses a throwaway persistent-state, chezmoi keeps no cross-run memory: run_once_ / run_onchange_ scripts effectively run on every --create / --force. Source run_ scripts must therefore be idempotent. This is intentional — it keeps runs isolated, pollutes no host state, and behaves predictably across targets.

Source-state filename grammar

Overlay uses chezmoi-native source-state filenames: dot_ (a leading dot), executable_ (executable bit), and run_ (scripts, including run_once_ / run_onchange_ / run_before_ / run_after_). Symlink overlays (symlink_) are deferred to a future --link.

Programmatic use

The CLI is a thin shell over an importable core:

import { overlay } from '@williamthorsen/overlay';

const result = await overlay({ source: './scaffold', target: './repo', mode: 'create' });
// result: { mode, entries, scripts, counts, exitCode }

overlay(options) returns a structured OverlayResult — never printed text — so other TypeScript code can compose with it.