No description
  • Rust 94.7%
  • Nix 5.3%
Find a file
2026-09-09 23:41:50 +02:00
.forgejo/workflows add crane-backed build/clippy/test checks + forgejo CI to run them 2026-07-29 18:35:11 +02:00
examples add runnable examples 2026-08-02 03:56:09 +02:00
nix add nix flake: pinned devshell + treefmt formatter/check 2026-07-29 15:58:37 +02:00
src Cargo.toml: make libc a unix-only target dependency 2026-08-12 19:28:13 +02:00
.gitignore commit Cargo.lock, drop anyhow section from README (per mara's review) 2026-07-29 15:55:32 +02:00
Cargo.lock bump version to 0.1.1 2026-09-09 23:41:50 +02:00
Cargo.toml bump version to 0.1.1 2026-09-09 23:41:50 +02:00
clippy.toml deny clippy::unwrap_used and expect_used 2026-08-02 01:37:14 +02:00
flake.lock add crane-backed build/clippy/test checks + forgejo CI to run them 2026-07-29 18:35:11 +02:00
flake.nix add runnable examples 2026-08-02 03:56:09 +02:00
LICENSE Initial commit 2026-07-29 13:23:20 +02:00
README.md lock down the public API before 0.1.0 2026-08-02 01:47:39 +02:00

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 application types, hard-coded 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, without reimplementing subprocess plumbing, stream-json classification, or session bookkeeping yourself.

Installation

[dependencies]
hive-claude = "0.1"

Or cargo add hive-claude. The crate drives the Claude Code CLI as a subprocess, so that binary is a runtime requirement. It need not be on PATH — point Config at it directly:

Config {
    program: Some("/nix/store/…/bin/claude".into()),
    ..Default::default()
}

Shape

Two layers — reach for the high-level one:

  • InfiniteSession { name, store, policy } — a durable session that keeps itself alive across the context window. .run(&config, prompt, &sink)Result<Progress, Error> does resume-or-create, compacts reactively on overflow (compact + retry once), and proactively after a clean turn when the policy says so (optional checkpoint turn, then compact). .compact(…) forces one. Progress { created, compacted, telemetry } reports what happened — including the turn's parsed Telemetry, finalised at the result event.
  • Claude::run(&config, &attach, prompt, &sink)Result<(), Error> — the low-level driver: one turn, one Attach target (Resume / Create / Continue / OneOff). A clean turn is Ok(()); every other state is an Error variant.

Supporting pieces:

  • CompactionPolicy — decides when to compact. PercentPolicy (percent, default_window, checkpoint_prompt) compacts at a percent of the model window; NeverCompact never does.
  • Config — the invocation (model, effort, cwd, prompt/MCP files, tools, extra args, and program — which binary to spawn, defaulting to claude on PATH).
  • Sink — a trait with no-op defaults; implement what you care about to observe stream events, non-JSON stdout, and stderr. NoopSink ignores all.
  • SessionStore — locate and archive on-disk sessions by title.
  • Telemetry (context, cost, context_window, model) — everything the driver parses from a turn's stream, returned in Progress. Usage is the minimal slice (context_tokens, context_window) the policy sees.

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::{Attach, Claude, Config, Error, NoopSink};

# async fn ex() {
let config = Config { model: Some("haiku".into()), ..Default::default() };
match Claude::run(&config, &Attach::Resume("my-session".into()), "hello", &NoopSink).await {
    Ok(()) => {}
    Err(Error::PromptTooLong) => { /* caller compacts + retries */ }
    Err(Error::RateLimited)   => { /* caller parks + retries */ }
    Err(other)                => eprintln!("claude: {other}"),
}
# }

License

Apache-2.0 — see LICENSE.

AI use disclosure

This crate's code, tests, and documentation were written by an AI coding agent, with human direction and review.