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
@@ -11,8 +11,10 @@ DESIGN DECISIONS:
11
11
  - Automatic session cleanup and archiving
12
12
  - Thread-safe session operations
13
13
  - Non-blocking validation with structured warnings
14
+ - Async-first design with periodic auto-save task
14
15
  """
15
16
 
17
+ import asyncio
16
18
  import gzip
17
19
  import json
18
20
  import uuid
@@ -217,8 +219,39 @@ class SessionManager(ISessionManager):
217
219
  self.config_service = config_service
218
220
  self.logger = get_logger("SessionManager")
219
221
  self._sessions_cache: Dict[str, SessionInfo] = {}
222
+ self._auto_save_task: Optional[asyncio.Task] = None
223
+ self._running = False
220
224
  self._load_sessions()
221
225
 
226
+ # Start auto-save task if enabled and event loop is running
227
+ if config_service:
228
+ auto_save_enabled = config_service.get("session.auto_save", True)
229
+ if auto_save_enabled:
230
+ self._start_auto_save()
231
+ else:
232
+ self.logger.info("Auto-save disabled by configuration")
233
+ else:
234
+ self.logger.debug("No config service provided, auto-save not started")
235
+
236
+ def _start_auto_save(self) -> None:
237
+ """Start the auto-save background task.
238
+
239
+ WHY: Separated from __init__ to allow safe initialization without event loop.
240
+ Can be called when event loop is available.
241
+ """
242
+ try:
243
+ loop = asyncio.get_running_loop()
244
+ self._running = True
245
+ self._auto_save_task = loop.create_task(self._periodic_session_save())
246
+ self.logger.info("Auto-save task started")
247
+ except RuntimeError:
248
+ # No event loop running, schedule for later
249
+ self.logger.debug(
250
+ "No event loop running, auto-save will start when loop is available"
251
+ )
252
+ # Set flag so we know to start it later
253
+ self._running = True
254
+
222
255
  def create_session(
223
256
  self, context: str = "default", options: Optional[Dict[str, Any]] = None
224
257
  ) -> SessionInfo:
@@ -477,6 +510,60 @@ class SessionManager(ISessionManager):
477
510
  self.logger.error(f"Failed to load sessions: {e}")
478
511
  self._sessions_cache = {}
479
512
 
513
+ async def _periodic_session_save(self) -> None:
514
+ """Periodically save sessions to disk.
515
+
516
+ WHY: Ensures sessions are persisted regularly to prevent data loss.
517
+ Follows the async pattern from EventAggregator._periodic_cleanup().
518
+ """
519
+ if not self.config_service:
520
+ self.logger.warning("No config service, cannot determine save interval")
521
+ return
522
+
523
+ save_interval = self.config_service.get("session.save_interval", 300)
524
+ self.logger.info(f"Starting periodic session save (interval: {save_interval}s)")
525
+
526
+ while self._running:
527
+ try:
528
+ await asyncio.sleep(save_interval)
529
+
530
+ if self._sessions_cache:
531
+ self._save_sessions()
532
+ self.logger.debug(
533
+ f"Auto-saved {len(self._sessions_cache)} session(s)"
534
+ )
535
+ else:
536
+ self.logger.debug("No sessions to save")
537
+
538
+ except asyncio.CancelledError:
539
+ self.logger.info("Auto-save task cancelled")
540
+ break
541
+ except Exception as e:
542
+ self.logger.error(f"Error in auto-save task: {e}")
543
+
544
+ async def cleanup(self) -> None:
545
+ """Clean up resources and stop background tasks.
546
+
547
+ WHY: Ensures graceful shutdown of the SessionManager and all background tasks.
548
+ """
549
+ self.logger.info("Shutting down SessionManager...")
550
+ self._running = False
551
+
552
+ # Cancel auto-save task
553
+ if self._auto_save_task and not self._auto_save_task.done():
554
+ self._auto_save_task.cancel()
555
+ try:
556
+ await self._auto_save_task
557
+ except asyncio.CancelledError:
558
+ pass
559
+
560
+ # Final save before shutdown
561
+ if self._sessions_cache:
562
+ self._save_sessions()
563
+ self.logger.info(f"Final save: {len(self._sessions_cache)} session(s)")
564
+
565
+ self.logger.info("SessionManager shutdown complete")
566
+
480
567
 
481
568
  # Context manager for session management
482
569
  class ManagedSession:
@@ -0,0 +1,504 @@
1
+ """Session Pause Manager Service.
2
+
3
+ WHY: This service creates session pause documents that capture complete conversation
4
+ context, git state, todos, and working directory for seamless resume.
5
+
6
+ DESIGN DECISIONS:
7
+ - Three format output (JSON, YAML, Markdown) for different use cases
8
+ - Atomic file operations using StateStorage
9
+ - Git integration for automatic commits
10
+ - Compatible with SessionResumeHelper for resume workflow
11
+ - LATEST-SESSION.txt pointer for quick access
12
+ """
13
+
14
+ import subprocess
15
+ from datetime import datetime, timezone
16
+ from pathlib import Path
17
+ from typing import Any, Dict, Optional
18
+
19
+ import yaml
20
+
21
+ from claude_mpm.core.logger import get_logger
22
+ from claude_mpm.storage.state_storage import StateStorage
23
+
24
+ logger = get_logger(__name__)
25
+
26
+
27
+ class SessionPauseManager:
28
+ """Manages creating pause sessions and capturing state."""
29
+
30
+ def __init__(self, project_path: Optional[Path] = None):
31
+ """Initialize session pause manager.
32
+
33
+ Args:
34
+ project_path: Project root path (default: current directory)
35
+ """
36
+ self.project_path = (project_path or Path.cwd()).resolve()
37
+ # Use flattened structure: .claude-mpm/sessions/ instead of sessions/pause/
38
+ self.pause_dir = self.project_path / ".claude-mpm" / "sessions"
39
+ self.pause_dir.mkdir(parents=True, exist_ok=True)
40
+ self.storage = StateStorage(self.pause_dir)
41
+
42
+ def create_pause_session(
43
+ self,
44
+ message: Optional[str] = None,
45
+ skip_commit: bool = False,
46
+ export_path: Optional[str] = None,
47
+ ) -> str:
48
+ """Create a pause session with captured state.
49
+
50
+ Args:
51
+ message: Optional pause reason/context message
52
+ skip_commit: Skip git commit of session state
53
+ export_path: Optional export location for session file
54
+
55
+ Returns:
56
+ Session ID
57
+
58
+ Raises:
59
+ Exception: If session creation fails
60
+ """
61
+ logger.info("Creating pause session")
62
+
63
+ # Generate session ID
64
+ session_id = f"session-{datetime.now(timezone.utc).strftime('%Y%m%d-%H%M%S')}"
65
+
66
+ # Capture state
67
+ state = self._capture_state(session_id, message)
68
+
69
+ # Save JSON format
70
+ json_path = self.pause_dir / f"{session_id}.json"
71
+ if not self.storage.write_json(state, json_path, atomic=True):
72
+ raise RuntimeError(f"Failed to write JSON to {json_path}")
73
+ logger.debug(f"Saved JSON: {json_path}")
74
+
75
+ # Save YAML format
76
+ yaml_path = self.pause_dir / f"{session_id}.yaml"
77
+ self._save_yaml(state, yaml_path)
78
+ logger.debug(f"Saved YAML: {yaml_path}")
79
+
80
+ # Save Markdown format
81
+ md_path = self.pause_dir / f"{session_id}.md"
82
+ md_content = self._generate_markdown(state)
83
+ md_path.write_text(md_content)
84
+ logger.debug(f"Saved Markdown: {md_path}")
85
+
86
+ # Update LATEST-SESSION.txt pointer
87
+ self._update_latest_pointer(session_id)
88
+
89
+ # Optional export
90
+ if export_path:
91
+ export_file = Path(export_path).resolve()
92
+ if not self.storage.write_json(state, export_file, atomic=True):
93
+ logger.warning(f"Failed to export to {export_file}")
94
+ else:
95
+ logger.info(f"Exported session to {export_file}")
96
+
97
+ # Optional git commit
98
+ if not skip_commit and self._is_git_repo():
99
+ self._commit_pause_session(session_id, message)
100
+
101
+ logger.info(f"Pause session created: {session_id}")
102
+ return session_id
103
+
104
+ def _capture_state(self, session_id: str, message: Optional[str]) -> Dict[str, Any]:
105
+ """Capture current session state.
106
+
107
+ Args:
108
+ session_id: Session identifier
109
+ message: Optional context message
110
+
111
+ Returns:
112
+ Complete state dictionary
113
+ """
114
+ # Get git context
115
+ git_context = self._get_git_context()
116
+
117
+ # Build state dictionary
118
+ return {
119
+ "session_id": session_id,
120
+ "paused_at": datetime.now(timezone.utc).isoformat(),
121
+ "duration_hours": 0, # Can be calculated if session start time known
122
+ "context_usage": {
123
+ "tokens_used": 0, # Would need Claude API integration
124
+ "tokens_total": 200000,
125
+ "percentage": 0,
126
+ },
127
+ "conversation": {
128
+ "primary_task": "Manual pause - see message below",
129
+ "current_phase": "In progress",
130
+ "summary": message or "No summary provided",
131
+ "accomplishments": [],
132
+ "next_steps": [],
133
+ },
134
+ "git_context": git_context,
135
+ "active_context": {
136
+ "working_directory": str(self.project_path),
137
+ },
138
+ "important_reminders": [],
139
+ "resume_instructions": {
140
+ "quick_start": [
141
+ f"Read {session_id}.md for full context",
142
+ "Run: git status to check current state",
143
+ "Run: cat .claude-mpm/sessions/LATEST-SESSION.txt",
144
+ ],
145
+ "files_to_review": [],
146
+ "validation_commands": {
147
+ "check_git": "git status && git log -1 --stat",
148
+ "check_session": f"cat .claude-mpm/sessions/{session_id}.md",
149
+ },
150
+ },
151
+ "open_questions": [],
152
+ "performance_metrics": {},
153
+ "todos": {"active": [], "completed": []},
154
+ "version": self._get_project_version(),
155
+ "build": "current",
156
+ "project_path": str(self.project_path),
157
+ }
158
+
159
+ def _get_git_context(self) -> Dict[str, Any]:
160
+ """Get git repository context.
161
+
162
+ Returns:
163
+ Git context dictionary
164
+ """
165
+ if not self._is_git_repo():
166
+ return {
167
+ "is_git_repo": False,
168
+ "branch": None,
169
+ "recent_commits": [],
170
+ "status": {
171
+ "clean": True,
172
+ "modified_files": [],
173
+ "untracked_files": [],
174
+ },
175
+ }
176
+
177
+ try:
178
+ # Get current branch
179
+ branch = subprocess.check_output(
180
+ ["git", "branch", "--show-current"],
181
+ cwd=self.project_path,
182
+ text=True,
183
+ stderr=subprocess.DEVNULL,
184
+ ).strip()
185
+
186
+ # Get recent commits (last 5)
187
+ commit_log = subprocess.check_output(
188
+ ["git", "log", "-5", "--pretty=format:%h|%an|%ai|%s"],
189
+ cwd=self.project_path,
190
+ text=True,
191
+ stderr=subprocess.DEVNULL,
192
+ ).strip()
193
+
194
+ recent_commits = []
195
+ for line in commit_log.split("\n"):
196
+ if line:
197
+ parts = line.split("|", 3)
198
+ if len(parts) == 4:
199
+ recent_commits.append(
200
+ {
201
+ "sha": parts[0],
202
+ "author": parts[1],
203
+ "timestamp": parts[2],
204
+ "message": parts[3],
205
+ }
206
+ )
207
+
208
+ # Get status
209
+ status_output = subprocess.check_output(
210
+ ["git", "status", "--porcelain"],
211
+ cwd=self.project_path,
212
+ text=True,
213
+ stderr=subprocess.DEVNULL,
214
+ ).strip()
215
+
216
+ modified_files = []
217
+ untracked_files = []
218
+ if status_output:
219
+ for line in status_output.split("\n"):
220
+ if line.startswith("??"):
221
+ untracked_files.append(line[3:])
222
+ elif line:
223
+ modified_files.append(line[3:])
224
+
225
+ return {
226
+ "is_git_repo": True,
227
+ "branch": branch,
228
+ "recent_commits": recent_commits,
229
+ "status": {
230
+ "clean": len(modified_files) == 0 and len(untracked_files) == 0,
231
+ "modified_files": modified_files,
232
+ "untracked_files": untracked_files,
233
+ },
234
+ }
235
+
236
+ except subprocess.CalledProcessError as e:
237
+ logger.warning(f"Git command failed: {e}")
238
+ return {
239
+ "is_git_repo": True,
240
+ "branch": "unknown",
241
+ "recent_commits": [],
242
+ "status": {"clean": True, "modified_files": [], "untracked_files": []},
243
+ }
244
+
245
+ def _is_git_repo(self) -> bool:
246
+ """Check if directory is a git repository.
247
+
248
+ Returns:
249
+ True if git repository exists
250
+ """
251
+ return (self.project_path / ".git").exists()
252
+
253
+ def _save_yaml(self, state: Dict[str, Any], yaml_path: Path) -> None:
254
+ """Save state as YAML format.
255
+
256
+ Args:
257
+ state: State dictionary
258
+ yaml_path: Target YAML file path
259
+ """
260
+ try:
261
+ with yaml_path.open("w") as f:
262
+ yaml.dump(
263
+ state,
264
+ f,
265
+ default_flow_style=False,
266
+ allow_unicode=True,
267
+ sort_keys=False,
268
+ )
269
+ except Exception as e:
270
+ logger.error(f"Failed to write YAML to {yaml_path}: {e}")
271
+ raise
272
+
273
+ def _generate_markdown(self, state: Dict[str, Any]) -> str:
274
+ """Generate human-readable markdown format.
275
+
276
+ Args:
277
+ state: State dictionary
278
+
279
+ Returns:
280
+ Markdown formatted string
281
+ """
282
+ session_id = state["session_id"]
283
+ paused_at = state["paused_at"]
284
+ conversation = state["conversation"]
285
+ git_context = state["git_context"]
286
+ active_context = state["active_context"]
287
+
288
+ lines = [
289
+ "# Claude MPM Session Pause Document",
290
+ "",
291
+ "## Session Metadata",
292
+ "",
293
+ f"**Session ID**: `{session_id}`",
294
+ f"**Paused At**: {paused_at}",
295
+ f"**Project**: `{state['project_path']}`",
296
+ f"**Version**: {state.get('version', 'unknown')}",
297
+ "",
298
+ "## What You Were Working On",
299
+ "",
300
+ f"**Primary Task**: {conversation['primary_task']}",
301
+ f"**Current Phase**: {conversation['current_phase']}",
302
+ "",
303
+ "**Summary**:",
304
+ f"{conversation['summary']}",
305
+ "",
306
+ ]
307
+
308
+ # Accomplishments
309
+ if conversation.get("accomplishments"):
310
+ lines.append("## Accomplishments This Session")
311
+ lines.append("")
312
+ for item in conversation["accomplishments"]:
313
+ lines.append(f"- {item}")
314
+ lines.append("")
315
+
316
+ # Next steps
317
+ if conversation.get("next_steps"):
318
+ lines.append("## Next Steps (Priority Order)")
319
+ lines.append("")
320
+ for i, step in enumerate(conversation["next_steps"], 1):
321
+ if isinstance(step, dict):
322
+ lines.append(
323
+ f"{i}. **{step.get('task', 'Unknown task')}** (Priority: {step.get('priority', '?')})"
324
+ )
325
+ if step.get("estimated_hours"):
326
+ lines.append(f" - Est. time: {step['estimated_hours']}")
327
+ if step.get("status"):
328
+ lines.append(f" - Status: {step['status']}")
329
+ if step.get("notes"):
330
+ lines.append(f" - Notes: {step['notes']}")
331
+ else:
332
+ lines.append(f"{i}. {step}")
333
+ lines.append("")
334
+
335
+ # Active context
336
+ lines.extend(
337
+ [
338
+ "## Active Context",
339
+ "",
340
+ f"**Working Directory**: `{active_context['working_directory']}`",
341
+ "",
342
+ ]
343
+ )
344
+
345
+ # Git context
346
+ lines.append("## Git Context")
347
+ lines.append("")
348
+ if git_context["is_git_repo"]:
349
+ lines.append(f"**Branch**: `{git_context['branch']}`")
350
+ lines.append(
351
+ f"**Status**: {'Clean' if git_context['status']['clean'] else 'Modified'}"
352
+ )
353
+ lines.append("")
354
+
355
+ if git_context["status"]["modified_files"]:
356
+ lines.append("**Modified files**:")
357
+ for f in git_context["status"]["modified_files"][:10]:
358
+ lines.append(f"- `{f}`")
359
+ lines.append("")
360
+
361
+ if git_context["recent_commits"]:
362
+ lines.append("**Recent commits**:")
363
+ for commit in git_context["recent_commits"]:
364
+ lines.append(
365
+ f"- `{commit['sha']}` - {commit['message']} ({commit['author']})"
366
+ )
367
+ lines.append("")
368
+ else:
369
+ lines.append("*Not a git repository*")
370
+ lines.append("")
371
+
372
+ # Important reminders
373
+ if state.get("important_reminders"):
374
+ lines.append("## Important Reminders")
375
+ lines.append("")
376
+ for reminder in state["important_reminders"]:
377
+ lines.append(f"- {reminder}")
378
+ lines.append("")
379
+
380
+ # Resume instructions
381
+ lines.extend(
382
+ [
383
+ "## Resume Instructions",
384
+ "",
385
+ "### Quick Resume (5 minutes)",
386
+ "",
387
+ ]
388
+ )
389
+ for instruction in state["resume_instructions"]["quick_start"]:
390
+ lines.append(f"1. {instruction}")
391
+ lines.append("")
392
+
393
+ if state["resume_instructions"]["validation_commands"]:
394
+ lines.append("### Validation Commands")
395
+ lines.append("")
396
+ lines.append("```bash")
397
+ for cmd in state["resume_instructions"]["validation_commands"].values():
398
+ lines.append(cmd)
399
+ lines.append("```")
400
+ lines.append("")
401
+
402
+ # Footer
403
+ lines.extend(
404
+ [
405
+ "---",
406
+ "",
407
+ "Resume with: `/mpm-init resume` or `cat .claude-mpm/sessions/LATEST-SESSION.txt`",
408
+ "",
409
+ ]
410
+ )
411
+
412
+ return "\n".join(lines)
413
+
414
+ def _update_latest_pointer(self, session_id: str) -> None:
415
+ """Update LATEST-SESSION.txt pointer.
416
+
417
+ Args:
418
+ session_id: Session identifier
419
+ """
420
+ try:
421
+ latest_file = self.pause_dir / "LATEST-SESSION.txt"
422
+ content = f"""Latest Session: {session_id}
423
+ Paused At: {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S %Z')}
424
+ Project: {self.project_path}
425
+
426
+ Files:
427
+ - {session_id}.json (machine-readable)
428
+ - {session_id}.yaml (human-readable config)
429
+ - {session_id}.md (documentation)
430
+
431
+ Quick Resume:
432
+ /mpm-init resume
433
+
434
+ Full Context:
435
+ cat .claude-mpm/sessions/{session_id}.md
436
+
437
+ Validation:
438
+ git status && git log -1 --stat
439
+ """
440
+ latest_file.write_text(content)
441
+ logger.debug(f"Updated LATEST-SESSION.txt: {session_id}")
442
+ except Exception as e:
443
+ logger.warning(f"Failed to update LATEST-SESSION.txt: {e}")
444
+
445
+ def _commit_pause_session(self, session_id: str, message: Optional[str]) -> None:
446
+ """Create git commit for pause session.
447
+
448
+ Args:
449
+ session_id: Session identifier
450
+ message: Optional context message
451
+ """
452
+ try:
453
+ # Add session files
454
+ subprocess.run(
455
+ ["git", "add", ".claude-mpm/sessions/"],
456
+ cwd=self.project_path,
457
+ check=True,
458
+ capture_output=True,
459
+ )
460
+
461
+ # Build commit message
462
+ commit_msg = f"session: pause at {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}\n\nSession ID: {session_id}"
463
+ if message:
464
+ commit_msg += f"\nContext: {message}"
465
+
466
+ # Create commit
467
+ subprocess.run(
468
+ ["git", "commit", "-m", commit_msg],
469
+ cwd=self.project_path,
470
+ check=True,
471
+ capture_output=True,
472
+ )
473
+
474
+ logger.info(f"Created git commit for pause session: {session_id}")
475
+
476
+ except subprocess.CalledProcessError as e:
477
+ # Non-fatal - pause still succeeded
478
+ logger.warning(f"Failed to create git commit: {e.stderr.decode()}")
479
+
480
+ def _get_project_version(self) -> str:
481
+ """Get project version from pyproject.toml or package.
482
+
483
+ Returns:
484
+ Version string or 'unknown'
485
+ """
486
+ try:
487
+ # Try pyproject.toml
488
+ pyproject = self.project_path / "pyproject.toml"
489
+ if pyproject.exists():
490
+ content = pyproject.read_text()
491
+ for line in content.split("\n"):
492
+ if line.startswith("version"):
493
+ return line.split("=")[1].strip().strip('"')
494
+
495
+ # Try package __version__
496
+ import claude_mpm
497
+
498
+ if hasattr(claude_mpm, "__version__"):
499
+ return claude_mpm.__version__
500
+
501
+ except Exception:
502
+ pass
503
+
504
+ return "unknown"