use crate::memory::{self, MemoryResult};
use std::sync::atomic::{AtomicBool, Ordering};
const MAX_SNIPPETS: usize = 2;
const SNIPPET_CHARS: usize = 500;
const MAX_HINT_CHARS: usize = 900;
const MIN_RANK: f64 = 0.1;
static STORE_UNAVAILABLE: AtomicBool = AtomicBool::new(false);
pub async fn hints_for(query: &str) -> Option<String> {
if STORE_UNAVAILABLE.load(Ordering::Relaxed) {
return None;
}
let store = match memory::get_store() {
Ok(s) => s,
Err(_) => {
STORE_UNAVAILABLE.store(true, Ordering::Relaxed);
return None;
}
};
let results = match memory::search_brain(store, query, MAX_SNIPPETS * 3).await {
Ok(r) => r,
Err(_) => return None,
};
format_hints(&results)
}
fn format_hints(results: &[MemoryResult]) -> Option<String> {
let mut block = String::from("\n\n─── relevant brain notes ───\n");
let mut count = 0usize;
for r in results {
if count >= MAX_SNIPPETS {
break;
}
if r.rank < MIN_RANK {
continue;
}
let name = std::path::Path::new(&r.path)
.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_else(|| r.path.clone());
let snippet: String = r.snippet.chars().take(SNIPPET_CHARS).collect();
block.push_str(&format!("• **{name}**: {snippet}\n"));
count += 1;
}
if count == 0 {
return None;
}
if block.chars().count() > MAX_HINT_CHARS {
let mut capped: String = block.chars().take(MAX_HINT_CHARS).collect();
capped.push('…');
return Some(capped);
}
Some(block)
}
#[cfg(test)]
mod tests {
use super::*;
fn result(path: &str, snippet: &str, rank: f64) -> MemoryResult {
MemoryResult {
path: path.to_string(),
snippet: snippet.to_string(),
rank,
}
}
#[test]
fn empty_results_yield_none() {
assert_eq!(format_hints(&[]), None);
}
#[test]
fn all_below_threshold_yield_none() {
let results = vec![
result("TOOLS.md", "telegram_send routing", 0.05),
result("AGENTS.md", "github rules", 0.01),
];
assert_eq!(format_hints(&results), None);
}
#[test]
fn relevant_results_yield_block() {
let results = vec![result("TOOLS.md", "telegram_send is canonical", 0.9)];
let block = format_hints(&results).expect("expected a hint block");
assert!(block.contains("relevant brain notes"));
assert!(block.contains("TOOLS.md"));
assert!(block.contains("telegram_send is canonical"));
}
#[test]
fn low_rank_results_are_filtered() {
let results = vec![
result("TOOLS.md", "good match", 0.8),
result("AGENTS.md", "noise", 0.02),
];
let block = format_hints(&results).expect("expected a hint block");
assert!(block.contains("TOOLS.md"));
assert!(!block.contains("AGENTS.md"));
}
#[test]
fn caps_at_max_snippets() {
let results = vec![
result("TOOLS.md", "one", 0.9),
result("AGENTS.md", "two", 0.8),
result("CODE.md", "three", 0.7),
];
let block = format_hints(&results).expect("expected a hint block");
assert!(block.contains("TOOLS.md"));
assert!(block.contains("AGENTS.md"));
assert!(!block.contains("CODE.md"));
}
#[test]
fn truncates_long_snippets() {
let long = "y".repeat(600);
let results = vec![result("TOOLS.md", &long, 0.9)];
let block = format_hints(&results).expect("expected a hint block");
let ys: usize = block.chars().filter(|c| *c == 'y').count();
assert_eq!(ys, SNIPPET_CHARS);
}
#[test]
fn caps_whole_block() {
let results = vec![
result("TOOLS.md", &"a".repeat(500), 0.9),
result("AGENTS.md", &"b".repeat(500), 0.8),
];
let block = format_hints(&results).expect("expected a hint block");
assert!(block.chars().count() <= MAX_HINT_CHARS + 1);
}
#[test]
fn uses_file_name_not_full_path() {
let results = vec![result("/home/u/.opencrabs/TOOLS.md", "routing", 0.9)];
let block = format_hints(&results).expect("expected a hint block");
assert!(block.contains("TOOLS.md"));
assert!(!block.contains("/home/u"));
}
}