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.
@@ -1,5 +1,5 @@
1
1
  claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
2
- claude_mpm/VERSION,sha256=FFRZhOvxPjL4clpLdj7K74TxFIkhB1BHeUD9jed261o,7
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=dufJ9WQzS5mbEdpSHqOWTk2rBmSO8xkxSJ1D8gsgzZk,72451
104
- claude_mpm/cli/commands/mpm_init_handler.py,sha256=M8S2mRl-wyjM8biP-hRl5V1UYOtYxDyD2KQTAhSw8mM,4869
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=1FpjO6-5q0Fqgd2w3RvGVDX5tW8iG5zGJhYq3GibvJI,9531
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=WcEj7r4HyTHQc7-NVpYlDNOtZrlMs4VH-vm3K1X7At8,13329
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.0.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
803
- claude_mpm-4.11.0.dist-info/METADATA,sha256=MEj3DXeNTTBP_5amuoK5rdIplBHmiEwKSSRJvteORTc,17967
804
- claude_mpm-4.11.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
805
- claude_mpm-4.11.0.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
806
- claude_mpm-4.11.0.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
807
- claude_mpm-4.11.0.dist-info/RECORD,,
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)