Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fecd8bf33f | ||
|
|
b4431a4dcd | ||
|
|
d6acd885dd | ||
|
|
cb0c55775a | ||
|
|
410b0c42ca |
No files matched your search
Generated
+20
@@ -2654,6 +2654,7 @@ dependencies = [
|
|||||||
"schemars",
|
"schemars",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"serde_yaml",
|
||||||
"tantivy",
|
"tantivy",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.20",
|
"thiserror 2.0.20",
|
||||||
@@ -4342,6 +4343,19 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_yaml"
|
||||||
|
version = "0.9.34+deprecated"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"itoa",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
"unsafe-libyaml",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -5438,6 +5452,12 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unsafe-libyaml"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "untrusted"
|
name = "untrusted"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|||||||
+129
-5
@@ -1088,7 +1088,7 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
"properties": {
|
"properties": {
|
||||||
"action": {
|
"action": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["get_active", "read", "open", "create_scratch", "save", "reload", "close", "list", "search"],
|
"enum": ["get_active", "read", "open", "create_scratch", "save", "reload", "close", "list", "search", "edit"],
|
||||||
"description": "Action to perform on the buffer"
|
"description": "Action to perform on the buffer"
|
||||||
},
|
},
|
||||||
"file": { "type": "string", "description": "Target file path (for open, read, search)" },
|
"file": { "type": "string", "description": "Target file path (for open, read, search)" },
|
||||||
@@ -1099,7 +1099,22 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
"start_line": { "type": "integer", "description": "1-indexed start line (for read)" },
|
"start_line": { "type": "integer", "description": "1-indexed start line (for read)" },
|
||||||
"end_line": { "type": "integer", "description": "1-indexed end line (for read)" },
|
"end_line": { "type": "integer", "description": "1-indexed end line (for read)" },
|
||||||
"pattern": { "type": "string", "description": "Regex or substring pattern to search for (for search)" },
|
"pattern": { "type": "string", "description": "Regex or substring pattern to search for (for search)" },
|
||||||
"force": { "type": "boolean", "description": "Force reload or close (for reload, close)" }
|
"force": { "type": "boolean", "description": "Force reload or close (for reload, close)" },
|
||||||
|
"edits": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "Array of edits to apply sequentially (for edit). Grouped by file, applied in descending order.",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": { "type": "string", "description": "Target file path" },
|
||||||
|
"start_line": { "type": "integer", "description": "1-indexed start line" },
|
||||||
|
"end_line": { "type": "integer", "description": "1-indexed end line" },
|
||||||
|
"replacement_content": { "type": "string", "description": "New content" },
|
||||||
|
"expected_content": { "type": "string", "description": "Optional: exact content expected in the replacement range to prevent line drift corruption" }
|
||||||
|
},
|
||||||
|
"required": ["file", "start_line", "end_line", "replacement_content"]
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"required": ["action"]
|
"required": ["action"]
|
||||||
}
|
}
|
||||||
@@ -1324,6 +1339,109 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
execute_nvim_lua_with_args(code, vec![args_rmp])
|
execute_nvim_lua_with_args(code, vec![args_rmp])
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
"edit" => {
|
||||||
|
let mut modified_args = args.clone();
|
||||||
|
if let Some(edits) = modified_args.get_mut("edits").and_then(|v| v.as_array_mut()) {
|
||||||
|
for edit in edits.iter_mut() {
|
||||||
|
if let Some(content) = edit.get_mut("replacement_content") {
|
||||||
|
if let Some(s) = content.as_str() {
|
||||||
|
let lines: Vec<String> = s.split('\n').map(|l| l.trim_end_matches('\r').to_string()).collect();
|
||||||
|
*content = serde_json::json!(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if let Some(content) = modified_args.get_mut("replacement_content") {
|
||||||
|
if let Some(s) = content.as_str() {
|
||||||
|
let lines: Vec<String> = s.split('\n').map(|l| l.trim_end_matches('\r').to_string()).collect();
|
||||||
|
*content = serde_json::json!(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let args_rmp = json_to_rmpv(&modified_args);
|
||||||
|
let code = "
|
||||||
|
local args = ...
|
||||||
|
local edits = args.edits
|
||||||
|
if not edits or type(edits) ~= 'table' then
|
||||||
|
if args.file and args.start_line and args.end_line and args.replacement_content then
|
||||||
|
edits = {
|
||||||
|
{
|
||||||
|
file = args.file,
|
||||||
|
start_line = tonumber(args.start_line),
|
||||||
|
end_line = tonumber(args.end_line),
|
||||||
|
replacement_content = args.replacement_content,
|
||||||
|
expected_content = args.expected_content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
error(\"Missing 'edits' array or valid single edit parameters\")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local grouped_edits = {}
|
||||||
|
for i, edit in ipairs(edits) do
|
||||||
|
local file = edit.file
|
||||||
|
if not file or file == '' or file == vim.NIL then
|
||||||
|
error(\"Edit missing 'file'\")
|
||||||
|
end
|
||||||
|
if not grouped_edits[file] then
|
||||||
|
grouped_edits[file] = {}
|
||||||
|
end
|
||||||
|
table.insert(grouped_edits[file], {
|
||||||
|
start_line = tonumber(edit.start_line),
|
||||||
|
end_line = tonumber(edit.end_line),
|
||||||
|
replacement_content = edit.replacement_content,
|
||||||
|
expected_content = edit.expected_content,
|
||||||
|
original_index = i
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
local results = {}
|
||||||
|
for file, file_edits in pairs(grouped_edits) do
|
||||||
|
local bufnr = vim.fn.bufnr(file, true)
|
||||||
|
if vim.fn.bufloaded(bufnr) == 0 then
|
||||||
|
vim.fn.bufload(bufnr)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.sort(file_edits, function(a, b)
|
||||||
|
return a.start_line > b.start_line
|
||||||
|
end)
|
||||||
|
|
||||||
|
for _, edit in ipairs(file_edits) do
|
||||||
|
local sl = edit.start_line
|
||||||
|
local el = edit.end_line
|
||||||
|
local rc = edit.replacement_content
|
||||||
|
local ec = edit.expected_content
|
||||||
|
|
||||||
|
if not sl then error(\"Edit missing 'start_line'\") end
|
||||||
|
if not el then error(\"Edit missing 'end_line'\") end
|
||||||
|
if not rc or rc == vim.NIL then error(\"Edit missing 'replacement_content'\") end
|
||||||
|
|
||||||
|
if ec and type(ec) == 'string' and ec ~= vim.NIL and ec ~= '' then
|
||||||
|
local current_lines = vim.api.nvim_buf_get_lines(bufnr, sl - 1, el, false)
|
||||||
|
local current_text = table.concat(current_lines, '\\n')
|
||||||
|
local ec_clean = string.gsub(ec, '\\r', '')
|
||||||
|
if current_text ~= ec_clean then
|
||||||
|
error(\"Safety check failed in \" .. file .. \": Expected content at lines \" .. tostring(sl) .. \"-\" .. tostring(el) .. \" does not match actual content.\\nExpected:\\n\" .. ec_clean .. \"\\nActual:\\n\" .. current_text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
vim.api.nvim_buf_set_lines(bufnr, sl - 1, el, false, rc)
|
||||||
|
end
|
||||||
|
|
||||||
|
local win = vim.fn.bufwinid(bufnr)
|
||||||
|
if win ~= -1 and #file_edits > 0 then
|
||||||
|
pcall(vim.api.nvim_win_set_cursor, win, {file_edits[#file_edits].start_line, 0})
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(results, \"Edited \" .. file .. \" (\" .. tostring(#file_edits) .. \" blocks)\")
|
||||||
|
end
|
||||||
|
|
||||||
|
return table.concat(results, ', ')
|
||||||
|
";
|
||||||
|
handle_lua_result!(
|
||||||
|
id,
|
||||||
|
execute_nvim_lua_with_args(code, vec![args_rmp])
|
||||||
|
)
|
||||||
|
}
|
||||||
"open" => {
|
"open" => {
|
||||||
let args_rmp = json_to_rmpv(args);
|
let args_rmp = json_to_rmpv(args);
|
||||||
let code = "
|
let code = "
|
||||||
@@ -1356,7 +1474,14 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
"create_scratch" | "open_buffer" => {
|
"create_scratch" | "open_buffer" => {
|
||||||
let args_rmp = json_to_rmpv(args);
|
let mut modified_args = args.clone();
|
||||||
|
if let Some(content) = modified_args.get_mut("content") {
|
||||||
|
if let Some(s) = content.as_str() {
|
||||||
|
let lines: Vec<String> = s.split('\n').map(|l| l.trim_end_matches('\r').to_string()).collect();
|
||||||
|
*content = serde_json::json!(lines);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let args_rmp = json_to_rmpv(&modified_args);
|
||||||
let code = "
|
let code = "
|
||||||
local args = ...
|
local args = ...
|
||||||
local name = (args.name and args.name ~= '') and args.name or '[Antigravity Scratch]'
|
local name = (args.name and args.name ~= '') and args.name or '[Antigravity Scratch]'
|
||||||
@@ -1366,8 +1491,7 @@ pub async fn run_mcp_loop(app_name: &str, app_version: &str) {
|
|||||||
vim.bo[buf].buftype = 'nofile'
|
vim.bo[buf].buftype = 'nofile'
|
||||||
vim.bo[buf].bufhidden = 'hide'
|
vim.bo[buf].bufhidden = 'hide'
|
||||||
if args.content then
|
if args.content then
|
||||||
local lines = vim.split(args.content, '\\n')
|
vim.api.nvim_buf_set_lines(buf, 0, -1, false, args.content)
|
||||||
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
|
|
||||||
end
|
end
|
||||||
if args.filetype and args.filetype ~= '' then
|
if args.filetype and args.filetype ~= '' then
|
||||||
vim.bo[buf].filetype = args.filetype
|
vim.bo[buf].filetype = args.filetype
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ tokio-stream = { version = "0.1", features = ["sync"] }
|
|||||||
chrono = { version = "0.4.45", features = ["serde"] }
|
chrono = { version = "0.4.45", features = ["serde"] }
|
||||||
ocrs = "0.13.1"
|
ocrs = "0.13.1"
|
||||||
rten = "0.26.0"
|
rten = "0.26.0"
|
||||||
|
serde_yaml = "0.9.34"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
chrono = "0.4.45"
|
chrono = "0.4.45"
|
||||||
|
|||||||
@@ -40,6 +40,9 @@ pub enum AppError {
|
|||||||
|
|
||||||
#[error("I/O Error: {0}")]
|
#[error("I/O Error: {0}")]
|
||||||
IoError(#[from] std::io::Error),
|
IoError(#[from] std::io::Error),
|
||||||
|
|
||||||
|
#[error("YAML Serialization Error: {0}")]
|
||||||
|
YamlError(#[from] serde_yaml::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<AppError> for String {
|
impl From<AppError> for String {
|
||||||
@@ -81,6 +84,10 @@ impl IntoResponse for AppError {
|
|||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
format!("I/O Error: {}", e),
|
format!("I/O Error: {}", e),
|
||||||
),
|
),
|
||||||
|
AppError::YamlError(e) => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
format!("YAML Serialization Error: {}", e),
|
||||||
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = Json(json!({
|
let body = Json(json!({
|
||||||
|
|||||||
@@ -504,7 +504,7 @@ impl McpTool for FindSymbolReferencesHandler {
|
|||||||
matches.extend(disk_matches);
|
matches.extend(disk_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&matches)?)
|
Ok(serde_yaml::to_string(&matches)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -560,7 +560,7 @@ impl McpTool for GetCallersHandler {
|
|||||||
callers.extend(disk_callers);
|
callers.extend(disk_callers);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&callers)?)
|
Ok(serde_yaml::to_string(&callers)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,7 +649,7 @@ impl McpTool for AnalyzeImpactHandler {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&result)?)
|
Ok(serde_yaml::to_string(&result)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ impl McpTool for EnvironmentHandler {
|
|||||||
.env_fingerprints
|
.env_fingerprints
|
||||||
.read_with(|fps| fps.get(&ns).cloned());
|
.read_with(|fps| fps.get(&ns).cloned());
|
||||||
if let Some(fp) = data {
|
if let Some(fp) = data {
|
||||||
let data = Ok::<String, crate::error::AppError>(serde_json::to_string(&fp)?)?;
|
let data = Ok::<String, crate::error::AppError>(serde_yaml::to_string(&fp)?)?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
} else {
|
} else {
|
||||||
Ok("{}".to_string())
|
Ok("{}".to_string())
|
||||||
@@ -118,7 +118,7 @@ impl McpTool for EnvironmentHandler {
|
|||||||
EnvAction::GetDetails => {
|
EnvAction::GetDetails => {
|
||||||
let data = state.env.environments.read_with(|envs| {
|
let data = state.env.environments.read_with(|envs| {
|
||||||
let filtered: Vec<_> = envs.iter().filter(|e| e.namespace == ns).collect();
|
let filtered: Vec<_> = envs.iter().filter(|e| e.namespace == ns).collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ impl McpTool for GetActiveWorktreeContextHandler {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))??;
|
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))??;
|
||||||
|
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string_pretty(&result)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&result)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +168,7 @@ impl McpTool for QueryGitDiffsHandler {
|
|||||||
matches.extend(git_matches);
|
matches.extend(git_matches);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&matches)?)
|
Ok(serde_yaml::to_string(&matches)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -191,7 +191,7 @@ mod tests {
|
|||||||
.map_err(|e| format!("Failed to get worktree context: {}", e))
|
.map_err(|e| format!("Failed to get worktree context: {}", e))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
let parsed: serde_json::Value = serde_yaml::from_str(&result).unwrap();
|
||||||
assert!(parsed.get("branch").is_some());
|
assert!(parsed.get("branch").is_some());
|
||||||
assert!(parsed.get("modified_files").is_some());
|
assert!(parsed.get("modified_files").is_some());
|
||||||
assert!(parsed.get("diff").is_some());
|
assert!(parsed.get("diff").is_some());
|
||||||
@@ -210,7 +210,7 @@ mod tests {
|
|||||||
.map_err(|e| format!("Failed to get worktree context: {}", e))
|
.map_err(|e| format!("Failed to get worktree context: {}", e))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
let parsed: serde_json::Value = serde_yaml::from_str(&result).unwrap();
|
||||||
assert!(parsed.get("branch").is_some() || parsed.is_object());
|
assert!(parsed.get("branch").is_some() || parsed.is_object());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -542,7 +542,7 @@ impl McpTool for ReadGraphHandler {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result_json = serde_json::to_string(&serialized)?;
|
let mut result_json = serde_yaml::to_string(&serialized)?;
|
||||||
|
|
||||||
if let Some(max_tok) = max_tokens {
|
if let Some(max_tok) = max_tokens {
|
||||||
let max_chars = max_tok * 4;
|
let max_chars = max_tok * 4;
|
||||||
@@ -579,7 +579,7 @@ impl McpTool for ReadGraphHandler {
|
|||||||
obj.insert("has_more".to_string(), serde_json::Value::Bool(true));
|
obj.insert("has_more".to_string(), serde_json::Value::Bool(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(trunc_str) = serde_json::to_string(&truncated_val)
|
if let Ok(trunc_str) = serde_yaml::to_string(&truncated_val)
|
||||||
&& (trunc_str.len() <= max_chars || keep_count == 1) {
|
&& (trunc_str.len() <= max_chars || keep_count == 1) {
|
||||||
result_json = trunc_str;
|
result_json = trunc_str;
|
||||||
break;
|
break;
|
||||||
@@ -638,7 +638,7 @@ impl McpTool for SearchNodesHandler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&matched_entities)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&matched_entities)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@@ -679,7 +679,7 @@ impl McpTool for OpenNodesHandler {
|
|||||||
result.entities.insert(name, e);
|
result.entities.insert(name, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&result)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&result)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@@ -824,7 +824,7 @@ impl McpTool for CondenseEntityHandler {
|
|||||||
req.entity_name,
|
req.entity_name,
|
||||||
unique_obs.join("\n- ")
|
unique_obs.join("\n- ")
|
||||||
);
|
);
|
||||||
if let Ok(summary) = state.ollama.generate(&prompt, None, None).await {
|
if let Ok(summary) = state.ollama.generate(&prompt, None, None, None).await {
|
||||||
let lines: Vec<String> = summary
|
let lines: Vec<String> = summary
|
||||||
.lines()
|
.lines()
|
||||||
.map(|l| l.trim().trim_start_matches('-').trim().to_string())
|
.map(|l| l.trim().trim_start_matches('-').trim().to_string())
|
||||||
@@ -954,7 +954,7 @@ impl McpTool for FindOrphansHandler {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
});
|
});
|
||||||
let data = Ok::<String, crate::error::AppError>(serde_json::to_string(&orphans)?)?;
|
let data = Ok::<String, crate::error::AppError>(serde_yaml::to_string(&orphans)?)?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1047,7 +1047,7 @@ impl McpTool for GetSubgraphHandler {
|
|||||||
"relations": matched_relations,
|
"relations": matched_relations,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&result)?)
|
Ok(serde_yaml::to_string(&result)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1146,7 +1146,7 @@ impl McpTool for SweepGraphHealthHandler {
|
|||||||
"health_score": if orphans.is_empty() && duplicates.is_empty() && stale_entities.is_empty() { "100%" } else { "Needs Maintenance" }
|
"health_score": if orphans.is_empty() && duplicates.is_empty() && stale_entities.is_empty() { "100%" } else { "Needs Maintenance" }
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&report)?)
|
Ok(serde_yaml::to_string(&report)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1203,7 +1203,7 @@ impl McpTool for ResolveStaleSymbolsHandler {
|
|||||||
"status": "success"
|
"status": "success"
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&report)?)
|
Ok(serde_yaml::to_string(&report)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1643,7 +1643,7 @@ mod tests {
|
|||||||
.execute(json!({"limit": 1, "offset": 0}), state.clone())
|
.execute(json!({"limit": 1, "offset": 0}), state.clone())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let parsed_paged: serde_json::Value = serde_json::from_str(&paged_res).unwrap();
|
let parsed_paged: serde_json::Value = serde_yaml::from_str(&paged_res).unwrap();
|
||||||
assert_eq!(parsed_paged["total_count"], 2);
|
assert_eq!(parsed_paged["total_count"], 2);
|
||||||
assert_eq!(parsed_paged["has_more"], true);
|
assert_eq!(parsed_paged["has_more"], true);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-16
@@ -33,7 +33,7 @@ impl McpTool for LogErrorFixHandler {
|
|||||||
);
|
);
|
||||||
if let Ok(summary) = state
|
if let Ok(summary) = state
|
||||||
.ollama
|
.ollama
|
||||||
.generate(&prompt, Some(&state.ollama.reasoning_model), None)
|
.generate(&prompt, Some(&state.ollama.reasoning_model), None, None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let clean = summary.trim();
|
let clean = summary.trim();
|
||||||
@@ -142,7 +142,7 @@ impl McpTool for SearchErrorFixesHandler {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string_pretty(&suggestions)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&suggestions)?)
|
||||||
})?;
|
})?;
|
||||||
return Ok(data);
|
return Ok(data);
|
||||||
}
|
}
|
||||||
@@ -169,7 +169,7 @@ impl McpTool for SearchErrorFixesHandler {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string_pretty(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@@ -207,7 +207,7 @@ impl McpTool for LogCodeChangeHandler {
|
|||||||
"Summarize in 1 concise sentence the architectural impact of changing file '{}': {}",
|
"Summarize in 1 concise sentence the architectural impact of changing file '{}': {}",
|
||||||
req.file_path, description
|
req.file_path, description
|
||||||
);
|
);
|
||||||
if let Ok(summary) = state.ollama.generate(&prompt, None, None).await {
|
if let Ok(summary) = state.ollama.generate(&prompt, None, None, None).await {
|
||||||
let clean = summary.trim();
|
let clean = summary.trim();
|
||||||
if !clean.is_empty() {
|
if !clean.is_empty() {
|
||||||
description = format!("{} (AI Summary: {})", description, clean);
|
description = format!("{} (AI Summary: {})", description, clean);
|
||||||
@@ -358,7 +358,7 @@ impl McpTool for QueryRecentChangesHandler {
|
|||||||
.take(limit)
|
.take(limit)
|
||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@@ -575,7 +575,7 @@ impl McpTool for DecisionsHandler {
|
|||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
if include_body {
|
if include_body {
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
} else {
|
} else {
|
||||||
let compact: Vec<_> = filtered
|
let compact: Vec<_> = filtered
|
||||||
.iter()
|
.iter()
|
||||||
@@ -592,7 +592,7 @@ impl McpTool for DecisionsHandler {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&compact)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&compact)?)
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
@@ -738,7 +738,7 @@ impl McpTool for TechDebtHandler {
|
|||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut json_str = serde_json::to_string(&filtered)?;
|
let mut json_str = serde_yaml::to_string(&filtered)?;
|
||||||
if let Some(max_t) = req.max_tokens {
|
if let Some(max_t) = req.max_tokens {
|
||||||
let char_limit = max_t * 4;
|
let char_limit = max_t * 4;
|
||||||
if json_str.len() > char_limit {
|
if json_str.len() > char_limit {
|
||||||
@@ -1305,7 +1305,7 @@ impl McpTool for ManageCheckpointHandler {
|
|||||||
for s in snaps {
|
for s in snaps {
|
||||||
list.push(serde_json::json!({"type": "snapshot", "id": s.id, "description": s.description, "namespace": s.namespace}));
|
list.push(serde_json::json!({"type": "snapshot", "id": s.id, "description": s.description, "namespace": s.namespace}));
|
||||||
}
|
}
|
||||||
Ok(serde_json::to_string_pretty(&list)?)
|
Ok(serde_yaml::to_string(&list)?)
|
||||||
}
|
}
|
||||||
CheckpointAction::Delete => {
|
CheckpointAction::Delete => {
|
||||||
let name = req.name_or_id.ok_or_else(|| {
|
let name = req.name_or_id.ok_or_else(|| {
|
||||||
@@ -1427,7 +1427,7 @@ impl McpTool for QueryLineageHandler {
|
|||||||
"timeline": timeline
|
"timeline": timeline
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&res)?)
|
Ok(serde_yaml::to_string(&res)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1486,7 +1486,7 @@ impl McpTool for GetNextActionableTasksHandler {
|
|||||||
"tasks": actionable
|
"tasks": actionable
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&res)?)
|
Ok(serde_yaml::to_string(&res)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1555,7 +1555,7 @@ impl McpTool for HypothesesHandler {
|
|||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&filtered)?)
|
Ok(serde_yaml::to_string(&filtered)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1634,7 +1634,7 @@ impl McpTool for GetPreflightContextHandler {
|
|||||||
"recent_activities": recent_activities
|
"recent_activities": recent_activities
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&preflight)?)
|
Ok(serde_yaml::to_string(&preflight)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1742,7 +1742,7 @@ impl McpTool for AgentSignalsHandler {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(serde_json::to_string_pretty(&filtered)?)
|
Ok(serde_yaml::to_string(&filtered)?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1985,8 +1985,8 @@ mod tests {
|
|||||||
.await
|
.await
|
||||||
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
|
.map_err(|e| crate::error::AppError::Internal(e.to_string()))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let uuid_start = debt_list.find("id\":\"").unwrap() + 5;
|
let debt_list_val: serde_json::Value = serde_yaml::from_str(&debt_list).unwrap();
|
||||||
let uuid = &debt_list[uuid_start..uuid_start + 36];
|
let uuid = debt_list_val[0]["id"].as_str().unwrap();
|
||||||
|
|
||||||
let resolve_debt = TechDebtHandler;
|
let resolve_debt = TechDebtHandler;
|
||||||
resolve_debt
|
resolve_debt
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ impl McpTool for HandoffMemosHandler {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,12 +297,12 @@ impl McpTool for TasksHandler {
|
|||||||
_ => serde_json::json!({ "id": t.id, "title": t.title, "status": t.status, "description": t.description, "git_branch": t.git_branch }),
|
_ => serde_json::json!({ "id": t.id, "title": t.title, "status": t.status, "description": t.description, "git_branch": t.git_branch }),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
let mut json_str = serde_json::to_string(&filtered)?;
|
let mut json_str = serde_yaml::to_string(&filtered)?;
|
||||||
if let Some(max_t) = req.max_tokens {
|
if let Some(max_t) = req.max_tokens {
|
||||||
let char_limit = max_t * 4;
|
let char_limit = max_t * 4;
|
||||||
while json_str.len() > char_limit && !filtered.is_empty() {
|
while json_str.len() > char_limit && !filtered.is_empty() {
|
||||||
filtered.pop();
|
filtered.pop();
|
||||||
json_str = serde_json::to_string(&filtered)?;
|
json_str = serde_yaml::to_string(&filtered)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok::<String, crate::error::AppError>(json_str)
|
Ok::<String, crate::error::AppError>(json_str)
|
||||||
@@ -514,7 +514,7 @@ impl McpTool for MilestonesHandler {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string(&filtered)?)
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&filtered)?)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -758,7 +758,7 @@ impl McpTool for ClipboardHandler {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if let Some(img) = item {
|
if let Some(img) = item {
|
||||||
Ok(serde_json::to_string_pretty(&img)?)
|
Ok(serde_yaml::to_string(&img)?)
|
||||||
} else {
|
} else {
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"status": "no_image_in_clipboard",
|
"status": "no_image_in_clipboard",
|
||||||
@@ -786,7 +786,7 @@ impl McpTool for ClipboardHandler {
|
|||||||
|
|
||||||
state.record_activity("clipboard", "Retrieved text from clipboard cache", None);
|
state.record_activity("clipboard", "Retrieved text from clipboard cache", None);
|
||||||
if let Some(txt) = item {
|
if let Some(txt) = item {
|
||||||
Ok(serde_json::to_string_pretty(&txt)?)
|
Ok(serde_yaml::to_string(&txt)?)
|
||||||
} else {
|
} else {
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"text": "",
|
"text": "",
|
||||||
@@ -818,7 +818,7 @@ impl McpTool for ClipboardHandler {
|
|||||||
};
|
};
|
||||||
|
|
||||||
state.record_activity("clipboard", "Viewed clipboard history ring buffer", None);
|
state.record_activity("clipboard", "Viewed clipboard history ring buffer", None);
|
||||||
Ok(serde_json::to_string_pretty(&history)?)
|
Ok(serde_yaml::to_string(&history)?)
|
||||||
}
|
}
|
||||||
ClipboardAction::Clear => {
|
ClipboardAction::Clear => {
|
||||||
tokio::task::spawn_blocking(|| {
|
tokio::task::spawn_blocking(|| {
|
||||||
@@ -905,7 +905,7 @@ impl McpTool for ClipboardHandler {
|
|||||||
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))??;
|
.map_err(|e| crate::error::AppError::Internal(format!("Task panic: {}", e)))??;
|
||||||
|
|
||||||
state.record_activity("clipboard", "Read contents from OS clipboard", None);
|
state.record_activity("clipboard", "Read contents from OS clipboard", None);
|
||||||
Ok::<String, crate::error::AppError>(serde_json::to_string_pretty(&Value::Object(
|
Ok::<String, crate::error::AppError>(serde_yaml::to_string(&Value::Object(
|
||||||
out,
|
out,
|
||||||
))?)
|
))?)
|
||||||
}
|
}
|
||||||
@@ -1039,7 +1039,7 @@ mod tests {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
// Returns a JSON string, possibly {}
|
// Returns a JSON string, possibly {}
|
||||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
let parsed: serde_json::Value = serde_yaml::from_str(&result).unwrap();
|
||||||
assert!(parsed.is_object());
|
assert!(parsed.is_object());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1055,7 +1055,7 @@ mod tests {
|
|||||||
.map_err(|e| format!("Failed to read clipboard: {}", e))
|
.map_err(|e| format!("Failed to read clipboard: {}", e))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let parsed: serde_json::Value = serde_json::from_str(&result).unwrap();
|
let parsed: serde_json::Value = serde_yaml::from_str(&result).unwrap();
|
||||||
assert!(parsed.is_object());
|
assert!(parsed.is_object());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1175,7 +1175,7 @@ mod tests {
|
|||||||
.execute(json!({ "action": "image" }), Arc::clone(&state))
|
.execute(json!({ "action": "image" }), Arc::clone(&state))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let img_val: Value = serde_json::from_str(&img_res_str).unwrap();
|
let img_val: Value = serde_yaml::from_str(&img_res_str).unwrap();
|
||||||
assert_eq!(img_val["file_path"], "C:\\tmp\\image_b.jpg");
|
assert_eq!(img_val["file_path"], "C:\\tmp\\image_b.jpg");
|
||||||
assert_eq!(img_val["file_path_wsl"], "/mnt/c/tmp/image_b.jpg");
|
assert_eq!(img_val["file_path_wsl"], "/mnt/c/tmp/image_b.jpg");
|
||||||
assert_eq!(img_val["width"], 1200);
|
assert_eq!(img_val["width"], 1200);
|
||||||
@@ -1188,7 +1188,7 @@ mod tests {
|
|||||||
.execute(json!({ "action": "text" }), Arc::clone(&state))
|
.execute(json!({ "action": "text" }), Arc::clone(&state))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let text_val: Value = serde_json::from_str(&text_res_str).unwrap();
|
let text_val: Value = serde_yaml::from_str(&text_res_str).unwrap();
|
||||||
assert_eq!(text_val["text"], "Some random stackoverflow URL");
|
assert_eq!(text_val["text"], "Some random stackoverflow URL");
|
||||||
|
|
||||||
// Action 'history' MUST show both items
|
// Action 'history' MUST show both items
|
||||||
@@ -1196,7 +1196,7 @@ mod tests {
|
|||||||
.execute(json!({ "action": "history" }), Arc::clone(&state))
|
.execute(json!({ "action": "history" }), Arc::clone(&state))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let hist_val: Value = serde_json::from_str(&hist_res_str).unwrap();
|
let hist_val: Value = serde_yaml::from_str(&hist_res_str).unwrap();
|
||||||
let history = hist_val.as_array().unwrap();
|
let history = hist_val.as_array().unwrap();
|
||||||
assert!(history.len() >= 4);
|
assert!(history.len() >= 4);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ impl McpTool for SnippetsHandler {
|
|||||||
.total_cmp(&a["score"].as_f64().unwrap_or(0.0))
|
.total_cmp(&a["score"].as_f64().unwrap_or(0.0))
|
||||||
});
|
});
|
||||||
scored.truncate(10);
|
scored.truncate(10);
|
||||||
Ok(serde_json::to_string_pretty(&scored)?)
|
Ok(serde_yaml::to_string(&scored)?)
|
||||||
} else {
|
} else {
|
||||||
let data = state.code.snippets.read_with(|snippets| {
|
let data = state.code.snippets.read_with(|snippets| {
|
||||||
let results: Vec<_> = snippets
|
let results: Vec<_> = snippets
|
||||||
@@ -153,7 +153,7 @@ impl McpTool for SnippetsHandler {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
serde_json::to_string_pretty(&results)
|
serde_yaml::to_string(&results)
|
||||||
})?;
|
})?;
|
||||||
Ok(data)
|
Ok(data)
|
||||||
}
|
}
|
||||||
@@ -359,7 +359,7 @@ impl McpTool for SemanticCodeSearchHandler {
|
|||||||
);
|
);
|
||||||
if let Ok(summary) = state
|
if let Ok(summary) = state
|
||||||
.ollama
|
.ollama
|
||||||
.generate(&prompt, None, Some("Respond clearly and concisely."))
|
.generate(&prompt, None, Some("Respond clearly and concisely."), None)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
out.push_str("\n\n--- Local GraphRAG Summary ---\n");
|
out.push_str("\n\n--- Local GraphRAG Summary ---\n");
|
||||||
|
|||||||
@@ -308,6 +308,7 @@ pub async fn memory_consolidation_worker(state: Arc<MemoryState>) {
|
|||||||
&prompt,
|
&prompt,
|
||||||
None,
|
None,
|
||||||
Some("You are a helpful JSON-only data deduplication assistant. Output only JSON."),
|
Some("You are a helpful JSON-only data deduplication assistant. Output only JSON."),
|
||||||
|
Some("json"),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ struct GenerateRequest<'a> {
|
|||||||
options: Option<serde_json::Value>,
|
options: Option<serde_json::Value>,
|
||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
keep_alive: Option<&'a str>,
|
keep_alive: Option<&'a str>,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
format: Option<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
@@ -113,6 +115,7 @@ impl OllamaClient {
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
model_override: Option<&str>,
|
model_override: Option<&str>,
|
||||||
system: Option<&str>,
|
system: Option<&str>,
|
||||||
|
format: Option<&str>,
|
||||||
) -> Result<String, AppError> {
|
) -> Result<String, AppError> {
|
||||||
let model = model_override.unwrap_or(&self.coder_model);
|
let model = model_override.unwrap_or(&self.coder_model);
|
||||||
let url = format!("{}/api/generate", self.base_url.trim_end_matches('/'));
|
let url = format!("{}/api/generate", self.base_url.trim_end_matches('/'));
|
||||||
@@ -128,6 +131,7 @@ impl OllamaClient {
|
|||||||
"num_predict": 4096 // Give reasoning models plenty of output room
|
"num_predict": 4096 // Give reasoning models plenty of output room
|
||||||
})),
|
})),
|
||||||
keep_alive: Some("1h"),
|
keep_alive: Some("1h"),
|
||||||
|
format,
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
@@ -172,6 +176,7 @@ impl OllamaClient {
|
|||||||
"num_predict": 1024
|
"num_predict": 1024
|
||||||
})),
|
})),
|
||||||
keep_alive: Some("1h"),
|
keep_alive: Some("1h"),
|
||||||
|
format: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
let res = self
|
let res = self
|
||||||
|
|||||||
Reference in new issue
Block a user