diff options
| author | soryu <soryu@soryu.co> | 2026-05-17 21:22:34 +0100 |
|---|---|---|
| committer | soryu <soryu@soryu.co> | 2026-05-17 21:22:34 +0100 |
| commit | 857e717e6343fa5c2ae96664bdc64741d5ba6830 (patch) | |
| tree | 0f3898d9e2e2a3c312358dbf70c44f4ab1cf3648 /makima/src/llm/task_output.rs | |
| parent | ce29ae801bcc5a0ba76d5a8d1565242ab267a47d (diff) | |
| download | soryu-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.rs | 485 |
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")); - } -} |
