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

dotswap

v0.1.17

Published

Manage .env file profiles globally per project

Readme

dotswap

Manage .env file profiles globally per project.

Staying up to date

dotswap version shows the version you have installed alongside the latest published on npm, and tells you whether an update is available:

dotswap version

dotswap update (alias upgrade) fetches the latest version and installs it. For a global install it runs npm install --global dotswap@<latest-version> to upgrade to the newest published release. When you're running through npx — which always resolves the newest version on demand — it points you at npx dotswap@latest <command> instead of installing anything. When dotswap is a project dependency, it leaves the global install alone and tells you to update it with your own package manager (e.g. npm install dotswap@latest):

dotswap update

Showing the active env in your shell

dotswap current prints just the active environment for the current directory — nothing else — so you can surface it in your shell prompt. It stays quiet (prints nothing, exits 0) outside a dotswap project, so it is safe to call on every prompt:

dotswap current   # e.g. prints "preview"; prints nothing outside a project

Oh My Zsh

dotswap shell install sets everything up: it writes the plugin into your Oh My Zsh custom plugins directory and adds dotswap to the plugins=(...) list in ~/.zshrc. Restart your shell (or source ~/.zshrc) afterward:

dotswap shell install     # install the plugin and enable it in ~/.zshrc
dotswap shell uninstall   # reverse both steps
dotswap shell print       # print the plugin to stdout (for manual/other setups)

The plugin adds a right-hand prompt segment (❖ <env>) that refreshes when you cd between projects and immediately after dotswap switch. It only spawns dotswap in directories that contain a .dotswap.json, so other directories cost nothing. Options (set before Oh My Zsh loads):

  • DOTSWAP_CMD — override the invocation (default: global dotswap, else npx --yes dotswap). Install globally (npm install --global dotswap) for the snappiest prompt — this avoids npx entirely, which is both faster and immune to npx's install prompt stalling your shell after a new version is published.
  • DOTSWAP_PROMPT_PREFIX — text before the env name (default: "❖ ").
  • DOTSWAP_NO_RPROMPT=1 — leave RPROMPT alone and place $(dotswap_prompt_info) in your own PROMPT/RPROMPT instead.

The dotswap shell commands are Oh My Zsh–specific; they detect it via $ZSH and ~/.oh-my-zsh and refuse (without changing anything) when it is absent.

Other shells (bash, plain zsh, fish)

dotswap current is shell-agnostic, so any shell can build the same segment on top of it. Gate on a .dotswap.json first so non-project directories stay fast:

# bash — in ~/.bashrc
_dotswap_ps1() {
  [[ -r .dotswap.json ]] || return
  local env
  env="$(dotswap current)"
  # Strip to a safe charset before interpolating: PS1 re-evaluates $(...) /
  # backticks embedded in the result on every prompt render.
  printf ' (%s)' "${env//[^a-zA-Z0-9._-]/}"
}
PROMPT_COMMAND='__dotswap=$(_dotswap_ps1)'
PS1='\u@\h \w${__dotswap}\$ '
# plain zsh (no Oh My Zsh) — in ~/.zshrc
setopt PROMPT_SUBST
_dotswap_ps1() {
  [[ -r .dotswap.json ]] || return
  local env
  env="$(dotswap current)"
  # Strip to a safe charset before interpolating: PROMPT_SUBST re-evaluates
  # $(...) / backticks embedded in the result on every prompt render.
  printf ' (%s)' "${env//[^a-zA-Z0-9._-]/}"
}
PROMPT='%n@%m %~$(_dotswap_ps1)%# '
# fish — in ~/.config/fish/functions/fish_prompt.fish (or your prompt function)
function dotswap_prompt
    test -r .dotswap.json; or return
    set -l env (dotswap current)
    # Strip to a safe charset before printing — the env name is only checked
    # for path separators, not terminal escape sequences.
    printf ' (%s)' (string replace -ra '[^a-zA-Z0-9._-]' '' -- $env)
end
# then call `dotswap_prompt` from your fish_prompt

Browsing the store

The store keeps one directory per project under ~/.dotswap. Most commands (info, tree, list) act on the project of the current directory. To see every project saved on the machine, use projects (alias who):

# List all stored projects with env, file, and workspace counts
dotswap projects

# Inspect one: its environments, shared files, and the workspaces using it
dotswap projects swite-app

# Reveal absolute store paths, or emit machine-readable JSON
dotswap projects --paths
dotswap projects swite-app --json

Inspecting a project summarizes its workspaces by active env and lists the first few directories; pass --json for the complete, unabridged list.

To permanently delete a project's stored data — every environment, the shared segment, and the workspaces map — use remove (alias purge). It prints the blast radius and asks for confirmation (defaulting to No) before deleting:

dotswap remove old-project        # prompts before purging
dotswap remove old-project --yes  # skip the prompt (for scripts)

Only the store under ~/.dotswap/<project> is removed; the .dotswap.json files checked into your repos are left untouched.

Sharing envs with a teammate

dotswap export bundles a project's stored environments into a single file you can hand to someone else; dotswap import unpacks it into their store.

Exports are encrypted by default: export generates a random secret, prints it once, and locks the bundle with it. Send the file and the secret through separate channels — the bundle is useless without the secret.

# Bundle every env (production excluded by default) into ./<project>.dotswap
# and print the generated secret to share separately
dotswap export

# Use your own secret instead of a generated one
dotswap export --secret "our-agreed-passphrase"

# Only a couple of envs, to a chosen path
dotswap export -o team.dotswap --env local preview

# Skip the shared segment (envs only)
dotswap export --no-shared

# Opt out of encryption (not recommended — asks for confirmation)
dotswap export --plaintext

# On the other machine (prompts for the secret, or pass it with --secret)
dotswap import team.dotswap
dotswap import team.dotswap --secret "the-secret"
dotswap switch preview
dotswap restore

The bundle contains the stored files for the selected envs plus the .shared segment (drop it with --no-shared) and a copy of .dotswap.json. It never includes .workspaces.json (your machine-specific active-env map). Import validates every path, re-applies 0600/0700 permissions, and skips files that are already identical.

Bundles written to a path inside a git repo are added to .git/info/exclude automatically, so they never end up staged by git add . (--stdout output is unaffected, since nothing is written to disk).