Skip to main content

heru

Unified headless CLI for coding agents. One interface, one JSONL event envelope, one resume mechanism — regardless of which underlying agent you use:

Why

Every coding-agent CLI has its own flags, its own event format, its own notion of "session", its own quota endpoint, and its own quirks. If you want to drive them headlessly from a script or higher-level orchestrator, you end up writing a separate wrapper for each — and when a new one ships you start over.

heru is that wrapper, once. Point it at any supported CLI and get the same shape out.

Usage

# Fresh run
heru codex "summarize the diff between main and my branch"

# Resume a prior session
heru codex --resume <session-id> "now also run the tests"

# Different engine, same CLI shape
heru claude "find the bug in src/foo.py"

# Apply a launch profile without changing the engine/parser
heru codex --profile zodex "run the subagent smoke test"

All output is streamed as unified JSONL by default. Pass --raw to get the engine's native JSON/JSONL stream back for debugging.

The engine name is positional. The legacy --engine CLI form was removed in v2.0.0; see CHANGELOG.md and docs/migrations/2.0.0-remove-legacy-engine-flag.md.

Cross-provider usage/quota reporting lives in quse. Use quse directly for normalized quota windows.

Launch Profiles

Use --profile (-p) when an engine should launch with a different wrapper, config directory, environment cleanup, or preflight command while keeping the same engine parser and unified event output.

Profiles are read from ~/.config/heru/profiles.toml, or from the path in HERU_PROFILES_FILE.

Example for a Z.AI-routed Codex profile managed by ~/git/.agents:

[profiles.zodex]
engine = "codex"
command = "codex"
unset_env_file = "~/git/.agents/config/codex/zodex_env_unset.txt"
preflight = [["~/git/.agents/scripts/zodex_start_proxy.sh"]]

[profiles.zodex.env]
CODEX_HOME = "~/.zodex"

If you have a real wrapper executable, use it as command = "zodex" instead.

Then run:

heru codex --profile zodex "spawn one Python subagent and summarize the result"

Claude-style profile wrappers work the same way:

[profiles.zlaude]
engine = "claude"
command = "claude"
unset_env_file = "~/git/.agents/config/claude/zlaude_env_unset.txt"

[profiles.zlaude.env]
CLAUDE_CONFIG_DIR = "~/.zlaude"

Unified Event Schema

Each stdout line is a UnifiedEvent JSON object with these common fields:

  • kind: message, tool_call, tool_result, usage, error, status, or continuation
  • engine: the emitting engine name (codex, claude, copilot, gemini, opencode, goz)
  • sequence: zero-based event order within the run
  • timestamp: ISO-8601 timestamp for the emitted event
  • role: optional role (assistant, user, system)
  • content: assistant or status text when present
  • tool_name: tool identifier for tool-related events
  • tool_input: serialized tool input when present
  • tool_output: serialized tool output when present
  • error: error text when present
  • usage_delta: per-event usage fields extracted from the provider payload
  • continuation_id: session/thread identifier when the engine emits one
  • raw: the original provider-native payload

Example:

{"kind":"message","engine":"claude","sequence":0,"timestamp":"2026-04-10T19:00:00+00:00","role":"assistant","content":"Done.","raw":{"type":"assistant","message":{"content":"Done."}}}

Install

pip install heru
# or
uv add heru

Status

v2.0.0 is a physical extraction from litehive, which uses heru as its engine execution layer. Several things from the vision above are still in flight:

  • Unified JSONL output format — heru now emits one documented event envelope across all supported engines by default.
  • Unified --resume across all engines.
  • Positional engine argument (heru codex <prompt>).
  • Standalone sandboxing — heru currently executes agents directly; sandbox integration is a follow-up.

Layout

heru/
  adapters/    per-engine CLI wrappers (codex, claude, copilot, gemini, opencode, goz)
  base.py      ExternalCLIAdapter base class + CLIInvocation
  types.py     shared engine/stream/usage types
  main.py      entrypoint for `heru <engine> <prompt>`

tests/         unit tests for adapters, stream parsing, inactivity timeout

tests/contract/ is the standalone public-API contract suite. Treat it as the "thou shalt not break" directory: changing assertions in that folder is a semver-major change and requires the full breaking-change checklist below.

API Contract

The names below are heru's stable public contract. Changes to their signature, return shape, required fields, or documented behavior are breaking changes and require a semver-major release.

Public entrypoints

  • heru.get_engine(name): resolves a stable engine name such as codex or claude to the adapter instance heru will execute.
  • heru.ENGINE_CHOICES: lists the stable engine names accepted by the CLI and get_engine.
  • heru.main.main(argv=None): provides the public heru CLI entrypoint and preserves the supported argument contract.
  • heru <engine> --profile <name> <prompt>: applies a launch profile from ~/.config/heru/profiles.toml or HERU_PROFILES_FILE without changing engine identity.

Base adapter contract

  • heru.base.ExternalCLIAdapter: base class callers may subclass against; heru preserves its constructor shape and override points as the adapter contract.
  • heru.base.CLIInvocation: immutable invocation description containing argv, cwd, env, and optional stdin payload for a launch.
  • heru.base.CLIExecutionResult: immutable execution record containing exit status, stdout/stderr, pid, and transcript accessors for a finished run.

Engine adapters

  • heru.adapters.codex.CodexCLIAdapter: stable adapter for Codex CLI command building, transcript rendering, usage extraction, and continuation discovery.
  • heru.adapters.claude.ClaudeCLIAdapter: stable adapter for Claude Code invocation and unified stream parsing.
  • heru.adapters.copilot.CopilotCLIAdapter: stable adapter for GitHub Copilot CLI invocation and stream parsing.
  • heru.adapters.gemini.GeminiCLIAdapter: stable adapter for Gemini CLI invocation, usage extraction, and continuation parsing.
  • heru.adapters.opencode.OpenCodeAdapter: stable adapter for OpenCode CLI invocation and stream parsing under the current exported class name.
  • heru.adapters.goz.GozCLIAdapter: stable adapter for Goz CLI invocation, transcript extraction, and continuation parsing.

heru.types models

  • EngineUsageWindow: normalized usage-window counters and reset metadata extracted from provider output.
  • EngineUsageObservation: normalized per-run usage or quota observation reported by adapters.
  • UnifiedEvent: stable JSONL event schema emitted across engines.
  • LiveEvent: a UnifiedEvent instance used inside live timelines.
  • LiveTimeline: ordered live-event container with summary counts and task/subagent metadata.
  • ResourceLimitEvent: normalized process-resource failure details attached to stage reports.
  • RuntimeEngineContinuation: stable continuation/session handle for resume flows across engines.
  • SubagentRef: stable reference to a spawned subagent as reported in pipeline payloads.

Internal modules

The modules below are internal implementation details and may change without notice in any release:

  • heru._continuation
  • heru.adapters._codex_impl
  • heru.adapters._claude_impl
  • heru.adapters._copilot_impl
  • heru.adapters._gemini_impl
  • heru.adapters._opencode_impl
  • heru.adapters._goz_impl

The same rule applies to private helpers and any _-prefixed name in a public module unless this README explicitly lists it as part of the stable contract.

Stability Matrix

heru version Public API breakage
v0.1.0 Initial extracted public contract. No documented breaking public API changes yet.
v1.0.0 Breaking change: provider-specific quota dataclasses and CLI fields replaced by unified short_term / long_term usage windows.
v2.0.0 Breaking change: removed the legacy prompt-first --engine form; positional heru <engine> <prompt> is now required.
v2.1.0 Breaking change: removed heru usage; use quse for normalized quota reporting.

Submitting A Breaking Change

If you need to break any public API listed above:

  1. Bump heru's semver major version in pyproject.toml.
  2. Add a CHANGELOG entry that names the broken public API and the replacement.
  3. Add a migration note for litehive and any other known consumer that relies on the changed contract.

Tests

uv run pytest

tests/contract/ is included in the default uv run pytest discovery. Those tests are fixture-only and are intended to pass in a fresh heru-only virtualenv without litehive, engine CLIs, or network access.

If a PR changes any assertion under tests/contract/, that PR is changing the documented public contract and must also:

  1. Bump heru's major version in pyproject.toml.
  2. Add a CHANGELOG entry for the break.
  3. Add a migration note describing what downstream users, including litehive, must change.

Development

Install the versioned git hook with:

./scripts/install-hooks.sh

The installer symlinks scripts/pre-commit.sh into your real git hook directory using git rev-parse --git-path hooks/pre-commit, so it works from normal checkouts and git worktrees.

When staged files touch heru/ or tests/, the hook treats that commit as a potential change to heru's public contract with litehive and runs this focused smoke suite in the litehive repo:

cd ../litehive && uv run pytest -q \
  tests/test_runner_workflow.py \
  tests/test_engine_variants_and_timeline.py \
  tests/test_heru_cli.py \
  tests/test_observability_and_status.py

The hook looks for litehive in a sibling checkout at ../litehive, then falls back to LITEHIVE_REPO. If neither exists, it prints a warning and skips the smoke run so standalone heru users are not blocked.

The smoke suite is intended to protect the editable-install contract between heru and litehive: litehive imports heru directly from your local checkout, so breaking heru method names, CLI argv shape, or event schema can regress litehive immediately. If you need to bypass the hook intentionally, commit with git commit --no-verify, but that should be the exception rather than the default workflow.

Metadata

Release files for heru 2.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for heru 2.1.1
File Size Uploaded Provenance
heru-2.1.1.tar.gz 1.0 MB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for heru 2.1.1
File Interpreter ABI Platform
heru-2.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 1.1 MB

Release files / heru-2.1.1.tar.gz

Download URL heru-2.1.1.tar.gz
Size 1.0 MB
Tags Source
SHA-256 checksum
How to use checksums
34ad5570c4f2faf00d6c33b064b99555d04a9ed66124c528b7ffb648ce2da783
BLAKE2b-256 checksum
How to use checksums
3ec1194725c29f12bf43a913e8fa4796711fe9e701e16ab716d30fa08b635a29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Release files / heru-2.1.1-py3-none-any.whl

Download URL heru-2.1.1-py3-none-any.whl
Size 52.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e10d361f96fb1b156d9b1ef55a7d79f43d59313c1cc4dc9c59e458e488e197e1
BLAKE2b-256 checksum
How to use checksums
03b4d90c9986143320e0fec779f0724d55ba6412a726fd0a79942520ea3916a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.13.12

Security for heru 2.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

2.1.1 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page