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

@ianbrode/crew

v0.3.2

Published

Declarative runner for multi-repo local dev environments — compose without containers

Readme

crew

npm license node

Run your whole polyrepo dev environment with one command. Compose without containers.

crew is a Node.js CLI that reads a crew.yaml at your project root, clones the listed git repositories into a local apps/ folder, installs their dependencies, and runs them all in parallel with merged, prefixed logs. One file, one command, all your services up.

If you've ever written a bash script to git clone three repos, cd into each, npm install, then open three terminal tabs to npm run dev — this replaces it.

When you want this

You're actively developing across several packages at once — say a backend, a frontend, and a worker — each in its own repo, each with its own hot-reload. You edit code in one, see it reflected in another through an API call, then tweak the third. This is the daily loop.

Doing this through containers is painful: file-watch across volume mounts is unreliable or slow, rebuilds invalidate caches, mounted node_modules fight with host node_modules, and attaching a debugger to a running container is a ritual. For the inner dev loop — where you're changing code every few seconds — you want processes running natively on your machine, reading from real checkouts you can edit, branch, and commit in like any other repo.

crew is for exactly that case: multi-repo, natively-running, all dev servers alive at once, one command to bring them up, one Ctrl+C to bring them down.

Why not docker-compose?

Because you don't always want to containerize local dev. Sometimes you want:

  • Native speed — no volume mounts, no file-watch weirdness, no Rosetta translation on Apple Silicon.
  • Real debuggers — attach VS Code or your IDE directly to the Node/Python/whatever process, no remote-debug gymnastics.
  • Your actual repos — work happens in apps/my-service/ as a normal checkout you can branch, commit, and push from.
  • Heterogeneous stacks — one repo is Node, another is Poetry, another is a Rust binary. Each gets its own install and run command; crew doesn't care.

docker-compose is great for production-shaped local environments. crew is for the daily dev loop.

Install

npm install -D @ianbrode/crew

Package is published under the scope @ianbrode on npm. The CLI binary is plain crew — invoke as npx crew <command> or add it to an npm script.

Requires Node 18+ and git in your PATH.

Quick start

npx crew init     # creates crew.yaml and adds apps/ to .gitignore
# edit crew.yaml to list your repos
npx crew up       # clone → install → start everything

Hit Ctrl+C to stop all apps at once.

Interactive controls

When crew up / crew start runs in a terminal, the merged log stream gets a pinned legend bar at the bottom and a few keys to focus on one project:

| Key | Action | |---|---| | 1–9 | Solo that project — show only its output. Press the same digit again to go back to all. | | 0 | Show all projects again. | | Ctrl+C | Stop everything. |

The bottom bar always shows each project's number, name (in its log color), and a ✖ if it has exited. Soloing dims the others. The logs scroll above the bar.

This is TTY-only: when output is piped or redirected (CI, crew up | tee log.txt), crew emits the plain merged stream with no escape sequences and no legend bar — exactly as before.

Stopping containers

crew stops apps by sending SIGTERM then SIGKILL to the spawned process tree. That cleanly tears down native process trees (e.g. ./mvnw spring-boot:run → JVM child). A container is different: docker run / podman run is just an attached client — the container is owned by the daemon and is not in that process tree, so the grace-period SIGKILL can't reach it and would orphan it.

For those apps, add a stop: command. crew runs it on shutdown (with the app's cwd/env, output attributed to the app's prefix) alongside the SIGTERM:

apps:
  db:
    run: docker run --rm --name crew-db --init postgres:16
    stop: docker stop crew-db

No repo/install needed — a container isn't a checkout. Use --rm (auto-remove), --name (so stop can reference it), --init (forwards signals, reaps zombies), and keep it foreground (no -d) so crew owns the lifecycle.

crew.yaml

# crew.yaml
appsDir: apps         # optional, default "apps"

apps:
  api:
    repo: [email protected]:acme/api.git
    install: pnpm install
    run: pnpm dev

  web:
    repo: https://github.com/acme/web.git
    install: npm ci
    run: npm run dev
    env:
      PORT: "3001"

  worker:
    repo: [email protected]:acme/worker.git
    install: poetry install
    run: poetry run python -m worker
    cwd: ./src
    clone:
      args: ["--recurse-submodules", "--depth=1"]

  db:
    # no repo needed for a container — just run (and stop to tear it down)
    run: docker run --rm --name crew-db --init postgres:16
    stop: docker stop crew-db   # run on shutdown — see below

Fields

| Field | Required | Meaning | |---|---|---| | repo | no | Git URL (ssh or https). Omit for an app that isn't a checkout (a container or a local command). | | install | no | Shell command to install dependencies | | run | yes | Shell command to start the app | | stop | no | Shell command run on shutdown for apps whose process tree crew can't reach by signal (containers). See Stopping containers. | | env | no | Extra environment variables for install and run | | cwd | no | Relative path to cd into before running commands — resolved inside the cloned repo for a repo app, or relative to the project root for a repo-less app | | clone.args | no | Extra arguments passed verbatim to git clone (e.g. --recurse-submodules, --depth=1, --branch develop). Applied only on the initial clone. Ignored when there's no repo. |

repo and install are optional. An app with no repo is not cloned and runs from the project root (the directory containing crew.yaml); use cwd to run it elsewhere. Such apps run install (if set) on every sync — there's no install caching without a clone to store the marker in. This is how you run a container or a one-off local command. See Stopping containers.

App names from crew.yaml are used as folder names under appsDir/ (so apps/api, apps/web, …) and as log prefixes. They must match ^[a-zA-Z0-9_-]+$.

Commands

| Command | What it does | |---|---| | crew init | Create crew.yaml skeleton and ensure apps/ is in .gitignore | | crew sync | Clone missing repos, safely pull current branch where possible, re-install when the install command changes | | crew start | Run every run command in parallel with [name] log prefixes; Ctrl+C kills them all | | crew up | sync then start — the primary workflow. If sync fails for any app, prints a loud red banner naming each failure and its reason, and refuses to start any dev servers until you've fixed it | | crew status | Read-only: what's cloned, current branch, dirty state, ahead/behind, install-marker freshness | | crew reset | DESTRUCTIVE. Per app: git fetch → git reset --hard @{upstream} → git clean -fd. Drops local commits not on upstream, drops uncommitted changes, deletes untracked non-ignored files. Files matched by .gitignore (e.g. .env, node_modules, dist) are preserved. Prints a banner listing exactly what will happen and waits for yes on stdin; pass --yes to skip the prompt in scripts |

Flags

  • --only <a,b> — operate on a subset of apps (sync, start, up, reset)
  • --force — rerun install even if the marker says it's up to date (sync, up)
  • --yes — skip the confirmation prompt for crew reset
  • --config <path> — explicit path to a crew.yaml instead of auto-discovering by walking up from the cwd

How updates work

crew sync is deliberately careful about your local work. On every repo already cloned:

  1. Runs git fetch. Always.
  2. If the current branch has an upstream and the working tree is clean and a fast-forward is possible → git merge --ff-only.
  3. Otherwise → prints a skip notice and leaves the working tree untouched.

crew will never run git reset --hard, git stash, git checkout --, or anything else that could lose uncommitted work. If sync says "skipped: working tree dirty" — your changes are still there. If you're on a feature branch, your branch stays put; only the remote refs get refreshed.

Auth

crew does not manage credentials. For private repos, authenticate git the normal way (SSH key in agent, credential helper, gh auth login). Interactive prompts from git — passphrase, SSH host key confirmation, credential manager — flow straight through to your terminal.

Platform notes

  • macOS, Linux, Windows. Tested on Node 18 and 20 across all three in CI.
  • Ctrl+C tears down the whole process tree via tree-kill — SIGTERM + taskkill /T /F on Windows. Apps that spawn grandchildren (webpack, tsc --watch, python -m …) still die cleanly.
  • Shell: commands in install / run go through sh -c on Unix, cmd.exe /c on Windows. Stick to syntax both understand, or put a wrapper script inside the repo (run: ./scripts/dev.sh).

How it's different

| | docker-compose | foreman / overmind | turborepo / nx | crew | |---|---|---|---|---| | Multiple repos | ✗ (one tree) | ✗ | ✗ | ✓ | | Containers | ✓ | ✗ | ✗ | ✗ | | Heterogeneous langs | ✓ | ✓ | partial | ✓ | | Cross-platform | ✓ | macOS/Linux | ✓ | ✓ | | Native speed | ✗ | ✓ | ✓ | ✓ | | Safe git pull | ✗ | ✗ | ✗ | ✓ |

If your services already share a monorepo, use turbo or nx. If you need container parity with prod locally, use docker-compose. If your services live in separate repos and you want a one-command local stack without containers, use crew.

FAQ

Can I use this in CI? Not the intended use. crew is a dev-loop tool: interactive signals, pretty terminal output, long-running processes. CI wants reproducible single-shot scripts.

What if two apps need to start in order? Right now, no. All apps start in parallel. Apps that need a backend to be up should either retry, or you start them in two crew up --only <names> calls. Dependency ordering may come later if there's demand — open an issue.

Can I add a repo that's already cloned outside apps/? Not directly. crew clones into apps/<name>/. If you already have the repo somewhere, either symlink apps/my-app → your existing checkout (at your own risk with sync), or just cp -r it in.

What about secrets / .env files? Put them in the repos themselves. crew.yaml's env field is for inline overrides (like PORT=3001); for anything sensitive, rely on each repo's own dotenv loading.

Why is Ctrl+C so aggressive? Because dev servers that ignore SIGTERM are common (looking at you, webpack). crew sends SIGTERM, waits 5 seconds, then sends SIGKILL. Adjust by running your app in a wrapper if you need graceful shutdown.

Contributing

Issues and PRs welcome at https://github.com/yanbrod/crew.

git clone https://github.com/yanbrod/crew
cd crew
npm install
npm test            # vitest, 62 tests
npm run typecheck   # strict TypeScript
npm run build       # bundles to dist/cli.js

The source repo is yanbrod/crew (my GitHub handle); the npm package is @ianbrode/crew (my npm handle). Same project, two different account names across the two services.

The docs/superpowers/specs/ and docs/superpowers/plans/ folders contain the original design spec and implementation plan (historical — they reference the old working name apps-cli).

Links

License

MIT.