diff --git a/server/src/handlers/meta.rs b/server/src/handlers/meta.rs index acb8e7b..eed2d1c 100644 --- a/server/src/handlers/meta.rs +++ b/server/src/handlers/meta.rs @@ -1086,7 +1086,50 @@ impl McpTool for OmniSearchHandler { } } } - Ok(report.to_string()) + let mut out = String::new(); + out.push_str(&format!("## OmniSearch Results for '{}'\n\n", req.query)); + if report.get("_truncated_to_max_tokens").is_some() { + out.push_str("> [!WARNING] Results truncated to fit token limits.\n\n"); + } + + let sections = [ + ("Knowledge Graph", "knowledge_graph"), + ("Tasks", "tasks"), + ("Snippets", "snippets"), + ("ADRs", "adrs"), + ("Tech Debt", "tech_debts"), + ("Handoff Memos", "handoff_memos"), + ("Error Fixes", "error_fixes"), + ]; + + for (title, key) in sections { + if let Some(arr) = report.get(key).and_then(|v| v.as_array()) { + if arr.is_empty() { + continue; + } + out.push_str(&format!("### {}\n", title)); + for item in arr { + // Try to extract a title or ID + let id = item.get("id").or_else(|| item.get("name")).and_then(|v| v.as_str()).unwrap_or("Unknown"); + let text = item.get("title").or_else(|| item.get("description")).or_else(|| item.get("content")).and_then(|v| v.as_str()).unwrap_or(""); + let text_trunc: String = text.chars().take(200).collect(); + out.push_str(&format!("- **{}**: {}\n", id, text_trunc.replace('\n', " "))); + } + out.push_str("\n"); + } else if let Some(obj) = report.get(key).and_then(|v| v.as_object()) { + if obj.is_empty() { + continue; + } + out.push_str(&format!("### {}\n", title)); + for (id, val) in obj { + let text = val.get("entity_type").and_then(|v| v.as_str()).unwrap_or("Entity"); + out.push_str(&format!("- **{}** ({})\n", id, text)); + } + out.push_str("\n"); + } + } + + Ok(out) } } @@ -1131,13 +1174,15 @@ impl McpTool for GetProjectHealthHandler { .filter(|m| m.namespace == req.namespace && m.status != "done") .count() }); - let report = serde_json::json!({ - "active_tasks": active_tasks, - "unresolved_tech_debt": unresolved_debt, - "unread_handoff_memos": unread_memos, - "active_milestones": active_milestones - }); - Ok(report.to_string()) + let out = format!( + "## Project Health (Namespace: {})\n\n\ + - **Active Tasks:** {}\n\ + - **Unresolved Tech Debt:** {}\n\ + - **Unread Handoff Memos:** {}\n\ + - **Active Milestones:** {}\n", + req.namespace, active_tasks, unresolved_debt, unread_memos, active_milestones + ); + Ok(out) } } @@ -1373,13 +1418,15 @@ impl McpTool for QueryLineageHandler { timeline.sort_by_key(|item| item["timestamp"].as_u64().unwrap_or(0)); - let res = serde_json::json!({ - "query": req.query, - "lineage_count": timeline.len(), - "timeline": timeline - }); + let mut out = format!("## Lineage Query Results for '{}'\n\nFound {} items:\n\n", req.query, timeline.len()); + for item in timeline { + out.push_str(&format!("- **{}** (Type: `{}`)\n", item["id"].as_str().unwrap_or(""), item["type"].as_str().unwrap_or(""))); + if let Some(msg) = item["message"].as_str() { + out.push_str(&format!(" {}\n", msg.replace('\n', " "))); + } + } - Ok(serde_yaml::to_string(&res)?) + Ok(out) } } @@ -1434,12 +1481,21 @@ impl McpTool for GetNextActionableTasksHandler { actionable.truncate(limit); - let res = serde_json::json!({ - "actionable_count": actionable.len(), - "tasks": actionable - }); - - Ok(serde_yaml::to_string(&res)?) + let mut out = format!("Found {} actionable tasks:\n\n", actionable.len()); + for t in actionable { + out.push_str(&format!("- [{}] **{}** (ID: `{}`)\n", t.status, t.title, t.id)); + if !t.description.is_empty() { + let desc: String = t.description.chars().take(150).collect(); + out.push_str(&format!(" {}\n", desc.replace('\n', " "))); + } + if !t.dependencies.is_empty() { + out.push_str(&format!(" Dependencies: `{}`\n", t.dependencies.join("`, `"))); + } + if let Some(b) = &t.git_branch { + out.push_str(&format!(" Branch: `{}`\n", b)); + } + } + Ok(out) } } @@ -1573,17 +1629,40 @@ impl McpTool for GetPreflightContextHandler { .take(5) .collect(); - let preflight = serde_json::json!({ - "namespace": req.namespace, - "git_branch": req.git_branch, - "active_tasks": active_tasks, - "top_open_tech_debts": open_tech_debts, - "active_hypotheses": active_hypotheses, - "recent_terminal_commands": recent_commands, - "recent_activities": recent_activities - }); + let mut out = format!("## Preflight Context (Namespace: {}, Branch: {})\n\n", req.namespace, req.git_branch.unwrap_or_default()); + + out.push_str(&format!("### Active Tasks ({})\n", active_tasks.len())); + for t in active_tasks { + out.push_str(&format!("- [{}] **{}** (ID: `{}`)\n", t["status"].as_str().unwrap_or(""), t["title"].as_str().unwrap_or(""), t["id"].as_str().unwrap_or(""))); + } + out.push_str("\n"); - Ok(serde_yaml::to_string(&preflight)?) + out.push_str(&format!("### Top Open Tech Debt ({})\n", open_tech_debts.len())); + for d in open_tech_debts { + out.push_str(&format!("- **ID: {}**\n Description: {}\n", d["id"].as_str().unwrap_or(""), d["description"].as_str().unwrap_or(""))); + } + out.push_str("\n"); + + out.push_str(&format!("### Active Hypotheses ({})\n", active_hypotheses.len())); + for h in active_hypotheses { + out.push_str(&format!("- [{}] {}\n", h.status, h.hypothesis)); + } + out.push_str("\n"); + + out.push_str("### Recent Terminal Commands\n"); + for c in recent_commands { + out.push_str(&format!("- `{}` (Exit Code: {})\n", c.command, c.exit_code)); + } + out.push_str("\n"); + + out.push_str("### Recent Activities\n"); + for a in recent_activities { + let cat = a.get("category").and_then(|v| v.as_str()).unwrap_or("Unknown"); + let sum = a.get("summary").and_then(|v| v.as_str()).unwrap_or(""); + out.push_str(&format!("- {} ({})\n", cat, sum)); + } + + Ok(out) } } diff --git a/server/src/handlers/notes.rs b/server/src/handlers/notes.rs index afef914..cb44e15 100644 --- a/server/src/handlers/notes.rs +++ b/server/src/handlers/notes.rs @@ -168,13 +168,29 @@ impl McpTool for GenerateStandupReportHandler { .collect::>() }); - let report = serde_json::json!({ - "tasks_updated": filtered_tasks, - "code_changes": filtered_changes, - "session_summaries": filtered_summaries - }); - let report_str = report.to_string(); - Ok(report_str) + let mut out = String::new(); + out.push_str(&format!("## Standup Report (Past {} hours)\n\n", req.hours_lookback)); + + out.push_str(&format!("### Tasks Updated ({})\n", filtered_tasks.len())); + for t in filtered_tasks { + out.push_str(&format!("- [{}] **{}** (ID: `{}`)\n", t.status, t.title, t.id)); + } + out.push_str("\n"); + + out.push_str(&format!("### Code Changes ({})\n", filtered_changes.len())); + for c in filtered_changes { + let branch = c.git_branch.as_deref().unwrap_or("unknown"); + out.push_str(&format!("- `{}` (Branch: `{}`): {}\n", c.file_path, branch, c.description)); + } + out.push_str("\n"); + + out.push_str(&format!("### Session Summaries ({})\n", filtered_summaries.len())); + for s in filtered_summaries { + out.push_str(&format!("- {}\n", s.summary)); + } + out.push_str("\n"); + + Ok(out) } }