diff --git a/src/agents.rs b/src/agents.rs new file mode 100644 index 0000000..0d9f42c --- /dev/null +++ b/src/agents.rs @@ -0,0 +1,41 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum AgentType { + Planner, + DocAgent, + BuildAgent, + ChatAgent, + TestJudge, +} + +impl AgentType { + pub fn as_str(&self) -> &'static str { + match self { + AgentType::Planner => "planner", + AgentType::DocAgent => "doc_agent", + AgentType::BuildAgent => "build_agent", + AgentType::ChatAgent => "chat", + AgentType::TestJudge => "test_judge", + } + } + + pub fn from_str(s: &str) -> Option { + match s { + "planner" => Some(AgentType::Planner), + "doc_agent" => Some(AgentType::DocAgent), + "build_agent" => Some(AgentType::BuildAgent), + "chat" => Some(AgentType::ChatAgent), + "test_judge" => Some(AgentType::TestJudge), + _ => None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "snake_case")] +pub enum ApprovalMode { + Batch, + PerTool, +} diff --git a/src/enums.rs b/src/enums.rs index 25bcd40..f1d9d3f 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -8,6 +8,9 @@ pub enum ToDoStatus { Completed, Blocked, ReadyForAgent, + Delegated, + Failed, + PendingApproval, } impl Default for ToDoStatus { @@ -24,6 +27,9 @@ impl fmt::Display for ToDoStatus { ToDoStatus::Completed => write!(f, "completed"), ToDoStatus::Blocked => write!(f, "blocked"), ToDoStatus::ReadyForAgent => write!(f, "ready_for_agent"), + ToDoStatus::Delegated => write!(f, "delegated"), + ToDoStatus::Failed => write!(f, "failed"), + ToDoStatus::PendingApproval => write!(f, "pending_approval"), } } } @@ -37,6 +43,9 @@ impl std::str::FromStr for ToDoStatus { "completed" => Ok(ToDoStatus::Completed), "blocked" => Ok(ToDoStatus::Blocked), "ready_for_agent" => Ok(ToDoStatus::ReadyForAgent), + "delegated" => Ok(ToDoStatus::Delegated), + "failed" => Ok(ToDoStatus::Failed), + "pending_approval" => Ok(ToDoStatus::PendingApproval), _ => Err(format!("unknown status: {}", s)), } } @@ -191,3 +200,51 @@ pub enum TaskResultType { Error, Split, } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum ToDoSource { + User, + Planner, + Automation, + Delegation, +} + +impl Default for ToDoSource { + fn default() -> Self { + ToDoSource::User + } +} + +impl std::fmt::Display for ToDoSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ToDoSource::User => write!(f, "user"), + ToDoSource::Planner => write!(f, "planner"), + ToDoSource::Automation => write!(f, "automation"), + ToDoSource::Delegation => write!(f, "delegation"), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +#[serde(rename_all = "snake_case")] +pub enum TestStrategy { + Unit, + Integration, + E2e, + Property, + Manual, +} + +impl std::fmt::Display for TestStrategy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + TestStrategy::Unit => write!(f, "unit"), + TestStrategy::Integration => write!(f, "integration"), + TestStrategy::E2e => write!(f, "e2e"), + TestStrategy::Property => write!(f, "property"), + TestStrategy::Manual => write!(f, "manual"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index b7273f1..8eae7cc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub use chrono::{DateTime, Utc}; pub use stp_core::{CommunicationType, CommunicationValue, DataTypes, DataValue}; pub use uuid::Uuid; +pub mod agents; pub mod ai_response; pub mod coral; pub mod enums; @@ -12,6 +13,7 @@ pub mod task; pub mod todo; pub mod tools; +pub use agents::{AgentType, ApprovalMode}; pub use ai_response::{Conversation, Message}; pub use coral::{Coral, CoralId}; pub use enums::*; @@ -22,5 +24,5 @@ pub use task::{Task, TaskResult}; pub use todo::ToDo; pub use tools::{ format_tool_error, format_tool_result, parse_tool_call_blocks, parse_tool_call_stream, - ParsedToolCall, ToolDefinition, ToolParser, + tool_definitions, ParsedToolCall, ToolDefinition, ToolParser, }; diff --git a/src/task.rs b/src/task.rs index 7fbd37c..5034830 100644 --- a/src/task.rs +++ b/src/task.rs @@ -2,7 +2,8 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::enums::{TaskResultType, TaskStatus}; +use crate::agents::AgentType; +use crate::enums::{TaskResultType, TaskStatus, TestStrategy}; use crate::todo::ToDo; #[derive(Debug, Clone, Serialize, Deserialize)] @@ -146,6 +147,10 @@ pub struct TestCase { pub name: String, pub command: String, pub expected_output: Option, + #[serde(default)] + pub test_strategy: Option, + #[serde(default)] + pub suggested_test_files: Vec, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -156,6 +161,12 @@ pub struct ResourceLimits { pub timeout_seconds: u64, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SessionBudget { + pub max_tool_calls: u32, + pub max_tokens: u32, +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct FileRange { pub path: String, @@ -177,6 +188,12 @@ pub struct AgentTaskSpec { pub token_budget: usize, pub resource_limits: ResourceLimits, pub parent_task_id: Option, + pub pre_approved_plan_id: Option, + pub execution_token: Option, + pub session_budget: Option, + pub agent_type: Option, + #[serde(default)] + pub suggested_test_files: Vec, } impl AgentTaskSpec { @@ -199,6 +216,11 @@ impl AgentTaskSpec { timeout_seconds: 3600, // 1 hour }, parent_task_id: None, + pre_approved_plan_id: None, + execution_token: None, + session_budget: None, + agent_type: None, + suggested_test_files: Vec::new(), } } } diff --git a/src/todo.rs b/src/todo.rs index c693a51..80926c1 100644 --- a/src/todo.rs +++ b/src/todo.rs @@ -2,7 +2,7 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; -use crate::enums::ToDoStatus; +use crate::enums::{ToDoSource, ToDoStatus}; use crate::errors::ValidationError; use crate::{CommunicationType, CommunicationValue, DataTypes, DataValue}; @@ -24,6 +24,12 @@ pub struct ToDo { pub updated_at: DateTime, pub created_by: Uuid, pub project_id: Option, + pub parent_todo_id: Option, + pub subtask_ids: Vec, + pub affected_files: Vec, + pub source: ToDoSource, + pub automation_chain_id: Option, + pub tags: Vec, } impl Default for ToDo { @@ -40,6 +46,12 @@ impl Default for ToDo { updated_at: now, created_by: Uuid::nil(), project_id: None, + parent_todo_id: None, + subtask_ids: Vec::new(), + affected_files: Vec::new(), + source: ToDoSource::User, + automation_chain_id: None, + tags: Vec::new(), } } } @@ -58,6 +70,12 @@ impl ToDo { updated_at: now, created_by, project_id: None, + parent_todo_id: None, + subtask_ids: Vec::new(), + affected_files: Vec::new(), + source: ToDoSource::User, + automation_chain_id: None, + tags: Vec::new(), } } @@ -112,6 +130,15 @@ impl ToDo { (ToDoStatus::ReadyForAgent, ToDoStatus::InProgress) => true, (ToDoStatus::ReadyForAgent, ToDoStatus::Pending) => true, (ToDoStatus::ReadyForAgent, ToDoStatus::Blocked) => true, + (ToDoStatus::InProgress, ToDoStatus::Delegated) => true, + (ToDoStatus::InProgress, ToDoStatus::Failed) => true, + (ToDoStatus::InProgress, ToDoStatus::PendingApproval) => true, + (ToDoStatus::Delegated, ToDoStatus::InProgress) => true, + (ToDoStatus::Delegated, ToDoStatus::Completed) => true, + (ToDoStatus::Delegated, ToDoStatus::Blocked) => true, + (ToDoStatus::Failed, ToDoStatus::Pending) => true, + (ToDoStatus::PendingApproval, ToDoStatus::InProgress) => true, + (ToDoStatus::PendingApproval, ToDoStatus::Blocked) => true, _ => self.status == status, }; diff --git a/src/tools.rs b/src/tools.rs index da20468..7126886 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Serialize, Deserialize, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct ToolDefinition { #[serde(default)] pub id: String, @@ -19,6 +19,30 @@ pub struct ToolDefinition { /// Project-scoped tools are filtered out when no project is selected. #[serde(default)] pub project_scoped: bool, + /// Maximum execution time in milliseconds. Defaults to 30000 (30s). + #[serde(default = "default_timeout_ms")] + pub timeout_ms: u64, +} + +const fn default_timeout_ms() -> u64 { + 30000 +} + +impl Default for ToolDefinition { + fn default() -> Self { + Self { + id: String::new(), + name: String::new(), + description: String::new(), + category: String::new(), + parameters: serde_json::Value::Null, + dangerous: false, + requires_approval: false, + version: String::new(), + project_scoped: false, + timeout_ms: default_timeout_ms(), + } + } } impl ToolDefinition { @@ -277,6 +301,851 @@ pub struct ToolErrorInfo { pub message: String, } +macro_rules! tool { + ($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr) => { + ToolDefinition { + id: $name.to_string(), + name: $name.to_string(), + description: $desc.to_string(), + category: $cat.to_string(), + parameters: $params, + dangerous: $dangerous, + requires_approval: $approval, + version: "1.0.0".to_string(), + project_scoped: $scoped, + timeout_ms: default_timeout_ms(), + } + }; + ($name:expr, $desc:expr, $cat:expr, $dangerous:expr, $approval:expr, $scoped:expr, $params:expr, $timeout:expr) => { + ToolDefinition { + id: $name.to_string(), + name: $name.to_string(), + description: $desc.to_string(), + category: $cat.to_string(), + parameters: $params, + dangerous: $dangerous, + requires_approval: $approval, + version: "1.0.0".to_string(), + project_scoped: $scoped, + timeout_ms: $timeout, + } + }; +} + +/// Returns all built-in tool definitions across all categories. +pub fn tool_definitions() -> Vec { + vec![ + // ======================== + // Filesystem Tools + // ======================== + tool!( + "read_file", + "Read the content of a file with optional pagination", + "filesystem", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the file to read" }, + "offset": { "type": "number", "description": "Line number to start reading from (1-based)" }, + "limit": { "type": "number", "description": "Maximum number of lines to read" } + }, + "required": ["path"] + }) + ), + tool!( + "write_file", + "Create or overwrite a file with content", + "filesystem", + true, true, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the file to write" }, + "content": { "type": "string", "description": "Content to write to the file" }, + "create_dirs": { "type": "boolean", "description": "Create parent directories if they don't exist" } + }, + "required": ["path", "content"] + }) + ), + tool!( + "list_directory", + "List directory contents with optional filtering", + "filesystem", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the directory to list" }, + "include_hidden": { "type": "boolean", "description": "Include hidden files (starting with .)" }, + "recursive": { "type": "boolean", "description": "List subdirectories recursively" } + }, + "required": ["path"] + }) + ), + tool!( + "edit_file", + "Edit file contents with line-based operations (replace, insert, delete)", + "filesystem", + true, true, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the file to edit" }, + "edits": { + "type": "array", + "description": "Array of edit operations: {start_line, end_line (optional), new_content (optional), operation: 'replace'|'insert'|'delete'}", + "items": { + "type": "object", + "properties": { + "start_line": { "type": "number" }, + "end_line": { "type": "number" }, + "new_content": { "type": "string" }, + "operation": { "type": "string", "enum": ["replace", "insert", "delete"] } + }, + "required": ["start_line", "operation"] + } + } + }, + "required": ["path", "edits"] + }) + ), + tool!( + "search_files", + "Search for text patterns in files (grep-like)", + "filesystem", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Directory path to search in" }, + "pattern": { "type": "string", "description": "Search pattern (regex supported)" }, + "file_pattern": { "type": "string", "description": "File pattern to match (e.g., *.rs, *.ts)" } + }, + "required": ["path", "pattern"] + }) + ), + + // ======================== + // Execution Tools + // ======================== + tool!( + "bash", + "Execute a shell command and capture output", + "execution", + true, true, false, + serde_json::json!({ + "type": "object", + "properties": { + "command": { "type": "string", "description": "The shell command to execute" }, + "cwd": { "type": "string", "description": "Working directory for the command" }, + "timeout_ms": { "type": "number", "description": "Timeout in milliseconds" } + }, + "required": ["command"] + }) + ), + + // ======================== + // Git Tools + // ======================== + tool!( + "git_status", + "Show working tree status in a git repository", + "git", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "repo_path": { "type": "string", "description": "Path to the git repository" }, + "short": { "type": "boolean", "description": "Use short format" } + } + }) + ), + tool!( + "git_diff", + "Show changes between commits, commit and working tree, etc.", + "git", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "repo_path": { "type": "string", "description": "Path to the git repository" }, + "target": { "type": "string", "description": "Diff target (e.g., HEAD, HEAD~1, branch name)" }, + "file": { "type": "string", "description": "Only show diff for specific file" } + } + }) + ), + tool!( + "git_log", + "Show commit history in a git repository", + "git", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "repo_path": { "type": "string", "description": "Path to the git repository" }, + "max_count": { "type": "number", "description": "Maximum number of commits to show" } + } + }) + ), + tool!( + "git_branch", + "List or manage git branches", + "git", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "repo_path": { "type": "string", "description": "Path to the git repository" }, + "all": { "type": "boolean", "description": "List all branches including remote" } + } + }) + ), + + // ======================== + // Build & Test Tools + // ======================== + tool!( + "cargo_check", + "Run cargo check on a Rust project to verify compilation", + "build", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "project_path": { "type": "string", "description": "Path to the Rust project (default: cwd)" }, + "manifest_path": { "type": "string", "description": "Path to Cargo.toml" } + } + }) + ), + tool!( + "npm_build", + "Run npm build script in a JavaScript/TypeScript project", + "build", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "project_path": { "type": "string", "description": "Path to the npm project" }, + "script": { "type": "string", "description": "Script to run (default: build)" } + } + }) + ), + tool!( + "python_check", + "Check Python syntax or run linting on a Python file", + "build", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "project_path": { "type": "string", "description": "Path to Python project" } + }, + "required": ["project_path"] + }) + ), + tool!( + "test_runner", + "Run project tests with a test runner like pytest", + "build", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "project_path": { "type": "string", "description": "Path to the project" }, + "test_command": { "type": "string", "description": "Command to run tests" }, + "test_path": { "type": "string", "description": "Specific test path to run" } + } + }) + ), + + // ======================== + // Network Tools + // ======================== + tool!( + "web_search", + "Search the web for information using a search provider", + "network", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "query": { "type": "string", "description": "The search query" }, + "provider": { "type": "string", "description": "Search provider (exa, google, bing)" }, + "num_results": { "type": "number", "description": "Number of results to return" } + }, + "required": ["query"] + }) + ), + tool!( + "web_fetch", + "Fetch and parse web pages from URLs", + "network", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "url": { "type": "string", "description": "URL to fetch" }, + "format": { "type": "string", "description": "Response format: text, markdown, html" } + }, + "required": ["url"] + }) + ), + tool!( + "web_api", + "Make HTTP API requests to external services", + "network", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "url": { "type": "string", "description": "API endpoint URL" }, + "method": { "type": "string", "description": "HTTP method (GET, POST, PUT, DELETE, PATCH)" }, + "body": { "type": "string", "description": "Request body (JSON)" } + }, + "required": ["url", "method"] + }) + ), + + // ======================== + // Workspace Tools + // ======================== + tool!( + "create_workspace", + "Create an ephemeral workspace directory for isolated operations", + "workspace", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "name": { "type": "string", "description": "Optional workspace name (auto-generated if not provided)" }, + "base_path": { "type": "string", "description": "Base path for workspaces (default: /tmp)" } + } + }) + ), + tool!( + "delete_workspace", + "Delete an ephemeral workspace and all its contents", + "workspace", + true, true, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the workspace to delete" }, + "force": { "type": "boolean", "description": "Force deletion without confirmation" } + }, + "required": ["path"] + }) + ), + tool!( + "create_venv", + "Create a Python virtual environment", + "workspace", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path where to create the venv" }, + "name": { "type": "string", "description": "Name of virtual environment (default: venv)" } + }, + "required": ["path"] + }) + ), + tool!( + "install_dependencies", + "Install Python dependencies from requirements.txt or specified packages", + "workspace", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to project with requirements.txt" }, + "package": { "type": "string", "description": "Specific package to install" } + }, + "required": ["path"] + }) + ), + tool!( + "workspace_info", + "Get information and statistics about a workspace directory", + "workspace", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "path": { "type": "string", "description": "Path to the workspace" } + }, + "required": ["path"] + }) + ), + + // ======================== + // Kanban Tools + // ======================== + tool!( + "kanban_list_board", + "List all columns and todos in the Kanban board", + "kanban", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" } + }, + "required": ["project_id"] + }) + ), + 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" }, + "column_id": { "type": "string", "description": "Column ID to place the todo" }, + "title": { "type": "string", "description": "Title of the todo" }, + "description": { "type": "string", "description": "Optional description" }, + "priority": { "type": "number", "description": "Priority (1-1000)" }, + "deploy_agent": { "type": "boolean", "description": "Whether to deploy an agent for this todo" }, + "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"] + }) + ), + tool!( + "kanban_update_todo", + "Update a todo item's title, description, status, or priority", + "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 update" }, + "title": { "type": "string", "description": "New title" }, + "description": { "type": "string", "description": "New description" }, + "status": { "type": "string", "description": "New status (pending, in_progress, completed, blocked)" }, + "priority": { "type": "number", "description": "New priority" } + }, + "required": ["project_id", "todo_id"] + }) + ), + tool!( + "kanban_delete_todo", + "Delete a todo item from the Kanban board", + "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 delete" } + }, + "required": ["project_id", "todo_id"] + }) + ), + 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"] + }) + ), + tool!( + "kanban_create_task", + "Create a sub-task within a todo", + "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 parent todo" }, + "title": { "type": "string", "description": "Title of the task" }, + "description": { "type": "string", "description": "Optional description" } + }, + "required": ["project_id", "todo_id", "title"] + }) + ), + tool!( + "kanban_update_task", + "Update a sub-task's title, description, or status", + "kanban", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "task_id": { "type": "string", "description": "ID of the task to update" }, + "title": { "type": "string", "description": "New title" }, + "description": { "type": "string", "description": "New description" }, + "status": { "type": "string", "description": "New status" } + }, + "required": ["project_id", "task_id"] + }) + ), + tool!( + "kanban_add_tag", + "Add a tag to a todo item", + "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" }, + "tag_name": { "type": "string", "description": "Tag name" }, + "tag_color": { "type": "string", "description": "Tag color (hex, default: #6366f1)" } + }, + "required": ["project_id", "todo_id", "tag_name"] + }) + ), + tool!( + "kanban_create_column", + "Create a new column in the Kanban board", + "kanban", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "name": { "type": "string", "description": "Column name" }, + "column_order": { "type": "number", "description": "Display order" } + }, + "required": ["project_id", "name"] + }) + ), + + // ======================== + // Documentation Tools + // ======================== + tool!( + "document_file", + "Analyze and generate documentation for a file", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "file_id": { "type": "string" } + }, + "required": ["project_id", "file_id"] + }) + ), + tool!( + "document_project", + "Generate a comprehensive summary of all project files", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" } + }, + "required": ["project_id"] + }) + ), + tool!( + "find_references", + "Find all references to a symbol across the project", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "file_path": { "type": "string", "description": "Path to find all references for" }, + "symbol_name": { "type": "string", "description": "Specific symbol name to find" }, + "type": { "type": "string", "enum": ["function", "struct", "enum", "trait", "module", "type_alias"], "description": "Filter by symbol type" } + }, + "required": ["project_id"] + }) + ), + tool!( + "file_dependencies", + "Get incoming or outgoing dependencies for a file", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "file_path": { "type": "string" }, + "direction": { "type": "string", "enum": ["incoming", "outgoing", "both"], "description": "Direction of dependencies (default: both)" } + }, + "required": ["project_id", "file_path"] + }) + ), + tool!( + "documentation_tree", + "Build a directory tree showing documentation coverage", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "root": { "type": "string", "description": "Optional root path to limit the tree" } + }, + "required": ["project_id"] + }) + ), + tool!( + "verify_documentation", + "Verify documentation freshness and compute coverage stats", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" } + }, + "required": ["project_id"] + }) + ), + tool!( + "document_folder", + "Document all files within a folder", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "folder_path": { "type": "string", "description": "Relative folder path to document" } + }, + "required": ["project_id", "folder_path"] + }) + ), + tool!( + "store_file_doc", + "Store documentation for a file", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "file_path": { "type": "string", "description": "Relative file path" }, + "doc": { + "type": "object", + "description": "FileDoc-compatible documentation payload", + "properties": { + "summary": { "type": "string" }, + "functions": { "type": "array", "items": { "type": "object" } }, + "structs": { "type": "array", "items": { "type": "object" } } + } + } + }, + "required": ["project_id", "file_path", "doc"] + }) + ), + tool!( + "get_documentation_context", + "Get the documentation context for a file", + "documentation", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "file_path": { "type": "string", "description": "Relative file path to get context for" } + }, + "required": ["project_id", "file_path"] + }) + ), + + // ======================== + // File Tools + // ======================== + tool!( + "list_files", + "List all files in a project", + "files", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string" }, + "path_prefix": { "type": "string", "description": "Optional path prefix to filter files" } + }, + "required": ["project_id"] + }) + ), + + // ======================== + // Strategic Tools + // ======================== + tool!( + "propose_strategic_item", + "Propose a new high-level strategic goal or todo", + "strategic", + false, true, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "title": { "type": "string", "description": "Title of the strategic item" }, + "description": { "type": "string", "description": "Detailed description" }, + "rationale": { "type": "string", "description": "Why this item is important" }, + "proposer_id": { "type": "string", "description": "Unique ID to prevent duplicates" } + }, + "required": ["project_id", "title", "description", "rationale", "proposer_id"] + }) + ), + tool!( + "change_planner_mode", + "Suggest switching the Planner's personality mode", + "strategic", + false, true, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" }, + "mode": { "type": "string", "enum": ["cooperative", "critical", "red_team", "socratic"], "description": "The planner mode to switch to" }, + "rationale": { "type": "string", "description": "Why this mode change is beneficial" } + }, + "required": ["project_id", "mode", "rationale"] + }) + ), + tool!( + "audit_assumptions", + "List the user's current assumptions about the project and identify the weakest ones", + "strategic", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" } + }, + "required": ["project_id"] + }) + ), + tool!( + "identify_blind_spots", + "Query project data for missing dependency chains, undocumented risks, and recurring blockers", + "strategic", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" } + }, + "required": ["project_id"] + }) + ), + tool!( + "check_dependencies", + "Analyze todo dependency chains for circular or missing dependencies", + "strategic", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" } + }, + "required": ["project_id"] + }) + ), + tool!( + "evaluate_plan_risk", + "Score the current plan for feasibility, edge-case coverage, and alignment", + "strategic", + false, false, true, + serde_json::json!({ + "type": "object", + "properties": { + "project_id": { "type": "string", "description": "Project ID" } + }, + "required": ["project_id"] + }) + ), + tool!( + "compare_project_patterns", + "Compare kanban structures across projects to detect inconsistencies", + "strategic", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "project_ids": { "type": "array", "items": { "type": "string" }, "description": "Optional list of project IDs to compare" } + } + }) + ), + tool!( + "find_similar_risks", + "Search across projects for similar risk patterns", + "strategic", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "risk_type": { "type": "string", "description": "Type of risk (dependency, testing, security)" }, + "project_ids": { "type": "array", "items": { "type": "string" }, "description": "Optional list of project IDs" } + } + }) + ), + + // ======================== + // Agent Tools + // ======================== + tool!( + "report_completion", + "Report completion status of a todo back to the Bridge", + "agent", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "todo_id": { "type": "string", "description": "ID of the todo being reported" }, + "status": { "type": "string", "enum": ["done", "failed", "delegated", "needs_retry", "pending_approval"], "description": "Completion status" }, + "summary": { "type": "string", "description": "Summary of what was done (max 3 sentences)" }, + "artifact_refs": { "type": "array", "items": { "type": "string" }, "description": "File paths, test logs" }, + "retry_plan": { "type": "string", "description": "Optional retry plan if status is needs_retry" }, + "subtask_ids": { "type": "array", "items": { "type": "string" }, "description": "Subtask IDs if status is delegated" } + }, + "required": ["todo_id", "status"] + }) + ), + tool!( + "submit_batch_plan", + "Submit a batch plan of mutations for pre-approval before execution", + "agent", + false, false, false, + serde_json::json!({ + "type": "object", + "properties": { + "plan_id": { "type": "string", "description": "Auto-generated plan ID" }, + "steps": { "type": "array", "description": "List of mutation steps", "items": { + "type": "object", + "properties": { + "tool": { "type": "string", "description": "Tool name (write_file, edit_file, bash)" }, + "arguments": { "type": "object", "description": "Tool arguments" }, + "expected_files": { "type": "array", "items": { "type": "string" }, "description": "Files expected to be modified" } + }, + "required": ["tool", "arguments"] + }}, + "rationale": { "type": "string", "description": "Why this batch of changes is needed" } + }, + "required": ["plan_id", "steps", "rationale"] + }) + ), + ] +} + #[cfg(test)] mod tests { use super::*;