claude-mpm 4.18.0__py3-none-any.whl → 4.20.0__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 +1 -1
- claude_mpm/agents/BASE_ENGINEER.md +286 -0
- claude_mpm/agents/BASE_PM.md +238 -37
- claude_mpm/agents/PM_INSTRUCTIONS.md +40 -0
- claude_mpm/agents/templates/engineer.json +5 -1
- claude_mpm/agents/templates/python_engineer.json +8 -3
- claude_mpm/agents/templates/rust_engineer.json +12 -7
- claude_mpm/cli/commands/mpm_init.py +109 -24
- claude_mpm/commands/mpm-init.md +112 -6
- claude_mpm/core/config.py +42 -0
- claude_mpm/hooks/__init__.py +8 -0
- claude_mpm/hooks/session_resume_hook.py +121 -0
- claude_mpm/services/agents/deployment/agent_validator.py +17 -1
- claude_mpm/services/cli/resume_service.py +617 -0
- claude_mpm/services/cli/session_manager.py +87 -0
- claude_mpm/services/cli/session_resume_helper.py +352 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.dist-info}/METADATA +19 -4
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.dist-info}/RECORD +22 -19
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.dist-info}/WHEEL +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.dist-info}/entry_points.txt +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-4.18.0.dist-info → claude_mpm-4.20.0.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,352 @@
|
|
|
1
|
+
"""Session Resume Helper Service.
|
|
2
|
+
|
|
3
|
+
WHY: This service provides automatic session resume detection and prompting for PM startup.
|
|
4
|
+
It detects paused sessions, calculates git changes since pause, and presents resumption
|
|
5
|
+
context to users.
|
|
6
|
+
|
|
7
|
+
DESIGN DECISIONS:
|
|
8
|
+
- Project-specific session storage (.claude-mpm/sessions/pause/)
|
|
9
|
+
- Non-blocking detection with graceful degradation
|
|
10
|
+
- Git change detection for context updates
|
|
11
|
+
- User-friendly prompts with time elapsed information
|
|
12
|
+
- Integration with existing SessionManager infrastructure
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import subprocess
|
|
17
|
+
from datetime import datetime, timezone
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
20
|
+
|
|
21
|
+
from claude_mpm.core.logger import get_logger
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SessionResumeHelper:
|
|
27
|
+
"""Helper for automatic session resume detection and prompting."""
|
|
28
|
+
|
|
29
|
+
def __init__(self, project_path: Optional[Path] = None):
|
|
30
|
+
"""Initialize session resume helper.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
project_path: Project root path (default: current directory)
|
|
34
|
+
"""
|
|
35
|
+
self.project_path = project_path or Path.cwd()
|
|
36
|
+
self.pause_dir = self.project_path / ".claude-mpm" / "sessions" / "pause"
|
|
37
|
+
|
|
38
|
+
def has_paused_sessions(self) -> bool:
|
|
39
|
+
"""Check if there are any paused sessions.
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
True if paused sessions exist, False otherwise
|
|
43
|
+
"""
|
|
44
|
+
if not self.pause_dir.exists():
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
# Look for session JSON files
|
|
48
|
+
session_files = list(self.pause_dir.glob("session-*.json"))
|
|
49
|
+
return len(session_files) > 0
|
|
50
|
+
|
|
51
|
+
def get_most_recent_session(self) -> Optional[Dict[str, Any]]:
|
|
52
|
+
"""Get the most recent paused session.
|
|
53
|
+
|
|
54
|
+
Returns:
|
|
55
|
+
Session data dictionary or None if no sessions found
|
|
56
|
+
"""
|
|
57
|
+
if not self.pause_dir.exists():
|
|
58
|
+
return None
|
|
59
|
+
|
|
60
|
+
# Find all session files
|
|
61
|
+
session_files = list(self.pause_dir.glob("session-*.json"))
|
|
62
|
+
if not session_files:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
# Sort by modification time (most recent first)
|
|
66
|
+
session_files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
|
67
|
+
|
|
68
|
+
# Load the most recent session
|
|
69
|
+
try:
|
|
70
|
+
with session_files[0].open("r") as f:
|
|
71
|
+
session_data = json.load(f)
|
|
72
|
+
session_data["file_path"] = session_files[0]
|
|
73
|
+
return session_data
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(f"Failed to load session file {session_files[0]}: {e}")
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
def get_git_changes_since_pause(
|
|
79
|
+
self, paused_at: str, recent_commits: List[Dict[str, str]]
|
|
80
|
+
) -> Tuple[int, List[Dict[str, str]]]:
|
|
81
|
+
"""Calculate git changes since session was paused.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
paused_at: ISO-8601 timestamp when session was paused
|
|
85
|
+
recent_commits: List of recent commits from session data
|
|
86
|
+
|
|
87
|
+
Returns:
|
|
88
|
+
Tuple of (new_commit_count, new_commits_list)
|
|
89
|
+
"""
|
|
90
|
+
try:
|
|
91
|
+
# Parse pause timestamp
|
|
92
|
+
pause_time = datetime.fromisoformat(paused_at)
|
|
93
|
+
|
|
94
|
+
# Get commits since pause time
|
|
95
|
+
cmd = [
|
|
96
|
+
"git",
|
|
97
|
+
"log",
|
|
98
|
+
f'--since="{pause_time.isoformat()}"',
|
|
99
|
+
"--pretty=format:%h|%an|%ai|%s",
|
|
100
|
+
"--all",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
result = subprocess.run(
|
|
104
|
+
cmd,
|
|
105
|
+
cwd=self.project_path,
|
|
106
|
+
capture_output=True,
|
|
107
|
+
text=True,
|
|
108
|
+
check=False,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
if result.returncode != 0:
|
|
112
|
+
logger.warning(f"Git log command failed: {result.stderr}")
|
|
113
|
+
return 0, []
|
|
114
|
+
|
|
115
|
+
# Parse commit output
|
|
116
|
+
new_commits = []
|
|
117
|
+
for line in result.stdout.strip().split("\n"):
|
|
118
|
+
if line:
|
|
119
|
+
parts = line.split("|", 3)
|
|
120
|
+
if len(parts) == 4:
|
|
121
|
+
new_commits.append(
|
|
122
|
+
{
|
|
123
|
+
"sha": parts[0],
|
|
124
|
+
"author": parts[1],
|
|
125
|
+
"timestamp": parts[2],
|
|
126
|
+
"message": parts[3],
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
return len(new_commits), new_commits
|
|
131
|
+
|
|
132
|
+
except Exception as e:
|
|
133
|
+
logger.error(f"Failed to get git changes: {e}")
|
|
134
|
+
return 0, []
|
|
135
|
+
|
|
136
|
+
def get_time_elapsed(self, paused_at: str) -> str:
|
|
137
|
+
"""Calculate human-readable time elapsed since pause.
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
paused_at: ISO-8601 timestamp when session was paused
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
Human-readable time string (e.g., "2 hours ago", "3 days ago")
|
|
144
|
+
"""
|
|
145
|
+
try:
|
|
146
|
+
pause_time = datetime.fromisoformat(paused_at)
|
|
147
|
+
now = datetime.now(timezone.utc)
|
|
148
|
+
|
|
149
|
+
# Ensure pause_time is timezone-aware
|
|
150
|
+
if pause_time.tzinfo is None:
|
|
151
|
+
pause_time = pause_time.replace(tzinfo=timezone.utc)
|
|
152
|
+
|
|
153
|
+
delta = now - pause_time
|
|
154
|
+
|
|
155
|
+
# Calculate time components
|
|
156
|
+
days = delta.days
|
|
157
|
+
hours = delta.seconds // 3600
|
|
158
|
+
minutes = (delta.seconds % 3600) // 60
|
|
159
|
+
|
|
160
|
+
# Format human-readable string
|
|
161
|
+
if days > 0:
|
|
162
|
+
if days == 1:
|
|
163
|
+
return "1 day ago"
|
|
164
|
+
return f"{days} days ago"
|
|
165
|
+
if hours > 0:
|
|
166
|
+
if hours == 1:
|
|
167
|
+
return "1 hour ago"
|
|
168
|
+
return f"{hours} hours ago"
|
|
169
|
+
if minutes > 0:
|
|
170
|
+
if minutes == 1:
|
|
171
|
+
return "1 minute ago"
|
|
172
|
+
return f"{minutes} minutes ago"
|
|
173
|
+
return "just now"
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.error(f"Failed to calculate time elapsed: {e}")
|
|
177
|
+
return "unknown time ago"
|
|
178
|
+
|
|
179
|
+
def format_resume_prompt(self, session_data: Dict[str, Any]) -> str:
|
|
180
|
+
"""Format a user-friendly resume prompt.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
session_data: Session data dictionary
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Formatted prompt string for display
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
# Extract session information
|
|
190
|
+
paused_at = session_data.get("paused_at", "")
|
|
191
|
+
conversation = session_data.get("conversation", {})
|
|
192
|
+
git_context = session_data.get("git_context", {})
|
|
193
|
+
|
|
194
|
+
summary = conversation.get("summary", "No summary available")
|
|
195
|
+
accomplishments = conversation.get("accomplishments", [])
|
|
196
|
+
next_steps = conversation.get("next_steps", [])
|
|
197
|
+
|
|
198
|
+
# Calculate time elapsed
|
|
199
|
+
time_ago = self.get_time_elapsed(paused_at)
|
|
200
|
+
|
|
201
|
+
# Get git changes
|
|
202
|
+
recent_commits = git_context.get("recent_commits", [])
|
|
203
|
+
new_commit_count, new_commits = self.get_git_changes_since_pause(
|
|
204
|
+
paused_at, recent_commits
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Build prompt
|
|
208
|
+
lines = []
|
|
209
|
+
lines.append("\n" + "=" * 80)
|
|
210
|
+
lines.append("📋 PAUSED SESSION FOUND")
|
|
211
|
+
lines.append("=" * 80)
|
|
212
|
+
lines.append(f"\nPaused: {time_ago}")
|
|
213
|
+
lines.append(f"\nLast working on: {summary}")
|
|
214
|
+
|
|
215
|
+
if accomplishments:
|
|
216
|
+
lines.append("\nCompleted:")
|
|
217
|
+
for item in accomplishments[:5]: # Limit to first 5
|
|
218
|
+
lines.append(f" ✓ {item}")
|
|
219
|
+
if len(accomplishments) > 5:
|
|
220
|
+
lines.append(f" ... and {len(accomplishments) - 5} more")
|
|
221
|
+
|
|
222
|
+
if next_steps:
|
|
223
|
+
lines.append("\nNext steps:")
|
|
224
|
+
for item in next_steps[:5]: # Limit to first 5
|
|
225
|
+
lines.append(f" • {item}")
|
|
226
|
+
if len(next_steps) > 5:
|
|
227
|
+
lines.append(f" ... and {len(next_steps) - 5} more")
|
|
228
|
+
|
|
229
|
+
# Git changes information
|
|
230
|
+
if new_commit_count > 0:
|
|
231
|
+
lines.append(f"\nGit changes since pause: {new_commit_count} commits")
|
|
232
|
+
if new_commits:
|
|
233
|
+
lines.append("\nRecent commits:")
|
|
234
|
+
for commit in new_commits[:3]: # Show first 3
|
|
235
|
+
lines.append(
|
|
236
|
+
f" {commit['sha']} - {commit['message']} ({commit['author']})"
|
|
237
|
+
)
|
|
238
|
+
if len(new_commits) > 3:
|
|
239
|
+
lines.append(f" ... and {len(new_commits) - 3} more")
|
|
240
|
+
else:
|
|
241
|
+
lines.append("\nNo git changes since pause")
|
|
242
|
+
|
|
243
|
+
lines.append("\n" + "=" * 80)
|
|
244
|
+
lines.append(
|
|
245
|
+
"Use this context to resume work, or start fresh if not relevant."
|
|
246
|
+
)
|
|
247
|
+
lines.append("=" * 80 + "\n")
|
|
248
|
+
|
|
249
|
+
return "\n".join(lines)
|
|
250
|
+
|
|
251
|
+
except Exception as e:
|
|
252
|
+
logger.error(f"Failed to format resume prompt: {e}")
|
|
253
|
+
return "\n📋 Paused session found, but failed to format details.\n"
|
|
254
|
+
|
|
255
|
+
def check_and_display_resume_prompt(self) -> Optional[Dict[str, Any]]:
|
|
256
|
+
"""Check for paused sessions and display resume prompt if found.
|
|
257
|
+
|
|
258
|
+
This is the main entry point for PM startup integration.
|
|
259
|
+
|
|
260
|
+
Returns:
|
|
261
|
+
Session data if found and user should resume, None otherwise
|
|
262
|
+
"""
|
|
263
|
+
if not self.has_paused_sessions():
|
|
264
|
+
logger.debug("No paused sessions found")
|
|
265
|
+
return None
|
|
266
|
+
|
|
267
|
+
# Get most recent session
|
|
268
|
+
session_data = self.get_most_recent_session()
|
|
269
|
+
if not session_data:
|
|
270
|
+
logger.debug("Failed to load paused session data")
|
|
271
|
+
return None
|
|
272
|
+
|
|
273
|
+
# Display resume prompt
|
|
274
|
+
prompt_text = self.format_resume_prompt(session_data)
|
|
275
|
+
print(prompt_text)
|
|
276
|
+
|
|
277
|
+
# Return session data for PM to use
|
|
278
|
+
return session_data
|
|
279
|
+
|
|
280
|
+
def clear_session(self, session_data: Dict[str, Any]) -> bool:
|
|
281
|
+
"""Clear a paused session after successful resume.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
session_data: Session data dictionary with 'file_path' key
|
|
285
|
+
|
|
286
|
+
Returns:
|
|
287
|
+
True if successfully cleared, False otherwise
|
|
288
|
+
"""
|
|
289
|
+
try:
|
|
290
|
+
file_path = session_data.get("file_path")
|
|
291
|
+
if not file_path or not isinstance(file_path, Path):
|
|
292
|
+
logger.error("Invalid session file path")
|
|
293
|
+
return False
|
|
294
|
+
|
|
295
|
+
if file_path.exists():
|
|
296
|
+
file_path.unlink()
|
|
297
|
+
logger.info(f"Cleared paused session: {file_path}")
|
|
298
|
+
|
|
299
|
+
# Also remove SHA256 checksum file if exists
|
|
300
|
+
sha_file = file_path.parent / f".{file_path.name}.sha256"
|
|
301
|
+
if sha_file.exists():
|
|
302
|
+
sha_file.unlink()
|
|
303
|
+
logger.debug(f"Cleared session checksum: {sha_file}")
|
|
304
|
+
|
|
305
|
+
return True
|
|
306
|
+
logger.warning(f"Session file not found: {file_path}")
|
|
307
|
+
return False
|
|
308
|
+
|
|
309
|
+
except Exception as e:
|
|
310
|
+
logger.error(f"Failed to clear session: {e}")
|
|
311
|
+
return False
|
|
312
|
+
|
|
313
|
+
def get_session_count(self) -> int:
|
|
314
|
+
"""Get count of paused sessions.
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Number of paused sessions
|
|
318
|
+
"""
|
|
319
|
+
if not self.pause_dir.exists():
|
|
320
|
+
return 0
|
|
321
|
+
|
|
322
|
+
session_files = list(self.pause_dir.glob("session-*.json"))
|
|
323
|
+
return len(session_files)
|
|
324
|
+
|
|
325
|
+
def list_all_sessions(self) -> List[Dict[str, Any]]:
|
|
326
|
+
"""List all paused sessions sorted by most recent.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
List of session data dictionaries
|
|
330
|
+
"""
|
|
331
|
+
if not self.pause_dir.exists():
|
|
332
|
+
return []
|
|
333
|
+
|
|
334
|
+
session_files = list(self.pause_dir.glob("session-*.json"))
|
|
335
|
+
if not session_files:
|
|
336
|
+
return []
|
|
337
|
+
|
|
338
|
+
# Sort by modification time (most recent first)
|
|
339
|
+
session_files.sort(key=lambda p: p.stat().st_mtime, reverse=True)
|
|
340
|
+
|
|
341
|
+
sessions = []
|
|
342
|
+
for session_file in session_files:
|
|
343
|
+
try:
|
|
344
|
+
with session_file.open("r") as f:
|
|
345
|
+
session_data = json.load(f)
|
|
346
|
+
session_data["file_path"] = session_file
|
|
347
|
+
sessions.append(session_data)
|
|
348
|
+
except Exception as e:
|
|
349
|
+
logger.error(f"Failed to load session {session_file}: {e}")
|
|
350
|
+
continue
|
|
351
|
+
|
|
352
|
+
return sessions
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: claude-mpm
|
|
3
|
-
Version: 4.
|
|
3
|
+
Version: 4.20.0
|
|
4
4
|
Summary: Claude Multi-Agent Project Manager - Orchestrate Claude with agent delegation and ticket tracking
|
|
5
5
|
Author-email: Bob Matsuoka <bob@matsuoka.com>
|
|
6
6
|
Maintainer: Claude MPM Team
|
|
@@ -31,7 +31,7 @@ Requires-Dist: requests>=2.25.0
|
|
|
31
31
|
Requires-Dist: flask>=3.0.0
|
|
32
32
|
Requires-Dist: flask-cors>=4.0.0
|
|
33
33
|
Requires-Dist: watchdog>=3.0.0
|
|
34
|
-
Requires-Dist: python-socketio>=5.
|
|
34
|
+
Requires-Dist: python-socketio>=5.14.0
|
|
35
35
|
Requires-Dist: aiohttp>=3.9.0
|
|
36
36
|
Requires-Dist: aiohttp-cors<0.8.0,>=0.7.0
|
|
37
37
|
Requires-Dist: python-engineio>=4.8.0
|
|
@@ -75,7 +75,7 @@ Requires-Dist: sphinx>=7.2.0; extra == "docs"
|
|
|
75
75
|
Requires-Dist: sphinx-rtd-theme>=1.3.0; extra == "docs"
|
|
76
76
|
Requires-Dist: sphinx-autobuild>=2021.3.14; extra == "docs"
|
|
77
77
|
Provides-Extra: monitor
|
|
78
|
-
Requires-Dist: python-socketio>=5.
|
|
78
|
+
Requires-Dist: python-socketio>=5.14.0; extra == "monitor"
|
|
79
79
|
Requires-Dist: aiohttp>=3.9.0; extra == "monitor"
|
|
80
80
|
Requires-Dist: aiohttp-cors<0.8.0,>=0.7.0; extra == "monitor"
|
|
81
81
|
Requires-Dist: python-engineio>=4.8.0; extra == "monitor"
|
|
@@ -281,11 +281,26 @@ Claude MPM includes 15 specialized agents:
|
|
|
281
281
|
|
|
282
282
|
#### Core Development
|
|
283
283
|
- **Engineer** - Software development and implementation
|
|
284
|
-
- **Research** - Code analysis and research
|
|
284
|
+
- **Research** - Code analysis and research
|
|
285
285
|
- **Documentation** - Documentation creation and maintenance
|
|
286
286
|
- **QA** - Testing and quality assurance
|
|
287
287
|
- **Security** - Security analysis and implementation
|
|
288
288
|
|
|
289
|
+
#### Language-Specific Engineers
|
|
290
|
+
- **Python Engineer (v2.3.0)** - Type-safe, async-first Python with SOA patterns for non-trivial applications
|
|
291
|
+
- Service-oriented architecture with ABC interfaces for applications
|
|
292
|
+
- Lightweight script patterns for automation and one-off tasks
|
|
293
|
+
- Clear decision criteria for when to use DI/SOA vs simple functions
|
|
294
|
+
- Dependency injection containers with auto-resolution
|
|
295
|
+
- Use for: Web applications, microservices, data pipelines (DI/SOA) or scripts, CLI tools, notebooks (simple functions)
|
|
296
|
+
|
|
297
|
+
- **Rust Engineer (v1.1.0)** - Memory-safe, high-performance systems with trait-based service architecture
|
|
298
|
+
- Dependency injection with traits (constructor injection, trait objects)
|
|
299
|
+
- Service-oriented architecture patterns (repository, builder)
|
|
300
|
+
- Decision criteria for when to use DI/SOA vs simple code
|
|
301
|
+
- Async programming with tokio and zero-cost abstractions
|
|
302
|
+
- Use for: Web services, microservices (DI/SOA) or CLI tools, scripts (simple code)
|
|
303
|
+
|
|
289
304
|
#### Operations & Infrastructure
|
|
290
305
|
- **Ops** - Operations and deployment with advanced git commit authority and security verification (v2.2.2+)
|
|
291
306
|
- **Version Control** - Git and version management
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
claude_mpm/BUILD_NUMBER,sha256=9JfxhnDtr-8l3kCP2U5TVXSErptHoga8m7XA8zqgGOc,4
|
|
2
|
-
claude_mpm/VERSION,sha256=
|
|
2
|
+
claude_mpm/VERSION,sha256=C9gaUpftPvOukwiH7PjBD1e1du-u0JmCJYt7-K3-k9I,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
|
|
@@ -7,16 +7,16 @@ claude_mpm/init.py,sha256=HFJR_JmTHa53FAhbGusm3P1iXT6VR6V7S-YJz4pOZ1Q,15967
|
|
|
7
7
|
claude_mpm/ticket_wrapper.py,sha256=qe5xY579t7_7fK5nyeAfHN_fr7CXdeOD3jfXEc8-7yo,828
|
|
8
8
|
claude_mpm/agents/BASE_AGENT_TEMPLATE.md,sha256=wFqZssWaeCGaW04yy7eScYOlqJmsu1VhsA83s2X_yMU,9138
|
|
9
9
|
claude_mpm/agents/BASE_DOCUMENTATION.md,sha256=iGub94w3IRURz2PwT0YrfNegMlEzgZ9mzeWa_WBk1rA,1504
|
|
10
|
-
claude_mpm/agents/BASE_ENGINEER.md,sha256=
|
|
10
|
+
claude_mpm/agents/BASE_ENGINEER.md,sha256=I7BusSGQfuV0uSkRBro51qzCzagUEPrXnndIElypPJk,24434
|
|
11
11
|
claude_mpm/agents/BASE_OPS.md,sha256=azAjZTrj77IZIBgZxX2hcjPOQMznV0v6B4ZWNUhAT9g,7636
|
|
12
|
-
claude_mpm/agents/BASE_PM.md,sha256=
|
|
12
|
+
claude_mpm/agents/BASE_PM.md,sha256=qUQKwQDfnvVgmfAyb159GezzD9DR61f9CyKG-4V_LK0,14549
|
|
13
13
|
claude_mpm/agents/BASE_PROMPT_ENGINEER.md,sha256=DHw7KXA6Nw7RfTyQiY06B_hH6ng2meimhgUKpQp08MM,21736
|
|
14
14
|
claude_mpm/agents/BASE_QA.md,sha256=YtaYJSjWDfmFS3B6PtNvog4L54_w5K1rvpV0yqLhP20,5347
|
|
15
15
|
claude_mpm/agents/BASE_RESEARCH.md,sha256=2DZhDd5XxWWtsyNTBIwvtNWBu1JpFy5R5SAZDHh0otU,1690
|
|
16
16
|
claude_mpm/agents/INSTRUCTIONS_OLD_DEPRECATED.md,sha256=zQZhrhVq9NLCtSjVX-aC0xcgueemSuPhKyv0SjEOyIQ,25852
|
|
17
17
|
claude_mpm/agents/MEMORY.md,sha256=KbRwY_RYdOvTvFC2DD-ATfwjHkQWJ5PIjlGws_7RmjI,3307
|
|
18
18
|
claude_mpm/agents/OUTPUT_STYLE.md,sha256=fF9ydZuOgewDUNmaqJR_O7QV-feKiJ9bgPgk0NNtg_w,14270
|
|
19
|
-
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=
|
|
19
|
+
claude_mpm/agents/PM_INSTRUCTIONS.md,sha256=m8xKccB43RyghcuRA8ZUxxDCoiIJCz0OwC18a3BhRHI,38477
|
|
20
20
|
claude_mpm/agents/WORKFLOW.md,sha256=vJ9iXCVqAaeM_yVlXxbcP3bsL210-1BI3ZAanvWv4hI,9085
|
|
21
21
|
claude_mpm/agents/__init__.py,sha256=jRFxvV_DIZ-NdENa-703Xu3YpwvlQj6yv-mQ6FgmldM,3220
|
|
22
22
|
claude_mpm/agents/agent-template.yaml,sha256=mRlz5Yd0SmknTeoJWgFkZXzEF5T7OmGBJGs2-KPT93k,1969
|
|
@@ -40,7 +40,7 @@ claude_mpm/agents/templates/content-agent.json,sha256=KfE8ozTsLmQiJgKnX-6yH1bOhd
|
|
|
40
40
|
claude_mpm/agents/templates/dart_engineer.json,sha256=2XsrLpeL9-f94S7mq7e8Rela-W-dl2tGwksOY8S4nEo,22685
|
|
41
41
|
claude_mpm/agents/templates/data_engineer.json,sha256=BPGgVsQPdg5YHRxlSGSIfA3xBihIXGYcrWbtaeYGd6g,20028
|
|
42
42
|
claude_mpm/agents/templates/documentation.json,sha256=jdDZdz8eCW6jF-jow5l1Z6SFBHyd6t-34hNAb0bbfe4,10355
|
|
43
|
-
claude_mpm/agents/templates/engineer.json,sha256=
|
|
43
|
+
claude_mpm/agents/templates/engineer.json,sha256=1z2tXmuGfdk8_T43A__piMSHv-RK7BxwoyOMRPt8Qz8,6047
|
|
44
44
|
claude_mpm/agents/templates/gcp_ops_agent.json,sha256=HtW3m9S6FLxxoa3ATzOWB2YLuCsRdNnQ4pztuyei6Q0,11208
|
|
45
45
|
claude_mpm/agents/templates/git_file_tracking.md,sha256=r8qckL8pS2nIvZAImFgbjAzpaEhzV44B1lzz6V8Jg8M,18842
|
|
46
46
|
claude_mpm/agents/templates/golang_engineer.json,sha256=_I5G6fmQb1Qf5uIM4Q46HCujndU2Lq96V0CoH6qi3D4,12905
|
|
@@ -56,14 +56,14 @@ claude_mpm/agents/templates/pm_red_flags.md,sha256=spfVI3zO9hRFf50OgCn06Aa5opN-W
|
|
|
56
56
|
claude_mpm/agents/templates/product_owner.json,sha256=l-o__umM5c8pNaLGyzkRlkUtnl73h_BkaMWnMlnrPr4,40177
|
|
57
57
|
claude_mpm/agents/templates/project_organizer.json,sha256=J76f5g7SSkubs8hvN2h7GdSjvv-UDVYgQI1ozDFVfEc,9266
|
|
58
58
|
claude_mpm/agents/templates/prompt-engineer.json,sha256=7aIGOEu1PnEsC32VpXu9k4RkM8H-QLBBtoQfNzHuONc,37666
|
|
59
|
-
claude_mpm/agents/templates/python_engineer.json,sha256=
|
|
59
|
+
claude_mpm/agents/templates/python_engineer.json,sha256=i_Q3rCZMM57h-m3KqsiAWZ__fBpED7Iaza2ZbdcyPKE,50088
|
|
60
60
|
claude_mpm/agents/templates/qa.json,sha256=JBV9MNMaTatlwBQnB3hyeT0i9xkatrwC4-W8QNNcuWk,10788
|
|
61
61
|
claude_mpm/agents/templates/react_engineer.json,sha256=O3gaWqQwpkVYxR7WJdnQ81ZuFAmLvIsGCinbARuQPBI,13370
|
|
62
62
|
claude_mpm/agents/templates/refactoring_engineer.json,sha256=BybYP2ztusg4ehOv1JuAP0oAv8F3iwW_CV9gmboLTwU,12283
|
|
63
63
|
claude_mpm/agents/templates/research.json,sha256=tbhNU-FU-KpTtrPru4dyjnyoRAhXahS4UgWo7ny1yWY,14382
|
|
64
64
|
claude_mpm/agents/templates/response_format.md,sha256=p7TZBKaeJZsYWUmF7bB1TyMuYoWdf3--Pccttn-51gU,21354
|
|
65
65
|
claude_mpm/agents/templates/ruby-engineer.json,sha256=_kNFYfJiH9Vgmuh2X3FeEC7JfZokriUU9UGH4FT3pHc,12631
|
|
66
|
-
claude_mpm/agents/templates/rust_engineer.json,sha256=
|
|
66
|
+
claude_mpm/agents/templates/rust_engineer.json,sha256=A5P46_ZQu64WW3dYMmQPGy2SF8DsY2L2tC2iU0l7Zpo,20227
|
|
67
67
|
claude_mpm/agents/templates/security.json,sha256=c8_2hwccYOlrTlU97NH0RSg_8bA2dYT3U_FkGGnujNo,24646
|
|
68
68
|
claude_mpm/agents/templates/svelte-engineer.json,sha256=laJkAr0LSMKCfXXwvOvlgQF5Wst9uAPrybTZMK49f3o,17900
|
|
69
69
|
claude_mpm/agents/templates/ticketing.json,sha256=be4J8CUF4HY7pKo-MTSzrQB-Jqk1VXEoLta3t-25e5c,11985
|
|
@@ -127,7 +127,7 @@ claude_mpm/cli/commands/mcp_setup_external.py,sha256=hfBHkaioNa0JRDhahNEc8agyrUw
|
|
|
127
127
|
claude_mpm/cli/commands/mcp_tool_commands.py,sha256=q17GzlFT3JiLTrDqwPO2tz1-fKmPO5QU449syTnKTz4,1283
|
|
128
128
|
claude_mpm/cli/commands/memory.py,sha256=mMUedUGep-FL0bapjNCJbcLn0eDLsr4-bMVYtW3wSLE,27198
|
|
129
129
|
claude_mpm/cli/commands/monitor.py,sha256=Fjb68hf3dEwTFek2LV8Nh6iU0qEkY7qYlOn32IwNaNg,9566
|
|
130
|
-
claude_mpm/cli/commands/mpm_init.py,sha256=
|
|
130
|
+
claude_mpm/cli/commands/mpm_init.py,sha256=SE0UVhxguvQt7575Ci5re_tonNRYl0s_7HIlwJjZcps,78865
|
|
131
131
|
claude_mpm/cli/commands/mpm_init_handler.py,sha256=zKAat8KD8evsodDxF5T_EXqWYYxVY7umO3Y5gwPU7WQ,4737
|
|
132
132
|
claude_mpm/cli/commands/run.py,sha256=PB2H55piOPTy4yo4OBgbUCjMlcz9K79wbwpxQVc9m5Q,48225
|
|
133
133
|
claude_mpm/cli/commands/search.py,sha256=alv6udvKcn-xkqeBlLuPRvfSDV1yxEX4n9mjjRT5uLM,16581
|
|
@@ -174,7 +174,7 @@ claude_mpm/commands/mpm-auto-configure.md,sha256=vJtXiaEiujmozhKhqU8GsO95gHLs0QQ
|
|
|
174
174
|
claude_mpm/commands/mpm-config.md,sha256=79Eb-srRpEVV3HCHDHZc8SKec6_LVP6HbXDEVkZKLgw,2929
|
|
175
175
|
claude_mpm/commands/mpm-doctor.md,sha256=ut5LhFKVRw-2ecjMSPsnaTiRuFXa6Q9t-Wgl3CCnQvk,590
|
|
176
176
|
claude_mpm/commands/mpm-help.md,sha256=8lo0mvhAjFI124hAGX4G8iwkz1k3xDv2QT2Jbyqeg_8,6891
|
|
177
|
-
claude_mpm/commands/mpm-init.md,sha256=
|
|
177
|
+
claude_mpm/commands/mpm-init.md,sha256=ahPXWBR7x7_A028Bk9zrRk0Q36DbvCX2jhiMXTsdW7E,19716
|
|
178
178
|
claude_mpm/commands/mpm-monitor.md,sha256=onTHf9Yac1KkdZdENtY2Q5jyw0A-vZLYgoKkPCtZLUY,12193
|
|
179
179
|
claude_mpm/commands/mpm-organize.md,sha256=T-ysjhwgfW9irjUj02vuY_1jeMdabO_zxcShyjmqsiM,10153
|
|
180
180
|
claude_mpm/commands/mpm-status.md,sha256=oaM4ybL4ffp55nkT9F0mp_5H4tF-wX9mbqK-LEKEqUU,1919
|
|
@@ -195,7 +195,7 @@ claude_mpm/core/api_validator.py,sha256=U2PfksS1gEwMCkBMUjsUmFVPg2coZ6mwugBE_5kw
|
|
|
195
195
|
claude_mpm/core/base_service.py,sha256=4Zrt-vQ6g_KVk4hEfvd4QccuItYoOf7wUHCimIW6zJQ,29953
|
|
196
196
|
claude_mpm/core/cache.py,sha256=orh8B40oYnRRGmXdANR4P4f-KVMIHV0vg7vrsemk0no,17232
|
|
197
197
|
claude_mpm/core/claude_runner.py,sha256=h64Nbo0eh7pfTk_QMO3tb3rQZEEULRvSfLN8vgpZXdQ,34686
|
|
198
|
-
claude_mpm/core/config.py,sha256=
|
|
198
|
+
claude_mpm/core/config.py,sha256=qBus1zl0l56XntWaElWWyiqQ4mhDDPG7o1ELUOXiK2k,41300
|
|
199
199
|
claude_mpm/core/config_aliases.py,sha256=QpNNECkTY4TYYAhVlFZvB-msPnZrui90g19u0-v0HDE,9703
|
|
200
200
|
claude_mpm/core/config_constants.py,sha256=MEF35Y2Lj5FMzRdhgSgZrZeTzHfCzistPGZVY8INta4,10073
|
|
201
201
|
claude_mpm/core/constants.py,sha256=uBCd4kUbwSFKW79pH7_yUzRpMoNY2AcAflENTkIZIaU,11558
|
|
@@ -402,13 +402,14 @@ claude_mpm/experimental/__init__.py,sha256=R_aclOvWpvSTHWAx9QXyg9OIPVK2dXT5tQJhx
|
|
|
402
402
|
claude_mpm/experimental/cli_enhancements.py,sha256=PfAt-SI-crBoE0Dtx1JecpS5_6OT_0apJbo28KS6HUI,11541
|
|
403
403
|
claude_mpm/generators/__init__.py,sha256=rG8vwF_BjPmeMKvyMXpUA8uJ-7mtW2HTNfalZzgRlNk,153
|
|
404
404
|
claude_mpm/generators/agent_profile_generator.py,sha256=yTEFdZPUt4lAfXlQuIIxzRwOrWMaJhEJ3Z6Ofm48Rlc,5740
|
|
405
|
-
claude_mpm/hooks/__init__.py,sha256=
|
|
405
|
+
claude_mpm/hooks/__init__.py,sha256=T8VQOEtVW434xeN5J0W8qxqmBj5uE7moLqZ4cm8Uub0,1182
|
|
406
406
|
claude_mpm/hooks/base_hook.py,sha256=wKbT_0g3dhvkA48pTz4GJpZQw8URhaT0LpZnCc7CEas,5026
|
|
407
407
|
claude_mpm/hooks/instruction_reinforcement.py,sha256=ez7HE-7w6h744iLGe6X2UKaqt2NGf93P03ZCEzFE15I,11193
|
|
408
408
|
claude_mpm/hooks/kuzu_enrichment_hook.py,sha256=jghoEZX8fA6HZ1kM_5l93cuCyy-AMBjWp-nPW5EgaTk,8729
|
|
409
409
|
claude_mpm/hooks/kuzu_memory_hook.py,sha256=mWQYcQt3_6s0EjjK1zZ6rv4EaUI-lCgiUu5bbNry2Zs,12696
|
|
410
410
|
claude_mpm/hooks/kuzu_response_hook.py,sha256=iyVrsOrGpp-VFOjKC5GUnXro088Ftex-vHmfHsmAUv8,6136
|
|
411
411
|
claude_mpm/hooks/memory_integration_hook.py,sha256=F8Hf35hmbmhxi-qHQJac4zoWIr60ob3PCHa4P_rbxO8,16635
|
|
412
|
+
claude_mpm/hooks/session_resume_hook.py,sha256=0D0Yn30IYXqYPLzHitqiZ3i3hwUIFQGJk6bxXfYb67E,3784
|
|
412
413
|
claude_mpm/hooks/tool_call_interceptor.py,sha256=tYUBJHjbtaI5-HSWcz0aeUW0CaiQPypuDOTULQ0BCNI,7506
|
|
413
414
|
claude_mpm/hooks/validation_hooks.py,sha256=i5uOaXifItwJfIdjjLCxAwrkKdkmo2Qa4qMc5-dwsKw,6465
|
|
414
415
|
claude_mpm/hooks/claude_hooks/__init__.py,sha256=b4mud_g3S-3itHY_Dzpbb_SmdMEcJwtUU8fTcqpLqqs,130
|
|
@@ -496,7 +497,7 @@ claude_mpm/services/agents/deployment/agent_record_service.py,sha256=dNkygfKDn4V
|
|
|
496
497
|
claude_mpm/services/agents/deployment/agent_restore_handler.py,sha256=YDPVY4m_4l5wSFq9fxsTqm0ixX8AeTyzERh-PtabuUY,3101
|
|
497
498
|
claude_mpm/services/agents/deployment/agent_state_service.py,sha256=cFBv8GEdW8gmch5OlpI6zCQdv3ANEqYV1YL0VIBRp0U,11799
|
|
498
499
|
claude_mpm/services/agents/deployment/agent_template_builder.py,sha256=0apuovTjKbIPLKdo0xHXd1-ABZH5_twnErl4ooZHmFU,46004
|
|
499
|
-
claude_mpm/services/agents/deployment/agent_validator.py,sha256=
|
|
500
|
+
claude_mpm/services/agents/deployment/agent_validator.py,sha256=4kUKHotEq_3HSNMbgSDdbtxUOVuFxtbFch87qF54c7Y,13438
|
|
500
501
|
claude_mpm/services/agents/deployment/agent_version_manager.py,sha256=Q2COYmnWDvDxsBh_ikN9Govk3o8Dy1T0X-rioMpUsis,11189
|
|
501
502
|
claude_mpm/services/agents/deployment/agent_versioning.py,sha256=PrOrauoe0rUftZvNSf0Uhch9DkqY4rtDrJf2uUXX9oE,999
|
|
502
503
|
claude_mpm/services/agents/deployment/agents_directory_resolver.py,sha256=4ylwNMs9i8eGa1jSBkU0wW1O0Jvr4GNrrWX81F0wQWU,2175
|
|
@@ -577,7 +578,9 @@ claude_mpm/services/cli/agent_output_formatter.py,sha256=FcJ18m3CyeFr_wotJfEmIUR
|
|
|
577
578
|
claude_mpm/services/cli/agent_validation_service.py,sha256=QtpP9GOwCD2wW40winh5NDrYXXLiFd_CXFUBWeYiMCk,21746
|
|
578
579
|
claude_mpm/services/cli/memory_crud_service.py,sha256=tU0zegxba5lhpOCGqLijN2NfEMLwnEoYyjaF5cZ1A90,22863
|
|
579
580
|
claude_mpm/services/cli/memory_output_formatter.py,sha256=iNXMSu-K24rkgVhJURS-B6GqUBxR1XEZg8ZewOll0SU,24796
|
|
580
|
-
claude_mpm/services/cli/
|
|
581
|
+
claude_mpm/services/cli/resume_service.py,sha256=ENlx5ynuw1bBuPX6XOinOizLqWGNAlSUOu--oV_hrRs,21758
|
|
582
|
+
claude_mpm/services/cli/session_manager.py,sha256=-zG0osFnDYyta9iPCwmL5Dxu6Z-4_LHylFfqQAnIEvY,20126
|
|
583
|
+
claude_mpm/services/cli/session_resume_helper.py,sha256=w42iyGatfWCQCB1JmaL-HMjICJvca-iPL6yIZpzstMM,12177
|
|
581
584
|
claude_mpm/services/cli/startup_checker.py,sha256=MmztJ7yAnk5O5shArGDzRzwBDmx1WikhtE5s9P4IU9M,12251
|
|
582
585
|
claude_mpm/services/cli/unified_dashboard_manager.py,sha256=LH45wbiKn5GqEZWPH-18TQnsisR3vQchvSnxa-_CDZI,15607
|
|
583
586
|
claude_mpm/services/communication/__init__.py,sha256=b4qc7_Rqy4DE9q7BAUlfUZjoYG4uimAyUnE0irPcXyU,560
|
|
@@ -886,9 +889,9 @@ claude_mpm/utils/subprocess_utils.py,sha256=D0izRT8anjiUb_JG72zlJR_JAw1cDkb7kalN
|
|
|
886
889
|
claude_mpm/validation/__init__.py,sha256=YZhwE3mhit-lslvRLuwfX82xJ_k4haZeKmh4IWaVwtk,156
|
|
887
890
|
claude_mpm/validation/agent_validator.py,sha256=GprtAvu80VyMXcKGsK_VhYiXWA6BjKHv7O6HKx0AB9w,20917
|
|
888
891
|
claude_mpm/validation/frontmatter_validator.py,sha256=YpJlYNNYcV8u6hIOi3_jaRsDnzhbcQpjCBE6eyBKaFY,7076
|
|
889
|
-
claude_mpm-4.
|
|
890
|
-
claude_mpm-4.
|
|
891
|
-
claude_mpm-4.
|
|
892
|
-
claude_mpm-4.
|
|
893
|
-
claude_mpm-4.
|
|
894
|
-
claude_mpm-4.
|
|
892
|
+
claude_mpm-4.20.0.dist-info/licenses/LICENSE,sha256=lpaivOlPuBZW1ds05uQLJJswy8Rp_HMNieJEbFlqvLk,1072
|
|
893
|
+
claude_mpm-4.20.0.dist-info/METADATA,sha256=-OduKfJpVe--_73SzHskXFsYxvA0TAZRrGb6DQHHs_E,23459
|
|
894
|
+
claude_mpm-4.20.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
895
|
+
claude_mpm-4.20.0.dist-info/entry_points.txt,sha256=Vlw3GNi-OtTpKSrez04iNrPmxNxYDpIWxmJCxiZ5Tx8,526
|
|
896
|
+
claude_mpm-4.20.0.dist-info/top_level.txt,sha256=1nUg3FEaBySgm8t-s54jK5zoPnu3_eY6EP6IOlekyHA,11
|
|
897
|
+
claude_mpm-4.20.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|