flatten: move poc-memory contents to workspace root
No more subcrate nesting — src/, agents/, schema/, defaults/, build.rs all live at the workspace root. poc-daemon remains as the only workspace member. Crate name (poc-memory) and all imports unchanged. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
891cca57f8
commit
998b71e52c
113 changed files with 79 additions and 78 deletions
129
src/agent/tools/grep.rs
Normal file
129
src/agent/tools/grep.rs
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
// tools/grep.rs — Search file contents
|
||||
//
|
||||
// Prefers ripgrep (rg) for speed, falls back to grep -r if rg
|
||||
// isn't installed. Both produce compatible output.
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use serde::Deserialize;
|
||||
use serde_json::json;
|
||||
use std::process::Command;
|
||||
|
||||
use crate::agent::types::ToolDef;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct Args {
|
||||
pattern: String,
|
||||
#[serde(default = "default_path")]
|
||||
path: String,
|
||||
glob: Option<String>,
|
||||
#[serde(default)]
|
||||
show_content: bool,
|
||||
context_lines: Option<u64>,
|
||||
}
|
||||
|
||||
fn default_path() -> String { ".".into() }
|
||||
|
||||
pub fn definition() -> ToolDef {
|
||||
ToolDef::new(
|
||||
"grep",
|
||||
"Search for a pattern in files. Returns matching file paths by default, \
|
||||
or matching lines with context.",
|
||||
json!({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"description": "Regex pattern to search for"
|
||||
},
|
||||
"path": {
|
||||
"type": "string",
|
||||
"description": "Directory or file to search in (default: current directory)"
|
||||
},
|
||||
"glob": {
|
||||
"type": "string",
|
||||
"description": "Glob pattern to filter files (e.g. '*.rs', '*.py')"
|
||||
},
|
||||
"show_content": {
|
||||
"type": "boolean",
|
||||
"description": "Show matching lines instead of just file paths"
|
||||
},
|
||||
"context_lines": {
|
||||
"type": "integer",
|
||||
"description": "Number of context lines around matches (requires show_content)"
|
||||
}
|
||||
},
|
||||
"required": ["pattern"]
|
||||
}),
|
||||
)
|
||||
}
|
||||
|
||||
/// Check if ripgrep is available (cached after first check).
|
||||
fn has_rg() -> bool {
|
||||
use std::sync::OnceLock;
|
||||
static HAS_RG: OnceLock<bool> = OnceLock::new();
|
||||
*HAS_RG.get_or_init(|| Command::new("rg").arg("--version").output().is_ok())
|
||||
}
|
||||
|
||||
pub fn grep(args: &serde_json::Value) -> Result<String> {
|
||||
let a: Args = serde_json::from_value(args.clone())
|
||||
.context("invalid grep arguments")?;
|
||||
|
||||
let output = if has_rg() {
|
||||
run_search("rg", &a.pattern, &a.path, a.glob.as_deref(), a.show_content, a.context_lines, true)?
|
||||
} else {
|
||||
run_search("grep", &a.pattern, &a.path, a.glob.as_deref(), a.show_content, a.context_lines, false)?
|
||||
};
|
||||
|
||||
if output.is_empty() {
|
||||
return Ok("No matches found.".to_string());
|
||||
}
|
||||
|
||||
Ok(super::truncate_output(output, 30000))
|
||||
}
|
||||
|
||||
/// Run a grep/rg search. Unified implementation for both tools.
|
||||
fn run_search(
|
||||
tool: &str,
|
||||
pattern: &str,
|
||||
path: &str,
|
||||
file_glob: Option<&str>,
|
||||
show_content: bool,
|
||||
context: Option<u64>,
|
||||
use_rg: bool,
|
||||
) -> Result<String> {
|
||||
let mut cmd = Command::new(tool);
|
||||
|
||||
if use_rg {
|
||||
// ripgrep args
|
||||
if show_content {
|
||||
cmd.arg("-n");
|
||||
if let Some(c) = context {
|
||||
cmd.arg("-C").arg(c.to_string());
|
||||
}
|
||||
} else {
|
||||
cmd.arg("--files-with-matches");
|
||||
}
|
||||
if let Some(g) = file_glob {
|
||||
cmd.arg("--glob").arg(g);
|
||||
}
|
||||
} else {
|
||||
// grep args
|
||||
cmd.arg("-r"); // recursive
|
||||
if show_content {
|
||||
cmd.arg("-n"); // line numbers
|
||||
if let Some(c) = context {
|
||||
cmd.arg("-C").arg(c.to_string());
|
||||
}
|
||||
} else {
|
||||
cmd.arg("-l"); // files-with-matches
|
||||
}
|
||||
if let Some(g) = file_glob {
|
||||
cmd.arg("--include").arg(g);
|
||||
}
|
||||
cmd.arg("-E"); // extended regex
|
||||
}
|
||||
|
||||
cmd.arg(pattern).arg(path);
|
||||
let output = cmd.output().with_context(|| format!("Failed to run {}", tool))?;
|
||||
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue