From 503829425e878b1c3d921bd3ad78159d02d18e42 Mon Sep 17 00:00:00 2001 From: Rohan Sharma Date: Tue, 30 Jun 2026 19:25:57 -0700 Subject: [PATCH] feat(cli): add `weave apply` to materialize entity edits onto files weave tracks entity edits in the CRDT (via the MCP tools and claim flows) but had no way to write them back to the working files, so an edit stayed in the CRDT and never reached disk. `weave apply ` closes that loop: it reconstructs each file from its authoritative CRDT entity content plus the interstitial text between entities, and writes it out. This is the write side of weave. Paired with sem on the read side, an agent reads and understands code with sem, edits entities in weave's CRDT, and runs `weave apply` to project those edits onto the files, entirely in entity space and immune to line drift. Reuses the existing find_repo_root and reconstruct_file_from_crdt; the command only wires them to disk. --- crates/weave-cli/src/commands/apply.rs | 51 ++++++++++++++++++++++++++ crates/weave-cli/src/commands/mod.rs | 1 + crates/weave-cli/src/main.rs | 6 +++ 3 files changed, 58 insertions(+) create mode 100644 crates/weave-cli/src/commands/apply.rs diff --git a/crates/weave-cli/src/commands/apply.rs b/crates/weave-cli/src/commands/apply.rs new file mode 100644 index 00000000..39ce1f29 --- /dev/null +++ b/crates/weave-cli/src/commands/apply.rs @@ -0,0 +1,51 @@ +use colored::Colorize; +use weave_core::git::find_repo_root; +use weave_crdt::{reconstruct_file_from_crdt, EntityStateDoc}; + +/// Materialize entity edits from the CRDT back onto the working files. +/// +/// This is weave's write side: the CRDT is the authoritative source of an +/// entity's content, and this projects that content onto disk, reassembling +/// the file from its entities plus the interstitial text between them. It is +/// the counterpart to editing entities in the CRDT (via the MCP tools or +/// `weave claim` flows): edit entities, then `weave apply` to write the files. +pub fn run(files: &[String]) -> Result<(), Box> { + if files.is_empty() { + return Err("Specify one or more files to apply, e.g. `weave apply src/lib.rs`".into()); + } + + let repo_root = find_repo_root()?; + let state_path = repo_root.join(".weave").join("state.automerge"); + let state = EntityStateDoc::open(&state_path)?; + + let mut applied = 0usize; + for file in files { + let reconstructed = reconstruct_file_from_crdt(&state, file)?; + if reconstructed.is_empty() { + println!( + " {} {} — no entities tracked in the CRDT, skipping", + "!".yellow().bold(), + file + ); + continue; + } + + let target = repo_root.join(file); + let before = std::fs::read_to_string(&target).unwrap_or_default(); + if before == reconstructed { + println!(" {} {} — already up to date", "·".dimmed(), file); + continue; + } + + std::fs::write(&target, &reconstructed)?; + applied += 1; + println!(" {} {} — materialized from CRDT", "✓".green().bold(), file); + } + + println!( + "{} {} file(s) written from the CRDT.", + "weave apply:".bold(), + applied + ); + Ok(()) +} diff --git a/crates/weave-cli/src/commands/mod.rs b/crates/weave-cli/src/commands/mod.rs index c7001b1d..1db5211a 100644 --- a/crates/weave-cli/src/commands/mod.rs +++ b/crates/weave-cli/src/commands/mod.rs @@ -1,3 +1,4 @@ +pub mod apply; pub mod bench; pub mod bench_repo; pub mod claim; diff --git a/crates/weave-cli/src/main.rs b/crates/weave-cli/src/main.rs index a0b770ab..afda5a09 100644 --- a/crates/weave-cli/src/main.rs +++ b/crates/weave-cli/src/main.rs @@ -26,6 +26,11 @@ enum Commands { }, /// Remove weave merge driver from the current Git repo Unsetup, + /// Materialize entity edits from the CRDT back onto the working files + Apply { + /// One or more files to write from the CRDT + files: Vec, + }, /// Preview what a merge between branches would look like Preview { /// The branch to merge into HEAD @@ -99,6 +104,7 @@ fn main() { global, } => commands::setup::run(driver.as_deref(), local, global), Commands::Unsetup => commands::setup::unsetup(), + Commands::Apply { ref files } => commands::apply::run(files), Commands::Preview { ref branch, ref file,