claude-mpm 4.11.0__py3-none-any.whl → 4.11.1__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/cli/commands/mpm_init.py +216 -162
- claude_mpm/cli/commands/mpm_init_handler.py +14 -25
- claude_mpm/cli/parsers/mpm_init_parser.py +32 -31
- claude_mpm/commands/mpm-init.md +129 -78
- claude_mpm/utils/git_analyzer.py +305 -0
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/METADATA +1 -1
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/RECORD +12 -13
- claude_mpm/cli/commands/session_pause_manager.py +0 -389
- claude_mpm/cli/commands/session_resume_manager.py +0 -523
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/WHEEL +0 -0
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.11.0.dist-info → claude_mpm-4.11.1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
2
|
-
claude_mpm/VERSION,sha256
|
2
|
+
claude_mpm/VERSION,sha256=-pg3IRz2ArU7tcZ-4AUcxOirJEkowIcnQL1C8A2FelI,7
|
3
3
|
claude_mpm/__init__.py,sha256=UCw6j9e_tZQ3kJtTqmdfNv7MHyw9nD1jkj80WurwM2g,2064
|
4
4
|
claude_mpm/__main__.py,sha256=Ro5UBWBoQaSAIoSqWAr7zkbLyvi4sSy28WShqAhKJG0,723
|
5
5
|
claude_mpm/constants.py,sha256=sLjJF6Kw7H4V9WWeaEYltM-77TgXqzEMX5vx4ukM5-0,5977
|
@@ -100,12 +100,10 @@ claude_mpm/cli/commands/mcp_setup_external.py,sha256=hfBHkaioNa0JRDhahNEc8agyrUw
|
|
100
100
|
claude_mpm/cli/commands/mcp_tool_commands.py,sha256=q17GzlFT3JiLTrDqwPO2tz1-fKmPO5QU449syTnKTz4,1283
|
101
101
|
claude_mpm/cli/commands/memory.py,sha256=O4T5HGL-Ob_QPt2dZHQvoOrVohnaDKrBjyngq1Mcv1w,26185
|
102
102
|
claude_mpm/cli/commands/monitor.py,sha256=Fjb68hf3dEwTFek2LV8Nh6iU0qEkY7qYlOn32IwNaNg,9566
|
103
|
-
claude_mpm/cli/commands/mpm_init.py,sha256=
|
104
|
-
claude_mpm/cli/commands/mpm_init_handler.py,sha256=
|
103
|
+
claude_mpm/cli/commands/mpm_init.py,sha256=oNEF_hwpu7LCvj06vqXbmVJpXm7yo4ebODyQcZN52nM,74346
|
104
|
+
claude_mpm/cli/commands/mpm_init_handler.py,sha256=FZRoo_zfEakLc_W1G_ej_bhxtaU6ybngyBP5GLxaKD4,4583
|
105
105
|
claude_mpm/cli/commands/run.py,sha256=PB2H55piOPTy4yo4OBgbUCjMlcz9K79wbwpxQVc9m5Q,48225
|
106
106
|
claude_mpm/cli/commands/search.py,sha256=alv6udvKcn-xkqeBlLuPRvfSDV1yxEX4n9mjjRT5uLM,16581
|
107
|
-
claude_mpm/cli/commands/session_pause_manager.py,sha256=YCUrxOdsoFRKCqlAZJVk6x1HYcece8xqGTl7eCiPx9A,13123
|
108
|
-
claude_mpm/cli/commands/session_resume_manager.py,sha256=qWFQv3eLhorCln0rtgM8FOGjFlfFNs5c8ctYehgiYqI,18087
|
109
107
|
claude_mpm/cli/commands/tickets.py,sha256=kl2dklTBnG3Y4jUUJ_PcEVsTx4CtVJfkGWboWBx_mQM,21234
|
110
108
|
claude_mpm/cli/commands/uninstall.py,sha256=KGlVG6veEs1efLVjrZ3wSty7e1zVR9wpt-VXQA1RzWw,5945
|
111
109
|
claude_mpm/cli/commands/upgrade.py,sha256=NYMVONNlj78WHoQ6eyVInroE95AeQxUY2_TpjYFTdYE,5409
|
@@ -125,7 +123,7 @@ claude_mpm/cli/parsers/debug_parser.py,sha256=F7MZdmiXiPfiIPMv21ZUqB2cMT8Ho1LDmp
|
|
125
123
|
claude_mpm/cli/parsers/mcp_parser.py,sha256=2j6ULhdu55Z2k_-Gu2QxIsFoTQFbDCEMSGePXSuPoQQ,6532
|
126
124
|
claude_mpm/cli/parsers/memory_parser.py,sha256=ZwCDxJEgp-w03L-1tZsWTgisiwamP42s424bA5bvDJc,4760
|
127
125
|
claude_mpm/cli/parsers/monitor_parser.py,sha256=PeoznSi_5Bw6THK_Espl8M20o6dKvvBSmFzAbovkaFQ,4920
|
128
|
-
claude_mpm/cli/parsers/mpm_init_parser.py,sha256=
|
126
|
+
claude_mpm/cli/parsers/mpm_init_parser.py,sha256=EVyHc7KYpsyxhomZAUnlGS6kq43ix2wjrHLiM5Gf34E,9684
|
129
127
|
claude_mpm/cli/parsers/run_parser.py,sha256=cs34qNonFZG8uYxTYEt0rXi2LcPz3pw8D8hxiywih6w,4927
|
130
128
|
claude_mpm/cli/parsers/search_parser.py,sha256=L8-65kndg-zutSKpzj-eCvTNkeySCZ-WlSHdhk7pEak,6916
|
131
129
|
claude_mpm/cli/parsers/tickets_parser.py,sha256=FYl-VNH7PrZzfZUCcjnf6F7g6JXnL8YDxwrmR5svIcg,6966
|
@@ -143,7 +141,7 @@ claude_mpm/commands/mpm-agents.md,sha256=JnYPJ-eWvIEEtiCB6iPu182P2xDBRvU3ArVXQ7h
|
|
143
141
|
claude_mpm/commands/mpm-config.md,sha256=79Eb-srRpEVV3HCHDHZc8SKec6_LVP6HbXDEVkZKLgw,2929
|
144
142
|
claude_mpm/commands/mpm-doctor.md,sha256=ut5LhFKVRw-2ecjMSPsnaTiRuFXa6Q9t-Wgl3CCnQvk,590
|
145
143
|
claude_mpm/commands/mpm-help.md,sha256=zfhpE0Fd-wW5zWmYYAMRMT-xYK8saqbw-HXRD7csJHI,2850
|
146
|
-
claude_mpm/commands/mpm-init.md,sha256=
|
144
|
+
claude_mpm/commands/mpm-init.md,sha256=wwYHkToq8U5ALdhu8bDPygqAsKZ77aMaai7ZJC3oBqU,16054
|
147
145
|
claude_mpm/commands/mpm-monitor.md,sha256=onTHf9Yac1KkdZdENtY2Q5jyw0A-vZLYgoKkPCtZLUY,12193
|
148
146
|
claude_mpm/commands/mpm-organize.md,sha256=T-ysjhwgfW9irjUj02vuY_1jeMdabO_zxcShyjmqsiM,10153
|
149
147
|
claude_mpm/commands/mpm-status.md,sha256=oaM4ybL4ffp55nkT9F0mp_5H4tF-wX9mbqK-LEKEqUU,1919
|
@@ -789,6 +787,7 @@ claude_mpm/utils/environment_context.py,sha256=mCnRJqQLTyaAv-7M4bp9N9WqVgfSQb5xb
|
|
789
787
|
claude_mpm/utils/error_handler.py,sha256=RWL7DnXttJKCgYhevUm9XlMC33rEhX2CXo1IiCtwV4g,7969
|
790
788
|
claude_mpm/utils/file_utils.py,sha256=pv3MEKLsn4WIOra5JoHnCm_FaJbNcKMuS3EKuXAWyLc,7859
|
791
789
|
claude_mpm/utils/framework_detection.py,sha256=dCY-N0HzuYS9KBg1BjDw8mJnNb22JqmKBoodjTMzd5w,1183
|
790
|
+
claude_mpm/utils/git_analyzer.py,sha256=JZwahnhgPXsITRnq7jJBNK4or82bZjazQI-VGyXocP4,9323
|
792
791
|
claude_mpm/utils/import_migration_example.py,sha256=sKlV-apfkHDJzzez6uubHXjx91YF_5HPuzu2sLs_U0E,989
|
793
792
|
claude_mpm/utils/imports.py,sha256=IGcW0W6V-2jTmkVlO-l0Zodo3LRA3zkaumD9J2qn7dE,6654
|
794
793
|
claude_mpm/utils/log_cleanup.py,sha256=1d8K1V2uOFPGJJKFOk6E_AMj22sifAibOg-VQyGooWo,22301
|
@@ -799,9 +798,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
799
798
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
800
799
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
801
800
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
802
|
-
claude_mpm-4.11.
|
803
|
-
claude_mpm-4.11.
|
804
|
-
claude_mpm-4.11.
|
805
|
-
claude_mpm-4.11.
|
806
|
-
claude_mpm-4.11.
|
807
|
-
claude_mpm-4.11.
|
801
|
+
claude_mpm-4.11.1.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
802
|
+
claude_mpm-4.11.1.dist-info/METADATA,sha256=Wgt4BuOeYfVIUCmhtlUQwLKtCF4RAC_AEnw1W0_5pmI,17967
|
803
|
+
claude_mpm-4.11.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
804
|
+
claude_mpm-4.11.1.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
805
|
+
claude_mpm-4.11.1.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
806
|
+
claude_mpm-4.11.1.dist-info/RECORD,,
|
@@ -1,389 +0,0 @@
|
|
1
|
-
"""
|
2
|
-
Session Pause Manager - Captures and saves session state for later resumption.
|
3
|
-
|
4
|
-
This module provides functionality to pause a Claude MPM session by capturing:
|
5
|
-
- Conversation context and progress
|
6
|
-
- Git repository state
|
7
|
-
- Todo list status
|
8
|
-
- Working directory changes
|
9
|
-
|
10
|
-
The saved state enables seamless session resumption with full context.
|
11
|
-
"""
|
12
|
-
|
13
|
-
import subprocess
|
14
|
-
from datetime import datetime, timezone
|
15
|
-
from pathlib import Path
|
16
|
-
from typing import Any, Dict, List, Optional
|
17
|
-
|
18
|
-
from rich.console import Console
|
19
|
-
from rich.panel import Panel
|
20
|
-
|
21
|
-
from claude_mpm.core.logging_utils import get_logger
|
22
|
-
from claude_mpm.storage.state_storage import StateStorage
|
23
|
-
|
24
|
-
logger = get_logger(__name__)
|
25
|
-
console = Console()
|
26
|
-
|
27
|
-
|
28
|
-
class SessionPauseManager:
|
29
|
-
"""Manages pausing and saving session state."""
|
30
|
-
|
31
|
-
def __init__(self, project_path: Path):
|
32
|
-
"""Initialize Session Pause Manager.
|
33
|
-
|
34
|
-
Args:
|
35
|
-
project_path: Path to the project directory
|
36
|
-
"""
|
37
|
-
self.project_path = project_path
|
38
|
-
self.session_dir = project_path / ".claude-mpm" / "sessions" / "pause"
|
39
|
-
self.storage = StateStorage()
|
40
|
-
|
41
|
-
def pause_session(
|
42
|
-
self,
|
43
|
-
conversation_summary: Optional[str] = None,
|
44
|
-
accomplishments: Optional[List[str]] = None,
|
45
|
-
next_steps: Optional[List[str]] = None,
|
46
|
-
todos_active: Optional[List[Dict[str, str]]] = None,
|
47
|
-
todos_completed: Optional[List[Dict[str, str]]] = None,
|
48
|
-
) -> Dict[str, Any]:
|
49
|
-
"""Pause the current session and save state.
|
50
|
-
|
51
|
-
Args:
|
52
|
-
conversation_summary: Summary of what was being worked on
|
53
|
-
accomplishments: List of things accomplished in this session
|
54
|
-
next_steps: List of next steps to continue work
|
55
|
-
todos_active: Active todo items
|
56
|
-
todos_completed: Completed todo items
|
57
|
-
|
58
|
-
Returns:
|
59
|
-
Dict containing pause result with session_file path
|
60
|
-
"""
|
61
|
-
try:
|
62
|
-
# Ensure session directory exists
|
63
|
-
self.session_dir.mkdir(parents=True, exist_ok=True)
|
64
|
-
|
65
|
-
# Generate session ID
|
66
|
-
timestamp = datetime.now(timezone.utc)
|
67
|
-
session_id = f"session-{timestamp.strftime('%Y%m%d-%H%M%S')}"
|
68
|
-
|
69
|
-
# Capture git context
|
70
|
-
git_context = self._capture_git_context()
|
71
|
-
|
72
|
-
# Build session state
|
73
|
-
session_state = {
|
74
|
-
"session_id": session_id,
|
75
|
-
"paused_at": timestamp.isoformat(),
|
76
|
-
"conversation": {
|
77
|
-
"summary": conversation_summary
|
78
|
-
or "Session paused - context not provided",
|
79
|
-
"accomplishments": accomplishments or [],
|
80
|
-
"next_steps": next_steps or [],
|
81
|
-
},
|
82
|
-
"git_context": git_context,
|
83
|
-
"todos": {
|
84
|
-
"active": todos_active or [],
|
85
|
-
"completed": todos_completed or [],
|
86
|
-
},
|
87
|
-
"version": self._get_version(),
|
88
|
-
"build": self._get_build_number(),
|
89
|
-
"project_path": str(self.project_path),
|
90
|
-
}
|
91
|
-
|
92
|
-
# Save session state to file
|
93
|
-
session_file = self.session_dir / f"{session_id}.json"
|
94
|
-
success = self.storage.write_json(
|
95
|
-
session_state, session_file, atomic=True, compress=False
|
96
|
-
)
|
97
|
-
|
98
|
-
if not success:
|
99
|
-
return {
|
100
|
-
"status": "error",
|
101
|
-
"message": "Failed to write session state file",
|
102
|
-
}
|
103
|
-
|
104
|
-
# Create git commit with session state
|
105
|
-
commit_success = self._create_pause_commit(session_id, conversation_summary)
|
106
|
-
|
107
|
-
# Display success message
|
108
|
-
self._display_pause_success(session_id, session_file, commit_success)
|
109
|
-
|
110
|
-
return {
|
111
|
-
"status": "success",
|
112
|
-
"session_id": session_id,
|
113
|
-
"session_file": str(session_file),
|
114
|
-
"git_commit_created": commit_success,
|
115
|
-
"message": f"Session paused: {session_id}",
|
116
|
-
}
|
117
|
-
|
118
|
-
except Exception as e:
|
119
|
-
logger.error(f"Failed to pause session: {e}")
|
120
|
-
return {"status": "error", "message": str(e)}
|
121
|
-
|
122
|
-
def _capture_git_context(self) -> Dict[str, Any]:
|
123
|
-
"""Capture current git repository state.
|
124
|
-
|
125
|
-
Returns:
|
126
|
-
Dict containing git context information
|
127
|
-
"""
|
128
|
-
git_context: Dict[str, Any] = {
|
129
|
-
"is_git_repo": False,
|
130
|
-
"branch": None,
|
131
|
-
"recent_commits": [],
|
132
|
-
"status": {"clean": True, "modified_files": [], "untracked_files": []},
|
133
|
-
}
|
134
|
-
|
135
|
-
try:
|
136
|
-
# Check if git repo
|
137
|
-
result = subprocess.run(
|
138
|
-
["git", "rev-parse", "--git-dir"],
|
139
|
-
cwd=str(self.project_path),
|
140
|
-
capture_output=True,
|
141
|
-
text=True,
|
142
|
-
check=False,
|
143
|
-
)
|
144
|
-
|
145
|
-
if result.returncode != 0:
|
146
|
-
return git_context
|
147
|
-
|
148
|
-
git_context["is_git_repo"] = True
|
149
|
-
|
150
|
-
# Get current branch
|
151
|
-
result = subprocess.run(
|
152
|
-
["git", "branch", "--show-current"],
|
153
|
-
cwd=str(self.project_path),
|
154
|
-
capture_output=True,
|
155
|
-
text=True,
|
156
|
-
check=True,
|
157
|
-
)
|
158
|
-
git_context["branch"] = result.stdout.strip()
|
159
|
-
|
160
|
-
# Get recent commits (last 10)
|
161
|
-
result = subprocess.run(
|
162
|
-
["git", "log", "--format=%h|%an|%ai|%s", "-10"],
|
163
|
-
cwd=str(self.project_path),
|
164
|
-
capture_output=True,
|
165
|
-
text=True,
|
166
|
-
check=True,
|
167
|
-
)
|
168
|
-
|
169
|
-
commits = []
|
170
|
-
for line in result.stdout.strip().split("\n"):
|
171
|
-
if not line:
|
172
|
-
continue
|
173
|
-
parts = line.split("|", 3)
|
174
|
-
if len(parts) == 4:
|
175
|
-
sha, author, timestamp_str, message = parts
|
176
|
-
commits.append(
|
177
|
-
{
|
178
|
-
"sha": sha,
|
179
|
-
"author": author,
|
180
|
-
"timestamp": timestamp_str,
|
181
|
-
"message": message,
|
182
|
-
}
|
183
|
-
)
|
184
|
-
git_context["recent_commits"] = commits
|
185
|
-
|
186
|
-
# Get status
|
187
|
-
result = subprocess.run(
|
188
|
-
["git", "status", "--porcelain"],
|
189
|
-
cwd=str(self.project_path),
|
190
|
-
capture_output=True,
|
191
|
-
text=True,
|
192
|
-
check=True,
|
193
|
-
)
|
194
|
-
|
195
|
-
modified_files = []
|
196
|
-
untracked_files = []
|
197
|
-
|
198
|
-
for line in result.stdout.strip().split("\n"):
|
199
|
-
if not line:
|
200
|
-
continue
|
201
|
-
status_code = line[:2]
|
202
|
-
file_path = line[3:]
|
203
|
-
|
204
|
-
if status_code.startswith("??"):
|
205
|
-
untracked_files.append(file_path)
|
206
|
-
else:
|
207
|
-
modified_files.append(file_path)
|
208
|
-
|
209
|
-
git_context["status"] = {
|
210
|
-
"clean": len(modified_files) == 0 and len(untracked_files) == 0,
|
211
|
-
"modified_files": modified_files,
|
212
|
-
"untracked_files": untracked_files,
|
213
|
-
}
|
214
|
-
|
215
|
-
except Exception as e:
|
216
|
-
logger.warning(f"Could not capture git context: {e}")
|
217
|
-
|
218
|
-
return git_context
|
219
|
-
|
220
|
-
def _get_version(self) -> str:
|
221
|
-
"""Get Claude MPM version.
|
222
|
-
|
223
|
-
Returns:
|
224
|
-
Version string or "unknown"
|
225
|
-
"""
|
226
|
-
try:
|
227
|
-
version_file = Path(__file__).parent.parent.parent.parent.parent / "VERSION"
|
228
|
-
if version_file.exists():
|
229
|
-
return version_file.read_text().strip()
|
230
|
-
except Exception:
|
231
|
-
pass
|
232
|
-
return "unknown"
|
233
|
-
|
234
|
-
def _get_build_number(self) -> str:
|
235
|
-
"""Get Claude MPM build number.
|
236
|
-
|
237
|
-
Returns:
|
238
|
-
Build number string or "unknown"
|
239
|
-
"""
|
240
|
-
try:
|
241
|
-
build_file = (
|
242
|
-
Path(__file__).parent.parent.parent.parent.parent / "BUILD_NUMBER"
|
243
|
-
)
|
244
|
-
if build_file.exists():
|
245
|
-
return build_file.read_text().strip()
|
246
|
-
except Exception:
|
247
|
-
pass
|
248
|
-
return "unknown"
|
249
|
-
|
250
|
-
def _create_pause_commit(self, session_id: str, summary: Optional[str]) -> bool:
|
251
|
-
"""Create git commit with session pause information.
|
252
|
-
|
253
|
-
Args:
|
254
|
-
session_id: The session identifier
|
255
|
-
summary: Optional summary of what was being worked on
|
256
|
-
|
257
|
-
Returns:
|
258
|
-
True if commit was created successfully
|
259
|
-
"""
|
260
|
-
try:
|
261
|
-
# Check if we're in a git repo
|
262
|
-
result = subprocess.run(
|
263
|
-
["git", "rev-parse", "--git-dir"],
|
264
|
-
cwd=str(self.project_path),
|
265
|
-
capture_output=True,
|
266
|
-
text=True,
|
267
|
-
check=False,
|
268
|
-
)
|
269
|
-
|
270
|
-
if result.returncode != 0:
|
271
|
-
logger.debug("Not a git repository, skipping commit")
|
272
|
-
return False
|
273
|
-
|
274
|
-
# Add session files to git
|
275
|
-
subprocess.run(
|
276
|
-
["git", "add", ".claude-mpm/sessions/pause/"],
|
277
|
-
cwd=str(self.project_path),
|
278
|
-
capture_output=True,
|
279
|
-
check=False,
|
280
|
-
)
|
281
|
-
|
282
|
-
# Check if there are changes to commit
|
283
|
-
result = subprocess.run(
|
284
|
-
["git", "diff", "--cached", "--quiet"],
|
285
|
-
cwd=str(self.project_path),
|
286
|
-
check=False,
|
287
|
-
)
|
288
|
-
|
289
|
-
if result.returncode == 0:
|
290
|
-
logger.debug("No changes to commit")
|
291
|
-
return False
|
292
|
-
|
293
|
-
# Build commit message
|
294
|
-
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC")
|
295
|
-
context = summary or "Session paused"
|
296
|
-
|
297
|
-
commit_message = f"""session: pause at {timestamp}
|
298
|
-
|
299
|
-
Session ID: {session_id}
|
300
|
-
Context: {context}
|
301
|
-
|
302
|
-
🤖👥 Generated with [Claude MPM](https://github.com/bobmatnyc/claude-mpm)
|
303
|
-
|
304
|
-
Co-Authored-By: Claude <noreply@anthropic.com>"""
|
305
|
-
|
306
|
-
# Create commit
|
307
|
-
result = subprocess.run(
|
308
|
-
["git", "commit", "-m", commit_message],
|
309
|
-
cwd=str(self.project_path),
|
310
|
-
capture_output=True,
|
311
|
-
text=True,
|
312
|
-
check=False,
|
313
|
-
)
|
314
|
-
|
315
|
-
if result.returncode == 0:
|
316
|
-
logger.info(f"Created pause commit for session {session_id}")
|
317
|
-
return True
|
318
|
-
logger.warning(f"Could not create commit: {result.stderr}")
|
319
|
-
return False
|
320
|
-
|
321
|
-
except Exception as e:
|
322
|
-
logger.warning(f"Could not create pause commit: {e}")
|
323
|
-
return False
|
324
|
-
|
325
|
-
def _display_pause_success(
|
326
|
-
self, session_id: str, session_file: Path, commit_created: bool
|
327
|
-
) -> None:
|
328
|
-
"""Display success message for paused session.
|
329
|
-
|
330
|
-
Args:
|
331
|
-
session_id: The session identifier
|
332
|
-
session_file: Path to the session state file
|
333
|
-
commit_created: Whether git commit was created
|
334
|
-
"""
|
335
|
-
console.print()
|
336
|
-
console.print(
|
337
|
-
Panel(
|
338
|
-
f"[bold green]Session Paused Successfully[/bold green]\n\n"
|
339
|
-
f"[cyan]Session ID:[/cyan] {session_id}\n"
|
340
|
-
f"[cyan]State saved:[/cyan] {session_file}\n"
|
341
|
-
f"[cyan]Git commit:[/cyan] {'✓ Created' if commit_created else '✗ Not created (no git repo or no changes)'}\n\n"
|
342
|
-
f"[dim]To resume this session later, run:[/dim]\n"
|
343
|
-
f"[yellow] claude-mpm mpm-init pause resume[/yellow]",
|
344
|
-
title="🔴 Session Pause",
|
345
|
-
border_style="green",
|
346
|
-
)
|
347
|
-
)
|
348
|
-
console.print()
|
349
|
-
|
350
|
-
def list_paused_sessions(self) -> List[Dict[str, Any]]:
|
351
|
-
"""List all paused sessions.
|
352
|
-
|
353
|
-
Returns:
|
354
|
-
List of session information dicts
|
355
|
-
"""
|
356
|
-
if not self.session_dir.exists():
|
357
|
-
return []
|
358
|
-
|
359
|
-
sessions = []
|
360
|
-
for session_file in sorted(self.session_dir.glob("session-*.json")):
|
361
|
-
try:
|
362
|
-
state = self.storage.read_json(session_file)
|
363
|
-
if state:
|
364
|
-
sessions.append(
|
365
|
-
{
|
366
|
-
"session_id": state.get("session_id"),
|
367
|
-
"paused_at": state.get("paused_at"),
|
368
|
-
"summary": state.get("conversation", {}).get("summary"),
|
369
|
-
"file_path": str(session_file),
|
370
|
-
}
|
371
|
-
)
|
372
|
-
except Exception as e:
|
373
|
-
logger.warning(f"Could not read session file {session_file}: {e}")
|
374
|
-
|
375
|
-
return sessions
|
376
|
-
|
377
|
-
def get_latest_session(self) -> Optional[Dict[str, Any]]:
|
378
|
-
"""Get the most recent paused session.
|
379
|
-
|
380
|
-
Returns:
|
381
|
-
Session state dict or None if no sessions found
|
382
|
-
"""
|
383
|
-
sessions = self.list_paused_sessions()
|
384
|
-
if not sessions:
|
385
|
-
return None
|
386
|
-
|
387
|
-
# Return the most recent (last in sorted list)
|
388
|
-
latest_file = Path(sessions[-1]["file_path"])
|
389
|
-
return self.storage.read_json(latest_file)
|