1use super::Command;
2use crate::agent::AgentProvider;
3use crate::assets::layered::LayeredAssetManager;
4use crate::context::AppContext;
5use crate::docker::DockerManager;
6use crate::docker::composer::DockerComposer;
7use crate::docker::image_manager::DockerImageManager;
8use crate::docker::template_manager::DockerTemplateManager;
9use crate::git::RepoManager;
10use crate::repo_utils::find_repository_root;
11use crate::task::Task;
12use crate::task_runner::TaskRunner;
13use async_trait::async_trait;
14use std::error::Error;
15use std::path::Path;
16use std::sync::Arc;
17
18pub struct DebugCommand {
19 pub name: String,
20 pub agent: Option<String>,
21 pub tech_stack: Option<String>,
22 pub project: Option<String>,
23}
24
25#[async_trait]
26impl Command for DebugCommand {
27 async fn execute(&self, ctx: &AppContext) -> Result<(), Box<dyn Error>> {
28 println!("Starting debug session: {}", self.name);
29
30 let repo_root = find_repository_root(Path::new("."))?;
31
32 let tech_stack = match &self.tech_stack {
34 Some(ts) => {
35 println!("Using tech stack: {ts}");
36 ts.clone()
37 }
38 None => match ctx.repository_context().detect_tech_stack(&repo_root).await {
39 Ok(detected) => {
40 println!("Auto-detected tech stack: {detected}");
41 detected
42 }
43 Err(e) => {
44 eprintln!("Warning: Failed to detect tech stack: {e}. Using default.");
45 "default".to_string()
46 }
47 },
48 };
49
50 let project = match &self.project {
52 Some(p) => {
53 println!("Using project: {p}");
54 Some(p.clone())
55 }
56 None => {
57 match ctx
58 .repository_context()
59 .detect_project_name(&repo_root)
60 .await
61 {
62 Ok(detected) => {
63 println!("Auto-detected project name: {detected}");
64 Some(detected)
65 }
66 Err(e) => {
67 eprintln!("Warning: Failed to detect project name: {e}. Using default.");
68 Some("default".to_string())
69 }
70 }
71 }
72 };
73
74 let debug_instructions = format!(
76 "# Debug Session: {}\n\nThis is an interactive debug session for exploring and testing.",
77 self.name
78 );
79 let temp_dir = ctx.xdg_directories().runtime_dir().join("tmp");
80 ctx.file_system().create_dir(&temp_dir).await?;
81 let instructions_file = temp_dir.join(format!("{}-debug.md", self.name));
82 ctx.file_system()
83 .write_file(&instructions_file, &debug_instructions)
84 .await?;
85
86 let source_commit = ctx
88 .git_operations()
89 .get_current_commit(&repo_root)
90 .await
91 .unwrap_or_else(|_| "HEAD".to_string());
92
93 let timestamp = chrono::Local::now();
95 let task_id = format!("{}-debug-{}", timestamp.format("%Y-%m-%d-%H%M"), self.name);
96 let branch_name = format!("tsk/{task_id}");
97
98 let agent = self
99 .agent
100 .clone()
101 .unwrap_or_else(|| AgentProvider::default_agent().to_string());
102
103 let mut task = Task::new(
104 task_id.clone(),
105 repo_root.clone(),
106 self.name.clone(),
107 "debug".to_string(),
108 instructions_file.to_string_lossy().to_string(),
109 agent,
110 0, branch_name,
112 source_commit,
113 tech_stack,
114 project.unwrap_or_else(|| "default".to_string()),
115 chrono::Local::now(),
116 repo_root.clone(), );
118
119 let repo_manager = RepoManager::new(
120 ctx.xdg_directories(),
121 ctx.file_system(),
122 ctx.git_operations(),
123 );
124
125 let (copied_repo_path, _) = repo_manager
127 .copy_repo(&task_id, &repo_root, Some(&task.source_commit))
128 .await
129 .map_err(|e| format!("Failed to copy repository: {e}"))?;
130
131 task.copied_repo_path = copied_repo_path;
133 let docker_manager = DockerManager::new(ctx.docker_client(), ctx.file_system());
134
135 let asset_manager = Arc::new(LayeredAssetManager::new_with_standard_layers(
137 Some(&repo_root),
138 &ctx.xdg_directories(),
139 ));
140 let template_manager =
141 DockerTemplateManager::new(asset_manager.clone(), ctx.xdg_directories());
142 let composer = DockerComposer::new(DockerTemplateManager::new(
143 asset_manager,
144 ctx.xdg_directories(),
145 ));
146 let image_manager = Arc::new(DockerImageManager::new(
147 ctx.docker_client(),
148 template_manager,
149 composer,
150 ));
151
152 let task_runner = TaskRunner::new(
153 repo_manager,
154 docker_manager,
155 image_manager,
156 ctx.file_system(),
157 ctx.notification_client(),
158 );
159
160 task_runner
162 .execute_task(&task, true)
163 .await
164 .map_err(|e| e.message)?;
165
166 let _ = ctx.file_system().remove_file(&instructions_file).await;
168
169 Ok(())
170 }
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 #[test]
178 fn test_debug_command_structure() {
179 let cmd = DebugCommand {
180 name: "test-debug".to_string(),
181 agent: Some("claude_code".to_string()),
182 tech_stack: None,
183 project: None,
184 };
185
186 assert_eq!(cmd.name, "test-debug");
188 assert_eq!(cmd.agent, Some("claude_code".to_string()));
189 assert_eq!(cmd.tech_stack, None);
190 assert_eq!(cmd.project, None);
191 }
192}