summaryrefslogtreecommitdiff
path: root/makima/src/llm/task_output.rs
diff options
context:
space:
mode:
authorsoryu <soryu@soryu.co>2026-05-17 21:22:34 +0100
committersoryu <soryu@soryu.co>2026-05-17 21:22:34 +0100
commit857e717e6343fa5c2ae96664bdc64741d5ba6830 (patch)
tree0f3898d9e2e2a3c312358dbf70c44f4ab1cf3648 /makima/src/llm/task_output.rs
parentce29ae801bcc5a0ba76d5a8d1565242ab267a47d (diff)
downloadsoryu-remove-llm.tar.gz
soryu-remove-llm.zip
chore: remove LLM module + all dependent surfacesremove-llm
Wholesale removal of the LLM integration layer. ~14,200 LOC deleted across backend and frontend. All chat-driven UIs go with it. ## Backend - Delete `src/llm/` (7,400 LOC): claude/groq clients, contract_tools, contract_evaluator, discuss_tools, mesh_tools, phase_guidance, task_output, templates, markdown round-trip, tools, transcript_analyzer. - Delete handlers wholly dependent on LLM: - `chat.rs` (file-level LLM chat at /files/{id}/chat) - `mesh_chat.rs` (mesh & task LLM chat + history) - `templates.rs` (/contract-types listing) - Strip LLM uses from `mesh_daemon.rs`: - `compute_action_directive` (used phase_guidance::check_deliverables_met to nudge supervisors with "all tasks done" messages). The auto-PR path below still fires when all tasks finish, so no behaviour lost. - `crate::llm::markdown_to_body` → inline 1-line replacement that wraps markdown content in a single BodyElement::Markdown. The editor re-parses on display, so round-trip is preserved. - Drop routes: /files/{id}/chat, /mesh/chat, /mesh/chat/history, /mesh/tasks/{id}/chat, /contract-types. - Drop the matching openapi registrations. ## Frontend - Delete components that were LLM-only: - `mesh/UnifiedMeshChatInput.tsx` - `listen/DiscussContractModal.tsx` - `listen/TranscriptAnalysisPanel.tsx` - `listen/ContractPickerModal.tsx` - `files/CliInput.tsx` - Delete the entire /listen page (its primary value-add was voice → LLM analysis → contract creation; without LLM the page is just a transcript display with no obvious user purpose). - Delete `hooks/useMeshChatHistory.ts` and `lib/listenApi.ts` (transcript-analysis API client to the already-Phase-5-removed listen handlers). - Strip api.ts of LLM exports: LlmModel, ChatMessage/Request/Response, UserQuestion/Answer, chatWithFile, MeshChat* types & functions, getMeshChatHistory, clearMeshChatHistory, chatWithMeshContext, ContractTypeTemplate, listContractTypes, chatWithContract, getContractChatHistory, clearContractChatHistory, discussContract, PhaseDefinition, DeliverableDefinition. - mesh.tsx: drop UnifiedMeshChatInput render + the chatContext memo + handleTaskUpdatedFromCli (only consumer was the input). - files.tsx: drop CliInput render + handleGenerateFromElement + handleBodyUpdate + handleClearFocus + suggestedPrompt state (all CliInput-only). - NavStrip: drop the /listen link. - main.tsx: drop the /listen route. ## Net diff: 37 files changed, 58 insertions, 14,281 deletions. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'makima/src/llm/task_output.rs')
-rw-r--r--makima/src/llm/task_output.rs485
1 files changed, 0 insertions, 485 deletions
diff --git a/makima/src/llm/task_output.rs b/makima/src/llm/task_output.rs
deleted file mode 100644
index c7f6990..0000000
--- a/makima/src/llm/task_output.rs
+++ /dev/null
@@ -1,485 +0,0 @@
-//! Task output processing and task derivation utilities.
-//!
-//! This module provides utilities for:
-//! - Parsing task lists from markdown documents
-//! - Analyzing completed task outputs
-//! - Suggesting follow-up actions based on task results
-
-use regex::Regex;
-use serde::{Deserialize, Serialize};
-use uuid::Uuid;
-
-/// A parsed task from a markdown document
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct ParsedTask {
- /// Task name/title
- pub name: String,
- /// Task description or plan
- pub description: Option<String>,
- /// Group/phase this task belongs to
- pub group: Option<String>,
- /// Order within the group (0-indexed)
- pub order: usize,
- /// Whether this task was marked as completed in source
- pub completed: bool,
- /// Dependencies (names of other tasks)
- pub dependencies: Vec<String>,
-}
-
-/// Result of parsing tasks from a document
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct TaskParseResult {
- /// Successfully parsed tasks
- pub tasks: Vec<ParsedTask>,
- /// Groups/phases found
- pub groups: Vec<String>,
- /// Total tasks found
- pub total: usize,
- /// Any parsing warnings
- pub warnings: Vec<String>,
-}
-
-/// Impact on contract phase
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct PhaseImpact {
- /// Current phase
- pub phase: String,
- /// Whether phase targets are now met
- pub targets_met: bool,
- /// Tasks remaining in phase
- pub tasks_remaining: usize,
- /// Suggestion for phase transition
- pub transition_suggestion: Option<String>,
-}
-
-/// Suggested action based on task output
-#[derive(Debug, Clone, Serialize, Deserialize)]
-#[serde(tag = "type", rename_all = "snake_case")]
-pub enum SuggestedAction {
- /// Create a follow-up task
- CreateTask {
- name: String,
- plan: String,
- chain_from: Option<Uuid>,
- },
- /// Create a new file from template
- CreateFile {
- template_id: String,
- name: String,
- seed_content: Option<String>,
- },
- /// Update an existing file
- UpdateFile {
- file_id: Uuid,
- file_name: String,
- additions: String,
- },
- /// Advance to next phase
- AdvancePhase {
- to_phase: String,
- },
- /// Run the next chained task
- RunNextTask {
- task_id: Uuid,
- task_name: String,
- },
- /// Mark the contract as completed
- MarkContractComplete {
- contract_id: Uuid,
- },
-}
-
-/// Analysis of a completed task's output
-#[derive(Debug, Clone, Serialize, Deserialize)]
-pub struct TaskOutputAnalysis {
- /// Summary of what was accomplished
- pub summary: String,
- /// Files that were created/modified (from diff)
- pub files_affected: Vec<String>,
- /// Suggested next actions
- pub next_steps: Vec<SuggestedAction>,
- /// Impact on contract phase
- pub phase_impact: Option<PhaseImpact>,
-}
-
-/// Parse tasks from a markdown task breakdown document
-///
-/// Supports formats like:
-/// - `[ ] Task name`
-/// - `[x] Completed task`
-/// - `1. Task name`
-/// - `- Task name`
-///
-/// Groups are detected from `## Phase/Section` headings.
-pub fn parse_tasks_from_breakdown(content: &str) -> TaskParseResult {
- let mut tasks = Vec::new();
- let mut groups = Vec::new();
- let mut warnings = Vec::new();
- let mut current_group: Option<String> = None;
- let mut task_order = 0;
-
- // Patterns for task items
- let checkbox_pattern = Regex::new(r"^\s*[-*]\s*\[([ xX])\]\s*(.+)$").unwrap();
- let numbered_checkbox = Regex::new(r"^\s*\d+\.\s*\[([ xX])\]\s*(.+)$").unwrap();
- let numbered_pattern = Regex::new(r"^\s*\d+\.\s+(.+)$").unwrap();
- let bullet_pattern = Regex::new(r"^\s*[-*]\s+(.+)$").unwrap();
- let heading_pattern = Regex::new(r"^##\s+(?:Phase\s*\d*:?\s*)?(.+)$").unwrap();
-
- // Patterns for dependencies (inline)
- let depends_pattern = Regex::new(r"(?i)\(?\s*(?:depends on|after|requires):?\s*([^)]+)\)?").unwrap();
-
- for line in content.lines() {
- let trimmed = line.trim();
-
- // Skip empty lines
- if trimmed.is_empty() {
- continue;
- }
-
- // Check for section headings
- if let Some(caps) = heading_pattern.captures(trimmed) {
- let group_name = caps[1].trim().to_string();
- if !groups.contains(&group_name) {
- groups.push(group_name.clone());
- }
- current_group = Some(group_name);
- task_order = 0;
- continue;
- }
-
- // Try to parse as a task
- let mut task_name: Option<String> = None;
- let mut completed = false;
-
- // Try checkbox patterns first (more specific)
- if let Some(caps) = checkbox_pattern.captures(trimmed) {
- completed = &caps[1] != " ";
- task_name = Some(caps[2].trim().to_string());
- } else if let Some(caps) = numbered_checkbox.captures(trimmed) {
- completed = &caps[1] != " ";
- task_name = Some(caps[2].trim().to_string());
- } else if let Some(caps) = numbered_pattern.captures(trimmed) {
- task_name = Some(caps[1].trim().to_string());
- } else if let Some(caps) = bullet_pattern.captures(trimmed) {
- // Only treat as task if it looks like a task (has actionable verbs)
- let text = caps[1].trim();
- if looks_like_task(text) {
- task_name = Some(text.to_string());
- }
- }
-
- if let Some(name) = task_name {
- // Skip items that are clearly not tasks
- if name.to_lowercase().starts_with("note:") ||
- name.to_lowercase().starts_with("todo:") && name.len() < 10 ||
- name.starts_with('#') {
- continue;
- }
-
- // Extract dependencies if present
- let dependencies = if let Some(dep_caps) = depends_pattern.captures(&name) {
- dep_caps[1]
- .split(',')
- .map(|s| s.trim().to_string())
- .filter(|s| !s.is_empty())
- .collect()
- } else {
- Vec::new()
- };
-
- // Clean task name (remove dependency info)
- let clean_name = depends_pattern.replace(&name, "").trim().to_string();
-
- // Extract description if there's a colon
- let (final_name, description) = if let Some(idx) = clean_name.find(':') {
- let (n, d) = clean_name.split_at(idx);
- (n.trim().to_string(), Some(d[1..].trim().to_string()))
- } else {
- (clean_name, None)
- };
-
- tasks.push(ParsedTask {
- name: final_name,
- description,
- group: current_group.clone(),
- order: task_order,
- completed,
- dependencies,
- });
-
- task_order += 1;
- }
- }
-
- let total = tasks.len();
-
- // Add warnings
- if tasks.is_empty() {
- warnings.push("No tasks found in document. Ensure tasks are formatted as checkbox items (- [ ] Task) or numbered lists (1. Task).".to_string());
- }
-
- TaskParseResult {
- tasks,
- groups,
- total,
- warnings,
- }
-}
-
-/// Check if text looks like a task (has action verbs at word boundaries)
-fn looks_like_task(text: &str) -> bool {
- let lower = text.to_lowercase();
- let action_verbs = [
- "add", "create", "implement", "build", "write", "fix", "update",
- "refactor", "test", "configure", "set up", "setup", "deploy",
- "integrate", "migrate", "design", "review", "document", "remove",
- "delete", "modify", "change", "improve", "optimize", "enable",
- "disable", "install", "initialize", "define", "extend", "extract",
- ];
-
- // Check if text starts with an action verb (followed by space or end)
- for verb in &action_verbs {
- if lower.starts_with(verb) {
- // Check for word boundary after verb
- let after = &lower[verb.len()..];
- if after.is_empty() || after.starts_with(' ') || after.starts_with('_') {
- return true;
- }
- }
- // Check if verb appears after space with word boundary
- let pattern = format!(" {} ", verb);
- let pattern_end = format!(" {}", verb);
- if lower.contains(&pattern) {
- return true;
- }
- // Check if verb is at the end of string after a space
- if lower.ends_with(&pattern_end) && lower.len() > pattern_end.len() {
- return true;
- }
- }
- false
-}
-
-/// Analyze a completed task's output to suggest next actions
-pub fn analyze_task_output(
- _task_id: Uuid,
- task_name: &str,
- task_result: Option<&str>,
- task_diff: Option<&str>,
- contract_phase: &str,
- total_tasks: usize,
- completed_tasks: usize,
- next_task: Option<(Uuid, String)>,
- dev_notes_file: Option<(Uuid, String)>,
-) -> TaskOutputAnalysis {
- let mut next_steps = Vec::new();
- let mut files_affected = Vec::new();
-
- // Parse files from diff if available
- if let Some(diff) = task_diff {
- files_affected = extract_files_from_diff(diff);
- }
-
- // Generate summary
- let summary = if let Some(result) = task_result {
- if result.len() > 200 {
- format!("{}...", &result[..200])
- } else {
- result.to_string()
- }
- } else {
- format!("Task '{}' completed", task_name)
- };
-
- // If there's a next chained task, suggest running it
- if let Some((next_id, next_name)) = next_task {
- next_steps.push(SuggestedAction::RunNextTask {
- task_id: next_id,
- task_name: next_name,
- });
- }
-
- // Suggest updating Dev Notes if in execute phase and file exists
- if contract_phase == "execute" {
- if let Some((file_id, file_name)) = dev_notes_file {
- let additions = format!(
- "\n## Task: {}\n\n{}\n\n### Files Modified\n{}\n",
- task_name,
- summary,
- files_affected.iter()
- .map(|f| format!("- {}", f))
- .collect::<Vec<_>>()
- .join("\n")
- );
-
- next_steps.push(SuggestedAction::UpdateFile {
- file_id,
- file_name,
- additions,
- });
- } else {
- // Suggest creating Dev Notes
- next_steps.push(SuggestedAction::CreateFile {
- template_id: "dev-notes".to_string(),
- name: "Development Notes".to_string(),
- seed_content: Some(format!(
- "# Development Notes\n\n## Task: {}\n\n{}\n",
- task_name, summary
- )),
- });
- }
- }
-
- // Calculate phase impact
- let new_completed = completed_tasks + 1;
- let targets_met = new_completed >= total_tasks && total_tasks > 0;
- let tasks_remaining = total_tasks.saturating_sub(new_completed);
-
- let transition_suggestion = if targets_met && contract_phase == "execute" {
- Some("All tasks complete. Ready to advance to Review phase.".to_string())
- } else {
- None
- };
-
- // If targets are met, suggest phase transition
- if targets_met && contract_phase == "execute" {
- next_steps.push(SuggestedAction::AdvancePhase {
- to_phase: "review".to_string(),
- });
- }
-
- let phase_impact = Some(PhaseImpact {
- phase: contract_phase.to_string(),
- targets_met,
- tasks_remaining,
- transition_suggestion,
- });
-
- TaskOutputAnalysis {
- summary,
- files_affected,
- next_steps,
- phase_impact,
- }
-}
-
-/// Extract file paths from a git diff
-fn extract_files_from_diff(diff: &str) -> Vec<String> {
- let mut files = Vec::new();
- let file_pattern = Regex::new(r"^(?:diff --git a/|[+]{3} b/|[-]{3} a/)(.+)$").unwrap();
-
- for line in diff.lines() {
- if let Some(caps) = file_pattern.captures(line) {
- let path = caps[1].trim().to_string();
- // Skip /dev/null and duplicates
- if path != "/dev/null" && !files.contains(&path) {
- // Clean up path (remove a/ or b/ prefix from git diff)
- let clean_path = path.trim_start_matches("a/").trim_start_matches("b/").to_string();
- if !files.contains(&clean_path) {
- files.push(clean_path);
- }
- }
- }
- }
-
- files
-}
-
-/// Format parsed tasks for display
-pub fn format_parsed_tasks(result: &TaskParseResult) -> String {
- let mut output = String::new();
-
- if result.tasks.is_empty() {
- output.push_str("No tasks found in the document.\n");
- for warning in &result.warnings {
- output.push_str(&format!("Warning: {}\n", warning));
- }
- return output;
- }
-
- output.push_str(&format!("Found {} task(s)", result.total));
- if !result.groups.is_empty() {
- output.push_str(&format!(" in {} group(s)", result.groups.len()));
- }
- output.push_str(":\n\n");
-
- let mut current_group: Option<&str> = None;
- for (i, task) in result.tasks.iter().enumerate() {
- // Print group header if changed
- if task.group.as_deref() != current_group {
- current_group = task.group.as_deref();
- if let Some(group) = current_group {
- output.push_str(&format!("**{}**\n", group));
- }
- }
-
- let status = if task.completed { "[x]" } else { "[ ]" };
- output.push_str(&format!("{}. {} {}", i + 1, status, task.name));
-
- if !task.dependencies.is_empty() {
- output.push_str(&format!(" (depends on: {})", task.dependencies.join(", ")));
- }
-
- output.push('\n');
- }
-
- output
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_parse_checkbox_tasks() {
- let content = r#"
-## Phase 1: Setup
-- [ ] Set up project structure
-- [x] Configure dev environment
-
-## Phase 2: Features
-1. [ ] Implement authentication
-2. [ ] Add user dashboard
-"#;
-
- let result = parse_tasks_from_breakdown(content);
- assert_eq!(result.total, 4);
- assert_eq!(result.groups.len(), 2);
- assert!(!result.tasks[0].completed);
- assert!(result.tasks[1].completed);
- }
-
- #[test]
- fn test_parse_with_dependencies() {
- let content = r#"
-- [ ] Task A
-- [ ] Task B (depends on: Task A)
-"#;
-
- let result = parse_tasks_from_breakdown(content);
- assert_eq!(result.tasks[1].dependencies, vec!["Task A"]);
- }
-
- #[test]
- fn test_extract_files_from_diff() {
- let diff = r#"
-diff --git a/src/main.rs b/src/main.rs
---- a/src/main.rs
-+++ b/src/main.rs
-@@ -1,3 +1,4 @@
-+fn new_function() {}
-"#;
-
- let files = extract_files_from_diff(diff);
- assert!(files.contains(&"src/main.rs".to_string()));
- }
-
- #[test]
- fn test_looks_like_task() {
- assert!(looks_like_task("Add authentication"));
- assert!(looks_like_task("Create user model"));
- assert!(looks_like_task("implement feature X"));
- assert!(!looks_like_task("This is a note"));
- assert!(!looks_like_task("Summary of changes"));
- }
-}