# 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 is `Ok(())`; every non-completion state is an [`Error`] variant, so you branch with a single `match`. - **`Claude::run_resume_or_create(&config, title, prompt, &sink)`** → `Result`. 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. Use `NoopSink` when 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. ```rust 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`.**