Coverage for src / osiris_cli / context_manager.py: 0%
87 statements
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
« prev ^ index » next coverage.py v7.13.0, created at 2025-12-27 17:41 +0200
1#!/usr/bin/env python3
2"""
3Context File Manager for Osiris CLI
4Automatically loads and manages project context files
5"""
7from pathlib import Path
8from typing import List, Dict, Optional
9from rich.console import Console
10import fnmatch
12console = Console()
15class ContextManager:
16 """Manage project context files"""
18 def __init__(self):
19 self.contexts: Dict[str, str] = {}
20 self.auto_load_patterns = [
21 "OSIRIS.md",
22 "README.md",
23 ".osiris/context.md",
24 "docs/CONTEXT.md",
25 ]
27 def auto_load(self, directory: Path = None) -> int:
28 """Auto-load context files from directory"""
29 if directory is None:
30 directory = Path.cwd()
32 loaded = 0
34 # Try each pattern
35 for pattern in self.auto_load_patterns:
36 file_path = directory / pattern
37 if file_path.exists() and file_path.is_file():
38 try:
39 with open(file_path, 'r') as f:
40 content = f.read()
41 self.contexts[str(file_path)] = content
42 console.print(f"[green]✓[/green] Loaded context: {file_path.name}")
43 loaded += 1
44 except Exception as e:
45 console.print(f"[yellow]Warning: Could not load {file_path}: {e}[/yellow]")
47 return loaded
49 def add_file(self, file_path: Path) -> bool:
50 """Add a specific context file"""
51 if not file_path.exists():
52 console.print(f"[red]Error: File not found: {file_path}[/red]")
53 return False
55 try:
56 with open(file_path, 'r') as f:
57 content = f.read()
58 self.contexts[str(file_path)] = content
59 console.print(f"[green]✓[/green] Added context: {file_path.name}")
60 return True
61 except Exception as e:
62 console.print(f"[red]Error loading {file_path}: {e}[/red]")
63 return False
65 def add_directory(self, directory: Path, pattern: str = "*.md") -> int:
66 """Add all matching files from directory"""
67 loaded = 0
69 for file_path in directory.rglob(pattern):
70 if file_path.is_file():
71 if self.add_file(file_path):
72 loaded += 1
74 return loaded
76 def remove(self, file_path: str) -> bool:
77 """Remove a context file"""
78 if file_path in self.contexts:
79 del self.contexts[file_path]
80 console.print(f"[green]✓[/green] Removed context: {Path(file_path).name}")
81 return True
82 else:
83 console.print(f"[yellow]Context not found: {file_path}[/yellow]")
84 return False
86 def clear(self):
87 """Clear all contexts"""
88 self.contexts.clear()
89 console.print("[green]✓[/green] All contexts cleared")
91 def list_contexts(self) -> List[str]:
92 """List all loaded contexts"""
93 return list(self.contexts.keys())
95 def get_combined_context(self) -> str:
96 """Get all contexts combined"""
97 if not self.contexts:
98 return ""
100 combined = "# Project Context\n\n"
102 for file_path, content in self.contexts.items():
103 file_name = Path(file_path).name
104 combined += f"## {file_name}\n\n{content}\n\n"
106 return combined
108 def get_context_summary(self) -> str:
109 """Get summary of loaded contexts"""
110 if not self.contexts:
111 return "No context files loaded"
113 total_chars = sum(len(content) for content in self.contexts.values())
114 total_lines = sum(content.count('\n') for content in self.contexts.values())
116 summary = f"{len(self.contexts)} file(s) loaded\n"
117 summary += f"Total: {total_chars:,} characters, {total_lines:,} lines\n\n"
119 for file_path in self.contexts.keys():
120 file_name = Path(file_path).name
121 char_count = len(self.contexts[file_path])
122 summary += f" • {file_name} ({char_count:,} chars)\n"
124 return summary
126 def search(self, query: str) -> List[tuple]:
127 """Search across all contexts"""
128 results = []
129 query_lower = query.lower()
131 for file_path, content in self.contexts.items():
132 lines = content.split('\n')
133 for i, line in enumerate(lines, 1):
134 if query_lower in line.lower():
135 results.append((Path(file_path).name, i, line.strip()))
137 return results
140# Global context manager
141context_manager = ContextManager()