Skip to main content

lean_ctx/tools/
ctx_intent.rs

1use crate::core::cache::SessionCache;
2use crate::core::intent_protocol::{IntentRecord, IntentSubject};
3use crate::tools::CrpMode;
4
5pub fn handle(
6    _cache: &mut SessionCache,
7    query: &str,
8    project_root: &str,
9    _crp_mode: CrpMode,
10) -> String {
11    if query.trim().is_empty() {
12        return "ERROR: ctx_intent requires query".to_string();
13    }
14
15    let intent = crate::core::intent_protocol::intent_from_query(query, Some(project_root));
16    format_ack(&intent)
17}
18
19fn format_ack(intent: &IntentRecord) -> String {
20    format!(
21        "INTENT_OK id={} type={} source={} conf={:.0}% subj={}",
22        intent.id,
23        intent.intent_type.as_str(),
24        intent.source.as_str(),
25        (intent.confidence.clamp(0.0, 1.0) * 100.0).round(),
26        subject_short(&intent.subject),
27    )
28}
29
30fn subject_short(s: &IntentSubject) -> String {
31    match s {
32        IntentSubject::Project { root } => format!("project({})", root.as_deref().unwrap_or(".")),
33        IntentSubject::Command { command } => format!("cmd({})", truncate(command, 80)),
34        IntentSubject::Workflow { action } => format!("workflow({})", truncate(action, 60)),
35        IntentSubject::KnowledgeFact { category, key, .. } => format!("fact({category}/{key})"),
36        IntentSubject::KnowledgeQuery { category, query } => format!(
37            "recall({}/{})",
38            category.as_deref().unwrap_or("-"),
39            query.as_deref().unwrap_or("-")
40        ),
41        IntentSubject::Tool { name } => format!("tool({name})"),
42    }
43}
44
45fn truncate(s: &str, max: usize) -> String {
46    if s.chars().count() <= max {
47        return s.to_string();
48    }
49    let mut out = String::new();
50    for (i, ch) in s.chars().enumerate() {
51        if i + 1 >= max {
52            break;
53        }
54        out.push(ch);
55    }
56    out.push('…');
57    out
58}