use anyhow::{bail, Result};
use atlas::vault_store;
use cloud::client::{auth::load_token, upgrade::Tier};
use vault::fact::MemoryTier;
use vault::retrieval::query::VaultQuery;
pub fn handle(push: bool, pull: bool, project: Option<String>) -> Result<()> {
let token = load_token()
.ok_or_else(|| anyhow::anyhow!("Not authenticated. Run `bctx login` first."))?;
let tier = Tier::parse_tier(&token.tier);
if !tier.cloud_sync_enabled() {
bail!("Cloud sync requires the Beacon tier or higher.\nUpgrade at https://betterctx.com/upgrade");
}
let project_hash = project.unwrap_or_else(|| {
let cwd = std::env::current_dir().unwrap_or_default();
let hash = blake3::hash(cwd.to_string_lossy().as_bytes());
hash.to_hex()[..16].to_string()
});
let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
rt.block_on(async {
if push || !pull {
let facts = {
let vault = vault_store::vault().lock().unwrap();
vault.query(
&VaultQuery::new("")
.in_tier(MemoryTier::Crystallized)
.top_k(10_000),
)
};
let count = facts.len();
let resp = cloud::client::sync::push_vault(
&token.endpoint,
&token.access_token,
&project_hash,
facts,
)
.await?;
println!("Pushed {count} crystallized facts → cloud");
println!(" Accepted : {}", resp.accepted);
println!(" Merged : {}", resp.merged);
println!(" Version : {}", resp.server_version);
}
if pull {
let resp = cloud::client::sync::pull_vault(
&token.endpoint,
&token.access_token,
&project_hash,
)
.await?;
let count = resp.facts.len();
{
let mut vault = vault_store::vault().lock().unwrap();
for fact in resp.facts {
vault.insert(fact);
}
}
vault_store::flush();
println!(
"Pulled {count} facts from cloud (server version: {})",
resp.server_version
);
}
Ok::<_, anyhow::Error>(())
})
}