xeq
Run sequences of shell commands with a single word.
Define your commands once in a xeq.toml file. Run them from anywhere, on any OS, without rewriting them every time.
Table of Contents
- Why xeq?
- How does xeq compare?
- Installation
- Quick Start
- Commands
- TOML Format
- Features
- Examples
- How It Works
- Contributing
- License
Why xeq?
Every project has a setup ritual — install dependencies, build, run tests, configure things. You either memorize the steps, paste them from a notes file, or write a shell script that only works on your machine.
xeq gives you a better option:
- Write your commands once in a
xeq.tomlfile - Commit it to your repo
- Anyone on the team runs the exact same steps with one command — on Linux, macOS, or Windows
How does xeq compare?
| Feature | xeq | just | make |
|---|---|---|---|
| Config format | TOML | Custom syntax | Makefile syntax |
| Cross-platform | ✅ | ✅ | ⚠️ poor on Windows |
| Parallel execution | ✅ built-in | ❌ | ❌ |
| Nested scripts | ✅ (xeq://) |
✅ | ⚠️ |
| Variables | ✅ global + local | ✅ | ✅ |
| Argument passing | ✅ named + positional | ✅ named + defaults | ⚠️ |
| Flag toggle mechanic | ✅ | ❌ | ❌ |
.env loading |
✅ | ✅ | ❌ |
| Multi-language recipes | ❌ | ✅ | ❌ |
| Learning curve | None (TOML) | Low (new syntax) | High |
Installation
macOS / Linux
|
Windows (PowerShell)
iwr https://raw.githubusercontent.com/opmr0/xeq/main/install.ps1 -UseBasicParsing | iex
Via cargo
Quick Start
1. Create a xeq.toml in your project root:
[]
= [
"npm install",
"npm run build"
]
[]
= ["npm run dev"]
2. Point xeq at the file (one time only):
If there is a
xeq.tomlin your current directory, xeq finds it automatically — no config step needed.
3. Run any script by name:
That's it. From now on, anyone who clones the repo can run xeq run setup and get the exact same result.
Commands
xeq init
Creates a starter xeq.toml in the current directory. Will not overwrite an existing file.
xeq config [path]
Saves the path to your TOML file globally. You only need to do this once, or when you move the file.
xeq run <script> [flags]
Runs a script by name. Commands execute one at a time in order. If any command fails, xeq stops — unless you pass --continue-on-err.
| Flag | Short | Description |
|---|---|---|
--continue-on-err |
-C |
Keep going even if a command fails |
--quiet |
-q |
Hide xeq's own log messages |
--clear |
-c |
Clear the terminal before each command |
--parallel |
-p |
Run all commands at the same time |
--allow-recursion |
Let a script call itself | |
--args <values...> |
-a |
Pass arguments into the script — positional or key=value |
--global |
-g |
Use the globally saved path instead of the local xeq.toml |
xeq list
Shows all scripts in your TOML file — their names, descriptions, and commands.
TOML Format
A xeq.toml file contains named scripts. Each script needs at least a run array:
[]
= "What this script does"
= ["quiet"]
= [
"command one",
"command two"
]
- Script names are case-sensitive —
Buildandbuildare different scripts descriptionis optional and only shows inxeq listoptionsare optional — see Script Options
Features
1. Script Options
Bake default behavior into a script so you don't have to pass flags every time:
[]
= ["quiet", "parallel"]
= ["cargo build", "cargo test"]
Now xeq run build always runs quietly and in parallel — no flags needed.
Available options: quiet, clear, parallel, continue_on_err, allow_recursion
Toggling: CLI flags toggle script options. If a script has quiet set and you pass --quiet, it turns quiet off for that run.
2. Variables
Use a [vars] block to define reusable values. Reference them in commands with {{@varname}}:
[]
= "myapp:latest"
= "development"
[]
= ["docker build -t {{@image}} ."]
[]
= ["APP_ENV={{@env}} npm start"]
Local variables let a specific script override a global value:
[]
= "myapp:latest"
[]
= "myapp:build"
= ["docker build -t {{@image}} ."] # uses "myapp:build"
[]
= ["docker push {{@image}}"] # uses "myapp:latest"
Override at runtime using --args:
Resolution order — most specific wins:
--args (runtime) → local vars (per script) → global vars (file-level)
3. Arguments
For values that change every run, use positional placeholders {{1}}, {{2}}, etc.:
[]
= [
"npm create vite@latest {{1}} -- --template {{2}}",
"cd {{1}}",
"npm install"
]
# {{1}} = my-app
# {{2}} = react
Mix named and positional args in a single call:
4. Environment Variables
Reference system environment variables in commands using {{$VARNAME}}:
[]
= ["deploy --token {{$API_TOKEN}} --env {{$DEPLOY_ENV}}"]
xeq also automatically loads a .env file from the current directory if one exists, so your variables are available without setting them manually.
# .env
API_TOKEN=abc123
DEPLOY_ENV=production
5. Nested Scripts
A script can call other scripts using the xeq:// prefix:
[]
= ["npm install"]
[]
= ["npm run build"]
[]
= [
"xeq://install",
"xeq://build",
"npm run deploy"
]
Running xeq run deploy automatically runs install and build first, in order.
Circular dependency protection: If a script tries to call itself directly or through a chain, xeq exits with an error. Add
allow_recursiontooptionsif you intentionally need this.
6. Parallel Execution
Run all commands in a script at the same time instead of one by one:
[]
= ["parallel"]
= [
"cargo test",
"cargo clippy",
"cargo fmt --check"
]
Scripts with
cdcommands orxeq://calls cannot run in parallel — xeq will exit with a clear error if you try.
7. Local Configuration
xeq automatically detects a xeq.toml in your current directory. If one exists, it uses that. If not, it falls back to the globally saved path.
This means in most projects you never need to run xeq config at all — just put the file in the project root.
To force the global path, pass --global:
8. cd with Operators
xeq supports shell-style operators after cd, so you can chain commands naturally:
[]
= [
"cd my-app && npm install",
"cd /tmp || echo 'fallback'",
"cd build; echo always runs",
"cd server & echo background"
]
| Operator | Behavior |
|---|---|
&& |
Run next command only if cd succeeded |
|| |
Run next command only if cd failed |
; |
Always run next command |
& |
Spawn next command in background |
! |
Negate the result of cd |
Example Files
The examples/ folder has ready-to-use TOML files for common workflows.
| File | Description | Key Features Used |
|---|---|---|
react-tailwind.toml |
Scaffold and run a React + Tailwind project | variables, cd operators |
nextjs.toml |
Next.js project setup and pipeline | nested scripts, variables |
rust-project.toml |
Rust checks, build and publish | parallel, nested scripts |
docker.toml |
Docker image and container management | variables, nested scripts |
git-workflow.toml |
Common git operations | variables, arguments |
nested-scripts.toml |
CI pipeline from reusable pieces | nested scripts |
env-vars.toml |
Deploy and notify using env vars | {{$VAR}}, nested scripts |
python-project.toml |
Virtualenv, checks and PyPI publish | parallel, nested scripts, variables |
database.toml |
Migrations, seed, dump and restore | env vars, arguments, nested scripts |
monorepo.toml |
Multi-package frontend workspace | parallel, variables, nested scripts |
aws-deploy.toml |
ECR push and ECS deploy pipeline | env vars, nested scripts |
go-project.toml |
Go build, test and cross-compile | parallel, nested scripts, variables |
arguments.toml |
Positional and named arg patterns | arguments, variables |
script-options.toml |
Flag toggle mechanic demonstrations | options, parallel, quiet, continue_on_err |
How It Works
- xeq stores your TOML file path using the system config directory
- Commands run through
sh -con Linux/macOS andcmd /Con Windows cdcommands update the working directory for all subsequent commands in that script- Variables resolve in order:
--args→ local vars → global vars - Environment variables are loaded from
.envautomatically before any script runs - On failure, xeq exits with the same exit code as the failed command
- Script names are case-sensitive:
Buildandbuildare different scripts
Contributing
Contributions are welcome — whether it's a bug fix, a new feature, or an improvement to the docs. Open an issue.
Getting started:
Before submitting a PR:
- Run
cargo fmtto format your code - Run
cargo clippyand fix any warnings - Run
cargo testand make sure all tests pass - If you're adding a new feature, add tests for it
Project structure:
src/
main.rs # CLI parsing and command dispatch
config.rs # Path saving/loading and TOML reading
runner.rs # Script execution logic
types.rs # Shared types (Script, Scripts, Config, SavedPath)
macros.rs # log! and err! macros
examples/ # Ready-to-use TOML files
License
MIT — LICENSE