intent_engine/cli_handlers/
task.rs

1use crate::cli::TaskCommands;
2use crate::cli_handlers::read_stdin;
3use crate::cli_handlers::utils::print_task_context;
4use crate::error::{IntentError, Result};
5use crate::project::ProjectContext;
6use crate::tasks::TaskManager;
7use crate::workspace::WorkspaceManager;
8
9pub async fn handle_task_command(cmd: TaskCommands) -> Result<()> {
10    match cmd {
11        TaskCommands::Add {
12            name,
13            parent,
14            spec_stdin,
15        } => {
16            let ctx = ProjectContext::load_or_init().await?;
17            let task_mgr = TaskManager::new(&ctx.pool);
18
19            let spec = if spec_stdin {
20                Some(read_stdin()?)
21            } else {
22                None
23            };
24
25            let task = task_mgr
26                .add_task(&name, spec.as_deref(), parent, None)
27                .await?; // None = human (CLI)
28            println!("{}", serde_json::to_string_pretty(&task)?);
29        },
30
31        TaskCommands::Get { id, with_events } => {
32            let ctx = ProjectContext::load().await?;
33            let task_mgr = TaskManager::new(&ctx.pool);
34
35            if with_events {
36                let task = task_mgr.get_task_with_events(id).await?;
37                println!("{}", serde_json::to_string_pretty(&task)?);
38            } else {
39                let task = task_mgr.get_task(id).await?;
40                println!("{}", serde_json::to_string_pretty(&task)?);
41            }
42        },
43
44        TaskCommands::Update {
45            id,
46            name,
47            parent,
48            status,
49            complexity,
50            priority,
51            spec_stdin,
52        } => {
53            let ctx = ProjectContext::load_or_init().await?;
54            let task_mgr = TaskManager::new(&ctx.pool);
55
56            let spec = if spec_stdin {
57                Some(read_stdin()?)
58            } else {
59                None
60            };
61
62            // Convert priority string to integer
63            let priority_int = match &priority {
64                Some(p) => Some(crate::priority::PriorityLevel::parse_to_int(p)?),
65                None => None,
66            };
67
68            let parent_opt = parent.map(Some);
69            let task = task_mgr
70                .update_task(
71                    id,
72                    name.as_deref(),
73                    spec.as_deref(),
74                    parent_opt,
75                    status.as_deref(),
76                    complexity,
77                    priority_int,
78                )
79                .await?;
80            println!("{}", serde_json::to_string_pretty(&task)?);
81        },
82
83        TaskCommands::Del { id } => {
84            let ctx = ProjectContext::load_or_init().await?;
85            let task_mgr = TaskManager::new(&ctx.pool);
86
87            task_mgr.delete_task(id).await?;
88            println!(
89                "{}",
90                serde_json::to_string_pretty(&serde_json::json!({
91                    "success": true,
92                    "message": format!("Task {} deleted", id)
93                }))?
94            );
95        },
96
97        TaskCommands::List {
98            status,
99            parent,
100            sort_by,
101            limit,
102            offset,
103        } => {
104            let ctx = ProjectContext::load().await?;
105            let task_mgr = TaskManager::new(&ctx.pool);
106
107            let parent_opt = parent.map(|p| {
108                if p == "null" {
109                    None
110                } else {
111                    p.parse::<i64>().ok()
112                }
113            });
114
115            // Parse sort_by parameter
116            use crate::db::models::TaskSortBy;
117            let sort_by_parsed = match sort_by.as_deref() {
118                Some("id") => Some(TaskSortBy::Id),
119                Some("priority") => Some(TaskSortBy::Priority),
120                Some("time") => Some(TaskSortBy::Time),
121                Some("focus") => Some(TaskSortBy::FocusAware),
122                None => None, // Use default from find_tasks
123                Some(other) => {
124                    return Err(IntentError::InvalidInput(format!(
125                        "Invalid sort_by value '{}'. Valid values: id, priority, time, focus",
126                        other
127                    )));
128                },
129            };
130
131            let result = task_mgr
132                .find_tasks(status.as_deref(), parent_opt, sort_by_parsed, limit, offset)
133                .await?;
134            println!("{}", serde_json::to_string_pretty(&result)?);
135        },
136
137        TaskCommands::Start { id, with_events } => {
138            let ctx = ProjectContext::load_or_init().await?;
139            let task_mgr = TaskManager::new(&ctx.pool);
140
141            let task = task_mgr.start_task(id, with_events).await?;
142            println!("{}", serde_json::to_string_pretty(&task)?);
143        },
144
145        TaskCommands::Done => {
146            let ctx = ProjectContext::load_or_init().await?;
147            let task_mgr = TaskManager::new(&ctx.pool);
148
149            let task = task_mgr.done_task(false).await?; // false = human caller (CLI)
150            println!("{}", serde_json::to_string_pretty(&task)?);
151        },
152
153        TaskCommands::PickNext { format } => {
154            let ctx = ProjectContext::load_or_init().await?;
155            let task_mgr = TaskManager::new(&ctx.pool);
156
157            let response = task_mgr.pick_next().await?;
158
159            // Output based on format
160            match format.as_str() {
161                "json" => {
162                    println!("{}", serde_json::to_string_pretty(&response)?);
163                },
164                _ => {
165                    // Default to text format
166                    println!("{}", response.format_as_text());
167                },
168            }
169        },
170
171        TaskCommands::SpawnSubtask { name, spec_stdin } => {
172            let ctx = ProjectContext::load_or_init().await?;
173            let task_mgr = TaskManager::new(&ctx.pool);
174
175            let spec = if spec_stdin {
176                Some(read_stdin()?)
177            } else {
178                None
179            };
180
181            let subtask = task_mgr.spawn_subtask(&name, spec.as_deref()).await?;
182            println!("{}", serde_json::to_string_pretty(&subtask)?);
183        },
184
185        TaskCommands::DependsOn {
186            blocked_task_id,
187            blocking_task_id,
188        } => {
189            let ctx = ProjectContext::load().await?;
190
191            let dependency =
192                crate::dependencies::add_dependency(&ctx.pool, blocking_task_id, blocked_task_id)
193                    .await?;
194
195            let response = serde_json::json!({
196                "dependency_id": dependency.id,
197                "blocking_task_id": dependency.blocking_task_id,
198                "blocked_task_id": dependency.blocked_task_id,
199                "created_at": dependency.created_at,
200                "message": format!(
201                    "Dependency added: Task {} now depends on Task {}",
202                    blocked_task_id, blocking_task_id
203                )
204            });
205
206            println!("{}", serde_json::to_string_pretty(&response)?);
207        },
208
209        TaskCommands::Context { task_id } => {
210            let ctx = ProjectContext::load().await?;
211            let task_mgr = TaskManager::new(&ctx.pool);
212            let workspace_mgr = WorkspaceManager::new(&ctx.pool);
213
214            // If no task_id provided, use current task
215            let target_id = if let Some(id) = task_id {
216                id
217            } else {
218                let current = workspace_mgr.get_current_task().await?;
219                current.current_task_id.ok_or_else(|| {
220                    IntentError::InvalidInput(
221                        "No task currently focused. Use 'ie task start <ID>' or provide task_id"
222                            .to_string(),
223                    )
224                })?
225            };
226
227            let context = task_mgr.get_task_context(target_id).await?;
228
229            // Format and print the context
230            print_task_context(&context)?;
231        },
232    }
233
234    Ok(())
235}