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

@18ways/monorunner

v0.1.0

Published

Run and monitor local monorepo services from one terminal

Readme

monorunner is an interactive local development runner for repositories with several services. Define the commands you already use in dev.json; Monorunner starts a named group, assigns ports where needed, shows their state and logs, and shuts the group down cleanly.

It runs on Node.js. Your services can use npm, pnpm, Yarn, Bun, or any other command available on your PATH.

Quick Start

Install it in the repository that owns your services:

npm install --save-dev @18ways/monorunner
pnpm add -D @18ways/monorunner
# or
bun add -d @18ways/monorunner

Create dev.json in that repository's root:

{
  "defaultMode": "web",
  "modes": {
    "web": { "apps": ["frontend", "api"] },
    "all": { "apps": ["frontend", "api", "worker"] }
  },
  "apps": [
    {
      "id": "frontend",
      "name": "Frontend",
      "cwd": "apps/frontend",
      "command": "npm",
      "args": ["run", "dev"],
      "preferredPort": 3000
    },
    {
      "id": "api",
      "name": "API",
      "cwd": "apps/api",
      "command": "npm",
      "args": ["run", "dev"],
      "preferredPort": 3001
    },
    {
      "id": "worker",
      "name": "Worker",
      "cwd": "apps/worker",
      "command": "npm",
      "args": ["run", "dev"],
      "exposesPort": false
    }
  ]
}

Add a script:

{
  "scripts": {
    "dev": "monorunner"
  }
}

Then run npm run dev. Monorunner starts the web mode and opens its terminal monitor. Run npx @18ways/monorunner all to start a different mode.

What It Gives You

  • One place to start the services that make up a local development stack.
  • Automatic, stable port selection for services that expose a port.
  • A terminal monitor for status, resource use, logs, restarts, test runs, and clean shutdown.
  • A small plugin API for project-specific environment setup and monitor actions.

Monorunner is deliberately focused on the local developer loop. It does not replace your package manager, task graph, CI, or deployment system.

Commands

| Command | Description | | --- | --- | | monorunner | Start the configured defaultMode and open the monitor. | | monorunner <mode> | Start a named mode from dev.json. | | monorunner --help | Show usage without requiring a dev.json. | | monorunner --version | Print the installed package version. |

all is only a conventional name. Define any modes that make sense for your repository.

Configuration

dev.json has three required top-level fields:

| Field | Purpose | | --- | --- | | defaultMode | Mode to start when no mode is passed. | | modes | Named groups of app IDs. Every mode must contain at least one app. | | apps | The service definitions. | | plugins | Optional configuration for core and external plugins. |

Each app supports the following fields:

| Field | Required | Description | | --- | --- | --- | | id | Yes | Stable ID used in modes and monitor rows. | | name | Yes | Display name in the monitor. | | cwd | Yes | Working directory, relative to the repository root. | | command | Yes | Executable to run, such as npm, pnpm, bun, or node. | | args | Yes | Arguments for command. | | preferredPort | No | First port to try for this service. | | exposesPort | No | Set to false for workers, watchers, and other non-listening processes. | | injectPort | No | Controls how a selected port is provided to the command. | | env | No | Environment variables to add for this app. | | plugins | No | App-specific settings consumed by plugins. |

Ports

Give a listening service a preferredPort and Monorunner will reserve it when free, or scan upward when it is already in use. The selected value remains stable while the service is running.

By default, Monorunner sets PORT and appends -- --port <port> to the command. This is useful for Next.js-style scripts:

{
  "preferredPort": 3000
}

Override the convention when a service uses another interface:

{
  "preferredPort": 5173,
  "injectPort": {
    "env": "PORT",
    "args": ["--port", "{port}"]
  }
}

You can set only an environment variable, use another name, or keep the port only for the monitor:

{
  "preferredPort": 8080,
  "injectPort": {
    "env": "APP_PORT",
    "args": false
  }
}
{
  "preferredPort": 4000,
  "injectPort": false
}

The chosen value is always retained internally as MONORUNNER_PORT for display and managed shutdown.

Monitor

| Key | Action | | --- | --- | | Up/down | Select a service. | | / | Search installed action plugins. | | s | Start the selected service. | | r | Restart the selected service. | | k | Stop the selected service. | | l | Open full logs for the selected service. | | t | Run configured checks for services that are currently running. | | q | Shut down managed services and exit. |

Tests

The core tests plugin runs checks only for services that are currently online. Press t in the monitor to run it. Targets and tasks run in parallel.

With no configuration, Monorunner runs the test package script in every running service that defines one. Configure other package scripts with plugins.tests:

{
  "plugins": {
    "tests": {
      "tasks": ["lint", "typecheck", "test"]
    }
  }
}

String tasks are package-script names and are skipped when a target does not define them. For custom commands, use an object with id, command, args, and optional cwd; {targetId} and {targetCwd} are available inside arguments.

The status bar shows progress while checks run. A successful run remains visible as ✓ Tests passed until the next interaction. A failed run takes over the monitor, identifies affected services, and lets you continue, inspect the failing logs, or copy them.

Plugins

Plugins are ordinary packages. Add one by its package name in dev.json:

{
  "plugins": {
    "@acme/monorunner-plugin-example": true
  }
}

Plugin packages can export monorunnerPlugin, plugin, or a default export. The supported authoring API is available from @18ways/monorunner/plugin:

import type { DevPlugin } from '@18ways/monorunner/plugin';

export const monorunnerPlugin: DevPlugin = {
  id: 'example',
  title: 'Example',
  aliases: ['ex'],
  description: 'Adds project-specific environment variables.',
  getRuntimeEnv(context) {
    return {
      EXAMPLE_SERVICE: context.app.id,
    };
  },
};

Plugins can prepare local state, add environment variables, wrap service commands, add monitor keybindings, render a status message, and take over the monitor for focused flows. The plugin API is pre-1.0, so pin a compatible Monorunner version in published plugins rather than using an unrestricted peer range.

Runtime State

Monorunner writes local runtime state under .cache/monorunner/ by default. It contains generated runtime configuration, plugin state, and shutdown port maps. Override it with Monorunner-owned environment variables:

| Variable | Default | Description | | --- | --- | --- | | MONORUNNER_STATE_DIR | .cache/monorunner | State directory, relative to the repository root unless absolute. | | MONORUNNER_PROCESS_HOME | <state-dir>/home | Internal process-manager state directory. | | MONORUNNER_PROCESS_TIMEOUT_MS | 10000 | Timeout for process commands. | | MONORUNNER_QUERY_TIMEOUT_MS | 1000 | Timeout for lightweight process queries. |

Requirements

  • Node.js 20 or newer to run the published CLI.
  • The executables named in your app definitions must be available on PATH.
  • Bun is only needed when developing Monorunner from this repository.

Development

From this monorepo, run the source version directly:

bun packages/monorunner/src/cli.ts

Build and smoke-test the Node distribution:

bun run --cwd packages/monorunner test

Licence

MIT