diff options
Diffstat (limited to 'makima/src/llm/contract_tools.rs')
| -rw-r--r-- | makima/src/llm/contract_tools.rs | 1228 |
1 files changed, 0 insertions, 1228 deletions
diff --git a/makima/src/llm/contract_tools.rs b/makima/src/llm/contract_tools.rs deleted file mode 100644 index 38d1a7e..0000000 --- a/makima/src/llm/contract_tools.rs +++ /dev/null @@ -1,1228 +0,0 @@ -//! Tool definitions for contract management via LLM. -//! -//! These tools allow the LLM to manage contracts: create tasks, add files, -//! manage repositories, and handle phase transitions. - -use serde_json::json; -use uuid::Uuid; - -use super::tools::Tool; - -/// Available tools for contract management -pub static CONTRACT_TOOLS: once_cell::sync::Lazy<Vec<Tool>> = once_cell::sync::Lazy::new(|| { - vec![ - // ============================================================================= - // Query Tools - // ============================================================================= - Tool { - name: "get_contract_status".to_string(), - description: "Get an overview of the contract including current phase, file count, task count, and repository count.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "list_contract_files".to_string(), - description: "List all files in the contract with their names, descriptions, and phases.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "list_contract_tasks".to_string(), - description: "List all tasks in the contract with their names, status, and progress.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "list_contract_repositories".to_string(), - description: "List all repositories attached to the contract with their types and URLs/paths.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "read_file".to_string(), - description: "Read the full contents of a file including its body, transcript, and summary.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file to read" - } - }, - "required": ["file_id"] - }), - }, - // ============================================================================= - // File Management Tools - // ============================================================================= - Tool { - name: "create_empty_file".to_string(), - description: "Create a new empty file in the contract.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name for the new file" - }, - "description": { - "type": "string", - "description": "Optional description for the file" - } - }, - "required": ["name"] - }), - }, - // ============================================================================= - // Deliverable Management Tools - // ============================================================================= - Tool { - name: "mark_deliverable_complete".to_string(), - description: "Mark a phase deliverable as complete. Use this when you have verified that a deliverable requirement has been satisfied. Use get_phase_info or check_deliverables_met first to see available deliverable IDs.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "deliverable_id": { - "type": "string", - "description": "The ID of the deliverable to mark as complete (e.g., 'plan-document', 'pull-request', 'research-notes')" - }, - "phase": { - "type": "string", - "enum": ["research", "specify", "plan", "execute", "review"], - "description": "Phase the deliverable belongs to. Defaults to the current contract phase if not specified." - } - }, - "required": ["deliverable_id"] - }), - }, - // ============================================================================= - // Task Management Tools - // ============================================================================= - Tool { - name: "create_contract_task".to_string(), - description: "Create a new task within this contract. The task will be associated with the contract and can optionally use a contract repository.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Name of the task" - }, - "plan": { - "type": "string", - "description": "Detailed instructions/plan for what the task should accomplish" - }, - "repository_url": { - "type": "string", - "description": "Git repository URL or local path. If not specified, uses the contract's primary repository." - }, - "base_branch": { - "type": "string", - "description": "Optional base branch to start from (default: main)" - } - }, - "required": ["name", "plan"] - }), - }, - Tool { - name: "delegate_content_generation".to_string(), - description: "Create a task to generate substantial content instead of writing it directly. Use this for filling templates, writing documentation, generating user stories, or any substantial writing task. The task will be created and can be started separately.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file to update with generated content (optional - if not specified, creates a new task without file context)" - }, - "instruction": { - "type": "string", - "description": "Clear instructions for what content should be generated" - }, - "context": { - "type": "string", - "description": "Additional context to help generate appropriate content" - } - }, - "required": ["instruction"] - }), - }, - Tool { - name: "start_task".to_string(), - description: "Start a task that is in 'pending' status. The task will be sent to a connected daemon for execution. A daemon must be connected for this to work.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "task_id": { - "type": "string", - "description": "ID of the task to start" - } - }, - "required": ["task_id"] - }), - }, - // ============================================================================= - // Phase Management Tools - // ============================================================================= - Tool { - name: "get_phase_info".to_string(), - description: "Get detailed information about the current phase and what it means for the contract workflow.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "suggest_phase_transition".to_string(), - description: "Analyze whether the contract is ready to advance to the NEXT phase. Returns: currentPhase, nextPhase (the phase to advance TO), readiness status, and what's missing. Use this BEFORE calling advance_phase to know exactly which phase to advance to.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "advance_phase".to_string(), - description: "Advance the contract to the NEXT phase in sequence. Phases progress: research -> specify -> plan -> execute -> review. You can ONLY advance forward one step. Always use suggest_phase_transition first to check readiness and find the correct next phase. If the contract has phase_guard enabled, this will first return a pending_confirmation status with phase deliverables for user review. Call again with confirmed=true to complete the transition, or with feedback to request changes.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "new_phase": { - "type": "string", - "enum": ["specify", "plan", "execute", "review"], - "description": "The next phase to transition to. Must be exactly one step ahead of current phase (e.g., research->specify, specify->plan, plan->execute, execute->review)" - }, - "confirmed": { - "type": "boolean", - "description": "Set to true to confirm the phase transition when phase_guard is enabled. If omitted or false, returns deliverables for review." - }, - "feedback": { - "type": "string", - "description": "User feedback when requesting changes instead of confirming the transition. The feedback will be passed back to the task to address." - } - }, - "required": ["new_phase"] - }), - }, - // ============================================================================= - // Repository Management Tools - // ============================================================================= - Tool { - name: "list_daemon_directories".to_string(), - description: "List suggested directories from connected daemons. Use this to find valid local paths when the user wants to add a local repository or configure a target path. Returns working directories and home directories from connected agents.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "add_repository".to_string(), - description: "Add a repository to the contract. Can be a remote URL, local path, or create a managed repository.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "type": { - "type": "string", - "enum": ["remote", "local", "managed"], - "description": "Type of repository to add" - }, - "name": { - "type": "string", - "description": "Display name for the repository" - }, - "url": { - "type": "string", - "description": "Repository URL (for remote type) or local path (for local type). Not needed for managed." - }, - "is_primary": { - "type": "boolean", - "description": "Whether this should be the primary repository for the contract" - } - }, - "required": ["type", "name"] - }), - }, - Tool { - name: "set_primary_repository".to_string(), - description: "Set a repository as the primary repository for this contract. The primary repo is used by default for new tasks.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "repository_id": { - "type": "string", - "description": "ID of the repository to set as primary" - } - }, - "required": ["repository_id"] - }), - }, - // ============================================================================= - // Phase Guidance Tools - // ============================================================================= - Tool { - name: "get_phase_checklist".to_string(), - description: "Get a detailed checklist of phase deliverables showing what's been created vs what's expected. Includes completion percentage and suggestions for next steps.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - Tool { - name: "check_deliverables_met".to_string(), - description: "Check if all required deliverables are met for the current phase and whether the contract is ready to advance to the next phase. Returns detailed status including: deliverables_met (bool), ready_to_advance (bool), required_deliverables (list with status), missing items, and auto_progress_recommended (bool). Use this before calling advance_phase to ensure all requirements are satisfied. For simple contracts: Plan phase needs Plan document + Repository, Execute phase needs completed tasks + PR. For specification contracts: Each phase has specific required documents.".to_string(), - parameters: json!({ - "type": "object", - "properties": {} - }), - }, - // ============================================================================= - // Task Derivation Tools - // ============================================================================= - Tool { - name: "derive_tasks_from_file".to_string(), - description: "Parse a file (typically Task Breakdown) to extract a list of tasks. Returns structured task data that can be used with create_chained_tasks.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file to parse tasks from (usually a Task Breakdown document)" - } - }, - "required": ["file_id"] - }), - }, - Tool { - name: "create_chained_tasks".to_string(), - description: "Create multiple tasks in sequence with automatic chaining. Each task will continue from the previous task's work using continue_from_task_id.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "tasks": { - "type": "array", - "description": "List of tasks to create in order", - "items": { - "type": "object", - "properties": { - "name": { - "type": "string", - "description": "Task name" - }, - "plan": { - "type": "string", - "description": "Task plan/instructions" - } - }, - "required": ["name", "plan"] - } - } - }, - "required": ["tasks"] - }), - }, - // ============================================================================= - // Task Completion Processing Tools - // ============================================================================= - Tool { - name: "process_task_completion".to_string(), - description: "Analyze a completed task's output and suggest next actions. Returns summary, affected files, and suggestions for follow-up tasks or file updates.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "task_id": { - "type": "string", - "description": "ID of the completed task to analyze" - } - }, - "required": ["task_id"] - }), - }, - Tool { - name: "update_file_from_task".to_string(), - description: "Update a contract file with information from a completed task. Useful for updating Dev Notes or Implementation Log with task summaries.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file to update" - }, - "task_id": { - "type": "string", - "description": "ID of the task whose output should be added" - }, - "section_title": { - "type": "string", - "description": "Optional title for the section being added (e.g., 'Task: Implement Authentication')" - } - }, - "required": ["file_id", "task_id"] - }), - }, - // ============================================================================= - // Interactive Tools - // ============================================================================= - Tool { - name: "ask_user".to_string(), - description: "Ask the user one or more questions. Use this when you need clarification, want to offer choices, or need user input before proceeding. Questions can be single-select (user picks one option) or multi-select (user can pick multiple options). The question text supports markdown formatting.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "questions": { - "type": "array", - "description": "List of questions to ask the user", - "items": { - "type": "object", - "properties": { - "id": { - "type": "string", - "description": "Unique identifier for this question" - }, - "question": { - "type": "string", - "description": "The question to ask the user. Supports markdown formatting (bold, code, lists, etc.)" - }, - "options": { - "type": "array", - "items": { "type": "string" }, - "description": "Multiple choice options for the user to select from" - }, - "allowMultiple": { - "type": "boolean", - "description": "If true, user can select multiple options (multi-select). If false or omitted, user selects exactly one option (single-select). Default: false" - }, - "allowCustom": { - "type": "boolean", - "description": "If true, user can provide a custom text answer instead of selecting from options. Default: true" - } - }, - "required": ["id", "question", "options"] - } - } - }, - "required": ["questions"] - }), - }, - // ============================================================================= - // Transcript Analysis Tools - // ============================================================================= - Tool { - name: "analyze_transcript".to_string(), - description: "Analyze a file's transcript to extract requirements, decisions, and action items. Returns structured analysis including speaker statistics.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file containing the transcript to analyze" - } - }, - "required": ["file_id"] - }), - }, - Tool { - name: "create_contract_from_transcript".to_string(), - description: "Create a new contract from an analyzed transcript. Will extract requirements, decisions, and action items and create appropriate files and tasks in the new contract.".to_string(), - parameters: json!({ - "type": "object", - "properties": { - "file_id": { - "type": "string", - "description": "ID of the file containing the transcript" - }, - "name": { - "type": "string", - "description": "Optional name for the contract (otherwise auto-generated from analysis)" - }, - "description": { - "type": "string", - "description": "Optional description for the contract (otherwise auto-generated)" - }, - "include_requirements": { - "type": "boolean", - "description": "Whether to create a requirements file (default: true)" - }, - "include_decisions": { - "type": "boolean", - "description": "Whether to create a decisions file (default: true)" - }, - "include_action_items": { - "type": "boolean", - "description": "Whether to create tasks from action items (default: true)" - } - }, - "required": ["file_id"] - }), - }, - ] -}); - -/// Request for contract tool operations that require async database access -#[derive(Debug, Clone)] -pub enum ContractToolRequest { - // Query operations - GetContractStatus, - ListContractFiles, - ListContractTasks, - ListContractRepositories, - ReadFile { file_id: Uuid }, - - // File management - CreateEmptyFile { - name: String, - description: Option<String>, - }, - - // Deliverable management - MarkDeliverableComplete { - deliverable_id: String, - phase: Option<String>, - }, - - // Task management - CreateContractTask { - name: String, - plan: String, - repository_url: Option<String>, - base_branch: Option<String>, - }, - DelegateContentGeneration { - file_id: Option<Uuid>, - instruction: String, - context: Option<String>, - }, - StartTask { task_id: Uuid }, - - // Phase management - GetPhaseInfo, - SuggestPhaseTransition, - AdvancePhase { - new_phase: String, - /// Whether the user has confirmed the phase transition (for phase_guard) - confirmed: bool, - /// User feedback when they request changes instead of confirming - feedback: Option<String>, - }, - - // Repository management - ListDaemonDirectories, - AddRepository { - repo_type: String, - name: String, - url: Option<String>, - is_primary: bool, - }, - SetPrimaryRepository { repository_id: Uuid }, - - // Phase guidance - GetPhaseChecklist, - CheckDeliverablesMet, - - // Task derivation - DeriveTasksFromFile { file_id: Uuid }, - CreateChainedTasks { tasks: Vec<ChainedTaskDef> }, - - // Task completion processing - ProcessTaskCompletion { task_id: Uuid }, - UpdateFileFromTask { - file_id: Uuid, - task_id: Uuid, - section_title: Option<String>, - }, - - // Transcript analysis - AnalyzeTranscript { file_id: Uuid }, - CreateContractFromTranscript { - file_id: Uuid, - name: Option<String>, - description: Option<String>, - include_requirements: bool, - include_decisions: bool, - include_action_items: bool, - }, - -} - -/// Task definition for chained task creation -#[derive(Debug, Clone, serde::Deserialize)] -pub struct ChainedTaskDef { - pub name: String, - pub plan: String, -} - -/// Result from executing a contract tool -#[derive(Debug)] -pub struct ContractToolExecutionResult { - pub success: bool, - pub message: String, - pub data: Option<serde_json::Value>, - /// Request for async operations (handled by contract_chat handler) - pub request: Option<ContractToolRequest>, - /// Questions to ask the user (pauses conversation) - pub pending_questions: Option<Vec<super::tools::UserQuestion>>, -} - -/// Parse and validate a contract tool call, returning a ContractToolRequest for async handling -pub fn parse_contract_tool_call(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - match call.name.as_str() { - // Query operations - "get_contract_status" => parse_get_contract_status(), - "list_contract_files" => parse_list_contract_files(), - "list_contract_tasks" => parse_list_contract_tasks(), - "list_contract_repositories" => parse_list_contract_repositories(), - "read_file" => parse_read_file(call), - - // File management - "create_empty_file" => parse_create_empty_file(call), - - // Deliverable management - "mark_deliverable_complete" => parse_mark_deliverable_complete(call), - - // Task management - "create_contract_task" => parse_create_contract_task(call), - "delegate_content_generation" => parse_delegate_content_generation(call), - "start_task" => parse_start_task(call), - - // Phase management - "get_phase_info" => parse_get_phase_info(), - "suggest_phase_transition" => parse_suggest_phase_transition(), - "advance_phase" => parse_advance_phase(call), - - // Repository management - "list_daemon_directories" => parse_list_daemon_directories(), - "add_repository" => parse_add_repository(call), - "set_primary_repository" => parse_set_primary_repository(call), - - // Phase guidance - "get_phase_checklist" => parse_get_phase_checklist(), - "check_deliverables_met" => parse_check_deliverables_met(), - - // Task derivation - "derive_tasks_from_file" => parse_derive_tasks_from_file(call), - "create_chained_tasks" => parse_create_chained_tasks(call), - - // Task completion processing - "process_task_completion" => parse_process_task_completion(call), - "update_file_from_task" => parse_update_file_from_task(call), - - // Interactive tools - "ask_user" => parse_ask_user(call), - - // Transcript analysis tools - "analyze_transcript" => parse_analyze_transcript(call), - "create_contract_from_transcript" => parse_create_contract_from_transcript(call), - - _ => ContractToolExecutionResult { - success: false, - message: format!("Unknown contract tool: {}", call.name), - data: None, - request: None, - pending_questions: None, - }, - } -} - -// ============================================================================= -// Query Tool Parsing -// ============================================================================= - -fn parse_get_contract_status() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Getting contract status...".to_string(), - data: None, - request: Some(ContractToolRequest::GetContractStatus), - pending_questions: None, - } -} - -fn parse_list_contract_files() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Listing contract files...".to_string(), - data: None, - request: Some(ContractToolRequest::ListContractFiles), - pending_questions: None, - } -} - -fn parse_list_contract_tasks() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Listing contract tasks...".to_string(), - data: None, - request: Some(ContractToolRequest::ListContractTasks), - pending_questions: None, - } -} - -fn parse_list_contract_repositories() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Listing contract repositories...".to_string(), - data: None, - request: Some(ContractToolRequest::ListContractRepositories), - pending_questions: None, - } -} - -fn parse_read_file(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let file_id = parse_uuid_arg(call, "file_id"); - let Some(file_id) = file_id else { - return error_result("Missing or invalid required parameter: file_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Reading file...".to_string(), - data: None, - request: Some(ContractToolRequest::ReadFile { file_id }), - pending_questions: None, - } -} - -// ============================================================================= -// File Management Tool Parsing -// ============================================================================= - -fn parse_create_empty_file(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let name = call.arguments.get("name").and_then(|v| v.as_str()); - - let Some(name) = name else { - return error_result("Missing required parameter: name"); - }; - - let description = call - .arguments - .get("description") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: format!("Creating empty file '{}'...", name), - data: None, - request: Some(ContractToolRequest::CreateEmptyFile { - name: name.to_string(), - description, - }), - pending_questions: None, - } -} - -// ============================================================================= -// Deliverable Management Tool Parsing -// ============================================================================= - -fn parse_mark_deliverable_complete(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let deliverable_id = call - .arguments - .get("deliverable_id") - .and_then(|v| v.as_str()); - - let Some(deliverable_id) = deliverable_id else { - return error_result("Missing required parameter: deliverable_id"); - }; - - let phase = call - .arguments - .get("phase") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: format!("Marking deliverable '{}' as complete...", deliverable_id), - data: None, - request: Some(ContractToolRequest::MarkDeliverableComplete { - deliverable_id: deliverable_id.to_string(), - phase, - }), - pending_questions: None, - } -} - -// ============================================================================= -// Task Management Tool Parsing -// ============================================================================= - -fn parse_create_contract_task(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let name = call.arguments.get("name").and_then(|v| v.as_str()); - let plan = call.arguments.get("plan").and_then(|v| v.as_str()); - - let Some(name) = name else { - return error_result("Missing required parameter: name"); - }; - let Some(plan) = plan else { - return error_result("Missing required parameter: plan"); - }; - - let repository_url = call - .arguments - .get("repository_url") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - let base_branch = call - .arguments - .get("base_branch") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: format!("Creating task '{}'...", name), - data: None, - request: Some(ContractToolRequest::CreateContractTask { - name: name.to_string(), - plan: plan.to_string(), - repository_url, - base_branch, - }), - pending_questions: None, - } -} - -fn parse_delegate_content_generation(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let instruction = call.arguments.get("instruction").and_then(|v| v.as_str()); - - let Some(instruction) = instruction else { - return error_result("Missing required parameter: instruction"); - }; - - let file_id = parse_uuid_arg(call, "file_id"); - let context = call - .arguments - .get("context") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: "Creating content generation task...".to_string(), - data: None, - request: Some(ContractToolRequest::DelegateContentGeneration { - file_id, - instruction: instruction.to_string(), - context, - }), - pending_questions: None, - } -} - -fn parse_start_task(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let task_id = parse_uuid_arg(call, "task_id"); - - let Some(task_id) = task_id else { - return error_result("Missing or invalid required parameter: task_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Starting task...".to_string(), - data: None, - request: Some(ContractToolRequest::StartTask { task_id }), - pending_questions: None, - } -} - -// ============================================================================= -// Phase Management Tool Parsing -// ============================================================================= - -fn parse_get_phase_info() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Getting phase information...".to_string(), - data: None, - request: Some(ContractToolRequest::GetPhaseInfo), - pending_questions: None, - } -} - -fn parse_suggest_phase_transition() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Analyzing phase transition readiness...".to_string(), - data: None, - request: Some(ContractToolRequest::SuggestPhaseTransition), - pending_questions: None, - } -} - -fn parse_advance_phase(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let new_phase = call.arguments.get("new_phase").and_then(|v| v.as_str()); - - let Some(new_phase) = new_phase else { - return error_result("Missing required parameter: new_phase"); - }; - - let valid_phases = ["research", "specify", "plan", "execute", "review"]; - if !valid_phases.contains(&new_phase) { - return error_result("Invalid phase. Must be one of: research, specify, plan, execute, review"); - } - - // Parse optional confirmed flag (defaults to false for initial phase_guard check) - let confirmed = call - .arguments - .get("confirmed") - .and_then(|v| v.as_bool()) - .unwrap_or(false); - - // Parse optional feedback (for when user requests changes) - let feedback = call - .arguments - .get("feedback") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: format!("Advancing to '{}' phase...", new_phase), - data: None, - request: Some(ContractToolRequest::AdvancePhase { - new_phase: new_phase.to_string(), - confirmed, - feedback, - }), - pending_questions: None, - } -} - -// ============================================================================= -// Repository Management Tool Parsing -// ============================================================================= - -fn parse_list_daemon_directories() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Listing daemon directories...".to_string(), - data: None, - request: Some(ContractToolRequest::ListDaemonDirectories), - pending_questions: None, - } -} - -fn parse_add_repository(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let repo_type = call.arguments.get("type").and_then(|v| v.as_str()); - let name = call.arguments.get("name").and_then(|v| v.as_str()); - - let Some(repo_type) = repo_type else { - return error_result("Missing required parameter: type"); - }; - let Some(name) = name else { - return error_result("Missing required parameter: name"); - }; - - let valid_types = ["remote", "local", "managed"]; - if !valid_types.contains(&repo_type) { - return error_result("Invalid type. Must be one of: remote, local, managed"); - } - - let url = call - .arguments - .get("url") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - // Validate URL is provided for remote and local types - if (repo_type == "remote" || repo_type == "local") && url.is_none() { - return error_result("URL/path is required for remote and local repository types"); - } - - let is_primary = call - .arguments - .get("is_primary") - .and_then(|v| v.as_bool()) - .unwrap_or(false); - - ContractToolExecutionResult { - success: true, - message: format!("Adding {} repository '{}'...", repo_type, name), - data: None, - request: Some(ContractToolRequest::AddRepository { - repo_type: repo_type.to_string(), - name: name.to_string(), - url, - is_primary, - }), - pending_questions: None, - } -} - -fn parse_set_primary_repository(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let repository_id = parse_uuid_arg(call, "repository_id"); - let Some(repository_id) = repository_id else { - return error_result("Missing or invalid required parameter: repository_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Setting primary repository...".to_string(), - data: None, - request: Some(ContractToolRequest::SetPrimaryRepository { repository_id }), - pending_questions: None, - } -} - -// ============================================================================= -// Interactive Tool Parsing -// ============================================================================= - -fn parse_ask_user(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let questions_value = call.arguments.get("questions"); - - let Some(questions_array) = questions_value.and_then(|v| v.as_array()) else { - return error_result("Missing or invalid 'questions' parameter"); - }; - - let mut questions: Vec<super::tools::UserQuestion> = Vec::new(); - - for q in questions_array { - let id = q.get("id").and_then(|v| v.as_str()).unwrap_or("").to_string(); - let question = q.get("question").and_then(|v| v.as_str()).unwrap_or("").to_string(); - let options: Vec<String> = q - .get("options") - .and_then(|v| v.as_array()) - .map(|arr| { - arr.iter() - .filter_map(|o| o.as_str()) - .map(|s| s.to_string()) - .collect() - }) - .unwrap_or_default(); - let allow_multiple = q.get("allowMultiple").and_then(|v| v.as_bool()).unwrap_or(false); - let allow_custom = q.get("allowCustom").and_then(|v| v.as_bool()).unwrap_or(true); - - if id.is_empty() || question.is_empty() || options.is_empty() { - continue; - } - - questions.push(super::tools::UserQuestion { - id, - question, - options, - allow_multiple, - allow_custom, - }); - } - - if questions.is_empty() { - return error_result("No valid questions provided"); - } - - let question_count = questions.len(); - ContractToolExecutionResult { - success: true, - message: format!("Asking user {} question(s). Waiting for response...", question_count), - data: None, - request: None, - pending_questions: Some(questions), - } -} - -// ============================================================================= -// Phase Guidance Tool Parsing -// ============================================================================= - -fn parse_get_phase_checklist() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Getting phase checklist...".to_string(), - data: None, - request: Some(ContractToolRequest::GetPhaseChecklist), - pending_questions: None, - } -} - -fn parse_check_deliverables_met() -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: true, - message: "Checking if deliverables are met...".to_string(), - data: None, - request: Some(ContractToolRequest::CheckDeliverablesMet), - pending_questions: None, - } -} - -// ============================================================================= -// Task Derivation Tool Parsing -// ============================================================================= - -fn parse_derive_tasks_from_file(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let file_id = parse_uuid_arg(call, "file_id"); - let Some(file_id) = file_id else { - return error_result("Missing or invalid required parameter: file_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Deriving tasks from file...".to_string(), - data: None, - request: Some(ContractToolRequest::DeriveTasksFromFile { file_id }), - pending_questions: None, - } -} - -fn parse_create_chained_tasks(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let tasks_value = call.arguments.get("tasks"); - - let Some(tasks_array) = tasks_value.and_then(|v| v.as_array()) else { - return error_result("Missing or invalid 'tasks' parameter"); - }; - - let mut tasks: Vec<ChainedTaskDef> = Vec::new(); - - for task in tasks_array { - let name = task.get("name").and_then(|v| v.as_str()); - let plan = task.get("plan").and_then(|v| v.as_str()); - - match (name, plan) { - (Some(n), Some(p)) => { - tasks.push(ChainedTaskDef { - name: n.to_string(), - plan: p.to_string(), - }); - } - _ => { - return error_result("Each task must have 'name' and 'plan' fields"); - } - } - } - - if tasks.is_empty() { - return error_result("No valid tasks provided"); - } - - let task_count = tasks.len(); - ContractToolExecutionResult { - success: true, - message: format!("Creating {} chained task(s)...", task_count), - data: None, - request: Some(ContractToolRequest::CreateChainedTasks { tasks }), - pending_questions: None, - } -} - -// ============================================================================= -// Task Completion Processing Tool Parsing -// ============================================================================= - -fn parse_process_task_completion(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let task_id = parse_uuid_arg(call, "task_id"); - let Some(task_id) = task_id else { - return error_result("Missing or invalid required parameter: task_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Processing task completion...".to_string(), - data: None, - request: Some(ContractToolRequest::ProcessTaskCompletion { task_id }), - pending_questions: None, - } -} - -fn parse_update_file_from_task(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let file_id = parse_uuid_arg(call, "file_id"); - let task_id = parse_uuid_arg(call, "task_id"); - - let Some(file_id) = file_id else { - return error_result("Missing or invalid required parameter: file_id"); - }; - let Some(task_id) = task_id else { - return error_result("Missing or invalid required parameter: task_id"); - }; - - let section_title = call - .arguments - .get("section_title") - .and_then(|v| v.as_str()) - .map(|s| s.to_string()); - - ContractToolExecutionResult { - success: true, - message: "Updating file from task...".to_string(), - data: None, - request: Some(ContractToolRequest::UpdateFileFromTask { - file_id, - task_id, - section_title, - }), - pending_questions: None, - } -} - -// ============================================================================= -// Transcript Analysis Tool Parsing -// ============================================================================= - -fn parse_analyze_transcript(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let file_id = parse_uuid_arg(call, "file_id"); - let Some(file_id) = file_id else { - return error_result("Missing or invalid required parameter: file_id"); - }; - - ContractToolExecutionResult { - success: true, - message: "Analyzing transcript...".to_string(), - data: None, - request: Some(ContractToolRequest::AnalyzeTranscript { file_id }), - pending_questions: None, - } -} - -fn parse_create_contract_from_transcript(call: &super::tools::ToolCall) -> ContractToolExecutionResult { - let file_id = parse_uuid_arg(call, "file_id"); - let Some(file_id) = file_id else { - return error_result("Missing or invalid required parameter: file_id"); - }; - - let name = call.arguments.get("name").and_then(|v| v.as_str()).map(|s| s.to_string()); - let description = call.arguments.get("description").and_then(|v| v.as_str()).map(|s| s.to_string()); - let include_requirements = call.arguments.get("include_requirements").and_then(|v| v.as_bool()).unwrap_or(true); - let include_decisions = call.arguments.get("include_decisions").and_then(|v| v.as_bool()).unwrap_or(true); - let include_action_items = call.arguments.get("include_action_items").and_then(|v| v.as_bool()).unwrap_or(true); - - ContractToolExecutionResult { - success: true, - message: "Creating contract from transcript...".to_string(), - data: None, - request: Some(ContractToolRequest::CreateContractFromTranscript { - file_id, - name, - description, - include_requirements, - include_decisions, - include_action_items, - }), - pending_questions: None, - } -} - -// ============================================================================= -// Helper Functions -// ============================================================================= - -fn parse_uuid_arg(call: &super::tools::ToolCall, key: &str) -> Option<Uuid> { - call.arguments - .get(key) - .and_then(|v| v.as_str()) - .and_then(|s| Uuid::parse_str(s).ok()) -} - -fn error_result(message: &str) -> ContractToolExecutionResult { - ContractToolExecutionResult { - success: false, - message: message.to_string(), - data: None, - request: None, - pending_questions: None, - } -} |
