The problem
Every time I clone a repo, I have to figure out how to run it.
- Go project?
go test ./...— unless it usesmake test - Node project?
npm test— unless it usespnpmoryarnorbun - Rust?
cargo test. Python? Could bepytest,python -m unittest, orpoetry run pytest.
This is fine for my own projects. But when I'm reviewing a PR, onboarding a new teammate, or setting up CI for a polyglot team, it becomes noise.
What I built
rex is a single Go binary that detects your project's stack and runs the right command. No config file. No reading the README.
git clone https://github.com/someone/unknown-project
cd unknown-project
rex test # it just works
How it works under the hood
The detection logic is surprisingly simple — and that's the point.
Step 1: File scanning
rex walks your project root and looks for known files:
| File | Stack | Package manager |
|---|---|---|
go.mod |
Go | go modules |
package.json + pnpm-lock.yaml
|
Node | pnpm |
Cargo.toml |
Rust | cargo |
pyproject.toml + uv.lock
|
Python | uv |
pom.xml |
Java | Maven |
build.gradle |
Java | Gradle |
Gemfile |
Ruby | bundler |
composer.json |
PHP | composer |
mix.exs |
Elixir | mix |
build.zig |
Zig | zig |
This happens in <50ms because it's just os.Stat calls — no network, no parsing heavy files.
Step 2: Command mapping
Once the stack is known, rex maps standard verbs to the right tool:
// Go
test -> "go test ./..."
run -> "go run ./cmd/server"
build -> "go build ./..."
// Node (pnpm)
test -> "pnpm test"
run -> "pnpm run dev"
build -> "pnpm run build"
Step 3: Priority chain (the smart part)
rex doesn't blindly guess. It respects what's already there:
1. Justfile / Makefile (task runner overrides everything)
2. package.json scripts / Cargo.toml / go.mod (ecosystem native)
3. Language heuristics (fallback)
If a Makefile defines a test target, rex test runs make test — even in a Go project.
The GitHub Action
The most interesting part is how this became a GitHub Action.
Instead of requiring Go to be installed in the runner, the action downloads the correct binary directly from the release page:
- uses: rexrun-dev/rex@v0.4.0
with:
command: test
This single step replaces 10+ lines of stack-specific YAML. Works for all 12 supported languages.
Why composite actions?
I used GitHub's composite action type instead of Docker. Why?
- Faster: no container startup
- Smaller: no image to pull
- Portable: works on any runner (ubuntu, macOS, Windows)
The trade-off? The install script has to handle OS/arch detection:
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
case "$ARCH" in
x86_64) ARCH="amd64" ;;
aarch64|arm64) ARCH="arm64" ;;
esac
URL="https://github.com/rexrun-dev/rex/releases/download/${VERSION}/rex_${VERSION#v}_${OS}_${ARCH}.tar.gz"
curl -sL "$URL" | tar xz -C /usr/local/bin rex
Watch mode
A recent addition: rex watch polls the filesystem and re-runs your command on change.
rex watch test # re-run tests when files change
rex watch build # re-build on change
It's not using inotify or fsnotify — just a lightweight polling loop with hash-based change detection. This makes it portable across all platforms without CGO or platform-specific dependencies.
CI generator
rex ci generates a GitHub Actions workflow file customized for your detected stack:
$ rex ci
✓ created .github/workflows/ci.yml (go project)
The generated YAML uses the official setup actions for each language (setup-go, setup-node, setup-python, etc.) and runs the correct test command.
Numbers
- Binary size: ~3.5 MB
- Startup time: <50ms
- Languages supported: 12
- Lines of Go: ~2,000
- Zero runtime dependencies
Try it
brew tap rexrun-dev/tap && brew install rex
# or
go install rexrun.dev/rex/cmd/rex@latest
Or try the interactive playground at rexrun.dev — paste any GitHub repo URL and see what rex would detect.
GitHub: github.com/rexrun-dev/rex
Marketplace: github.com/marketplace/actions/rex-universal-project-runner
Top comments (0)