//! Unified K4000 MCP Server implementation
//!
//! Combines task analysis and code review functionality into a single server.

use std::path::PathBuf;
use std::process::Command;
use std::sync::Arc;

use crate::{
    generate_frontmatter, parse_frontmatter, Feature, FeatureApprovalMode, FeatureComplexity,
    Frontmatter, IterationStatus, ProjectConfig, ProjectContext, Question, QuestionCategory,
    ReviewCategory, ReviewFinding, ReviewSession, ReviewStatus, ReviewSummary, Severity,
    StateManager, TaskState, TaskStatus,
};
use rmcp::{
    model::{CallToolResult, Content, ServerCapabilities, ServerInfo},
    schemars, tool, Error as McpError, ServerHandler,
};
use serde::{Deserialize, Serialize};
use tokio::fs;
use uuid::Uuid;

/// Default maximum iterations before asking human
const DEFAULT_MAX_ITERATIONS: u32 = 3;

/// Unified K4000 MCP Server
///
/// Provides all task management and code review tools in a single server.
#[derive(Clone)]
pub struct K4000Server {
    state: Arc<StateManager>,
    max_iterations: u32,
}

// ==================== Request Types (Task) ====================

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CreateTaskRequest {
    #[schemars(
        description = "Name of the task (will be slugified: lowercase, spaces to hyphens, max 40 chars)"
    )]
    pub name: String,
    #[schemars(description = "Description of the task (written as-is to task.md)")]
    pub description: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct TaskNameRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct AnalyzeTaskRequest {
    #[schemars(description = "Name of the task to analyze")]
    pub task_name: String,
    #[schemars(description = "Path to the project (required if not set during create_task)")]
    pub project_path: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ProjectPathRequest {
    #[schemars(description = "Path to the project root")]
    pub project_path: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SaveQuestionsRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "JSON array of questions with id, question, and category fields")]
    pub questions_json: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SaveQaResponseRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the question being answered")]
    pub question_id: String,
    #[schemars(description = "The human's answer")]
    pub answer: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct UpdateTaskPlanRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "The complete updated plan.md content")]
    pub content: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SetStatusRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(
        description = "New status: draft, analyzing, awaiting_answers, ready, implementing, reviewing, done"
    )]
    pub status: String,
    #[schemars(description = "Files to review (required when setting status to 'reviewing')")]
    pub files: Option<Vec<String>>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ListProjectTasksRequest {
    #[schemars(
        description = "Path to the project root (optional - defaults to current working directory)"
    )]
    pub project_path: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ListTasksRequest {
    #[schemars(
        description = "Path to the project root (optional - if provided, only shows tasks for this project)"
    )]
    pub project_path: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MigrateTasksRequest {
    #[schemars(
        description = "Path to the project root (optional - defaults to current working directory)"
    )]
    pub project_path: Option<String>,
    #[schemars(description = "Whether to delete the old features/ folder after migration")]
    pub delete_old: Option<bool>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SaveFeaturesRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(
        description = "JSON array of features. Required fields: id, category, description, steps (verification steps array), acceptance_criteria (testable criteria array). Optional: complexity, dependencies"
    )]
    pub features_json: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MarkFeatureCompleteRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the feature to mark as complete (e.g., 'F1')")]
    pub feature_id: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MarkFeatureVerifiedRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the feature to mark as verified (e.g., 'F1')")]
    pub feature_id: String,
    #[schemars(
        description = "Confirmation that ALL verification steps have been verified. Must be true."
    )]
    pub confirmed: bool,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct LogProgressRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "Name of the agent logging this progress (e.g., 'claude', 'user')")]
    pub agent_name: String,
    #[schemars(description = "Progress message to log")]
    pub message: String,
    #[schemars(description = "Optional feature ID this progress relates to (e.g., 'F1')")]
    pub feature_id: Option<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct GetProgressRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
}

// ==================== Request Types (Review) ====================

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CreateReviewRequest {
    #[schemars(description = "Name of the task being reviewed")]
    pub task_name: String,
    #[schemars(description = "List of file paths to review")]
    pub files: Vec<String>,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct ReviewIdRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct SubmitFindingsRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(
        description = "JSON array of findings with category, file, severity, description, and optional line/suggestion"
    )]
    pub findings_json: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct MarkResolvedRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(description = "ID of the finding to mark as resolved")]
    pub finding_id: String,
}

#[derive(Debug, Deserialize, schemars::JsonSchema)]
pub struct CompleteIterationRequest {
    #[schemars(description = "Name of the task")]
    pub task_name: String,
    #[schemars(description = "ID of the review session")]
    pub review_id: String,
    #[schemars(description = "Whether to start the next iteration if needed")]
    pub start_next: bool,
}

// ==================== Response Types ====================

/// Summary of a task found in the project's tasks directory
#[derive(Debug, Serialize, Deserialize)]
pub struct ProjectTaskSummary {
    pub row: usize,
    pub name: String,
    pub folder_path: String,
    pub status: String,
    pub is_done: bool,
    pub question_count: usize,
    pub answered_count: usize,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub description: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub parts: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub estimate: Option<String>,
    pub has_task_md: bool,
    pub has_plan_md: bool,
    pub has_questions_md: bool,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct ListProjectTasksResponse {
    pub tasks: Vec<ProjectTaskSummary>,
    pub project_path: String,
    pub total_count: usize,
    pub done_count: usize,
    pub untracked_count: usize,
    pub has_features_folder: bool,
    pub feature_approval_mode: String,
    pub has_project_config: bool,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub config_message: Option<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct MigrationResult {
    pub success: bool,
    pub migrated_count: usize,
    pub migrated_tasks: Vec<String>,
    pub errors: Vec<String>,
    pub features_folder_deleted: bool,
}

// ==================== Internal Types ====================

#[derive(Debug, Serialize, Deserialize)]
pub struct QuestionInput {
    pub id: String,
    pub question: String,
    #[serde(default)]
    pub category: String,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FeatureInput {
    pub id: String,
    pub category: String,
    pub description: String,
    #[serde(default)]
    pub steps: Vec<String>,
    #[serde(default)]
    pub acceptance_criteria: Vec<String>,
    #[serde(default)]
    pub complexity: Option<String>,
    #[serde(default)]
    pub dependencies: Vec<String>,
}

#[derive(Debug, Serialize, Deserialize)]
pub struct FindingInput {
    pub category: String,
    pub file: String,
    #[serde(default)]
    pub line: Option<u32>,
    pub severity: String,
    pub description: String,
    #[serde(default)]
    pub suggestion: Option<String>,
}

// ==================== Helper Functions ====================

fn slugify_name(name: &str) -> String {
    let slug: String = name
        .chars()
        .map(|c| {
            if c.is_alphanumeric() {
                c.to_ascii_lowercase()
            } else if c == ' ' || c == '-' || c == '_' {
                '-'
            } else {
                '\0'
            }
        })
        .filter(|&c| c != '\0')
        .collect();

    let mut result = String::new();
    let mut prev_hyphen = false;
    for c in slug.chars() {
        if c == '-' {
            if !prev_hyphen && !result.is_empty() {
                result.push(c);
            }
            prev_hyphen = true;
        } else {
            result.push(c);
            prev_hyphen = false;
        }
    }

    let result = result.trim_end_matches('-');
    if result.len() > 40 {
        let truncated = &result[..40];
        if let Some(last_hyphen) = truncated.rfind('-') {
            if last_hyphen > 30 {
                return truncated[..last_hyphen].to_string();
            }
        }
        truncated.to_string()
    } else {
        result.to_string()
    }
}

/// Generate markdown format for questions (legacy, kept for reference)
#[allow(dead_code)]
fn generate_questions_md(questions: &[Question]) -> String {
    if questions.is_empty() {
        return String::from("# Questions & Answers\n\n*No questions yet.*\n");
    }

    let mut content = String::from("# Questions & Answers\n");

    for (i, q) in questions.iter().enumerate() {
        content.push_str(&format!("\n## Q{}: {}\n", i + 1, q.question));
        content.push_str(&format!("**Category:** {}\n", q.category));

        if let Some(answer) = &q.answer {
            content.push_str(&format!("**Answer:** {}\n", answer));
        } else {
            content.push_str("**Answer:** *Pending*\n");
        }
    }

    content
}

/// Generate JSON format for features (legacy, kept for reference)
#[allow(dead_code)]
fn generate_feature_list_json(features: &[Feature]) -> String {
    serde_json::to_string_pretty(features).unwrap_or_else(|_| "[]".to_string())
}

fn truncate_string(s: &str, max_len: usize) -> String {
    if s.len() <= max_len {
        return s.to_string();
    }
    let truncate_at = s
        .char_indices()
        .take_while(|(i, _)| *i < max_len - 3)
        .last()
        .map(|(i, c)| i + c.len_utf8())
        .unwrap_or(0);
    format!("{}...", &s[..truncate_at])
}

fn extract_description_from_task_md(markdown: &str) -> String {
    let mut found_content = false;
    for line in markdown.lines() {
        let line = line.trim();
        if line.is_empty() {
            continue;
        }
        if line.starts_with('#') {
            found_content = true;
            continue;
        }
        if found_content || !line.starts_with('#') {
            return truncate_string(line, 100);
        }
    }
    String::new()
}

fn get_cwd() -> String {
    std::env::current_dir()
        .map(|p| p.to_string_lossy().to_string())
        .unwrap_or_else(|_| ".".to_string())
}

async fn collect_task_folders(tasks_dir: &std::path::Path) -> Vec<(PathBuf, String)> {
    let mut results = Vec::new();

    let Ok(mut entries) = fs::read_dir(tasks_dir).await else {
        return results;
    };

    while let Ok(Some(entry)) = entries.next_entry().await {
        let path = entry.path();
        let name = entry.file_name().to_string_lossy().to_string();
        if name.starts_with('.') {
            continue;
        }

        if let Ok(file_type) = entry.file_type().await {
            if file_type.is_dir() {
                let task_md = path.join("task.md");
                if task_md.exists() {
                    results.push((path, name));
                }
            }
        }
    }

    results
}

async fn collect_tasks_from_state(state_dir: &std::path::Path) -> Vec<String> {
    let mut results = Vec::new();

    let Ok(mut entries) = fs::read_dir(state_dir).await else {
        return results;
    };

    while let Ok(Some(entry)) = entries.next_entry().await {
        let name = entry.file_name().to_string_lossy().to_string();
        if name.starts_with('.') {
            continue;
        }

        if let Ok(file_type) = entry.file_type().await {
            if file_type.is_dir() {
                let state_file = entry.path().join("state.json");
                if state_file.exists() {
                    results.push(name);
                }
            }
        }
    }

    results
}

fn parse_category(s: &str) -> ReviewCategory {
    match s.to_lowercase().as_str() {
        "bugs" | "bug" => ReviewCategory::Bugs,
        "best_practices" | "best-practices" | "bestpractices" => ReviewCategory::BestPractices,
        "security" => ReviewCategory::Security,
        "performance" => ReviewCategory::Performance,
        "simplification" | "simplify" => ReviewCategory::Simplification,
        _ => ReviewCategory::Bugs,
    }
}

fn parse_severity(s: &str) -> Severity {
    match s.to_lowercase().as_str() {
        "critical" => Severity::Critical,
        "high" => Severity::High,
        "medium" => Severity::Medium,
        "low" => Severity::Low,
        "info" | "informational" => Severity::Info,
        _ => Severity::Medium,
    }
}

// ==================== Server Implementation ====================

impl K4000Server {
    pub fn new(project_path: PathBuf) -> Self {
        Self {
            state: Arc::new(StateManager::new(project_path)),
            max_iterations: DEFAULT_MAX_ITERATIONS,
        }
    }

    pub fn with_max_iterations(mut self, max: u32) -> Self {
        self.max_iterations = max;
        self
    }

    fn state_for_project(&self, project_path: Option<&str>) -> StateManager {
        match project_path {
            Some(path) => StateManager::new(path),
            None => StateManager::new(get_cwd()),
        }
    }
}

#[tool(tool_box)]
impl K4000Server {
    // ==================== Task Tools ====================

    // NOTE: Not exposed as MCP tool - use list_project_tasks instead.
    // list_project_tasks provides richer info and checks old .state/ directory.
    #[allow(dead_code)]
    async fn list_tasks(&self, req: ListTasksRequest) -> Result<CallToolResult, McpError> {
        let state_mgr = self.state_for_project(req.project_path.as_deref());

        let all_tasks = state_mgr
            .list_tasks()
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let json = serde_json::to_string_pretty(&all_tasks)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(
        description = "List all task folders in the project's ./tasks/ directory. Shows status (done/implementing/untracked), and question counts. Use this to discover tasks to analyze and implement."
    )]
    async fn list_project_tasks(
        &self,
        #[tool(aggr)] req: ListProjectTasksRequest,
    ) -> Result<CallToolResult, McpError> {
        let project_path = req.project_path.unwrap_or_else(get_cwd);
        let state_mgr = StateManager::new(&project_path);
        let tasks_dir = state_mgr.tasks_dir();

        let has_features_folder = state_mgr.has_features_folder();
        // Ensure config is initialized (creates tasks/ and .config/ with defaults if missing)
        let (project_config, config_message) = match state_mgr.ensure_config_initialized().await {
            Ok((config, msg)) => (config, msg),
            Err(e) => {
                // Include error in config_message so user knows something went wrong
                (
                    ProjectConfig::default(),
                    Some(format!("Warning: Failed to initialize config: {}", e)),
                )
            }
        };
        let has_project_config = state_mgr.has_project_config();

        // tasks_dir should now exist after ensure_config_initialized, but check anyway
        if !tasks_dir.exists() {
            let response = ListProjectTasksResponse {
                tasks: vec![],
                project_path,
                total_count: 0,
                done_count: 0,
                untracked_count: 0,
                has_features_folder,
                feature_approval_mode: project_config.feature_approval_mode.to_string(),
                has_project_config,
                config_message,
            };
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        let task_folders = collect_task_folders(&tasks_dir).await;
        // Also check old .state/ directory for migration
        let state_tasks = collect_tasks_from_state(&state_mgr.old_state_dir()).await;

        let mut seen_names: std::collections::HashSet<String> = std::collections::HashSet::new();
        let mut all_tasks: Vec<(PathBuf, String)> = Vec::new();

        for (path, name) in task_folders {
            seen_names.insert(name.clone());
            all_tasks.push((path, name));
        }

        for name in state_tasks {
            if !seen_names.contains(&name) {
                let folder_path = state_mgr.task_folder(&name);
                all_tasks.push((folder_path, name));
            }
        }

        let mut tasks = Vec::new();
        let mut done_count = 0;
        let mut untracked_count = 0;

        for (folder_path, name) in all_tasks {
            let folder_path_str = folder_path.to_string_lossy().to_string();
            let state_opt = state_mgr.load_task_state(&name).await.ok();

            // Load questions from questions.json
            let questions = state_mgr.read_questions(&name).await.unwrap_or_default();
            let question_count = questions.len();
            let answered_count = questions.iter().filter(|q| q.answered).count();

            let (status, is_done) = match state_opt {
                Some(state) => {
                    let is_done = state.status == TaskStatus::Done;
                    (state.status.to_string(), is_done)
                }
                None => ("untracked".to_string(), false),
            };

            if is_done {
                done_count += 1;
            }
            if status == "untracked" {
                untracked_count += 1;
            }

            let has_task_md = state_mgr.task_md_path(&name).exists();
            let has_plan_md = state_mgr.plan_md_path(&name).exists();
            let has_questions_md = state_mgr.questions_json_path(&name).exists();

            let (description, parts, estimate) = if has_plan_md {
                if let Ok(plan_content) = state_mgr.read_plan_md(&name).await {
                    let parsed = parse_frontmatter(&plan_content);
                    if let Some(fm) = parsed.frontmatter {
                        let desc = if fm.description.is_empty() {
                            None
                        } else {
                            Some(fm.description)
                        };
                        let parts_str = if fm.parts.is_empty() {
                            None
                        } else {
                            Some(fm.parts.join(", "))
                        };
                        let est = fm.estimate.map(|e| e.normal);
                        (desc, parts_str, est)
                    } else {
                        (None, None, None)
                    }
                } else {
                    (None, None, None)
                }
            } else if has_task_md {
                if let Ok(task_content) = state_mgr.read_task_md(&name).await {
                    let desc = extract_description_from_task_md(&task_content);
                    let desc = if desc.is_empty() { None } else { Some(desc) };
                    (desc, None, None)
                } else {
                    (None, None, None)
                }
            } else {
                (None, None, None)
            };

            tasks.push(ProjectTaskSummary {
                row: 0,
                name,
                folder_path: folder_path_str,
                status,
                is_done,
                question_count,
                answered_count,
                description,
                parts,
                estimate,
                has_task_md,
                has_plan_md,
                has_questions_md,
            });
        }

        tasks.sort_by(|a, b| a.name.cmp(&b.name));

        for (i, task) in tasks.iter_mut().enumerate() {
            task.row = i + 1;
        }

        let total_count = tasks.len();
        let response = ListProjectTasksResponse {
            tasks,
            project_path,
            total_count,
            done_count,
            untracked_count,
            has_features_folder,
            feature_approval_mode: project_config.feature_approval_mode.to_string(),
            has_project_config,
            config_message,
        };

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Create a new task. Creates tasks/{name}/ folder with task.md containing the raw description. Always uses current working directory's ./tasks/ folder."
    )]
    async fn create_task(
        &self,
        #[tool(aggr)] req: CreateTaskRequest,
    ) -> Result<CallToolResult, McpError> {
        let project_path = get_cwd();
        let state_mgr = StateManager::new(&project_path);

        // Ensure config is initialized before any task operation (creates tasks/ and .config/)
        let config_message = match state_mgr.ensure_config_initialized().await {
            Ok((_, msg)) => msg,
            Err(e) => Some(format!("Warning: Failed to initialize config: {}", e)),
        };

        let task_name = slugify_name(&req.name);

        if task_name.is_empty() {
            return Ok(CallToolResult::success(vec![Content::text(
                "{\"success\": false, \"message\": \"Task name cannot be empty after slugification\"}"
                    .to_string(),
            )]));
        }

        if state_mgr.task_exists(&task_name).await {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Task '{}' already exists\"}}",
                task_name
            ))]));
        }

        let state = TaskState::new();

        state_mgr
            .save_task_state(&task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        state_mgr
            .ensure_task_folder(&task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let task_md_path = state_mgr.task_md_path(&task_name);
        fs::write(&task_md_path, &req.description)
            .await
            .map_err(|e| {
                McpError::internal_error(format!("Failed to write task.md: {}", e), None)
            })?;

        let _ = state_mgr
            .append_progress(&task_name, "k4000", "Task created")
            .await;

        let mut response = serde_json::json!({
            "success": true,
            "task_name": task_name,
            "original_name": req.name,
            "project_path": project_path,
            "folder_path": state_mgr.task_folder(&task_name).to_string_lossy().to_string(),
            "status": "draft",
            "message": "Task created successfully. task.md written with raw description.",
            "next_step": "Would you like me to analyze this task now? Call analyze_task to identify gaps and generate clarifying questions.",
            "prompt_user": true
        });

        if let Some(msg) = config_message {
            response["config_message"] = serde_json::Value::String(msg);
        }

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(description = "Get the current status and full state of a task")]
    async fn get_task_status(
        &self,
        #[tool(aggr)] req: TaskNameRequest,
    ) -> Result<CallToolResult, McpError> {
        let state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let json = serde_json::to_string_pretty(&state)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(description = "Get the task.md content (user's original description - read only)")]
    async fn get_task_document(
        &self,
        #[tool(aggr)] req: TaskNameRequest,
    ) -> Result<CallToolResult, McpError> {
        let content = self
            .state
            .read_task_md(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(content)]))
    }

    #[tool(
        description = "Analyze a task to identify gaps and prepare context for generating questions. Reads task.md and returns instructions."
    )]
    async fn analyze_task(
        &self,
        #[tool(aggr)] req: AnalyzeTaskRequest,
    ) -> Result<CallToolResult, McpError> {
        let project_path = req.project_path.clone().unwrap_or_else(get_cwd);
        let state_mgr = StateManager::new(&project_path);

        // Ensure config is initialized before any task operation (creates tasks/ and .config/)
        if let Err(e) = state_mgr.ensure_config_initialized().await {
            // Log warning but continue - config is not strictly required for analyze
            eprintln!("Warning: Failed to initialize config: {}", e);
        }

        let mut state = match state_mgr.load_task_state(&req.task_name).await {
            Ok(state) => state,
            Err(_) => {
                if !state_mgr.task_md_exists(&req.task_name).await {
                    return Err(McpError::internal_error(
                        format!(
                            "Task '{}' not found. Neither state nor task.md exists at: {}",
                            req.task_name,
                            state_mgr.task_md_path(&req.task_name).display()
                        ),
                        None,
                    ));
                }

                let state = TaskState::new();
                state_mgr
                    .save_task_state(&req.task_name, &state)
                    .await
                    .map_err(|e| {
                        McpError::internal_error(
                            format!("Failed to create state for untracked task: {}", e),
                            None,
                        )
                    })?;

                state
            }
        };

        let task_content = state_mgr
            .read_task_md(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        state.status = TaskStatus::Analyzing;
        state.touch();
        state_mgr
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let _ = state_mgr
            .append_progress(&req.task_name, "k4000", "Task analysis started")
            .await;

        let response = serde_json::json!({
            "task_name": req.task_name,
            "status": state.status.to_string(),
            "description": task_content,
            "project_path": project_path,
            "folder_path": state_mgr.task_folder(&req.task_name).to_string_lossy().to_string(),
            "instructions": "Analyze this task description and perform the following steps:\n\n1) EXTRACT FEATURES: Identify discrete features/capabilities from the description. Each feature should be a distinct unit of functionality. Call save_features with the extracted features. REQUIRED fields for each feature: id, category, description, steps (array of verification steps to confirm feature works), acceptance_criteria (array of clear, objective, testable criteria). Optional: complexity, dependencies.\n\n2) GENERATE QUESTIONS: For each feature and overall task, identify: unclear requirements, missing edge cases, integration points with existing code, testing considerations, and missing acceptance criteria. If acceptance criteria are missing or ambiguous, ask explicit questions to define clear, objective, testable acceptance_criteria.\n\n3) SAVE QUESTIONS: Call save_questions with the generated questions.\n\nThe feature list helps track implementation progress and ensures all capabilities are addressed."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Analyze project structure and return context about languages, frameworks, and patterns"
    )]
    async fn get_project_context(
        &self,
        #[tool(aggr)] req: ProjectPathRequest,
    ) -> Result<CallToolResult, McpError> {
        let context = ProjectContext {
            root_path: req.project_path,
            languages: vec![],
            frameworks: vec![],
            has_tests: false,
            test_framework: None,
            key_directories: vec![],
            patterns: vec![],
        };

        let json = serde_json::to_string_pretty(&context)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(
        description = "Save clarifying questions generated from task analysis. Writes to questions.json. Questions should reference specific features (e.g., 'For F1: ...') when applicable."
    )]
    async fn save_questions(
        &self,
        #[tool(aggr)] req: SaveQuestionsRequest,
    ) -> Result<CallToolResult, McpError> {
        let question_inputs: Vec<QuestionInput> = serde_json::from_str(&req.questions_json)
            .map_err(|e| {
                McpError::invalid_params(format!("Invalid questions JSON: {}", e), None)
            })?;

        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let questions: Vec<Question> = question_inputs
            .into_iter()
            .map(|q| {
                let category = match q.category.as_str() {
                    "clarification" => QuestionCategory::Clarification,
                    "missing_requirement" => QuestionCategory::MissingRequirement,
                    "edge_case" => QuestionCategory::EdgeCase,
                    "integration" => QuestionCategory::Integration,
                    "testing" => QuestionCategory::Testing,
                    _ => QuestionCategory::Other,
                };
                Question::new(q.id, q.question, category)
            })
            .collect();

        let count = questions.len();

        // Write questions to questions.json
        self.state
            .write_questions(&req.task_name, &questions)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Update state status
        state.status = TaskStatus::AwaitingAnswers;
        state.touch();
        self.state
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!("{} questions generated, awaiting answers", count),
            )
            .await;

        Ok(CallToolResult::success(vec![Content::text(format!(
            "{{\"success\": true, \"questions_count\": {}, \"message\": \"{} questions saved to questions.json. Status set to awaiting_answers.\"}}",
            count, count
        ))]))
    }

    #[tool(
        description = "Save the human's answer to a clarifying question. Updates questions.json."
    )]
    async fn save_qa_response(
        &self,
        #[tool(aggr)] req: SaveQaResponseRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Load questions from questions.json
        let mut questions = self
            .state
            .read_questions(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let mut found = false;
        for q in &mut questions {
            if q.id == req.question_id {
                q.answer = Some(req.answer.clone());
                q.answered = true;
                found = true;
                break;
            }
        }

        if !found {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Question '{}' not found\"}}",
                req.question_id
            ))]));
        }

        let all_answered = questions.iter().all(|q| q.answered);
        if all_answered {
            state.status = TaskStatus::Ready;
        }

        // Save questions to questions.json
        self.state
            .write_questions(&req.task_name, &questions)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Update state
        state.touch();
        self.state
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let progress_msg = if all_answered {
            "All questions answered, status set to ready".to_string()
        } else {
            format!("Question {} answered", req.question_id)
        };
        let _ = self
            .state
            .append_progress(&req.task_name, "k4000", &progress_msg)
            .await;

        let message = if all_answered {
            "Answer saved and questions.json updated. All questions answered - status set to ready."
        } else {
            "Answer saved and questions.json updated."
        };

        Ok(CallToolResult::success(vec![Content::text(format!(
            "{{\"success\": true, \"all_answered\": {}, \"message\": \"{}\"}}",
            all_answered, message
        ))]))
    }

    #[tool(
        description = "Update the plan.md document with implementation plan and estimate. This is the only file K4000 writes to besides questions.json."
    )]
    async fn update_task_plan(
        &self,
        #[tool(aggr)] req: UpdateTaskPlanRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        self.state
            .write_plan_md(&req.task_name, &req.content)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        state.touch();
        self.state
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Log progress for plan update (F4)
        let _ = self
            .state
            .append_progress(&req.task_name, "k4000", "Implementation plan updated")
            .await;

        let response = serde_json::json!({
            "success": true,
            "plan_path": self.state.plan_md_path(&req.task_name).to_string_lossy().to_string(),
            "message": "plan.md updated successfully."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Update the status of a task (draft, analyzing, awaiting_answers, ready, implementing, reviewing, done). When setting to 'reviewing', provide files array to automatically trigger code review."
    )]
    async fn set_task_status(
        &self,
        #[tool(aggr)] req: SetStatusRequest,
    ) -> Result<CallToolResult, McpError> {
        let new_status: TaskStatus = req
            .status
            .parse()
            .map_err(|e: String| McpError::invalid_params(e, None))?;

        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Load features from feature-list.json
        let features = self
            .state
            .read_features(&req.task_name)
            .await
            .unwrap_or_default();

        // Validate feature completion when setting status to 'done'
        if new_status == TaskStatus::Done && !features.is_empty() {
            let incomplete_count = features.iter().filter(|f| !f.passes).count();
            if incomplete_count > 0 {
                let incomplete_ids: Vec<String> = features
                    .iter()
                    .filter(|f| !f.passes)
                    .map(|f| f.id.clone())
                    .collect();

                let response = serde_json::json!({
                    "success": false,
                    "message": format!("Cannot set status to 'done': {} feature(s) not verified", incomplete_count),
                    "incomplete_features": incomplete_ids,
                    "total_features": features.len(),
                    "passing_features": features.len() - incomplete_count,
                    "hint": "Use mark_feature_complete then mark_feature_verified to verify each feature before setting task status to 'done'."
                });
                return Ok(CallToolResult::success(vec![Content::text(
                    serde_json::to_string_pretty(&response).unwrap(),
                )]));
            }
        }

        if new_status == TaskStatus::Done {
            let missing_criteria: Vec<String> = features
                .iter()
                .filter(|f| f.acceptance_criteria.is_empty())
                .map(|f| f.id.clone())
                .collect();

            if !missing_criteria.is_empty() {
                let config = self.state.load_project_config().await.unwrap_or_default();
                let response = serde_json::json!({
                    "success": false,
                    "message": "Cannot set status to 'done': acceptance criteria missing for one or more features.",
                    "missing_acceptance_criteria": missing_criteria,
                    "hint": format!("Define acceptance_criteria for each feature in feature-list.json. The agent should iterate up to {} times to satisfy them before marking done.", config.acceptance_criteria_max_iterations)
                });
                return Ok(CallToolResult::success(vec![Content::text(
                    serde_json::to_string_pretty(&response).unwrap(),
                )]));
            }
        }

        // Validate that a review has been approved when setting status to 'done'
        if new_status == TaskStatus::Done {
            let latest_review = self
                .state
                .get_latest_review(&req.task_name)
                .await
                .map_err(|e| McpError::internal_error(e.to_string(), None))?;

            match latest_review {
                None => {
                    let response = serde_json::json!({
                        "success": false,
                        "message": "Cannot set status to 'done': No code review found.",
                        "hint": "You must complete a code review first. Call set_task_status with status='reviewing' and files=[...], then create_review_session, submit_findings, and approve_review.",
                        "required_steps": [
                            "1. set_task_status(status='reviewing', files=[modified files])",
                            "2. create_review_session(task_name, files)",
                            "3. Review code from all 5 perspectives and submit_findings",
                            "4. Fix any critical/high issues",
                            "5. approve_review(task_name, review_id)",
                            "6. set_task_status(status='done')"
                        ]
                    });
                    return Ok(CallToolResult::success(vec![Content::text(
                        serde_json::to_string_pretty(&response).unwrap(),
                    )]));
                }
                Some(review) => {
                    if review.status != ReviewStatus::Approved {
                        let response = serde_json::json!({
                            "success": false,
                            "message": format!("Cannot set status to 'done': Review is not approved (current status: {})", review.status),
                            "review_id": review.id,
                            "review_status": review.status.to_string(),
                            "hint": if review.status == ReviewStatus::Pending || review.status == ReviewStatus::InProgress {
                                "Complete the review by calling submit_findings, then approve_review."
                            } else {
                                "Approve the review by calling approve_review(task_name, review_id)."
                            }
                        });
                        return Ok(CallToolResult::success(vec![Content::text(
                            serde_json::to_string_pretty(&response).unwrap(),
                        )]));
                    }
                }
            }
        }

        // Handle auto-commit when setting status to 'done'
        if new_status == TaskStatus::Done {
            let config = self.state.load_project_config().await.unwrap_or_default();
            if config.auto_commit_on_done && !state.modified_files.is_empty() {
                // Get description from plan.md frontmatter
                let plan_content = self
                    .state
                    .read_plan_md(&req.task_name)
                    .await
                    .unwrap_or_default();
                let parsed = parse_frontmatter(&plan_content);
                let description = parsed
                    .frontmatter
                    .map(|fm| fm.description)
                    .unwrap_or_default();

                // Stage the modified files
                let project_path = self.state.project_path();
                for file in &state.modified_files {
                    let output = Command::new("git")
                        .arg("add")
                        .arg(file)
                        .current_dir(project_path)
                        .output();

                    match output {
                        Ok(result) => {
                            if !result.status.success() {
                                let stderr = String::from_utf8_lossy(&result.stderr);
                                let response = serde_json::json!({
                                    "success": false,
                                    "message": format!("Auto-commit failed: could not stage file '{}'", file),
                                    "error": stderr.to_string(),
                                    "hint": "Fix the git issue and try again, or disable auto_commit_on_done in config.toml"
                                });
                                return Ok(CallToolResult::success(vec![Content::text(
                                    serde_json::to_string_pretty(&response).unwrap(),
                                )]));
                            }
                        }
                        Err(e) => {
                            let response = serde_json::json!({
                                "success": false,
                                "message": format!("Auto-commit failed: could not run git add for '{}': {}", file, e),
                                "hint": "Fix the git issue and try again, or disable auto_commit_on_done in config.toml"
                            });
                            return Ok(CallToolResult::success(vec![Content::text(
                                serde_json::to_string_pretty(&response).unwrap(),
                            )]));
                        }
                    }
                }

                // Create the commit
                let commit_message = if description.is_empty() {
                    format!("Complete task: {}", req.task_name)
                } else {
                    format!("Complete task: {}\n\n{}", req.task_name, description)
                };

                let output = Command::new("git")
                    .arg("commit")
                    .arg("-m")
                    .arg(&commit_message)
                    .current_dir(project_path)
                    .output();

                match output {
                    Ok(result) => {
                        if !result.status.success() {
                            let stderr = String::from_utf8_lossy(&result.stderr);
                            let response = serde_json::json!({
                                "success": false,
                                "message": format!("Auto-commit failed: git commit returned error"),
                                "error": stderr.to_string(),
                                "hint": "Fix the git issue (e.g., pre-commit hook failure) and try again, or disable auto_commit_on_done in config.toml"
                            });
                            return Ok(CallToolResult::success(vec![Content::text(
                                serde_json::to_string_pretty(&response).unwrap(),
                            )]));
                        }
                    }
                    Err(e) => {
                        let response = serde_json::json!({
                            "success": false,
                            "message": format!("Auto-commit failed: could not run git commit: {}", e),
                            "hint": "Ensure git is installed and accessible, or disable auto_commit_on_done in config.toml"
                        });
                        return Ok(CallToolResult::success(vec![Content::text(
                            serde_json::to_string_pretty(&response).unwrap(),
                        )]));
                    }
                }

                // Log the auto-commit
                let _ = self
                    .state
                    .append_progress(
                        &req.task_name,
                        "k4000",
                        &format!(
                            "Auto-committed {} file(s): {}",
                            state.modified_files.len(),
                            state.modified_files.join(", ")
                        ),
                    )
                    .await;
            }
        }

        let old_status = state.status.clone();
        state.status = new_status.clone();
        state.touch();

        // Handle reviewing status - save modified files for auto-commit
        if new_status == TaskStatus::Reviewing {
            let files = req.files.clone().unwrap_or_default();
            if !files.is_empty() {
                state.add_modified_files(&files);
            }
        }

        self.state
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!("Status changed: {} -> {}", old_status, new_status),
            )
            .await;

        // Handle reviewing status response
        if new_status == TaskStatus::Reviewing {
            let files = req.files.unwrap_or_default();
            if files.is_empty() {
                return Ok(CallToolResult::success(vec![Content::text(format!(
                    "{{\"success\": true, \"new_status\": \"reviewing\", \"message\": \"Status updated to 'reviewing'. WARNING: No files provided. Call create_review_session with the modified files to start the review.\", \"action_required\": \"create_review_session\", \"task_name\": \"{}\"}}",
                    req.task_name
                ))]));
            }

            let response = serde_json::json!({
                "success": true,
                "new_status": "reviewing",
                "message": "Status updated to 'reviewing'. AUTOMATIC REVIEW REQUIRED: Call create_review_session now.",
                "action_required": "create_review_session",
                "task_name": req.task_name,
                "files_to_review": files,
                "instructions": "You MUST now call create_review_session with the task_name and files_to_review listed above, then review from all 5 perspectives (bugs, best_practices, security, performance, simplification) and submit findings."
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        // Handle implementing status - show features with acceptance criteria for user confirmation
        if new_status == TaskStatus::Implementing {
            let mut features_display = String::new();
            for feature in &features {
                features_display.push_str(&format!(
                    "\n### {}: {} ({})\n",
                    feature.id, feature.description, feature.category
                ));
                if !feature.acceptance_criteria.is_empty() {
                    features_display.push_str("**Acceptance Criteria:**\n");
                    for criterion in &feature.acceptance_criteria {
                        features_display.push_str(&format!("- {}\n", criterion));
                    }
                } else {
                    features_display.push_str("**Acceptance Criteria:** _(none defined)_\n");
                }
            }

            let response = serde_json::json!({
                "success": true,
                "new_status": "implementing",
                "message": "Ready to begin implementation.",
                "features_to_implement": features_display.trim(),
                "instructions": "Present the features and acceptance criteria above to the user. Confirm they look correct before proceeding with implementation. Each feature must satisfy its acceptance criteria to be marked complete."
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        Ok(CallToolResult::success(vec![Content::text(format!(
            "{{\"success\": true, \"new_status\": \"{}\", \"message\": \"Status updated to '{}'\"}}",
            new_status, new_status
        ))]))
    }

    #[tool(
        description = "Migrate existing features/ folder to new tasks/ format. Creates task subfolders and splits content into task.md, plan.md, questions.md."
    )]
    async fn migrate_tasks(
        &self,
        #[tool(aggr)] req: MigrateTasksRequest,
    ) -> Result<CallToolResult, McpError> {
        let project_path = req.project_path.unwrap_or_else(get_cwd);
        let features_dir = PathBuf::from(&project_path).join("features");
        let state_mgr = StateManager::new(&project_path);

        if !features_dir.exists() {
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&MigrationResult {
                    success: false,
                    migrated_count: 0,
                    migrated_tasks: vec![],
                    errors: vec!["No features/ folder found to migrate".to_string()],
                    features_folder_deleted: false,
                })
                .unwrap(),
            )]));
        }

        let mut migrated = Vec::new();
        let mut errors = Vec::new();

        let mut entries = fs::read_dir(&features_dir)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        while let Ok(Some(entry)) = entries.next_entry().await {
            let path = entry.path();
            if path.extension().map(|e| e == "md").unwrap_or(false) {
                let file_name = path.file_stem().unwrap().to_string_lossy().to_string();
                let task_name = slugify_name(&file_name);

                match migrate_single_feature(&path, &task_name, &state_mgr).await {
                    Ok(_) => migrated.push(task_name),
                    Err(e) => errors.push(format!("{}: {}", file_name, e)),
                }
            }
        }

        let deleted = if req.delete_old.unwrap_or(false) && errors.is_empty() {
            fs::remove_dir_all(&features_dir).await.is_ok()
        } else {
            false
        };

        let result = MigrationResult {
            success: errors.is_empty(),
            migrated_count: migrated.len(),
            migrated_tasks: migrated,
            errors,
            features_folder_deleted: deleted,
        };

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&result).unwrap(),
        )]))
    }

    #[tool(
        description = "Save features extracted from task analysis. Writes to feature-list.json. Call this after analyze_task to record discrete features."
    )]
    async fn save_features(
        &self,
        #[tool(aggr)] req: SaveFeaturesRequest,
    ) -> Result<CallToolResult, McpError> {
        let feature_inputs: Vec<FeatureInput> = serde_json::from_str(&req.features_json)
            .map_err(|e| McpError::invalid_params(format!("Invalid features JSON: {}", e), None))?;

        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let features: Vec<Feature> = feature_inputs
            .into_iter()
            .map(|f| {
                let complexity = f.complexity.as_deref().map(|c| match c {
                    "simple" => FeatureComplexity::Simple,
                    "moderate" => FeatureComplexity::Moderate,
                    "complex" => FeatureComplexity::Complex,
                    _ => FeatureComplexity::Moderate,
                });

                Feature {
                    id: f.id,
                    category: f.category,
                    description: f.description,
                    steps: f.steps,
                    acceptance_criteria: f.acceptance_criteria,
                    passes: false,
                    complexity,
                    dependencies: f.dependencies,
                }
            })
            .collect();

        let count = features.len();

        // Write features to feature-list.json
        self.state
            .write_features(&req.task_name, &features)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Update state timestamp
        state.touch();
        self.state
            .save_task_state(&req.task_name, &state)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let json_path = self.state.feature_list_json_path(&req.task_name);

        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!("{} features saved to feature-list.json", count),
            )
            .await;

        let response = serde_json::json!({
            "success": true,
            "features_count": count,
            "feature_list_path": json_path.to_string_lossy().to_string(),
            "message": format!("{} features saved to feature-list.json.", count)
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Mark a feature as complete. Returns verification steps that must be confirmed before passes=true."
    )]
    async fn mark_feature_complete(
        &self,
        #[tool(aggr)] req: MarkFeatureCompleteRequest,
    ) -> Result<CallToolResult, McpError> {
        // Load features from feature-list.json
        let features = self
            .state
            .read_features(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let feature = features.iter().find(|f| f.id == req.feature_id);

        match feature {
            None => Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Feature '{}' not found in task '{}'\"}}",
                req.feature_id, req.task_name
            ))])),
            Some(f) => {
                if f.passes {
                    return Ok(CallToolResult::success(vec![Content::text(format!(
                        "{{\"success\": false, \"message\": \"Feature '{}' is already marked as passing\"}}",
                        req.feature_id
                    ))]));
                }

                if f.steps.is_empty() {
                    return Ok(CallToolResult::success(vec![Content::text(
                        serde_json::json!({
                            "success": false,
                            "feature_id": f.id,
                            "message": "Cannot mark feature as complete: no verification steps defined.",
                            "hint": "Features must have steps before verification."
                        })
                        .to_string(),
                    )]));
                }

                if f.acceptance_criteria.is_empty() {
                    return Ok(CallToolResult::success(vec![Content::text(
                        serde_json::json!({
                            "success": false,
                            "feature_id": f.id,
                            "message": "Cannot mark feature as complete: no acceptance criteria defined.",
                            "hint": "Features must define acceptance_criteria before verification."
                        })
                        .to_string(),
                    )]));
                }

                // Log progress for feature verification attempt (F5)
                let _ = self
                    .state
                    .append_progress(
                        &req.task_name,
                        "k4000",
                        &format!("Feature {} verification started", req.feature_id),
                    )
                    .await;

                let response = serde_json::json!({
                    "success": true,
                    "feature_id": f.id,
                    "description": f.description,
                    "verification_steps": f.steps,
                    "acceptance_criteria": f.acceptance_criteria,
                    "message": "IMPORTANT: You must verify ALL steps and acceptance criteria before calling mark_feature_verified.",
                    "instructions": format!(
                        "Verify each step and acceptance criterion for feature {}:\n{}\n\nAfter confirming ALL items pass, call mark_feature_verified with confirmed=true.",
                        f.id,
                        f.steps.iter().enumerate().map(|(i, s)| format!("Step {}: {}", i + 1, s)).chain(
                            f.acceptance_criteria.iter().enumerate().map(|(i, c)| format!("Criteria {}: {}", i + 1, c))
                        ).collect::<Vec<_>>().join("\n")
                    ),
                    "next_action": "mark_feature_verified"
                });

                Ok(CallToolResult::success(vec![Content::text(
                    serde_json::to_string_pretty(&response).unwrap(),
                )]))
            }
        }
    }

    #[tool(
        description = "Mark a feature as verified after confirming all verification steps pass. This is the final step to set passes=true."
    )]
    async fn mark_feature_verified(
        &self,
        #[tool(aggr)] req: MarkFeatureVerifiedRequest,
    ) -> Result<CallToolResult, McpError> {
        if !req.confirmed {
            return Ok(CallToolResult::success(vec![Content::text(
                "{\"success\": false, \"message\": \"You must set confirmed=true to verify a feature. This confirms you have verified ALL steps.\"}".to_string()
            )]));
        }

        let mut state = self
            .state
            .load_task_state(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Load features from feature-list.json
        let mut features = self
            .state
            .read_features(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Load project config to determine approval mode
        let project_config = self.state.load_project_config().await.unwrap_or_default();
        let approval_mode = state.get_effective_approval_mode(&project_config);

        let feature_idx = features.iter().position(|f| f.id == req.feature_id);

        match feature_idx {
            None => Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Feature '{}' not found in task '{}'\"}}",
                req.feature_id, req.task_name
            ))])),
            Some(idx) => {
                let f = &features[idx];
                if f.steps.is_empty() {
                    return Ok(CallToolResult::success(vec![Content::text(
                        serde_json::json!({
                            "success": false,
                            "message": "Cannot verify feature: no verification steps defined.",
                            "hint": "Ensure the feature has steps before verifying."
                        })
                        .to_string(),
                    )]));
                }
                if f.acceptance_criteria.is_empty() {
                    return Ok(CallToolResult::success(vec![Content::text(
                        serde_json::json!({
                            "success": false,
                            "message": "Cannot verify feature: no acceptance criteria defined.",
                            "hint": "Ensure the feature has acceptance_criteria before verifying."
                        })
                        .to_string(),
                    )]));
                }

                // Mark feature as passing
                features[idx].passes = true;

                // Write features to feature-list.json
                self.state
                    .write_features(&req.task_name, &features)
                    .await
                    .map_err(|e| McpError::internal_error(e.to_string(), None))?;

                // Update state timestamp
                state.touch();
                self.state
                    .save_task_state(&req.task_name, &state)
                    .await
                    .map_err(|e| McpError::internal_error(e.to_string(), None))?;

                let passing = features.iter().filter(|f| f.passes).count();
                let total = features.len();
                let all_complete = passing == total;

                // Find next incomplete feature (for messaging)
                let next_feature = features.iter().find(|f| !f.passes).map(|f| f.id.clone());

                // Build response based on approval mode
                let (await_approval, next_step_message) = if all_complete {
                    // All features done - no need to wait
                    (
                        false,
                        "All features complete! Proceed to code review.".to_string(),
                    )
                } else {
                    match approval_mode {
                        FeatureApprovalMode::Manual => {
                            let next_id = next_feature.clone().unwrap_or_default();
                            (
                                true,
                                format!(
                                    "MANUAL APPROVAL MODE: Feature '{}' verified. Before starting the next feature ({}), please ask the user: \"Feature {} is complete. Shall I proceed to {}?\"",
                                    req.feature_id, next_id, req.feature_id, next_id
                                ),
                            )
                        }
                        FeatureApprovalMode::Auto => {
                            let next_id = next_feature.clone().unwrap_or_default();
                            (
                                false,
                                format!(
                                    "AUTO MODE: Feature '{}' verified. Proceeding to next feature: {}",
                                    req.feature_id, next_id
                                ),
                            )
                        }
                    }
                };

                let _ = self
                    .state
                    .append_progress(
                        &req.task_name,
                        "k4000",
                        &format!(
                            "Feature {} verified and marked as passing ({}/{})",
                            req.feature_id, passing, total
                        ),
                    )
                    .await;

                let response = serde_json::json!({
                    "success": true,
                    "feature_id": req.feature_id,
                    "passes": true,
                    "passing_features": passing,
                    "total_features": total,
                    "all_complete": all_complete,
                    "next_feature": next_feature,
                    "approval_mode": approval_mode.to_string(),
                    "await_approval": await_approval,
                    "message": format!("Feature '{}' verified and marked as passing. ({}/{})", req.feature_id, passing, total),
                    "next_step": next_step_message
                });

                Ok(CallToolResult::success(vec![Content::text(
                    serde_json::to_string_pretty(&response).unwrap(),
                )]))
            }
        }
    }

    #[tool(
        description = "Log a progress entry for a task. Appends to progress.txt with timestamp and agent name. Optionally include feature_id to track progress on specific features."
    )]
    async fn log_progress(
        &self,
        #[tool(aggr)] req: LogProgressRequest,
    ) -> Result<CallToolResult, McpError> {
        if !self.state.task_exists(&req.task_name).await {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Task '{}' not found\"}}",
                req.task_name
            ))]));
        }

        self.state
            .append_progress_with_feature(
                &req.task_name,
                &req.agent_name,
                req.feature_id.as_deref(),
                &req.message,
            )
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(
            "{\"success\": true, \"message\": \"Progress logged successfully.\"}".to_string(),
        )]))
    }

    #[tool(
        description = "Get the progress log for a task. Returns the full progress.txt content for context restoration."
    )]
    async fn get_progress(
        &self,
        #[tool(aggr)] req: GetProgressRequest,
    ) -> Result<CallToolResult, McpError> {
        let content = self
            .state
            .read_progress(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        if content.is_empty() {
            let response = serde_json::json!({
                "task_name": req.task_name,
                "has_progress": false,
                "content": "",
                "message": "No progress logged yet for this task."
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        let line_count = content.lines().count();
        let response = serde_json::json!({
            "task_name": req.task_name,
            "has_progress": true,
            "line_count": line_count,
            "content": content
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    // ==================== Review Tools ====================

    #[tool(description = "Create a new review session for a task. Returns the review session ID.")]
    async fn create_review_session(
        &self,
        #[tool(aggr)] req: CreateReviewRequest,
    ) -> Result<CallToolResult, McpError> {
        let reviews = self
            .state
            .list_reviews(&req.task_name)
            .await
            .unwrap_or_default();
        let iteration = reviews.len() as u32 + 1;

        let review_id = Uuid::new_v4().to_string();
        let mut session = ReviewSession::new(&review_id, &req.task_name, iteration);
        session.files_reviewed = req.files.clone();
        session.status = ReviewStatus::InProgress;

        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Log progress for review session creation (F3)
        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!(
                    "Review session created (iteration {}, {} files)",
                    iteration,
                    req.files.len()
                ),
            )
            .await;

        let response = serde_json::json!({
            "success": true,
            "review_id": review_id,
            "task_name": req.task_name,
            "iteration": iteration,
            "files_to_review": req.files,
            "instructions": "Review the files from 5 perspectives: bugs, best_practices, security, performance, simplification. Submit findings using submit_findings tool."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(description = "Get the current state and findings of a review session")]
    async fn get_review_session(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let json = serde_json::to_string_pretty(&session)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(description = "List all review sessions for a task with summary information")]
    async fn list_reviews(
        &self,
        #[tool(aggr)] req: TaskNameRequest,
    ) -> Result<CallToolResult, McpError> {
        let reviews = self
            .state
            .list_reviews(&req.task_name)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let mut summaries: Vec<ReviewSummary> = Vec::new();
        for review_id in reviews {
            if let Ok(session) = self.state.load_review(&req.task_name, &review_id).await {
                summaries.push(ReviewSummary::from(&session));
            }
        }

        let json = serde_json::to_string_pretty(&summaries)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(
        description = "Submit findings from code review. Findings should include category (bugs, best_practices, security, performance, simplification), file, severity (critical, high, medium, low, info), and description."
    )]
    async fn submit_findings(
        &self,
        #[tool(aggr)] req: SubmitFindingsRequest,
    ) -> Result<CallToolResult, McpError> {
        let finding_inputs: Vec<FindingInput> = serde_json::from_str(&req.findings_json)
            .map_err(|e| McpError::invalid_params(format!("Invalid findings JSON: {}", e), None))?;

        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let new_findings: Vec<ReviewFinding> = finding_inputs
            .into_iter()
            .map(|f| {
                let category = parse_category(&f.category);
                let severity = parse_severity(&f.severity);
                let id = Uuid::new_v4().to_string();

                let mut finding =
                    ReviewFinding::new(&id, category, &f.file, severity, &f.description);
                if let Some(line) = f.line {
                    finding = finding.with_line(line);
                }
                if let Some(ref suggestion) = f.suggestion {
                    finding = finding.with_suggestion(suggestion);
                }
                finding
            })
            .collect();

        let count = new_findings.len();
        session.findings.extend(new_findings);

        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let summary = ReviewSummary::from(&session);

        // Log progress for findings submission (F3)
        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!(
                    "Review findings submitted: {} new ({} critical, {} high)",
                    count, summary.critical_findings, summary.high_findings
                ),
            )
            .await;

        let response = serde_json::json!({
            "success": true,
            "findings_added": count,
            "total_findings": summary.total_findings,
            "critical_findings": summary.critical_findings,
            "high_findings": summary.high_findings,
            "message": format!("{} findings added to review session.", count)
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(description = "Mark a specific finding as resolved after it has been addressed")]
    async fn mark_finding_resolved(
        &self,
        #[tool(aggr)] req: MarkResolvedRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let mut found = false;
        for finding in &mut session.findings {
            if finding.id == req.finding_id {
                finding.resolved = true;
                found = true;
                break;
            }
        }

        if !found {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Finding '{}' not found\"}}",
                req.finding_id
            ))]));
        }

        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let summary = ReviewSummary::from(&session);

        let response = serde_json::json!({
            "success": true,
            "unresolved_remaining": summary.unresolved_findings,
            "message": "Finding marked as resolved."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Check if the review iteration limit has been reached and whether human intervention is needed"
    )]
    async fn check_iteration_limit(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let status = IterationStatus::new(session.iteration, self.max_iterations);

        let json = serde_json::to_string_pretty(&status)
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        Ok(CallToolResult::success(vec![Content::text(json)]))
    }

    #[tool(description = "Approve the review session, marking it as fully reviewed and accepted")]
    async fn approve_review(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let critical = session.critical_findings().len();
        let high = session.high_findings().len();

        if critical > 0 || high > 0 {
            return Ok(CallToolResult::success(vec![Content::text(format!(
                "{{\"success\": false, \"message\": \"Cannot approve: {} critical and {} high severity findings remain unresolved.\"}}",
                critical, high
            ))]));
        }

        session.approve();

        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Log progress for review approval (F3)
        let _ = self
            .state
            .append_progress(&req.task_name, "k4000", "Review approved")
            .await;

        let response = serde_json::json!({
            "success": true,
            "status": "approved",
            "message": "Review session approved. Code is ready for deployment."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(
        description = "Complete the current review iteration. If unresolved findings remain, optionally start the next iteration."
    )]
    async fn complete_iteration(
        &self,
        #[tool(aggr)] req: CompleteIterationRequest,
    ) -> Result<CallToolResult, McpError> {
        let mut session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let summary = ReviewSummary::from(&session);

        session.complete();
        self.state
            .save_review(&req.task_name, &session)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        // Log progress for iteration completion (F3)
        let _ = self
            .state
            .append_progress(
                &req.task_name,
                "k4000",
                &format!(
                    "Review iteration {} completed ({} unresolved)",
                    session.iteration, summary.unresolved_findings
                ),
            )
            .await;

        if summary.unresolved_findings == 0 {
            let response = serde_json::json!({
                "success": true,
                "status": "complete",
                "all_resolved": true,
                "message": "All findings resolved. Consider approving the review."
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        let iteration_status = IterationStatus::new(session.iteration, self.max_iterations);

        if iteration_status.needs_human_intervention && !req.start_next {
            let response = serde_json::json!({
                "success": true,
                "status": "iteration_limit_reached",
                "unresolved_findings": summary.unresolved_findings,
                "iteration": session.iteration,
                "max_iterations": self.max_iterations,
                "message": iteration_status.message
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        if req.start_next {
            let new_id = Uuid::new_v4().to_string();
            let mut new_session =
                ReviewSession::new(&new_id, &req.task_name, session.iteration + 1);
            new_session.files_reviewed = session.files_reviewed.clone();
            new_session.status = ReviewStatus::InProgress;

            self.state
                .save_review(&req.task_name, &new_session)
                .await
                .map_err(|e| McpError::internal_error(e.to_string(), None))?;

            let response = serde_json::json!({
                "success": true,
                "status": "next_iteration_started",
                "new_review_id": new_id,
                "iteration": session.iteration + 1,
                "unresolved_from_previous": summary.unresolved_findings,
                "message": format!("Iteration {} started. Review the fixes and check for remaining issues.", session.iteration + 1)
            });
            return Ok(CallToolResult::success(vec![Content::text(
                serde_json::to_string_pretty(&response).unwrap(),
            )]));
        }

        let response = serde_json::json!({
            "success": true,
            "status": "complete_with_unresolved",
            "unresolved_findings": summary.unresolved_findings,
            "message": "Iteration complete but unresolved findings remain. Use start_next=true to begin next iteration."
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }

    #[tool(description = "Get a summary breakdown of unresolved findings by category and severity")]
    async fn get_findings_summary(
        &self,
        #[tool(aggr)] req: ReviewIdRequest,
    ) -> Result<CallToolResult, McpError> {
        let session = self
            .state
            .load_review(&req.task_name, &req.review_id)
            .await
            .map_err(|e| McpError::internal_error(e.to_string(), None))?;

        let unresolved = session.unresolved_findings();

        let mut by_category: std::collections::HashMap<String, usize> =
            std::collections::HashMap::new();
        let mut by_severity: std::collections::HashMap<String, usize> =
            std::collections::HashMap::new();

        for finding in &unresolved {
            *by_category.entry(finding.category.to_string()).or_insert(0) += 1;
            *by_severity.entry(finding.severity.to_string()).or_insert(0) += 1;
        }

        let response = serde_json::json!({
            "review_id": req.review_id,
            "total_unresolved": unresolved.len(),
            "by_category": by_category,
            "by_severity": by_severity,
            "findings": unresolved.iter().map(|f| serde_json::json!({
                "id": f.id,
                "category": f.category.to_string(),
                "severity": f.severity.to_string(),
                "file": f.file,
                "line": f.line,
                "description": f.description
            })).collect::<Vec<_>>()
        });

        Ok(CallToolResult::success(vec![Content::text(
            serde_json::to_string_pretty(&response).unwrap(),
        )]))
    }
}

// ==================== Migration Helper ====================

async fn migrate_single_feature(
    feature_path: &PathBuf,
    task_name: &str,
    state_mgr: &StateManager,
) -> Result<(), String> {
    let content = fs::read_to_string(feature_path)
        .await
        .map_err(|e| format!("Failed to read: {}", e))?;

    let parsed = parse_frontmatter(&content);

    state_mgr
        .ensure_task_folder(task_name)
        .await
        .map_err(|e| format!("Failed to create folder: {}", e))?;

    let task_md = extract_task_md(&parsed.body);
    let task_md_path = state_mgr.task_md_path(task_name);
    fs::write(&task_md_path, &task_md)
        .await
        .map_err(|e| format!("Failed to write task.md: {}", e))?;

    let plan_md = extract_plan_md(&parsed.body, parsed.frontmatter.as_ref());
    if !plan_md.is_empty() {
        state_mgr
            .write_plan_md(task_name, &plan_md)
            .await
            .map_err(|e| format!("Failed to write plan.md: {}", e))?;
    }

    // Extract questions from the old markdown format and convert to JSON
    let questions_md = extract_questions_md(&parsed.body);
    if !questions_md.is_empty() {
        // Parse markdown questions into Question structs
        let questions = parse_questions_from_md(&questions_md);
        if !questions.is_empty() {
            state_mgr
                .write_questions(task_name, &questions)
                .await
                .map_err(|e| format!("Failed to write questions.json: {}", e))?;
        }
    }

    // Create state without name parameter (name is derived from folder)
    let state = TaskState::new();
    // Note: status from old frontmatter is ignored since we can't read it anymore
    // New tasks start in draft status

    state_mgr
        .save_task_state(task_name, &state)
        .await
        .map_err(|e| format!("Failed to save state: {}", e))?;

    Ok(())
}

/// Parse questions from old markdown format into Question structs
fn parse_questions_from_md(md: &str) -> Vec<Question> {
    let mut questions = Vec::new();
    let mut current_id = String::new();
    let mut current_question = String::new();
    let mut current_category = QuestionCategory::Other;
    let mut current_answer: Option<String> = None;

    for line in md.lines() {
        // Parse question header: ## Q1: Question text
        if line.starts_with("## Q") {
            // Save previous question if any
            if !current_id.is_empty() && !current_question.is_empty() {
                let mut q = Question::new(&current_id, &current_question, current_category.clone());
                if let Some(ans) = current_answer.take() {
                    q.answer = Some(ans);
                    q.answered = true;
                }
                questions.push(q);
            }

            // Parse new question
            if let Some(colon_pos) = line.find(':') {
                let id_part = &line[3..colon_pos].trim();
                current_id = id_part.to_lowercase();
                current_question = line[colon_pos + 1..].trim().to_string();
            }
            current_category = QuestionCategory::Other;
            current_answer = None;
        } else if line.starts_with("**Category:**") {
            let cat = line.trim_start_matches("**Category:**").trim();
            current_category = match cat {
                "clarification" => QuestionCategory::Clarification,
                "missing_requirement" => QuestionCategory::MissingRequirement,
                "edge_case" => QuestionCategory::EdgeCase,
                "integration" => QuestionCategory::Integration,
                "testing" => QuestionCategory::Testing,
                _ => QuestionCategory::Other,
            };
        } else if line.starts_with("**Answer:**") {
            let ans = line.trim_start_matches("**Answer:**").trim();
            if ans != "*Pending*" && !ans.is_empty() {
                current_answer = Some(ans.to_string());
            }
        }
    }

    // Don't forget the last question
    if !current_id.is_empty() && !current_question.is_empty() {
        let mut q = Question::new(&current_id, &current_question, current_category);
        if let Some(ans) = current_answer {
            q.answer = Some(ans);
            q.answered = true;
        }
        questions.push(q);
    }

    questions
}

fn extract_task_md(body: &str) -> String {
    let mut result = String::new();
    let mut in_description = false;
    let mut found_section = false;

    for line in body.lines() {
        if line.starts_with("## ") || line.starts_with("---") {
            if in_description {
                break;
            }
            if line.contains("Description") {
                in_description = true;
                found_section = true;
                continue;
            }
        }

        if line.starts_with("# ") && !found_section {
            result.push_str(line);
            result.push('\n');
            continue;
        }

        if in_description {
            result.push_str(line);
            result.push('\n');
        }
    }

    if result.is_empty() {
        for line in body.lines() {
            if line.starts_with("## Q & A")
                || line.starts_with("## Implementation Plan")
                || line.starts_with("## Estimate")
            {
                break;
            }
            result.push_str(line);
            result.push('\n');
        }
    }

    result.trim().to_string()
}

fn extract_plan_md(body: &str, frontmatter: Option<&Frontmatter>) -> String {
    let mut result = String::new();
    let mut capturing = false;

    if let Some(fm) = frontmatter {
        result.push_str(&generate_frontmatter(fm));
    }

    for line in body.lines() {
        if line.starts_with("## Implementation Plan") || line.starts_with("## Estimate") {
            capturing = true;
        }
        if capturing && (line.starts_with("## Q & A") || line.starts_with("## Complete Definition"))
        {
            if !line.starts_with("## Implementation Plan") && !line.starts_with("## Estimate") {
                capturing = false;
            }
        }
        if capturing {
            result.push_str(line);
            result.push('\n');
        }
    }

    result.trim().to_string()
}

fn extract_questions_md(body: &str) -> String {
    let mut result = String::from("# Questions & Answers\n");
    let mut capturing = false;

    for line in body.lines() {
        if line.starts_with("## Q & A") {
            capturing = true;
            continue;
        }
        if capturing && line.starts_with("## ") && !line.starts_with("### Q") {
            break;
        }
        if capturing {
            result.push_str(line);
            result.push('\n');
        }
    }

    if result.trim() == "# Questions & Answers" {
        return String::new();
    }

    result.trim().to_string()
}

#[tool(tool_box)]
impl ServerHandler for K4000Server {
    fn get_info(&self) -> ServerInfo {
        ServerInfo {
            protocol_version: Default::default(),
            capabilities: ServerCapabilities::builder().enable_tools().build(),
            server_info: rmcp::model::Implementation::from_build_env(),
            instructions: Some(
                "K4000 MCP Server - AI-assisted task development and code review. Provides tools for analyzing task documents, managing Q&A, tracking task development, and performing code reviews from 5 perspectives: bugs, best practices, security, performance, and simplification.".to_string(),
            ),
        }
    }
}
