GitHub - urtti/ez: Source code of Mac CLI tool ez, distribution via Homebrew

GitHub

4 min read Original article ↗

A macOS CLI tool for project-specific command aliases. Define commands locally within project directories, making team workflows more efficient and discoverable.

Demo

Features

  • Project-scoped storage - Aliases live in .ez_cli.json files at the directory level, keeping commands tethered to their respective projects
  • Safety through locality - No global aliases means no accidental damage in a different directory
  • Team collaboration - Commit the config file to version control so new team members get immediate access to established commands. As with a Makefile or npm run, running an alias from a cloned repo runs whatever commands that repo's authors defined — review .ez_cli.json before running aliases from sources you don't trust
  • Fast - Built in Swift with instant startup times and no third-party dependencies — the only package used is Apple's own swift-argument-parser
  • Secrets management - Store API keys and tokens in Apple Keychain, reference them in aliases without exposing values in terminal output or the process table
  • Private - Makes no network calls; run history is recorded locally in ~/.ez/runs.db and never leaves your machine
  • Interactive support - Full terminal passthrough for interactive applications like vim and ssh
  • Shell integration - zsh tab completion for command discovery
  • Built-in analytics - Local runtime tracking; ez stats shows per-alias duration history and trends

Installation

brew tap urtti/ez && brew install ez

Upgrade an existing install:

brew update && brew upgrade ez

See CHANGELOG.md for what changed in each release.

Usage

Add an alias:

ez add deploy "./scripts/deploy.sh --env prod"

Run an alias:

List all aliases:

Remove an alias:

Parameterized aliases with {1}, {2}, ... placeholders:

ez add tag 'git tag -a {1} -m "Release {1}"'
ez tag v2.0.0  # → git tag -a v2.0.0 -m "Release v2.0.0"

Extra arguments are automatically appended to the end of the command:

ez add gs "git stash"
ez gs pop              # → git stash pop

ez add greet 'echo hello {1}'
ez greet world a b     # → echo hello world a b

Store secrets in Apple Keychain and reference them in aliases:

ez add-secret --key EZ_API_KEY          # prompts for the value with typing hidden
ez add deploy 'curl -H "Authorization: {EZ_API_KEY}" https://api.example.com/deploy'
ez deploy  # secret is injected at runtime, never shown in terminal output

In scripts, pipe the value on stdin instead:

op read "op://vault/api/key" | ez add-secret --key EZ_API_KEY --force

(--value is also accepted but deprecated — it leaves the secret in shell history and ps — and will be removed in a future release.)

Remove a secret:

ez remove-secret EZ_API_KEY

Run multiple commands sequentially:

ez build && ez test && ez deploy

Run commands in parallel:

How It Works

Aliases are stored in .ez_cli.json files within each directory. This keeps commands context-specific and prevents conflicts between projects. To clear all aliases in a directory, simply delete the .ez_cli.json file.

Every alias run is also recorded in a local SQLite database at ~/.ez/runs.db (override the location with $EZCLI_HOME): working directory, alias name, the command template — never substituted arguments or secret values — exit code, duration, and timestamp, plus machine context for reading the timing series later (hardware model, CPU, core counts, RAM, macOS version, a locally generated random machine ID stored at ~/.ez/machine_id, and a runs-since-boot counter — nothing derived from your hostname or username). View it with ez stats <alias> -v. Nothing is ever sent anywhere; delete the files to clear all history.

Requirements

  • macOS 15.0+

Development

Building

Running from source

Testing

Run the acceptance test suite:

The script builds the binary, runs it in an isolated temp directory, and asserts on output — nothing touches your real aliases, run history, or (aside from a dedicated canary key it cleans up) your Keychain.

Interactive TTY features (vim, less, signal handling) can't be automated; verify those manually with:

./acceptance-test-interactive.sh

Project Structure

  • ezcli/ - Source code
  • acceptance-test.sh - Automated test suite
  • acceptance-test-interactive.sh - Manual tests for interactive/TTY features

Build Requirements

  • Swift 6.0+
  • Xcode 16.4+

Xcode Setup

If building for device or distribution, set your own Apple Development Team in the Xcode project settings.

License

MIT