Skip to main content
Run one or more package.json scripts across the packages and modules of your workspace, in dependency order, with a content-addressed task cache. Tasks whose inputs have not changed are restored from the cache instead of re-executed.

Usage

Examples

Build and lint every package and module:
Run only specific modules and packages:
Stream plain logs instead of the interactive view (for CI):
Force every task to re-run, ignoring the cache:

Options

Execution

Each command in --commands runs as a group: first every build, then every lint, and so on. Within a group, targets run in workspace dependency order — a target waits for the targets it depends on — with as many tasks in flight as the machine has available parallelism. A target whose package.json does not define the script is skipped without an error. If any task fails, the run stops immediately: remaining tasks are cancelled, the failing task’s output is printed, and the command exits with code 1.
build, fmt, lint and test have graduated to standalone commands with their own caches. When you name one of them in --commands, workspace:run delegates to build, fmt, lint or test directly, in the order requested, so the behaviour never drifts from running each standalone.

Rust crates

A directory carrying a Cargo.toml is a workspace member even without a package.json. For any command such a crate does not define itself, workspace:run falls back to the matching Cargo command:

Output

Without --logs, an interactive view keeps a live status bar pinned to the bottom of the terminal while finished tasks scroll into your normal terminal history above it — scrollback is preserved, not taken over.
  • Every task prints a permanent line when it finishes: a green ✔ billing:build 1.2s on success, ✔ user:lint cached on a cache hit, or a red ✖ web:build failed exit 1 on failure. Pending and skipped tasks are not printed.
  • The status bar shows a progress bar with the completed/total count and elapsed time, plus one line per running task — each with a spinner and that task’s latest log line.
  • Press ctrl+c (or q) to abort.
In a non-interactive terminal the command falls back to streaming automatically. With --logs, each task streams its output live, prefixed with the task label so concurrent tasks stay readable, and closes with a or line and its duration:

Caching

Results are cached in var/cache/workspace at the project root. A task’s cache key hashes every source file of its target individually, the fingerprints of all transitive workspace dependencies, the script text, and the root config files (package.json, bun.lock, tsconfig.json, biome.jsonc). Build artifacts such as node_modules, dist, and var are excluded, so a task re-runs exactly when something it can observe has changed. File hashes are memoised by size and modification time, which turns the fingerprint of an unchanged target into a directory walk. A cache hit prints a ✔ … cached line instead of re-running the script. Only successful runs are cached. The cache directory is safe to delete at any time; the next run simply rebuilds it.