v1.0.0 — open source & MIT licensed

Ship faster from your terminal.

Hanif CLI bundles the chores that slow your day — syncing, branching, squashing, versioning, multi-account git setup, env vars, and SVG conversion — into short, friendly commands that just work.

Zero runtime dependencies macOS · Linux · WSL Pure bash · auditable
curl -fsSL https://raw.githubusercontent.com/hanif-mianjee/hanif-cli-tools/main/install.sh | bash
Installs in under 10 seconds Read the script before you pipe it 30+ commands & aliases
7Command groups
30+Commands & aliases
1Curl to install
0Runtime deps
Features

Built for the work you actually do

Every command was born from a real, repeated annoyance. No clever‑for‑clever‑sake; just the shortcuts a senior engineer would write — and then never share.

One‑shot git sync

hanif sync updates main, rebases your branch, and prunes merged branches — the choreography you do ten times a day, in one breath.

See git commands →

Smart branch names

hanif nf add user auth becomes feature/add_user_auth. Ticket numbers are detected; names get sanitized; no quotes required.

Naming examples →

Interactive squash

Pick the commit to squash into from a numbered list. Hanif preserves every squashed hash in the final message — so review history stays auditable.

See squash workflow →

SemVer with RC flow

hanif bv minor bumps version everywhere — package.json, Cargo.toml, pyproject.toml — tags it, and stages an RC. hanif bv release promotes.

Versioning workflow →

Persistent env vars

hanif env set API_KEY=… writes to a managed file and wires your shell once. Secrets are masked in list; values are shell‑quoted, never eval‑ed.

Env management →

Multi‑account git, solved

hanif gsetup work generates an SSH key, writes a per‑profile gitconfig, and adds an includeIf so git auto‑switches identity by directory.

Setup walkthrough →

SVG → PNG, batched

hanif svg convert icon.svg 16,32,64 produces every size you need. hanif svg chrome emits a complete extension icon set.

SVG examples →

One‑step .gitignore

hanif gi .env appends to .gitignore AND removes the file from git tracking — without deleting it from disk.

Gitignore examples →

Self‑updating

A daily background check whispers when a new version is out. hanif self-update upgrades in place — no package manager required.

Updates →
Command reference

Every command, with real‑world scenarios

Each command below shows the syntax, options, and at least one realistic scenario you'll recognize from your own workflow. Click Copy on any block to drop it into your terminal.

Git workflow — sync, up, upall, clean, rb, pull, st, amend

The everyday choreography of working on a feature branch — wrapped up.
hanif synchanif uphanif upallhanif cleanhanif rb <branch>hanif pullhanif sthanif amend

Scenario: Monday morning catch‑up

You return after the weekend. Your team merged 14 PRs. You have a feature branch in flight. Instead of fetching, switching, pulling, switching back, and rebasing by hand:

$ hanif sync
→ Updating main…
✓ main is up to date with origin/main
→ Rebasing feature/checkout-v2 onto main
✓ Rebase complete — 7 commits replayed
→ Cleaning merged branches…
✓ Deleted 4 stale branches: feature/login-bug, feature/spinner, …

Scenario: Start a new ticket without reaching for quotes

hanif nf sanitizes your description, lower‑cases it, extracts ticket IDs, and enforces a 60‑char ceiling so your CI doesn't choke on absurd refs.

$ hanif nf add user authentication
 Switched to a new branch feature/add_user_authentication

$ hanif nf "OM-755: fix login bug on Safari"
 Switched to a new branch feature/om-755_fix_login_bug_on_safari

$ hanif nf JIRA-123 quick patch
 Switched to a new branch feature/jira-123_quick_patch

Scenario: Refresh every local branch (long PR review cycles)

Working a release branch alongside main? hanif upall walks every local branch, fast‑forwards what it can, and reports any branch that diverged so you can rebase it intentionally.

$ hanif upall
 main           updated (3 commits)
 release/2025-q2  updated (12 commits)
 feature/billing  diverged — rebase manually with hanif rb main

Scenario: Amend without re‑typing the message

You forgot to git add one file. hanif amend stages everything and amends with the existing message. Pass a string to override.

$ hanif amend                       # keep the existing message
$ hanif amend "fix: handle null user in middleware"

hanif squash — interactive commit squashing

Pick a commit to squash into — or pass a hash (or a hash range) directly. Hanif handles the rebase and writes a clean message that preserves every original commit hash.
hanif squashhanif squash 5hanif squash <hash>hanif squash <older> <newer>hanif squash --help

Scenario: PR with 9 messy commits, ready for review

You've been WIP‑committing all afternoon. Reviewers want one clean commit. hanif squash 9 shows the last 9 commits; pick the one to squash into and (optionally) supply a custom message.

$ hanif squash 9
  1) c1d2e3f  fix tests
  2) a3b4c5d  more tweaks
  3) 9f8e7d6  WIP refactor
  ...
  6) 1a6c6d8  Third commit       ← squash into this one

Custom message? (blank to use commit's own message):
OM-1200 Major refactor

 Squashed 5 commits.

The resulting commit body keeps every original hash for full traceability:

OM-1200 Major refactor

* c1d2e3f fix tests
* a3b4c5d more tweaks
* 9f8e7d6 WIP refactor
* ef3798f Fourth commit
* a524b8f Fifth commit

Scenario: Quick "fixup" cleanup

Just two stray fixup commits to fold in? Skip the count — the default is the last 20 commits, more than enough for nearly any feature branch.

$ hanif squash

Scenario: You already know the base hash

Skip the picker entirely. Pass the commit hash and Hanif squashes everything from that commit through HEAD into one — same custom-message prompt, no scrolling through a list.

$ hanif squash 1a6c6d8

Scenario: Squash a range, keep the trailing commits

Pass two hashes — <older> <newer> — and Hanif squashes the inclusive range, then cherry-picks every commit that came after <newer> back on top. Requires a clean working tree and a checked-out branch; the original HEAD is printed so you can recover via git reset --hard if anything goes wrong.

$ hanif squash 1a6c6d8 ef3798f

hanif bv — semantic versioning with RC workflow

Bumps the version in every file you've configured, commits, tags, and (optionally) pushes. Speaks Node, Python, Rust, Java, and friends out of the box.
hanif bvhanif bv inithanif bv patch|minor|majorhanif bv rchanif bv release

Scenario: Cut your first release candidate

A polyglot repo with package.json, Cargo.toml, and a __init__.py:

$ hanif bv init
 Detected: Node.js · Rust · Python (poetry)
 Wrote .bumpversion.cfg with current_version = 0.4.2

$ hanif bv minor
 0.4.2 → 0.5.0-rc0
 Updated package.json, Cargo.toml, src/myapp/__init__.py
 Tagged v0.5.0-rc0
Push tag now? [Y/n]

Scenario: Iterate on an RC, then promote to GA

Bake the release candidate, fix bugs, bump rc as needed, and promote when QA signs off.

$ hanif bv rc      # 0.5.0-rc0 → 0.5.0-rc1
$ hanif bv rc      # 0.5.0-rc1 → 0.5.0-rc2
$ hanif bv release # 0.5.0-rc2 → 0.5.0  ✓ tagged & ready

Scenario: Hot patch on a Friday afternoon

$ hanif bv patch   # 0.5.0 → 0.5.1-rc0  → iterate or release

Tag conflicts? Hanif detects them and asks before clobbering. Pre‑flight verification runs before any file is rewritten so you never end up with a half‑bumped repo.

hanif env — persistent environment variables, the safe way

Writes to a Hanif‑managed file and asks before touching your shell profile. Secrets are masked. Your input is never eval‑ed.
hanif e sethanif env listhanif env gethanif env unsethanif env sourcehanif env path

Scenario: Set an API key once, use it everywhere

$ hanif env set OPENAI_API_KEY=sk-abc123…
First run — append source line to ~/.zshrc? [Y/n] y
 Saved. Open a new shell or run eval $(hanif env source)

Scenario: List your env, secrets masked by default

$ hanif env list
┌──────────────────┬───────────────────────┐
│ KEY              │ VALUE                 │
├──────────────────┼───────────────────────┤
│ DATABASE_URL     │ postgres://…/db       │
│ OPENAI_API_KEY   │ sk-•••••••••••••••••  │
│ STRIPE_SECRET    │ sk_live_••••••••••••• │
└──────────────────┴───────────────────────┘

Anything matching *TOKEN*, *SECRET*, *PASSWORD*, *KEY*, or *API* is masked. Reveal a single value with hanif env get OPENAI_API_KEY.

Scenario: Move to a new machine

Your env file lives at ~/.hanif/env.sh (or ~/.hanif/env.fish for fish users). Copy it across, run hanif env path to confirm wiring, and you're done.

$ hanif env path
env file : ~/.hanif/env.sh
profile  : ~/.zshrc  ✓ wired

hanif gsetup — multi‑account git in one shot

Generates a dedicated SSH key, writes a per‑profile gitconfig, and uses includeIf so git auto‑switches identity by directory.
hanif gsetup workhanif gsetup personalhanif git-setup freelance

Scenario: Day‑1 on a new laptop with work + personal accounts

Run it twice — once per identity:

$ hanif gsetup work
Repos directory? [~/code/work]
Name?           [Hanif Mianjee]
Email?          hanif@acme.com
 Generated ~/.ssh/id_ed25519_work
 Wrote   ~/.gitconfig-work
 Appended includeIf block to ~/.gitconfig
 Add this public key to GitHub / Azure DevOps:
ssh-ed25519 AAAA…work

$ hanif gsetup personal   # same flow, ~/code/personal

Scenario: Clone a work repo and just… commit

Anything cloned under ~/code/work/ automatically uses your work name, email, and SSH key — no per‑repo configuration, no core.sshCommand incantations.

$ cd ~/code/work
$ git clone git@github.com:acme/billing.git
$ cd billing
$ git config user.email
hanif@acme.com   # ✓ correct identity, automatically

Re‑running hanif gsetup work updates the existing includeIf block in place — no duplicates, ever.

hanif gi — ignore & untrack in one step

Adds the path to .gitignore and runs git rm --cached for you. Files stay on disk; only the index entry is removed.
hanif gihanif gitignore

Scenario: You committed .env by mistake

$ hanif gi .env
 Added .env to .gitignore
 Removed from git index (file kept on disk)
 Don't forget to commit: git commit -m "chore: ignore .env"

Scenario: Patterns and folders

$ hanif gi node_modules/
$ hanif gi "*.log"
$ hanif gi .DS_Store

Already in .gitignore? Hanif notices and skips. No .gitignore file? It creates one.

hanif svg — convert SVG to PNG at every size you need

Auto‑detects rsvg-convert, Inkscape, or ImageMagick — uses whichever is available.
hanif svg converthanif svg chrome

Scenario: App icon at multiple sizes

$ hanif svg convert icon.svg 16,32,64,128,256
 Wrote icon-16.png  icon-32.png  icon-64.png  icon-128.png  icon-256.png

Scenario: A complete Chrome extension icon set

$ hanif svg chrome icon.svg
 Wrote icon-16.png icon-32.png icon-48.png icon-128.png to ./icons/

Scenario: Custom prefix and output directory

$ hanif svg convert logo.svg 100,200,400 --prefix brand -o dist/

hanif self-update & hanif help

Stay current. Get help on any command, anywhere.
hanif self-updatehanif helphanif help <topic>hanif <cmd> --help
$ hanif self-update
 Current: 0.4.0   Latest: 1.0.0
 Updated successfully. Run hanif help to see what's new.

$ hanif help squash
$ hanif bv --help
Why Hanif

Designed by a developer who got tired of typing the same 11 things

Hanif CLI is opinionated where it matters and out of your way everywhere else. Here's what makes it stick.

Fast — written in pure bash

No Node, Python, or runtime to install. Each command is a tiny shell function that runs in milliseconds and is easy to audit.

Safe by default

Inputs are validated with strict regexes, values are printf '%q'‑quoted, secrets are masked, and every destructive write is preceded by a .bak.

Cross‑platform

Macs, Linux servers, GitHub Codespaces, WSL on Windows — Hanif uses portable sed wrappers and tested fallbacks across shells (bash, zsh, fish).

Extensible — add your own commands

Drop a file in lib/commands/, call register_command, done. The dispatcher auto‑discovers it. No edits to the entry point.

Tested & CI‑backed

A growing test suite runs on every push. Tests are isolated to a temp HOME so your machine is never touched.

Friendly output

Color, icons, boxes, and aligned tables when your terminal supports them — clean ASCII when it doesn't (or when NO_COLOR is set). Pipe‑safe by design.

Installation

Up and running in under a minute

Pick the path that matches your taste. All paths give you the same hanif binary on your $PATH.

One‑liner (recommended)

curl -fsSL https://raw.githubusercontent.com/hanif-mianjee/hanif-cli-tools/main/install.sh | bash

Installs to ~/.hanif-cli and links hanif into ~/.local/bin. The script is short and human‑readable — feel free to read it on GitHub before piping.

Manual install (full control)

$ git clone https://github.com/hanif-mianjee/hanif-cli-tools.git ~/.hanif-cli
$ ln -s ~/.hanif-cli/bin/hanif ~/.local/bin/hanif
$ hanif version

Make sure ~/.local/bin is on your PATH. Add export PATH="$HOME/.local/bin:$PATH" to your shell profile if it isn't.

Verify your install

$ hanif version
hanif 1.0.0
$ hanif help

Don't see hanif? Open a new terminal so your PATH is reloaded, or run source ~/.zshrc (or your equivalent).

Uninstall (we'll miss you)

$ rm -rf ~/.hanif-cli ~/.local/bin/hanif
# optional: remove ~/.hanif/ if you used hanif env
Loved by busy developers

Quiet superpower for your terminal

Real reactions from people who type git commands hundreds of times a week.

"hanif sync alone saves me ten keystrokes and one context switch every hour. The smart branch names mean I stopped quoting things in zsh forever."
PA Priya A. — Senior Engineer
"The interactive squash is what I'd build if I had time to build it. The fact that it preserves every original commit hash in the message is *chef's kiss*."
MK Marcus K. — Tech Lead
"I have four GitHub accounts. hanif gsetup replaced two pages of notes I'd been copy‑pasting for years. Just works."
SY Sora Y. — Indie Dev
FAQ

Questions, answered

Is it safe to pipe a script from curl into bash?
It's only as safe as the source. Hanif's installer is short, hosted from this repo on GitHub, and easy to read end‑to‑end. If you'd rather inspect first, read install.sh on GitHub or use the manual install above.
Does Hanif need root or sudo?
No. Everything installs into your $HOME (~/.hanif-cli + a symlink in ~/.local/bin). Uninstall is rm -rf.
Will it touch my dotfiles without asking?
Only with explicit confirmation. hanif env appends a single source line to your shell profile after a "y/N" prompt; hanif gsetup appends an includeIf block to ~/.gitconfig wrapped in clear markers (and backs the file up first).
Does it work on Windows?
Through WSL (Windows Subsystem for Linux), yes — exactly like Linux. Native cmd.exe / PowerShell isn't supported.
What shell do I need?
Bash 4+ for the engine. Hanif itself integrates with bash, zsh, and fish for env wiring.
How do I keep Hanif up to date?
Run hanif self-update. A daily background check whispers when there's a new release; you can disable it by setting HANIF_SKIP_UPDATE_CHECK=1.
Can I add my own commands?
Absolutely — that's the point. Drop a file in lib/commands/your-cmd.sh that calls register_command …, and the dispatcher will pick it up automatically. See CONTRIBUTING.md.
License?
MIT. Use it at work, fork it, repackage it — go wild.

Add Hanif to your terminal in 10 seconds

Stop typing the same git incantations. Start shipping.