π§° A collection of small terminal tools that share one config file, one sqlite database, and one dashboard design language β a task tracker, a git-worktree manager, a tmux wrapper, and a few glances that come together in a tmux "control center".
| crate | what it is |
|---|---|
htask |
a little sqlite-backed task tracker (+ hprobe, a prod-alert investigator) |
htree |
a git worktree wrapper |
hmux |
a tmux wrapper (+ a live session-switcher dashboard) |
hagent |
a dashboard of live Claude Code agents β who's working, idle, or blocked |
hsys |
a system monitor β CPU/RAM/disk/net gauges + processes/ports, with kill |
hclock |
a clock for a tmux pane β time, date, load/uptime/battery |
hlib |
shared plumbing (settings, db, theme & co.) β glue, not a tool |
skills |
agent skills for Claude Code, symlinked in by just install |
Build and install everything with just install (see below). Every
binary also takes a global --config-dir <dir> to point at a different config
home, and --version / --help. Then:
Bare htask opens the dashboard; the subcommands are the CLI over the same
store.
htask add fix the flaky login test +bug repo:api # words = title; +tag, key:value, and
# URLs / owner/repo#123 / #channel peel off
htask add review acme/api#421 # source auto-detected from the issue ref
htask add --title "literal: title" -d "a description"
htask ls # live tasks, one per line
htask ls --status todo --tag repo:api # filter by --status / --tag / --source / -a archived
htask ls --json # machine output (the inter-tool contract)
htask show 9f3ac1 # one task in full (--json too)
htask start 9f3ac1 # mark in-progress
htask done 9f3ac1 # mark done (undo β back to todo)
htask edit 9f3ac1 --add-tag prio:high --rm-tag bug # field edits; bare `edit <id>` opens $EDITOR
htask archive 9f3ac1 # the only removal β status is preservedIDs are the last 6 hex of a task's uuid; any unambiguous suffix resolves (an ambiguous one lists the candidates rather than guessing).
htask start --repo β the orchestrator. Given one or more --repo, start
does more than flip status: it marks the task in-progress, then for each repo
creates a git worktree (via htree) and opens a tmux session (via hmux) to
work in β a per-task workbench at <workbench_root>/<short-id>/<repo>, every
worktree on branch hiradp/<slug>-<short-id>, in a session named
htask-<short-id>-<slug>. Re-running resumes it: existing worktrees and the
session are reused, so start doubles as "get me back into this task".
htask start 9f3ac1 --repo api --repo web # a worktree per repo + a tmux session, then drop in
htask start 9f3ac1 --repo api --base main --branch wip # fork from main, name the branch/session yourselfDashboard keys:
| key | does |
|---|---|
j / k, β / β |
move selection |
g / G |
first / last |
enter |
detail popup |
tab |
cycle the status filter |
v |
live β archived |
s / d / u |
start Β· done Β· undo |
S |
start in repos (multi-select picker β type to filter, space toggles, ^a all, enter starts) |
o |
quick-add (words +tag key:value url) |
a |
archive (with a y/n confirm) |
b |
open the task's source in the browser |
r |
refresh Β· ? help Β· q quit |
Mouse: click selects, double-click opens the source (or the detail popup), the wheel scrolls.
A second binary that ships in the htask crate. hprobe <alertβ¦> joins the words
into a task titled by the alert and sourced prod, then opens an agent-only
tmux session (just the coding agent β no editor/git windows) seeded with a
prompt. It's idempotent per incident: re-running with the same alert resumes
the live investigation instead of spawning a duplicate (a resolved alert that
re-fires starts fresh).
hprobe checkout latency spike in us-east-1The repo it runs in and the prompt template are [hprobe] settings ({args} in
the template is replaced by the alert text).
Per-task worktrees laid out <into>/<repo>, tracked in the shared db. It's the
plumbing htask start drives, but it stands on its own.
htree add my-feature --into ~/wb/9f3ac1 --repo api # worktree on branch my-feature at ~/wb/9f3ac1/api; prints the path
htree add my-feature --into ~/wb/9f3ac1 --repo api --reuse # idempotent: reprint the path if it already exists
htree add my-feature --into ~/wb/9f3ac1 --repo owner/api --base main
htree ls # registered worktrees (--repo to filter, --json for machine output)
htree repos # repos you can add to: under repos_root + the registry (--json)
htree rm my-feature # remove the worktree (--branch also deletes the branch, --force if dirty)
htree clean # sweep up: drop stale registrations + remove clean, fully-merged worktrees--repo resolves a path, an owner/repo or bare name under [htree] repos_root, or the basename of a repo already in the registry; with none, the
repo containing the current directory.
Bare hmux is a live session-switcher dashboard; the subcommands build sessions.
hmux # dashboard: live sessions, color-coded by kind; enter switches
hmux open work ~/wb/9f3ac1/api ~/wb/9f3ac1/web # create-or-attach: a window per worktree (editor beside git) + an agent window
hmux open probe ~/wb/x/api --agent-only # build only the agent window (an incident probe)
hmux open work ~/wb/x/api --agent "claude --model opus" # override the agent window's command
hmux ctrl # the control center (see below)
hmux tools # a session of plain shells, one per [hmux] tools diropen is create-or-attach (an existing session is re-entered, never rebuilt β
this is what makes htask start resumable). It attaches when stdout is a
terminal, otherwise prints the session name (the contract orchestrators
consume). Dashboard keys: j / k move, g / G top/bottom, enter switch,
q quit.
Shows every Claude Code agent across your tmux panes and what it's doing β
blocked (needs you), working, or idle β with blocked sorted to the
top so a "needs you" never hides. State is fed by Claude Code hooks that report
into <data_dir>/agents/; install-hooks wires those up.
hagent # the dashboard; enter switches to the agent's pane, r refresh, q quit
hagent install-hooks # merge hagent's hooks into your ~/.claude*/settings.json (additive; run by `just install`)
hagent install-hooks --dry-run # show which config dirs would be written, change nothing(hagent hook is the internal writer the hooks invoke on each event β not run
by hand.)
CPU / RAM / disk / network gauges above a process-and-port list you can kill
from. No subcommands; [hsys] refresh_secs sets the idle refresh cadence
(default 2s).
Keys: j / k move, x kill, a toggle all β mine (your terminal's
processes, the default), l toggle listening-only β all, r refresh, q quit.
Time and date, with load average, uptime, and battery underneath (gathered
best-effort β a field that's missing is simply dropped). No subcommands;
[hclock] refresh_secs / status_refresh_secs tune the redraw and status
cadences. q / Esc / Ctrl-C quit.
The tools stand alone, but hmux ctrl is where they come together: one
create-or-attach tmux session whose single window is a 2Γ3 grid of the suite's
own glances β
ββββββββββββββ¬βββββββββββββ¬βββββββββββββ
β htask β hclock β hmux β top
ββββββββββββββΌβββββββββββββΌβββββββββββββ€
β hsys β a shell β hagent β bottom
ββββββββββββββ΄βββββββββββββ΄βββββββββββββ
β with the cursor landing in the bottom-middle shell to drive from. htask and
hmux are fixed; the clock, the bottom-left (hsys), and the bottom-right
(hagent) panes run commands you can swap in [hmux] config. Because it's
create-or-attach, it's a stable home you drop back into rather than rebuild.
Its companion hmux tools opens a second session of plain shell windows, one
per directory listed in [hmux] tools β the repos you like to keep a terminal
in. Both pair well with a tmux key binding of your own (the author binds
prefix + ! to hmux ctrl and prefix + @ to hmux tools).
A macOS/Linux suite that leans on tmux and git (and lsof behind hsys,
uptime / pmset behind hclock); build it with a Rust toolchain.
Everything goes through just:
just # list the recipes (this, but fresher)
just install # binaries onto PATH, seed config, symlink agent skills, wire hagent hooks
just fix # auto-fix: clippy suggestions, then fmt
just check # fmt + clippy, read-only, warnings are errors
just test # run the tests
just ci # check + test β the whole gauntletIf it's not in the justfile, you probably don't need it.
One file for the whole suite: ~/.config/htools/config.toml (under
$XDG_CONFIG_HOME if set). just install seeds it from
config/default.toml, never clobbering an existing one,
and that same default is embedded into every binary as a floor β so a missing or
sparse file still loads.
Top-level keys are suite-wide; each [tool] section is that tool's own. Values
layer embedded defaults β the file β HTOOLS_* environment variables (__
separates nested keys, e.g. HTOOLS_HTREE__REPOS_ROOT=/srv/repos).
data_dir = "~/.local/share/htools" # suite-wide: holds htools.db and agents/
refresh_secs = 1.0 # suite-wide: dashboards' idle refresh, seconds (per-tool override)
[htree]
repos_root = "~/Code" # repos live at <repos_root>/<owner>/<repo>
[htask]
workbench_root = "~/.local/share/htools/workbench" # start --repo lays out <root>/<short-id>/<repo>
[hmux]
editor = "nvim" # each worktree window: editor (run as `<editor> .`) beside git
git = "lazygit"
agent = "claude" # the agent window's command
clock = "hclock" # ctrl's top-middle pane
ports = "hsys" # ctrl's bottom-left pane
agents = "hagent" # ctrl's bottom-right pane
tools = [] # `hmux tools` windows, one per existing dir (leading ~ expands)
[hprobe]
repo = "probe" # the repo an investigation runs in
prompt = "Can you investigate {args}" # the agent's seed prompt; {args} = the alert text
[hsys]
refresh_secs = 2.0 # slower: a refresh walks procs and shells to lsof
[hclock]
refresh_secs = 0.25 # redraw cadence β sub-second so the seconds tick smoothly
status_refresh_secs = 5.0 # load/uptime/battery re-read, far slowerMIT β see LICENSE.