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

devtooie

v0.7.1

Published

Dependency-aware CLI for running a monorepo's local dev processes.

Readme

devtooie

A dependency-aware terminal UI (TUI) for running a monorepo's packages during local development.

dev + TUI → devtooie.

You describe your packages once, in a small typed config file. devtooie resolves build-time, dev-time, and runtime dependencies between them, builds whatever needs building (in the right order), and then runs the packages you picked.

devtooie's terminal UI driving the example monorepo

Features

  • Dependency-aware builds. Declare build/dev/runtime deps once; devtooie builds what needs building, in the right order, before it runs anything.
  • Language-agnostic packages. A package is driven through a handful of named scripts, so it can be written in anything: a Node package (via its package.json) or a Go, Rust, … package (via a Makefile with the equivalent targets). See Package supporting scripts.
  • Streamed, filterable logs. Every package's output is streamed live into one combined view; filter it down to a single package or a search term on the fly (the f hotkey; matching is case- and accent-insensitive).
  • Structured logs, by default. devtooie auto-formats every package's JSON logs (Go slog, pino, winston, …) as a colored [LEVEL] message for local dev — no NODE_ENV branching in your app, and nothing to configure. See Logging.
  • Two run modes. An interactive terminal UI to pick and watch packages, or a --plain log-streaming mode for coding agents.
  • One-off commands. devtooie cmd runs a single command (or a package script/target) in a package's directory with that package's resolved environment — for migrations, seeds, scrapers, or an agent driving your project. See Running one-off commands.
  • Per-package hierarchical .env loading. Each package's .env files (workspace- and package-scoped) are resolved and injected into its process automatically — and live-reloaded, restarting the affected package when a file changes.
  • Readiness ordering. healthcheck + waitFor hold a package until the services it needs are actually up.
  • Lifecycle-aware. Each package declares whether its dev process watches or just builds, so you (or an agent) know exactly what to do after a code edit.
  • Control API + agent skill. A localhost HTTP API drives a running session headlessly and lets a second invocation hand off cleanly; an installable skill teaches a coding agent to use it.

Example

A complete, runnable example monorepo lives in example/: four packages — a shared TypeScript library (isomorphic), a Node API (backend), a Go worker driven through a Makefile (worker), and a web frontend — wired up with dependency-aware builds, .env loading, healthchecks, and waitFor readiness ordering.

Requirements

  • Node ≥22.18. devtooie.config.ts is imported directly, so it needs Node's native TypeScript type-stripping — unflagged in 22.18 (and, on the 23.x line, 23.6).
  • Unix only (macOS/Linux). Windows is not supported.
  • pnpm. Node packages are run with pnpm run <script>, and packages that depend on each other are resolved through pnpm workspace links (workspace:*). (Makefile packages are run with make instead.)
  • A package.json (or Makefile) per package with the scripts devtooie drives (dev, build, ...) — see Package supporting scripts below.

Install

pnpm add -D devtooie

Getting started: devtooie init

pnpm devtooie init

This is an interactive, idempotent setup flow. It will:

  1. Ask whether to install the agent skill (recommended: yes).
  2. Scaffold devtooie.config.ts at your repo root (an existing config file is left untouched).
  3. Reconcile a root tsconfig.json so the config type-checks with Node globals in scope (idempotent — your other settings are left untouched).
  4. If you opted in to the skill, install it.

Pass -y/--yes to accept the defaults non-interactively.

After that, fill in the scaffolded config's packages array with your real packages (see below) and run pnpm devtooie.

devtooie.config.ts

The one file you author and commit — the single source of truth the CLI reads on every run.

import { defineConfig } from 'devtooie';

export default defineConfig({
  // keyed by package name — the key is the name, so there's no `name` field
  packages: {
    'core-api': {
      port: 3001, // is provided as PORT environment variable to the process
      // `healthcheck` and `urls` take a string or a callback over this package's
      // `{ envs, tokens, port }` — devtooie does no string interpolation of its own.
      healthcheck: ({ port }) => `http://localhost:${port}/health`,
    },
    worker: {
      // a dev process that doesn't watch files: it builds once, then runs. devtooie
      // doesn't watch your source, so after you edit its code you (or an agent, via the
      // control API) restart it — the command's flags say which. See docs/package-lifecycle.md.
      command: ['start', { watches: false, builds: true }],
    },
    web: {
      port: 3000,
      waitFor: ['core-api'], // hold until core-api's healthcheck passes — typo-checked
      deps: { runtime: ['core-api'] }, // selecting web also runs core-api
    },
  },
});

See Configuration options for every defineConfig and package field.

Running the Terminal UI

pnpm devtooie

Package supporting scripts

devtooie drives each package through named scripts — a Node package declares them in its package.json scripts; a package in any other language (Go, Rust, …) declares the equivalent make targets in a Makefile. devtooie invokes them as pnpm run <name> or make <name>.

  • dev — the long-running process devtooie starts and streams. An application usually needs only this; devtooie builds its dependencies for it.
  • build — a shared library that other packages build against adds this too, so devtooie can build it in the build phase before its dependents start.

A shared library (Node) — dev + build:

// packages/shared/package.json
{
  "name": "shared",
  "scripts": {
    "dev": "tsc --watch", // re-emits dist on change
    "build": "tsc",
  },
}

An application needs only a dev process — a Node backend:

// packages/backend/package.json
{
  "name": "backend",
  "scripts": {
    "dev": "node --watch --watch-path=./src src/index.ts",
  },
}

devtooie runs the dev script exactly as written, so what the process watches is up to the script. See docs/package-lifecycle.md for why --watch-path is worth adding when you use Node's own watcher.

…or a Go program, via a Makefile:

# packages/worker/Makefile
.PHONY: dev
dev:
	@go run .

An app can add build + clean too, for the occasional case where you need to rebuild it from scratch to clear stale build output — those enable the rebuild command (the b hotkey / POST /command/rebuild); see Package lifecycle.

Running one-off commands (devtooie cmd)

Sometimes you don't want the whole session — you just need to run one command with a package's exact environment: a migration, a seed script, a scraper, a REPL. devtooie cmd does that. It runs a command in a package's directory with that package's resolved env vars injected — the same environment the TUI would give it:

cd packages/api
devtooie cmd -- pnpm run migrate     # a literal command, in api's dir with api's env
devtooie cmd -c seed -- --rows=100   # run api's `seed` script/target, forwarding args
cd ../..
devtooie cmd -p api -- pnpm start    # or target a package by name, from anywhere

The package is inferred from your current directory (or named explicitly with -p). Output streams to your terminal and is also written to a logfile. It's especially handy for a coding agent driving your project. Full reference: devtooie cmd.

Configuration options

The full defineConfig and per-package field reference — including dependencies, TypeScript project references, and typed package names — lives in docs/configuration.md.

Logging

devtooie auto-formats structured (JSON) logs — from Go slog, pino, winston, … — into a colored [LEVEL] message for local dev, with no NODE_ENV branching and nothing to configure. You can add on-screen timestamps, and customize that JSON rendering per package with the logging helpers — or, for output that isn't JSON, write a logs.formatter over the raw line yourself. See docs/logging.md.

Every session is also teed to a timestamped logfile. Read the current one from another terminal with devtooie logs (or devtooie logs -f to follow it live) — see devtooie logs.

Package lifecycle when you edit code

A package's command flags declare whether its dev process watches or just builds, which tells you (or an agent) whether to restart or rebuild it after a code edit. See docs/package-lifecycle.md.

Environment (.env) loading

devtooie loads .env files for every package it runs and injects them into that package's child process — merged over the current process.env without mutating it. Parsing is handled by dotenvx under the hood. Files are resolved at two scopes: the workspace root and the package's own directory. Only files that exist are loaded.

your-monorepo/
├── .env                     # workspace scope — base for every package
├── .env.local               # workspace scope, higher precedence
├── .env.development         # workspace scope, the default mode
└── packages/
    ├── core-api/
    │   ├── .env              # package scope — overrides workspace scope
    │   └── .env.local        # package scope, higher precedence
    └── web/
        └── .env

Files for a mode, ascending precedence within a scope:

  1. .env
  2. .env.local
  3. .env.<mode>
  4. .env.<mode>.local

The two .local files are the personal tier — commit .env and .env.<mode>, and keep .env*.local out of git.

Package scope overrides workspace scope, and within a scope a later file overrides an earlier one. ${VAR} references expand against already-loaded files and the current environment.

The ambient environment wins over the files, as in Next.js, Vite and node --env-file — so FOO=bar devtooie overrides a file for one run. Name the exceptions with env.override when a file needs to extend an inherited value:

defineConfig({
  env: { override: ['NODE_OPTIONS'] },   // or `true` for every variable
  packages: {/* … */},
});

With that, NODE_OPTIONS=$NODE_OPTIONS --flag appends to whatever the shell already set instead of losing to it.

Modes

--mode <name> picks which .env.<mode> files load; it defaults to development, so plain devtooie loads .env.development.

devtooie --mode test               # loads .env.test / .env.test.local
devtooie --mode test cmd -- vitest # same environment, one-off command

Any name works (test, staging, e2e), and the mode is passed to every child process as DEVTOOIE_MODE. Modes are exclusive — --mode test does not load .env.development — so values shared across modes belong in .env and .env.local, which load in every mode.

--mode deliberately leaves NODE_ENV alone (a mode name is free-form, and NODE_ENV isn't). Set it from the mode's own file if you want it: NODE_ENV=test in .env.test.

A package's port is also injected as PORT (an explicit .env PORT still overrides it). The reverse direction works too — port may be a callback that reads these same resolved files to decide the port:

backend: { port: ({ envs }) => Number(envs.BACKEND_PORT) }

healthcheck and urls take the same kind of callback, which additionally gets the resolved port. healthcheck also accepts { url, timeout }, for a service slow to answer while it warms up — see Readiness probing. See Configuration for the details.

While a session runs, devtooie watches these files (and where new ones would appear) and restarts the affected package(s) on change — editing a workspace-level file restarts every running package that uses it.

The same resolution is available as a standalone command: from inside a package's directory, devtooie cmd -- <command> runs a one-off command in that package's dir with its resolved env (or invoke one of its scripts/targets with -c) — see devtooie cmd.

Advanced CLI usage

Every flag and subcommand — plus devtooie cmd for running a command in a package's environment on demand — is documented in docs/cli.md.

Agent skill

If you opt in during devtooie init, devtooie installs an agent-facing skill file at .claude/skills/devtooie/SKILL.md (and, best-effort, under .agents/ / .cursor/ if those directories already exist). It teaches a coding agent how to check whether an app in the repo is already running, run devtooie headlessly (--plain -p <package>), drive a running session through the control API, read the logfile for debugging, and onboard a new package. The installed file is managed — treat it as generated, not something to hand-edit. devtooie init and every devtooie run refresh it to the installed version.

The skill points the agent at a single consolidated guide, packages/devtooie/docs/agents.md — the same material as this README plus how to drive devtooie headlessly, in one self-contained file. It's the one doc that ships inside the package (so the skill can load it from node_modules); the topic docs above live at the repo root.

Control API

While a session runs, devtooie exposes a localhost-only HTTP API for driving it (restart/rebuild a package, query status, hand off between invocations). See docs/control-api.md.

License

MIT