Coverage for session_mgmt_mcp/core/session_manager.py: 0.00%

161 statements  

« prev     ^ index     » next       coverage.py v7.10.6, created at 2025-09-01 05:22 -0700

1#!/usr/bin/env python3 

2"""Session lifecycle management for session-mgmt-mcp. 

3 

4This module handles session initialization, quality assessment, checkpoints, 

5and cleanup operations. 

6""" 

7 

8import os 

9import shutil 

10from datetime import datetime 

11from pathlib import Path 

12from typing import Any 

13 

14from session_mgmt_mcp.utils.git_operations import ( 

15 create_checkpoint_commit, 

16 is_git_repository, 

17) 

18from session_mgmt_mcp.utils.logging import get_session_logger 

19 

20 

21class SessionLifecycleManager: 

22 """Manages session lifecycle operations.""" 

23 

24 def __init__(self) -> None: 

25 self.logger = get_session_logger() 

26 self.current_project: str | None = None 

27 

28 async def calculate_quality_score(self) -> dict[str, Any]: 

29 """Calculate session quality score based on multiple factors.""" 

30 current_dir = Path(os.environ.get("PWD", Path.cwd())) 

31 

32 # Project health indicators (40% of score) 

33 project_context = await self.analyze_project_context(current_dir) 

34 project_score = ( 

35 sum(1 for detected in project_context.values() if detected) 

36 / len(project_context) 

37 ) * 40 

38 

39 # Permissions health (20% of score) - placeholder for now 

40 permissions_score = 10 # Basic score until permissions system is integrated 

41 

42 # Session management availability (20% of score) 

43 session_score = 20 # Always available in this refactored version 

44 

45 # Tool availability (20% of score) 

46 uv_available = shutil.which("uv") is not None 

47 tool_score = 20 if uv_available else 10 

48 

49 total_score = int( 

50 project_score + permissions_score + session_score + tool_score, 

51 ) 

52 

53 return { 

54 "total_score": total_score, 

55 "breakdown": { 

56 "project_health": project_score, 

57 "permissions": permissions_score, 

58 "session_management": session_score, 

59 "tools": tool_score, 

60 }, 

61 "recommendations": self._generate_quality_recommendations( 

62 total_score, 

63 project_context, 

64 uv_available, 

65 ), 

66 } 

67 

68 def _generate_quality_recommendations( 

69 self, 

70 score: int, 

71 project_context: dict, 

72 uv_available: bool, 

73 ) -> list[str]: 

74 """Generate quality improvement recommendations based on score factors.""" 

75 recommendations = [] 

76 

77 if score < 50: 

78 recommendations.append( 

79 "Session needs attention - multiple areas for improvement", 

80 ) 

81 

82 if not project_context.get("has_pyproject_toml", False): 

83 recommendations.append( 

84 "Consider adding pyproject.toml for modern Python project structure", 

85 ) 

86 

87 if not project_context.get("has_git_repo", False): 

88 recommendations.append("Initialize git repository for version control") 

89 

90 if not uv_available: 

91 recommendations.append( 

92 "Install UV package manager for improved dependency management", 

93 ) 

94 

95 if not project_context.get("has_tests", False): 

96 recommendations.append("Add test suite to improve code quality") 

97 

98 if score >= 80: 

99 recommendations.append("Excellent session setup! Keep up the good work.") 

100 elif score >= 60: 

101 recommendations.append("Good session quality with room for optimization.") 

102 

103 return recommendations[:5] # Limit to top 5 recommendations 

104 

105 async def analyze_project_context(self, project_dir: Path) -> dict[str, bool]: 

106 """Analyze project directory for common indicators and patterns.""" 

107 indicators = { 

108 "has_pyproject_toml": (project_dir / "pyproject.toml").exists(), 

109 "has_setup_py": (project_dir / "setup.py").exists(), 

110 "has_requirements_txt": (project_dir / "requirements.txt").exists(), 

111 "has_readme": any( 

112 (project_dir / name).exists() 

113 for name in ["README.md", "README.rst", "README.txt", "readme.md"] 

114 ), 

115 "has_git_repo": is_git_repository(project_dir), 

116 "has_venv": any( 

117 (project_dir / name).exists() 

118 for name in [".venv", "venv", ".env", "env"] 

119 ), 

120 "has_tests": any( 

121 (project_dir / name).exists() for name in ["tests", "test", "testing"] 

122 ), 

123 "has_src_structure": (project_dir / "src").exists(), 

124 "has_docs": any( 

125 (project_dir / name).exists() for name in ["docs", "documentation"] 

126 ), 

127 "has_ci_cd": any( 

128 (project_dir / name).exists() 

129 for name in [".github", ".gitlab-ci.yml", ".travis.yml", "Jenkinsfile"] 

130 ), 

131 } 

132 

133 # Additional context from file patterns 

134 try: 

135 python_files = list(project_dir.glob("**/*.py")) 

136 indicators["has_python_files"] = len(python_files) > 0 

137 

138 # Check for common Python frameworks 

139 for py_file in python_files[:10]: # Sample first 10 files 

140 try: 

141 with open(py_file, encoding="utf-8") as f: 

142 content = f.read(1000) # Read first 1000 chars 

143 if "import fastapi" in content or "from fastapi" in content: 

144 indicators["uses_fastapi"] = True 

145 if "import django" in content or "from django" in content: 

146 indicators["uses_django"] = True 

147 if "import flask" in content or "from flask" in content: 

148 indicators["uses_flask"] = True 

149 except (UnicodeDecodeError, PermissionError): 

150 continue 

151 

152 except Exception as e: 

153 self.logger.warning(f"Error analyzing Python files: {e}") 

154 

155 return indicators 

156 

157 async def perform_quality_assessment(self) -> tuple[int, dict]: 

158 """Perform quality assessment and return score and data.""" 

159 quality_data = await self.calculate_quality_score() 

160 quality_score = quality_data["total_score"] 

161 return quality_score, quality_data 

162 

163 def format_quality_results( 

164 self, 

165 quality_score: int, 

166 quality_data: dict, 

167 checkpoint_result: dict | None = None, 

168 ) -> list[str]: 

169 """Format quality assessment results for display.""" 

170 output = [] 

171 

172 # Quality status 

173 if quality_score >= 80: 

174 output.append(f"✅ Session quality: EXCELLENT (Score: {quality_score}/100)") 

175 elif quality_score >= 60: 

176 output.append(f"✅ Session quality: GOOD (Score: {quality_score}/100)") 

177 else: 

178 output.append( 

179 f"⚠️ Session quality: NEEDS ATTENTION (Score: {quality_score}/100)", 

180 ) 

181 

182 # Quality breakdown 

183 output.append("\n📈 Quality breakdown:") 

184 breakdown = quality_data["breakdown"] 

185 output.append(f" • Project health: {breakdown['project_health']:.1f}/40") 

186 output.append(f" • Permissions: {breakdown['permissions']:.1f}/20") 

187 output.append(f" • Session tools: {breakdown['session_management']:.1f}/20") 

188 output.append(f" • Tool availability: {breakdown['tools']:.1f}/20") 

189 

190 # Recommendations 

191 recommendations = quality_data["recommendations"] 

192 if recommendations: 

193 output.append("\n💡 Recommendations:") 

194 for rec in recommendations[:3]: 

195 output.append(f"{rec}") 

196 

197 # Session management specific results 

198 if checkpoint_result: 

199 strengths = checkpoint_result.get("strengths", []) 

200 if strengths: 

201 output.append("\n🌟 Session strengths:") 

202 for strength in strengths[:3]: 

203 output.append(f"{strength}") 

204 

205 session_stats = checkpoint_result.get("session_stats", {}) 

206 if session_stats: 

207 output.append("\n⏱️ Session progress:") 

208 output.append( 

209 f" • Duration: {session_stats.get('duration_minutes', 0)} minutes", 

210 ) 

211 output.append( 

212 f" • Checkpoints: {session_stats.get('total_checkpoints', 0)}", 

213 ) 

214 output.append( 

215 f" • Success rate: {session_stats.get('success_rate', 0):.1f}%", 

216 ) 

217 

218 return output 

219 

220 async def perform_git_checkpoint( 

221 self, 

222 current_dir: Path, 

223 quality_score: int, 

224 ) -> list[str]: 

225 """Handle git operations for checkpoint commit using the new git utilities.""" 

226 output = [] 

227 output.append("\n" + "=" * 50) 

228 output.append("📦 Git Checkpoint Commit") 

229 output.append("=" * 50) 

230 

231 try: 

232 # Use the new git utilities 

233 success, result, git_output = create_checkpoint_commit( 

234 current_dir, 

235 self.current_project or "Unknown", 

236 quality_score, 

237 ) 

238 

239 output.extend(git_output) 

240 

241 if success and result != "clean": 

242 self.logger.info( 

243 "Checkpoint commit created", 

244 project=self.current_project, 

245 commit_hash=result, 

246 quality_score=quality_score, 

247 ) 

248 

249 except Exception as e: 

250 output.append(f"\n⚠️ Git operations error: {e}") 

251 self.logger.exception( 

252 "Git checkpoint error occurred", 

253 error=str(e), 

254 project=self.current_project, 

255 ) 

256 

257 return output 

258 

259 async def initialize_session( 

260 self, 

261 working_directory: str | None = None, 

262 ) -> dict[str, Any]: 

263 """Initialize a new session with comprehensive setup.""" 

264 try: 

265 # Set working directory 

266 if working_directory: 

267 os.chdir(working_directory) 

268 

269 current_dir = Path.cwd() 

270 self.current_project = current_dir.name 

271 

272 # Create .claude directory structure 

273 claude_dir = Path.home() / ".claude" 

274 claude_dir.mkdir(exist_ok=True) 

275 (claude_dir / "data").mkdir(exist_ok=True) 

276 (claude_dir / "logs").mkdir(exist_ok=True) 

277 

278 # Analyze project context 

279 project_context = await self.analyze_project_context(current_dir) 

280 quality_score, quality_data = await self.perform_quality_assessment() 

281 

282 self.logger.info( 

283 "Session initialized", 

284 project=self.current_project, 

285 quality_score=quality_score, 

286 working_directory=str(current_dir), 

287 ) 

288 

289 return { 

290 "success": True, 

291 "project": self.current_project, 

292 "working_directory": str(current_dir), 

293 "quality_score": quality_score, 

294 "quality_data": quality_data, 

295 "project_context": project_context, 

296 "claude_directory": str(claude_dir), 

297 } 

298 

299 except Exception as e: 

300 self.logger.exception("Session initialization failed", error=str(e)) 

301 return {"success": False, "error": str(e)} 

302 

303 async def checkpoint_session(self) -> dict[str, Any]: 

304 """Perform a comprehensive session checkpoint.""" 

305 try: 

306 current_dir = Path.cwd() 

307 self.current_project = current_dir.name 

308 

309 # Quality assessment 

310 quality_score, quality_data = await self.perform_quality_assessment() 

311 

312 # Git checkpoint 

313 git_output = await self.perform_git_checkpoint(current_dir, quality_score) 

314 

315 # Format results 

316 quality_output = self.format_quality_results(quality_score, quality_data) 

317 

318 self.logger.info( 

319 "Session checkpoint completed", 

320 project=self.current_project, 

321 quality_score=quality_score, 

322 ) 

323 

324 return { 

325 "success": True, 

326 "quality_score": quality_score, 

327 "quality_output": quality_output, 

328 "git_output": git_output, 

329 "timestamp": datetime.now().isoformat(), 

330 } 

331 

332 except Exception as e: 

333 self.logger.exception("Session checkpoint failed", error=str(e)) 

334 return {"success": False, "error": str(e)} 

335 

336 async def end_session(self) -> dict[str, Any]: 

337 """End the current session with cleanup and summary.""" 

338 try: 

339 current_dir = Path.cwd() 

340 self.current_project = current_dir.name 

341 

342 # Final quality assessment 

343 quality_score, quality_data = await self.perform_quality_assessment() 

344 

345 # Create session summary 

346 summary = { 

347 "project": self.current_project, 

348 "final_quality_score": quality_score, 

349 "session_end_time": datetime.now().isoformat(), 

350 "working_directory": str(current_dir), 

351 "recommendations": quality_data.get("recommendations", []), 

352 } 

353 

354 self.logger.info( 

355 "Session ended", 

356 project=self.current_project, 

357 final_quality_score=quality_score, 

358 ) 

359 

360 return {"success": True, "summary": summary} 

361 

362 except Exception as e: 

363 self.logger.exception("Session end failed", error=str(e)) 

364 return {"success": False, "error": str(e)} 

365 

366 async def get_session_status( 

367 self, 

368 working_directory: str | None = None, 

369 ) -> dict[str, Any]: 

370 """Get current session status and health information.""" 

371 try: 

372 current_dir = Path(working_directory) if working_directory else Path.cwd() 

373 

374 self.current_project = current_dir.name 

375 

376 # Get comprehensive status 

377 project_context = await self.analyze_project_context(current_dir) 

378 quality_score, quality_data = await self.perform_quality_assessment() 

379 

380 # Check system health 

381 uv_available = shutil.which("uv") is not None 

382 git_available = is_git_repository(current_dir) 

383 claude_dir = Path.home() / ".claude" 

384 claude_dir_exists = claude_dir.exists() 

385 

386 return { 

387 "success": True, 

388 "project": self.current_project, 

389 "working_directory": str(current_dir), 

390 "quality_score": quality_score, 

391 "quality_breakdown": quality_data["breakdown"], 

392 "recommendations": quality_data["recommendations"], 

393 "project_context": project_context, 

394 "system_health": { 

395 "uv_available": uv_available, 

396 "git_repository": git_available, 

397 "claude_directory": claude_dir_exists, 

398 }, 

399 "timestamp": datetime.now().isoformat(), 

400 } 

401 

402 except Exception as e: 

403 self.logger.exception("Failed to get session status", error=str(e)) 

404 return {"success": False, "error": str(e)}