mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-30 15:03:57 +00:00
* fix(agent): inject skill tools into agent registry and unify ToolResult (#341) Skill tools (Notion, Gmail, etc.) from the QuickJS runtime were invisible to the agent's tool loop — the LLM could never call them. This change: 1. Adds SkillToolBridge to wrap skill ToolDefinitions as Tool trait impls and inject them into the agent's tool registry via collect_skill_tools(). 2. Unifies ToolResult across built-in and skill tools — both now use the MCP-spec type (content blocks + is_error) from skills::types, eliminating the need for result conversion between the two systems. 3. Adds convenience constructors (ToolResult::success/error/json) and accessor methods (output/text) to simplify all tool implementations. 4. Adds diagnostic logging in the tool loop for tool registry contents and tool lookup results. Verified: agent_chat RPC successfully called Notion list-all-pages tool and returned real workspace data through the agent loop. Closes #341 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix(tools): address CodeRabbit review feedback on PR #360 - cron_run: preserve execution details (result_output) on failure - delegate: fix mismatched bracket in agent response header format string - run_linter: include lint diagnostics in error output, not just exit code - run_tests: include test output in error result, not just exit code - skill_bridge: prevent namespaced tool-name collisions with dedup + __ delimiter validation; sanitize runtime errors in model-facing output Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * style: apply cargo fmt to skill_bridge.rs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
105 lines
3.1 KiB
Rust
105 lines
3.1 KiB
Rust
//! Tool: read_diff — structured git diff output for the Critic archetype.
|
|
|
|
use super::traits::{PermissionLevel, Tool, ToolResult};
|
|
use async_trait::async_trait;
|
|
use serde_json::json;
|
|
use std::path::PathBuf;
|
|
|
|
/// Returns `git diff` output in a structured format.
|
|
pub struct ReadDiffTool {
|
|
workspace_dir: PathBuf,
|
|
}
|
|
|
|
impl ReadDiffTool {
|
|
pub fn new(workspace_dir: PathBuf) -> Self {
|
|
Self { workspace_dir }
|
|
}
|
|
}
|
|
|
|
#[async_trait]
|
|
impl Tool for ReadDiffTool {
|
|
fn name(&self) -> &str {
|
|
"read_diff"
|
|
}
|
|
|
|
fn description(&self) -> &str {
|
|
"Get the git diff of current changes. Can diff staged, unstaged, or against a \
|
|
specific base branch/commit. Returns file paths and hunks."
|
|
}
|
|
|
|
fn parameters_schema(&self) -> serde_json::Value {
|
|
json!({
|
|
"type": "object",
|
|
"properties": {
|
|
"base": {
|
|
"type": "string",
|
|
"description": "Base ref to diff against (e.g. 'main', 'HEAD~3'). Default: unstaged changes."
|
|
},
|
|
"staged": {
|
|
"type": "boolean",
|
|
"description": "Show staged changes only (--cached). Default: false."
|
|
},
|
|
"path_filter": {
|
|
"type": "string",
|
|
"description": "Limit diff to a specific path or glob."
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
fn permission_level(&self) -> PermissionLevel {
|
|
PermissionLevel::ReadOnly
|
|
}
|
|
|
|
async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
|
|
let base = args.get("base").and_then(|v| v.as_str());
|
|
let staged = args
|
|
.get("staged")
|
|
.and_then(|v| v.as_bool())
|
|
.unwrap_or(false);
|
|
let path_filter = args.get("path_filter").and_then(|v| v.as_str());
|
|
|
|
let mut git_args = vec!["diff", "--stat", "-p"];
|
|
|
|
if staged {
|
|
git_args.push("--cached");
|
|
}
|
|
|
|
let base_str = base.map(|b| b.to_string());
|
|
if let Some(ref bs) = base_str {
|
|
git_args.push(bs);
|
|
}
|
|
|
|
if let Some(pf) = path_filter {
|
|
git_args.push("--");
|
|
git_args.push(pf);
|
|
}
|
|
|
|
tracing::debug!(
|
|
workspace = %self.workspace_dir.display(),
|
|
?git_args,
|
|
"[read_diff] running git diff"
|
|
);
|
|
|
|
let output = tokio::process::Command::new("git")
|
|
.args(&git_args)
|
|
.current_dir(&self.workspace_dir)
|
|
.output()
|
|
.await?;
|
|
|
|
if output.status.success() {
|
|
let diff = String::from_utf8_lossy(&output.stdout);
|
|
tracing::debug!("[read_diff] success, diff length={}", diff.len());
|
|
if diff.trim().is_empty() {
|
|
Ok(ToolResult::success("No changes found."))
|
|
} else {
|
|
Ok(ToolResult::success(diff.to_string()))
|
|
}
|
|
} else {
|
|
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
tracing::debug!("[read_diff] failed: {stderr}");
|
|
Ok(ToolResult::error(stderr.to_string()))
|
|
}
|
|
}
|
|
}
|