1use anyhow::Result;
6use regex::Regex;
7use serde_json::Value;
8use std::env;
9use std::fs;
10use std::io::{self, Read};
11use std::path::PathBuf;
12use std::process::Command;
13
14pub async fn handle_user_prompt_submit() -> Result<()> {
16 let mut input = String::new();
18 io::stdin().read_to_string(&mut input)?;
19
20 let json: Value = serde_json::from_str(&input)
22 .unwrap_or_else(|_| serde_json::json!({"prompt": input.trim()}));
23
24 let user_prompt = json["prompt"].as_str().unwrap_or(&input);
25
26 eprintln!("DEBUG: user_prompt length = {}", user_prompt.len());
28 eprintln!(
29 "DEBUG: user_prompt preview = {:?}",
30 &user_prompt.chars().take(100).collect::<String>()
31 );
32
33 println!("=== Smart Tree Context Intelligence ===");
35 println!();
36
37 let paths = extract_paths_from_prompt(user_prompt);
39 if !paths.is_empty() {
40 analyze_paths(&paths)?;
41 }
42
43 let project_keywords = extract_project_keywords(user_prompt);
45 if !project_keywords.is_empty() {
46 search_mem8_context(&project_keywords)?;
47 }
48
49 provide_current_context(user_prompt)?;
51
52 provide_topic_context(user_prompt)?;
54
55 if detect_code_intent(user_prompt) {
57 show_recent_changes()?;
58 }
59
60 println!("=== End Context ===");
61 println!();
62
63 store_conversation_in_mem8(user_prompt)?;
65
66 Ok(())
67}
68
69fn extract_paths_from_prompt(prompt: &str) -> Vec<PathBuf> {
71 let path_regex = Regex::new(
72 r"(/[a-zA-Z0-9_/.~-]+|~/[a-zA-Z0-9_/.~-]+|\./[a-zA-Z0-9_/.~-]+|[a-zA-Z0-9_-]+\.[a-zA-Z]{2,4})"
73 ).unwrap();
74
75 path_regex
76 .find_iter(prompt)
77 .filter_map(|m| {
78 let path_str = m.as_str();
79
80 let expanded = if path_str.starts_with('~') {
82 if let Some(home) = dirs::home_dir() {
83 home.join(&path_str[2..])
84 } else {
85 PathBuf::from(path_str)
86 }
87 } else {
88 PathBuf::from(path_str)
89 };
90
91 if expanded.exists() {
93 Some(expanded)
94 } else {
95 let current = env::current_dir().ok()?;
97 let relative = current.join(path_str);
98 if relative.exists() {
99 Some(relative)
100 } else {
101 None
102 }
103 }
104 })
105 .take(5)
106 .collect()
107}
108
109fn analyze_paths(paths: &[PathBuf]) -> Result<()> {
111 println!("### 📁 Path Analysis");
112
113 for path in paths {
114 if path.is_dir() {
115 println!("\n**Directory**: `{}`", path.display());
116
117 let output = Command::new("st")
119 .args(["--mode", "summary-ai", "--depth", "2"])
120 .arg(path)
121 .output();
122
123 if let Ok(output) = output {
124 if output.status.success() {
125 let tree = String::from_utf8_lossy(&output.stdout);
126 for (i, line) in tree.lines().take(20).enumerate() {
128 if i == 0 {
129 println!("```");
130 }
131 println!("{}", line);
132 }
133 println!("```");
134 }
135 }
136 } else if path.is_file() {
137 let size = fs::metadata(path).map(|m| m.len()).unwrap_or(0);
138 println!("\n**File**: `{}` ({} bytes)", path.display(), size);
139
140 if let Some(ext) = path.extension() {
142 if matches!(ext.to_str(), Some("rs" | "py" | "js" | "ts" | "go")) {
143 let output = Command::new("st")
144 .args(["--mode", "function-markdown"])
145 .arg(path)
146 .output();
147
148 if let Ok(output) = output {
149 if output.status.success() {
150 let functions = String::from_utf8_lossy(&output.stdout);
151 println!("Functions:");
152 for line in functions.lines().take(10) {
153 println!(" {}", line);
154 }
155 }
156 }
157 }
158 }
159 }
160 }
161
162 println!();
163 Ok(())
164}
165
166fn extract_project_keywords(prompt: &str) -> Vec<String> {
168 let keywords_regex = Regex::new(
169 r"(?i)(mem8|smart-tree|smart tree|qdrant|ayeverse|g8t|marqant|aye|bitnet|termust|wave compass|quantum)"
170 ).unwrap();
171
172 keywords_regex
173 .find_iter(prompt)
174 .map(|m| m.as_str().to_lowercase())
175 .collect::<std::collections::HashSet<_>>()
176 .into_iter()
177 .collect()
178}
179
180fn search_mem8_context(keywords: &[String]) -> Result<()> {
182 use std::collections::HashSet;
183
184 println!("### 🧠 MEM8 Context");
185
186 let mut found_any = false;
187
188 if let Some(home) = dirs::home_dir() {
190 let conversations_dir = home.join(".mem8").join("conversations");
191
192 if conversations_dir.exists() {
193 let mut matched_files = HashSet::new();
194
195 if let Ok(entries) = fs::read_dir(&conversations_dir) {
197 for entry in entries.flatten() {
198 if let Some(name) = entry.path().file_name() {
199 let name_str = name.to_string_lossy().to_lowercase();
200
201 for keyword in keywords {
203 if name_str.contains(&keyword.to_lowercase()) {
204 matched_files.insert(entry.path());
205 break;
206 }
207 }
208 }
209 }
210 }
211
212 if !matched_files.is_empty() {
213 println!("\n**Recent conversations:**");
214 for path in matched_files.iter().take(3) {
215 if let Some(name) = path.file_stem() {
216 println!(" • {}", name.to_string_lossy());
217 found_any = true;
218 }
219 }
220 }
221 }
222
223 let anchors_path = home.join(".mem8").join("memory_anchors.json");
225 if anchors_path.exists() {
226 if let Ok(contents) = fs::read_to_string(&anchors_path) {
228 if let Ok(json) = serde_json::from_str::<Value>(&contents) {
229 if let Some(anchors) = json.as_array() {
230 let mut found_memories = Vec::new();
231
232 for anchor in anchors {
233 if let Some(context) = anchor["context"].as_str() {
234 let context_lower = context.to_lowercase();
235
236 for keyword in keywords {
237 if context_lower.contains(&keyword.to_lowercase()) {
238 found_memories.push((
239 anchor["anchor_type"].as_str().unwrap_or("unknown"),
240 context,
241 keyword.as_str(),
242 ));
243 break;
244 }
245 }
246 }
247 }
248
249 if !found_memories.is_empty() {
250 println!("\n**Anchored memories:**");
251 for (anchor_type, context, keyword) in found_memories.iter().take(3) {
252 let preview: String = context.chars().take(80).collect();
253 println!(" • [{}] {}: {}...", keyword, anchor_type, preview);
254 found_any = true;
255 }
256 }
257 }
258 }
259 }
260 }
261 }
262
263 if !found_any {
264 println!("\nNo specific memories found. Consider anchoring important context with:");
265 println!("`st --memory-anchor <type> <keywords> <context>`");
266 }
267
268 println!();
269 Ok(())
270}
271
272fn provide_current_context(prompt: &str) -> Result<()> {
274 if !prompt.contains("pwd") && !prompt.contains("./") && !prompt.contains("current") {
276 let current_dir = env::current_dir()?;
277
278 if current_dir.join(".git").exists() {
280 println!("### 📍 Current Repository Context");
281
282 let branch_output = Command::new("git")
284 .args(["branch", "--show-current"])
285 .output();
286
287 if let Ok(output) = branch_output {
288 if output.status.success() {
289 let branch = String::from_utf8_lossy(&output.stdout).trim().to_string();
290 println!("**Branch**: `{}`", branch);
291 }
292 }
293
294 let commit_output = Command::new("git")
296 .args(["log", "-1", "--oneline"])
297 .output();
298
299 if let Ok(output) = commit_output {
300 if output.status.success() {
301 let commit = String::from_utf8_lossy(&output.stdout).trim().to_string();
302 println!("**Last commit**: {}", commit);
303 }
304 }
305
306 let tree_output = Command::new("st")
308 .args(["--mode", "git-status", "--depth", "1"])
309 .arg(".")
310 .output();
311
312 if let Ok(output) = tree_output {
313 if output.status.success() {
314 let tree = String::from_utf8_lossy(&output.stdout);
315 println!("\n**File status:**");
316 println!("```");
317 for line in tree.lines().take(15) {
318 println!("{}", line);
319 }
320 println!("```");
321 }
322 }
323
324 println!();
325 }
326 }
327
328 Ok(())
329}
330
331fn provide_topic_context(prompt: &str) -> Result<()> {
333 let lower = prompt.to_lowercase();
334
335 if lower.contains("wave")
337 || lower.contains("compass")
338 || lower.contains("signature")
339 || lower.contains("dashboard")
340 {
341 println!("### 🌊 Wave Signature & Dashboard");
342 println!("- **Quantum signatures**: `src/quantum_wave_signature.rs`");
343 println!(
344 "- **Web dashboard**: `src/web_dashboard/` (browser-based terminal + file browser)"
345 );
346 println!("- **4.3 billion unique states** via 32-bit encoding");
347 println!("- **Real PTY terminal** with vim/htop support");
348 println!();
349 }
350
351 if lower.contains("termust")
353 || lower.contains("oxidation")
354 || lower.contains("rust") && lower.contains("file")
355 {
356 println!("### 🦀 Termust - File Oxidation");
357 println!("- **Main**: `/aidata/ayeverse/termust/`");
358 println!("- **Oxidation engine**: `termust/src/oxidation.rs`");
359 println!("- **Horse apples**: `termust/src/horse_apples.rs`");
360 println!("- **Jerry Maguire mode**: SHOW ME THE MONEY!");
361 println!();
362 }
363
364 if lower.contains("mem8") || lower.contains("memory") || lower.contains("consciousness") {
366 println!("### 🧠 MEM8 System");
367 println!("- **Binary format**: `src/mem8_binary.rs`");
368 println!("- **Format converter**: `src/m8_format_converter.rs`");
369 println!("- **Consciousness**: `src/m8_consciousness.rs`");
370 println!("- **973x faster** than traditional vector stores");
371 println!("- **Wave-based** with 44.1kHz consciousness sampling");
372 println!();
373 }
374
375 Ok(())
376}
377
378fn show_recent_changes() -> Result<()> {
380 let output = Command::new("git")
381 .args(["log", "--oneline", "-5"])
382 .output();
383
384 if let Ok(output) = output {
385 if output.status.success() {
386 let log = String::from_utf8_lossy(&output.stdout);
387 if !log.trim().is_empty() {
388 println!("### 📝 Recent Changes");
389 println!("```");
390 for line in log.lines() {
391 println!("{}", line);
392 }
393 println!("```");
394 println!();
395 }
396 }
397 }
398
399 Ok(())
400}
401
402fn detect_code_intent(prompt: &str) -> bool {
404 let code_words = [
405 "code",
406 "function",
407 "implement",
408 "fix",
409 "bug",
410 "error",
411 "compile",
412 "build",
413 "test",
414 "refactor",
415 "optimize",
416 "method",
417 "class",
418 "struct",
419 "trait",
420 "module",
421 "import",
422 "syntax",
423 "debug",
424 "breakpoint",
425 "variable",
426 "type",
427 ];
428
429 let lower = prompt.to_lowercase();
430 code_words.iter().any(|&word| lower.contains(word))
431}
432
433fn store_conversation_in_mem8(user_prompt: &str) -> Result<()> {
436 if user_prompt.trim().is_empty() {
438 return Ok(());
439 }
440
441 let payload = serde_json::json!({
443 "role": "user",
444 "content": user_prompt,
445 "metadata": {
446 "source": "smart-tree-hook",
447 "timestamp": chrono::Utc::now().to_rfc3339(),
448 "project_dir": env::var("CLAUDE_PROJECT_DIR").ok(),
449 "working_dir": env::current_dir().ok().map(|p| p.display().to_string()),
450 }
451 });
452
453 let client = reqwest::blocking::Client::builder()
455 .timeout(std::time::Duration::from_secs(2))
456 .build()?;
457
458 match client
459 .post("http://localhost:8425/api/mem8/conversation")
460 .header("Content-Type", "application/json")
461 .json(&payload)
462 .send()
463 {
464 Ok(response) if response.status().is_success() => {
465 Ok(())
467 }
468 Ok(response) => {
469 eprintln!("⚠️ MEM8 storage warning: HTTP {}", response.status());
471 Ok(())
472 }
473 Err(e) => {
474 eprintln!("⚠️ MEM8 storage skipped: {}", e);
476 Ok(())
477 }
478 }
479}