pub mod claude;
pub mod codex;
pub mod grok;
mod pack;
pub use claude::ClaudePack;
pub use codex::CodexPack;
pub use grok::GrokPack;
pub use pack::{Pack, PackContext};
static GROK: GrokPack = GrokPack;
static CLAUDE: ClaudePack = ClaudePack;
static CODEX: CodexPack = CodexPack;
const PACKS: &[&(dyn Pack + 'static)] = &[&GROK, &CLAUDE, &CODEX];
#[must_use]
pub fn available() -> Vec<&'static str> {
PACKS.iter().map(|pack| pack.name()).collect()
}
pub fn resolve(name: &str) -> Result<&'static dyn Pack, UnknownHarness> {
PACKS
.iter()
.copied()
.find(|pack| pack.name() == name)
.ok_or_else(|| UnknownHarness {
requested: name.to_owned(),
available: available(),
})
}
#[derive(Debug, thiserror::Error)]
#[error("unknown harness `{requested}`; available: {}", .available.join(", "))]
pub struct UnknownHarness {
pub requested: String,
pub available: Vec<&'static str>,
}
#[cfg(test)]
mod tests {
#![allow(clippy::unnecessary_literal_bound)]
use super::*;
use async_trait::async_trait;
use locode_host::{Host, HostConfig};
use locode_protocol::{ContentBlock, Message, Role};
use locode_tools::{Registry, Tool, ToolCtx, ToolError, ToolKind, ToolOutput};
use serde::Serialize;
use serde_json::{Value, json};
use std::path::PathBuf;
use std::sync::Arc;
use tokio_util::sync::CancellationToken;
fn test_host() -> (tempfile::TempDir, Arc<Host>) {
let dir = tempfile::tempdir().unwrap();
let host = Arc::new(Host::new(HostConfig::new(dir.path())).unwrap());
(dir, host)
}
#[derive(Serialize)]
struct EchoOut {
echoed: String,
}
impl ToolOutput for EchoOut {
fn to_prompt_text(&self) -> String {
self.echoed.clone()
}
}
struct Echo;
#[async_trait]
impl Tool for Echo {
type Args = Value;
type Output = EchoOut;
fn kind(&self) -> ToolKind {
ToolKind::Shell
}
fn description(&self) -> &str {
"echo"
}
async fn run(&self, _ctx: &ToolCtx, args: Value) -> Result<EchoOut, ToolError> {
Ok(EchoOut {
echoed: args.to_string(),
})
}
}
struct FakePack;
impl Pack for FakePack {
fn name(&self) -> &'static str {
"fake"
}
fn register(&self, _host: &Arc<Host>, registry: &mut Registry) {
registry.register("alpha", Echo);
registry.register("beta", Echo);
}
fn preamble(&self, _ctx: &PackContext) -> Vec<Message> {
vec![Message {
role: Role::System,
content: vec![ContentBlock::Text {
text: "fake pack".into(),
}],
}]
}
}
struct DupPack;
impl Pack for DupPack {
fn name(&self) -> &'static str {
"dup"
}
fn register(&self, _host: &Arc<Host>, registry: &mut Registry) {
registry.register("alpha", Echo);
registry.register("alpha", Echo); }
fn preamble(&self, _ctx: &PackContext) -> Vec<Message> {
Vec::new()
}
}
fn ctx(headless: bool) -> PackContext {
PackContext {
cwd: PathBuf::from("/repo"),
os: "linux".into(),
shell: "/bin/bash".into(),
date: "2026-07-18".into(),
headless,
is_git_repo: false,
model: None,
os_version: None,
timezone: None,
strip_identity: false,
}
}
#[test]
fn resolve_grok_returns_grok_pack() {
assert_eq!(resolve("grok").unwrap().name(), "grok");
}
#[test]
fn available_lists_wired_packs() {
assert_eq!(available(), vec!["grok", "claude", "codex"]);
}
#[test]
fn resolve_claude_returns_claude_pack() {
assert_eq!(resolve("claude").unwrap().name(), "claude");
}
#[test]
fn resolve_codex_returns_codex_pack() {
assert_eq!(resolve("codex").unwrap().name(), "codex");
}
#[test]
fn unknown_harness_errors_clearly() {
let err = resolve("gpt").err().expect("unknown harness");
let msg = err.to_string();
assert!(msg.contains("gpt"), "{msg}");
assert!(msg.contains("grok"), "names the available packs: {msg}");
assert_eq!(err.requested, "gpt");
}
#[test]
fn pack_builds_expected_specs() {
let (_dir, host) = test_host();
let registry = FakePack.build_registry(&host);
assert!(registry.contains("alpha"));
assert!(registry.contains("beta"));
let specs = registry.specs();
let mut names: Vec<&str> = specs.iter().map(|s| s.name.as_str()).collect();
names.sort_unstable();
assert_eq!(names, vec!["alpha", "beta"]);
let alpha = specs.iter().find(|s| s.name == "alpha").unwrap();
assert_eq!(alpha.description, "echo");
}
#[tokio::test]
async fn pack_routes_to_impl() {
let (_dir, host) = test_host();
let registry = FakePack.build_registry(&host);
let tool_ctx = ToolCtx::new(
PathBuf::from("/repo"),
"c1".into(),
PathBuf::from("/repo"),
CancellationToken::new(),
);
let dispatched = registry
.dispatch("alpha", json!({ "x": 1 }), &tool_ctx)
.await;
assert!(dispatched.record.ok, "routes to the registered impl");
assert_eq!(dispatched.record.name, "alpha");
assert_eq!(dispatched.record.output["echoed"], json!(r#"{"x":1}"#));
}
#[test]
#[should_panic(expected = "duplicate tool registration")]
fn duplicate_registration_panics() {
let (_dir, host) = test_host();
let _ = DupPack.build_registry(&host);
}
#[test]
fn grok_tools_expose_kinds_for_the_approval_seam() {
use locode_tools::ToolKind;
let (_dir, host) = test_host();
let registry = resolve("grok").unwrap().build_registry(&host);
for (tool, kind) in [
("run_terminal_cmd", ToolKind::Shell),
("read_file", ToolKind::Read),
("search_replace", ToolKind::Edit),
("grep", ToolKind::Grep),
("list_dir", ToolKind::Glob),
] {
assert_eq!(registry.kind_of(tool), Some(kind), "kind_of({tool})");
}
assert_eq!(registry.kind_of("no_such_tool"), None);
}
#[test]
fn grok_registers_real_tools_and_preamble() {
let (_dir, host) = test_host();
let pack = resolve("grok").unwrap();
let registry = pack.build_registry(&host);
for tool in [
"run_terminal_cmd",
"read_file",
"search_replace",
"grep",
"list_dir",
] {
assert!(registry.contains(tool), "grok pack registers {tool}");
}
let headless = pack.preamble(&ctx(true));
assert_eq!(headless.len(), 2);
assert_eq!(headless[0].role, Role::System);
assert_eq!(headless[1].role, Role::User);
let interactive = pack.preamble(&ctx(false));
assert_ne!(
headless, interactive,
"headless branch changes the identity"
);
}
}