1#!/usr/bin/env python3

2"""Quality Engine for session-mgmt-mcp.

3

4 This module contains all quality-related functions extracted from server.py

5 following Phase 2.3 of the decomposition plan. These functions handle:

6- Quality score calculation and recommendations

7- Context compaction analysis and optimization

8- Token usage and conversation flow analysis

9- Memory patterns and workflow intelligence

10- Proactive quality monitoring

11

12 All functions maintain their original signatures and behavior for backward compatibility.

13"""

14

15 from __future__ import annotations

16

17 import asyncio

18 import os

19 from contextlib import suppress

20 from datetime import datetime

21 from pathlib import Path

22 from typing import TYPE_CHECKING, Any

23

24 if TYPE_CHECKING:

25 from session_buddy.reflection_tools import ReflectionDatabase

26

27# Import utility functions

28 from session_buddy.utils.file_utils import (

29 _cleanup_session_logs,

30 _cleanup_temp_files,

31 _cleanup_uv_cache,

32)

33 from session_buddy.utils.git_utils import _optimize_git_repository

34 from session_buddy.utils.quality import (

35 check_git_activity as _check_git_activity,

36)

37 from session_buddy.utils.quality import (

38 count_significant_files as _count_significant_files,

39)

40 from session_buddy.utils.quality import (

41 create_empty_summary as _create_empty_summary,

42)

43 from session_buddy.utils.quality import (

44 ensure_summary_defaults as _ensure_summary_defaults,

45)

46 from session_buddy.utils.quality import (

47 evaluate_git_activity_heuristic as _evaluate_git_activity_heuristic,

48)

49 from session_buddy.utils.quality import (

50 evaluate_large_project_heuristic as _evaluate_large_project_heuristic,

51)

52 from session_buddy.utils.quality import (

53 evaluate_python_project_heuristic as _evaluate_python_project_heuristic,

54)

55 from session_buddy.utils.quality import (

56 get_default_compaction_reason as _get_default_compaction_reason,

57)

58 from session_buddy.utils.quality import (

59 get_error_summary as _get_error_summary,

60)

61 from session_buddy.utils.quality import (

62 get_fallback_compaction_reason as _get_fallback_compaction_reason,

63)

64 from session_buddy.utils.quality import (

65 get_fallback_summary as _get_fallback_summary,

66)

67 from session_buddy.utils.quality import (

68 process_recent_reflections as _process_recent_reflections,

69)

70 from session_buddy.utils.quality_utils import (

71 _analyze_quality_trend,

72 _extract_quality_scores,

73 _generate_quality_trend_recommendations,

74 _get_intelligence_error_result,

75 _get_time_based_recommendations,

76)

77 from session_buddy.utils.quality_utils_v2 import calculate_quality_score_v2

78 from session_buddy.utils.server_helpers import _add_current_session_context

79

80# Extracted functions (compaction, recommendations, summary helpers) have been moved

81# to session_buddy.utils.quality module for better modularity and reusability.

82

83# ======================

84# Main Quality Functions (5)

85# ======================

86

87

88 def should_suggest_compact() -> tuple[bool, str]:

89 """Determine if compacting would be beneficial and provide reasoning.

90 Returns (should_compact, reason).

91

92 Note: High complexity is necessary for comprehensive heuristic analysis

93 of project state, git activity, and development patterns.

94 """

95 # Heuristics for when compaction might be needed:

96 # 1. Large projects with many files

97 # 2. Active development (recent git activity)

98 # 3. Complex task sequences

99 # 4. Session duration indicators

100

101 try:

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

103

104 # Count significant files in project as a complexity indicator

105 file_count = _count_significant_files(current_dir)

106

107 # Large project heuristic

108 should_compact, reason = _evaluate_large_project_heuristic(file_count)

109 if should_compact:

110 return should_compact, reason

111

112 # Check for active development via git

113 git_activity = _check_git_activity(current_dir)

114 should_compact, reason = _evaluate_git_activity_heuristic(git_activity)

115 if should_compact: 115 ↛ 119 line 115 didn't jump to line 119 because the condition on line 115 was always true

116 return should_compact, reason

117

118 # Check for common patterns suggesting complex session

119 should_compact, reason = _evaluate_python_project_heuristic(current_dir)

120 if should_compact:

121 return should_compact, reason

122

123 # Default to not suggesting unless we have clear indicators

124 return False, _get_default_compaction_reason()

125

126 except Exception:

127 # If we can't determine, err on the side of suggesting compaction for safety

128 return True, _get_fallback_compaction_reason()

129

130

131 async def _optimize_reflection_database() -> str:

132 """Optimize the reflection database."""

133 try:

134 from session_buddy.reflection_tools import get_reflection_database

135

136 db = await get_reflection_database()

137 await db.get_stats()

138 db_size_before = (

139 Path(db.db_path).stat().st_size if Path(db.db_path).exists() else 0

140 )

141

142 if db.conn: 142 ↛ 155 line 142 didn't jump to line 155 because the condition on line 142 was always true

143 conn = (

144 db.conn

145 ) # Capture the connection to help mypy understand it's not None

146 await asyncio.get_event_loop().run_in_executor(

147 None,

148 lambda: conn.execute("VACUUM"),

149 )

150 await asyncio.get_event_loop().run_in_executor(

151 None,

152 lambda: conn.execute("ANALYZE"),

153 )

154

155 db_size_after = (

156 Path(db.db_path).stat().st_size if Path(db.db_path).exists() else 0

157 )

158 space_saved = db_size_before - db_size_after

159

160 return f"🗄️ Database: {'Optimized reflection DB, saved ' + str(space_saved) + ' bytes' if space_saved > 0 else 'Reflection DB already optimized'}"

161

162 except ImportError:

163 return "ℹ️ Database: Reflection tools not available"

164 except Exception as e:

165 return f"⚠️ Database: Optimization skipped - {str(e)[:50]}"

166

167

168 async def _analyze_context_compaction() -> list[str]:

169 """Analyze and recommend context compaction."""

170 results = []

171

172 try:

173 should_compact, reason = should_suggest_compact()

174 results.extend(("\n🔍 Context Compaction Analysis", f"📊 {reason}"))

175

176 if should_compact: 176 ↛ 195 line 176 didn't jump to line 195 because the condition on line 176 was always true

177 results.extend(

178 [

179 "",

180 "🔄 RECOMMENDATION: Run /compact to optimize context",

181 "📝 Benefits of compaction:",

182 " • Improved response speed and accuracy",

183 " • Better focus on current development context",

184 " • Reduced memory usage for complex sessions",

185 " • Cleaner conversation flow",

186 "",

187 "💡 WORKFLOW: After this checkpoint completes, run: /compact",

188 "🔄 Context compaction should be applied for optimal performance",

189 ],

190 )

191

192 except Exception as e:

193 results.append(f"⚠️ Compaction analysis skipped: {str(e)[:50]}")

194

195 return results

196

197

198 async def _store_context_summary(conversation_summary: dict[str, Any]) -> None:

199 """Store conversation summary for future context retrieval."""

200 with suppress(ImportError, RuntimeError, OSError, ValueError, AttributeError):

201 from session_buddy.reflection_tools import get_reflection_database

202

203 db = await get_reflection_database()

204 summary_text = f"Session context: {', '.join(conversation_summary.get('key_topics', [])[:3])}"

205 if conversation_summary.get("decisions_made"):

206 summary_text += (

207 f". Key decisions: {conversation_summary['decisions_made'][0]}"

208 )

209

210 await db.store_reflection(summary_text, ["context-summary", "compaction"])

211

212

213 async def perform_strategic_compaction() -> list[str]:

214 """Perform strategic compaction and optimization tasks."""

215 results = []

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

217

218 # Database optimization

219 results.append(await _optimize_reflection_database())

220

221 # Log cleanup

222 results.append(_cleanup_session_logs())

223

224 # Temp file cleanup

225 results.append(_cleanup_temp_files(current_dir))

226

227 # Git optimization

228 results.extend(_optimize_git_repository(current_dir))

229

230 # UV cache cleanup

231 results.append(_cleanup_uv_cache())

232

233 # Context compaction analysis

234 results.extend(await _analyze_context_compaction())

235

236 # Summary

237 total_operations = len([r for r in results if not r.startswith(("ℹ️", "⚠️", "⏱️"))])

238 results.extend(

239 [

240 f"\n📊 Strategic compaction complete: {total_operations} optimization tasks performed",

241 "🎯 Recommendation: Conversation context should be compacted automatically",

242 ],

243 )

244

245 return results

246

247

248 async def monitor_proactive_quality() -> dict[str, Any]:

249 """Phase 3: Proactive Quality Monitoring with Early Warning System."""

250 try:

251 quality_alerts = []

252 quality_trend = "stable"

253 recommend_checkpoint = False

254

255 # Check if reflection tools are available

256 try:

257 (

258 quality_trend,

259 quality_alerts,

260 recommend_checkpoint,

261 ) = await _perform_quality_analysis()

262 except ImportError:

263 quality_alerts.append("Reflection tools not available")

264

265 return {

266 "quality_trend": quality_trend,

267 "alerts": quality_alerts,

268 "recommend_checkpoint": recommend_checkpoint,

269 "monitoring_active": True,

270 }

271

272 except Exception as e:

273 return _get_quality_error_result(e)

274

275

276# ======================

277# Context Analysis Functions (4)

278# ======================

279

280

281 async def _generate_basic_insights(

282 quality_score: float,

283 conversation_summary: dict[str, Any],

284) -> list[str]:

285 """Generate basic session insights from quality score and conversation summary."""

286 insights = []

287

288 insights.append(

289 f"Session checkpoint completed with quality score: {quality_score}/100",

290 )

291

292 if conversation_summary["key_topics"]:

293 insights.append(

294 f"Key discussion topics: {', '.join(conversation_summary['key_topics'][:3])}",

295 )

296

297 if conversation_summary["decisions_made"]:

298 insights.append(

299 f"Important decisions: {conversation_summary['decisions_made'][0]}",

300 )

301

302 if conversation_summary["next_steps"]:

303 insights.append(

304 f"Next steps identified: {conversation_summary['next_steps'][0]}",

305 )

306

307 return insights

308

309

310 async def _add_project_context_insights(insights: list[str]) -> None:

311 """Add project context analysis to insights."""

312 from session_buddy.utils.project_analysis import analyze_project_context

313

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

315 project_context = await analyze_project_context(current_dir)

316 context_items = [k for k, v in project_context.items() if v]

317 if context_items:

318 insights.append(f"Active project context: {', '.join(context_items)}")

319

320

321 def _generate_session_tags(quality_score: float) -> list[str]:

322 """Generate contextual tags for session reflection storage."""

323 # Import to avoid circular dependency

324 from session_buddy.reflection_tools import get_current_project

325

326 current_project = get_current_project()

327 tags = ["checkpoint", "session-summary", current_project or "unknown-project"]

328 if quality_score >= 80:

329 tags.append("excellent-session")

330 elif quality_score < 60: 330 ↛ 332 line 330 didn't jump to line 332 because the condition on line 330 was always true

331 tags.append("needs-attention")

332 return tags

333

334

335 async def summarize_current_conversation() -> dict[str, Any]:

336 """Phase 3: AI-Powered Conversation Summarization."""

337 try:

338 summary = _create_empty_summary()

339

340 # Check if reflection tools are available

341 try:

342 from session_buddy.reflection_tools import get_reflection_database

343

344 db = await get_reflection_database()

345 await _process_recent_reflections(db, summary)

346 _add_current_session_context(summary)

347 _ensure_summary_defaults(summary)

348 except ImportError:

349 summary = _get_fallback_summary()

350 except Exception:

351 summary = _get_fallback_summary()

352

353 return summary

354

355 except Exception as e:

356 return _get_error_summary(e)

357

358

359# ======================

360# Token & Conversation Analysis Functions (5)

361# ======================

362

363

364 async def analyze_token_usage_patterns() -> dict[str, Any]:

365 """Phase 3A: Intelligent token usage analysis with smart triggers."""

366 try:

367 conv_stats = await _get_conversation_statistics()

368 analysis = _analyze_context_usage_patterns(conv_stats)

369 return _finalize_token_analysis(analysis)

370

371 except Exception as error:

372 return {

373 "needs_attention": False,

374 "status": "analysis_failed",

375 "estimated_length": "unknown",

376 "recommend_compact": False,

377 "recommend_clear": False,

378 "error": str(error),

379 }

380

381

382 async def _get_conversation_statistics() -> dict[str, int]:

383 """Get conversation statistics from memory system."""

384 conv_stats: dict[str, int] = {

385 "total_conversations": 0,

386 "recent_activity": 0,

387 }

388

389 with suppress(ImportError, RuntimeError, OSError, ValueError, AttributeError):

390 from session_buddy.reflection_tools import get_reflection_database

391

392 db = await get_reflection_database()

393 stats = await db.get_stats()

394 conv_stats["total_conversations"] = stats.get("conversations_count", 0)

395

396 return conv_stats

397

398

399 def _analyze_context_usage_patterns(conv_stats: dict[str, int]) -> dict[str, Any]:

400 """Analyze context usage patterns and generate recommendations."""

401 estimated_length = "moderate"

402 needs_attention = False

403 recommend_compact = False

404 recommend_clear = False

405

406 total_conversations = conv_stats["total_conversations"]

407

408 # Progressive thresholds based on conversation count

409 if total_conversations > 3: 409 ↛ 410 line 409 didn't jump to line 410 because the condition on line 409 was never true

410 estimated_length = "extensive"

411 needs_attention = True

412 recommend_compact = True

413

414 if total_conversations > 10: 414 ↛ 415 line 414 didn't jump to line 415 because the condition on line 414 was never true

415 estimated_length = "very long"

416 needs_attention = True

417 recommend_compact = True

418

419 if total_conversations > 20: 419 ↛ 420 line 419 didn't jump to line 420 because the condition on line 419 was never true

420 estimated_length = "extremely long"

421 needs_attention = True

422 recommend_compact = True

423 recommend_clear = True

424

425 return {

426 "estimated_length": estimated_length,

427 "needs_attention": needs_attention,

428 "recommend_compact": recommend_compact,

429 "recommend_clear": recommend_clear,

430 }

431

432

433 def _finalize_token_analysis(analysis: dict[str, Any]) -> dict[str, Any]:

434 """Finalize token analysis with checkpoint override."""

435 # Override: ALWAYS recommend compaction during checkpoints

436 analysis["recommend_compact"] = True

437 analysis["needs_attention"] = True

438

439 if analysis["estimated_length"] == "moderate": 439 ↛ 442 line 439 didn't jump to line 442 because the condition on line 439 was always true

440 analysis["estimated_length"] = "checkpoint-session"

441

442 analysis["status"] = (

443 "optimal" if not analysis["needs_attention"] else "needs optimization"

444 )

445

446 return analysis

447

448

449 async def analyze_conversation_flow() -> dict[str, Any]:

450 """Phase 3A: Analyze conversation patterns and flow."""

451 try:

452 # Analyze recent reflection patterns to understand session flow

453 try:

454 from session_buddy.reflection_tools import get_reflection_database

455

456 db = await get_reflection_database()

457

458 # Search recent reflections for patterns

459 recent_reflections = await db.search_reflections(

460 "session checkpoint",

461 limit=5,

462 )

463

464 if recent_reflections:

465 # Analyze pattern based on recent reflections

466 if any("excellent" in r["content"].lower() for r in recent_reflections):

467 pattern_type = "productive_development"

468 recommendations = [

469 "Continue current productive workflow",

470 "Consider documenting successful patterns",

471 "Maintain current checkpoint frequency",

472 ]

473 elif any(

474 "attention" in r["content"].lower() for r in recent_reflections

475 ):

476 pattern_type = "optimization_needed"

477 recommendations = [

478 "Review recent workflow changes",

479 "Consider more frequent checkpoints",

480 "Use search tools to find successful patterns",

481 ]

482 else:

483 pattern_type = "steady_progress"

484 recommendations = [

485 "Maintain current workflow patterns",

486 "Consider periodic workflow evaluation",

487 ]

488 else:

489 pattern_type = "new_session"

490 recommendations = [

491 "Establish workflow patterns through regular checkpoints",

492 ]

493

494 except ImportError:

495 pattern_type = "basic_session"

496 recommendations = ["Enable reflection tools for advanced flow analysis"]

497 except Exception:

498 pattern_type = "analysis_unavailable"

499 recommendations = [

500 "Use regular checkpoints to establish workflow patterns",

501 ]

502

503 return {

504 "pattern_type": pattern_type,

505 "recommendations": recommendations,

506 "confidence": "pattern_based",

507 }

508

509 except Exception as e:

510 return {

511 "pattern_type": "analysis_failed",

512 "recommendations": ["Use basic workflow patterns"],

513 "error": str(e),

514 }

515

516

517# ======================

518# Memory & Workflow Functions (4)

519# ======================

520

521

522 async def analyze_memory_patterns(db: Any, conv_count: int) -> dict[str, Any]:

523 """Phase 3A: Advanced memory pattern analysis."""

524 try:

525 # Analyze conversation history for intelligent insights

526 if conv_count == 0:

527 return {

528 "summary": "New session - no historical patterns yet",

529 "proactive_suggestions": [

530 "Start building conversation history for better insights",

531 ],

532 }

533 if conv_count < 5: 533 ↛ 534 line 533 didn't jump to line 534 because the condition on line 533 was never true

534 return {

535 "summary": f"{conv_count} conversations stored - building pattern recognition",

536 "proactive_suggestions": [

537 "Continue regular checkpoints to build session intelligence",

538 "Use store_reflection for important insights",

539 ],

540 }

541 if conv_count < 20: 541 ↛ 549 line 541 didn't jump to line 549 because the condition on line 541 was always true

542 return {

543 "summary": f"{conv_count} conversations stored - developing patterns",

544 "proactive_suggestions": [

545 "Use reflect_on_past to leverage growing knowledge base",

546 "Search previous solutions before starting new implementations",

547 ],

548 }

549 return {

550 "summary": f"{conv_count} conversations - rich pattern recognition available",

551 "proactive_suggestions": [

552 "Leverage extensive history with targeted searches",

553 "Consider workflow optimization based on successful patterns",

554 "Use conversation history to accelerate problem-solving",

555 ],

556 }

557

558 except Exception as e:

559 return {

560 "summary": "Memory analysis unavailable",

561 "proactive_suggestions": [

562 "Use basic memory tools for conversation tracking",

563 ],

564 "error": str(e),

565 }

566

567

568 async def analyze_project_workflow_patterns(current_dir: Path) -> dict[str, Any]:

569 """Phase 3A: Project-specific workflow pattern analysis."""

570 try:

571 project_characteristics = _detect_project_characteristics(current_dir)

572 workflow_recommendations = _generate_workflow_recommendations(

573 project_characteristics,

574 )

575

576 return {

577 "workflow_recommendations": workflow_recommendations,

578 "project_characteristics": project_characteristics,

579 }

580

581 except Exception as e:

582 return {

583 "workflow_recommendations": ["Use basic project workflow patterns"],

584 "error": str(e),

585 }

586

587

588 def _generate_workflow_recommendations(characteristics: dict[str, bool]) -> list[str]:

589 """Generate workflow recommendations based on project characteristics."""

590 recommendations = []

591

592 if characteristics["has_tests"]:

593 recommendations.extend(

594 [

595 "Use targeted test commands for specific test scenarios",

596 "Consider test-driven development workflow with regular testing",

597 ],

598 )

599

600 if characteristics["has_git"]:

601 recommendations.extend(

602 [

603 "Leverage git context for branch-specific development",

604 "Use commit messages to track progress patterns",

605 ],

606 )

607

608 if characteristics["has_python"] and characteristics["has_tests"]:

609 recommendations.append(

610 "Python+Testing: Consider pytest workflows with coverage analysis",

611 )

612

613 if characteristics["has_node"]: 613 ↛ 614 line 613 didn't jump to line 614 because the condition on line 613 was never true

614 recommendations.append(

615 "Node.js project: Leverage npm/yarn scripts in development workflow",

616 )

617

618 if characteristics["has_docker"]: 618 ↛ 619 line 618 didn't jump to line 619 because the condition on line 618 was never true

619 recommendations.append(

620 "Containerized project: Consider container-based development workflows",

621 )

622

623 # Default recommendations if no specific patterns detected

624 if not recommendations:

625 recommendations.append(

626 "Establish project-specific workflow patterns through regular checkpoints",

627 )

628

629 return recommendations

630

631

632 def _detect_project_characteristics(current_dir: Path) -> dict[str, bool]:

633 """Detect project characteristics from directory structure."""

634 return {

635 "has_tests": (current_dir / "tests").exists()

636 or (current_dir / "test").exists(),

637 "has_git": (current_dir / ".git").exists(),

638 "has_python": (current_dir / "pyproject.toml").exists()

639 or (current_dir / "requirements.txt").exists(),

640 "has_node": (current_dir / "package.json").exists(),

641 "has_docker": (current_dir / "Dockerfile").exists()

642 or (current_dir / "docker-compose.yml").exists(),

643 }

644

645

646 def _check_workflow_drift(quality_scores: list[float]) -> tuple[list[str], bool]:

647 """Check for workflow drift indicators."""

648 quality_alerts = []

649 recommend_checkpoint = False

650

651 if len(quality_scores) >= 4:

652 variance = max(quality_scores) - min(quality_scores)

653 if variance > 30:

654 quality_alerts.append(

655 "High quality variance detected - workflow inconsistency",

656 )

657 recommend_checkpoint = True

658

659 return quality_alerts, recommend_checkpoint

660

661

662# ======================

663# Intelligence & Insights Functions (5)

664# ======================

665

666

667 async def _capture_intelligence_insights(

668 db: ReflectionDatabase,

669 tags: list[str],

670 results: list[str],

671) -> None:

672 """Capture session intelligence insights."""

673 intelligence = await generate_session_intelligence()

674 if intelligence["priority_actions"]:

675 intel_summary = f"Session intelligence: {intelligence['intelligence_level']}. "

676 intel_summary += f"Priority: {intelligence['priority_actions'][0]}"

677

678 intel_id = await db.store_reflection(

679 intel_summary,

680 [*tags, "intelligence", "proactive"],

681 )

682 results.append(f"🧠 Intelligence insights stored: {intel_id[:12]}...")

683

684

685 async def _analyze_reflection_based_intelligence() -> list[str]:

686 """Analyze recent reflections for intelligence recommendations."""

687 try:

688 from session_buddy.reflection_tools import get_reflection_database

689

690 db = await get_reflection_database()

691 recent_reflections = await db.search_reflections("checkpoint", limit=3)

692

693 if recent_reflections:

694 recent_scores = _extract_quality_scores(recent_reflections)

695 return _generate_quality_trend_recommendations(recent_scores)

696

697 except ImportError:

698 return []

699 except Exception:

700 return ["Enable reflection analysis for session trend intelligence"]

701

702 return []

703

704

705 async def generate_session_intelligence() -> dict[str, Any]:

706 """Phase 3A: Generate proactive session intelligence and priority actions."""

707 try:

708 current_time = datetime.now()

709

710 # Gather all recommendation sources

711 priority_actions = []

712 priority_actions.extend(_get_time_based_recommendations(current_time.hour))

713 priority_actions.extend(await _analyze_reflection_based_intelligence())

714 priority_actions = _ensure_default_recommendations(priority_actions)

715

716 return {

717 "priority_actions": priority_actions,

718 "intelligence_level": "proactive",

719 "timestamp": current_time.isoformat(),

720 }

721

722 except Exception as e:

723 return _get_intelligence_error_result(e)

724

725

726 def _ensure_default_recommendations(priority_actions: list[str]) -> list[str]:

727 """Ensure at least one recommendation is provided."""

728 if not priority_actions: 728 ↛ 729 line 728 didn't jump to line 729 because the condition on line 728 was never true

729 return ["Continue regular checkpoint monitoring"]

730 return priority_actions

731

732

733# ======================

734# Quality Analysis & Recommendations Functions (6)

735# ======================

736

737

738 async def _perform_quality_analysis() -> tuple[str, list[str], bool]:

739 """Perform quality analysis with reflection data."""

740 quality_alerts = []

741 quality_trend = "stable"

742 recommend_checkpoint = False

743

744 try:

745 from session_buddy.reflection_tools import get_reflection_database

746

747 db = await get_reflection_database()

748 recent_reflections = await db.search_reflections("quality score", limit=5)

749 quality_scores = _extract_quality_scores(recent_reflections)

750

751 if quality_scores:

752 trend, trend_alerts, trend_checkpoint = _analyze_quality_trend(

753 quality_scores,

754 )

755 quality_trend = trend

756 quality_alerts.extend(trend_alerts)

757 recommend_checkpoint = recommend_checkpoint or trend_checkpoint

758

759 drift_alerts, drift_checkpoint = _check_workflow_drift(quality_scores)

760 quality_alerts.extend(drift_alerts)

761 recommend_checkpoint = recommend_checkpoint or drift_checkpoint

762

763 except (ImportError, Exception):

764 quality_alerts.append("Quality monitoring analysis unavailable")

765

766 return quality_trend, quality_alerts, recommend_checkpoint

767

768

769 def _get_quality_error_result(error: Exception) -> dict[str, Any]:

770 """Get error result for quality monitoring failure."""

771 return {

772 "quality_trend": "unknown",

773 "alerts": ["Quality monitoring failed"],

774 "recommend_checkpoint": False,

775 "monitoring_active": False,

776 "error": str(error),

777 }

778

779

780 async def _analyze_token_usage_recommendations(results: list[str]) -> None:

781 """Analyze token usage and add recommendations."""

782 token_analysis = await analyze_token_usage_patterns()

783 if token_analysis["needs_attention"]: 783 ↛ 806 line 783 didn't jump to line 806 because the condition on line 783 was always true

784 results.extend(

785 (

786 f"⚠️ Context usage: {token_analysis['status']}",

787 f" Estimated conversation length: {token_analysis['estimated_length']}",

788 )

789 )

790

791 # Smart compaction triggers - PRIORITY RECOMMENDATIONS

792 if token_analysis["recommend_compact"]: 792 ↛ 801 line 792 didn't jump to line 801 because the condition on line 792 was always true

793 results.extend(

794 (

795 "🚨 CRITICAL AUTO-RECOMMENDATION: Context compaction required",

796 "🔄 This checkpoint has prepared conversation summary for compaction",

797 "💡 Compaction should be applied automatically after this checkpoint",

798 )

799 )

800

801 if token_analysis["recommend_clear"]: 801 ↛ 802 line 801 didn't jump to line 802 because the condition on line 801 was never true

802 results.append(

803 "🆕 AUTO-RECOMMENDATION: Consider /clear for fresh context after compaction",

804 )

805 else:

806 results.append(f"✅ Context usage: {token_analysis['status']}")

807

808

809 async def _analyze_conversation_flow_recommendations(results: list[str]) -> None:

810 """Analyze conversation flow and add recommendations."""

811 flow_analysis = await analyze_conversation_flow()

812 results.append(f"📊 Session flow: {flow_analysis['pattern_type']}")

813

814 if flow_analysis["recommendations"]: 814 ↛ exit line 814 didn't return from function '_analyze_conversation_flow_recommendations' because the condition on line 814 was always true

815 results.append("🎯 Flow-based recommendations:")

816 for rec in flow_analysis["recommendations"][:3]:

817 results.append(f" • {rec}")

818

819

820 async def _analyze_memory_recommendations(results: list[str]) -> None:

821 """Analyze memory patterns and add recommendations."""

822 try:

823 from session_buddy.reflection_tools import get_reflection_database

824

825 db = await get_reflection_database()

826 stats = await db.get_stats()

827 conv_count = stats.get("conversations_count", 0)

828

829 # Advanced memory analysis

830 memory_insights = await analyze_memory_patterns(db, conv_count)

831 results.append(f"📚 Memory insights: {memory_insights['summary']}")

832

833 if memory_insights["proactive_suggestions"]: 833 ↛ exit line 833 didn't return from function '_analyze_memory_recommendations' because the condition on line 833 was always true

834 results.append("💡 Proactive suggestions:")

835 for suggestion in memory_insights["proactive_suggestions"][:2]:

836 results.append(f" • {suggestion}")

837

838 except ImportError:

839 pass

840 except Exception:

841 results.append("📚 Memory system available for conversation search")

842

843

844# ======================

845# Advanced Metrics & Context Analysis (2)

846# ======================

847

848

849 async def analyze_advanced_context_metrics() -> dict[str, Any]:

850 """Phase 3A: Advanced context metrics analysis."""

851 return {

852 "estimated_tokens": 0, # Placeholder for actual token counting

853 "context_density": "moderate",

854 "conversation_depth": "active",

855 }

856

857

858 async def analyze_context_usage() -> list[str]:

859 """Phase 2 & 3A: Advanced context analysis with intelligent recommendations."""

860 results = []

861

862 try:

863 results.append("🔍 Advanced context analysis and optimization...")

864

865 # Phase 3A: Advanced Context Intelligence

866 await analyze_advanced_context_metrics()

867

868 # Run all analysis components

869 await _analyze_token_usage_recommendations(results)

870 await _analyze_conversation_flow_recommendations(results)

871 await _analyze_memory_recommendations(results)

872 await _analyze_project_workflow_recommendations(results)

873 await _analyze_session_intelligence_recommendations(results)

874 await _analyze_quality_monitoring_recommendations(results)

875

876 except Exception as e:

877 await _add_fallback_recommendations(results, e)

878

879 return results

880

881

882 async def _analyze_project_workflow_recommendations(results: list[str]) -> None:

883 """Analyze project workflow patterns and add recommendations."""

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

885 project_insights = await analyze_project_workflow_patterns(current_dir)

886

887 if project_insights["workflow_recommendations"]: 887 ↛ exit line 887 didn't return from function '_analyze_project_workflow_recommendations' because the condition on line 887 was always true

888 results.append("🚀 Workflow optimizations:")

889 for opt in project_insights["workflow_recommendations"][:2]:

890 results.append(f" • {opt}")

891

892

893 async def _analyze_session_intelligence_recommendations(results: list[str]) -> None:

894 """Analyze session intelligence and add recommendations."""

895 session_intelligence = await generate_session_intelligence()

896 if session_intelligence["priority_actions"]: 896 ↛ exit line 896 didn't return from function '_analyze_session_intelligence_recommendations' because the condition on line 896 was always true

897 results.append("\n🧠 Session Intelligence:")

898 for action in session_intelligence["priority_actions"][:3]:

899 results.append(f" • {action}")

900

901

902 async def _analyze_quality_monitoring_recommendations(results: list[str]) -> None:

903 """Analyze quality monitoring and add recommendations."""

904 quality_monitoring = await monitor_proactive_quality()

905 if quality_monitoring["monitoring_active"]: 905 ↛ exit line 905 didn't return from function '_analyze_quality_monitoring_recommendations' because the condition on line 905 was always true

906 results.append(f"\n📊 Quality Trend: {quality_monitoring['quality_trend']}")

907

908 if quality_monitoring["alerts"]: 908 ↛ 913 line 908 didn't jump to line 913 because the condition on line 908 was always true

909 results.append("⚠️ Quality Alerts:")

910 for alert in quality_monitoring["alerts"][:2]:

911 results.append(f" • {alert}")

912

913 if quality_monitoring["recommend_checkpoint"]: 913 ↛ 914 line 913 didn't jump to line 914 because the condition on line 913 was never true

914 results.append("🔄 PROACTIVE RECOMMENDATION: Consider immediate checkpoint")

915

916

917 async def _add_fallback_recommendations(results: list[str], error: Exception) -> None:

918 """Add fallback recommendations when analysis fails."""

919 results.append(f"❌ Advanced context analysis failed: {str(error)[:60]}...")

920 results.append("💡 Falling back to basic context management recommendations")

921

922 # Fallback to basic recommendations

923 results.append("🎯 Basic context actions:")

924 results.append(" • Use /compact for conversation summarization")

925 results.append(" • Use /clear for fresh context on new topics")

926 results.append(" • Use search tools to retrieve relevant discussions")

927

928

929# ======================

930# Quality Score Calculation (Fixed)

931# ======================

932

933

934 async def _perform_quality_assessment() -> tuple[int, dict[str, Any]]:

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

936 quality_data = await calculate_quality_score()

937 quality_score = quality_data["total_score"]

938 return quality_score, quality_data

939

940

941 async def calculate_quality_score(project_dir: Path | None = None) -> dict[str, Any]:

942 """Calculate session quality score using V2 algorithm.

943

944 This function fixes the bug where server.py was calling calculate_quality_score()

945 but the actual function is calculate_quality_score_v2() in utils.

946

947 Args:

948 project_dir: Path to the project directory. If not provided, will use current directory.

949

950 Returns dict with:

951 - total_score: int (0-100)

952 - version: str

953 - project_health: dict

954 - permissions_health: dict

955 - session_health: dict

956 - tool_health: dict

957 - recommendations: list[str]

958

959 """

960 if project_dir is None:

961 project_dir = Path(os.environ.get("PWD", Path.cwd()))

962

963 quality_result = await calculate_quality_score_v2(project_dir=project_dir)

964

965 # Convert dataclass to dict to maintain compatibility and include breakdown

966 result_dict = {

967 "total_score": int(

968 quality_result.total_score,

969 ), # Convert to int for backward compatibility

970 "version": quality_result.version,

971 "project_health": {

972 "total": quality_result.project_health.total,

973 "tooling_score": quality_result.project_health.tooling_score,

974 "maturity_score": quality_result.project_health.maturity_score,

975 "details": quality_result.project_health.details,

976 },

977 "permissions_health": quality_result.trust_score.details, # Using trust_score details for permissions

978 "session_health": {"status": "active"}, # Placeholder for session health

979 "tool_health": {

980 "count": quality_result.trust_score.tool_ecosystem,

981 }, # Using trust_score for tool health

982 "recommendations": quality_result.recommendations,

983 "timestamp": quality_result.timestamp,

984 "trust_score": quality_result.trust_score.details, # Add trust_score for backward compatibility

985 }

986

987 # Add breakdown key for backward compatibility with tests

988 result_dict["breakdown"] = {

989 "project_health": quality_result.project_health.total,

990 "permissions": sum(quality_result.trust_score.details.values())

991 if quality_result.trust_score.details

992 else 0,

993 "session_management": 20, # Fixed value as in original tests

994 "tools": quality_result.trust_score.tool_ecosystem,

995 "code_quality": quality_result.project_health.tooling_score, # Add code_quality key for tests

996 "dev_velocity": quality_result.dev_velocity.total, # Use actual dev_velocity score

997 "security": quality_result.security.total, # Add security key for tests

998 }

999

1000 return result_dict