Compare commits

...
4 Commits
8 changed files with 290 additions and 65 deletions

No files matched your search

+135
View File
@@ -478,6 +478,12 @@
<button class="tab-button" onclick="switchTab('search-tab', this)">Omni-Search</button> <button class="tab-button" onclick="switchTab('search-tab', this)">Omni-Search</button>
<button class="tab-button" onclick="switchTab('task-tab', this)">Task Board</button> <button class="tab-button" onclick="switchTab('task-tab', this)">Task Board</button>
<button class="tab-button" onclick="switchTab('sticky-tab', this)">Sticky Notes</button> <button class="tab-button" onclick="switchTab('sticky-tab', this)">Sticky Notes</button>
<button class="tab-button" onclick="switchTab('techdebt-tab', this)">Tech Debt</button>
<button class="tab-button" onclick="switchTab('adrs-tab', this)">Decisions (ADR)</button>
<button class="tab-button" onclick="switchTab('workspaces-tab', this)">Workspaces</button>
<button class="tab-button" onclick="switchTab('memos-tab', this)">Handoffs</button>
<button class="tab-button" onclick="switchTab('snippets-tab', this)">Snippets</button>
<button class="tab-button" onclick="switchTab('pr-tab', this)">PRs</button>
<button class="tab-button" onclick="switchTab('activity-tab', this)">Live Activity</button> <button class="tab-button" onclick="switchTab('activity-tab', this)">Live Activity</button>
</div> </div>
@@ -529,6 +535,50 @@
</div> </div>
</div> </div>
<div id="techdebt-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>Tech Debt</h2>
<div id="techdebt-container" style="display:flex; flex-direction:column; gap:15px; margin-bottom: 20px;"></div>
<h2 style="border-top: 1px solid var(--border-color); padding-top: 15px;">Resolved Error Patterns</h2>
<div id="errorfixes-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="adrs-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>Architecture Decision Records</h2>
<div id="adrs-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="workspaces-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>Context Workspaces</h2>
<div id="workspaces-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="memos-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>Handoff Memos & Milestones</h2>
<div id="memos-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="snippets-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>Snippets Library</h2>
<div id="snippets-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="pr-tab" class="tab-content">
<div class="panel" style="flex:1; display:flex; flex-direction:column; overflow-y:auto;">
<h2>PR Checklists</h2>
<div id="pr-container" style="display:flex; flex-direction:column; gap:15px;"></div>
</div>
</div>
<div id="activity-tab" class="tab-content"> <div id="activity-tab" class="tab-content">
<div class="panel activity-panel" style="flex:1; display:flex; flex-direction:column;"> <div class="panel activity-panel" style="flex:1; display:flex; flex-direction:column;">
<div id="activity-feed" style="flex:1;"></div> <div id="activity-feed" style="flex:1;"></div>
@@ -549,9 +599,16 @@
// Resize graph when becoming visible again // Resize graph when becoming visible again
network.redraw(); network.redraw();
network.fit(); network.fit();
loadGraph();
} else if (tabId === 'activity-tab') { } else if (tabId === 'activity-tab') {
const feed = document.getElementById('activity-feed'); const feed = document.getElementById('activity-feed');
feed.scrollTop = feed.scrollHeight; feed.scrollTop = feed.scrollHeight;
} else if (tabId === 'task-tab') {
loadTasks();
} else if (tabId === 'sticky-tab') {
loadStickyNotes();
} else {
loadAllExtras();
} }
} }
@@ -986,6 +1043,82 @@
} }
} }
async function loadGenericList(endpoint, containerId, formatter) {
try {
const res = await fetch(endpoint);
const data = await res.json();
const container = document.getElementById(containerId);
if (!container) return;
if (!data || data.length === 0) {
container.innerHTML = '<div style="color:var(--text-secondary); padding:20px; text-align:center;">No items recorded.</div>';
return;
}
container.innerHTML = data.map(item => {
const date = item.timestamp ? new Date(item.timestamp * 1000).toLocaleString() : '';
return `<div class="task-card" style="border-left-color: #9b59b6;">
<div style="font-size:0.75em; color:var(--text-secondary); float:right;">${date}</div>
${formatter(item)}
</div>`;
}).join('');
} catch (err) {
console.error(`Failed to load ${endpoint}`, err);
}
}
function loadAllExtras() {
loadGenericList('/api/tech_debts', 'techdebt-container', item => `
<strong>${item.title}</strong>
<div>${item.description}</div>
<div style="margin-top:10px;">
<span style="background:var(--canvas-bg); padding:2px 6px; border-radius:4px; font-size:0.8em; margin-right:8px; border:1px solid var(--border-color);">Severity: ${item.severity || 'Medium'}</span>
<span style="background:var(--canvas-bg); padding:2px 6px; border-radius:4px; font-size:0.8em; border:1px solid var(--border-color);">Status: ${item.status || 'open'}</span>
</div>
`);
loadGenericList('/api/error_fixes', 'errorfixes-container', item => `
<strong>${item.error_message || 'Error'}</strong>
<div style="font-size:0.9em; margin-bottom:8px;"><em>Fix:</em> ${item.fix_description || ''}</div>
<div style="font-family:monospace; font-size:0.85em; color:#e74c3c;">Files: ${(item.files_changed || []).join(', ')}</div>
`);
loadGenericList('/api/adrs', 'adrs-container', item => `
<strong>${item.title}</strong>
<div style="font-size:0.9em; margin-bottom:8px;"><em>Context:</em> ${item.context || ''}</div>
<div style="font-size:0.9em; margin-bottom:8px;"><em>Decision:</em> ${item.decision || ''}</div>
<div style="font-size:0.9em;"><em>Consequences:</em> ${item.consequences || ''}</div>
`);
loadGenericList('/api/context_workspaces', 'workspaces-container', item => `
<strong>${item.name}</strong>
<div style="margin-bottom:8px;">${item.description || ''}</div>
<div style="font-family:monospace; font-size:0.85em; color:#3498db;">${(item.paths || []).join(', ')}</div>
`);
loadGenericList('/api/handoff_memos', 'memos-container', item => `
<strong>Memo from ${item.author || 'System'}</strong>
<div style="white-space:pre-wrap; font-size:0.9em; line-height:1.4;">${item.content || item.summary || ''}</div>
`);
loadGenericList('/api/snippets', 'snippets-container', item => `
<strong>${item.description || 'Snippet'}</strong>
<div style="margin-bottom:8px; font-size:0.8em;">Language: ${item.language || 'txt'} | Tags: ${(item.tags || []).join(', ')}</div>
<pre style="background:#111417; color:#d1d8e0; padding:10px; border-radius:4px; overflow-x:auto; font-size:0.85em;">${item.content || item.code || ''}</pre>
`);
loadGenericList('/api/pr_checklists', 'pr-container', item => `
<strong>${item.name || 'Checklist'}</strong>
<ul style="padding-left:20px; font-size:0.9em;">
${(item.items || []).map(i => {
const check = i.is_completed ? '☑' : '☐';
const strike = i.is_completed ? 'text-decoration:line-through; color:var(--text-secondary);' : '';
return `<li style="${strike}">${check} ${i.description}</li>`;
}).join('')}
</ul>
`);
}
async function loadVersion() { async function loadVersion() {
try { try {
const res = await fetch('/api/version'); const res = await fetch('/api/version');
@@ -1005,6 +1138,7 @@
loadTasks(); loadTasks();
loadStickyNotes(); loadStickyNotes();
loadActivityHistory(); loadActivityHistory();
loadAllExtras();
setupWS(); setupWS();
// Listen to theme toggle changes to update graph font colors // Listen to theme toggle changes to update graph font colors
@@ -1015,6 +1149,7 @@
loadGraph(); loadGraph();
loadTasks(); loadTasks();
loadStickyNotes(); loadStickyNotes();
loadAllExtras();
}, 5000); }, 5000);
</script> </script>
</body> </body>
+4 -4
View File
@@ -62,7 +62,7 @@ impl McpTool for ReadEnvFingerprintHandler {
.read_with(|fps| fps.get(&req.namespace).cloned()); .read_with(|fps| fps.get(&req.namespace).cloned());
if let Some(fp) = data { if let Some(fp) = data {
let data = serde_json::to_string(&fp).unwrap_or_default(); let data = serde_json::to_string(&fp).unwrap_or_default();
Ok(data.to_string()) Ok(data)
} else { } else {
Ok("{}".to_string()) Ok("{}".to_string())
} }
@@ -155,9 +155,9 @@ impl McpTool for GetEnvironmentDetailsHandler {
.iter() .iter()
.filter(|e| e.namespace == req.namespace) .filter(|e| e.namespace == req.namespace)
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
+5 -5
View File
@@ -380,8 +380,8 @@ impl McpTool for SearchNodesHandler {
result.entities.insert(id, e); result.entities.insert(id, e);
} }
} }
serde_json::to_string(&result).unwrap_or_default() serde_json::to_string(&result).map_err(|e| e.to_string())
}); })?;
Ok(data) Ok(data)
} }
} }
@@ -418,8 +418,8 @@ impl McpTool for OpenNodesHandler {
result.entities.insert(name, e); result.entities.insert(name, e);
} }
} }
serde_json::to_string(&result).unwrap_or_default() serde_json::to_string(&result).map_err(|e| e.to_string())
}); })?;
Ok(data) Ok(data)
} }
} }
@@ -620,7 +620,7 @@ impl McpTool for FindOrphansHandler {
.collect::<Vec<String>>() .collect::<Vec<String>>()
}); });
let data = serde_json::to_string(&orphans).unwrap_or_default(); let data = serde_json::to_string(&orphans).unwrap_or_default();
Ok(data.to_string()) Ok(data)
} }
} }
+100 -31
View File
@@ -70,9 +70,9 @@ impl McpTool for QueryDecisionsHandler {
} }
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -129,9 +129,9 @@ impl McpTool for SearchErrorFixesHandler {
|| contains_ignore_ascii_case(&f.solution, &q) || contains_ignore_ascii_case(&f.solution, &q)
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -180,8 +180,8 @@ impl McpTool for QueryRecentChangesHandler {
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = state let data = state
.ledger .ledger
.read_with(|l| serde_json::to_string(l).unwrap_or_else(|_| "[]".to_string())); .read_with(|l| serde_json::to_string(l).map_err(|e| e.to_string()))?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -229,7 +229,7 @@ impl McpTool for ReadPreferencesHandler {
let data = state let data = state
.prefs .prefs
.read_with(|prefs| serde_json::to_string(prefs).unwrap_or_default()); .read_with(|prefs| serde_json::to_string(prefs).unwrap_or_default());
Ok(data.to_string()) Ok(data)
} }
} }
@@ -319,9 +319,9 @@ impl McpTool for ListTechDebtHandler {
d.namespace == req.namespace && (req.include_resolved || !d.is_resolved) d.namespace == req.namespace && (req.include_resolved || !d.is_resolved)
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -339,24 +339,39 @@ impl McpTool for OmniSearchHandler {
async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let req: OmniSearchTool = serde_json::from_value(args).map_err(|e| e.to_string())?; let req: OmniSearchTool = serde_json::from_value(args).map_err(|e| e.to_string())?;
let matches = { let limit = req.limit.unwrap_or(5);
let idx = state.get_search_index(); let include_body = req.include_body.unwrap_or(false);
idx.search(&req.query, req.namespace.as_deref()) let matches = match state.get_search_index().search(&req.query, req.namespace.as_deref()) {
.unwrap_or_default() Ok(m) => m,
Err(e) => {
return Err(format!(
"Search query failed (possibly malformed Lucene syntax). Error: {}",
e
));
}
}; };
println!("OMNI SEARCH MATCHES: {:?}", matches); println!("OMNI SEARCH MATCHES: {:?}", matches);
let kg_json = state.read_graph(|full| { let kg_json = state.read_graph(|full| {
let mut kg_entities = std::collections::HashMap::new(); let mut kg_entities = std::collections::HashMap::new();
let mut count = 0;
for (id, doc_type, _, _, _) in &matches { for (id, doc_type, _, _, _) in &matches {
if doc_type == "entity" if doc_type == "entity"
&& let Some(e) = full.entities.get(id) && let Some(e) = full.entities.get(id)
{ {
kg_entities.insert(id, e); if count >= limit { continue; }
count += 1;
if !include_body {
let mut summary = e.clone();
summary.observations = vec![];
kg_entities.insert(id.clone(), summary);
} else {
kg_entities.insert(id.clone(), e.clone());
}
} }
} }
serde_json::to_value(&kg_entities).unwrap_or_default() serde_json::to_value(&kg_entities).map_err(|e| e.to_string())
}); })?;
let mut matched_tasks = std::collections::HashSet::new(); let mut matched_tasks = std::collections::HashSet::new();
let mut matched_snippets = std::collections::HashSet::new(); let mut matched_snippets = std::collections::HashSet::new();
@@ -380,25 +395,58 @@ impl McpTool for OmniSearchHandler {
let filtered: Vec<_> = all_tasks let filtered: Vec<_> = all_tasks
.iter() .iter()
.filter(|t| matched_tasks.contains(t.id.as_str())) .filter(|t| matched_tasks.contains(t.id.as_str()))
.take(limit)
.map(|t| {
if !include_body {
let mut summary = t.clone();
summary.description = "".to_string();
summary.acceptance_criteria = vec![];
summary
} else {
t.clone()
}
})
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() serde_json::to_value(&filtered).map_err(|e| e.to_string())
}); })?;
let snippets_json = state.snippets.read_with(|all_snippets| { let snippets_json = state.snippets.read_with(|all_snippets| {
let filtered: Vec<_> = all_snippets let filtered: Vec<_> = all_snippets
.iter() .iter()
.filter(|s| matched_snippets.contains(s.name.as_str())) .filter(|s| matched_snippets.contains(s.name.as_str()))
.take(limit)
.map(|s| {
if !include_body {
let mut summary = s.clone();
summary.code = "".to_string();
summary
} else {
s.clone()
}
})
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() serde_json::to_value(&filtered).map_err(|e| e.to_string())
}); })?;
let adrs_json = state.adrs.read_with(|all_adrs| { let adrs_json = state.adrs.read_with(|all_adrs| {
let filtered: Vec<_> = all_adrs let filtered: Vec<_> = all_adrs
.iter() .iter()
.filter(|a| matched_adrs.contains(a.id.as_str())) .filter(|a| matched_adrs.contains(a.id.as_str()))
.take(limit)
.map(|a| {
if !include_body {
let mut summary = a.clone();
summary.context = "".to_string();
summary.decision = "".to_string();
summary.consequence = "".to_string();
summary
} else {
a.clone()
}
})
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() serde_json::to_value(&filtered).map_err(|e| e.to_string())
}); })?;
let q = req.query; let q = req.query;
let tech_debts_json = state.tech_debts.read_with(|debts| { let tech_debts_json = state.tech_debts.read_with(|debts| {
@@ -409,9 +457,20 @@ impl McpTool for OmniSearchHandler {
&& (contains_ignore_ascii_case(&d.description, &q) && (contains_ignore_ascii_case(&d.description, &q)
|| contains_ignore_ascii_case(&d.ideal_solution, &q)) || contains_ignore_ascii_case(&d.ideal_solution, &q))
}) })
.take(limit)
.map(|d| {
if !include_body {
let mut summary = d.clone();
summary.description = "".to_string();
summary.ideal_solution = "".to_string();
summary
} else {
d.clone()
}
})
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() serde_json::to_value(&filtered).map_err(|e| e.to_string())
}); })?;
let memos_json = state.handoff_memos.read_with(|memos| { let memos_json = state.handoff_memos.read_with(|memos| {
let filtered: Vec<_> = memos let filtered: Vec<_> = memos
@@ -420,9 +479,19 @@ impl McpTool for OmniSearchHandler {
req.namespace.as_ref().is_none_or(|ns| m.namespace == *ns) req.namespace.as_ref().is_none_or(|ns| m.namespace == *ns)
&& contains_ignore_ascii_case(&m.content, &q) && contains_ignore_ascii_case(&m.content, &q)
}) })
.take(limit)
.map(|m| {
if !include_body {
let mut summary = m.clone();
summary.content = "".to_string();
summary
} else {
m.clone()
}
})
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() 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.error_fixes.read_with(|fixes| {
let filtered: Vec<_> = fixes let filtered: Vec<_> = fixes
@@ -432,8 +501,8 @@ impl McpTool for OmniSearchHandler {
|| contains_ignore_ascii_case(&f.solution, &q) || contains_ignore_ascii_case(&f.solution, &q)
}) })
.collect(); .collect();
serde_json::to_value(&filtered).unwrap_or_default() serde_json::to_value(&filtered).map_err(|e| e.to_string())
}); })?;
let report = serde_json::json!({ let report = serde_json::json!({
"knowledge_graph": kg_json, "knowledge_graph": kg_json,
+5 -5
View File
@@ -49,8 +49,8 @@ impl McpTool for ReadStickyNotesHandler {
async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> { async fn execute(&self, _args: Value, state: Arc<MemoryState>) -> Result<String, String> {
let data = state let data = state
.sticky .sticky
.read_with(|s| serde_json::to_string(s).unwrap_or_else(|_| "[]".to_string())); .read_with(|s| serde_json::to_string(s).map_err(|e| e.to_string()))?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -167,9 +167,9 @@ impl McpTool for ReadHandoffMemosHandler {
} }
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
+4 -4
View File
@@ -293,8 +293,8 @@ impl McpTool for ListActiveTasksHandler {
status_match && branch_match status_match && branch_match
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data) Ok(data)
} }
} }
@@ -474,8 +474,8 @@ impl McpTool for ListMilestonesHandler {
} }
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data) Ok(data)
} }
} }
+15 -15
View File
@@ -89,9 +89,9 @@ impl McpTool for ListPinnedFilesHandler {
ns_match && branch_match ns_match && branch_match
}) })
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -154,9 +154,9 @@ impl McpTool for SearchSnippetsHandler {
|| contains_ignore_ascii_case(&s.language, &query) || contains_ignore_ascii_case(&s.language, &query)
}) })
.collect(); .collect();
serde_json::to_string(&results).unwrap_or_default() serde_json::to_string(&results).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -245,9 +245,9 @@ impl McpTool for LoadContextWorkspaceHandler {
.iter() .iter()
.filter(|w| w.namespace == req.namespace && w.name == req.name) .filter(|w| w.namespace == req.namespace && w.name == req.name)
.collect(); .collect();
serde_json::to_string(&filtered.first()).unwrap_or_default() serde_json::to_string(&filtered.first()).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -271,9 +271,9 @@ impl McpTool for ListContextWorkspacesHandler {
serde_json::from_value(args).map_err(|e| e.to_string())?; serde_json::from_value(args).map_err(|e| e.to_string())?;
let data = state.context_workspaces.read_with(|ws| { let data = state.context_workspaces.read_with(|ws| {
let filtered: Vec<_> = ws.iter().filter(|w| req.namespace.as_ref().map_or(true, |ns| &w.namespace == ns)).collect(); let filtered: Vec<_> = ws.iter().filter(|w| req.namespace.as_ref().map_or(true, |ns| &w.namespace == ns)).collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
@@ -325,9 +325,9 @@ impl McpTool for GetPrChecklistHandler {
.iter() .iter()
.filter(|i| i.namespace == req.namespace) .filter(|i| i.namespace == req.namespace)
.collect(); .collect();
serde_json::to_string(&filtered).unwrap_or_default() serde_json::to_string(&filtered).map_err(|e| e.to_string())
}); })?;
Ok(data.to_string()) Ok(data)
} }
} }
+22 -1
View File
@@ -69,6 +69,10 @@ pub struct SearchNodesTool {
pub query: String, pub query: String,
/// Optional namespace to restrict the search to. /// Optional namespace to restrict the search to.
pub namespace: Option<String>, pub namespace: Option<String>,
/// Limit the number of results to avoid context limit overflow. Defaults to 10.
pub limit: Option<usize>,
/// Include the full observations of the matched items. If false, returns only IDs and types (recommended for LLMs to prevent context bloat). Defaults to false.
pub include_body: Option<bool>,
} }
/// Open and retrieve full details of specific nodes in the knowledge graph. /// Open and retrieve full details of specific nodes in the knowledge graph.
@@ -192,6 +196,10 @@ pub struct StoreSnippetTool {
pub struct SearchSnippetsTool { pub struct SearchSnippetsTool {
/// The search query. /// The search query.
pub query: String, pub query: String,
/// Limit the number of results to avoid context limit overflow. Defaults to 5.
pub limit: Option<usize>,
/// Include the full code content of the matched items. If false, returns only IDs and descriptions (recommended for LLMs to prevent context bloat). Defaults to false.
pub include_body: Option<bool>,
} }
/// Delete a stored code snippet. /// Delete a stored code snippet.
@@ -219,6 +227,10 @@ pub struct LogDecisionTool {
pub struct QueryDecisionsTool { pub struct QueryDecisionsTool {
/// Optional search query. /// Optional search query.
pub query: Option<String>, pub query: Option<String>,
/// Limit the number of results to avoid context limit overflow. Defaults to 5.
pub limit: Option<usize>,
/// Include the full body/content of the matched ADRs. If false, returns only titles and IDs (recommended for LLMs to prevent context bloat). Defaults to false.
pub include_body: Option<bool>,
} }
/// Merge two entities in the knowledge graph into one. /// Merge two entities in the knowledge graph into one.
@@ -265,6 +277,10 @@ pub struct LogErrorFixTool {
pub struct SearchErrorFixesTool { pub struct SearchErrorFixesTool {
/// The search query. /// The search query.
pub query: String, pub query: String,
/// Limit the number of results to avoid context limit overflow. Defaults to 5.
pub limit: Option<usize>,
/// Include the full solution details. If false, returns only error signatures (recommended for LLMs to prevent context bloat). Defaults to false.
pub include_body: Option<bool>,
} }
/// Pin a file to keep it explicitly in the context workspace. /// Pin a file to keep it explicitly in the context workspace.
@@ -523,12 +539,17 @@ pub struct ListContextWorkspacesTool {
} }
/// Search across all memory stores (Graph, Tasks, Snippets, ADRs, etc.). /// Search across all memory stores (Graph, Tasks, Snippets, ADRs, etc.).
/// Highly recommended for discovery. Supports Lucene query syntax.
#[derive(Debug, Deserialize, Serialize, JsonSchema)] #[derive(Debug, Deserialize, Serialize, JsonSchema)]
pub struct OmniSearchTool { pub struct OmniSearchTool {
/// The search query. /// The search query. Supports Lucene syntax (e.g., 'title:"auth" AND status:open').
pub query: String, pub query: String,
/// Optional namespace to restrict the search to. /// Optional namespace to restrict the search to.
pub namespace: Option<String>, pub namespace: Option<String>,
/// Limit the number of results per category to avoid context limit overflow. Defaults to 5.
pub limit: Option<usize>,
/// Include the full body/content of the matched items. If false, returns only IDs and titles (recommended for LLMs to prevent context bloat). Defaults to false.
pub include_body: Option<bool>,
} }
/// Get a health digest of the project. /// Get a health digest of the project.