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

bun-workspaces

v1.11.1

Published

A monorepo management tool for Bun, with a CLI and API to enhance Bun's native workspaces.

Readme

Full Documentation: https://bunworkspaces.com

Changelog: GitHub Releases

Example Projects: Repository

bun-workspaces

A monorepo tool that enhances native Bun workspaces.

  • Works right away, with no boilerplate required 🍽️
  • Get rich metadata about your monorepo 🤖
  • Orchestrate your workspaces' package.json scripts 🎻
  • Run one-off Bun Shell commands in your workspaces 🐚
  • Use with Bun as your package manager for Node projects 🎁
  • Determine affected workspaces based on changed files 🕸️
  • AI: Provides an AGENTS.md file and an MCP server! 🛠️

To get started, all you need is a repo using Bun's workspaces feature for nested JavaScript/TypeScript packages. This adds enhanced features on top of plain workspaces.

Start running some CLI commands right away in your repo, or take full advantage of the TypeScript API and its features.

This package is unopinionated and works with any project structure you want. Think of this as a power suit you can snap onto native workspaces, rather than whole new monorepo framework.

Quick Start

Installation:

$ # Install to use the API and/or lock your CLI version for your project
$ bun add --dev bun-workspaces
$ # Start using the CLI with or without the installation step
$ bunx bun-workspaces --help

Note that you need to run bun install in your project for bun-workspaces to find your project's workspaces. This is because it reads bun.lock. This also means that if you update your workspaces, such as changing their name, you must run bun install for the change to reflect.

CLI

Full CLI documentation here

# You can add this to .bashrc, .zshrc, or similar.
# You can also invoke "bw" in your root package.json scripts.
alias bw="bunx bun-workspaces"

# List all workspaces in your project
bw list-workspaces

# ls is an alias for list-workspaces
bw ls --json --pretty # Output as formatted JSON

# Get info about a workspace
bw workspace-info my-workspace
bw info my-workspace --json --pretty # info is alias for workspace-info

# Get info about a script, such as the workspaces that have it
bw script-info my-script

# Run the lint script for all workspaces
# that have it in their package.json "scripts" field
bw run-script lint

# run is an alias for run-script
bw run lint my-workspace # Run for a single workspace
bw run lint my-workspace-a my-workspace-b # Run for multiple workspaces
bw run lint my-alias-a my-alias-b # Run by alias (set by optional config)

# A workspace's script will wait until any workspaces it depends on have completed
# Similar to Bun's --filter behavior
bw run lint --dep-order

# Continue running scripts even if a dependency fails
bw run lint --dep-order --ignore-dep-failure

bw run lint "my-workspace-*" # Run for matching workspace names
bw run lint "alias:my-alias-*" "path:my-glob/**/*" "tag:my-tag" # Use matching specifiers
bw run lint "*" "not:path:my-path/*" # Run for all workspaces not in my-path/

bw run lint --args="--my-appended-args" # Add args to each script call
bw run lint --args="--my-arg=<workspaceName>" # Use the workspace name in args

bw run "bun build" --inline # Run an inline command via the Bun shell

# Scripts run in parallel by default
bw run lint --parallel=false # Run in series
bw run lint --parallel=2 # Run in parallel with a max of 2 concurrent scripts
bw run lint --parallel=auto # Default, based on number of available logical CPUs
bw run lint --parallel=50% # Run in parallel with a max of 50% of the "auto" limit

# Use the grouped output style (default when on a TTY)
bw run my-script --output-style=grouped

# Set the max preview lines for script output in grouped output style
bw run my-script --output-style=grouped --grouped-lines=auto
bw run my-script --output-style=grouped --grouped-lines=10

# Use simple script output with workspace prefixes (default when not on a TTY)
bw run my-script --output-style=prefixed

# Use the plain output style (no workspace prefixes)
bw run my-script --output-style=plain

# List affected workspaces based on git diff (main vs. HEAD when not configured)
bw list-affected

# Set the git base and head for comparison
bw list-affected --base=my-branch-a --head=my-branch-b

# See detailed reasons for affected workspaces
bw list-affected --explain --detailed

# Run a script across the workspaces affected by a change
bw run-affected my-script

# Silence all output of the run command
bw --log-level=silent run my-script --output-style=none

# Show usage (you can pass --help to any command)
bw help
bw --help

# Show version
bw --version

# Pass --cwd to any command
bw --cwd=/path/to/your/project ls
bw --cwd=/path/to/your/project run my-script

# Pass --log-level to any command (debug, info, warn, error, or silent)
bw --log-level=debug ls

API

Full API documentation here

import { createFileSystemProject } from "bun-workspaces";

// A Project contains the core functionality of bun-workspaces.
// Below defaults to process.cwd() for the project root directory
// Pass { rootDirectory: "path/to/your/project" } to use a different root directory
const project = createFileSystemProject();

// A Workspace that matches the name or alias "my-workspace"
const myWorkspace = project.findWorkspaceByNameOrAlias("my-workspace");

// Array of workspaces whose names match the wildcard pattern
const wildcardWorkspaces = project.findWorkspacesByPattern("my-workspace-*");

// Array of workspaces that have "my-script" in their package.json "scripts"
const workspacesWithScript = project.listWorkspacesWithScript("my-script");

// Run a script in a workspace
const runSingleScript = async () => {
  const { output, exit } = project.runWorkspaceScript({
    workspaceNameOrAlias: "my-workspace",
    script: "my-script",

    // Optional. Arguments to add to the command
    // Can be a string or an array of strings
    // If string, the argv will be parsed POSIX-style
    args: ["--my", "--appended", "--args"],

    // Optional. Whether to ignore all output from the script.
    // This saves memory when you don't need script output.
    ignoreOutput: false,
  });

  // Get a stream of the script subprocess's output
  for await (const { chunk, metadata } of output.text()) {
    // console.log(chunk); // The output chunk's content (string)
    // console.log(metadata.streamName); // The output stream, "stdout" or "stderr"
    // console.log(metadata.workspace); // The target Workspace
  }

  // Get data about the script execution after it exits
  const exitResult = await exit;

  // exitResult.exitCode // The exit code (number)
  // exitResult.signal // The exit signal (string), or null
  // exitResult.success // true if exit code was 0
  // exitResult.startTimeISO // Start time (string)
  // exitResult.endTimeISO // End time (string)
  // exitResult.durationMs // Duration in milliseconds (number)
  // exitResult.metadata.workspace // The target workspace (Workspace)
};

// Run a script in all workspaces that have it in their package.json "scripts" field
const runManyScripts = async () => {
  const { output, summary } = project.runScriptAcrossWorkspaces({
    // Optional. This will run in all matching workspaces that have my-script
    // Accepts same values as the CLI run-script command's workspace patterns
    // When not provided, all workspaces that have the script will be used.
    workspacePatterns: ["my-workspace", "my-name-pattern-*"],

    // Required. The package.json "scripts" field name to run
    script: "my-script",

    // Optional. Arguments to add to the command (same as for runWorkspaceScript)
    args: ["--my", "--appended", "--args"],

    // Optional. Whether to run the scripts in parallel (default: true)
    parallel: true,

    // Optional. When true, a workspace's script will wait
    // until any workspaces it depends on have completed
    dependencyOrder: false,

    // Optional. When true and dependencyOrder is true,
    // continue running scripts even if a dependency fails
    ignoreDependencyFailure: false,

    // Optional. Whether to ignore all output from the scripts.
    // This saves memory when you don't need script output.
    ignoreOutput: false,

    // Optional, callback when script starts, skips, or exits
    onScriptEvent: (event, { workspace, exitResult }) => {
      // event: "start", "skip", "exit"
    },
  });

  // Get a stream of script output
  for await (const { chunk, metadata } of output.text()) {
    // console.log(chunk); // the output chunk's content (string)
    // console.log(metadata.streamName); // "stdout" or "stderr"
    // console.log(metadata.workspace); // the Workspace that the output came from
  }

  // Get final summary data and script exit details after all scripts have completed
  const summaryResult = await summary;

  // summaryResult.totalCount // Total number of scripts
  // summaryResult.allSuccess // true if all scripts succeeded
  // summaryResult.successCount // Number of scripts that succeeded
  // summaryResult.failureCount // Number of scripts that failed
  // summaryResult.startTimeISO // Start time (string)
  // summaryResult.endTimeISO // End time (string)
  // summaryResult.durationMs // Total duration in milliseconds (number)

  // The exit details of each workspace script
  for (const exitResult of summaryResult.scriptResults) {
    // exitResult.exitCode // The exit code (number)
    // exitResult.signal // The exit signal (string), or null
    // exitResult.success // true if exit code was 0
    // exitResult.startTimeISO // Start time (ISO string)
    // exitResult.endTimeISO // End time (ISO string)
    // exitResult.durationMs // Duration in milliseconds (number)
    // exitResult.metadata.workspace // The target workspace (Workspace)
  }
};

Configuration

bun-workspaces has no required configuration, but there are optional config files.

Workspace Config

Workspace configs can be placed in a workspace's directory at bw.workspace.ts.

Workspace configuration documentation here

// bw.workspace.ts — place in a workspace directory

// Also supported: bw.workspace.js, bw.workspace.json, bw.workspace.jsonc,
// or a "bw" key in package.json

import { defineWorkspaceConfig } from "bun-workspaces/config";

export default defineWorkspaceConfig({
  alias: "my-web-app", // shorthand name; use array for multiple
  tags: ["app", "frontend"],
  // Optional, for configuring affected workspace resolution inputs
  // Applies to all scripts that don't configure their own inputs
  defaultInputs: {
    // File paths, directory paths, or globs relative to the workspace's path.
    // Default is all git-trackable files in the workspace directory.
    files: ["src/**/*.ts", "!src/**/*.test.ts"],
    // Workspaces to treat like dependencies that aren't package.json dependencies
    workspacePatterns: ["tag:lib"],
    // Dependency names (e.g. "react") to treat as dependencies (default: all)
    externalDependencies: ["react"],
  },
  scripts: {
    // lower order runs first in sequenced script execution
    build: {
      // Optional, for setting the default script execution order
      order: 1,
      // Optional, for configuring affected workspace resolution inputs
      // Applies to the build script only
      inputs: { files: ["src/**/*.ts"] },
    },
    test: { order: 2 },
  },
  rules: {
    workspaceDependencies: {
      // Only "my-workspace" or workspaces tagged "lib" are allowed as dependencies
      allowPatterns: ["tag:lib", "my-workspace"],
      // Workspaces tagged "backend" are forbidden as dependencies
      denyPatterns: ["tag:backend"],
    },
  },
});

Root Config

A root config can be placed in the project root directory at bw.root.ts, which can also apply workspace configs in bulk by using workspace patterns.

Root configuration documentation here

More on workspace pattern configs here

// bw.root.ts — place in your project root directory
// Also supported: bw.root.js, bw.root.json, bw.root.jsonc,
// or a "bw-root" key in package.json

import { defineRootConfig } from "bun-workspaces/config";

export default defineRootConfig({
  defaults: {
    // default value for --parallel option
    parallelMax: 4,
    // default value for --shell option
    shell: "system",
    // default value for global --include-root-workspace option
    includeRootWorkspace: false,
  },

  // Apply workspace configs in bulk by workspace pattern, in order.
  // Each entry merges into matching workspaces' accumulated config.
  // Pattern matching reflects aliases and tags added by earlier entries.
  workspacePatternConfigs: [
    {
      patterns: ["path:packages/apps/**/*"],
      config: { tags: ["app"] },
    },
    {
      patterns: ["path:packages/libs/**/*"],
      config: { tags: ["lib"] },
    },
    {
      // "tag:app" matches because the first entry added it
      patterns: ["tag:app"],
      config: {
        // Inputs always override previous entries instead of deep merging
        defaultInputs: { files: ["src/**/*.ts"] },
        scripts: {
          build: { order: 1, inputs: { files: ["src/**/*.ts"] } },
        },
        rules: {
          workspaceDependencies: {
            allowPatterns: ["tag:lib"], // apps may only depend on libs
          },
        },
      },
    },
    {
      patterns: ["tag:app"],
      // Factory form: receives static workspace data and accumulated config
      config: (workspace, prevConfig) => ({
        alias: workspace.name.replace(/^@my-scope\//, ""),
      }),
    },
  ],
});

bun-workspaces is independent from the Bun project and is not affiliated with or endorsed by Anthropic. This project aims to enhance the experience of Bun for its users.

Developed By: