claude-mpm 5.4.89__py3-none-any.whl → 5.4.91__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/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
- claude_mpm/agents/PM_INSTRUCTIONS.md +67 -779
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/startup.py +51 -14
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +17 -0
- claude_mpm/init.py +1 -1
- claude_mpm/services/pm_skills_deployer.py +177 -83
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.4.91.dist-info/METADATA +377 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/RECORD +31 -27
- claude_mpm-5.4.89.dist-info/METADATA +0 -1023
- /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.89.dist-info → claude_mpm-5.4.91.dist-info}/top_level.txt +0 -0
|
@@ -688,9 +688,9 @@ class MPMInitCommand:
|
|
|
688
688
|
return len(text) // 4
|
|
689
689
|
|
|
690
690
|
def _deploy_pm_skills(self) -> None:
|
|
691
|
-
"""Deploy PM skills templates to project .claude
|
|
691
|
+
"""Deploy PM skills templates to project .claude directory.
|
|
692
692
|
|
|
693
|
-
Copies PM skills from bundled templates to .claude
|
|
693
|
+
Copies PM skills from bundled templates to .claude/skills/
|
|
694
694
|
with version tracking and checksum validation.
|
|
695
695
|
"""
|
|
696
696
|
try:
|
claude_mpm/cli/startup.py
CHANGED
|
@@ -1139,33 +1139,70 @@ def show_skill_summary():
|
|
|
1139
1139
|
|
|
1140
1140
|
|
|
1141
1141
|
def verify_and_show_pm_skills():
|
|
1142
|
-
"""Verify PM skills and display status.
|
|
1142
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1143
1143
|
|
|
1144
|
-
WHY: PM skills are
|
|
1145
|
-
|
|
1144
|
+
WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
|
|
1145
|
+
framework knowledge is unavailable at startup. Enhanced validation
|
|
1146
|
+
checks all required skills exist, are not corrupted, and auto-repairs
|
|
1147
|
+
if needed.
|
|
1148
|
+
|
|
1149
|
+
Shows deployment status:
|
|
1150
|
+
- "✓ PM skills: 8/8 verified" if all required skills are valid
|
|
1151
|
+
- "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
|
|
1152
|
+
- Non-blocking but visible warning if auto-repair fails
|
|
1146
1153
|
"""
|
|
1147
1154
|
try:
|
|
1148
1155
|
from pathlib import Path
|
|
1149
1156
|
|
|
1150
|
-
from ..services.pm_skills_deployer import
|
|
1157
|
+
from ..services.pm_skills_deployer import (
|
|
1158
|
+
REQUIRED_PM_SKILLS,
|
|
1159
|
+
PMSkillsDeployerService,
|
|
1160
|
+
)
|
|
1151
1161
|
|
|
1152
1162
|
deployer = PMSkillsDeployerService()
|
|
1153
1163
|
project_dir = Path.cwd()
|
|
1154
1164
|
|
|
1155
|
-
|
|
1165
|
+
# Verify with auto-repair enabled
|
|
1166
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1156
1167
|
|
|
1157
1168
|
if result.verified:
|
|
1158
|
-
# Show verified status
|
|
1159
|
-
|
|
1169
|
+
# Show verified status with count
|
|
1170
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1171
|
+
print(
|
|
1172
|
+
f"✓ PM skills: {total_required}/{total_required} verified", flush=True
|
|
1173
|
+
)
|
|
1160
1174
|
else:
|
|
1161
|
-
#
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1175
|
+
# Show warning with details
|
|
1176
|
+
missing_count = len(result.missing_skills)
|
|
1177
|
+
corrupted_count = len(result.corrupted_skills)
|
|
1178
|
+
|
|
1179
|
+
# Build status message
|
|
1180
|
+
issues = []
|
|
1181
|
+
if missing_count > 0:
|
|
1182
|
+
issues.append(f"{missing_count} missing")
|
|
1183
|
+
if corrupted_count > 0:
|
|
1184
|
+
issues.append(f"{corrupted_count} corrupted")
|
|
1185
|
+
|
|
1186
|
+
status = ", ".join(issues)
|
|
1187
|
+
|
|
1188
|
+
# Check if auto-repair was attempted
|
|
1189
|
+
if "Auto-repaired" in result.message:
|
|
1190
|
+
# Auto-repair succeeded
|
|
1191
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1192
|
+
print(
|
|
1193
|
+
f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
|
|
1194
|
+
flush=True,
|
|
1195
|
+
)
|
|
1167
1196
|
else:
|
|
1168
|
-
|
|
1197
|
+
# Auto-repair failed or not attempted
|
|
1198
|
+
print(f"⚠ PM skills: {status}", flush=True)
|
|
1199
|
+
|
|
1200
|
+
# Log warnings for debugging
|
|
1201
|
+
from ..core.logger import get_logger
|
|
1202
|
+
|
|
1203
|
+
logger = get_logger("cli")
|
|
1204
|
+
for warning in result.warnings:
|
|
1205
|
+
logger.warning(f"PM skills: {warning}")
|
|
1169
1206
|
|
|
1170
1207
|
except ImportError:
|
|
1171
1208
|
# PM skills deployer not available - skip silently
|
|
@@ -7,7 +7,7 @@ Shows welcome message, version info, ASCII art, and what's new section.
|
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
9
|
import shutil
|
|
10
|
-
import subprocess
|
|
10
|
+
import subprocess # nosec B404 - required for git operations
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import List
|
|
13
13
|
|
|
@@ -65,8 +65,8 @@ def _get_recent_commits(max_commits: int = 3) -> List[str]:
|
|
|
65
65
|
if not is_git_repository("."):
|
|
66
66
|
return []
|
|
67
67
|
|
|
68
|
-
# Run git log with custom format
|
|
69
|
-
result = subprocess.run(
|
|
68
|
+
# Run git log with custom format (safe - no user input)
|
|
69
|
+
result = subprocess.run( # nosec B603 B607
|
|
70
70
|
["git", "log", "--format=%h • %ar • %s", f"-{max_commits}"],
|
|
71
71
|
capture_output=True,
|
|
72
72
|
text=True,
|
|
@@ -216,6 +216,59 @@ def _get_cwd_display(max_width: int = 40) -> str:
|
|
|
216
216
|
return "..." + cwd[-(max_width - 3) :]
|
|
217
217
|
|
|
218
218
|
|
|
219
|
+
def _count_mpm_skills() -> int:
|
|
220
|
+
"""
|
|
221
|
+
Count user-level MPM skills from ~/.claude/skills/.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Number of skill directories with SKILL.md files
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
user_skills_dir = Path.home() / ".claude" / "skills"
|
|
228
|
+
if not user_skills_dir.exists():
|
|
229
|
+
return 0
|
|
230
|
+
|
|
231
|
+
# Count directories with SKILL.md (skill directories)
|
|
232
|
+
skill_count = 0
|
|
233
|
+
for item in user_skills_dir.iterdir():
|
|
234
|
+
if item.is_dir():
|
|
235
|
+
skill_file = item / "SKILL.md"
|
|
236
|
+
if skill_file.exists():
|
|
237
|
+
skill_count += 1
|
|
238
|
+
# Also count standalone .md files (legacy format)
|
|
239
|
+
elif item.is_file() and item.suffix == ".md" and item.name != "README.md":
|
|
240
|
+
skill_count += 1
|
|
241
|
+
|
|
242
|
+
return skill_count
|
|
243
|
+
except Exception:
|
|
244
|
+
# Silent failure - return 0 if any error
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _count_deployed_agents() -> int:
|
|
249
|
+
"""
|
|
250
|
+
Count deployed agents from .claude/agents/.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Number of deployed agent files
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
257
|
+
if not deploy_target.exists():
|
|
258
|
+
return 0
|
|
259
|
+
|
|
260
|
+
# Count .md files, excluding README and other docs
|
|
261
|
+
agent_files = [
|
|
262
|
+
f
|
|
263
|
+
for f in deploy_target.glob("*.md")
|
|
264
|
+
if not f.name.startswith(("README", "INSTRUCTIONS", "."))
|
|
265
|
+
]
|
|
266
|
+
return len(agent_files)
|
|
267
|
+
except Exception:
|
|
268
|
+
# Silent failure - return 0 if any error
|
|
269
|
+
return 0
|
|
270
|
+
|
|
271
|
+
|
|
219
272
|
def _format_two_column_line(
|
|
220
273
|
left: str, right: str, left_panel_width: int, right_panel_width: int
|
|
221
274
|
) -> str:
|
|
@@ -402,11 +455,25 @@ def display_startup_banner(version: str, logging_level: str) -> None:
|
|
|
402
455
|
)
|
|
403
456
|
)
|
|
404
457
|
|
|
405
|
-
# Line 10: Model info | separator
|
|
458
|
+
# Line 10: Model info with counts | separator
|
|
406
459
|
separator = "─" * right_panel_width
|
|
460
|
+
agent_count = _count_deployed_agents()
|
|
461
|
+
skill_count = _count_mpm_skills()
|
|
462
|
+
|
|
463
|
+
# Format: "Sonnet 4.5 · 44 agents, 19 skills"
|
|
464
|
+
if agent_count > 0 or skill_count > 0:
|
|
465
|
+
counts_text = []
|
|
466
|
+
if agent_count > 0:
|
|
467
|
+
counts_text.append(f"{agent_count} agent{'s' if agent_count != 1 else ''}")
|
|
468
|
+
if skill_count > 0:
|
|
469
|
+
counts_text.append(f"{skill_count} skill{'s' if skill_count != 1 else ''}")
|
|
470
|
+
model_info = f"Sonnet 4.5 · {', '.join(counts_text)}"
|
|
471
|
+
else:
|
|
472
|
+
model_info = "Sonnet 4.5 · Claude MPM"
|
|
473
|
+
|
|
407
474
|
lines.append(
|
|
408
475
|
_format_two_column_line(
|
|
409
|
-
|
|
476
|
+
model_info, separator, left_panel_width, right_panel_width
|
|
410
477
|
)
|
|
411
478
|
)
|
|
412
479
|
|
|
Binary file
|
|
@@ -200,6 +200,23 @@ class EventHandlers:
|
|
|
200
200
|
|
|
201
201
|
self.hook_handler._emit_socketio_event("", "pre_tool", pre_tool_data)
|
|
202
202
|
|
|
203
|
+
# Handle TodoWrite specially - emit dedicated todo_updated event
|
|
204
|
+
# WHY: Frontend expects todo_updated events for dashboard display
|
|
205
|
+
# The broadcaster.todo_updated() method exists but was never called
|
|
206
|
+
if tool_name == "TodoWrite" and tool_params.get("todos"):
|
|
207
|
+
todo_data = {
|
|
208
|
+
"todos": tool_params["todos"],
|
|
209
|
+
"total_count": len(tool_params["todos"]),
|
|
210
|
+
"session_id": session_id,
|
|
211
|
+
"timestamp": timestamp,
|
|
212
|
+
}
|
|
213
|
+
self.hook_handler._emit_socketio_event("", "todo_updated", todo_data)
|
|
214
|
+
if DEBUG:
|
|
215
|
+
print(
|
|
216
|
+
f" - Emitted todo_updated event with {len(tool_params['todos'])} todos for session {session_id[:8]}...",
|
|
217
|
+
file=sys.stderr,
|
|
218
|
+
)
|
|
219
|
+
|
|
203
220
|
def _handle_task_delegation(
|
|
204
221
|
self, tool_input: dict, pre_tool_data: dict, session_id: str
|
|
205
222
|
):
|
claude_mpm/init.py
CHANGED