tasks.py
1 """ 2 Task management for Ag3ntum. 3 4 Handles reading tasks from files and CLI, and managing task state. 5 """ 6 import logging 7 from pathlib import Path 8 from typing import Optional 9 10 from .exceptions import TaskError 11 12 logger = logging.getLogger(__name__) 13 14 15 class TaskManager: 16 """ 17 Manages task loading and processing. 18 19 Tasks can be loaded from: 20 - A custom file path via --task-file 21 - Directly from a string via --task 22 """ 23 24 def __init__(self, working_dir: Optional[Path] = None) -> None: 25 """ 26 Initialize the task manager. 27 28 Args: 29 working_dir: Working directory for task files. 30 """ 31 self._working_dir = working_dir or Path.cwd() 32 33 def load_from_file(self, file_path: str) -> str: 34 """ 35 Load task from a file. 36 37 Args: 38 file_path: Path to task file (absolute or relative to working dir). 39 40 Returns: 41 The task content as a string. 42 43 Raises: 44 TaskError: If the file cannot be read. 45 """ 46 task_path = Path(file_path) 47 if not task_path.is_absolute(): 48 task_path = self._working_dir / task_path 49 50 if not task_path.exists(): 51 raise TaskError(f"Task file not found: {task_path}") 52 53 try: 54 content = task_path.read_text().strip() 55 if not content: 56 raise TaskError(f"Task file is empty: {task_path}") 57 58 logger.info(f"Loaded task from {task_path}") 59 return content 60 except IOError as e: 61 raise TaskError(f"Failed to read task file {task_path}: {e}") from e 62 63 def load_from_string(self, task: str) -> str: 64 """ 65 Load task from a string. 66 67 Args: 68 task: The task content as a string. 69 70 Returns: 71 The task content (validated). 72 73 Raises: 74 TaskError: If the task is empty. 75 """ 76 content = task.strip() 77 if not content: 78 raise TaskError("Task cannot be empty") 79 80 return content 81 82 def load( 83 self, 84 task: Optional[str] = None, 85 file_path: Optional[str] = None 86 ) -> str: 87 """ 88 Load task from string or file. 89 90 Priority: 91 1. If task string is provided, use it 92 2. If file_path is provided, load from that file 93 3. Otherwise, look for input/task.md in working directory 94 95 Args: 96 task: Task content as a string. 97 file_path: Path to task file. 98 99 Returns: 100 The task content. 101 102 Raises: 103 TaskError: If no task can be loaded. 104 """ 105 if task: 106 return self.load_from_string(task) 107 108 # Use provided file_path or default to input/task.md 109 default_path = "input/task.md" 110 return self.load_from_file(file_path or default_path) 111 112 113 def load_task( 114 task: Optional[str] = None, 115 file_path: Optional[str] = None, 116 working_dir: Optional[Path] = None 117 ) -> str: 118 """ 119 Convenience function to load a task. 120 121 Args: 122 task: Task content as a string. 123 file_path: Path to task file. 124 working_dir: Working directory for task files. 125 126 Returns: 127 The task content. 128 """ 129 manager = TaskManager(working_dir) 130 return manager.load(task, file_path)