use anyhow::{Context, Result};
use std::path::Path;
use crate::{sessions, snapshot};
const STATE_FILES: &[(&str, &str)] = &[
("snapshots", "md"),
("locks", "lock"),
("pending", "md"),
("crdt", "yrs"),
("pre-response", "md"),
];
pub fn run(old_path: &Path, new_path: &Path) -> Result<()> {
if !new_path.exists() {
anyhow::bail!("new path does not exist: {}", new_path.display());
}
let old_hash = if old_path.exists() {
snapshot::doc_hash(old_path)?
} else {
let abs = if old_path.is_absolute() {
old_path.to_string_lossy().to_string()
} else {
let cwd = std::env::current_dir()
.context("failed to get current directory")?;
cwd.join(old_path).to_string_lossy().to_string()
};
snapshot::doc_hash_from_str(&abs)
};
let new_hash = snapshot::doc_hash(new_path)?;
if old_hash == new_hash {
eprintln!("[rename] hashes match — nothing to migrate");
return Ok(());
}
let canonical_new = new_path.canonicalize()?;
let project_root = snapshot::find_project_root(&canonical_new)
.context("no .agent-doc/ directory found above new path")?;
let mut migrated = 0u32;
for &(subdir, ext) in STATE_FILES {
let dir = project_root.join(".agent-doc").join(subdir);
let old_file = dir.join(format!("{}.{}", old_hash, ext));
let new_file = dir.join(format!("{}.{}", new_hash, ext));
if !old_file.exists() {
continue;
}
if new_file.exists() {
anyhow::bail!(
"destination already exists: {} (would overwrite)",
new_file.display()
);
}
std::fs::rename(&old_file, &new_file)
.with_context(|| format!("failed to rename {} → {}", old_file.display(), new_file.display()))?;
eprintln!("[rename] {}/{}.{} → {}.{}", subdir, &old_hash[..8], ext, &new_hash[..8], ext);
migrated += 1;
}
let locks_dir = project_root.join(".agent-doc/locks");
let old_snap_lock = locks_dir.join(format!("{}.md.lock", old_hash));
let new_snap_lock = locks_dir.join(format!("{}.md.lock", new_hash));
if old_snap_lock.exists() && !new_snap_lock.exists() {
std::fs::rename(&old_snap_lock, &new_snap_lock)?;
migrated += 1;
}
let crdt_dir = project_root.join(".agent-doc/crdt");
let old_crdt_lock = crdt_dir.join(format!("{}.yrs.lock", old_hash));
let new_crdt_lock = crdt_dir.join(format!("{}.yrs.lock", new_hash));
if old_crdt_lock.exists() && !new_crdt_lock.exists() {
std::fs::rename(&old_crdt_lock, &new_crdt_lock)?;
migrated += 1;
}
let old_path_str = old_path.to_string_lossy().to_string();
let new_path_str = new_path.to_string_lossy().to_string();
let old_abs = if old_path.is_absolute() {
old_path_str.clone()
} else {
let cwd = std::env::current_dir().unwrap_or_default();
cwd.join(old_path).to_string_lossy().to_string()
};
let new_rel = new_path_str.clone();
let _lock = sessions::RegistryLock::acquire(&sessions::registry_path())?;
let mut registry = sessions::load()?;
let mut updated_sessions = 0u32;
for (_sid, entry) in registry.iter_mut() {
if entry.file == old_path_str || entry.file == old_abs {
entry.file = new_rel.clone();
updated_sessions += 1;
}
}
if updated_sessions > 0 {
sessions::save(®istry)?;
}
eprintln!(
"[rename] migrated {} state file(s), updated {} session(s): {} → {}",
migrated, updated_sessions, old_path.display(), new_path.display()
);
Ok(())
}