Skip to content

CLI reference

Every command, flag, environment variable, exit status and file. For a walk through rather than a lookup, start at the quickstart.

hmz [<command> [<args>...]]

A line naming no command opens the terminal interface. A line naming something that is not a command is a usage error listing the commands there are. Everything after the command name reaches that command untouched — --help included — so each answers for its own arguments.

There are two commands. hmz exec runs a flow, and is the one anybody types. hmz internal is the door onto the four lines humanize spawns for itself — listed rather than hidden, because a command nobody can discover is a command nobody can debug, and these are exactly what turns up in a process table when a run has gone wrong.

python -m hmz is the same command line, which is how a turn spawns itself under an anchor.

Who is reading

Every command is written twice over: for somebody at a terminal, and for a program.

  • A terminal gets colour and layout. hmz exec draws the run as it happens — which agent is working and in which conversation, what it says, the tools it runs, the sub-agents it starts, a clock at the foot while it thinks, and what the turn cost when it lands.
  • A pipe or a file gets exactly the same lines with no escape sequences in them. What each turn answered still goes to stdout and the run itself to stderr, so hmz exec … > answer.txt and hmz exec … 2>/dev/null mean what they always did.
  • A program gets --json: one JSON object per line, flushed as it is written, and nothing else on stdout at all — anything the flow prints is put on stderr for the duration, so a stray line cannot break the stream.

NO_COLOR, FORCE_COLOR and TERM=dumb are honoured, in that order of authority. See environment variables.

hmz

hmz                  # opens the terminal interface
hmz --version        # prints the installed version
hmz --help           # lists the commands

There is no command that opens the interface. Naming nothing at all is how it opens.

It opens on a run held apart from this terminal, so that closing the terminal is not what ends a day's work: a line naming no command reads whichever run is already being held in this directory and starts one where none is. It opens it in this process instead, which is also what happens with no terminal to hand over to — output going to a file, a suite driving the interface itself — and what happens if a run cannot be held at all, which is said on stderr and then done without.

It opens on whatever this workspace was last set up to run. The line says nothing about that: which flow, what drives it and what it is set up with are chosen at the prompt, and what was chosen there is what the next hmz here opens on — so a run that is always the same run is set up once rather than spelled out again by every line that reads it.

Nothing is started either: the interface opens ready, and the first thing you say is still what starts it.

hmz exec

Runs a flow in the current directory, on the agents it is given.

hmz exec -f|--flow <flow> -a|--agent <spec>[,<spec>...] [-a ...] [-c|--config <path>] [--json] <task>

<spec> := [<name>=]<cli>[@<provider>]/<model>:<effort>
Argument
-f, --flow <flow>[:<name>]Required. The flow to drive: the name of one humanize ships, <where>/<flow> for one any other place holds — a flowverse, or local/user for your own — or the path to a flow anywhere else. A file that holds several flows is said which, after a colon. See where flows live.
-c, --config <path>A YAML file of what to set the flow up with, one field per line, under the names the flow declared — only for a flow that says it can be set up. The flow's own model checks it before the first turn. One key of it is reserved: budget: is the run's allowance, a mapping of hours, tokens and dollars, and is lifted out before the flow's model sees it.
-a, --agent <spec>[,<spec>...]One for each agent the flow drives — several to an option, separated by commas, and the option repeated as often as suits. Unnamed they fill the flow's places in the order it takes them; <name>= fills the place the flow calls that. None at all for a flow whose only side is you, since nobody chooses what the person runs.
--jsonWrite the run for a program: one JSON object on stdout per thing an agent says, as it says it. See Watching a run.
<task>Required. What the flow is to have the agents do, as the text itself. Put -- before it if it starts with a dash.

Watching a run

At a terminal, the run is drawn as it happens:

console
$ hmz exec -f rlar -a claude/claude-opus-5:high -a codex/gpt-5.6-sol:high "fix the build"
● builder is working
● Bash(pytest -q tests/)
● I fixed add() and the tests pass.
✻ input 40.0k · output 1.2k · $0.61 · claude-opus-5 · builder
✻ Worked for 74s · builder

The star under a turn says what it cost: the tokens by kind, then what those came to in money where the model is one somebody lists, then which model and which agent. A model nobody lists shows the tokens and no figure at all — $0.00 beside a turn that cost something would be a claim about a bill, and a wrong one.

While a turn is thinking — which is most of a turn — a clock sits at the foot of the screen saying which agent is working and for how long, or how many turns are open where there are several. It is drawn only where a terminal is reading and escapes are wanted — a clock is written in them, cursor and all, so NO_COLOR and TERM=dumb mean no clock — and it is gone when the run is.

Piped or redirected, the same lines are written with no escape sequences in them. The run goes to stderr and what each turn answered to stdout, which is where every script written against hmz exec reads it:

sh
hmz exec -f chat -a claude/claude-opus-5:high "summarise CHANGELOG.md" > summary.txt

--json writes the run for a program instead — NDJSON, one object a line, flushed as each is written:

console
$ hmz exec -f chat -a claude/claude-opus-5:high --json "say hello" | jq -c 'select(.kind == "result")'
{"at":1789026740.6,"agent":"assistant","cli":"claude","model":"claude-opus-5","session":"1d1ff959","kind":"result","text":"Hello.","whose":"","tokens":{"claude-opus-5":2080},"spent":{"input":2000,"output":80}}
Key
atWhen it was said, as a Unix timestamp.
agentWhich agent said it, by the name the flow gave it.
cli, modelWhich backend and model it was said on.
sessionThe backend's own id for the conversation, or "" before the backend has named one.
kindbegins and ends bracket a turn; text is the agent talking, reasoning it thinking aloud, tool it using one, subagent/subagent-ends an agent it started of its own, asks it stopping to ask, failed a turn that went wrong, result the answer it ends on.
textThe words themselves.
whoseWhich of a turn's several things it is about — the backend's id for a sub-agent — and "" for everything else.
tokensWhat the turn cost, per model. Only a result carries it, and only from a backend that says.
spentThe same cost by the kind of token it went on.

Every object carries every key, whether or not it has anything to put in it. While --json is on, nothing else reaches stdout: whatever the flow prints goes to stderr instead, so one stray line cannot break the stream.

Writing an agent

claude/claude-opus-4-8:high
claude@deepseek/claude-opus-4-8:high
builder=claude/claude-opus-4-8:high
claude/claude-opus-4-8:high,codex/gpt-5.6-sol:max

One -a is one agent or a list of them separated by commas, and every -a on the line adds to the same list in the order they were written — so a flow of four agents is one option or four, whichever reads better.

  • <cli> is agy, claude, codex, dsh, grok, kimi, mimo, opencode, pi, qwen or zcode — or any CLI of your own added at /providers. Several also answer to the longer name they are installed under: antigravity, claude-code, deepseek-harness, grok-build, kimi-code, qwen-code, mimocode, mimo-code and zcode-cli.
  • <model> and <effort> are whatever that CLI is asked for — humanize does not check them against a list, so a model your account has and this documentation does not still works.
  • A model may hold slashes of its own — Kimi Code's are kimi-code/k3, and pi, opencode, mimocode and ZCode name every model as provider/id — so the CLI is read from the front and the effort from after the last colon.
  • An @ after the CLI names the provider that agent's turns run as — the account, not the model: claude@deepseek. A CLI is never spelled with an @ in it, so the two are told apart wherever an agent is written. An agent that names none runs its CLI as you already run it.
  • A <name>= before the CLI says which of the flow's agents this one is — the field name from the named tuple the flow declares, so builder=claude/claude-opus-4-8:high. Name every agent on the line or none of them: an agent with no name fills the flow's next place, which cannot be counted while the others are filled by name. A name the flow has not got, one given twice, a place left unnamed, and a name given to a flow that declared a plain tuple are each refused before anything runs, saying what the flow does declare.
  • permission= and web_search= are not settings of -a. What an agent may do and whether it may read the internet are the flow's, declared beside the agent it drives, and a line that writes one is refused before anything runs, saying where it is said instead. See Permissions and Writing a flow.
  • cli=, model=, effort=, provider=, service_tier= and config.KEY= are gone. = and , say which place an agent fills now, so the two spellings cannot both be read, and a line that writes one of those is refused saying so. A latency tier and a backend-native override are still an agent's to carry — they are set where the agent is made, from the SDK or by the flow, rather than on the line that names one.

Names beat position. A line that names its agents is put in the flow's order whatever order you wrote them in; a line that names none fills the places in the order the flow takes them. Two agents of one spelling are two agents either way, which is what makes a flow of an actor and a reviewer at one configuration what it says it is.

What is refused before anything runs

A flow that is not there, has no run, does not say how many agents it drives, or drives a different number than were given, is a usage error — reported before the first turn rather than partway into a loop with a turn's work already behind it:

console
$ hmz exec -f rlar -a claude/claude-opus-4-8:high "fix the build"
hmz exec: error: rlar: the flow drives 2 agents, 1 given

Whatever else a flow does as it is imported is the flow's own, and fails as it would anywhere.

Examples

sh
hmz exec -f ralph_loop -a claude/claude-opus-4-8:high "$(cat TASK.md)"
hmz exec -f flame_chase -a claude/claude-opus-4-8:max -a codex/gpt-5.6-sol:max "fix the build"
hmz exec -f rlar -a claude/claude-opus-4-8:high -a claude/claude-opus-4-8:high "$(cat TASK.md)"
hmz exec -f rlar -a claude/claude-opus-4-8:high,codex/gpt-5.6-sol:high "$(cat TASK.md)"
hmz exec -f rlar -a actor=claude/claude-opus-4-8:high -a reviewer=codex/gpt-5.6-sol:high "$(cat TASK.md)"
hmz exec -f flame_chase -a claude@anthropic/claude-opus-5:max -a claude@deepseek/deepseek-chat:high "fix the build"
hmz exec -f ./flows/mine -a kimi/kimi-code/k3:swarmmax "port this to asyncio"
hmz exec -f ralph_loop -a pi/openai-codex/gpt-5.5:high "$(cat TASK.md)"
hmz exec -f ralph_loop -a opencode/opencode/big-pickle:high "$(cat TASK.md)"
hmz exec -f ralph_loop -a claude/claude-opus-4-8:high -- "--force is not a flag here"
hmz exec -f humanize1:rlcr -c setup.yaml -a claude/claude-opus-5:max \
    -a codex/gpt-5.6-sol:xhigh "add undo"

Nobody is at a prompt, so an agent that stops to ask is told nobody answered and carries on.

hmz internal

hmz internal COMMAND [ARGS...]

The four lines humanize starts processes with, under one name. Nobody types one of these: each exists because starting a process needs a command line, and each takes arguments humanize renders for it.

They are listed and documented rather than hidden behind an undocumented name, because every one of them is what a person finds rather than what they run — the process under a turn in ps, the line in a backend's MCP configuration, the program a hook's error came from. A door marked internal tells you both what is there and that it is not for you; a listing that left them out would simply be untrue about what humanize runs.

Command
hmz internal anchorA turn whose work lands on another machine, and — under serve — the half that lands it.
hmz internal credA program run with its credentials answered out of an account's own directory.
hmz internal hookOne moment of a coding agent's hook table, carried to the flow whose moment it is.
hmz internal toolsA coding agent's tool calls, carried to the flow whose callbacks they are.

hmz internal anchor

Runs a coding agent on this machine whose work lands on another one. See Remote execution.

Not a command anybody types. humanize spawns it for every turn whose work lands on another machine, and the zipapp bootstrapped onto a target runs hmz internal anchor serve to answer one — the same reason hmz internal tools is a command line. It still runs when it is typed, which is what --check is for.

hmz internal anchor [options] AGENT [ARGS...]

Everything after the agent's name is the agent's own.

FlagDefault
--target URL$HUMANIZE_TARGET, else localssh://HOST, docker://CONTAINER, tcp://HOST:PORT, or local[:DIR].
--workspace PATHthis directoryThe project directory as it exists on the target.
--chdir PATH--workspaceWhere inside that workspace the agent starts, as the target names it. What a session opened at a directory comes to: the agent is put in this machine's mirror of it.
--remote-path PATH--workspaceWhere that workspace really lives on the target, if not at the same path.
--shadow PATH--workspaceThe local mirror directory. Defaulting to the workspace path is what makes the paths the agent sees the target's own.
--local-path PATHKeep this path on this machine even when it is inside the workspace. Repeatable.
--local-exec PATHRun programs under this path here rather than on the target. Repeatable.
--redirect FROM=TOAnswer this path with that one — the file it names, or everything under the directory it names — and keep what it is answered with local. What a turn under a provider is given. Repeatable.
--private NAMEKeep this variable out of what the agent's commands are run with on the target: a credential it was given to reach its model provider is its own. Repeatable.
--net {local,remote}localWhere the agent's own TCP connections go. Local keeps its model provider reachable. Commands it spawns always use the target's network.
--net-allow HOST[:PORT]With --net remote, keep connections to this host local. Repeatable.
--token TOKEN$HUMANIZE_TOKENShared secret a tcp:// target expects.
--forceoffUse the mirror directory even if it already holds unrelated files.
--nativeoffRun the CLI already installed on the target instead of supervising one here. No mirror, nothing traced: this process starts it there and carries its three streams, its signals and its exit status. The flags above that describe a mirror say nothing under it.
--hush NAMEWith --native, run the CLI on the target without this variable, whoever left it there. The other half of --private: a key in the target's own shell profile outranks the account the turn was given, and merely not sending one does not remove it. Repeatable.
--project NAME=DIRWith --native, put this directory of credentials on the target for the length of the turn and set NAME to where it landed. Written where only the target's user may read it, and removed when the turn is over. Repeatable.
--carry DIR=PATHWith --native, put this directory into the target's copy of the workspace at PATH for the length of the turn — which is how a flow's own skills get there. Nothing already at PATH is written over. Repeatable.
--installs LINEWith --native, the line that installs this CLI, said where the target has nothing to run.
--checkoffConnect, report what was found, and exit without running anything.
--log-level {debug,info,warning,error}$HUMANIZE_LOG, else warningLogging verbosity. The log goes to stderr.

Settings no session could run under — a target nobody can read, a --net that is neither, a credential bound for something that is not a variable — exit 2 the way argparse's own rejections do. A --native session whose CLI the target has not got exits 127, the status every shell uses for a command it could not find, so that whatever spawned it reads it as a CLI that is not installed.

sh
hmz internal anchor --target ssh://build-box claude
hmz internal anchor --target ssh://gpu-01 codex exec "run the test suite"
hmz internal anchor --target docker://build-container --workspace /srv/project claude
hmz internal anchor --native --target docker://build-container --remote-path /srv/project claude
hmz internal anchor --check --target ssh://build-box

hmz internal anchor serve

The other half of a session: replays on this machine what an hmz internal anchor elsewhere asks of it. Needs only a POSIX system and a recent python3 — no root, no compiler, nothing installed.

hmz internal anchor serve --export VIRTUAL[:REAL] (--stdio | --listen [HOST:]PORT) [--token TOKEN]
Flag
--export VIRTUAL[:REAL]Required, repeatable. Expose a directory. VIRTUAL is the path the agent believes it is using; REAL is where it is here.
--stdioServe one session over stdin/stdout. This is what a bootstrapped target runs.
--listen [HOST:]PORTServe TCP connections on this address. A bare port listens on 127.0.0.1.
--token TOKENShared secret required from clients. Defaults to $HUMANIZE_TOKEN.
--log-levelAs for hmz internal anchor.

--stdio and --listen are mutually exclusive, and one is required.

Listening on anything but loopback without --token is refused. An open port is equivalent to a shell on that machine — read Security.

sh
hmz internal anchor serve --listen 0.0.0.0:7777 --export /srv/project --token "$SECRET"

hmz internal cred

sh
hmz internal cred --map FROM=TO [--map ...] -- COMMAND [ARGS...]

Runs a program with some of its paths answered by others, and exits with its status. What a turn under a provider is spawned as, and what a login run for one is spawned as: the program runs here, unchanged and on this terminal, and the handful of syscalls that name one of its credential files are handed a path inside that account's directory instead.

Not a command anybody types. It is a command of its own rather than something the driver does in this process because the supervisor forks the program and takes the process's signal handling with it, which a flow pumping turns from threads of its own has none to lend.

Flag
--map FROM=TORequired, repeatable. Answer this path — the file it names, or everything under the directory it names — with that one.
--Ends the flags. Everything after it is the program and its own arguments.

A line with nothing to answer, or nothing to run, is a usage error. A run that could not be supervised exits 1 rather than running the program unsupervised: that would be a turn taken as whoever is at this machine, which is the wrong account rather than a failed turn.

hmz internal hook

sh
hmz internal hook --at <socket>

Carries one call of a coding agent's own hook table to the flow whose moment it is, and the verdict back again: it reads the call on its stdin, sends it to the flow's socket and writes the answer back out. It is what makes a refusal at PreToolUse stop the tool rather than describe one that has already run.

Not a command anybody types. A CLI takes a hook by starting a program and waiting for what it says, so there is a program — humanize writes this line into the CLI's own hook table and spawns it once per moment.

Flag
--at PATHRequired. The unix socket the flow is serving its moments on.

It exits 0 whatever the flow said, and never with the status these CLIs read as the hook itself having refused: a relay that could not reach anybody would otherwise be refusing on a flow's behalf without having asked it. A socket that is not there lets the tool through and says so on stderr, where the CLI shows it and carries on.

hmz internal tools

sh
hmz internal tools --at <socket>

Carries the tool protocol between a coding agent and the flow whose callbacks it is: it reads its stdin into the flow's socket and the flow's answers back out to its stdout, and does nothing else.

Not a command anybody types. A CLI takes a tool by starting a program, so there is a program — the same reason hmz internal cred exists. humanize spawns it and tells the backend to run it; a socket that is not there exits 1, which the CLI reads as tools being unavailable rather than as a turn that failed.

Flag
--at PATHRequired. The unix socket the flow is serving its toolbox on.

Environment variables

VariableRead by
HUMANIZE_HOMEeverythingWhere humanize keeps what outlives one run. Defaults to ~/.humanize.
HUMANIZE_TARGEThmz internal anchorDefault for --target.
HUMANIZE_TOKENhmz internal anchor, hmz internal anchor serveDefault for --token.
HUMANIZE_LOGhmz internal anchor, hmz internal anchor serveDefault for --log-level.
HUMANIZE_DAEMONhmz with no commandoff, 0 or no opens the interface in this terminal rather than holding the run apart from it. Anything else — including empty — is silence, and silence holds the run.
HUMANIZE_SENTRYeverythingon or off, answering the reporting question for one process without writing anything down. Nothing else is looked at while it is set.
HUMANIZE_WATCHDOGeverything that runs a turnHow long a turn may say nothing before the watchdog looks at it, in seconds, overriding each backend's own. 0 turns it off.
HUMANIZE_SHADOWShmz internal anchor, a container or a machine an agent works onWhere the mirrors coganchor has been pointed at are recorded. Defaults to ~/.cache/humanize/shadows.
CLAUDE_CONFIG_DIRthe traces /epics gathers, the TUI's cost readoutClaude Code's home. Defaults to ~/.claude.
CODEX_HOMEsameCodex's home. Defaults to ~/.codex.
DSH_HOMEsameDeepSeek Harness's home. Defaults to ~/.dsh.
GROK_HOMEthe model list, the cost readoutGrok Build's home. Defaults to ~/.grok.
KIMI_CODE_HOMEsameKimi Code's home. Defaults to ~/.kimi-code.
PI_CODING_AGENT_DIRsamepi's home. Defaults to ~/.pi/agent.
QWEN_HOMEsameQwen Code's home. Defaults to ~/.qwen.
XDG_DATA_HOMEthe model listWhere opencode and mimocode keep their data. Defaults to ~/.local/share.
NO_COLORevery command, the TUIHonoured. Set to anything non-empty, nothing writes an escape sequence — and it wins over FORCE_COLOR.
FORCE_COLORevery commandSet to anything but 0, a command writes colour into something that is not a terminal — which is what a CI log wants. It does not make a run believe somebody is watching it: a piped run is still written plainly in shape, just in colour.
TERMevery commanddumb is a terminal saying it could not read escapes, and is honoured as NO_COLOR is.
TEXTUAL_THEMEthe TUINames a Textual theme to use instead of humanize's own, which is your terminal's sixteen colours. A name no theme answers to is ignored.

Antigravity CLI and ZCode are the two backends whose homes cannot be moved: neither reads a variable of its own, so their state is always ~/.gemini/antigravity-cli and ~/.zcode.

A backend home that does not exist is skipped rather than being an error.

Set inside an anchored agent, so that it and the commands it spawns can tell:

Variable
HUMANIZEThe version of the half that launched it.
HUMANIZE_TARGETThe target its work is landing on.
HUMANIZE_WORKSPACEThe workspace as the target has it.

Files

PathWritten by
~/.humanize/epics/<workspace>/<datetime>-<hex>/epic.jsonlevery run of a flowWhat the run was: the flow, the agents, every session opened and as which account, how it ended. See Epics.
~/.humanize/epics/<workspace>/<datetime>-<hex>/epic.<flow>_<hex>.jsonlthe same, per flow that run calledWhat that call was, written the same way: a called flow opens sessions and calls flows of its own. The run's own record says which file each call is in.
~/.humanize/epics/<workspace>/<datetime>-<hex>/sessions/<session>/the sameA link per file each session was logged to, for reading a run back. humanize reads and writes the logs where the backend keeps them.
~/.humanize/epics/<workspace>/<datetime>-<hex>/state.jsona resumable flowWhat that flow left behind, which the next run of it picks up.
~/.humanize/epics/<workspace>/<datetime>-<hex>/profile.jsonla run of a workspace that asked to be profiledThe programs the run started, sampled while it ran.
~/.humanize/epics/<workspace>/<datetime>-<hex>/traces/export.trace.jsonexporting on /epicsThe trace of that run, gathered as it was exported. One gathered by hand is named for the moment instead.
~/.humanize/providers/<cli>/<name>/provider.jsona in /providersWhat a provider was made by, and what a turn under it runs with. 0600, in a directory at 0700.
~/.humanize/providers/<cli>/<name>/{home,user}/...the CLI's own loginThat provider's credentials, at the names the CLI keeps its own under.
~/.humanize/providers/<cli>/<name>/models.jsona in /providers, rWhat that account may name: what its endpoint serves where it has one, and what the CLI said where it has not. Never the credential either was asked under. Goes when the account does.
~/.humanize/local/<cli>.jsonwhat enter opens in /providersWhat the account this machine is signed into does when it fails: where it falls back to, and how a turn under it is tried again.
~/.humanize/acp.jsona CLI of your own, added where /providers asks which CLIThe CLIs of your own that speak the Agent Client Protocol, as {name: [argv…]}. A backend from the moment it is written.
~/.humanize/models/<cli>.jsonthe TUI, rThe same, for the CLI as you already run it.
~/.humanize/settings.yamlthe TUIWhat each workspace was last set up to run and whether its runs are profiled, and the settings that are not a workspace's — enable_sentry, the answer to the reporting question.
~/.humanize/history.jsonlthe TUIWhat has been typed at the prompt before, and where.
~/.humanize/daemons/<project>-<digest>/daemon.sockhmz with no commandThe socket a terminal reaches a held run through. 0600.
~/.humanize/daemons/<project>-<digest>/daemon.jsonthe sameWhich process is holding it, which workspace, and since when.
~/.humanize/daemons/<project>-<digest>/daemon.logthe sameWhatever could not be said through a terminal about that run — what the daemon itself could not say, and what went wrong in a process reaching for its socket.
.humanize/<run>.epic.tar.gzexport it, on a run of /epicsOne whole run, packaged up to send: its records, its session logs in full, and a manifest. 0600.
~/.humanize/flowverses/<name>/a in /flowversesA flowverse, cloned. Every flow in it is offered as <name>/<flow>.
~/.humanize/skills/<owner>-<repo>-<digest>/a flow that named oneA repository of skills a flow brings, cloned. The digest is of the URL, so two repositories of one name on two hosts are two directories. Fetched again the next time a run asks for it.
.humanize/flows/*/youThis project's own flows, offered as local/<flow>.
~/.humanize/flows/*/youYour flows in every project, offered as user/<flow>.

~/.humanize is $HUMANIZE_HOME where that is set. The directories are made by whatever writes into them.

Exit statuses

0It did what it was asked.
1It could not: the target could not be reached, the listener could not be started, a turn could not be supervised.
2The command line was wrong — argparse's own rejections, a flow that is not there or takes other agents, a malformed listen address, a non-loopback listener with no token.
130Interrupted.
the agent's ownhmz internal anchor exits with the status of the program it ran, and hmz internal cred with that of the program it supervised.

Python entry points

Every way in — the command, and every sheet of the interface — is a shell around a call you can make yourself. The layer each lives in is named in Architecture.

Every one of them is Hmz — the object the command line itself holds, which it reaches as from hmz.runtime import Hmz and which hmz.sdk hands out to whoever is calling humanize from outside:

python
from hmz.sdk import Hmz

hmz = Hmz()
hmz.exec(["-f", "ralph_loop", "-a", "claude/claude-opus-5:high", "fix the build"])
hmz.epics.trace(output="run.trace.json")
hmz.accounts.all("claude")
hmz.verses.add("humanfia/flowverse")

The layers under it are reachable directly where that is what you want — Hmz composes them and restates none of them:

python
from hmz.runtime.runner import Runner          # hmz exec
from hmz.runtime.tracing import collect        # the trace /epics gathers
from hmz.coganchor import connect      # hmz internal anchor
from hmz.coganchor import check        # hmz internal anchor --check
from hmz.daemon import running, start  # the run hmz holds apart from the terminal
  • Runner(flow, agents).run(task)Flows
  • collect(workspace, *, sessions=…, agents=…, output=…, start=…, end=…, profile=…)Tracing
  • connect(command, config) / check(config)Remote execution
  • running(workspace) / start(opens)Daemon

Released under the Apache-2.0 licence.