diff --git a/server/src/fix_env_ws.py b/server/src/fix_env_ws.py new file mode 100644 index 0000000..2adb226 --- /dev/null +++ b/server/src/fix_env_ws.py @@ -0,0 +1,61 @@ +import os + +def fix_env_and_workspaces(): + filepath = 'server/src/handlers_v2/env.rs' + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + old_env = """ let fps = state.env_fingerprints.read(); + if let Some(fp) = fps.get(&req.namespace) {""" + + new_env = """ let data = state.env_fingerprints.read_with(|fps| { + fps.get(&req.namespace).cloned() + }); + if let Some(fp) = data {""" + + if old_env in content: + content = content.replace(old_env, new_env) + print('Replaced env read') + else: + print('Could not find old_env') + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + + filepath = 'server/src/handlers_v2/workspaces.rs' + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + old_ws = """ let query = req.query.to_lowercase(); + let snippets = state.snippets.read(); + let mut results = Vec::new(); + for s in snippets { + if contains_ignore_ascii_case(&s.name, &query) + || contains_ignore_ascii_case(&s.description, &query) + || contains_ignore_ascii_case(&s.language, &query) + { + results.push(s); + } + } + let data = serde_json::to_string(&results).unwrap_or_default();""" + + new_ws = """ let query = req.query.to_lowercase(); + let data = state.snippets.read_with(|snippets| { + let results: Vec<_> = snippets.iter().filter(|s| { + contains_ignore_ascii_case(&s.name, &query) + || contains_ignore_ascii_case(&s.description, &query) + || contains_ignore_ascii_case(&s.language, &query) + }).cloned().collect(); + serde_json::to_string(&results).unwrap_or_default() + });""" + + if old_ws in content: + content = content.replace(old_ws, new_ws) + print('Replaced workspaces read') + else: + print('Could not find old_ws') + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + +fix_env_and_workspaces() diff --git a/server/src/fix_main.py b/server/src/fix_main.py new file mode 100644 index 0000000..bd9df6f --- /dev/null +++ b/server/src/fix_main.py @@ -0,0 +1,60 @@ +import os + +def fix_main_stats(): + filepath = 'server/src/main.rs' + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + old_stats = """ let (entities, relations) = { + let graph = state_clone.get_full_graph(); + (graph.entities.len(), graph.relations.len()) + }; + let tasks = state_clone.tasks.read().len(); + let snippets = state_clone.snippets.read().len(); + let tech_debts = state_clone.tech_debts.read().len(); + let adrs = state_clone.adrs.read().len(); + + let ledger = state_clone.ledger.read().len(); + let sticky = state_clone.sticky.read().len(); + let error_fixes = state_clone.error_fixes.read().len(); + let pinned_files = state_clone.pinned_files.read().len(); + let session_summaries = state_clone.session_summaries.read().len(); + let handoff_memos = state_clone.handoff_memos.read().len(); + let env_fingerprints = state_clone.env_fingerprints.read().len(); + let env_requirements = state_clone.env_requirements.read().len(); + let milestones = state_clone.milestones.read().len(); + let environments = state_clone.environments.read().len(); + let pr_checklists = state_clone.pr_checklists.read().len(); + let gates = state_clone.gates.read().len(); + let context_workspaces = state_clone.context_workspaces.read().len();""" + + new_stats = """ let (entities, relations) = state_clone.read_graph(|g| (g.entities.len(), g.relations.len())); + let tasks = state_clone.tasks.read_with(|items| items.len()); + let snippets = state_clone.snippets.read_with(|items| items.len()); + let tech_debts = state_clone.tech_debts.read_with(|items| items.len()); + let adrs = state_clone.adrs.read_with(|items| items.len()); + + let ledger = state_clone.ledger.read_with(|items| items.len()); + let sticky = state_clone.sticky.read_with(|items| items.len()); + let error_fixes = state_clone.error_fixes.read_with(|items| items.len()); + let pinned_files = state_clone.pinned_files.read_with(|items| items.len()); + let session_summaries = state_clone.session_summaries.read_with(|items| items.len()); + let handoff_memos = state_clone.handoff_memos.read_with(|items| items.len()); + let env_fingerprints = state_clone.env_fingerprints.read_with(|items| items.len()); + let env_requirements = state_clone.env_requirements.read_with(|items| items.len()); + let milestones = state_clone.milestones.read_with(|items| items.len()); + let environments = state_clone.environments.read_with(|items| items.len()); + let pr_checklists = state_clone.pr_checklists.read_with(|items| items.len()); + let gates = state_clone.gates.read_with(|items| items.len()); + let context_workspaces = state_clone.context_workspaces.read_with(|items| items.len());""" + + if old_stats in content: + content = content.replace(old_stats, new_stats) + print('Replaced stats reads') + else: + print('Could not find old_stats') + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + +fix_main_stats() diff --git a/server/src/fix_meta.py b/server/src/fix_meta.py new file mode 100644 index 0000000..0c23291 --- /dev/null +++ b/server/src/fix_meta.py @@ -0,0 +1,111 @@ +import os + +def fix_meta(): + filepath = 'server/src/handlers_v2/meta.rs' + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + old_omni = """ let q = req.query.to_lowercase(); + let tech_debts: Vec<_> = state + .tech_debts + .read() + .into_iter() + .filter(|d| { + req.namespace.as_ref().is_none_or(|ns| d.namespace == *ns) + && (contains_ignore_ascii_case(&d.description, &q) + || contains_ignore_ascii_case(&d.ideal_solution, &q)) + }) + .collect(); + let memos: Vec<_> = state + .handoff_memos + .read() + .into_iter() + .filter(|m| { + req.namespace.as_ref().is_none_or(|ns| m.namespace == *ns) + && contains_ignore_ascii_case(&m.content, &q) + }) + .collect(); + let error_fixes: Vec<_> = state + .error_fixes + .read() + .into_iter() + .filter(|f| { + contains_ignore_ascii_case(&f.signature, &q) + || contains_ignore_ascii_case(&f.solution, &q) + }) + .collect();""" + + new_omni = """ let q = req.query.to_lowercase(); + let tech_debts = state.tech_debts.read_with(|debts| { + debts.iter().filter(|d| { + req.namespace.as_ref().is_none_or(|ns| d.namespace == *ns) + && (contains_ignore_ascii_case(&d.description, &q) + || contains_ignore_ascii_case(&d.ideal_solution, &q)) + }).cloned().collect::>() + }); + let memos = state.handoff_memos.read_with(|memos| { + memos.iter().filter(|m| { + req.namespace.as_ref().is_none_or(|ns| m.namespace == *ns) + && contains_ignore_ascii_case(&m.content, &q) + }).cloned().collect::>() + }); + let error_fixes = state.error_fixes.read_with(|fixes| { + fixes.iter().filter(|f| { + contains_ignore_ascii_case(&f.signature, &q) + || contains_ignore_ascii_case(&f.solution, &q) + }).cloned().collect::>() + });""" + + if old_omni in content: + content = content.replace(old_omni, new_omni) + print('Replaced omni_search vecs') + else: + print('Could not find old_omni') + + old_health = """ let active_tasks = state + .tasks + .read() + .into_iter() + .filter(|t| t.status != "done") + .count(); + let unresolved_debt = state + .tech_debts + .read() + .into_iter() + .filter(|d| d.namespace == req.namespace && !d.is_resolved) + .count(); + let unread_memos = state + .handoff_memos + .read() + .into_iter() + .filter(|m| m.namespace == req.namespace) + .count(); + let active_milestones = state + .milestones + .read() + .into_iter() + .filter(|m| m.namespace == req.namespace && m.status != "done") + .count(); + let remaining_checklists = state + .pr_checklists + .read() + .into_iter() + .filter(|c| c.namespace == req.namespace) + .count();""" + + new_health = """ let active_tasks = state.tasks.read_with(|tasks| tasks.iter().filter(|t| t.status != "done").count()); + let unresolved_debt = state.tech_debts.read_with(|debts| debts.iter().filter(|d| d.namespace == req.namespace && !d.is_resolved).count()); + let unread_memos = state.handoff_memos.read_with(|memos| memos.iter().filter(|m| m.namespace == req.namespace).count()); + let active_milestones = state.milestones.read_with(|milestones| milestones.iter().filter(|m| m.namespace == req.namespace && m.status != "done").count()); + let remaining_checklists = state.pr_checklists.read_with(|checklists| checklists.iter().filter(|c| c.namespace == req.namespace).count());""" + + if old_health in content: + content = content.replace(old_health, new_health) + print('Replaced health counters') + else: + print('Could not find old_health') + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + +fix_meta() diff --git a/server/src/fix_notes.py b/server/src/fix_notes.py new file mode 100644 index 0000000..d6439c8 --- /dev/null +++ b/server/src/fix_notes.py @@ -0,0 +1,40 @@ +import os + +def fix_notes(): + filepath = 'server/src/handlers_v2/notes.rs' + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + + old_standup = """ let tasks = state + .tasks + .read() + .into_iter() + .filter(|t| t.updated_at >= cutoff) + .collect::>(); + let changes = state + .ledger + .read() + .into_iter() + .filter(|c| c.timestamp >= cutoff) + .collect::>(); + let summaries = state + .session_summaries + .read() + .into_iter() + .filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff) + .collect::>();""" + + new_standup = """ let tasks = state.tasks.read_with(|items| items.iter().filter(|t| t.updated_at >= cutoff).cloned().collect::>()); + let changes = state.ledger.read_with(|items| items.iter().filter(|c| c.timestamp >= cutoff).cloned().collect::>()); + let summaries = state.session_summaries.read_with(|items| items.iter().filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff).cloned().collect::>());""" + + if old_standup in content: + content = content.replace(old_standup, new_standup) + print('Replaced standup in notes.rs') + else: + print('Could not find old_standup in notes.rs') + + with open(filepath, 'w', encoding='utf-8') as f: + f.write(content) + +fix_notes() diff --git a/server/src/handlers_v2/env.rs b/server/src/handlers_v2/env.rs index 944ca24..2381908 100644 --- a/server/src/handlers_v2/env.rs +++ b/server/src/handlers_v2/env.rs @@ -61,9 +61,11 @@ impl McpTool for ReadEnvFingerprintHandler { async fn execute(&self, args: Value, state: Arc) -> Result { let req: ReadEnvFingerprintTool = serde_json::from_value(args).map_err(|e| e.to_string())?; - let fps = state.env_fingerprints.read(); - if let Some(fp) = fps.get(&req.namespace) { - let data = serde_json::to_string(fp).unwrap_or_default(); + let data = state.env_fingerprints.read_with(|fps| { + fps.get(&req.namespace).cloned() + }); + if let Some(fp) = data { + let data = serde_json::to_string(&fp).unwrap_or_default(); Ok(data.to_string()) } else { Ok("{}".to_string()) @@ -164,3 +166,4 @@ impl McpTool for GetEnvironmentDetailsHandler { } + diff --git a/server/src/handlers_v2/meta.rs b/server/src/handlers_v2/meta.rs index 88135f7..3743155 100644 --- a/server/src/handlers_v2/meta.rs +++ b/server/src/handlers_v2/meta.rs @@ -368,60 +368,57 @@ impl McpTool for OmniSearchHandler { } } }); - for t in state.tasks.read() { - if matches - .iter() - .any(|(id, typ, _, _, _)| id == &t.id && typ == "task") - { - tasks.push(t); + state.tasks.read_with(|all_tasks| { + for t in all_tasks { + if matches + .iter() + .any(|(id, typ, _, _, _)| id == &t.id && typ == "task") + { + tasks.push(t.clone()); + } } - } - for s in state.snippets.read() { - if matches - .iter() - .any(|(id, typ, _, _, _)| id == &s.name && typ == "snippet") - { - snippets.push(s); + }); + state.snippets.read_with(|all_snippets| { + for s in all_snippets { + if matches + .iter() + .any(|(id, typ, _, _, _)| id == &s.name && typ == "snippet") + { + snippets.push(s.clone()); + } } - } - for a in state.adrs.read() { - if matches - .iter() - .any(|(id, typ, _, _, _)| id == &a.id && typ == "adr") - { - adrs.push(a); + }); + state.adrs.read_with(|all_adrs| { + for a in all_adrs { + if matches + .iter() + .any(|(id, typ, _, _, _)| id == &a.id && typ == "adr") + { + adrs.push(a.clone()); + } } - } + }); let q = req.query.to_lowercase(); - let tech_debts: Vec<_> = state - .tech_debts - .read() - .into_iter() - .filter(|d| { + let tech_debts = state.tech_debts.read_with(|debts| { + debts.iter().filter(|d| { req.namespace.as_ref().is_none_or(|ns| d.namespace == *ns) && (contains_ignore_ascii_case(&d.description, &q) || contains_ignore_ascii_case(&d.ideal_solution, &q)) - }) - .collect(); - let memos: Vec<_> = state - .handoff_memos - .read() - .into_iter() - .filter(|m| { + }).cloned().collect::>() + }); + let memos = state.handoff_memos.read_with(|memos| { + memos.iter().filter(|m| { req.namespace.as_ref().is_none_or(|ns| m.namespace == *ns) && contains_ignore_ascii_case(&m.content, &q) - }) - .collect(); - let error_fixes: Vec<_> = state - .error_fixes - .read() - .into_iter() - .filter(|f| { + }).cloned().collect::>() + }); + let error_fixes = state.error_fixes.read_with(|fixes| { + fixes.iter().filter(|f| { contains_ignore_ascii_case(&f.signature, &q) || contains_ignore_ascii_case(&f.solution, &q) - }) - .collect(); + }).cloned().collect::>() + }); let report = serde_json::json!({ "knowledge_graph": kg.entities, @@ -453,36 +450,11 @@ impl McpTool for GetProjectHealthHandler { async fn execute(&self, args: Value, state: Arc) -> Result { let req: GetProjectHealthTool = serde_json::from_value(args).map_err(|e| e.to_string())?; - let active_tasks = state - .tasks - .read() - .into_iter() - .filter(|t| t.status != "done") - .count(); - let unresolved_debt = state - .tech_debts - .read() - .into_iter() - .filter(|d| d.namespace == req.namespace && !d.is_resolved) - .count(); - let unread_memos = state - .handoff_memos - .read() - .into_iter() - .filter(|m| m.namespace == req.namespace) - .count(); - let active_milestones = state - .milestones - .read() - .into_iter() - .filter(|m| m.namespace == req.namespace && m.status != "done") - .count(); - let remaining_checklists = state - .pr_checklists - .read() - .into_iter() - .filter(|c| c.namespace == req.namespace) - .count(); + let active_tasks = state.tasks.read_with(|tasks| tasks.iter().filter(|t| t.status != "done").count()); + let unresolved_debt = state.tech_debts.read_with(|debts| debts.iter().filter(|d| d.namespace == req.namespace && !d.is_resolved).count()); + let unread_memos = state.handoff_memos.read_with(|memos| memos.iter().filter(|m| m.namespace == req.namespace).count()); + let active_milestones = state.milestones.read_with(|milestones| milestones.iter().filter(|m| m.namespace == req.namespace && m.status != "done").count()); + let remaining_checklists = state.pr_checklists.read_with(|checklists| checklists.iter().filter(|c| c.namespace == req.namespace).count()); let report = serde_json::json!({ "active_tasks": active_tasks, @@ -498,3 +470,4 @@ impl McpTool for GetProjectHealthHandler { use crate::handlers_v2::utils::*; + diff --git a/server/src/handlers_v2/notes.rs b/server/src/handlers_v2/notes.rs index ab1d350..ab91ec1 100644 --- a/server/src/handlers_v2/notes.rs +++ b/server/src/handlers_v2/notes.rs @@ -254,24 +254,9 @@ impl McpTool for GenerateStandupReportHandler { .unwrap_or_default() .as_secs() .saturating_sub(req.hours_lookback * 3600); - let tasks = state - .tasks - .read() - .into_iter() - .filter(|t| t.updated_at >= cutoff) - .collect::>(); - let changes = state - .ledger - .read() - .into_iter() - .filter(|c| c.timestamp >= cutoff) - .collect::>(); - let summaries = state - .session_summaries - .read() - .into_iter() - .filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff) - .collect::>(); + let tasks = state.tasks.read_with(|items| items.iter().filter(|t| t.updated_at >= cutoff).cloned().collect::>()); + let changes = state.ledger.read_with(|items| items.iter().filter(|c| c.timestamp >= cutoff).cloned().collect::>()); + let summaries = state.session_summaries.read_with(|items| items.iter().filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff).cloned().collect::>()); let report = serde_json::json!({ "tasks_updated": tasks, "code_changes": changes, "session_summaries": summaries }); Ok(report.to_string()) } diff --git a/server/src/handlers_v2/workspaces.rs b/server/src/handlers_v2/workspaces.rs index 66baf12..34f6906 100644 --- a/server/src/handlers_v2/workspaces.rs +++ b/server/src/handlers_v2/workspaces.rs @@ -147,17 +147,14 @@ impl McpTool for SearchSnippetsHandler { async fn execute(&self, args: Value, state: Arc) -> Result { let req: SearchSnippetsTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let query = req.query.to_lowercase(); - let snippets = state.snippets.read(); - let mut results = Vec::new(); - for s in snippets { - if contains_ignore_ascii_case(&s.name, &query) - || contains_ignore_ascii_case(&s.description, &query) - || contains_ignore_ascii_case(&s.language, &query) - { - results.push(s); - } - } - let data = serde_json::to_string(&results).unwrap_or_default(); + let data = state.snippets.read_with(|snippets| { + let results: Vec<_> = snippets.iter().filter(|s| { + contains_ignore_ascii_case(&s.name, &query) + || contains_ignore_ascii_case(&s.description, &query) + || contains_ignore_ascii_case(&s.language, &query) + }).cloned().collect(); + serde_json::to_string(&results).unwrap_or_default() + }); Ok(data.to_string()) } } diff --git a/server/src/main.rs b/server/src/main.rs index baab1c1..2912d82 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -355,28 +355,25 @@ async fn run_server(state: Arc) -> Result<(), Box