a command runner, not a build system

One interface over every runner in your repo.

tsr is a lightweight, polyglot, repo-aware task runner. It wraps the native runners you already have — npm, bun, cargo, go, uv — adds a task dependency graph and opt-in parallelism, and delegates caching to Turbo/Nx instead of reinventing it.

~/app — tsr ci
$ tsr ci
├─ lint   → cargo clippy
├─ test   → npm run test
└─ build  → turbo run build
 
✓ lint    ok    1.2s
✓ test    ok    3.4s
✓ build   ok    0.9s
 
✓ ci passed — exit 0

Why tsr

A thin unifying layer — predictable by default, delegate by design.

No startup tax

Metachar-free run strings are spawned directly (execvp-style) — no npm run / Node boot to pay on the common path.

🌐

Polyglot

One entry point across every ecosystem. A bare [tasks.test]auto-detects each package's runner: cargo, go, npm/bun, uv.

🔗

Dependency graph

Declare deps and get a DAG. Sequential by default; opt into concurrency with parallel = true. Fail-fast, always.

🧩

Three task forms

delegate to a backend, run a command directly, or let tsr auto-detect the native runner — resolved by precedence.

🐚

Safe mini-shell

$VAR, && || ; and quoting are supported; pipes, redirects and globs are rejected up front, not half-run.

📦

Delegate caching

Content-hash and remote caching are ceded to Turbo/Nx — never reimplemented. tsr stays a lightweight command runner.

How it compares

tsr is a command runner, not a build system — it unifies the runners you have and cedes caching to the tools built for it. Here's where it lands next to the usual suspects.

Capabilitytsrnpmbundenojustgo-taskmiseTurbo/Nx
Auto-detects each package's runnercargo / go / npm / bun / uv from a bare task
Dependency graph (DAG)
Opt-in parallelism🟡🟡🟡
Monorepo workspace fan-outrun one task across every package🟡🟡🟡
Resolves node_modules/.bincall vite / eslint like npm run
Declarative env vars & .env[env] blocks + auto-loaded .env🟡🟡🟡
Native speed, no runtime boot🟡🟡🟡🟡
Single static binary
Content-hash / remote cachingtsr delegates this to Turbo/Nx by design🔀🟡

✅ yes   🟡 partial / needs a plugin   🔀 delegated by design   ❌ no  ·  see the speed numbers →

One file, every task

tasks.toml is both the config and the workspace-root anchor. Run tsr <task> from anywhere in the repo.

  • run — spawn a command directly.
  • packages — fan out across a monorepo (glob or manifest name).
  • delegate — hand off to Turbo, Make, or any binary.
  • deps + parallel — the graph, opt-in concurrency.
# tasks.toml
[workspace]
members = ["apps/*", "packages/*"]

[tasks.dev]
run = "vite"
dir = "apps/web"

[tasks.test]
packages = ["apps/*"]        # auto-detect

[tasks.build]
delegate = "turbo"           # → turbo run build

[tasks.ci]
deps = ["lint", "test", "build"]
parallel = true