diff --git a/src/tools.rs b/src/tools.rs index 1772b7a..94f89bc 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -86,10 +86,7 @@ impl ToolDefinition { } pub fn to_short_doc(&self) -> String { - format!( - "- {}: {}\n", - self.name, self.description - ) + format!("- {}: {}\n", self.name, self.description) } /// Strip the project_id parameter from the tool's JSON schema. @@ -141,12 +138,17 @@ impl ToolDefinition { } /// Filter tools by execution context - pub fn filter_by_context(tools: &[ToolDefinition], context: ExecutionContext) -> Vec { + pub fn filter_by_context( + tools: &[ToolDefinition], + context: ExecutionContext, + ) -> Vec { match context { ExecutionContext::Both => tools.to_vec(), context => tools .iter() - .filter(|t| t.execution_context == context || t.execution_context == ExecutionContext::Both) + .filter(|t| { + t.execution_context == context || t.execution_context == ExecutionContext::Both + }) .cloned() .collect(), } @@ -154,11 +156,7 @@ impl ToolDefinition { /// Filter tools to only include non-deprecated ones pub fn filter_active(tools: &[ToolDefinition]) -> Vec { - tools - .iter() - .filter(|t| !t.deprecated) - .cloned() - .collect() + tools.iter().filter(|t| !t.deprecated).cloned().collect() } /// Check compatibility with another tool definition @@ -1099,15 +1097,16 @@ pub fn tool_definitions() -> Vec { ), tool!( "kanban_create_todo", - "Create a new todo item in a Kanban column", - "kanban", - false, false, true, - serde_json::json!({ - "type": "object", - "properties": { - "project_id": { "type": "string", "description": "Project ID" }, - "board_id": { "type": "string", "description": "Board ID (defaults to project's primary board)" }, - "column_id": { "type": "string", "description": "Column ID to place the todo" }, + "Create a new todo item in a Kanban board", + "kanban", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "board_id": { "type": "string", "description": "Board ID (defaults to project's primary board)" }, + "status": { "type": "string", "enum": ["Ready","InProgress","Done","Blocked","Delegated","Failed","PendingApproval"], "description": "Initial status of the todo" }, + "title": { "type": "string", "description": "Title of the todo" }, "description": { "type": "string", "description": "Optional description" }, "priority": { "type": "number", "description": "Priority (1-1000)" }, @@ -1115,7 +1114,7 @@ pub fn tool_definitions() -> Vec { "agent_prompt": { "type": "string", "description": "Optional agent prompt if deploy_agent is true" }, "agent_task_details": { "type": "string", "description": "Optional agent task details" } }, - "required": ["project_id", "column_id", "title"] + "required": ["project_id", "title"] }) ), tool!( @@ -1152,19 +1151,19 @@ pub fn tool_definitions() -> Vec { ), tool!( "kanban_move_todo", - "Move a todo to a different column or reorder it", - "kanban", - false, false, true, - serde_json::json!({ - "type": "object", - "properties": { - "project_id": { "type": "string", "description": "Project ID" }, - "todo_id": { "type": "string", "description": "ID of the todo to move" }, - "column_id": { "type": "string", "description": "Target column ID" }, - "task_order": { "type": "number", "description": "New order position" } - }, - "required": ["project_id", "todo_id", "column_id"] - }) + "Move a todo item to a different status", + "kanban", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "todo_id": { "type": "string", "description": "ID of the todo to move" }, + "status": { "type": "string", "enum": ["Ready","InProgress","Done","Blocked","Delegated","Failed","PendingApproval"], "description": "Target status" }, + "task_order": { "type": "number", "description": "New order position" } + }, + "required": ["project_id", "todo_id"] + }) ), tool!( "kanban_create_task", @@ -2014,10 +2013,7 @@ Done with tools. #[test] fn test_execution_context_default() { - assert_eq!( - ExecutionContext::default(), - ExecutionContext::Both - ); + assert_eq!(ExecutionContext::default(), ExecutionContext::Both); } #[test] @@ -2043,7 +2039,10 @@ Done with tools. "```\nSome trailing text.", ]; let results = parse_tool_call_stream(chunks.into_iter()); - assert!(results.is_empty(), "tool-result: at stream start should be rejected"); + assert!( + results.is_empty(), + "tool-result: at stream start should be rejected" + ); } #[test] @@ -2053,7 +2052,10 @@ Done with tools. "```tool-result:write_file\n{\"call_id\": \"c2\"}\n```\n", ]; let results = parse_tool_call_stream(chunks.into_iter()); - assert!(results.is_empty(), "multiple tool-result: blocks should all be rejected"); + assert!( + results.is_empty(), + "multiple tool-result: blocks should all be rejected" + ); } #[test] @@ -2064,15 +2066,24 @@ Done with tools. "```tool:search_files\n{\"call_id\": \"c3\"}\n```\n", ]; let results = parse_tool_call_stream(chunks.into_iter()); - assert_eq!(results.len(), 2, "should only parse the tool: blocks, not tool-result:"); + assert_eq!( + results.len(), + 2, + "should only parse the tool: blocks, not tool-result:" + ); assert_eq!(results[0].call_id, "c1"); assert_eq!(results[1].call_id, "c3"); } #[test] fn test_streaming_tool_result_only_chunk() { - let results = parse_tool_call_stream(vec!["```tool-result:read_file\n{\"call_id\": \"c1\"}\n```\n"].into_iter()); - assert!(results.is_empty(), "tool-result: as only chunk content should be rejected"); + let results = parse_tool_call_stream( + vec!["```tool-result:read_file\n{\"call_id\": \"c1\"}\n```\n"].into_iter(), + ); + assert!( + results.is_empty(), + "tool-result: as only chunk content should be rejected" + ); } #[test] @@ -2083,12 +2094,20 @@ Done with tools. "Some text after.", ]; let results = parse_tool_call_stream(chunks.into_iter()); - assert!(results.is_empty(), "tool-result: in middle of text should be rejected"); + assert!( + results.is_empty(), + "tool-result: in middle of text should be rejected" + ); } #[test] fn test_streaming_finish_rejects_tool_result_no_closing_fence() { - let results = parse_tool_call_stream(vec!["```tool-result:read_file\n{\"call_id\": \"c1\"}\n"].into_iter()); - assert!(results.is_empty(), "tool-result: without closing fence should be rejected by finish()"); + let results = parse_tool_call_stream( + vec!["```tool-result:read_file\n{\"call_id\": \"c1\"}\n"].into_iter(), + ); + assert!( + results.is_empty(), + "tool-result: without closing fence should be rejected by finish()" + ); } }