cargo fmt
This commit is contained in:
@@ -91,8 +91,7 @@ impl Tool for EditTool {
|
||||
let occurrences = if params.replace_all { count } else { 1 };
|
||||
|
||||
self.fs.write(¶ms.file_path, new_text.as_bytes())?;
|
||||
self.tracker
|
||||
.record(¶ms.file_path, new_text.as_bytes());
|
||||
self.tracker.record(¶ms.file_path, new_text.as_bytes());
|
||||
|
||||
let summary = format!(
|
||||
"Edited {} ({} replacement{})",
|
||||
|
||||
@@ -6,9 +6,7 @@ use std::sync::Arc;
|
||||
use async_trait::async_trait;
|
||||
use grep_regex::RegexMatcherBuilder;
|
||||
use grep_searcher::sinks::UTF8 as UTF8Sink;
|
||||
use grep_searcher::{
|
||||
BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkMatch,
|
||||
};
|
||||
use grep_searcher::{BinaryDetection, Searcher, SearcherBuilder, Sink, SinkContext, SinkMatch};
|
||||
use ignore::WalkBuilder;
|
||||
use ignore::overrides::OverrideBuilder;
|
||||
use ignore::types::TypesBuilder;
|
||||
@@ -94,10 +92,9 @@ impl Tool for GrepTool {
|
||||
);
|
||||
|
||||
let default_base = self.fs.scope().root().to_path_buf();
|
||||
let report =
|
||||
tokio::task::spawn_blocking(move || run_grep(default_base, params))
|
||||
.await
|
||||
.map_err(|e| ToolError::Internal(format!("spawn_blocking failed: {e}")))??;
|
||||
let report = tokio::task::spawn_blocking(move || run_grep(default_base, params))
|
||||
.await
|
||||
.map_err(|e| ToolError::Internal(format!("spawn_blocking failed: {e}")))??;
|
||||
|
||||
Ok(report.render())
|
||||
}
|
||||
@@ -212,12 +209,7 @@ impl GrepReport {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
body.push_str(&format!(
|
||||
"{}{}{}\n",
|
||||
line.path.display(),
|
||||
sep,
|
||||
line.text
|
||||
));
|
||||
body.push_str(&format!("{}{}{}\n", line.path.display(), sep, line.text));
|
||||
}
|
||||
let mut summary = format!(
|
||||
"{} matching line(s) in {} file(s)",
|
||||
@@ -285,7 +277,8 @@ fn run_grep(default_base: PathBuf, p: GrepParams) -> Result<GrepReport, ToolsErr
|
||||
}
|
||||
if let Some(g) = p.glob.as_deref() {
|
||||
let mut ob = OverrideBuilder::new(&base);
|
||||
ob.add(g).map_err(|e| ToolsError::InvalidGlob(e.to_string()))?;
|
||||
ob.add(g)
|
||||
.map_err(|e| ToolsError::InvalidGlob(e.to_string()))?;
|
||||
let ov = ob
|
||||
.build()
|
||||
.map_err(|e| ToolsError::InvalidGlob(e.to_string()))?;
|
||||
@@ -414,11 +407,7 @@ struct ContentSink<'a> {
|
||||
impl Sink for ContentSink<'_> {
|
||||
type Error = std::io::Error;
|
||||
|
||||
fn matched(
|
||||
&mut self,
|
||||
_searcher: &Searcher,
|
||||
mat: &SinkMatch<'_>,
|
||||
) -> Result<bool, Self::Error> {
|
||||
fn matched(&mut self, _searcher: &Searcher, mat: &SinkMatch<'_>) -> Result<bool, Self::Error> {
|
||||
let idx = *self.matches_seen;
|
||||
*self.matches_seen += 1;
|
||||
|
||||
@@ -589,10 +578,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn grep_multiline() {
|
||||
let (dir, fs) = setup();
|
||||
touch(
|
||||
&dir.path().join("a.txt"),
|
||||
"start\nfoo\nbar\nend\n",
|
||||
);
|
||||
touch(&dir.path().join("a.txt"), "start\nfoo\nbar\nend\n");
|
||||
|
||||
let def = grep_tool(fs);
|
||||
let (_, tool) = def();
|
||||
|
||||
@@ -39,10 +39,7 @@ pub use write::write_tool;
|
||||
/// All returned factories share the same tracker instance so that
|
||||
/// `Read` / `Write` / `Edit` see a consistent history across tool
|
||||
/// invocations within a single session.
|
||||
pub fn builtin_tools(
|
||||
fs: ScopedFs,
|
||||
tracker: Tracker,
|
||||
) -> Vec<llm_worker::tool::ToolDefinition> {
|
||||
pub fn builtin_tools(fs: ScopedFs, tracker: Tracker) -> Vec<llm_worker::tool::ToolDefinition> {
|
||||
vec![
|
||||
read_tool(fs.clone(), tracker.clone()),
|
||||
write_tool(fs.clone(), tracker.clone()),
|
||||
|
||||
@@ -103,10 +103,7 @@ impl ScopedFs {
|
||||
let existed = path.exists();
|
||||
|
||||
let parent = path.parent().ok_or_else(|| {
|
||||
ToolsError::InvalidArgument(format!(
|
||||
"path has no parent directory: {}",
|
||||
path.display()
|
||||
))
|
||||
ToolsError::InvalidArgument(format!("path has no parent directory: {}", path.display()))
|
||||
})?;
|
||||
if !parent.as_os_str().is_empty() && !parent.exists() {
|
||||
std::fs::create_dir_all(parent).map_err(|e| ToolsError::io(parent, e))?;
|
||||
@@ -119,7 +116,8 @@ impl ScopedFs {
|
||||
};
|
||||
let mut tmp = tempfile::NamedTempFile::new_in(tmp_parent)
|
||||
.map_err(|e| ToolsError::io(tmp_parent, e))?;
|
||||
tmp.write_all(content).map_err(|e| ToolsError::io(path, e))?;
|
||||
tmp.write_all(content)
|
||||
.map_err(|e| ToolsError::io(path, e))?;
|
||||
tmp.as_file()
|
||||
.sync_all()
|
||||
.map_err(|e| ToolsError::io(path, e))?;
|
||||
|
||||
@@ -48,7 +48,9 @@ impl Tool for WriteTool {
|
||||
self.tracker.verify(¶ms.file_path, ¤t)?;
|
||||
}
|
||||
|
||||
let outcome = self.fs.write(¶ms.file_path, params.content.as_bytes())?;
|
||||
let outcome = self
|
||||
.fs
|
||||
.write(¶ms.file_path, params.content.as_bytes())?;
|
||||
|
||||
// Refresh the history entry to reflect the newly-written content,
|
||||
// so a subsequent Edit / Write can proceed without a re-read.
|
||||
@@ -57,7 +59,11 @@ impl Tool for WriteTool {
|
||||
|
||||
let summary = format!(
|
||||
"{} {} ({} bytes)",
|
||||
if outcome.created { "Created" } else { "Overwrote" },
|
||||
if outcome.created {
|
||||
"Created"
|
||||
} else {
|
||||
"Overwrote"
|
||||
},
|
||||
params.file_path.display(),
|
||||
outcome.bytes_written
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@ use llm_worker::tool::{Tool, ToolDefinition};
|
||||
use manifest::Scope;
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
use tools::{Tracker, ScopedFs, builtin_tools};
|
||||
use tools::{ScopedFs, Tracker, builtin_tools};
|
||||
|
||||
struct Registry {
|
||||
entries: Vec<(llm_worker::tool::ToolMeta, Arc<dyn Tool>)>,
|
||||
@@ -54,10 +54,7 @@ async fn unicode_path_and_content() {
|
||||
|
||||
let read = reg.get("Read");
|
||||
let out = read
|
||||
.execute(
|
||||
&json!({ "file_path": file.to_str().unwrap() })
|
||||
.to_string(),
|
||||
)
|
||||
.execute(&json!({ "file_path": file.to_str().unwrap() }).to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
let body = out.content.unwrap();
|
||||
@@ -81,11 +78,9 @@ async fn symlink_to_outside_scope_is_rejected_for_write() {
|
||||
|
||||
// Read tool must work against the symlink (read is unrestricted).
|
||||
let read = reg.get("Read");
|
||||
read.execute(
|
||||
&json!({ "file_path": link.to_str().unwrap() }).to_string(),
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
read.execute(&json!({ "file_path": link.to_str().unwrap() }).to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Write through the symlink must be rejected because canonicalization
|
||||
// resolves it to outside the scope.
|
||||
|
||||
@@ -11,7 +11,7 @@ use llm_worker::tool::{Tool, ToolDefinition, ToolMeta};
|
||||
use manifest::Scope;
|
||||
use serde_json::json;
|
||||
use tempfile::TempDir;
|
||||
use tools::{Tracker, ScopedFs, builtin_tools};
|
||||
use tools::{ScopedFs, Tracker, builtin_tools};
|
||||
|
||||
struct Registry {
|
||||
entries: Vec<(ToolMeta, Arc<dyn Tool>)>,
|
||||
@@ -50,10 +50,7 @@ async fn call(tool: &Arc<dyn Tool>, input: serde_json::Value) -> llm_worker::too
|
||||
.expect("tool execution failed")
|
||||
}
|
||||
|
||||
async fn call_err(
|
||||
tool: &Arc<dyn Tool>,
|
||||
input: serde_json::Value,
|
||||
) -> llm_worker::tool::ToolError {
|
||||
async fn call_err(tool: &Arc<dyn Tool>, input: serde_json::Value) -> llm_worker::tool::ToolError {
|
||||
tool.execute(&input.to_string())
|
||||
.await
|
||||
.expect_err("expected error")
|
||||
@@ -71,7 +68,11 @@ fn builtin_tools_registers_all_five() {
|
||||
fn meta_has_description_and_schema() {
|
||||
let (_dir, reg) = setup();
|
||||
for (meta, _) in ®.entries {
|
||||
assert!(!meta.description.is_empty(), "{} missing description", meta.name);
|
||||
assert!(
|
||||
!meta.description.is_empty(),
|
||||
"{} missing description",
|
||||
meta.name
|
||||
);
|
||||
// Input schema must be a JSON object
|
||||
assert!(
|
||||
meta.input_schema.is_object(),
|
||||
@@ -283,7 +284,11 @@ async fn tracker_recent_files_tracks_read_write_edit() {
|
||||
std::fs::write(&a, "one\n").unwrap();
|
||||
|
||||
// Read `a` — should appear in recency.
|
||||
call(®.get("Read"), json!({ "file_path": a.to_str().unwrap() })).await;
|
||||
call(
|
||||
®.get("Read"),
|
||||
json!({ "file_path": a.to_str().unwrap() }),
|
||||
)
|
||||
.await;
|
||||
// Write `b` (new file) — should appear ahead of `a`.
|
||||
call(
|
||||
®.get("Write"),
|
||||
@@ -303,8 +308,14 @@ async fn tracker_recent_files_tracks_read_write_edit() {
|
||||
|
||||
let recent = tracker.recent_files(10);
|
||||
assert_eq!(recent.len(), 2);
|
||||
assert!(recent[0].ends_with("a.txt"), "front should be a.txt: {recent:?}");
|
||||
assert!(recent[1].ends_with("b.txt"), "second should be b.txt: {recent:?}");
|
||||
assert!(
|
||||
recent[0].ends_with("a.txt"),
|
||||
"front should be a.txt: {recent:?}"
|
||||
);
|
||||
assert!(
|
||||
recent[1].ends_with("b.txt"),
|
||||
"second should be b.txt: {recent:?}"
|
||||
);
|
||||
}
|
||||
|
||||
// Sanity: unused Path import guard
|
||||
|
||||
Reference in New Issue
Block a user