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.
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.
| Capability | tsr | npm | bun | deno | just | go-task | mise | Turbo/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 ·
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