{"id":35132890,"url":"https://github.com/bun-workspaces/bun-workspaces","last_synced_at":"2026-05-08T02:37:40.076Z","repository":{"id":267170686,"uuid":"900444316","full_name":"bun-workspaces/bun-workspaces","owner":"bun-workspaces","description":"A tool for managing monorepos using Bun's native workspaces feature. Upgrade how you develop JavaScript and TypeScript projects with the bun-workspaces CLI and API.","archived":false,"fork":false,"pushed_at":"2026-03-27T16:21:16.000Z","size":2640,"stargazers_count":44,"open_issues_count":2,"forks_count":4,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-03-27T17:45:40.078Z","etag":null,"topics":["api","bun","cli","javascript","monorepo","npm","npm-package","typescript","workspace","workspaces"],"latest_commit_sha":null,"homepage":"https://bunworkspaces.com","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bun-workspaces.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"bun-workspaces","buy_me_a_coffee":"scottmorse"}},"created_at":"2024-12-08T19:36:42.000Z","updated_at":"2026-03-27T16:21:19.000Z","dependencies_parsed_at":"2026-03-03T00:00:45.106Z","dependency_job_id":null,"html_url":"https://github.com/bun-workspaces/bun-workspaces","commit_stats":null,"previous_names":["scottmorse/bun-workspaces","bun-workspaces/bun-workspaces"],"tags_count":47,"template":false,"template_full_name":null,"purl":"pkg:github/bun-workspaces/bun-workspaces","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bun-workspaces%2Fbun-workspaces","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bun-workspaces%2Fbun-workspaces/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bun-workspaces%2Fbun-workspaces/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bun-workspaces%2Fbun-workspaces/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bun-workspaces","download_url":"https://codeload.github.com/bun-workspaces/bun-workspaces/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bun-workspaces%2Fbun-workspaces/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31312917,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-02T12:59:32.332Z","status":"ssl_error","status_checked_at":"2026-04-02T12:54:48.875Z","response_time":89,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","bun","cli","javascript","monorepo","npm","npm-package","typescript","workspace","workspaces"],"created_at":"2025-12-28T07:05:02.268Z","updated_at":"2026-05-08T02:37:40.042Z","avatar_url":"https://github.com/bun-workspaces.png","language":"TypeScript","funding_links":["https://github.com/sponsors/bun-workspaces","https://buymeacoffee.com/scottmorse"],"categories":[],"sub_categories":[],"readme":"\u003ca href=\"https://bunworkspaces.com\"\u003e\n\u003cimg src=\"./workspaces/web/documentation-website/src/pages/public/images/png/bwunster-bg-banner-wide_3000x900.png\" alt=\"bun-workspaces\" width=\"100%\" /\u003e\n\u003c/a\u003e\n\n\u003cbr/\u003e\n\nFull Documentation: [https://bunworkspaces.com](https://bunworkspaces.com)\n\nChangelog: [GitHub Releases](https://github.com/bun-workspaces/bun-workspaces/releases)\n\n# bun-workspaces\n\nA [monorepo](http://sonarsource.com/resources/library/monorepo/) tool that enhances native [Bun workspaces](https://bun.sh/docs/install/workspaces).\n\n- Works right away, with **no boilerplate required** 🍽️\n- Get **rich metadata** about your monorepo 🤖\n- **Orchestrate** your workspaces' package.json scripts 🎻\n- Run one-off [**Bun Shell**](https://bun.com/docs/runtime/shell) commands in your workspaces 🐚\n- Use with Bun as your package manager for **Node** projects 🎁\n- Determine **affected workspaces** based on changed files 🕸️\n- Use the [MCP server](https://bunworkspaces.com/ai/mcp) to make your AI tooling aware of `bun-workspaces` and its documentation resources! 🛠️\n\nTo 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.\n\nStart running some [CLI commands](https://bunworkspaces.com/cli) right away in your repo, or take full advantage of the [TypeScript API](https://bunworkspaces.com/api) and its features.\n\nThis 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.\n\n## Quick Start\n\nInstallation:\n\n```bash\n$ # Install to use the API and/or lock your CLI version for your project\n$ bun add --dev bun-workspaces\n$ # Start using the CLI with or without the installation step\n$ bunx bun-workspaces --help\n```\n\nNote 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.\n\n### CLI\n\n[Full CLI documentation here](https://bunworkspaces.com/cli)\n\n```bash\n# You can add this to .bashrc, .zshrc, or similar.\n# You can also invoke \"bw\" in your root package.json scripts.\nalias bw=\"bunx bun-workspaces\"\n\n# List all workspaces in your project\nbw list-workspaces\n\n# ls is an alias for list-workspaces\nbw ls --json --pretty # Output as formatted JSON\n\n# Get info about a workspace\nbw workspace-info my-workspace\nbw info my-workspace --json --pretty # info is alias for workspace-info\n\n# Get info about a script, such as the workspaces that have it\nbw script-info my-script\n\n# Run the lint script for all workspaces\n# that have it in their package.json \"scripts\" field\nbw run-script lint\n\n# run is an alias for run-script\nbw run lint my-workspace # Run for a single workspace\nbw run lint my-workspace-a my-workspace-b # Run for multiple workspaces\nbw run lint my-alias-a my-alias-b # Run by alias (set by optional config)\n\n# A workspace's script will wait until any workspaces it depends on have completed\n# Similar to Bun's --filter behavior\nbw run lint --dep-order\n\n# Continue running scripts even if a dependency fails\nbw run lint --dep-order --ignore-dep-failure\n\nbw run lint \"my-workspace-*\" # Run for matching workspace names\nbw run lint \"alias:my-alias-*\" \"path:my-glob/**/*\" \"tag:my-tag\" # Use matching specifiers\nbw run lint \"*\" \"not:path:my-path/*\" # Run for all workspaces not in my-path/\n\nbw run lint --args=\"--my-appended-args\" # Add args to each script call\nbw run lint --args=\"--my-arg=\u003cworkspaceName\u003e\" # Use the workspace name in args\n\nbw run \"bun build\" --inline # Run an inline command via the Bun shell\n\n# Scripts run in parallel by default\nbw run lint --parallel=false # Run in series\nbw run lint --parallel=2 # Run in parallel with a max of 2 concurrent scripts\nbw run lint --parallel=auto # Default, based on number of available logical CPUs\nbw run lint --parallel=50% # Run in parallel with a max of 50% of the \"auto\" limit\n\n# Use the grouped output style (default when on a TTY)\nbw run my-script --output-style=grouped\n\n# Set the max preview lines for script output in grouped output style\nbw run my-script --output-style=grouped --grouped-lines=auto\nbw run my-script --output-style=grouped --grouped-lines=10\n\n# Use simple script output with workspace prefixes (default when not on a TTY)\nbw run my-script --output-style=prefixed\n\n# Use the plain output style (no workspace prefixes)\nbw run my-script --output-style=plain\n\n# List affected workspaces based on git diff (main vs. HEAD when not configured)\nbw list-affected\n\n# Set the git base and head for comparison\nbw list-affected --base=my-branch-a --head=my-branch-b\n\n# See detailed reasons for affected workspaces\nbw list-affected --explain --detailed\n\n# Run a script across the workspaces affected by a change\nbw run-affected my-script\n\n# Silence all output of the run command\nbw --log-level=silent run my-script --output-style=none\n\n# Show usage (you can pass --help to any command)\nbw help\nbw --help\n\n# Show version\nbw --version\n\n# Pass --cwd to any command\nbw --cwd=/path/to/your/project ls\nbw --cwd=/path/to/your/project run my-script\n\n# Pass --log-level to any command (debug, info, warn, error, or silent)\nbw --log-level=debug ls\n```\n\n### API\n\n[Full API documentation here](https://bunworkspaces.com/api)\n\n```typescript\nimport { createFileSystemProject } from \"bun-workspaces\";\n\n// A Project contains the core functionality of bun-workspaces.\n// Below defaults to process.cwd() for the project root directory\n// Pass { rootDirectory: \"path/to/your/project\" } to use a different root directory\nconst project = createFileSystemProject();\n\n// A Workspace that matches the name or alias \"my-workspace\"\nconst myWorkspace = project.findWorkspaceByNameOrAlias(\"my-workspace\");\n\n// Array of workspaces whose names match the wildcard pattern\nconst wildcardWorkspaces = project.findWorkspacesByPattern(\"my-workspace-*\");\n\n// Array of workspaces that have \"my-script\" in their package.json \"scripts\"\nconst workspacesWithScript = project.listWorkspacesWithScript(\"my-script\");\n\n// Run a script in a workspace\nconst runSingleScript = async () =\u003e {\n  const { output, exit } = project.runWorkspaceScript({\n    workspaceNameOrAlias: \"my-workspace\",\n    script: \"my-script\",\n\n    // Optional. Arguments to add to the command\n    // Can be a string or an array of strings\n    // If string, the argv will be parsed POSIX-style\n    args: [\"--my\", \"--appended\", \"--args\"],\n\n    // Optional. Whether to ignore all output from the script.\n    // This saves memory when you don't need script output.\n    ignoreOutput: false,\n  });\n\n  // Get a stream of the script subprocess's output\n  for await (const { chunk, metadata } of output.text()) {\n    // console.log(chunk); // The output chunk's content (string)\n    // console.log(metadata.streamName); // The output stream, \"stdout\" or \"stderr\"\n    // console.log(metadata.workspace); // The target Workspace\n  }\n\n  // Get data about the script execution after it exits\n  const exitResult = await exit;\n\n  // exitResult.exitCode // The exit code (number)\n  // exitResult.signal // The exit signal (string), or null\n  // exitResult.success // true if exit code was 0\n  // exitResult.startTimeISO // Start time (string)\n  // exitResult.endTimeISO // End time (string)\n  // exitResult.durationMs // Duration in milliseconds (number)\n  // exitResult.metadata.workspace // The target workspace (Workspace)\n};\n\n// Run a script in all workspaces that have it in their package.json \"scripts\" field\nconst runManyScripts = async () =\u003e {\n  const { output, summary } = project.runScriptAcrossWorkspaces({\n    // Optional. This will run in all matching workspaces that have my-script\n    // Accepts same values as the CLI run-script command's workspace patterns\n    // When not provided, all workspaces that have the script will be used.\n    workspacePatterns: [\"my-workspace\", \"my-name-pattern-*\"],\n\n    // Required. The package.json \"scripts\" field name to run\n    script: \"my-script\",\n\n    // Optional. Arguments to add to the command (same as for runWorkspaceScript)\n    args: [\"--my\", \"--appended\", \"--args\"],\n\n    // Optional. Whether to run the scripts in parallel (default: true)\n    parallel: true,\n\n    // Optional. When true, a workspace's script will wait\n    // until any workspaces it depends on have completed\n    dependencyOrder: false,\n\n    // Optional. When true and dependencyOrder is true,\n    // continue running scripts even if a dependency fails\n    ignoreDependencyFailure: false,\n\n    // Optional. Whether to ignore all output from the scripts.\n    // This saves memory when you don't need script output.\n    ignoreOutput: false,\n\n    // Optional, callback when script starts, skips, or exits\n    onScriptEvent: (event, { workspace, exitResult }) =\u003e {\n      // event: \"start\", \"skip\", \"exit\"\n    },\n  });\n\n  // Get a stream of script output\n  for await (const { chunk, metadata } of output.text()) {\n    // console.log(chunk); // the output chunk's content (string)\n    // console.log(metadata.streamName); // \"stdout\" or \"stderr\"\n    // console.log(metadata.workspace); // the Workspace that the output came from\n  }\n\n  // Get final summary data and script exit details after all scripts have completed\n  const summaryResult = await summary;\n\n  // summaryResult.totalCount // Total number of scripts\n  // summaryResult.allSuccess // true if all scripts succeeded\n  // summaryResult.successCount // Number of scripts that succeeded\n  // summaryResult.failureCount // Number of scripts that failed\n  // summaryResult.startTimeISO // Start time (string)\n  // summaryResult.endTimeISO // End time (string)\n  // summaryResult.durationMs // Total duration in milliseconds (number)\n\n  // The exit details of each workspace script\n  for (const exitResult of summaryResult.scriptResults) {\n    // exitResult.exitCode // The exit code (number)\n    // exitResult.signal // The exit signal (string), or null\n    // exitResult.success // true if exit code was 0\n    // exitResult.startTimeISO // Start time (ISO string)\n    // exitResult.endTimeISO // End time (ISO string)\n    // exitResult.durationMs // Duration in milliseconds (number)\n    // exitResult.metadata.workspace // The target workspace (Workspace)\n  }\n};\n```\n\n### Configuration\n\n`bun-workspaces` has no required configuration, but there are optional config files.\n\n#### Workspace Config\n\nWorkspace configs can be placed in a workspace's directory at `bw.workspace.ts`.\n\n[Workspace configuration documentation here](https://bunworkspaces.com/config/workspace)\n\n```typescript\n// bw.workspace.ts — place in a workspace directory\n\n// Also supported: bw.workspace.js, bw.workspace.json, bw.workspace.jsonc,\n// or a \"bw\" key in package.json\n\nimport { defineWorkspaceConfig } from \"bun-workspaces/config\";\n\nexport default defineWorkspaceConfig({\n  alias: \"my-web-app\", // shorthand name; use array for multiple\n  tags: [\"app\", \"frontend\"],\n  // Optional, for configuring affected workspace resolution inputs\n  // Applies to all scripts that don't configure their own inputs\n  defaultInputs: {\n    // File paths, directory paths, or globs relative to the workspace's path.\n    // Default is all git-trackable files in the workspace directory.\n    files: [\"src/**/*.ts\", \"!src/**/*.test.ts\"],\n    // Workspaces to treat like dependencies that aren't package.json dependencies\n    workspacePatterns: [\"tag:lib\"],\n    // Dependency names (e.g. \"react\") to treat as dependencies (default: all)\n    externalDependencies: [\"react\"],\n  },\n  scripts: {\n    // lower order runs first in sequenced script execution\n    build: {\n      // Optional, for setting the default script execution order\n      order: 1,\n      // Optional, for configuring affected workspace resolution inputs\n      // Applies to the build script only\n      inputs: { files: [\"src/**/*.ts\"] },\n    },\n    test: { order: 2 },\n  },\n  rules: {\n    workspaceDependencies: {\n      // Only \"my-workspace\" or workspaces tagged \"lib\" are allowed as dependencies\n      allowPatterns: [\"tag:lib\", \"my-workspace\"],\n      // Workspaces tagged \"backend\" are forbidden as dependencies\n      denyPatterns: [\"tag:backend\"],\n    },\n  },\n});\n```\n\n#### Root Config\n\nA root config can be placed in the project root directory at `bw.root.ts`,\nwhich can also apply workspace configs in bulk by using workspace patterns.\n\n[Root configuration documentation here](https://bunworkspaces.com/config/root)\n\n[More on workspace pattern configs here](https://bunworkspaces.com/config/workspace-pattern-configs)\n\n```typescript\n// bw.root.ts — place in your project root directory\n// Also supported: bw.root.js, bw.root.json, bw.root.jsonc,\n// or a \"bw-root\" key in package.json\n\nimport { defineRootConfig } from \"bun-workspaces/config\";\n\nexport default defineRootConfig({\n  defaults: {\n    // default value for --parallel option\n    parallelMax: 4,\n    // default value for --shell option\n    shell: \"system\",\n    // default value for global --include-root-workspace option\n    includeRootWorkspace: false,\n  },\n\n  // Apply workspace configs in bulk by workspace pattern, in order.\n  // Each entry merges into matching workspaces' accumulated config.\n  // Pattern matching reflects aliases and tags added by earlier entries.\n  workspacePatternConfigs: [\n    {\n      patterns: [\"path:packages/apps/**/*\"],\n      config: { tags: [\"app\"] },\n    },\n    {\n      patterns: [\"path:packages/libs/**/*\"],\n      config: { tags: [\"lib\"] },\n    },\n    {\n      // \"tag:app\" matches because the first entry added it\n      patterns: [\"tag:app\"],\n      config: {\n        // Inputs always override previous entries instead of deep merging\n        defaultInputs: { files: [\"src/**/*.ts\"] },\n        scripts: {\n          build: { order: 1, inputs: { files: [\"src/**/*.ts\"] } },\n        },\n        rules: {\n          workspaceDependencies: {\n            allowPatterns: [\"tag:lib\"], // apps may only depend on libs\n          },\n        },\n      },\n    },\n    {\n      patterns: [\"tag:app\"],\n      // Factory form: receives static workspace data and accumulated config\n      config: (workspace, prevConfig) =\u003e ({\n        alias: workspace.name.replace(/^@my-scope\\//, \"\"),\n      }),\n    },\n  ],\n});\n```\n\n_`bun-workspaces` is independent from the [Bun](https://bun.sh) project and is not affiliated with or endorsed by Anthropic. This project aims to enhance the experience of Bun for its users._\n\nDeveloped By:\n\n\u003ca href=\"https://smorsic.io\" target=\"_blank\" rel=\"noopener noreferrer\"\u003e\n  \u003cimg src=\"./workspaces/web/documentation-website/src/pages/public/images/png/smorsic-banner_light_803x300.png\" alt=\"Smorsic Labs logo\" width=\"280\" /\u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbun-workspaces%2Fbun-workspaces","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbun-workspaces%2Fbun-workspaces","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbun-workspaces%2Fbun-workspaces/lists"}