Refactor: Dismantle MemoryState into semantic domain sub-structs

This commit is contained in:
Riz Ashraf committed 2026-09-30 21:13:03 +01:00
1 parent 0e866f2465
commit a34554b7ff
13 files changed
+186 -158

No files matched your search

+6 -6
View File
@@ -23,7 +23,7 @@ impl McpTool for UpdateEnvFingerprintHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: UpdateEnvFingerprintTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
state.env_fingerprints.modify(|fps| {
state.env.env_fingerprints.modify(|fps| {
fps.insert(
req.namespace.clone(),
crate::models::EnvFingerprint {
@@ -58,7 +58,7 @@ impl McpTool for ReadEnvFingerprintHandler {
let req: ReadEnvFingerprintTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state
.env_fingerprints
.env.env_fingerprints
.read_with(|fps| fps.get(&req.namespace).cloned());
if let Some(fp) = data {
let data = Ok::<String, crate::error::AppError>(serde_json::to_string(&fp)?)?;
@@ -86,7 +86,7 @@ impl McpTool for LogEnvRequirementHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LogEnvRequirementTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.env_requirements.modify(|reqs| {
state.env.env_requirements.modify(|reqs| {
reqs.retain(|r| !(r.namespace == req.namespace && r.key == req.key));
reqs.push(crate::models::EnvRequirement {
namespace: req.namespace,
@@ -117,7 +117,7 @@ impl McpTool for RegisterEnvironmentHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: RegisterEnvironmentTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
state.environments.modify(|envs| {
state.env.environments.modify(|envs| {
envs.retain(|e| !(e.namespace == req.namespace && e.name == req.name));
envs.push(crate::models::EnvironmentDetail {
namespace: req.namespace,
@@ -150,7 +150,7 @@ impl McpTool for GetEnvironmentDetailsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: GetEnvironmentDetailsTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.environments.read_with(|envs| {
let data = state.env.environments.read_with(|envs| {
let filtered: Vec<_> = envs
.iter()
.filter(|e| e.namespace == req.namespace)
@@ -197,7 +197,7 @@ mod tests {
let state = Arc::new(MemoryState::new(dir.path().to_str().unwrap()));
// Ensure namespace is present in test setup
state.environments.modify(|e| {
state.env.environments.modify(|e| {
e.push(crate::models::EnvironmentDetail {
namespace: "global".to_string(),
name: "test".to_string(),
+24 -24
View File
@@ -24,7 +24,7 @@ impl McpTool for LogDecisionHandler {
let idx = state.get_search_index();
let mut final_id = String::new();
state.adrs.modify(|adrs| {
state.code.adrs.modify(|adrs| {
if let Some(superseded_id) = &req.supersedes {
for old_adr in adrs.iter_mut() {
if old_adr.id == *superseded_id {
@@ -70,7 +70,7 @@ impl McpTool for QueryDecisionsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: QueryDecisionsTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.adrs.read_with(|adrs| {
let data = state.code.adrs.read_with(|adrs| {
let filtered: Vec<_> = adrs
.iter()
.filter(|a| {
@@ -108,7 +108,7 @@ impl McpTool for DeleteDecisionHandler {
let req: crate::tools::DeleteDecisionTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
state.adrs.modify(|adrs| {
state.code.adrs.modify(|adrs| {
if let Some(pos) = adrs.iter().position(|a| a.id == req.id) {
adrs.remove(pos);
found = true;
@@ -140,7 +140,7 @@ impl McpTool for LogErrorFixHandler {
let req: LogErrorFixTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let text_to_embed = format!("Signature: {}\nSolution: {}", req.signature, req.solution);
let embedding = crate::embedding::generate_embedding_async(text_to_embed).await.ok();
state.error_fixes.modify(|fixes| {
state.code.error_fixes.modify(|fixes| {
fixes.push(crate::models::ErrorFix {
signature: req.signature,
solution: req.solution,
@@ -172,7 +172,7 @@ impl McpTool for SearchErrorFixesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: SearchErrorFixesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let q = req.query;
let data = state.error_fixes.read_with(|fixes| {
let data = state.code.error_fixes.read_with(|fixes| {
let filtered: Vec<_> = fixes
.iter()
.filter(|f| {
@@ -200,7 +200,7 @@ impl McpTool for LogCodeChangeHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LogCodeChangeTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.ledger.modify(|ledger| {
state.code.ledger.modify(|ledger| {
ledger.push(CodeChange {
timestamp: crate::handlers::utils::now_secs(),
file_path: req.file_path,
@@ -230,7 +230,7 @@ impl McpTool for QueryRecentChangesHandler {
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let data = state
.ledger
.code.ledger
.read_with(|l| Ok::<String, crate::error::AppError>(serde_json::to_string(l)?))?;
Ok(data)
}
@@ -250,7 +250,7 @@ impl McpTool for LearnPreferenceHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LearnPreferenceTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.prefs.modify(|prefs| {
state.env.prefs.modify(|prefs| {
prefs.insert(
req.key.clone(),
crate::models::Preference {
@@ -278,7 +278,7 @@ impl McpTool for ReadPreferencesHandler {
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
state
.prefs
.env.prefs
.read_with(|prefs| Ok::<String, crate::error::AppError>(serde_json::to_string(prefs)?))
}
}
@@ -299,7 +299,7 @@ impl McpTool for LogTechDebtHandler {
let req: LogTechDebtTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let text_to_embed = format!("Description: {}\nIdeal Solution: {}", req.description, req.ideal_solution);
let embedding = crate::embedding::generate_embedding_async(text_to_embed).await.ok();
state.tech_debts.modify(|debts| {
state.code.tech_debts.modify(|debts| {
debts.push(crate::models::TechDebt {
id: uuid::Uuid::new_v4().to_string(),
namespace: req.namespace,
@@ -334,7 +334,7 @@ impl McpTool for ResolveTechDebtHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ResolveTechDebtTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
state.tech_debts.modify(|debts| {
state.code.tech_debts.modify(|debts| {
for d in debts.iter_mut() {
if d.id == req.id {
d.is_resolved = true;
@@ -365,7 +365,7 @@ impl McpTool for ListTechDebtHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListTechDebtTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.tech_debts.read_with(|debts| {
let data = state.code.tech_debts.read_with(|debts| {
let filtered: Vec<_> = debts
.iter()
.filter(|d| {
@@ -451,7 +451,7 @@ impl McpTool for OmniSearchHandler {
}
}
let tasks_json = state.tasks.read_with(|all_tasks| {
let tasks_json = state.project.tasks.read_with(|all_tasks| {
let filtered: Vec<_> = all_tasks
.iter()
.filter(|t| matched_tasks.contains(t.id.as_str()))
@@ -470,7 +470,7 @@ impl McpTool for OmniSearchHandler {
serde_json::to_value(&filtered).map_err(|e| e.to_string())
})?;
let snippets_json = state.snippets.read_with(|all_snippets| {
let snippets_json = state.code.snippets.read_with(|all_snippets| {
let mut scored: Vec<_> = all_snippets.iter().map(|s| {
let mut score = 0.0;
if matched_snippets.contains(s.name.as_str()) || contains_ignore_ascii_case(&s.name, &q) || contains_ignore_ascii_case(&s.description, &q) {
@@ -495,7 +495,7 @@ impl McpTool for OmniSearchHandler {
serde_json::to_value(&filtered).map_err(|e| e.to_string())
})?;
let adrs_json = state.adrs.read_with(|all_adrs| {
let adrs_json = state.code.adrs.read_with(|all_adrs| {
let filtered: Vec<_> = all_adrs
.iter()
.filter(|a| matched_adrs.contains(a.id.as_str()))
@@ -516,7 +516,7 @@ impl McpTool for OmniSearchHandler {
})?;
let q = req.query;
let tech_debts_json = state.tech_debts.read_with(|debts| {
let tech_debts_json = state.code.tech_debts.read_with(|debts| {
let mut scored: Vec<_> = debts.iter().map(|d| {
if req.namespace.as_ref().is_some_and(|ns| d.namespace != *ns) {
return (d, 0.0);
@@ -545,7 +545,7 @@ impl McpTool for OmniSearchHandler {
serde_json::to_value(&filtered).map_err(|e| e.to_string())
})?;
let memos_json = state.handoff_memos.read_with(|memos| {
let memos_json = state.telemetry.handoff_memos.read_with(|memos| {
let filtered: Vec<_> = memos
.iter()
.filter(|m| {
@@ -566,7 +566,7 @@ impl McpTool for OmniSearchHandler {
serde_json::to_value(&filtered).map_err(|e| e.to_string())
})?;
let error_fixes_json = state.error_fixes.read_with(|fixes| {
let error_fixes_json = state.code.error_fixes.read_with(|fixes| {
let mut scored: Vec<_> = fixes.iter().map(|f| {
let mut score = 0.0;
if contains_ignore_ascii_case(&f.signature, &q) || contains_ignore_ascii_case(&f.solution, &q) {
@@ -614,27 +614,27 @@ impl McpTool for GetProjectHealthHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: GetProjectHealthTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let active_tasks = state
.tasks
.project.tasks
.read_with(|tasks| tasks.iter().filter(|t| t.status != "done").count());
let unresolved_debt = state.tech_debts.read_with(|debts| {
let unresolved_debt = state.code.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| {
let unread_memos = state.telemetry.handoff_memos.read_with(|memos| {
memos
.iter()
.filter(|m| m.namespace == req.namespace)
.count()
});
let active_milestones = state.milestones.read_with(|milestones| {
let active_milestones = state.project.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| {
let remaining_checklists = state.project.pr_checklists.read_with(|checklists| {
checklists
.iter()
.filter(|c| c.namespace == req.namespace)
@@ -821,7 +821,7 @@ mod tests {
};
{
state.tasks.modify(|t| {
state.project.tasks.modify(|t| {
t.push(task.clone());
});
}
+11 -11
View File
@@ -21,7 +21,7 @@ impl McpTool for AddStickyNoteHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.sticky.modify(|notes| {
state.code.sticky.modify(|notes| {
notes.push(StickyNote {
timestamp: crate::handlers::utils::now_secs(),
content: req.content,
@@ -49,7 +49,7 @@ impl McpTool for ReadStickyNotesHandler {
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let data = state
.sticky
.code.sticky
.read_with(|s| Ok::<String, crate::error::AppError>(serde_json::to_string(s)?))?;
Ok(data)
}
@@ -73,7 +73,7 @@ impl McpTool for DeleteStickyNoteHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: DeleteStickyNoteTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
state.sticky.modify(|notes| {
state.code.sticky.modify(|notes| {
if req.index > 0 && req.index <= notes.len() {
notes.remove(req.index - 1);
success = true;
@@ -103,7 +103,7 @@ impl McpTool for ClearStickyNotesHandler {
}
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
state.sticky.modify(|notes| {
state.code.sticky.modify(|notes| {
notes.clear();
});
Ok("All sticky notes cleared.".to_string())
@@ -127,7 +127,7 @@ impl McpTool for LeaveHandoffMemoHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LeaveHandoffMemoTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.handoff_memos.modify(|memos| {
state.telemetry.handoff_memos.modify(|memos| {
memos.push(crate::models::HandoffMemo {
id: uuid::Uuid::new_v4().to_string(),
author: "agy".to_string(),
@@ -158,7 +158,7 @@ impl McpTool for ReadHandoffMemosHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ReadHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.handoff_memos.read_with(|items| {
let data = state.telemetry.handoff_memos.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| {
@@ -194,7 +194,7 @@ impl McpTool for ClearHandoffMemosHandler {
let req: ClearHandoffMemosTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let ids: HashSet<_> = req.ids.into_iter().collect();
state
.handoff_memos
.telemetry.handoff_memos
.modify(|memos| memos.retain(|m| !ids.contains(&m.id)));
Ok("Handoff memos cleared".to_string())
}
@@ -217,7 +217,7 @@ impl McpTool for AddSessionSummaryHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddSessionSummaryTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.session_summaries.modify(|summaries| {
state.telemetry.session_summaries.modify(|summaries| {
summaries.push(crate::models::SessionSummary {
summary: req.summary,
namespace: req.namespace,
@@ -249,9 +249,9 @@ impl McpTool for GenerateStandupReportHandler {
serde_json::from_value(args).map_err(|e| e.to_string())?;
let cutoff = crate::handlers::utils::now_secs().saturating_sub(req.hours_lookback * 3600);
let report_str = state.tasks.read_with(|items| {
state.ledger.read_with(|changes| {
state.session_summaries.read_with(|summaries| {
let report_str = state.project.tasks.read_with(|items| {
state.code.ledger.read_with(|changes| {
state.telemetry.session_summaries.read_with(|summaries| {
let filtered_tasks: Vec<_> = items.iter().filter(|t| t.updated_at >= cutoff).collect();
let filtered_changes: Vec<_> = changes.iter().filter(|c| c.timestamp >= cutoff).collect();
let filtered_summaries: Vec<_> = summaries.iter().filter(|s| s.namespace == req.namespace && s.timestamp >= cutoff).collect();
+10 -10
View File
@@ -40,7 +40,7 @@ impl McpTool for AddTaskHandler {
};
let idx = state.get_search_index();
drop(idx.index_task(&task));
state.tasks.modify(|tasks| {
state.project.tasks.modify(|tasks| {
tasks.push(task);
});
Ok(format!("Task added with ID: {}", task_id).to_string())
@@ -63,7 +63,7 @@ impl McpTool for DeleteTaskHandler {
let req: DeleteTaskTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut deleted_count = 0;
let mut actually_deleted = Vec::new();
state.tasks.modify(|tasks| {
state.project.tasks.modify(|tasks| {
let initial_len = tasks.len();
// Build index-based children map
@@ -141,7 +141,7 @@ impl McpTool for UpdateTaskStatusHandler {
let mut blocker_details = String::new();
let target_status = req.status.to_lowercase();
state.tasks.modify(|tasks| {
state.project.tasks.modify(|tasks| {
// Find target task
let target_idx = tasks
.iter()
@@ -279,7 +279,7 @@ impl McpTool for ListActiveTasksHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListActiveTasksTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.tasks.read_with(|tasks| {
let data = state.project.tasks.read_with(|tasks| {
let filtered: Vec<_> = tasks
.iter()
.filter(|t| {
@@ -319,7 +319,7 @@ impl McpTool for SetAcceptanceCriteriaHandler {
let req: SetAcceptanceCriteriaTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
state.tasks.modify(|tasks| {
state.project.tasks.modify(|tasks| {
if let Some(task) = tasks.iter_mut().rev().find(|t| t.title == req.task_title) {
task.acceptance_criteria = req
.criteria
@@ -362,7 +362,7 @@ impl McpTool for VerifyAcceptanceCriteriaHandler {
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut success = false;
let mut already_met = false;
state.tasks.modify(|tasks| {
state.project.tasks.modify(|tasks| {
if let Some(task) = tasks.iter_mut().find(|t| t.id == req.task_id)
&& let Some(ac) = task
.acceptance_criteria
@@ -405,7 +405,7 @@ impl McpTool for AddMilestoneHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddMilestoneTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.milestones.modify(|ms| {
state.project.milestones.modify(|ms| {
ms.push(crate::models::Milestone {
id: uuid::Uuid::new_v4().to_string(),
title: req.title,
@@ -433,7 +433,7 @@ impl McpTool for UpdateMilestoneHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: UpdateMilestoneTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
state.milestones.modify(|ms| {
state.project.milestones.modify(|ms| {
for m in ms.iter_mut() {
if m.id == req.id {
m.status = req.status.clone();
@@ -464,7 +464,7 @@ impl McpTool for ListMilestonesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListMilestonesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.milestones.read_with(|items| {
let data = state.project.milestones.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| {
@@ -559,7 +559,7 @@ mod tests {
assert!(res1.contains("Milestone added"));
// Fetch milestone ID from state directly to update
let ms_id = state.milestones.read_with(|ms| ms[0].id.clone());
let ms_id = state.project.milestones.read_with(|ms| ms[0].id.clone());
// Update Milestone
let update_ms = UpdateMilestoneHandler;
+15 -15
View File
@@ -20,7 +20,7 @@ impl McpTool for PinFileHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: PinFileTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.pinned_files.modify(|pinned| {
state.project.pinned_files.modify(|pinned| {
pinned.retain(|p| p.namespace != req.namespace || p.file_path != req.file_path);
pinned.push(crate::models::PinnedFile {
namespace: req.namespace,
@@ -47,7 +47,7 @@ impl McpTool for UnpinFileHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: UnpinFileTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state.pinned_files.modify(|pinned| {
state.project.pinned_files.modify(|pinned| {
pinned.retain(|p| p.namespace != req.namespace || p.file_path != req.file_path)
});
Ok("File unpinned".to_string())
@@ -71,7 +71,7 @@ impl McpTool for ListPinnedFilesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListPinnedFilesTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.pinned_files.read_with(|pinned| {
let data = state.project.pinned_files.read_with(|pinned| {
let filtered: Vec<_> = pinned
.iter()
.filter(|p| {
@@ -124,7 +124,7 @@ impl McpTool for StoreSnippetHandler {
let idx = state.get_search_index();
drop(idx.index_snippet(&snippet));
state.snippets.modify(|snippets| {
state.code.snippets.modify(|snippets| {
snippets.retain(|s| s.name != req_name);
snippets.push(snippet);
});
@@ -148,7 +148,7 @@ impl McpTool for SearchSnippetsHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: SearchSnippetsTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let query = req.query;
let data = state.snippets.read_with(|snippets| {
let data = state.code.snippets.read_with(|snippets| {
let results: Vec<_> = snippets
.iter()
.filter(|s| {
@@ -178,7 +178,7 @@ impl McpTool for DeleteSnippetHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: DeleteSnippetTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut deleted = false;
state.snippets.modify(|snippets| {
state.code.snippets.modify(|snippets| {
let orig = snippets.len();
snippets.retain(|s| s.name != req.name);
deleted = snippets.len() < orig;
@@ -211,7 +211,7 @@ impl McpTool for SaveContextWorkspaceHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: SaveContextWorkspaceTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
state.context_workspaces.modify(|ws| {
state.project.context_workspaces.modify(|ws| {
ws.retain(|w| w.namespace != req.namespace || w.name != req.name);
ws.push(crate::models::ContextWorkspace {
namespace: req.namespace,
@@ -243,7 +243,7 @@ impl McpTool for LoadContextWorkspaceHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: LoadContextWorkspaceTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.context_workspaces.read_with(|ws| {
let data = state.project.context_workspaces.read_with(|ws| {
let filtered: Vec<_> = ws
.iter()
.filter(|w| w.namespace == req.namespace && w.name == req.name)
@@ -272,7 +272,7 @@ impl McpTool for ListContextWorkspacesHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ListContextWorkspacesTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.context_workspaces.read_with(|ws| {
let data = state.project.context_workspaces.read_with(|ws| {
let filtered: Vec<_> = ws
.iter()
.filter(|w| req.namespace.as_ref().is_none_or(|ns| &w.namespace == ns))
@@ -303,7 +303,7 @@ impl McpTool for DeleteContextWorkspaceHandler {
serde_json::from_value(args).map_err(|e| e.to_string())?;
let mut found = false;
state.context_workspaces.modify(|ws| {
state.project.context_workspaces.modify(|ws| {
if let Some(pos) = ws
.iter()
.position(|w| w.namespace == req.namespace && w.name == req.name)
@@ -339,7 +339,7 @@ impl McpTool for AddPrChecklistItemHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: AddPrChecklistItemTool =
serde_json::from_value(args).map_err(|e| e.to_string())?;
state.pr_checklists.modify(|items| {
state.project.pr_checklists.modify(|items| {
items.push(crate::models::PrChecklistItem {
namespace: req.namespace,
id: uuid::Uuid::new_v4().to_string(),
@@ -364,7 +364,7 @@ impl McpTool for GetPrChecklistHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: GetPrChecklistTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.pr_checklists.read_with(|items| {
let data = state.project.pr_checklists.read_with(|items| {
let filtered: Vec<_> = items
.iter()
.filter(|i| i.namespace == req.namespace)
@@ -393,7 +393,7 @@ impl McpTool for ClearPrChecklistHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> crate::error::Result<String> {
let req: ClearPrChecklistTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
state
.pr_checklists
.project.pr_checklists
.modify(|items| items.retain(|i| i.namespace != req.namespace));
Ok("PR checklist cleared".to_string())
}
@@ -625,14 +625,14 @@ impl McpTool for SemanticCodeSearchHandler {
let mut texts_to_embed = Vec::new();
let mut metadata = Vec::new();
let snippets = state.snippets.read_with(|snips| snips.clone());
let snippets = state.code.snippets.read_with(|snips| snips.clone());
for snippet in snippets {
let combined = format!("{} {} {}", snippet.name, snippet.description, snippet.code);
texts_to_embed.push(combined);
metadata.push((snippet.name, snippet.description));
}
let sticky = state.sticky.read_with(|s| s.clone());
let sticky = state.code.sticky.read_with(|s| s.clone());
for note in sticky {
texts_to_embed.push(note.content.clone());
metadata.push(("StickyNote".to_string(), note.content.chars().take(200).collect::<String>()));