lean_ctx/core/gateway/adapters/
codebase_pack.rs1use crate::core::tokens::count_tokens;
15
16const PACK_TOOLS: [&str; 2] = ["pack_codebase", "pack_remote_repository"];
18
19#[must_use]
23pub fn transform(server: &str, tool: &str, text: &str, budget_tokens: usize) -> Option<String> {
24 if !PACK_TOOLS.contains(&tool) {
25 return None;
26 }
27 let v: serde_json::Value = serde_json::from_str(text.trim()).ok()?;
28 let output_id = v.get("outputId").and_then(serde_json::Value::as_str)?;
29
30 let id = crate::core::archive::store(&format!("gateway:{server}::{tool}"), tool, text, None)?;
31 let tokens = count_tokens(text);
32
33 let mut summary = String::new();
34 if let Some(dir) = v
35 .get("directoryStructure")
36 .and_then(serde_json::Value::as_str)
37 {
38 summary.push_str("directoryStructure:\n");
39 summary.push_str(dir.trim_end());
40 summary.push('\n');
41 }
42 for key in ["totalFiles", "totalTokens", "totalCharacters"] {
43 if let Some(n) = v.get(key) {
44 summary.push_str(&format!("{key}: {n}\n"));
45 }
46 }
47 let summary = super::super::postprocess::compress::to_budget(&summary, budget_tokens);
49
50 Some(format!(
51 "{summary}\nrepomix outputId: {output_id} \
52 (grep via: ctx_tools call {server}::grep_repomix_output)\n{}",
53 crate::core::archive::format_hint(&id, text.len(), tokens)
54 ))
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60
61 fn pack_json() -> String {
62 serde_json::json!({
63 "outputId": "rmx_abc123",
64 "directoryStructure": "src/\n main.rs\n lib.rs\n",
65 "totalFiles": 2,
66 "totalTokens": 1234
67 })
68 .to_string()
69 }
70
71 #[test]
72 fn ignores_non_pack_tools() {
73 assert!(transform("repomix", "grep_repomix_output", &pack_json(), 2000).is_none());
74 }
75
76 #[test]
77 fn ignores_non_json() {
78 assert!(transform("repomix", "pack_codebase", "not json", 2000).is_none());
79 }
80
81 #[test]
82 fn pack_becomes_handle_with_outputid() {
83 let _lock = crate::core::data_dir::test_env_lock();
84 let tmp = tempfile::tempdir().unwrap();
85 crate::test_env::set_var("LEAN_CTX_DATA_DIR", tmp.path());
86 crate::test_env::set_var("LEAN_CTX_ARCHIVE", "1");
87
88 let out = transform("repomix", "pack_codebase", &pack_json(), 2000).expect("transform");
89 assert!(out.contains("rmx_abc123"), "surfaces repomix outputId");
90 assert!(
91 out.contains("ctx_expand"),
92 "offers lean-ctx retrieval handle"
93 );
94 assert!(
95 out.contains("directoryStructure"),
96 "keeps the structure summary"
97 );
98
99 let again = transform("repomix", "pack_codebase", &pack_json(), 2000).unwrap();
101 assert_eq!(out, again);
102
103 crate::test_env::remove_var("LEAN_CTX_ARCHIVE");
104 crate::test_env::remove_var("LEAN_CTX_DATA_DIR");
105 }
106}