pub use zeph_config::ToolDensity;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolKind {
Run,
Explore,
Edit,
Web,
Mcp,
Other,
}
impl ToolKind {
#[must_use]
pub fn classify(tool_name: &str) -> Self {
if tool_name.starts_with("mcp__") {
return Self::Mcp;
}
match tool_name {
"bash" | "shell" | "run_command" | "run_shell_command" => Self::Run,
"read_file" | "list_dir" | "glob" | "grep" | "find" | "ls" | "Read" | "list_files" => {
Self::Explore
}
"write_file" | "edit_file" | "patch" | "Write" | "Edit" => Self::Edit,
"web_search" | "web_scrape" | "fetch" | "WebSearch" | "WebFetch" => Self::Web,
_ => Self::Other,
}
}
#[must_use]
pub fn is_groupable(self) -> bool {
matches!(self, Self::Run | Self::Explore)
}
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::Run => "run",
Self::Explore => "explore",
Self::Edit => "edit",
Self::Web => "web",
Self::Mcp => "mcp",
Self::Other => "tool",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ToolStatus {
Running,
Success,
Failure,
}
impl ToolStatus {
#[must_use]
pub fn from_streaming_and_success(streaming: bool, success: Option<bool>) -> Self {
if streaming {
Self::Running
} else {
match success {
Some(false) => Self::Failure,
_ => Self::Success,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tool_kind_classify_run() {
assert_eq!(ToolKind::classify("bash"), ToolKind::Run);
assert_eq!(ToolKind::classify("shell"), ToolKind::Run);
assert_eq!(ToolKind::classify("run_command"), ToolKind::Run);
}
#[test]
fn tool_kind_classify_explore() {
assert_eq!(ToolKind::classify("read_file"), ToolKind::Explore);
assert_eq!(ToolKind::classify("list_dir"), ToolKind::Explore);
assert_eq!(ToolKind::classify("grep"), ToolKind::Explore);
assert_eq!(ToolKind::classify("glob"), ToolKind::Explore);
}
#[test]
fn tool_kind_classify_edit() {
assert_eq!(ToolKind::classify("write_file"), ToolKind::Edit);
assert_eq!(ToolKind::classify("edit_file"), ToolKind::Edit);
assert_eq!(ToolKind::classify("patch"), ToolKind::Edit);
}
#[test]
fn tool_kind_classify_web() {
assert_eq!(ToolKind::classify("web_search"), ToolKind::Web);
assert_eq!(ToolKind::classify("web_scrape"), ToolKind::Web);
assert_eq!(ToolKind::classify("fetch"), ToolKind::Web);
}
#[test]
fn tool_kind_classify_mcp() {
assert_eq!(ToolKind::classify("mcp__github__list_prs"), ToolKind::Mcp);
assert_eq!(ToolKind::classify("mcp__slack__send"), ToolKind::Mcp);
}
#[test]
fn tool_kind_classify_other() {
assert_eq!(ToolKind::classify("unknown_tool"), ToolKind::Other);
assert_eq!(ToolKind::classify("memory_search"), ToolKind::Other);
}
#[test]
fn tool_kind_groupable() {
assert!(ToolKind::Run.is_groupable());
assert!(ToolKind::Explore.is_groupable());
assert!(!ToolKind::Edit.is_groupable());
assert!(!ToolKind::Web.is_groupable());
assert!(!ToolKind::Mcp.is_groupable());
assert!(!ToolKind::Other.is_groupable());
}
#[test]
fn tool_density_cycle() {
assert_eq!(ToolDensity::Compact.cycle(), ToolDensity::Inline);
assert_eq!(ToolDensity::Inline.cycle(), ToolDensity::Block);
assert_eq!(ToolDensity::Block.cycle(), ToolDensity::Compact);
}
#[test]
fn tool_density_default_is_inline() {
assert_eq!(ToolDensity::default(), ToolDensity::Inline);
}
#[test]
fn tool_status_from_streaming_and_success() {
assert_eq!(
ToolStatus::from_streaming_and_success(true, None),
ToolStatus::Running
);
assert_eq!(
ToolStatus::from_streaming_and_success(false, Some(true)),
ToolStatus::Success
);
assert_eq!(
ToolStatus::from_streaming_and_success(false, Some(false)),
ToolStatus::Failure
);
assert_eq!(
ToolStatus::from_streaming_and_success(false, None),
ToolStatus::Success
);
}
}