use crate::config::ContextMount;
use crate::config::RepoConfigManager;
use crate::config::RepoMappingManager;
use crate::config::SyncStrategy;
use crate::config::validation::sanitize_mount_name;
use crate::git::utils::find_repo_root;
use crate::git::utils::get_control_repo_root;
use crate::git::utils::get_remote_url;
use crate::git::utils::is_git_repo;
use crate::utils::paths::expand_path;
use anyhow::Context;
use anyhow::Result;
use anyhow::bail;
use colored::Colorize;
use std::path::PathBuf;
pub async fn execute(
path: PathBuf,
mount_path: Option<String>,
sync: SyncStrategy,
_description: Option<String>,
) -> Result<()> {
println!("{} mount configuration...", "Adding".green());
let mut repo_mapping = RepoMappingManager::new()?;
let path_str = path.to_string_lossy();
let resolved_path = if path_str.starts_with("git@")
|| path_str.starts_with("https://")
|| path_str.starts_with("http://")
|| path_str.starts_with("ssh://")
{
match repo_mapping.resolve_url(&path_str)? {
Some(local_path) => {
println!("Found cloned repository at: {}", local_path.display());
local_path
}
None => {
bail!(
"Cannot add remote URL directly. Repository must be cloned first.\n\n\
To add this repository, use one of:\n\n\
1. Clone and manage automatically:\n\
thoughts mount clone {path_str}\n\n\
2. Clone manually then add:\n\
git clone {path_str} /path/to/local\n\
thoughts mount add /path/to/local"
);
}
}
} else {
path
};
let expanded = expand_path(&resolved_path)?;
if !expanded.exists() {
bail!("Path does not exist: {}", expanded.display());
}
if !expanded.is_dir() {
bail!("Path is not a directory: {}", expanded.display());
}
let (mount_name, url, subpath) = if is_git_repo(&expanded) {
println!("Detected git repository at: {}", expanded.display());
let url = get_remote_url(&expanded)
.context("Git repository has no remote URL. Add a remote first.")?;
println!("Found remote URL: {url}");
repo_mapping.add_mapping(&url, expanded.clone(), false)?;
let mount_name = mount_path.unwrap_or_else(|| {
expanded
.file_name()
.and_then(|n| n.to_str())
.map_or_else(|| "unnamed".to_string(), sanitize_mount_name)
});
(mount_name, url, None)
} else if let Ok(repo_root) = find_repo_root(&expanded) {
println!("Path is within git repository: {}", repo_root.display());
let url = get_remote_url(&repo_root).context("Parent repository has no remote URL")?;
repo_mapping.add_mapping(&url, repo_root.clone(), false)?;
let subpath = expanded
.strip_prefix(&repo_root)?
.to_string_lossy()
.to_string();
let mount_name = mount_path.unwrap_or_else(|| {
expanded
.file_name()
.and_then(|n| n.to_str())
.map_or_else(|| "unnamed".to_string(), sanitize_mount_name)
});
(mount_name, url, Some(subpath))
} else {
bail!(
"Path '{}' is not a git repository.\n\n\
The mount add command requires a git repository.\n\
If you want to mount a regular directory, please initialize it as a git repository first:\n\
cd {}\n\
git init\n\
git remote add origin <URL>",
expanded.display(),
expanded.display()
);
};
let repo_root = get_control_repo_root(&std::env::current_dir()?)?;
let mgr = RepoConfigManager::new(repo_root);
let was_v1 = matches!(mgr.peek_config_version()?, Some(v) if v == "1.0");
let mut cfg = mgr.ensure_v2_default()?;
if cfg
.context_mounts
.iter()
.any(|m| m.mount_path == mount_name)
{
bail!("Mount '{mount_name}' already exists");
}
cfg.context_mounts.push(ContextMount {
remote: url,
subpath,
mount_path: mount_name.clone(),
sync,
});
let warnings = mgr.save_v2_validated(&cfg)?;
for w in warnings {
eprintln!("Warning: {w}");
}
println!("✓ Added repository mount '{mount_name}'");
if was_v1 {
eprintln!("Upgraded to v2 config. See MIGRATION_V1_TO_V2.md");
}
crate::mount::auto_mount::update_active_mounts().await?;
Ok(())
}