use rag_rat_core::{Config, OutputFormat};
use crate::cli::{DreamArgs, MemoryArgs, MemoryCommand};
use crate::commands::output_format;
use crate::open_index;
use crate::render::print_output;
pub(crate) fn dream(config: &Config, args: &DreamArgs) -> anyhow::Result<()> {
let _lock = rag_rat_core::locks::WriteLock::acquire_blocking(&config.database)?;
let db = open_index(config)?;
let now_ms = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_millis() as i64)
.unwrap_or(0);
let report = db.dream_run(rag_rat_core::dream::DreamOptions {
now_ms,
limit: args.limit.unwrap_or(20) as usize,
})?;
print_output(&report)
}
fn symbol_bind_target(
hit: &rag_rat_core::query::symbol::SymbolHit,
) -> rag_rat_core::query::memory::RepoMemoryBindTarget {
rag_rat_core::query::memory::RepoMemoryBindTarget {
symbol_id: Some(hit.symbol_id),
logical_symbol_id: hit.logical_symbol_id,
..Default::default()
}
}
fn path_bind_target(path: String) -> rag_rat_core::query::memory::RepoMemoryBindTarget {
rag_rat_core::query::memory::RepoMemoryBindTarget { path: Some(path), ..Default::default() }
}
fn dir_bind_target(dir: String) -> rag_rat_core::query::memory::RepoMemoryBindTarget {
rag_rat_core::query::memory::RepoMemoryBindTarget { dir: Some(dir), ..Default::default() }
}
fn chunk_bind_target(chunk_id: i64) -> rag_rat_core::query::memory::RepoMemoryBindTarget {
rag_rat_core::query::memory::RepoMemoryBindTarget {
chunk_id: Some(chunk_id),
..Default::default()
}
}
pub(crate) fn memory(config: &Config, args: &MemoryArgs) -> anyhow::Result<()> {
match &args.command {
MemoryCommand::Doctor => {
let db = open_index(config)?;
let entries = db.memory_doctor()?;
if output_format() == OutputFormat::Json {
print_output(&entries)?;
let any_gone = entries.iter().any(|e| e.anchor_status == "gone");
if any_gone {
anyhow::bail!("one or more memories have gone anchors");
}
return Ok(());
}
if entries.is_empty() {
eprintln!("All active memory anchors are current.");
return Ok(());
}
let mut any_gone = false;
for entry in &entries {
eprintln!("[{}] {} ({})", entry.anchor_status, entry.title, entry.memory_id);
eprintln!(" binding: {} {}", entry.binding_kind, entry.binding_id);
if entry.candidates.is_empty() {
if entry.anchor_status == "gone" {
eprintln!(
" -> code appears deleted; rag-rat memory mark-obsolete {}",
entry.memory_id
);
}
} else {
for candidate in &entry.candidates {
eprintln!(
" rag-rat memory rebind {} --symbol-path {}",
entry.memory_id, candidate
);
}
}
if entry.anchor_status == "gone" {
any_gone = true;
}
}
if any_gone {
anyhow::bail!("one or more memories have gone anchors");
}
Ok(())
},
MemoryCommand::Rebind { memory_id, symbol, symbol_path, symbol_id, path, chunk, dir } => {
let db = open_index(config)?;
let bind = if symbol.is_some() || symbol_path.is_some() || symbol_id.is_some() {
let selector = rag_rat_core::query::symbol::SymbolSelector {
logical_symbol_id: None,
symbol_id: *symbol_id,
symbol_path: symbol_path.clone(),
symbol: symbol.clone(),
language: None,
allow_ambiguous: false,
limit: 10,
};
let label = symbol
.as_deref()
.or(symbol_path.as_deref())
.map(str::to_string)
.unwrap_or_else(|| format!("#{}", symbol_id.unwrap_or_default()));
match db.select_symbol_for_bind(&selector)? {
Ok(Some(hit)) => symbol_bind_target(&hit),
Ok(None) => anyhow::bail!("symbol `{label}` not found"),
Err(disambiguation) => anyhow::bail!(
"symbol `{label}` is ambiguous — disambiguate with one of:\n{}",
disambiguation
.candidates
.iter()
.map(|c| format!(
" --symbol-id {} ({} in {})",
c.symbol_id, c.qualified_name, c.path
))
.collect::<Vec<_>>()
.join("\n")
),
}
} else if let Some(path) = path {
path_bind_target(path.clone())
} else if let Some(chunk_id) = chunk {
chunk_bind_target(*chunk_id)
} else if let Some(dir) = dir {
dir_bind_target(dir.clone())
} else {
anyhow::bail!(
"memory rebind needs one of --symbol <name>, --symbol-path <path::name>, \
--symbol-id <id>, --path <path>, --chunk <id>, or --dir <dir>"
);
};
print_output(&db.memory_rebind(memory_id, bind)?)
},
MemoryCommand::List { kind } => {
let db = open_index(config)?;
let summaries = db.memory_list(kind.as_deref())?;
if output_format() == OutputFormat::Json {
return print_output(&summaries);
}
if summaries.is_empty() {
eprintln!("No memories found.");
return Ok(());
}
for s in &summaries {
println!(
"{} [{}/{}] {} ({}:{})",
s.memory_id, s.kind, s.status, s.title, s.binding_kind, s.binding_id
);
}
Ok(())
},
MemoryCommand::Show { memory_id } => {
let db = open_index(config)?;
let Some(memory) = db.memory_get(memory_id)? else {
anyhow::bail!("memory `{memory_id}` not found");
};
if output_format() == OutputFormat::Json {
return print_output(&memory);
}
println!("Title: {}", memory.title);
println!("Kind: {} / {} / {}", memory.kind, memory.status, memory.confidence);
println!();
println!("{}", memory.body);
if !memory.bindings.is_empty() {
println!();
println!("Bindings:");
for b in &memory.bindings {
println!(" {} {} [{}]", b.binding_kind, b.binding_id, b.anchor_status);
}
}
Ok(())
},
}
}