Skip to main content
Glama
by 8b-is
st_context_aware.rs18.5 kB
// ST Context-Aware System - The helper who knows what you need! // "Like a good roadie who hands you the right guitar at the right time" - The Cheet 🎸 use anyhow::{Context, Result}; use serde::{Deserialize, Serialize}; use std::collections::{HashMap, VecDeque}; use std::path::{Path, PathBuf}; use std::sync::{Arc, RwLock}; use std::time::SystemTime; /// Context types that ST tracks #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum WorkContext { /// Writing new code Coding { language: String, focus_file: PathBuf, }, /// Debugging/fixing issues Debugging { error_pattern: String, files: Vec<PathBuf>, }, /// Refactoring code Refactoring { pattern: String, scope: PathBuf }, /// Exploring/understanding codebase Exploring { depth: usize, areas_visited: Vec<PathBuf>, }, /// Testing/validation Testing { test_files: Vec<PathBuf>, target_files: Vec<PathBuf>, }, /// Documentation Documenting { doc_type: String, target: PathBuf }, /// Performance optimization Optimizing { metrics: Vec<String>, hotspots: Vec<PathBuf>, }, /// Searching for something specific Hunting { query: String, found_locations: Vec<PathBuf>, }, /// Building/compilation Building { build_system: String, targets: Vec<String>, }, /// Git operations VersionControl { operation: String, changed_files: Vec<PathBuf>, }, } /// A single operation in context #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ContextualOperation { pub timestamp: SystemTime, pub operation: String, pub path: PathBuf, pub result_summary: String, pub context_hints: Vec<String>, } /// Smart context tracker #[derive(Debug, Clone)] pub struct StContextTracker { /// Current work context current_context: Arc<RwLock<Option<WorkContext>>>, /// Recent operations (last 50) operation_history: Arc<RwLock<VecDeque<ContextualOperation>>>, /// Pattern recognition cache #[allow(dead_code)] patterns: Arc<RwLock<HashMap<String, Vec<String>>>>, /// Project-specific knowledge project_knowledge: Arc<RwLock<ProjectKnowledge>>, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct ProjectKnowledge { /// Key files in the project pub key_files: Vec<PathBuf>, /// Common search patterns pub common_searches: HashMap<String, usize>, /// Frequently accessed directories pub hot_directories: HashMap<PathBuf, usize>, /// Known build commands pub build_commands: Vec<String>, /// Test patterns pub test_patterns: Vec<String>, /// Documentation locations pub doc_locations: Vec<PathBuf>, } impl Default for StContextTracker { fn default() -> Self { Self::new() } } impl StContextTracker { pub fn new() -> Self { Self { current_context: Arc::new(RwLock::new(None)), operation_history: Arc::new(RwLock::new(VecDeque::with_capacity(50))), patterns: Arc::new(RwLock::new(HashMap::new())), project_knowledge: Arc::new(RwLock::new(ProjectKnowledge::default())), } } /// Analyze recent operations to determine context pub fn analyze_context(&self) -> Result<WorkContext> { let history = self.operation_history.read().unwrap(); if history.is_empty() { return Ok(WorkContext::Exploring { depth: 3, areas_visited: vec![], }); } // Look at recent operations let recent_ops: Vec<_> = history.iter().take(10).collect(); // Count operation types let mut search_count = 0; let mut edit_count = 0; let mut read_count = 0; let mut test_count = 0; let mut _build_count = 0; for op in &recent_ops { if op.operation.contains("search") || op.operation.contains("grep") { search_count += 1; } if op.operation.contains("edit") || op.operation.contains("write") { edit_count += 1; } if op.operation.contains("read") || op.operation.contains("view") { read_count += 1; } if op.path.to_string_lossy().contains("test") { test_count += 1; } if op.operation.contains("build") || op.operation.contains("compile") { _build_count += 1; } } // Determine context based on patterns if search_count >= 3 { // Multiple searches = hunting for something let query = recent_ops .iter() .find(|op| op.operation.contains("search")) .map(|op| op.operation.clone()) .unwrap_or_default(); Ok(WorkContext::Hunting { query, found_locations: vec![], }) } else if edit_count >= 2 && test_count >= 1 { // Edits + tests = active development let language = Self::detect_language(&recent_ops[0].path); Ok(WorkContext::Coding { language, focus_file: recent_ops[0].path.clone(), }) } else if test_count >= 2 { // Lots of test activity Ok(WorkContext::Testing { test_files: recent_ops .iter() .filter(|op| op.path.to_string_lossy().contains("test")) .map(|op| op.path.clone()) .collect(), target_files: vec![], }) } else if read_count >= 4 { // Lots of reading = exploring Ok(WorkContext::Exploring { depth: 5, areas_visited: recent_ops.iter().map(|op| op.path.clone()).collect(), }) } else { // Default to exploring Ok(WorkContext::Exploring { depth: 3, areas_visited: vec![], }) } } /// Get smart suggestions based on context pub fn get_suggestions(&self, _current_path: &Path) -> Vec<String> { let context = self.current_context.read().unwrap(); let knowledge = self.project_knowledge.read().unwrap(); match context.as_ref() { Some(WorkContext::Coding { language, focus_file, }) => vec![ format!( "💡 Working on {}? Try: st --mode relations --focus {}", language, focus_file.display() ), format!( "🧪 Run tests: st --search test --type {}", Self::lang_to_ext(language) ), format!( "📊 See impact: st --mode quantum-semantic {}", focus_file.parent().unwrap_or(Path::new(".")).display() ), ], Some(WorkContext::Debugging { error_pattern, .. }) => vec![ format!( "🔍 Search for error: st --search \"{}\" --mode ai", error_pattern ), format!("📈 Recent changes: st --newer-than 1 --sort newest"), format!("🌳 Check dependencies: st --mode relations"), ], Some(WorkContext::Exploring { depth, areas_visited, }) => { let mut suggestions = vec![ format!("🗺️ Get overview: st --mode summary-ai --depth {}", depth), format!("🧭 Semantic map: st --mode semantic"), ]; // Suggest unexplored areas if let Some(hot_dir) = knowledge .hot_directories .iter() .filter(|(path, _)| !areas_visited.contains(path)) .max_by_key(|(_, count)| *count) .map(|(path, _)| path) { suggestions.push(format!("🔥 Check hot area: st {}", hot_dir.display())); } suggestions } Some(WorkContext::Testing { .. }) => vec![ format!("🧪 Run all tests: st --search \"test_\" --type rs"), format!("📊 Coverage gaps: st --mode waste tests/"), format!("🔗 Test dependencies: st --mode relations --focus tests/"), ], Some(WorkContext::Hunting { query, found_locations, }) => { let mut suggestions = vec![format!( "🎯 Refine search: st --search \"{}\" --type rs", query )]; if !found_locations.is_empty() { suggestions.push(format!( "📍 Focus area: st --mode ai {}", found_locations[0].display() )); } // Suggest similar past searches if let Some(similar) = knowledge .common_searches .keys() .find(|s| s.contains(query) || query.contains(s.as_str())) { suggestions.push(format!("💭 Similar search: st --search \"{}\"", similar)); } suggestions } _ => vec![ "🌟 Quick overview: st --mode summary-ai".to_string(), "🔍 Search code: st --search \"pattern\" --type rs".to_string(), "📊 See structure: st --mode semantic".to_string(), ], } } /// Record an operation pub fn record_operation(&self, op: ContextualOperation) -> Result<()> { let mut history = self.operation_history.write().unwrap(); // Add to history history.push_front(op.clone()); if history.len() > 50 { history.pop_back(); } // Update knowledge let mut knowledge = self.project_knowledge.write().unwrap(); // Track hot directories let dir = op.path.parent().unwrap_or(&op.path); *knowledge.hot_directories.entry(dir.to_owned()).or_insert(0) += 1; // Track searches if op.operation.contains("search") { if let Some(query) = op.operation.split("search").nth(1) { let query = query.trim().to_string(); *knowledge.common_searches.entry(query).or_insert(0) += 1; } } // Update context based on new operation self.update_context()?; Ok(()) } /// Update context based on recent operations fn update_context(&self) -> Result<()> { let new_context = self.analyze_context()?; let mut current = self.current_context.write().unwrap(); *current = Some(new_context); Ok(()) } /// Get optimal ST arguments for current context pub fn get_optimal_args(&self, _base_command: &str) -> Vec<String> { let context = self.current_context.read().unwrap(); match context.as_ref() { Some(WorkContext::Coding { .. }) => { vec![ "--depth".to_string(), "5".to_string(), "--mode".to_string(), "ai".to_string(), ] } Some(WorkContext::Debugging { .. }) => { vec![ "--depth".to_string(), "0".to_string(), // Auto depth "--mode".to_string(), "ai".to_string(), "--compress".to_string(), ] // Easier to scan } Some(WorkContext::Exploring { depth, .. }) => { vec![ "--depth".to_string(), depth.to_string(), "--mode".to_string(), "semantic".to_string(), ] } Some(WorkContext::Testing { .. }) => { vec![ "--search".to_string(), "test".to_string(), "--mode".to_string(), "relations".to_string(), ] } Some(WorkContext::Hunting { .. }) => { vec![ "--mode".to_string(), "ai".to_string(), "--stream".to_string(), ] // For large searches } _ => vec!["--depth".to_string(), "0".to_string()], // Auto } } /// Detect programming language from path fn detect_language(path: &Path) -> String { match path.extension().and_then(|s| s.to_str()) { Some("rs") => "rust".to_string(), Some("py") => "python".to_string(), Some("js") | Some("jsx") => "javascript".to_string(), Some("ts") | Some("tsx") => "typescript".to_string(), Some("go") => "go".to_string(), Some("java") => "java".to_string(), Some("cpp") | Some("cc") | Some("cxx") => "cpp".to_string(), Some("c") | Some("h") => "c".to_string(), _ => "unknown".to_string(), } } fn lang_to_ext(lang: &str) -> &str { match lang { "rust" => "rs", "python" => "py", "javascript" => "js", "typescript" => "ts", "go" => "go", "java" => "java", "cpp" => "cpp", "c" => "c", _ => "*", } } /// Save context to disk pub fn save_context(&self, path: &Path) -> Result<()> { let context_file = path.join(".st_context.json"); let data = serde_json::json!({ "current_context": self.current_context.read().unwrap().clone(), "project_knowledge": self.project_knowledge.read().unwrap().clone(), "history": self.operation_history.read().unwrap().clone(), }); std::fs::write(context_file, serde_json::to_string_pretty(&data)?) .context("Failed to save context")?; Ok(()) } /// Load context from disk pub fn load_context(&self, path: &Path) -> Result<()> { let context_file = path.join(".st_context.json"); if context_file.exists() { let data = std::fs::read_to_string(context_file)?; let json: serde_json::Value = serde_json::from_str(&data)?; // Restore context if let Some(ctx) = json.get("current_context") { if let Ok(context) = serde_json::from_value::<WorkContext>(ctx.clone()) { *self.current_context.write().unwrap() = Some(context); } } // Restore knowledge if let Some(know) = json.get("project_knowledge") { if let Ok(knowledge) = serde_json::from_value::<ProjectKnowledge>(know.clone()) { *self.project_knowledge.write().unwrap() = knowledge; } } } Ok(()) } } /// Context-aware ST command builder pub struct ContextualStCommand { tracker: Arc<StContextTracker>, base_args: Vec<String>, } impl ContextualStCommand { pub fn new(tracker: Arc<StContextTracker>) -> Self { Self { tracker, base_args: vec![], } } /// Build command with context awareness pub fn build(&self, intent: &str) -> Vec<String> { let mut args = self.base_args.clone(); // Get optimal args based on context let context_args = self.tracker.get_optimal_args(intent); args.extend(context_args); // Add specific args based on intent match intent { "explore" => { if !args.contains(&"--mode".to_string()) { args.extend(vec!["--mode".to_string(), "summary-ai".to_string()]); } } "debug" => { args.extend(vec!["--compress".to_string()]); } "document" => { args.extend(vec!["--mode".to_string(), "function-markdown".to_string()]); } _ => {} } args } /// Get suggestions for next command pub fn suggest_next(&self) -> Vec<String> { self.tracker.get_suggestions(Path::new(".")) } } #[cfg(test)] mod tests { use super::*; #[test] #[ignore = "Hangs - needs investigation"] fn test_context_detection() { let tracker = StContextTracker::new(); // Simulate some operations tracker .record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: "search TODO".to_string(), path: PathBuf::from("src/main.rs"), result_summary: "Found 5 matches".to_string(), context_hints: vec!["searching".to_string()], }) .unwrap(); tracker .record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: "search FIXME".to_string(), path: PathBuf::from("src/lib.rs"), result_summary: "Found 2 matches".to_string(), context_hints: vec!["searching".to_string()], }) .unwrap(); tracker .record_operation(ContextualOperation { timestamp: SystemTime::now(), operation: "search bug".to_string(), path: PathBuf::from("tests/test.rs"), result_summary: "Found 1 match".to_string(), context_hints: vec!["searching".to_string()], }) .unwrap(); // Should detect hunting context let context = tracker.analyze_context().unwrap(); match context { WorkContext::Hunting { .. } => {} // Expected - test passes _ => panic!("Expected Hunting context"), } // Get suggestions let suggestions = tracker.get_suggestions(Path::new(".")); assert!(!suggestions.is_empty()); } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/8b-is/smart-tree'

If you have feedback or need assistance with the MCP directory API, please join our Discord server