| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .. | ||
| src | ||
| Cargo.toml | ||
| README.md | ||
hive-claude
A small, reusable async driver for headless claude --print (Claude Code CLI)
sessions. It spawns the CLI, streams and classifies the stream-json output,
and reports the result. It knows only about the Claude Code CLI — no
hyperhive types, policy, watermarks, or logging. Compaction, retry, and reset
policy belong to the caller.
When to use it
Reach for this crate whenever you need to run the claude CLI from Rust and
react to how a turn ended. It is the shared substrate under
hive-ag3nt's turn loop; new callers (tools, tests, other agents) should build
on it rather than shelling out to claude by hand.
Shape
Claude::run(&config, &session, prompt, &sink)→Result<(), Error>. A clean turn isOk(()); every non-completion state is an [Error] variant, so you branch with a singlematch.Claude::run_resume_or_create(&config, title, prompt, &sink)→Result<bool>. Resumes a titled session, creating it on first use.Ok(true)means a fresh session was minted.Config— the invocation (model, effort, cwd, prompt/MCP files, tools, extra args).Session— which session to attach to (Resume/Create/Continue/OneOff).Sink— a trait with no-op defaults; implement the methods you care about to observe stream events, non-JSON stdout, and stderr. UseNoopSinkwhen you only want the result.SessionStore— locate and archive on-disk sessions by title.
Error unifies the two things that can stop a turn: recognized sentinels
(PromptTooLong, RateLimited, AuthFailed, SessionNotFound) and hard
failures (Spawn, Stdin, Wait, Exit, Io). Sentinels are expected
control-flow, not crashes — the caller compacts, parks, re-auths, or creates a
session in response.
use hive_claude::{Claude, Config, Error, NoopSink, Session};
let config = Config { model: "haiku".into(), ..Default::default() };
match Claude::run(&config, &Session::Resume("my-session".into()), "hello", &NoopSink).await {
Ok(()) => {}
Err(Error::PromptTooLong) => { /* compact + retry */ }
Err(Error::RateLimited) => { /* park + retry */ }
Err(other) => eprintln!("claude: {other}"),
}
thiserror here, anyhow in the apps
This is a library, so it returns a concrete, matchable Error enum built
with thiserror: callers can tell a rate-limit from a spawn failure and act
accordingly. Libraries should never force their callers into anyhow's
type-erased error.
The applications (the hive-* binaries) use anyhow instead — at the top
level you usually only want to add context and bubble a failure up, not match
on it. A hive_claude::Error converts into an anyhow::Error for free at the
? boundary. Rule of thumb: libraries return thiserror enums, binaries
consume them with anyhow.