Skip to content

phongndo/hz

Repository files navigation

hz

Quality Release

hz is a fast terminal workflow for parallel development with agents. It creates isolated Git workspaces, makes it fast to move between them, hands off diffs safely, and runs repo-local lifecycle hooks.

Status

hz is pre-1.0. The current release is usable for local Git worktree, handoff, lifecycle, shell integration, machine-readable JSON output, and install/update workflows. Prefer --machine or --json for scripts, and expect command shapes to keep tightening before 1.0.

What hz does today

  • Creates isolated Git worktrees for parallel human or AI-agent tasks.
  • Forks the current worktree state into a detached scratch worktree.
  • Installs shell integration so hz worktree new, hz worktree fork, hz worktree cd, and hz worktree handoff can change your current directory.
  • Applies uncommitted changes between linked worktrees without forcing a commit.
  • Moves branch ownership between worktrees when both sides are clean.
  • Runs repo-local setup and cleanup hooks on explicit opt-in create/remove flags for reproducible agent workspaces.
  • Exposes JSON and --machine modes for agents and automation.
  • Lists, finds, prunes, and removes managed and unmanaged worktrees.
  • Pins managed worktrees so auto-prune will not remove them.
  • Installs and updates release binaries from GitHub releases.

Quickstart

# one-time repo setup
hz init
git add .hz
git commit -m "Add hz lifecycle config"
hz install zsh      # or bash/fish; restart or source your shell rc file

# create an isolated workspace for a task/agent and cd into it
hz worktree new fix-login

# run your terminal AI agent or manual workflow here
# ...edit files...

# hand the diff back to the linked local worktree and return there
hz worktree handoff

# review with dx if desired, then clean up
dx
hz worktree rm -f fix-login    # force is needed if the source worktree still has handed-off changes

Without shell integration, use hz worktree path <target> to print a worktree path and cd "$(hz worktree path <target>)" from your shell.

For agents and scripts, prefer the machine-readable surface:

hz --machine worktree new fix-login --repo .
hz --machine worktree list --repo .
hz --machine worktree handoff fix-login --repo .
hz --machine worktree remove fix-login --repo . --force

hz init writes repo-local lifecycle files under .hz/. Commit them before starting the task flow above so the destination worktree is clean when hz worktree handoff applies changes back to it. For a throwaway first demo where you do not need lifecycle hooks, skip hz init.

Installation

Install the latest release with the shell installer:

curl -fsSL https://raw.githubusercontent.com/phongndo/hz/main/scripts/install.sh | sh

The curl installer is the only supported install path for now. Homebrew, mise, Cargo, and other package-manager installs are deprecated; reinstall with the command above if you used one of those paths before.

The installer downloads the matching GitHub release archive for macOS or Linux, verifies its SHA-256 file with shasum or sha256sum, and installs hz to ~/.local/bin by default. Set HZ_ALLOW_UNVERIFIED=1 only when you explicitly want to install without checksum verification.

curl -fsSL https://raw.githubusercontent.com/phongndo/hz/main/scripts/install.sh | HZ_VERSION=0.6.0 sh
curl -fsSL https://raw.githubusercontent.com/phongndo/hz/main/scripts/install.sh | HZ_INSTALL_DIR=/usr/local/bin sh

Verify the installed binary:

which -a hz
hz --version

The default curl install path is ~/.local/bin/hz; if you set HZ_INSTALL_DIR, the first hz on PATH should come from that directory.

Update a curl-installed binary in place:

hz update
hz update --target-version 0.6.0

After installing the binary, install shell integration for auto-cd and completions:

hz install zsh
source ~/.zshrc

Core workflows

Worktrees

hz worktree new creates a scratch Git worktree with a human-facing handle and a UUID directory under ~/.hz/worktrees/<repo>/ by default. Pass a name or --branch to create a branch-backed worktree:

hz worktree new fix-login
hz worktree fork
hz worktree new
hz worktree ls
hz worktree pwd
hz worktree path fix-login
hz worktree cd fix-login
hz worktree cd
hz worktree rm fix-login

Use hz wt ... as a shorter alias for hz worktree ....

hz worktree new without a name generates a four-character lowercase alphanumeric handle and leaves the worktree on a detached HEAD. Managed worktrees are registered in ~/.hz/registry.json.

hz worktree pwd prints the current worktree target (local, a branch name, or a detached handle). Pass --json to include the target, repo, and path.

hz worktree fork creates a new detached worktree at the current HEAD and applies the current worktree diff there, including untracked files. The source worktree is left unchanged. Pass an optional handle to name the detached fork, or --no-diff to fork only the current HEAD without copying local changes:

hz worktree fork
hz worktree fork alt-try
hz worktree fork --no-diff

hz worktree ls, hz worktree cd, and hz worktree rm also detect unmanaged Git worktrees created by other tools. Removing an unmanaged worktree outside ~/.hz/worktrees/<repo>/ asks for confirmation because the path is not in hz's worktree namespace. Add [worktree].user_managed_roots in .hz/hz.toml for other directories that should be treated as user-managed by hz.

Detached scratch worktrees are capped at 10 by default. Creating another detached worktree auto-removes the oldest clean managed detached worktrees until the cap is satisfied. Pinned, branch-backed, unmanaged, dirty, unknown, and current worktrees are not auto-removed. If there are not enough removable worktrees, hz worktree new and hz worktree handoff --new refuse to create another detached worktree. Set [worktree].max_detached in .hz/hz.toml, or pass --max-detached <count> to hz worktree new or hz worktree handoff --new; 0 disables auto-pruning.

Branch-backed worktrees are also capped at 10 by default. Creating another branch-backed worktree auto-removes the oldest clean managed branch-backed worktrees until the cap is satisfied. Removing a branch-backed worktree removes only the checkout; the Git branch remains in the repo and can be checked out again later. Pinned, detached, unmanaged, dirty, unknown, and current worktrees are not auto-removed. Set [worktree].max_branch_worktrees in .hz/hz.toml, or pass --max-branch-worktrees <count> to hz worktree new or branch-backed hz worktree handoff --new; 0 disables auto-pruning.

Use hz worktree pin <target...> to keep managed worktrees out of auto-prune, and hz worktree unpin <target...> to make them eligible again. hz worktree ls --pinned shows only pinned worktrees; hz worktree ls --unpinned shows only unpinned worktrees. Pinned worktrees can still be removed explicitly with hz worktree rm.

Auto-pruning is enabled by default. Set [worktree].auto_prune = false to opt into keeping managed worktrees instead of deleting them at the configured limits. Dirty and current worktrees are still protected from auto-removal.

Repo config can set the default base branch for new worktrees and additional user-managed worktree roots:

# .hz/hz.toml
[worktree]
auto_prune = true
max_detached = 10
max_branch_worktrees = 10
default_base = "dev"
user_managed_roots = ["~/.codex/worktrees"]

With that config, hz worktree new feature/ui behaves like hz worktree new feature/ui --base dev. Passing --base main still wins for deliberate main-based work.

Ignored local files are not present in a fresh Git worktree. To copy selected ignored setup files into new managed hz worktrees, add a root .worktreeinclude file using Gitignore-style patterns:

# .worktreeinclude
.env
.env.local
config/secrets.json

hz copies only ignored files that match .worktreeinclude; tracked files and other untracked files are left alone. Source symlinks are skipped, and existing destination files are never overwritten.

Shell integration

hz worktree cd prints a path for scripts. To make hz worktree new and hz worktree cd change the current shell directory, run hz install <shell> once to update your shell rc file:

hz install zsh
hz install bash
hz install fish

For zsh, this updates ~/.zshrc. Restart your shell or run source ~/.zshrc after install. With the integration loaded, plain hz worktree new ... or hz worktree fork ... creates the worktree and changes into it, and hz worktree cd returns to the local repo root. --json, --path-only, and help calls still pass through to the real binary without changing directories.

The shell integration also installs completions for zsh, bash, and fish. Completions include command aliases such as hz worktree cd, hz worktree ls, and hz worktree rm, nested hz worktree ... commands, command flags such as --machine, shell names for hz init/hz shell, and live worktree targets for commands that accept them.

Human and machine-readable output

By default, commands are human-facing. They keep terminal output readable and, when shell integration is installed, commands such as hz worktree new, hz worktree fork, hz worktree cd, and hz worktree handoff can change the current shell directory.

Pass --json to data-producing commands when another process needs parseable stdout. Pass --machine to force JSON, bypass shell auto-cd, and refuse unsafe prompts in non-interactive paths:

hz worktree pwd --json
hz worktree path fix-login --json
hz --machine worktree list

For compatibility, hz agent ... remains as a machine-readable alias for the same worktree commands.

Handoff

hz worktree handoff applies the current worktree's uncommitted diff to its linked counterpart by default. From a linked worktree it applies the patch to local. From local, pass a worktree handle such as hz worktree handoff fix-login to apply the local patch there. After a patch handoff, running hz worktree handoff from local defaults back to the last linked worktree. The destination must be clean unless its current diff still matches the last patch handed off between that pair; in that case hz safely replaces it with the source diff. Source changes are left in place. With shell integration loaded, successful handoffs change into the destination worktree unless --json, --machine, --path-only, or help is passed.

Use hz worktree handoff --new to create a new detached destination worktree and apply the current patch there. Use hz worktree handoff --new fix-login to create a branch-backed destination worktree named fix-login.

Use hz worktree handoff <worktree> --branch to move branch ownership instead of applying a patch. Branch handoff is clean-only on both sides.

Diff review

Diff review now lives in the separate dx CLI. Use dx from any hz worktree when you want the terminal review UI or plain diff output:

dx
dx --staged
dx --base main
dx --pr 123
dx --patch changes.diff

Repo lifecycle

hz init initializes repo-local lifecycle config:

.hz/hz.toml
.hz/environment/setup
.hz/environment/cleanup

.hz/hz.toml declares the commands hz should run:

[worktree]
max_detached = 10
max_branch_worktrees = 10
# user_managed_roots = ["~/.codex/worktrees"]

[lifecycle]
setup = [".hz/environment/setup"]
cleanup = [".hz/environment/cleanup"]

.hz/environment/setup and .hz/environment/cleanup are executable script files. Edit them to contain the repo setup and cleanup commands an agent worktree should run. hz worktree new --setup runs the configured setup command after creating a worktree, and hz worktree rm --cleanup runs the configured cleanup command before removing one. Use --no-setup or --no-cleanup to explicitly suppress a hook when using aliases or wrapper scripts. Hook stdout is forwarded to stderr so --json and --path-only output stays machine-readable.

Lifecycle config is read from the target worktree. Commit .hz/hz.toml and any referenced scripts before opting in to hz worktree new --setup for newly created worktrees.

Config and display

hz worktree ls display is configurable from .hz/hz.toml:

[list]
headers = "auto" # auto | always | never
columns = ["marker", "target", "status", "modified", "path"]
compact_columns = ["marker", "target", "status"]

[color]
mode = "auto" # auto | always | never
scheme = "terminal"

Columns can include marker, target, branch, handle, status, base, modified, and path. Color defaults to terminal-native ANSI colors so the user's terminal color scheme decides the actual palette. Custom hz worktree ls color schemes can opt into different ANSI color names while still letting the terminal theme provide the actual colors.

See docs/config.md for the full config reference.

For compatibility, hz init <shell> still installs shell integration, but hz install <shell> is the documented command for shell setup.

CLI reference

See docs/cli.md for the full human and machine command reference, common options, and scripting notes.

Architecture

crates/hz-cli       command parsing, terminal output, install/update, and CLI UX
crates/hz-bench     headless command benchmark utilities
crates/hz-command   command facade shared by CLI and future runtime callers
crates/hz-core      shared errors and common models
crates/hz-git       low-level Git integration boundary
crates/hz-worktree  worktree domain boundary: new, path, list, handoff, remove

The command crate is the main extension seam. CLI and future runtime code should call hz-command instead of duplicating workflow logic. Provider or agent-runtime integrations can sit beside these crates without forcing the existing command surface to depend on plugin machinery.

Development

Install the repo Rust toolchain:

rustup show

Or enter the Nix development shell:

nix develop

Run local checks:

just setup
just hooks       # validate hk.pkl config
just hk-check     # run git pre-commit checks via hk
just check
just test
just build
just smoke

For contribution guidelines, dev-shell details, and CI expectations, see CONTRIBUTING.md.

CI and releases

.github/workflows/quality.yml runs rust-analyzer diagnostics, formatter, Clippy, workspace tests, a full workspace build, a headless hz-bench smoke, and installer/update smoke tests against a local release fixture. .github/workflows/release.yml builds release archives with SHA-256 checksum files for supported macOS and Linux targets when a v*.*.* tag is pushed.

License

MIT. See LICENSE.

About

Fast terminal workspaces for parallel development with agents.

Resources

License

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors

Languages