Syncing
This commit is contained in:
parent
e45a8063e3
commit
212fea579f
2 changed files with 535 additions and 1 deletions
|
|
@ -24,5 +24,8 @@ pub use task::{Task, TaskResult};
|
||||||
pub use todo::ToDo;
|
pub use todo::ToDo;
|
||||||
pub use tools::{
|
pub use tools::{
|
||||||
format_tool_error, format_tool_result, parse_tool_call_blocks, parse_tool_call_stream,
|
format_tool_error, format_tool_result, parse_tool_call_blocks, parse_tool_call_stream,
|
||||||
tool_definitions, ParsedToolCall, ToolDefinition, ToolParser,
|
tool_definitions, CompositionStep, ExecutionContext, MarketplaceSearchResults, ParsedToolCall,
|
||||||
|
TestAction, ToolComposition, ToolDefinition, ToolDependency, ToolDocumentation, ToolExample,
|
||||||
|
ToolListing, ToolParser, ToolPlugin, ToolTest, ToolTestResult, ToolTestSuite,
|
||||||
|
ToolTestSuiteResult, ToolVersion,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
531
src/tools.rs
531
src/tools.rs
|
|
@ -1,5 +1,21 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
/// Context in which a tool should be available
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ExecutionContext {
|
||||||
|
Reef,
|
||||||
|
Krill,
|
||||||
|
Both,
|
||||||
|
Agent,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ExecutionContext {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ToolDefinition {
|
pub struct ToolDefinition {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|
@ -22,6 +38,15 @@ pub struct ToolDefinition {
|
||||||
/// Maximum execution time in milliseconds. Defaults to 30000 (30s).
|
/// Maximum execution time in milliseconds. Defaults to 30000 (30s).
|
||||||
#[serde(default = "default_timeout_ms")]
|
#[serde(default = "default_timeout_ms")]
|
||||||
pub timeout_ms: u64,
|
pub timeout_ms: u64,
|
||||||
|
/// Whether this tool version is deprecated
|
||||||
|
#[serde(default)]
|
||||||
|
pub deprecated: bool,
|
||||||
|
/// Message shown when tool is deprecated, explaining migration path
|
||||||
|
#[serde(default)]
|
||||||
|
pub deprecation_message: String,
|
||||||
|
/// Execution context indicating where this tool should be available
|
||||||
|
#[serde(default)]
|
||||||
|
pub execution_context: ExecutionContext,
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn default_timeout_ms() -> u64 {
|
const fn default_timeout_ms() -> u64 {
|
||||||
|
|
@ -41,6 +66,9 @@ impl Default for ToolDefinition {
|
||||||
version: String::new(),
|
version: String::new(),
|
||||||
project_scoped: false,
|
project_scoped: false,
|
||||||
timeout_ms: default_timeout_ms(),
|
timeout_ms: default_timeout_ms(),
|
||||||
|
deprecated: false,
|
||||||
|
deprecation_message: String::new(),
|
||||||
|
execution_context: ExecutionContext::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,6 +105,72 @@ impl ToolDefinition {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Check if this tool version is deprecated
|
||||||
|
pub fn is_deprecated(&self) -> bool {
|
||||||
|
self.deprecated
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get deprecation message with migration guidance
|
||||||
|
pub fn deprecation_notice(&self) -> Option<&str> {
|
||||||
|
if self.deprecated && !self.deprecation_message.is_empty() {
|
||||||
|
Some(&self.deprecation_message)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compare tool versions. Returns true if self is newer than other.
|
||||||
|
/// Uses simple semver comparison (major.minor.patch).
|
||||||
|
pub fn is_newer_than(&self, other: &Self) -> bool {
|
||||||
|
compare_versions(&self.version, &other.version) == std::cmp::Ordering::Greater
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter tools by execution context
|
||||||
|
pub fn filter_by_context(tools: &[ToolDefinition], context: ExecutionContext) -> Vec<ToolDefinition> {
|
||||||
|
match context {
|
||||||
|
ExecutionContext::Both => tools.to_vec(),
|
||||||
|
context => tools
|
||||||
|
.iter()
|
||||||
|
.filter(|t| t.execution_context == context || t.execution_context == ExecutionContext::Both)
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter tools to only include non-deprecated ones
|
||||||
|
pub fn filter_active(tools: &[ToolDefinition]) -> Vec<ToolDefinition> {
|
||||||
|
tools
|
||||||
|
.iter()
|
||||||
|
.filter(|t| !t.deprecated)
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Check compatibility with another tool definition
|
||||||
|
pub fn is_compatible_with(&self, other: &Self) -> bool {
|
||||||
|
self.name == other.name && !self.deprecated && !other.deprecated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple semver comparison (major.minor.patch)
|
||||||
|
fn compare_versions(a: &str, b: &str) -> std::cmp::Ordering {
|
||||||
|
let parse_version = |v: &str| -> Vec<u64> {
|
||||||
|
v.split('.')
|
||||||
|
.filter_map(|part| part.parse::<u64>().ok())
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
let v1 = parse_version(a);
|
||||||
|
let v2 = parse_version(b);
|
||||||
|
|
||||||
|
for (p1, p2) in v1.iter().zip(v2.iter()) {
|
||||||
|
match p1.cmp(p2) {
|
||||||
|
std::cmp::Ordering::Equal => continue,
|
||||||
|
non_eq => return non_eq,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
v1.len().cmp(&v2.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
|
@ -301,6 +395,149 @@ pub struct ToolErrorInfo {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extended version tracking for tool definitions.
|
||||||
|
/// Provides schema versioning, deprecation notices, and migration guidance.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolVersion {
|
||||||
|
pub version: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub schema: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deprecation_notes: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub migration_guide: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ToolVersion {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
version: "1.0.0".to_string(),
|
||||||
|
schema: serde_json::Value::Null,
|
||||||
|
deprecation_notes: None,
|
||||||
|
migration_guide: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Execution context for tool filtering.
|
||||||
|
/// Determines which tools are available in which execution environment.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ToolContext {
|
||||||
|
Reef,
|
||||||
|
Krill,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ToolContext {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Both
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tool category classification for UI and filtering.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
pub enum ToolCategory {
|
||||||
|
Filesystem,
|
||||||
|
Execution,
|
||||||
|
Git,
|
||||||
|
Compilation,
|
||||||
|
Network,
|
||||||
|
Workspace,
|
||||||
|
Kanban,
|
||||||
|
Documentation,
|
||||||
|
Strategic,
|
||||||
|
Agent,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolCategory {
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Filesystem => "filesystem",
|
||||||
|
Self::Execution => "execution",
|
||||||
|
Self::Git => "git",
|
||||||
|
Self::Compilation => "compilation",
|
||||||
|
Self::Network => "network",
|
||||||
|
Self::Workspace => "workspace",
|
||||||
|
Self::Kanban => "kanban",
|
||||||
|
Self::Documentation => "documentation",
|
||||||
|
Self::Strategic => "strategic",
|
||||||
|
Self::Agent => "agent",
|
||||||
|
Self::Other => "other",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A composed workflow of multiple tool steps executed in sequence
|
||||||
|
/// with dependency-based ordering.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolComposition {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: String,
|
||||||
|
pub steps: Vec<CompositionStep>,
|
||||||
|
#[serde(default = "default_composition_timeout_ms")]
|
||||||
|
pub timeout_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default_composition_timeout_ms() -> u64 {
|
||||||
|
60000
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single step within a tool composition.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CompositionStep {
|
||||||
|
pub tool: String,
|
||||||
|
pub arguments: serde_json::Value,
|
||||||
|
/// Indices of steps this step depends on (0-based).
|
||||||
|
/// If None, the step has no dependencies and can run immediately.
|
||||||
|
#[serde(default)]
|
||||||
|
pub depends_on: Option<Vec<usize>>,
|
||||||
|
/// Optional label for referencing step outputs
|
||||||
|
#[serde(default)]
|
||||||
|
pub label: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declares a dependency of one tool on another tool version.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolDependency {
|
||||||
|
pub tool: String,
|
||||||
|
#[serde(default = "default_dependency_version")]
|
||||||
|
pub version: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub optional: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub conflicts: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_dependency_version() -> String {
|
||||||
|
"1.0.0".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToolDependency {
|
||||||
|
pub fn new(tool: &str, version: &str) -> Self {
|
||||||
|
Self {
|
||||||
|
tool: tool.to_string(),
|
||||||
|
version: version.to_string(),
|
||||||
|
optional: false,
|
||||||
|
conflicts: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn optional(mut self) -> Self {
|
||||||
|
self.optional = true;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_conflict(mut self, tool: &str) -> Self {
|
||||||
|
self.conflicts.push(tool.to_string());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! tool {
|
macro_rules! tool {
|
||||||
($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr) => {
|
($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr) => {
|
||||||
ToolDefinition {
|
ToolDefinition {
|
||||||
|
|
@ -314,6 +551,9 @@ macro_rules! tool {
|
||||||
version: "1.0.0".to_string(),
|
version: "1.0.0".to_string(),
|
||||||
project_scoped: $scoped,
|
project_scoped: $scoped,
|
||||||
timeout_ms: default_timeout_ms(),
|
timeout_ms: default_timeout_ms(),
|
||||||
|
deprecated: false,
|
||||||
|
deprecation_message: String::new(),
|
||||||
|
execution_context: ExecutionContext::default(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr, $timeout:expr) => {
|
($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr, $timeout:expr) => {
|
||||||
|
|
@ -328,10 +568,157 @@ macro_rules! tool {
|
||||||
version: "1.0.0".to_string(),
|
version: "1.0.0".to_string(),
|
||||||
project_scoped: $scoped,
|
project_scoped: $scoped,
|
||||||
timeout_ms: $timeout,
|
timeout_ms: $timeout,
|
||||||
|
deprecated: false,
|
||||||
|
deprecation_message: String::new(),
|
||||||
|
execution_context: ExecutionContext::default(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Trait for implementing custom tool plugins.
|
||||||
|
/// Plugins can be registered at runtime to extend the tool ecosystem.
|
||||||
|
pub trait ToolPlugin: Send + Sync {
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
fn description(&self) -> &str;
|
||||||
|
fn category(&self) -> &str;
|
||||||
|
fn schema(&self) -> serde_json::Value;
|
||||||
|
fn execute(&self, args: serde_json::Value) -> Result<serde_json::Value, String>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A marketplace listing for a published tool.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolListing {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub author: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub author_id: String,
|
||||||
|
pub description: String,
|
||||||
|
pub version: String,
|
||||||
|
pub category: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub downloads: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub rating: f64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub review_count: u64,
|
||||||
|
#[serde(default)]
|
||||||
|
pub source_url: Option<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub schema: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub published_at: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Results from a marketplace search.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct MarketplaceSearchResults {
|
||||||
|
pub listings: Vec<ToolListing>,
|
||||||
|
pub total_count: usize,
|
||||||
|
pub page: usize,
|
||||||
|
pub page_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A documented example for a tool.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolExample {
|
||||||
|
pub id: String,
|
||||||
|
pub tool_name: String,
|
||||||
|
pub title: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub description: String,
|
||||||
|
pub arguments: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub expected_output: Option<serde_json::Value>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub tags: Vec<String>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub created_at: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub author: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single test case for a tool.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolTest {
|
||||||
|
pub name: String,
|
||||||
|
pub input: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub expected_output: Option<serde_json::Value>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub expected_error: Option<String>,
|
||||||
|
#[serde(default = "default_test_timeout")]
|
||||||
|
pub timeout_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
const fn default_test_timeout() -> u64 {
|
||||||
|
30000
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A suite of tests for a specific tool.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolTestSuite {
|
||||||
|
pub tool_name: String,
|
||||||
|
pub tests: Vec<ToolTest>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub setup_actions: Vec<TestAction>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub teardown_actions: Vec<TestAction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A setup or teardown action for a test suite.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct TestAction {
|
||||||
|
pub tool: String,
|
||||||
|
pub arguments: serde_json::Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Result of running a single tool test.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolTestResult {
|
||||||
|
pub test_name: String,
|
||||||
|
pub passed: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub actual_output: Option<serde_json::Value>,
|
||||||
|
#[serde(default)]
|
||||||
|
pub error: Option<String>,
|
||||||
|
pub duration_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Results of running a full test suite.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolTestSuiteResult {
|
||||||
|
pub tool_name: String,
|
||||||
|
pub total: usize,
|
||||||
|
pub passed: usize,
|
||||||
|
pub failed: usize,
|
||||||
|
pub results: Vec<ToolTestResult>,
|
||||||
|
pub total_duration_ms: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generated documentation for a tool.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ToolDocumentation {
|
||||||
|
pub tool_name: String,
|
||||||
|
pub description: String,
|
||||||
|
pub category: String,
|
||||||
|
pub version: String,
|
||||||
|
pub markdown: String,
|
||||||
|
pub parameters: serde_json::Value,
|
||||||
|
#[serde(default)]
|
||||||
|
pub dangerous: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub requires_approval: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deprecated: bool,
|
||||||
|
#[serde(default)]
|
||||||
|
pub deprecation_message: String,
|
||||||
|
#[serde(default)]
|
||||||
|
pub examples: Vec<ToolExample>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns all built-in tool definitions across all categories.
|
/// Returns all built-in tool definitions across all categories.
|
||||||
pub fn tool_definitions() -> Vec<ToolDefinition> {
|
pub fn tool_definitions() -> Vec<ToolDefinition> {
|
||||||
vec![
|
vec![
|
||||||
|
|
@ -1485,4 +1872,148 @@ Done with tools.
|
||||||
assert!(matches!(deserialized.status, ToolResultStatus::Success));
|
assert!(matches!(deserialized.status, ToolResultStatus::Success));
|
||||||
assert_eq!(deserialized.data.unwrap()["result"], "ok");
|
assert_eq!(deserialized.data.unwrap()["result"], "ok");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tool_definition_is_deprecated() {
|
||||||
|
let mut def = ToolDefinition::default();
|
||||||
|
assert!(!def.is_deprecated());
|
||||||
|
|
||||||
|
def.deprecated = true;
|
||||||
|
assert!(def.is_deprecated());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tool_definition_deprecation_notice() {
|
||||||
|
let mut def = ToolDefinition::default();
|
||||||
|
assert!(def.deprecation_notice().is_none());
|
||||||
|
|
||||||
|
def.deprecated = true;
|
||||||
|
assert!(def.deprecation_notice().is_none());
|
||||||
|
|
||||||
|
def.deprecation_message = "Use new_tool instead".to_string();
|
||||||
|
assert_eq!(def.deprecation_notice(), Some("Use new_tool instead"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tool_version_comparison() {
|
||||||
|
let old = ToolDefinition {
|
||||||
|
version: "1.0.0".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let new = ToolDefinition {
|
||||||
|
version: "2.0.0".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert!(new.is_newer_than(&old));
|
||||||
|
assert!(!old.is_newer_than(&new));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filter_by_context() {
|
||||||
|
let reef_tool = ToolDefinition {
|
||||||
|
name: "reef_tool".to_string(),
|
||||||
|
execution_context: ExecutionContext::Reef,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let krill_tool = ToolDefinition {
|
||||||
|
name: "krill_tool".to_string(),
|
||||||
|
execution_context: ExecutionContext::Krill,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let both_tool = ToolDefinition {
|
||||||
|
name: "both_tool".to_string(),
|
||||||
|
execution_context: ExecutionContext::Both,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tools = vec![reef_tool, krill_tool, both_tool];
|
||||||
|
|
||||||
|
let reef_tools = ToolDefinition::filter_by_context(&tools, ExecutionContext::Reef);
|
||||||
|
assert_eq!(reef_tools.len(), 2);
|
||||||
|
assert!(reef_tools.iter().any(|t| t.name == "reef_tool"));
|
||||||
|
assert!(reef_tools.iter().any(|t| t.name == "both_tool"));
|
||||||
|
|
||||||
|
let krill_tools = ToolDefinition::filter_by_context(&tools, ExecutionContext::Krill);
|
||||||
|
assert_eq!(krill_tools.len(), 2);
|
||||||
|
assert!(krill_tools.iter().any(|t| t.name == "krill_tool"));
|
||||||
|
assert!(krill_tools.iter().any(|t| t.name == "both_tool"));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_filter_active_excludes_deprecated() {
|
||||||
|
let active = ToolDefinition {
|
||||||
|
name: "active".to_string(),
|
||||||
|
deprecated: false,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let deprecated = ToolDefinition {
|
||||||
|
name: "deprecated".to_string(),
|
||||||
|
deprecated: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
let tools = vec![active, deprecated];
|
||||||
|
let active_tools = ToolDefinition::filter_active(&tools);
|
||||||
|
assert_eq!(active_tools.len(), 1);
|
||||||
|
assert_eq!(active_tools[0].name, "active");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_tool_compatibility() {
|
||||||
|
let tool_a = ToolDefinition {
|
||||||
|
name: "my_tool".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let tool_b = ToolDefinition {
|
||||||
|
name: "my_tool".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
let tool_c = ToolDefinition {
|
||||||
|
name: "other_tool".to_string(),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(tool_a.is_compatible_with(&tool_b));
|
||||||
|
assert!(!tool_a.is_compatible_with(&tool_c));
|
||||||
|
|
||||||
|
let mut deprecated_tool = ToolDefinition {
|
||||||
|
name: "my_tool".to_string(),
|
||||||
|
deprecated: true,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert!(!tool_a.is_compatible_with(&deprecated_tool));
|
||||||
|
deprecated_tool.deprecated = false;
|
||||||
|
assert!(tool_a.is_compatible_with(&deprecated_tool));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toolversion_default() {
|
||||||
|
let version = ToolVersion::default();
|
||||||
|
assert_eq!(version.version, "1.0.0");
|
||||||
|
assert!(version.deprecation_notes.is_none());
|
||||||
|
assert!(version.migration_guide.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_execution_context_default() {
|
||||||
|
assert_eq!(
|
||||||
|
ExecutionContext::default(),
|
||||||
|
ExecutionContext::Both
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_toolcategory_as_str() {
|
||||||
|
assert_eq!(ToolCategory::Filesystem.as_str(), "filesystem");
|
||||||
|
assert_eq!(ToolCategory::Execution.as_str(), "execution");
|
||||||
|
assert_eq!(ToolCategory::Git.as_str(), "git");
|
||||||
|
assert_eq!(ToolCategory::Compilation.as_str(), "compilation");
|
||||||
|
assert_eq!(ToolCategory::Network.as_str(), "network");
|
||||||
|
assert_eq!(ToolCategory::Workspace.as_str(), "workspace");
|
||||||
|
assert_eq!(ToolCategory::Kanban.as_str(), "kanban");
|
||||||
|
assert_eq!(ToolCategory::Documentation.as_str(), "documentation");
|
||||||
|
assert_eq!(ToolCategory::Strategic.as_str(), "strategic");
|
||||||
|
assert_eq!(ToolCategory::Agent.as_str(), "agent");
|
||||||
|
assert_eq!(ToolCategory::Other.as_str(), "other");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue