agent harness for the terminal
__ __ _ __ / / / / | |/ / / /_/ / | / / __ / / | /_/ /_/ /_/|_|
HX runs in your project directory, reads and edits your code, runs commands in a sandboxed shell, and shows you what every turn costs.
/fullscreen is opt-in.
.hx/. A project copy shadows
your own, and HX only ever reads them - it writes nothing into your repo.
/rewind restores files from content-addressed snapshots;
/compact and /resume keep long work going.
02 — the session
One turn, start to finish: a file reference, a couple of tool calls, a command it is not allowed to run, and a status bar that never stops telling you what this is costing.
Nothing was repainted. Nothing was erased.
03 — models and routes
Three routes. Which one serves a turn is decided by the prefix on the model id alone, so "what paid for that" is always answerable by reading it.
OpenRouter
anthropic/claude-sonnet-4.5
Any model OpenRouter carries, on your own API key. The default, and the route Claude models take.
per token · API key
ChatGPT Plus / Pro
openai-codex/gpt-5.6-terra
Sign in over OAuth and run Codex models against the subscription you already pay for, instead of per token.
your subscription · OAuth
Devin
devin/swe-1-6
The same arrangement for a Devin subscription, Enterprise tenants included.
your subscription · OAuth
/effort — clamped down to what the model actually advertises
the status bar appends it: gpt-5.6-terra (sub) · high
There is no /route command and no auto-selection. Routing is a prefix
on the model id; a second concept would be a second thing to be wrong about.
There is no Claude Pro/Max route. Anthropic rejects OAuth tokens unless the request impersonates Claude Code, which HX will not do. Claude models stay available through OpenRouter.
04 — safety, two layers
Rules decide what may be attempted. The OS sandbox decides what is reachable if something gets through anyway.
Shell commands are decomposed into their real segments, so a dangerous tail
cannot ride in on an allowed head. It is argument-aware too: sed -i,
find -exec and git branch -D still ask.
With no sandbox backend on the machine, the status bar says
no-sandbox rather than implying protection that is not there.
Tool(specifier) strings: Bash(git commit:*),
Read(**/.ssh/**).~/.hx/projects/<project>/settings.local.json.~/.ssh, ~/.aws and HX's own auth.json
are unreadable from inside it.permissions.allow_network opens it.
shift+tab cycles. In plan, mutating tools are not offered
to the model at all - it is not asked to behave, it cannot.
05 — context engineering
The prefix is stable so the cache stays warm. Everything volatile is injected late, onto the newest message, where it cannot move the breakpoint.
Todos, the git branch and stale-file notices land on the newest user message, with stale copies stripped. The cached prefix never moves.
Snaps to turn edges. Recent turns and todos survive verbatim; superseded messages are flagged, not deleted.
25k chars or 2000 lines. Head and tail are kept, the rest spills to the session directory and the model is handed the path.
Restores files from a content-addressed snapshot taken before every write. It reports the files it did not restore because something outside changed them, leaves shell side effects alone, and deliberately does not rewind the cost ledger - you still spent that.
06 — the prompt line
Most of the interface is one input line. What you type first decides what it is.
Commands. Everything the session can be told to do about itself, rather than about your code.
Files. Completion over the project, so a path goes into the prompt exactly as it is on disk.
Shell. Runs the command directly, without a model turn. It skips the model, not the safety layers - the rules and the sandbox still apply.
enter mid-turn
Queues. The turn finishes, then yours runs. /queue shows what is
waiting; /queue clear drops it.
alt+enter mid-turn
Steers. The model call is cut mid-stream and your message goes in instead. Tools already running are allowed to finish.
ctrl+p palette
ctrl+l model picker
ctrl+o expand tool output
shift+tab cycle modes
all rebindable in ~/.hx/keybindings.json
07 — extending it
Project copies live in ./.hx/ and shadow your own in
~/.hx/. Check them in, or don't.
.hx/skills/<name>/SKILL.md
Only the name and description sit in context. The body loads when the model
calls Skill(name), and allowed-tools can narrow the
toolset while it is active.
--- name: deploy description: Tag, build and ship a release allowed-tools: Read, Bash --- 1. Run the tests. 2. Tag the commit.
.hx/agents/<name>.md
Built in: explore, plan, general. A
subagent never receives Task, so it cannot recurse. Give it its
own model if you like.
--- name: reviewer description: Reviews a diff against conventions tools: Read, Grep model: openai/gpt-5 --- You review code. Cite file:line.
.hx/mcp.json
stdio and HTTP transports, connected concurrently with a per-server timeout. A broken server is dropped with a warning instead of taking the session down.
{
"mcpServers": {
"local": { "command": "python", "args": ["server.py"] },
"remote": { "url": "https://example.com/mcp" }
}
}
~/.hx/themes/<name>.json
Every role is required - a missing one is an error, not a silent black. The role names are pi's, so pi theme files load here unchanged.
{
"name": "mine", "dark": true,
"vars": { "green": "#b5bd68" },
"colors": { "background": "#18181e",
"success": "green" /* a vars key */ }
}
08 — fits your machine
… and defaults underneath
Permission rule lists and prompt.append are unioned
across layers. Everything else is replaced.
HX never writes into your repo. ./.hx/settings.json is read-only to
it, and "always allow" lands in ~/.hx/ instead.
| hx | interactive TUI in the current directory |
| hx -p "…" | headless; assistant text to stdout, tool activity to stderr |
| hx resume | resume the last session here, or one by id |
| hx prompt | print the system prompt this directory would use |
| hx mcp | list, add and remove MCP servers |
| hx auth | which routes have a credential, and where from |
| hx docs | the manual, or one section of it |
| hx changelog | what shipped in each version |
| hx upgrade | update to the latest release |
--model --mode --cwd --no-sandbox --system-prompt --append-system-prompt
hx docs and hx changelog ship inside the wheel. They answer
from the version you actually have installed, offline, and the system prompt points
the agent at them instead of its own recollection.
09 — install
macOS and Linux, Python 3.11 or newer.
STEP 01
The script bootstraps uv if you don't have it, pins a Python, and is safe to re-run - it upgrades in place.
$ curl -fsSL https://raw.githubusercontent.com/aletisunil/hx/main/install.sh | sh
Bootstraps uv, pins Python 3.12, installs HX as an isolated tool.
$ uv tool install hx-cli
If you'd rather not pipe a script into a shell.
$ pipx install hx-cli
Same isolated-tool arrangement, if pipx is what you already use.
$ git clone https://github.com/aletisunil/hx && cd hx $ uv sync --extra dev $ uv run hx
Runs straight from the checkout.
Command not found? export PATH="$(uv tool dir --bin):$PATH"
STEP 02
One credential for one route. They live in ~/.hx/auth.json, mode
0600, one entry each.
$ hx auth set # paste the key, hidden input
On first run HX asks for one. Get a key at openrouter.ai/keys. Pay per token.
$ hx auth login openai-codex
Needs a paid ChatGPT plan. Opens auth.openai.com and
redirects to a loopback listener; over SSH, paste the final URL back, or set
HX_LOGIN_DEVICE_CODE=1.
$ hx auth login devin
Needs a Devin subscription. Enterprise tenants sign in against their own API server.
Optional: hx auth set tavily turns on
WebSearch and WebFetch.
STEP 03
Inside a project directory. That is the whole setup.
$ cd ~/your-project $ hx
Then /model to pick one,
shift+tab to change permission mode, and /init to write an
AGENTS.md for the project.
Update later with hx upgrade.
The PyPI package is hx-cli. The command it installs is hx,
and that is what you type - the longer name never appears again after install.
The plain name hx on PyPI is registered by someone else and has no
releases, so uv tool install hx fails with "no versions of hx".
Install hx-cli.
10 — reference
Python 3.11, 3.12, 3.13, 3.14 Platforms macOS, Linux Sandbox Seatbelt, bubblewrap License MIT Version v0.2.2