ez

THE SAME COMMANDS, EVERY STACK

ez check lints and runs the tests, whether that repo is a React frontend, a Swift backend or an iOS app. Each project says what check means once, in a file you commit, so the whole team gets the same commands.
Runs get timed too, so you know roughly what you're in for next time.

macOS ยท one native Swift binary ยท MIT licensed

Terminal recording: ez test reports the run was 74% slower than its median, ez stats shows three weeks of drift, and ez stats lists each alias with its duration trend
Install
brew tap urtti/ez && brew install ez
Update
brew update && brew upgrade ez

What it does

One word, any stack

Each project defines its own check or deploy in a .ez_cli.json file, so the same word does the tech-appropriate thing in every repo you own.

Team sharing

Commit the file and the next person to clone the repo gets every alias with it. No setup instructions in the README.

Keychain secrets

Secrets stay in the Apple Keychain and reach the command through its environment. Never on disk, never printed, never sitting in ps for anyone to read.

Parameterized

Drop {1} and {2} into an alias and they fill in from the arguments you pass. Anything extra just gets appended.

NEW IN 1.3

Run history

Every run gets written down locally, so you know whether a build is a coffee or a thirty-second wait. ez stats gives you success rate, median, p90 and a trend per alias, and flags a run that lands well off its own median.

NEW IN 1.3

Parallel mode

Add an alias with -p and its commands run at the same time, each timed separately, with one Ctrl+C stopping the lot.

Real exit codes

An alias exits with the code of whatever it ran, so ez test && ez deploy behaves the way you'd expect in CI and in scripts.

Interactive passthrough

vim, less and ssh work through an alias exactly as they would on their own. The terminal is handed straight through.

Private & Local

No telemetry, no network calls of its own. Run history sits in a local SQLite file at ~/.ez/runs.db and stays there.

How it looks in use

Create an alias and use it

$ ez add deploy "./scripts/deploy.sh --env prod"๐Ÿ˜ ez deploy now stores ./scripts/deploy.sh --env prod. Execute in this directory with ez deploy.
$ ez deploy๐Ÿ˜ Executing: ./scripts/deploy.sh --env prod
Deploying to production...
๐Ÿ˜โฑ๏ธ 47.900 s

Add & use secrets

$ ez add-secret --key EZ_API_KEYEnter value for EZ_API_KEY:
๐Ÿ˜ Secret 'EZ_API_KEY' stored in keychain.
$ ez add upload 'curl -H "Authorization: {EZ_API_KEY}" ...'๐Ÿ˜ ez upload now stores curl -H "Authorization: {EZ_API_KEY}" ... Execute in this directory with ez upload.
$ ez upload๐Ÿ˜ Executing: curl -H "Authorization: {EZ_API_KEY}" https://api.example.com
The value is read from Keychain and passed through the environment โ€” it never reaches the screen, the process table, or disk.

Create alias with parameters

$ ez add tag 'git tag -a {1} -m "Release {1}"'๐Ÿ˜ ez tag now stores git tag -a {1} -m "Release {1}". Execute in this directory with ez tag.
$ ez tag v2.0.0๐Ÿ˜ Executing: git tag -a v2.0.0 -m "Release v2.0.0"

See which commands got slower

NEW IN 1.3

Runs are recorded in a local SQLite file, scoped to the directory they ran in. Only the alias definition is stored, never your arguments or resolved secrets.

$ ez stats
๐Ÿ˜ Run history
alias success rate median duration duration trend
ez build 18 of 18 12.400 s โ†‘ 33% slower
ez deploy 6 of 6 47.900 s ยท needs 4 more runs
ez test 22 of 25 4.135 s โ†’ steady
$ ez stats build
๐Ÿ˜ Recent runs ez build (18)
2026-08-19 15:29:02 16.800 s ok
2026-08-19 14:29:02 15.900 s ok
2026-08-19 10:29:02 12.100 s ok
...
๐Ÿ˜ Summary ez build (18 successful run(s))
min 11.600 s median 12.400 s p90 16.800 s max 17.200 s
trend โ†‘ 33% slower โ€” last 5 median 16.100 s vs 12.100 s before

Run commands concurrently

NEW IN 1.3
$ ez add -p checks "npm run lint" "npm run typecheck"๐Ÿ˜ ez checks now stores npm run lint | npm run typecheck. Execute in this directory with ez checks.
$ ez checks
๐Ÿ˜ Executing: npm run lint | npm run typecheck
๐Ÿ˜ Running in parallel: npm run lint, npm run typecheck
Started [PID:41482] npm run lint...
Started [PID:41483] npm run typecheck...
๐Ÿ˜โฑ๏ธ [PID:41482] npm run lint 2.110 s
๐Ÿ˜โฑ๏ธ [PID:41483] npm run typecheck 3.810 s
$ ez checks && ez deployAn alias exits with the code of the work it ran, so a failed check stops the chain.