claude-mpm 4.15.6__py3-none-any.whl → 4.21.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (209) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  3. claude_mpm/agents/BASE_PM.md +272 -23
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +49 -0
  5. claude_mpm/agents/agent_loader.py +4 -4
  6. claude_mpm/agents/templates/engineer.json +5 -1
  7. claude_mpm/agents/templates/php-engineer.json +10 -4
  8. claude_mpm/agents/templates/python_engineer.json +8 -3
  9. claude_mpm/agents/templates/rust_engineer.json +12 -7
  10. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  11. claude_mpm/cli/commands/__init__.py +2 -0
  12. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  13. claude_mpm/cli/commands/mpm_init/core.py +525 -0
  14. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  15. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  16. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  17. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  18. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  19. claude_mpm/cli/commands/mpm_init_handler.py +67 -1
  20. claude_mpm/cli/commands/skills.py +488 -0
  21. claude_mpm/cli/executor.py +2 -0
  22. claude_mpm/cli/parsers/base_parser.py +7 -0
  23. claude_mpm/cli/parsers/mpm_init_parser.py +42 -0
  24. claude_mpm/cli/parsers/skills_parser.py +137 -0
  25. claude_mpm/cli/startup.py +57 -0
  26. claude_mpm/commands/mpm-auto-configure.md +52 -0
  27. claude_mpm/commands/mpm-help.md +6 -0
  28. claude_mpm/commands/mpm-init.md +112 -6
  29. claude_mpm/commands/mpm-resume.md +372 -0
  30. claude_mpm/commands/mpm-version.md +113 -0
  31. claude_mpm/commands/mpm.md +2 -0
  32. claude_mpm/config/agent_config.py +2 -2
  33. claude_mpm/constants.py +12 -0
  34. claude_mpm/core/config.py +42 -0
  35. claude_mpm/core/factories.py +1 -1
  36. claude_mpm/core/interfaces.py +56 -1
  37. claude_mpm/core/optimized_agent_loader.py +3 -3
  38. claude_mpm/hooks/__init__.py +8 -0
  39. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  40. claude_mpm/hooks/session_resume_hook.py +121 -0
  41. claude_mpm/models/resume_log.py +340 -0
  42. claude_mpm/services/agents/auto_config_manager.py +1 -1
  43. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  44. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  45. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  46. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  47. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  48. claude_mpm/services/agents/local_template_manager.py +1 -1
  49. claude_mpm/services/agents/recommender.py +47 -0
  50. claude_mpm/services/cli/resume_service.py +617 -0
  51. claude_mpm/services/cli/session_manager.py +87 -0
  52. claude_mpm/services/cli/session_pause_manager.py +504 -0
  53. claude_mpm/services/cli/session_resume_helper.py +372 -0
  54. claude_mpm/services/core/base.py +26 -11
  55. claude_mpm/services/core/interfaces.py +56 -1
  56. claude_mpm/services/core/models/agent_config.py +3 -0
  57. claude_mpm/services/core/models/process.py +4 -0
  58. claude_mpm/services/core/path_resolver.py +1 -1
  59. claude_mpm/services/diagnostics/models.py +21 -0
  60. claude_mpm/services/event_bus/relay.py +23 -7
  61. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  62. claude_mpm/services/local_ops/__init__.py +2 -0
  63. claude_mpm/services/mcp_config_manager.py +7 -131
  64. claude_mpm/services/mcp_gateway/auto_configure.py +31 -25
  65. claude_mpm/services/mcp_gateway/core/process_pool.py +19 -10
  66. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +26 -21
  67. claude_mpm/services/memory/failure_tracker.py +19 -4
  68. claude_mpm/services/session_manager.py +205 -1
  69. claude_mpm/services/unified/deployment_strategies/local.py +1 -1
  70. claude_mpm/services/version_service.py +104 -1
  71. claude_mpm/skills/__init__.py +21 -0
  72. claude_mpm/skills/agent_skills_injector.py +324 -0
  73. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  74. claude_mpm/skills/bundled/api-documentation.md +393 -0
  75. claude_mpm/skills/bundled/async-testing.md +571 -0
  76. claude_mpm/skills/bundled/code-review.md +143 -0
  77. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  78. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  79. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  80. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  81. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  82. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  83. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  84. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  85. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  86. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  87. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  88. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  89. claude_mpm/skills/bundled/database-migration.md +199 -0
  90. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  91. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  92. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  93. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  94. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  95. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  96. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  97. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  98. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  99. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  100. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  101. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  102. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  103. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  104. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  105. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  106. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  107. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  108. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  109. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  110. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  111. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  112. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  113. claude_mpm/skills/bundled/git-workflow.md +414 -0
  114. claude_mpm/skills/bundled/imagemagick.md +204 -0
  115. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  116. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  117. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  118. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  119. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  120. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  121. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  122. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  123. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  124. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  125. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  126. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  127. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  128. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  129. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  130. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  131. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  132. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  133. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  134. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  135. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  136. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  137. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  138. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  139. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  140. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  141. claude_mpm/skills/bundled/pdf.md +141 -0
  142. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  143. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  144. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  145. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  146. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  147. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  148. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  149. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  150. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  151. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  152. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  153. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  154. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  155. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  156. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  157. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  158. claude_mpm/skills/bundled/security-scanning.md +327 -0
  159. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  160. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  161. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  162. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  163. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  164. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  165. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  166. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  167. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  168. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  169. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  170. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  171. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  172. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  173. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  174. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  175. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  176. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  177. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  178. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  179. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  180. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  181. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  182. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  183. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  184. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  185. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  186. claude_mpm/skills/bundled/xlsx.md +157 -0
  187. claude_mpm/skills/registry.py +97 -9
  188. claude_mpm/skills/skills_registry.py +348 -0
  189. claude_mpm/skills/skills_service.py +739 -0
  190. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  191. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  192. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  193. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  194. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  195. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  196. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  197. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  198. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  199. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  200. claude_mpm/utils/agent_dependency_loader.py +2 -2
  201. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/METADATA +211 -33
  202. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/RECORD +206 -64
  203. claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md +0 -602
  204. claude_mpm/cli/commands/mpm_init.py +0 -2008
  205. claude_mpm/tools/code_tree_analyzer.py +0 -1825
  206. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/WHEEL +0 -0
  207. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/entry_points.txt +0 -0
  208. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/licenses/LICENSE +0 -0
  209. {claude_mpm-4.15.6.dist-info → claude_mpm-4.21.3.dist-info}/top_level.txt +0 -0
@@ -12,6 +12,11 @@ from .failure_learning import (
12
12
  from .kuzu_enrichment_hook import KuzuEnrichmentHook, get_kuzu_enrichment_hook
13
13
  from .kuzu_memory_hook import KuzuMemoryHook, get_kuzu_memory_hook
14
14
  from .kuzu_response_hook import KuzuResponseHook, get_kuzu_response_hook
15
+ from .session_resume_hook import (
16
+ SessionResumeStartupHook,
17
+ get_session_resume_hook,
18
+ trigger_session_resume_check,
19
+ )
15
20
 
16
21
  __all__ = [
17
22
  "BaseHook",
@@ -24,10 +29,13 @@ __all__ = [
24
29
  "KuzuMemoryHook",
25
30
  "KuzuResponseHook",
26
31
  "LearningExtractionHook",
32
+ "SessionResumeStartupHook",
27
33
  "get_failure_detection_hook",
28
34
  "get_fix_detection_hook",
29
35
  "get_kuzu_enrichment_hook",
30
36
  "get_kuzu_memory_hook",
31
37
  "get_kuzu_response_hook",
32
38
  "get_learning_extraction_hook",
39
+ "get_session_resume_hook",
40
+ "trigger_session_resume_check",
33
41
  ]
@@ -268,7 +268,10 @@ class ResponseTrackingManager:
268
268
  def track_stop_response(
269
269
  self, event: dict, session_id: str, metadata: dict, pending_prompts: dict
270
270
  ):
271
- """Track response for stop events."""
271
+ """Track response for stop events.
272
+
273
+ Captures Claude API stop_reason and usage data for context management.
274
+ """
272
275
  if not (self.response_tracking_enabled and self.response_tracker):
273
276
  return
274
277
 
@@ -294,6 +297,37 @@ class ResponseTrackingManager:
294
297
  # Add prompt timestamp to metadata
295
298
  metadata["prompt_timestamp"] = prompt_data.get("timestamp")
296
299
 
300
+ # Capture Claude API stop_reason if available
301
+ if "stop_reason" in event:
302
+ metadata["stop_reason"] = event["stop_reason"]
303
+ if DEBUG:
304
+ print(
305
+ f" - Captured stop_reason: {event['stop_reason']}",
306
+ file=sys.stderr,
307
+ )
308
+
309
+ # Capture Claude API usage data if available
310
+ if "usage" in event:
311
+ usage_data = event["usage"]
312
+ metadata["usage"] = {
313
+ "input_tokens": usage_data.get("input_tokens", 0),
314
+ "output_tokens": usage_data.get("output_tokens", 0),
315
+ "cache_creation_input_tokens": usage_data.get(
316
+ "cache_creation_input_tokens", 0
317
+ ),
318
+ "cache_read_input_tokens": usage_data.get(
319
+ "cache_read_input_tokens", 0
320
+ ),
321
+ }
322
+ if DEBUG:
323
+ total_tokens = usage_data.get(
324
+ "input_tokens", 0
325
+ ) + usage_data.get("output_tokens", 0)
326
+ print(
327
+ f" - Captured usage: {total_tokens} total tokens",
328
+ file=sys.stderr,
329
+ )
330
+
297
331
  # Track the main Claude response
298
332
  file_path = self.response_tracker.track_response(
299
333
  agent_name="claude_main",
@@ -0,0 +1,121 @@
1
+ """Session Resume Startup Hook.
2
+
3
+ WHY: This hook automatically checks for paused sessions on PM startup and displays
4
+ resume context to help users continue their work seamlessly.
5
+
6
+ DESIGN DECISIONS:
7
+ - Runs automatically on PM startup
8
+ - Non-blocking: doesn't prevent PM from starting if check fails
9
+ - Displays context to stdout for user visibility
10
+ - Integrates with existing session pause/resume infrastructure
11
+ """
12
+
13
+ from pathlib import Path
14
+ from typing import Any, Dict, Optional
15
+
16
+ from claude_mpm.core.logger import get_logger
17
+ from claude_mpm.services.cli.session_resume_helper import SessionResumeHelper
18
+
19
+ logger = get_logger(__name__)
20
+
21
+
22
+ class SessionResumeStartupHook:
23
+ """Hook for automatic session resume detection on PM startup."""
24
+
25
+ def __init__(self, project_path: Optional[Path] = None):
26
+ """Initialize the session resume hook.
27
+
28
+ Args:
29
+ project_path: Project root path (default: current directory)
30
+ """
31
+ self.project_path = project_path or Path.cwd()
32
+ self.resume_helper = SessionResumeHelper(self.project_path)
33
+ self._session_displayed = False
34
+
35
+ def on_pm_startup(self) -> Optional[Dict[str, Any]]:
36
+ """Execute on PM startup to check for paused sessions.
37
+
38
+ Returns:
39
+ Session data if paused session found, None otherwise
40
+ """
41
+ try:
42
+ # Check if we already displayed a session in this process
43
+ if self._session_displayed:
44
+ logger.debug("Session already displayed, skipping")
45
+ return None
46
+
47
+ # Check for paused sessions
48
+ session_data = self.resume_helper.check_and_display_resume_prompt()
49
+
50
+ if session_data:
51
+ self._session_displayed = True
52
+ logger.info("Paused session context displayed to user")
53
+
54
+ return session_data
55
+
56
+ except Exception as e:
57
+ logger.error(f"Failed to check for paused sessions: {e}", exc_info=True)
58
+ return None
59
+
60
+ def get_session_count(self) -> int:
61
+ """Get count of paused sessions.
62
+
63
+ Returns:
64
+ Number of paused sessions
65
+ """
66
+ try:
67
+ return self.resume_helper.get_session_count()
68
+ except Exception as e:
69
+ logger.error(f"Failed to get session count: {e}")
70
+ return 0
71
+
72
+ def clear_displayed_session(self, session_data: Dict[str, Any]) -> bool:
73
+ """Clear a session after it has been displayed and user has acknowledged.
74
+
75
+ Args:
76
+ session_data: Session data to clear
77
+
78
+ Returns:
79
+ True if successfully cleared, False otherwise
80
+ """
81
+ try:
82
+ return self.resume_helper.clear_session(session_data)
83
+ except Exception as e:
84
+ logger.error(f"Failed to clear session: {e}")
85
+ return False
86
+
87
+
88
+ # Global hook instance
89
+ _session_resume_hook: Optional[SessionResumeStartupHook] = None
90
+
91
+
92
+ def get_session_resume_hook(
93
+ project_path: Optional[Path] = None,
94
+ ) -> SessionResumeStartupHook:
95
+ """Get or create the global session resume hook instance.
96
+
97
+ Args:
98
+ project_path: Project root path (default: current directory)
99
+
100
+ Returns:
101
+ SessionResumeStartupHook instance
102
+ """
103
+ global _session_resume_hook
104
+
105
+ if _session_resume_hook is None:
106
+ _session_resume_hook = SessionResumeStartupHook(project_path)
107
+ logger.debug("Created session resume hook instance")
108
+
109
+ return _session_resume_hook
110
+
111
+
112
+ def trigger_session_resume_check() -> Optional[Dict[str, Any]]:
113
+ """Trigger a session resume check (convenience function).
114
+
115
+ This is the main entry point for PM startup integration.
116
+
117
+ Returns:
118
+ Session data if found, None otherwise
119
+ """
120
+ hook = get_session_resume_hook()
121
+ return hook.on_pm_startup()
@@ -0,0 +1,340 @@
1
+ """Resume Log Data Model.
2
+
3
+ This module defines the data structure for session resume logs that enable
4
+ seamless context restoration when Claude hits token limits.
5
+
6
+ Design Philosophy:
7
+ - Target 10k tokens maximum per resume log
8
+ - Human-readable markdown format
9
+ - Structured sections with token budgets
10
+ - Optimized for Claude consumption on session resume
11
+ """
12
+
13
+ from dataclasses import dataclass, field
14
+ from datetime import datetime, timezone
15
+ from pathlib import Path
16
+ from typing import Any, Dict, List, Optional
17
+
18
+ from claude_mpm.core.logging_utils import get_logger
19
+
20
+ logger = get_logger(__name__)
21
+
22
+
23
+ @dataclass
24
+ class ContextMetrics:
25
+ """Context window usage metrics."""
26
+
27
+ total_budget: int = 200000
28
+ used_tokens: int = 0
29
+ remaining_tokens: int = 0
30
+ percentage_used: float = 0.0
31
+ stop_reason: Optional[str] = None
32
+ model: str = "claude-sonnet-4.5"
33
+ session_id: str = ""
34
+ timestamp: str = field(
35
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
36
+ )
37
+
38
+ def to_dict(self) -> Dict[str, Any]:
39
+ """Convert to dictionary."""
40
+ return {
41
+ "total_budget": self.total_budget,
42
+ "used_tokens": self.used_tokens,
43
+ "remaining_tokens": self.remaining_tokens,
44
+ "percentage_used": self.percentage_used,
45
+ "stop_reason": self.stop_reason,
46
+ "model": self.model,
47
+ "session_id": self.session_id,
48
+ "timestamp": self.timestamp,
49
+ }
50
+
51
+ @classmethod
52
+ def from_dict(cls, data: Dict[str, Any]) -> "ContextMetrics":
53
+ """Create from dictionary."""
54
+ return cls(
55
+ total_budget=data.get("total_budget", 200000),
56
+ used_tokens=data.get("used_tokens", 0),
57
+ remaining_tokens=data.get("remaining_tokens", 0),
58
+ percentage_used=data.get("percentage_used", 0.0),
59
+ stop_reason=data.get("stop_reason"),
60
+ model=data.get("model", "claude-sonnet-4.5"),
61
+ session_id=data.get("session_id", ""),
62
+ timestamp=data.get("timestamp", datetime.now(timezone.utc).isoformat()),
63
+ )
64
+
65
+
66
+ @dataclass
67
+ class ResumeLog:
68
+ """Resume log containing all information needed to restore session context.
69
+
70
+ Token Budget Distribution (10k tokens total):
71
+ - Context Metrics: 500 tokens
72
+ - Mission Summary: 1,000 tokens
73
+ - Accomplishments: 2,000 tokens
74
+ - Key Findings: 2,500 tokens
75
+ - Decisions & Rationale: 1,500 tokens
76
+ - Next Steps: 1,500 tokens
77
+ - Critical Context: 1,000 tokens
78
+ """
79
+
80
+ # Session identification
81
+ session_id: str
82
+ previous_session_id: Optional[str] = None
83
+ created_at: str = field(
84
+ default_factory=lambda: datetime.now(timezone.utc).isoformat()
85
+ )
86
+
87
+ # Context metrics
88
+ context_metrics: ContextMetrics = field(default_factory=ContextMetrics)
89
+
90
+ # Core content sections (with token budgets)
91
+ mission_summary: str = "" # 1,000 tokens - What was the overall goal?
92
+ accomplishments: List[str] = field(
93
+ default_factory=list
94
+ ) # 2,000 tokens - What was completed?
95
+ key_findings: List[str] = field(
96
+ default_factory=list
97
+ ) # 2,500 tokens - What was discovered?
98
+ decisions_made: List[Dict[str, str]] = field(
99
+ default_factory=list
100
+ ) # 1,500 tokens - What choices were made and why?
101
+ next_steps: List[str] = field(
102
+ default_factory=list
103
+ ) # 1,500 tokens - What needs to happen next?
104
+ critical_context: Dict[str, Any] = field(
105
+ default_factory=dict
106
+ ) # 1,000 tokens - Essential state/data
107
+
108
+ # Metadata
109
+ files_modified: List[str] = field(default_factory=list)
110
+ agents_used: Dict[str, int] = field(default_factory=dict)
111
+ errors_encountered: List[str] = field(default_factory=list)
112
+ warnings: List[str] = field(default_factory=list)
113
+
114
+ def to_markdown(self) -> str:
115
+ """Generate markdown format for Claude consumption.
116
+
117
+ Returns:
118
+ Markdown-formatted resume log
119
+ """
120
+ sections = []
121
+
122
+ # Header
123
+ sections.append(f"# Session Resume Log: {self.session_id}\n")
124
+ sections.append(f"**Created**: {self.created_at}")
125
+ if self.previous_session_id:
126
+ sections.append(f"**Previous Session**: {self.previous_session_id}")
127
+ sections.append("")
128
+
129
+ # Context Metrics (500 tokens)
130
+ sections.append("## Context Metrics\n")
131
+ sections.append(f"- **Model**: {self.context_metrics.model}")
132
+ sections.append(
133
+ f"- **Tokens Used**: {self.context_metrics.used_tokens:,} / {self.context_metrics.total_budget:,}"
134
+ )
135
+ sections.append(
136
+ f"- **Percentage**: {self.context_metrics.percentage_used:.1f}%"
137
+ )
138
+ sections.append(
139
+ f"- **Remaining**: {self.context_metrics.remaining_tokens:,} tokens"
140
+ )
141
+ if self.context_metrics.stop_reason:
142
+ sections.append(f"- **Stop Reason**: {self.context_metrics.stop_reason}")
143
+ sections.append("")
144
+
145
+ # Mission Summary (1,000 tokens)
146
+ sections.append("## Mission Summary\n")
147
+ sections.append(
148
+ self.mission_summary
149
+ if self.mission_summary
150
+ else "_No mission summary provided_"
151
+ )
152
+ sections.append("")
153
+
154
+ # Accomplishments (2,000 tokens)
155
+ sections.append("## Accomplishments\n")
156
+ if self.accomplishments:
157
+ for i, item in enumerate(self.accomplishments, 1):
158
+ sections.append(f"{i}. {item}")
159
+ else:
160
+ sections.append("_No accomplishments recorded_")
161
+ sections.append("")
162
+
163
+ # Key Findings (2,500 tokens)
164
+ sections.append("## Key Findings\n")
165
+ if self.key_findings:
166
+ for i, finding in enumerate(self.key_findings, 1):
167
+ sections.append(f"{i}. {finding}")
168
+ else:
169
+ sections.append("_No key findings recorded_")
170
+ sections.append("")
171
+
172
+ # Decisions & Rationale (1,500 tokens)
173
+ sections.append("## Decisions & Rationale\n")
174
+ if self.decisions_made:
175
+ for i, decision in enumerate(self.decisions_made, 1):
176
+ decision_text = decision.get("decision", "")
177
+ rationale = decision.get("rationale", "")
178
+ sections.append(f"{i}. **Decision**: {decision_text}")
179
+ if rationale:
180
+ sections.append(f" **Rationale**: {rationale}")
181
+ else:
182
+ sections.append("_No decisions recorded_")
183
+ sections.append("")
184
+
185
+ # Next Steps (1,500 tokens)
186
+ sections.append("## Next Steps\n")
187
+ if self.next_steps:
188
+ for i, step in enumerate(self.next_steps, 1):
189
+ sections.append(f"{i}. {step}")
190
+ else:
191
+ sections.append("_No next steps defined_")
192
+ sections.append("")
193
+
194
+ # Critical Context (1,000 tokens)
195
+ sections.append("## Critical Context\n")
196
+ if self.critical_context:
197
+ for key, value in self.critical_context.items():
198
+ sections.append(f"- **{key}**: {value}")
199
+ else:
200
+ sections.append("_No critical context preserved_")
201
+ sections.append("")
202
+
203
+ # Metadata
204
+ sections.append("## Session Metadata\n")
205
+ if self.files_modified:
206
+ sections.append(f"**Files Modified** ({len(self.files_modified)}):")
207
+ for file in self.files_modified[:20]: # Limit to first 20
208
+ sections.append(f"- {file}")
209
+ if len(self.files_modified) > 20:
210
+ sections.append(f"- ... and {len(self.files_modified) - 20} more")
211
+ sections.append("")
212
+
213
+ if self.agents_used:
214
+ sections.append("**Agents Used**:")
215
+ for agent, count in self.agents_used.items():
216
+ sections.append(f"- {agent}: {count} delegations")
217
+ sections.append("")
218
+
219
+ if self.errors_encountered:
220
+ sections.append(f"**Errors** ({len(self.errors_encountered)}):")
221
+ for error in self.errors_encountered[:5]: # Limit to first 5
222
+ sections.append(f"- {error}")
223
+ sections.append("")
224
+
225
+ if self.warnings:
226
+ sections.append(f"**Warnings** ({len(self.warnings)}):")
227
+ for warning in self.warnings[:5]: # Limit to first 5
228
+ sections.append(f"- {warning}")
229
+ sections.append("")
230
+
231
+ return "\n".join(sections)
232
+
233
+ def to_dict(self) -> Dict[str, Any]:
234
+ """Convert to dictionary for JSON serialization."""
235
+ return {
236
+ "session_id": self.session_id,
237
+ "previous_session_id": self.previous_session_id,
238
+ "created_at": self.created_at,
239
+ "context_metrics": self.context_metrics.to_dict(),
240
+ "mission_summary": self.mission_summary,
241
+ "accomplishments": self.accomplishments,
242
+ "key_findings": self.key_findings,
243
+ "decisions_made": self.decisions_made,
244
+ "next_steps": self.next_steps,
245
+ "critical_context": self.critical_context,
246
+ "files_modified": self.files_modified,
247
+ "agents_used": self.agents_used,
248
+ "errors_encountered": self.errors_encountered,
249
+ "warnings": self.warnings,
250
+ }
251
+
252
+ @classmethod
253
+ def from_dict(cls, data: Dict[str, Any]) -> "ResumeLog":
254
+ """Create from dictionary."""
255
+ context_metrics_data = data.get("context_metrics", {})
256
+ context_metrics = ContextMetrics.from_dict(context_metrics_data)
257
+
258
+ return cls(
259
+ session_id=data.get("session_id", ""),
260
+ previous_session_id=data.get("previous_session_id"),
261
+ created_at=data.get("created_at", datetime.now(timezone.utc).isoformat()),
262
+ context_metrics=context_metrics,
263
+ mission_summary=data.get("mission_summary", ""),
264
+ accomplishments=data.get("accomplishments", []),
265
+ key_findings=data.get("key_findings", []),
266
+ decisions_made=data.get("decisions_made", []),
267
+ next_steps=data.get("next_steps", []),
268
+ critical_context=data.get("critical_context", {}),
269
+ files_modified=data.get("files_modified", []),
270
+ agents_used=data.get("agents_used", {}),
271
+ errors_encountered=data.get("errors_encountered", []),
272
+ warnings=data.get("warnings", []),
273
+ )
274
+
275
+ def save(self, storage_dir: Optional[Path] = None) -> Path:
276
+ """Save resume log to markdown file.
277
+
278
+ Args:
279
+ storage_dir: Directory to save the log (default: .claude-mpm/resume-logs)
280
+
281
+ Returns:
282
+ Path to saved file
283
+ """
284
+ if storage_dir is None:
285
+ storage_dir = Path.home() / ".claude-mpm" / "resume-logs"
286
+
287
+ storage_dir.mkdir(parents=True, exist_ok=True)
288
+
289
+ # Generate filename
290
+ file_path = storage_dir / f"session-{self.session_id}.md"
291
+
292
+ try:
293
+ # Write markdown file
294
+ markdown_content = self.to_markdown()
295
+ file_path.write_text(markdown_content, encoding="utf-8")
296
+
297
+ logger.info(f"Resume log saved: {file_path}")
298
+ return file_path
299
+
300
+ except Exception as e:
301
+ logger.error(f"Failed to save resume log: {e}")
302
+ raise
303
+
304
+ @classmethod
305
+ def load(
306
+ cls, session_id: str, storage_dir: Optional[Path] = None
307
+ ) -> Optional["ResumeLog"]:
308
+ """Load resume log from file.
309
+
310
+ Args:
311
+ session_id: Session ID to load
312
+ storage_dir: Directory to load from (default: .claude-mpm/resume-logs)
313
+
314
+ Returns:
315
+ ResumeLog instance or None if not found
316
+ """
317
+ if storage_dir is None:
318
+ storage_dir = Path.home() / ".claude-mpm" / "resume-logs"
319
+
320
+ file_path = storage_dir / f"session-{session_id}.md"
321
+
322
+ if not file_path.exists():
323
+ logger.debug(f"Resume log not found: {file_path}")
324
+ return None
325
+
326
+ try:
327
+ # For now, we just return the markdown content
328
+ # In the future, could parse markdown back to structured data
329
+ _ = file_path.read_text(encoding="utf-8")
330
+ logger.info(f"Resume log loaded: {file_path}")
331
+
332
+ # Return a basic ResumeLog with the markdown content embedded
333
+ return cls(
334
+ session_id=session_id,
335
+ mission_summary=f"Loaded from previous session. See full context in {file_path}",
336
+ )
337
+
338
+ except Exception as e:
339
+ logger.error(f"Failed to load resume log: {e}")
340
+ return None
@@ -678,7 +678,7 @@ class AutoConfigManagerService(BaseService, IAutoConfigManager):
678
678
  agent_id, agent_name, success=True
679
679
  )
680
680
  deployed.append(agent_id)
681
- self.logger.info(f"Successfully deployed agent: {agent_id}")
681
+ self.logger.debug(f"Successfully deployed agent: {agent_id}")
682
682
 
683
683
  except Exception as e:
684
684
  self.logger.error(
@@ -71,7 +71,7 @@ class AgentConfigurationManager:
71
71
  # Cache the result
72
72
  self._base_agent_cache = (base_agent_data, base_agent_version)
73
73
 
74
- self.logger.info(f"Loaded base agent from {self.base_agent_path}")
74
+ self.logger.debug(f"Loaded base agent from {self.base_agent_path}")
75
75
  return self._base_agent_cache
76
76
 
77
77
  except Exception as e:
@@ -107,7 +107,7 @@ class AgentRecordService(BaseService):
107
107
  record = self._deserialize_record(record_data)
108
108
  records[agent_name] = record
109
109
 
110
- self.logger.info(f"Loaded {len(records)} agent records")
110
+ self.logger.debug(f"Loaded {len(records)} agent records")
111
111
  else:
112
112
  self.logger.debug("No existing records file found")
113
113
 
@@ -329,10 +329,26 @@ class AgentValidator:
329
329
  "type": "agent", # Default type
330
330
  }
331
331
 
332
- # Extract from YAML frontmatter
332
+ # Extract ONLY from YAML frontmatter (between --- markers)
333
333
  lines = content.split("\n")
334
+ in_frontmatter = False
335
+ frontmatter_ended = False
336
+
334
337
  for line in lines:
335
338
  stripped_line = line.strip()
339
+
340
+ # Track frontmatter boundaries
341
+ if stripped_line == "---":
342
+ if not in_frontmatter:
343
+ in_frontmatter = True
344
+ continue
345
+ frontmatter_ended = True
346
+ break # Stop parsing after frontmatter ends
347
+
348
+ # Only parse within frontmatter
349
+ if not in_frontmatter or frontmatter_ended:
350
+ continue
351
+
336
352
  if stripped_line.startswith("name:"):
337
353
  agent_info["name"] = stripped_line.split(":", 1)[1].strip().strip("\"'")
338
354
  elif stripped_line.startswith("description:"):
@@ -224,7 +224,7 @@ class AsyncAgentDeploymentService:
224
224
 
225
225
  elapsed = (time.time() - start_time) * 1000
226
226
  self._metrics["time_saved_ms"] += max(0, (len(directories) * 75) - elapsed)
227
- self.logger.info(f"Discovered agents in {elapsed:.1f}ms (parallel scan)")
227
+ self.logger.debug(f"Discovered agents in {elapsed:.1f}ms (parallel scan)")
228
228
 
229
229
  return discovered
230
230
 
@@ -90,7 +90,7 @@ class LocalTemplateDeploymentService:
90
90
  logger.error(f"Failed to deploy local template {agent_id}: {e}")
91
91
  results["errors"].append(f"{agent_id}: {e}")
92
92
 
93
- logger.info(
93
+ logger.debug(
94
94
  f"Local template deployment: deployed={len(results['deployed'])}, "
95
95
  f"updated={len(results['updated'])}, skipped={len(results['skipped'])}, "
96
96
  f"errors={len(results['errors'])}"
@@ -182,7 +182,7 @@ class LocalAgentTemplateManager:
182
182
  self._discover_templates_in_dir(self.user_agents_dir, "user")
183
183
 
184
184
  self._cache_valid = True
185
- logger.info(f"Discovered {len(self._template_cache)} local agent templates")
185
+ logger.debug(f"Discovered {len(self._template_cache)} local agent templates")
186
186
 
187
187
  return self._template_cache
188
188
 
@@ -226,6 +226,53 @@ class AgentRecommenderService(BaseService, IAgentRecommender):
226
226
  if max_agents is not None:
227
227
  recommendations = recommendations[:max_agents]
228
228
 
229
+ # Check if toolchain is unknown and we have no recommendations
230
+ if not recommendations and toolchain.primary_language.lower() == "unknown":
231
+ self.logger.info("Toolchain unknown - applying default configuration")
232
+
233
+ # Get default configuration
234
+ default_config = self._capabilities_config.get("default_configuration", {})
235
+ if default_config.get("enabled", False):
236
+ default_agents = default_config.get("agents", [])
237
+ default_confidence = default_config.get("min_confidence", 0.7)
238
+
239
+ for default_agent in default_agents:
240
+ agent_id = default_agent.get("agent_id")
241
+
242
+ # Skip if agent doesn't exist in capabilities
243
+ if agent_id not in agent_configs:
244
+ self.logger.warning(f"Default agent not found: {agent_id}")
245
+ continue
246
+
247
+ agent_config = agent_configs[agent_id]
248
+ capabilities = self.get_agent_capabilities(agent_id)
249
+
250
+ recommendation = AgentRecommendation(
251
+ agent_id=agent_id,
252
+ agent_name=agent_config.get("name", agent_id),
253
+ confidence_score=default_confidence,
254
+ match_reasons=[
255
+ "Default configuration applied - toolchain detection unsuccessful",
256
+ default_agent.get("reasoning", "General-purpose agent"),
257
+ ],
258
+ concerns=[
259
+ "Consider manually selecting specialized agents for better results"
260
+ ],
261
+ capabilities=capabilities,
262
+ deployment_priority=default_agent.get("priority", 10),
263
+ configuration_hints={"default_deployment": True},
264
+ metadata={
265
+ "is_default": True,
266
+ "specialization": agent_config.get("specialization"),
267
+ "auto_deploy": agent_config.get("auto_deploy", True),
268
+ },
269
+ )
270
+ recommendations.append(recommendation)
271
+
272
+ self.logger.info(
273
+ f"Applied {len(recommendations)} default agent recommendations"
274
+ )
275
+
229
276
  self.logger.info(
230
277
  f"Generated {len(recommendations)} agent recommendations "
231
278
  f"for project: {toolchain.project_path}"