Skip to main content

agents_tools/
lib.rs

1//! Shared, reusable implementations of agent editing tools.
2//!
3//! The crate exposes two layers:
4//!
5//! - [`apply_diff_to_text`] applies a complete update body to an in-memory
6//!   string. It supports multiple anchored sections and is always available.
7//! - `apply_diff` parses a complete multi-file patch and applies it beneath a
8//!   filesystem root. It is available with the default `filesystem` feature.
9//!
10//! # Patch format
11//!
12//! Filesystem patches use a single envelope containing one or more file
13//! operations:
14//!
15//! ```text
16//! *** Begin Patch
17//! *** Add File: src/new.rs
18//! +pub fn new() {}
19//! *** Update File: src/lib.rs
20//! @@ pub fn old() {
21//! -    before();
22//! +    after();
23//! *** Update File: src/name.rs
24//! *** Move to: src/renamed.rs
25//! *** Delete File: src/unused.rs
26//! *** End Patch
27//! ```
28//!
29//! Update bodies may contain multiple `@@` sections. Lines beginning with a
30//! space are context, `-` removes a line, and `+` inserts a line. Add blocks
31//! require every content line to begin with `+`.
32//!
33//! # Failure and concurrency model
34//!
35//! Patch syntax and unsafe paths are validated before filesystem work begins.
36//! Each connected logical file is then planned fully in memory and committed
37//! independently. A failed file remains unchanged while unrelated files in the
38//! same patch can still succeed; inspect `ApplyDiffReport::failed` for those
39//! failures.
40//!
41//! File replacements are written completely, synchronized, and atomically
42//! installed, so readers do not observe partially written contents. Overlapping
43//! paths are serialized within the process, and the on-disk snapshot is checked
44//! again immediately before each commit to detect observed external changes.
45//!
46//! # Feature flags
47//!
48//! The `filesystem` feature is enabled by default and provides the filesystem
49//! API and serializable reports. Text-only users can disable default features:
50//!
51//! ```toml
52//! agents-tools = { version = "0.1", default-features = false }
53//! ```
54//!
55//! # In-memory example
56//!
57//! ```
58//! use agents_tools::apply_diff_to_text;
59//!
60//! let updated = apply_diff_to_text(
61//!     "fn run() {\n    before();\n}\n",
62//!     "@@ fn run() {\n-    before();\n+    after();",
63//! )?;
64//!
65//! assert_eq!(updated, "fn run() {\n    after();\n}\n");
66//! # Ok::<(), agents_tools::ApplyDiffError>(())
67//! ```
68
69#![forbid(unsafe_code)]
70
71mod apply_diff;
72#[cfg(feature = "filesystem")]
73mod filesystem;
74
75pub use apply_diff::{
76    ApplyDiffError, ApplyDiffMode, apply_diff_to_text, apply_diff_to_text_with_mode,
77};
78#[cfg(feature = "filesystem")]
79pub use filesystem::{AppliedFile, ApplyDiffReport, FailedFile, FileChangeKind, apply_diff};