claude-mpm 4.21.0__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.

claude_mpm/VERSION CHANGED
@@ -1 +1 @@
1
- 4.21.0
1
+ 4.21.3
@@ -87,6 +87,9 @@ Available Commands:
87
87
  /mpm-init [update]
88
88
  Initialize or update project documentation
89
89
 
90
+ /mpm-resume
91
+ Create session resume files for easy work resumption
92
+
90
93
  /mpm-monitor [start|stop|restart|status|port]
91
94
  Manage Socket.IO monitoring server and dashboard
92
95
 
@@ -0,0 +1,372 @@
1
+ # /mpm-resume - Load Previous Session
2
+
3
+ Load and display context from the most recent paused session to seamlessly continue your work.
4
+
5
+ ## Usage
6
+
7
+ ```
8
+ /mpm-resume
9
+ ```
10
+
11
+ ## Description
12
+
13
+ This command **loads and displays** the most recent paused session from automatic session saves, allowing you to resume work with full context restoration.
14
+
15
+ Unlike `/mpm-init pause` which *creates* a new session save, this command *loads* existing session data that was automatically created when context usage reached 70% (140k/200k tokens).
16
+
17
+ **Key Points:**
18
+ - Reads from `.claude-mpm/sessions/` directory (automatically created at 70% context)
19
+ - Displays up to ~40,000 tokens (~20% of 200k context budget)
20
+ - Shows session summary, completed work, in-progress tasks, and git context
21
+ - Does NOT create any new files - only reads and displays
22
+
23
+ ## What Gets Displayed
24
+
25
+ When you run `/mpm-resume`, PM will display:
26
+
27
+ ### Session Summary
28
+ - **Time Elapsed**: How long ago the session was paused
29
+ - **Context Usage**: Token usage at time of pause (e.g., "67.6% - 135,259/200,000 tokens")
30
+ - **Working On**: What you were working on when paused
31
+ - **Session Duration**: How long the previous session lasted
32
+
33
+ ### Completed Work
34
+ - List of accomplishments from the paused session
35
+ - What was delivered and committed
36
+ - Key milestones achieved
37
+
38
+ ### Current Tasks
39
+ - **In Progress**: Tasks that were being worked on
40
+ - **Pending**: Tasks that were planned but not started
41
+ - **Completed**: Recently finished tasks for context
42
+
43
+ ### Git Context
44
+ - **Branch**: Current branch name
45
+ - **Recent Commits**: Last 5-10 commits with SHAs and messages
46
+ - **Status**: Clean/dirty working directory
47
+ - **Changes Since Pause**: New commits made since session was saved
48
+
49
+ ### Next Recommended Actions
50
+ - Priority-ordered list of next steps
51
+ - Estimated time for each task
52
+ - Status and blockers for pending work
53
+
54
+ ## Session Storage Location
55
+
56
+ Sessions are automatically saved to:
57
+ ```
58
+ <project-root>/.claude-mpm/sessions/session-YYYYMMDD-HHMMSS.json
59
+ ```
60
+
61
+ **Legacy location** (backward compatible):
62
+ ```
63
+ <project-root>/.claude-mpm/sessions/pause/session-YYYYMMDD-HHMMSS.json
64
+ ```
65
+
66
+ The system automatically checks both locations and uses the most recent session.
67
+
68
+ ## Example Output
69
+
70
+ ```
71
+ ================================================================================
72
+ 📋 PAUSED SESSION FOUND
73
+ ================================================================================
74
+
75
+ Paused: 2 hours ago
76
+
77
+ Last working on: Week 2 Skills Integration - Content Preparation
78
+
79
+ Completed:
80
+ ✓ Week 1: Complete infrastructure - 8,900 lines of production-ready code
81
+ ✓ Week 1: Skills loading system with automatic progressive disclosure
82
+ ✓ Week 2: 15 of 23 skills downloaded (65% complete)
83
+ ✓ Week 2: 2 Tier 1 skills refactored to progressive disclosure
84
+ ✓ Code quality: All CRITICAL and HIGH issues resolved
85
+
86
+ Next steps:
87
+ • Refactor Tier 2 skills (verification-before-completion, webapp-testing)
88
+ • Download remaining 8 skills from community repositories
89
+ • Refactor remaining 13 skills to progressive disclosure
90
+ • Generate license attributions for all bundled skills
91
+
92
+ Git changes since pause: 3 commits
93
+
94
+ Recent commits:
95
+ ac765731 - feat(skills): Week 2 progress - 15 skills downloaded (Bob Matsuoka)
96
+ 205e532e - fix(skills): address CRITICAL and HIGH priority issues (Bob Matsuoka)
97
+ 06a6d6a0 - feat: add automated pre-publish cleanup to release (Bob Matsuoka)
98
+
99
+ Context Usage: 67.6% (135,259/200,000 tokens)
100
+ Session Duration: 12 hours
101
+
102
+ ================================================================================
103
+ Use this context to resume work, or start fresh if not relevant.
104
+ ================================================================================
105
+ ```
106
+
107
+ ## Implementation
108
+
109
+ When PM receives `/mpm-resume`, it should:
110
+
111
+ 1. **Check for Sessions**
112
+ ```python
113
+ from claude_mpm.services.cli.session_resume_helper import SessionResumeHelper
114
+
115
+ helper = SessionResumeHelper()
116
+ if not helper.has_paused_sessions():
117
+ return "No paused sessions found"
118
+ ```
119
+
120
+ 2. **Load Most Recent Session**
121
+ ```python
122
+ session_data = helper.get_most_recent_session()
123
+ if not session_data:
124
+ return "Failed to load session data"
125
+ ```
126
+
127
+ 3. **Format and Display Context**
128
+ ```python
129
+ # Extract key information
130
+ conversation = session_data.get("conversation", {})
131
+ git_context = session_data.get("git_context", {})
132
+ context_usage = session_data.get("context_usage", {})
133
+ todos = session_data.get("todos", {})
134
+
135
+ # Display formatted output (see Example Output above)
136
+ ```
137
+
138
+ 4. **Calculate Git Changes**
139
+ ```python
140
+ # Get commits since pause
141
+ paused_at = session_data.get("paused_at")
142
+ new_commits = helper.get_git_changes_since_pause(paused_at, [])
143
+ ```
144
+
145
+ 5. **Limit Output to ~40k Tokens**
146
+ - Session summary: ~2k tokens
147
+ - Accomplishments (first 10): ~3k tokens
148
+ - Next steps (first 10): ~3k tokens
149
+ - Git context: ~5k tokens
150
+ - Todos: ~2k tokens
151
+ - Recent commits (up to 10): ~5k tokens
152
+ - **Total**: ~20k tokens (well under 40k limit)
153
+
154
+ ## Token Budget Management
155
+
156
+ **Context Budget**: 200,000 tokens total
157
+ **Resume Load**: ~20,000-40,000 tokens (10-20% of context)
158
+
159
+ This leaves 160,000+ tokens for actual work after loading session context.
160
+
161
+ ## Session Data Format
162
+
163
+ Sessions are stored as JSON with this structure:
164
+
165
+ ```json
166
+ {
167
+ "session_id": "session-YYYYMMDD-HHMMSS",
168
+ "paused_at": "ISO-8601 timestamp",
169
+ "duration_hours": 12,
170
+ "context_usage": {
171
+ "tokens_used": 135259,
172
+ "tokens_total": 200000,
173
+ "percentage": 67.6
174
+ },
175
+ "conversation": {
176
+ "primary_task": "What user was working on",
177
+ "current_phase": "Current phase of work",
178
+ "summary": "Brief summary",
179
+ "accomplishments": ["list of completed items"],
180
+ "next_steps": [
181
+ {
182
+ "priority": 1,
183
+ "task": "Task description",
184
+ "estimated_hours": "8-12",
185
+ "status": "ready"
186
+ }
187
+ ]
188
+ },
189
+ "git_context": {
190
+ "branch": "main",
191
+ "recent_commits": [...],
192
+ "status": {...}
193
+ },
194
+ "todos": {
195
+ "active": [...],
196
+ "completed": [...]
197
+ }
198
+ }
199
+ ```
200
+
201
+ ## When to Use This Command
202
+
203
+ Use `/mpm-resume` when:
204
+ - **Starting a new session**: After closing and reopening Claude CLI
205
+ - **Context unclear**: You need to remember what you were working on
206
+ - **After a break**: Coming back after hours or days
207
+ - **Team handoff**: Another developer wants to understand current state
208
+ - **Lost context**: Accidentally closed CLI and need to recover
209
+
210
+ ## Differences from Automatic Resume
211
+
212
+ | Feature | Automatic Resume (70% context) | /mpm-resume Command |
213
+ |---------|-------------------------------|---------------------|
214
+ | **Trigger** | Automatic at 70% context | Manual user command |
215
+ | **When** | PM startup (if session exists) | Anytime during session |
216
+ | **Creates Files** | No (reads existing) | No (reads existing) |
217
+ | **Session Source** | Same (`.claude-mpm/sessions/`) | Same (`.claude-mpm/sessions/`) |
218
+ | **Display Format** | Identical | Identical |
219
+ | **Token Usage** | ~20-40k tokens | ~20-40k tokens |
220
+
221
+ Both features use the **same underlying system** (`SessionResumeHelper`), just triggered differently.
222
+
223
+ ## No Files Created
224
+
225
+ **IMPORTANT**: This command does NOT create any new files.
226
+
227
+ It ONLY reads from existing session files that were automatically created by the system at 70% context usage.
228
+
229
+ If you want to manually create a session save (for example, at 50% context before hitting 70%), use a different workflow or wait for automatic save at 70%.
230
+
231
+ ## Related Features
232
+
233
+ - **Automatic Session Save**: System creates sessions at 70% context automatically
234
+ - **Automatic Session Resume**: PM startup hook displays sessions automatically
235
+ - `/mpm-init pause`: Manual session pause workflow (if available)
236
+ - `/mpm-init context`: Analyze git history for intelligent resumption
237
+ - `/mpm-status`: Check current MPM status
238
+
239
+ ## Error Handling
240
+
241
+ ### No Sessions Found
242
+ ```
243
+ No paused sessions found in .claude-mpm/sessions/
244
+
245
+ To create a session save, continue working until context reaches 70% (140k tokens),
246
+ at which point the system will automatically save your session state.
247
+ ```
248
+
249
+ ### Failed to Load Session
250
+ ```
251
+ Paused session file found but failed to load.
252
+
253
+ File: .claude-mpm/sessions/session-20251107-152740.json
254
+ Error: Invalid JSON format
255
+
256
+ You may need to manually inspect or delete this file.
257
+ ```
258
+
259
+ ### Invalid Session Format
260
+ ```
261
+ Session file loaded but missing required fields.
262
+
263
+ The session file may be corrupted or from an older version.
264
+ Consider running /mpm-doctor to check system health.
265
+ ```
266
+
267
+ ## Benefits
268
+
269
+ - **Instant Context**: Get full context in seconds without reading git logs
270
+ - **No Mental Load**: Don't need to remember what you were doing
271
+ - **Zero File Creation**: Pure read operation, no side effects
272
+ - **Team Collaboration**: Share context with team members
273
+ - **Graceful Recovery**: Recover from accidental CLI closures
274
+ - **Smart Filtering**: Only shows relevant information (~20k tokens)
275
+ - **Git Awareness**: See what changed since pause
276
+
277
+ ## Best Practices
278
+
279
+ 1. **Use Early**: Run `/mpm-resume` at start of each session if sessions exist
280
+ 2. **Check Git Changes**: Pay attention to commits made since pause
281
+ 3. **Validate Context**: Verify the session is still relevant before continuing
282
+ 4. **Clear Old Sessions**: Periodically clean up old session files
283
+ 5. **Combine with Git**: Use alongside `git log` for complete picture
284
+
285
+ ## Technical Details
286
+
287
+ **Implementation Files:**
288
+ - Service: `/src/claude_mpm/services/cli/session_resume_helper.py`
289
+ - Hook: `/src/claude_mpm/hooks/session_resume_hook.py`
290
+ - Command: This file
291
+
292
+ **Key Functions:**
293
+ - `SessionResumeHelper.has_paused_sessions()` - Check if sessions exist
294
+ - `SessionResumeHelper.get_most_recent_session()` - Load latest session
295
+ - `SessionResumeHelper.format_resume_prompt()` - Format display output
296
+ - `SessionResumeHelper.get_git_changes_since_pause()` - Calculate git delta
297
+
298
+ **Token Estimation:**
299
+ - Session metadata: 1-2k tokens
300
+ - Accomplishments (10 items): 2-4k tokens
301
+ - Next steps (10 items): 2-4k tokens
302
+ - Git commits (10 commits): 3-5k tokens
303
+ - Todos (20 items): 2-3k tokens
304
+ - Formatting/structure: 1-2k tokens
305
+ - **Total**: 11-20k tokens (safely under 40k limit)
306
+
307
+ ## Troubleshooting
308
+
309
+ ### Session Not Found
310
+ **Problem**: Command reports no sessions exist
311
+
312
+ **Solutions:**
313
+ 1. Check directory exists: `ls .claude-mpm/sessions/`
314
+ 2. Check for legacy location: `ls .claude-mpm/sessions/pause/`
315
+ 3. Verify session files: `ls .claude-mpm/sessions/session-*.json`
316
+ 4. Session auto-saves at 70% context - may not exist yet
317
+
318
+ ### Git Changes Not Showing
319
+ **Problem**: "No git changes since pause" but commits were made
320
+
321
+ **Solutions:**
322
+ 1. Verify git repository: `git status`
323
+ 2. Check commit timestamps: `git log --since="<pause_time>"`
324
+ 3. Ensure session timestamp is correct
325
+ 4. Check timezone issues
326
+
327
+ ### Display Too Large
328
+ **Problem**: Session context exceeds token budget
329
+
330
+ **Solutions:**
331
+ 1. System automatically limits to first 10 items
332
+ 2. Full session details available in JSON file
333
+ 3. Use `cat .claude-mpm/sessions/session-*.json` for complete data
334
+ 4. Summary is optimized for 20k tokens max
335
+
336
+ ## Example Session Resume Workflow
337
+
338
+ ```bash
339
+ # User starts new Claude CLI session
340
+ $ claude-code
341
+
342
+ # PM automatically checks for sessions on startup
343
+ # (Automatic resume hook displays session if found)
344
+
345
+ # OR user manually requests resume
346
+ User: "/mpm-resume"
347
+
348
+ # PM loads and displays session context
349
+ PM: [Displays formatted session context as shown in Example Output]
350
+
351
+ # User decides to continue work
352
+ User: "Let's continue with the next priority task"
353
+
354
+ # PM uses session context to understand what to do next
355
+ PM: "Based on the paused session, the next priority is to refactor
356
+ the verification-before-completion skill. I'll delegate this to Engineer..."
357
+ ```
358
+
359
+ ## Version History
360
+
361
+ - **v4.21.1**: Fixed command behavior - now loads sessions instead of creating files
362
+ - **v4.21.0**: Added `/mpm-resume` command (incorrect behavior - created files)
363
+ - **v4.19.0**: Automatic session resume infrastructure implemented
364
+ - **v4.18.x**: Session pause/resume foundation
365
+
366
+ ## Support
367
+
368
+ For issues or questions:
369
+ - Run `/mpm-doctor` to check system health
370
+ - Check logs: `.claude-mpm/logs/claude-mpm.log`
371
+ - Verify session files: `ls -la .claude-mpm/sessions/`
372
+ - Review documentation: `/docs/features/session-auto-resume.md`
@@ -8,6 +8,7 @@ Available MPM commands:
8
8
  - /mpm-help - Show command help
9
9
  - /mpm-status - Show MPM status
10
10
  - /mpm-config - Manage configuration
11
+ - /mpm-resume - Create session resume files
11
12
  - /mpm-version - Display version information for project, agents, and skills
12
13
 
13
14
  Claude MPM extends Claude Code with:
@@ -9,6 +9,7 @@ and lifecycle management.
9
9
  Part of TSK-0046: Service Layer Architecture Reorganization
10
10
  """
11
11
 
12
+ import threading
12
13
  from abc import ABC, abstractmethod
13
14
  from typing import Any, Dict, Optional
14
15
 
@@ -221,29 +222,43 @@ class SingletonService(SyncBaseService):
221
222
  """
222
223
  Base class for singleton services.
223
224
 
224
- Ensures only one instance of the service exists.
225
+ Ensures only one instance of the service exists with thread-safe initialization.
226
+ Uses double-checked locking pattern to prevent race conditions.
225
227
  """
226
228
 
227
229
  _instances: Dict[type, "SingletonService"] = {}
230
+ _lock = threading.Lock()
228
231
 
229
232
  def __new__(cls, *args, **kwargs):
230
- """Ensure only one instance exists."""
233
+ """Ensure only one instance exists with thread-safe initialization."""
234
+ # Fast path - check without lock
231
235
  if cls not in cls._instances:
232
- cls._instances[cls] = super().__new__(cls)
236
+ # Slow path - acquire lock and double-check
237
+ with cls._lock:
238
+ if cls not in cls._instances:
239
+ cls._instances[cls] = super().__new__(cls)
233
240
  return cls._instances[cls]
234
241
 
235
242
  @classmethod
236
243
  def get_instance(cls) -> "SingletonService":
237
- """Get the singleton instance."""
244
+ """Get the singleton instance with thread-safe initialization."""
245
+ # Fast path - check without lock
238
246
  if cls not in cls._instances:
239
- cls._instances[cls] = cls()
247
+ # Slow path - acquire lock and double-check
248
+ with cls._lock:
249
+ if cls not in cls._instances:
250
+ cls._instances[cls] = cls()
240
251
  return cls._instances[cls]
241
252
 
242
253
  @classmethod
243
254
  def clear_instance(cls) -> None:
244
- """Clear the singleton instance (useful for testing)."""
245
- if cls in cls._instances:
246
- instance = cls._instances[cls]
247
- if hasattr(instance, "shutdown") and not instance.is_shutdown:
248
- instance.shutdown()
249
- del cls._instances[cls]
255
+ """Clear the singleton instance (useful for testing).
256
+
257
+ Thread-safe implementation ensures proper cleanup.
258
+ """
259
+ with cls._lock:
260
+ if cls in cls._instances:
261
+ instance = cls._instances[cls]
262
+ if hasattr(instance, "shutdown") and not instance.is_shutdown:
263
+ instance.shutdown()
264
+ del cls._instances[cls]
@@ -9,6 +9,7 @@ WHY separate relay component:
9
9
  """
10
10
 
11
11
  import os
12
+ import threading
12
13
  import time
13
14
  from datetime import datetime, timezone
14
15
  from typing import Any, Dict, Optional
@@ -271,11 +272,15 @@ class SocketIORelay:
271
272
 
272
273
  # Global relay instance
273
274
  _relay_instance: Optional[SocketIORelay] = None
275
+ _relay_lock = threading.Lock()
274
276
 
275
277
 
276
278
  def get_relay(port: Optional[int] = None) -> SocketIORelay:
277
279
  """Get or create the global SocketIO relay instance.
278
280
 
281
+ Thread-safe implementation using double-checked locking pattern to
282
+ prevent race conditions during concurrent initialization.
283
+
279
284
  Args:
280
285
  port: Optional port number
281
286
 
@@ -283,9 +288,16 @@ def get_relay(port: Optional[int] = None) -> SocketIORelay:
283
288
  SocketIORelay: The relay instance
284
289
  """
285
290
  global _relay_instance
286
- if _relay_instance is None:
287
- _relay_instance = SocketIORelay(port)
288
- return _relay_instance
291
+
292
+ # Fast path - check without lock
293
+ if _relay_instance is not None:
294
+ return _relay_instance
295
+
296
+ # Slow path - acquire lock and double-check
297
+ with _relay_lock:
298
+ if _relay_instance is None:
299
+ _relay_instance = SocketIORelay(port)
300
+ return _relay_instance
289
301
 
290
302
 
291
303
  def start_relay(port: Optional[int] = None) -> SocketIORelay:
@@ -303,8 +315,12 @@ def start_relay(port: Optional[int] = None) -> SocketIORelay:
303
315
 
304
316
 
305
317
  def stop_relay() -> None:
306
- """Stop the global SocketIO relay."""
318
+ """Stop the global SocketIO relay.
319
+
320
+ Thread-safe implementation ensures proper cleanup.
321
+ """
307
322
  global _relay_instance
308
- if _relay_instance:
309
- _relay_instance.stop()
310
- _relay_instance = None
323
+ with _relay_lock:
324
+ if _relay_instance:
325
+ _relay_instance.stop()
326
+ _relay_instance = None
@@ -29,6 +29,7 @@ Example flow:
29
29
 
30
30
  import logging
31
31
  import re
32
+ import threading
32
33
  from dataclasses import dataclass, field
33
34
  from datetime import datetime, timezone
34
35
  from typing import Dict, List, Optional, Tuple
@@ -536,6 +537,7 @@ class FailureTracker:
536
537
 
537
538
  # Singleton instance for session-level tracking
538
539
  _tracker_instance: Optional[FailureTracker] = None
540
+ _tracker_lock = threading.Lock()
539
541
 
540
542
 
541
543
  def get_failure_tracker() -> FailureTracker:
@@ -544,13 +546,23 @@ def get_failure_tracker() -> FailureTracker:
544
546
  WHY: Session-level tracking requires a singleton to maintain state
545
547
  across multiple hook invocations during the same session.
546
548
 
549
+ Thread-safe implementation using double-checked locking pattern to
550
+ prevent race conditions during concurrent initialization.
551
+
547
552
  Returns:
548
553
  The FailureTracker singleton instance
549
554
  """
550
555
  global _tracker_instance
551
- if _tracker_instance is None:
552
- _tracker_instance = FailureTracker()
553
- return _tracker_instance
556
+
557
+ # Fast path - check without lock
558
+ if _tracker_instance is not None:
559
+ return _tracker_instance
560
+
561
+ # Slow path - acquire lock and double-check
562
+ with _tracker_lock:
563
+ if _tracker_instance is None:
564
+ _tracker_instance = FailureTracker()
565
+ return _tracker_instance
554
566
 
555
567
 
556
568
  def reset_failure_tracker() -> None:
@@ -558,6 +570,9 @@ def reset_failure_tracker() -> None:
558
570
 
559
571
  WHY: Tests need to reset state between runs. Also useful for
560
572
  explicitly starting a new tracking session.
573
+
574
+ Thread-safe implementation ensures proper cleanup.
561
575
  """
562
576
  global _tracker_instance
563
- _tracker_instance = None
577
+ with _tracker_lock:
578
+ _tracker_instance = None
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Code Tree Analyzer
4
+ ==================
5
+
6
+ Analyzes source code using AST to extract structure and metrics,
7
+ supporting multiple languages and emitting incremental events for visualization.
8
+
9
+ This module has been refactored from a single 1,825-line file into focused,
10
+ maintainable components while preserving complete backward compatibility.
11
+
12
+ Public API:
13
+ -----------
14
+ - CodeTreeAnalyzer: Main analyzer class
15
+ - CodeNode: Data structure for code nodes
16
+ - GitignoreManager: Gitignore pattern matching
17
+ - PythonAnalyzer: Python AST analysis
18
+ - MultiLanguageAnalyzer: Multi-language support
19
+
20
+ Example Usage:
21
+ --------------
22
+ from claude_mpm.tools.code_tree_analyzer import CodeTreeAnalyzer
23
+
24
+ analyzer = CodeTreeAnalyzer(emit_events=True)
25
+ result = analyzer.analyze_directory(Path("/path/to/code"))
26
+ print(result['stats'])
27
+ """
28
+
29
+ # Public API - Backward compatible imports
30
+ from .core import CodeTreeAnalyzer
31
+ from .gitignore import GitignoreManager
32
+ from .models import CodeNode
33
+ from .multilang_analyzer import MultiLanguageAnalyzer
34
+ from .python_analyzer import PythonAnalyzer
35
+
36
+ __all__ = [
37
+ "CodeNode",
38
+ "CodeTreeAnalyzer",
39
+ "GitignoreManager",
40
+ "MultiLanguageAnalyzer",
41
+ "PythonAnalyzer",
42
+ ]
43
+
44
+ # Version info
45
+ __version__ = "2.0.0" # Major refactoring while maintaining API compatibility