claude-mpm 5.4.64__py3-none-any.whl → 5.4.96__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/CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md +405 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
- claude_mpm/agents/PM_INSTRUCTIONS.md +82 -686
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/autotodos.py +526 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/skills.py +166 -14
- claude_mpm/cli/executor.py +89 -0
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +59 -1
- claude_mpm/cli/startup.py +202 -367
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/commands/mpm-session-resume.md +1 -1
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/output_style_manager.py +21 -13
- claude_mpm/core/unified_config.py +50 -8
- claude_mpm/core/unified_paths.py +30 -13
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +216 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +28 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +317 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +177 -83
- claude_mpm/services/skills/git_skill_source_manager.py +5 -1
- claude_mpm/services/skills/selective_skill_deployer.py +114 -26
- 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-bug-reporting/SKILL.md +248 -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/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.4.96.dist-info/METADATA +377 -0
- {claude_mpm-5.4.64.dist-info → claude_mpm-5.4.96.dist-info}/RECORD +153 -131
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm-5.4.64.dist-info/METADATA +0 -999
- /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.64.dist-info → claude_mpm-5.4.96.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.64.dist-info → claude_mpm-5.4.96.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.64.dist-info → claude_mpm-5.4.96.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.64.dist-info → claude_mpm-5.4.96.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.64.dist-info → claude_mpm-5.4.96.dist-info}/top_level.txt +0 -0
|
@@ -1,27 +1,30 @@
|
|
|
1
1
|
"""PM Skills Deployer Service - Deploy bundled PM skills to projects.
|
|
2
2
|
|
|
3
|
-
WHY: PM agents require specific
|
|
3
|
+
WHY: PM agents require specific framework management skills for proper operation.
|
|
4
4
|
This service manages deployment of bundled PM skills from the claude-mpm
|
|
5
|
-
package to
|
|
5
|
+
package to the Claude Code skills directory with version tracking.
|
|
6
6
|
|
|
7
7
|
DESIGN DECISIONS:
|
|
8
|
-
- Deploys from src/claude_mpm/skills/bundled/pm/ to .claude
|
|
8
|
+
- Deploys from src/claude_mpm/skills/bundled/pm/ to .claude/skills/
|
|
9
|
+
- Skills named mpm-* (framework management skills)
|
|
10
|
+
- Direct deployment (no intermediate .claude-mpm/skills/pm/ step)
|
|
9
11
|
- Uses package-relative paths (works for both installed and dev mode)
|
|
10
|
-
- Supports
|
|
11
|
-
|
|
12
|
-
2. Flat files: skill-name.md (legacy format in .claude-mpm/templates/)
|
|
13
|
-
- Per-project deployment (NOT global like Claude Code skills)
|
|
12
|
+
- Supports directory structure: mpm-skill-name/SKILL.md
|
|
13
|
+
- Per-project deployment to .claude/skills/ (Claude Code location)
|
|
14
14
|
- Version tracking via .claude-mpm/pm_skills_registry.yaml
|
|
15
15
|
- Checksum validation for integrity verification
|
|
16
|
+
- Conflict resolution: mpm-* skills from src WIN (overwrite existing)
|
|
17
|
+
- Non-mpm-* skills in .claude/skills/ are untouched (user/git managed)
|
|
16
18
|
- Non-blocking verification (returns warnings, doesn't halt execution)
|
|
17
19
|
- Force flag to redeploy even if versions match
|
|
18
20
|
|
|
19
21
|
ARCHITECTURE:
|
|
20
22
|
1. Discovery: Find bundled PM skills in package (skills/bundled/pm/)
|
|
21
|
-
2. Deployment: Copy SKILL.md files to .claude
|
|
22
|
-
3.
|
|
23
|
-
4.
|
|
24
|
-
5.
|
|
23
|
+
2. Deployment: Copy SKILL.md files to .claude/skills/{name}/SKILL.md
|
|
24
|
+
3. Conflict Check: Overwrite mpm-* skills, preserve non-mpm-* skills
|
|
25
|
+
4. Registry: Track deployed versions and checksums
|
|
26
|
+
5. Verification: Check deployment status (non-blocking)
|
|
27
|
+
6. Updates: Compare bundled vs deployed versions
|
|
25
28
|
|
|
26
29
|
PATH RESOLUTION:
|
|
27
30
|
- Installed package: Uses __file__ to find skills/bundled/pm/
|
|
@@ -47,6 +50,22 @@ from claude_mpm.core.mixins import LoggerMixin
|
|
|
47
50
|
# Security constants
|
|
48
51
|
MAX_YAML_SIZE = 10 * 1024 * 1024 # 10MB limit to prevent YAML bombs
|
|
49
52
|
|
|
53
|
+
# Required PM skills that MUST be deployed for PM agent to function properly
|
|
54
|
+
# These are framework management skills that PM uses for orchestration
|
|
55
|
+
REQUIRED_PM_SKILLS = [
|
|
56
|
+
"mpm-git-file-tracking",
|
|
57
|
+
"mpm-pr-workflow",
|
|
58
|
+
"mpm-ticketing-integration",
|
|
59
|
+
"mpm-delegation-patterns",
|
|
60
|
+
"mpm-verification-protocols",
|
|
61
|
+
"mpm-bug-reporting",
|
|
62
|
+
"mpm-teaching-mode",
|
|
63
|
+
"mpm-agent-update-workflow",
|
|
64
|
+
"mpm-circuit-breaker-enforcement",
|
|
65
|
+
"mpm-tool-usage-guide",
|
|
66
|
+
"mpm-session-management",
|
|
67
|
+
]
|
|
68
|
+
|
|
50
69
|
|
|
51
70
|
@dataclass
|
|
52
71
|
class PMSkillInfo:
|
|
@@ -96,6 +115,7 @@ class VerificationResult:
|
|
|
96
115
|
verified: Whether all skills are properly deployed
|
|
97
116
|
warnings: List of warning messages
|
|
98
117
|
missing_skills: List of missing skill names
|
|
118
|
+
corrupted_skills: List of corrupted skill names (checksum mismatch)
|
|
99
119
|
outdated_skills: List of outdated skill names
|
|
100
120
|
message: Summary message
|
|
101
121
|
skill_count: Total number of deployed skills
|
|
@@ -104,6 +124,7 @@ class VerificationResult:
|
|
|
104
124
|
verified: bool
|
|
105
125
|
warnings: List[str]
|
|
106
126
|
missing_skills: List[str]
|
|
127
|
+
corrupted_skills: List[str]
|
|
107
128
|
outdated_skills: List[str]
|
|
108
129
|
message: str
|
|
109
130
|
skill_count: int = 0
|
|
@@ -130,8 +151,9 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
130
151
|
"""Deploy and manage PM skills from bundled sources to projects.
|
|
131
152
|
|
|
132
153
|
This service provides:
|
|
133
|
-
- Discovery of bundled PM skills (
|
|
134
|
-
- Deployment to .claude
|
|
154
|
+
- Discovery of bundled PM skills (mpm-* framework management skills)
|
|
155
|
+
- Deployment to .claude/skills/ (Claude Code location)
|
|
156
|
+
- Conflict resolution (mpm-* skills from src WIN)
|
|
135
157
|
- Version tracking via pm_skills_registry.yaml
|
|
136
158
|
- Checksum validation for integrity
|
|
137
159
|
- Non-blocking verification (warnings only)
|
|
@@ -140,7 +162,7 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
140
162
|
Example:
|
|
141
163
|
>>> deployer = PMSkillsDeployerService()
|
|
142
164
|
>>> result = deployer.deploy_pm_skills(Path("/project/root"))
|
|
143
|
-
>>> print(f"Deployed {len(result.deployed)} skills")
|
|
165
|
+
>>> print(f"Deployed {len(result.deployed)} skills to .claude/skills/")
|
|
144
166
|
>>>
|
|
145
167
|
>>> verify_result = deployer.verify_pm_skills(Path("/project/root"))
|
|
146
168
|
>>> if not verify_result.verified:
|
|
@@ -246,9 +268,9 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
246
268
|
project_dir: Project root directory
|
|
247
269
|
|
|
248
270
|
Returns:
|
|
249
|
-
Path to .claude
|
|
271
|
+
Path to .claude/skills/
|
|
250
272
|
"""
|
|
251
|
-
return project_dir / ".claude
|
|
273
|
+
return project_dir / ".claude" / "skills"
|
|
252
274
|
|
|
253
275
|
def _load_registry(self, project_dir: Path) -> Dict[str, Any]:
|
|
254
276
|
"""Load PM skills registry with security checks.
|
|
@@ -322,14 +344,12 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
322
344
|
def _discover_bundled_pm_skills(self) -> List[Dict[str, Any]]:
|
|
323
345
|
"""Discover all PM skills in bundled templates directory.
|
|
324
346
|
|
|
325
|
-
PM skills
|
|
326
|
-
1. Directory structure: pm-skill-name/SKILL.md (new format)
|
|
327
|
-
2. Flat files: skill-name.md (legacy format for .claude-mpm/templates/)
|
|
347
|
+
PM skills follow mpm-skill-name/SKILL.md structure.
|
|
328
348
|
|
|
329
349
|
Returns:
|
|
330
350
|
List of skill dictionaries containing:
|
|
331
|
-
- name: Skill name (directory
|
|
332
|
-
- path: Full path to skill file (SKILL.md
|
|
351
|
+
- name: Skill name (directory name, e.g., mpm-git-file-tracking)
|
|
352
|
+
- path: Full path to skill file (SKILL.md)
|
|
333
353
|
- type: File type (always 'md')
|
|
334
354
|
"""
|
|
335
355
|
skills = []
|
|
@@ -340,11 +360,16 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
340
360
|
)
|
|
341
361
|
return skills
|
|
342
362
|
|
|
343
|
-
# Scan for skill directories containing SKILL.md
|
|
363
|
+
# Scan for skill directories containing SKILL.md
|
|
344
364
|
for skill_dir in self.bundled_pm_skills_path.iterdir():
|
|
345
365
|
if not skill_dir.is_dir() or skill_dir.name.startswith("."):
|
|
346
366
|
continue
|
|
347
367
|
|
|
368
|
+
# Only process mpm-* skills (framework management)
|
|
369
|
+
if not skill_dir.name.startswith("mpm-"):
|
|
370
|
+
self.logger.debug(f"Skipping non-mpm skill: {skill_dir.name}")
|
|
371
|
+
continue
|
|
372
|
+
|
|
348
373
|
skill_file = skill_dir / "SKILL.md"
|
|
349
374
|
if skill_file.exists():
|
|
350
375
|
skills.append(
|
|
@@ -355,19 +380,6 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
355
380
|
}
|
|
356
381
|
)
|
|
357
382
|
|
|
358
|
-
# Fallback: Scan for .md files directly (legacy format)
|
|
359
|
-
for skill_file in self.bundled_pm_skills_path.glob("*.md"):
|
|
360
|
-
if skill_file.name.startswith("."):
|
|
361
|
-
continue
|
|
362
|
-
|
|
363
|
-
skills.append(
|
|
364
|
-
{
|
|
365
|
-
"name": skill_file.stem,
|
|
366
|
-
"path": skill_file,
|
|
367
|
-
"type": "md",
|
|
368
|
-
}
|
|
369
|
-
)
|
|
370
|
-
|
|
371
383
|
self.logger.info(f"Discovered {len(skills)} bundled PM skills")
|
|
372
384
|
return skills
|
|
373
385
|
|
|
@@ -379,9 +391,13 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
379
391
|
) -> DeploymentResult:
|
|
380
392
|
"""Deploy bundled PM skills to project directory.
|
|
381
393
|
|
|
382
|
-
Copies PM skills from bundled templates to .claude
|
|
394
|
+
Copies PM skills from bundled templates to .claude/skills/{name}/SKILL.md
|
|
383
395
|
and updates registry with version and checksum information.
|
|
384
396
|
|
|
397
|
+
Conflict Resolution:
|
|
398
|
+
- mpm-* skills from src WIN (overwrite existing)
|
|
399
|
+
- Non-mpm-* skills in .claude/skills/ are untouched
|
|
400
|
+
|
|
385
401
|
Args:
|
|
386
402
|
project_dir: Project root directory
|
|
387
403
|
force: If True, redeploy even if skill already exists
|
|
@@ -392,7 +408,7 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
392
408
|
|
|
393
409
|
Example:
|
|
394
410
|
>>> result = deployer.deploy_pm_skills(Path("/project"), force=True)
|
|
395
|
-
>>> print(f"Deployed: {len(result.deployed)}")
|
|
411
|
+
>>> print(f"Deployed: {len(result.deployed)} to .claude/skills/")
|
|
396
412
|
"""
|
|
397
413
|
skills = self._discover_bundled_pm_skills()
|
|
398
414
|
deployed = []
|
|
@@ -447,8 +463,12 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
447
463
|
if progress_callback:
|
|
448
464
|
progress_callback(skill_name, idx + 1, total_skills)
|
|
449
465
|
|
|
450
|
-
#
|
|
451
|
-
|
|
466
|
+
# Create skill directory: .claude/skills/{skill_name}/
|
|
467
|
+
skill_dir = deployment_dir / skill_name
|
|
468
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
469
|
+
|
|
470
|
+
# Target path: .claude/skills/{skill_name}/SKILL.md
|
|
471
|
+
target_path = skill_dir / "SKILL.md"
|
|
452
472
|
|
|
453
473
|
# SECURITY: Validate target path
|
|
454
474
|
if not self._validate_safe_path(deployment_dir, target_path):
|
|
@@ -468,7 +488,7 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
468
488
|
)
|
|
469
489
|
continue
|
|
470
490
|
|
|
471
|
-
# Deploy skill
|
|
491
|
+
# Deploy skill (overwrites if exists - mpm-* skills WIN)
|
|
472
492
|
shutil.copy2(source_path, target_path)
|
|
473
493
|
|
|
474
494
|
# Add to deployed list
|
|
@@ -520,76 +540,107 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
520
540
|
message=message,
|
|
521
541
|
)
|
|
522
542
|
|
|
523
|
-
def verify_pm_skills(
|
|
524
|
-
|
|
543
|
+
def verify_pm_skills(
|
|
544
|
+
self, project_dir: Path, auto_repair: bool = True
|
|
545
|
+
) -> VerificationResult:
|
|
546
|
+
"""Verify PM skills are properly deployed with enhanced validation.
|
|
547
|
+
|
|
548
|
+
Checks ALL required PM skills for:
|
|
549
|
+
- Existence in deployment directory
|
|
550
|
+
- File integrity (non-empty, valid checksums)
|
|
551
|
+
- Version currency (compared to bundled source)
|
|
525
552
|
|
|
526
|
-
|
|
527
|
-
|
|
553
|
+
Auto-repair logic:
|
|
554
|
+
- If auto_repair=True (default), automatically deploys missing/corrupted skills
|
|
555
|
+
- Reports what was fixed in the result
|
|
528
556
|
|
|
529
557
|
Args:
|
|
530
558
|
project_dir: Project root directory
|
|
559
|
+
auto_repair: If True, auto-deploy missing/corrupted skills (default: True)
|
|
531
560
|
|
|
532
561
|
Returns:
|
|
533
|
-
VerificationResult with verification status
|
|
562
|
+
VerificationResult with detailed verification status:
|
|
563
|
+
- verified: True if all required skills are deployed and valid
|
|
564
|
+
- missing_skills: List of required skills not deployed
|
|
565
|
+
- corrupted_skills: List of skills with checksum mismatches
|
|
566
|
+
- warnings: List of warning messages
|
|
567
|
+
- skill_count: Total number of deployed skills
|
|
534
568
|
|
|
535
569
|
Example:
|
|
536
570
|
>>> result = deployer.verify_pm_skills(Path("/project"))
|
|
537
571
|
>>> if not result.verified:
|
|
538
|
-
...
|
|
539
|
-
...
|
|
572
|
+
... print(f"Missing: {result.missing_skills}")
|
|
573
|
+
... print(f"Corrupted: {result.corrupted_skills}")
|
|
540
574
|
"""
|
|
541
575
|
warnings = []
|
|
542
576
|
missing_skills = []
|
|
577
|
+
corrupted_skills = []
|
|
543
578
|
outdated_skills = []
|
|
544
579
|
|
|
545
580
|
# Check if registry exists
|
|
546
581
|
registry = self._load_registry(project_dir)
|
|
547
|
-
if not registry:
|
|
548
|
-
warnings.append("PM skills registry not found or invalid")
|
|
549
|
-
missing_skills.append("all")
|
|
550
|
-
return VerificationResult(
|
|
551
|
-
verified=False,
|
|
552
|
-
warnings=warnings,
|
|
553
|
-
missing_skills=missing_skills,
|
|
554
|
-
outdated_skills=outdated_skills,
|
|
555
|
-
message="PM skills not deployed. Run 'claude-mpm init' to deploy.",
|
|
556
|
-
skill_count=0,
|
|
557
|
-
)
|
|
558
|
-
|
|
559
|
-
# Check each registered skill exists
|
|
560
582
|
deployment_dir = self._get_deployment_dir(project_dir)
|
|
561
|
-
|
|
583
|
+
deployed_skills_data = registry.get("skills", []) if registry else []
|
|
562
584
|
|
|
563
|
-
for
|
|
564
|
-
|
|
565
|
-
skill_file = deployment_dir / f"{skill_name}.md"
|
|
585
|
+
# Build lookup for deployed skills
|
|
586
|
+
deployed_lookup = {skill["name"]: skill for skill in deployed_skills_data}
|
|
566
587
|
|
|
588
|
+
# Check ALL required PM skills
|
|
589
|
+
for required_skill in REQUIRED_PM_SKILLS:
|
|
590
|
+
# Check if skill is in registry
|
|
591
|
+
if required_skill not in deployed_lookup:
|
|
592
|
+
warnings.append(f"Required PM skill missing: {required_skill}")
|
|
593
|
+
missing_skills.append(required_skill)
|
|
594
|
+
continue
|
|
595
|
+
|
|
596
|
+
# Check if skill file exists
|
|
597
|
+
skill_file = deployment_dir / required_skill / "SKILL.md"
|
|
567
598
|
if not skill_file.exists():
|
|
568
|
-
warnings.append(
|
|
569
|
-
|
|
599
|
+
warnings.append(
|
|
600
|
+
f"Required PM skill file missing: {required_skill}/SKILL.md"
|
|
601
|
+
)
|
|
602
|
+
missing_skills.append(required_skill)
|
|
603
|
+
continue
|
|
604
|
+
|
|
605
|
+
# Check if skill file is empty/corrupted
|
|
606
|
+
try:
|
|
607
|
+
file_size = skill_file.stat().st_size
|
|
608
|
+
if file_size == 0:
|
|
609
|
+
warnings.append(
|
|
610
|
+
f"Required PM skill file is empty: {required_skill}/SKILL.md"
|
|
611
|
+
)
|
|
612
|
+
corrupted_skills.append(required_skill)
|
|
613
|
+
continue
|
|
614
|
+
except OSError as e:
|
|
615
|
+
warnings.append(
|
|
616
|
+
f"Cannot read required PM skill file: {required_skill}/SKILL.md - {e}"
|
|
617
|
+
)
|
|
618
|
+
corrupted_skills.append(required_skill)
|
|
570
619
|
continue
|
|
571
620
|
|
|
572
621
|
# Verify checksum
|
|
622
|
+
deployed_skill = deployed_lookup[required_skill]
|
|
573
623
|
current_checksum = self._compute_checksum(skill_file)
|
|
574
|
-
expected_checksum =
|
|
624
|
+
expected_checksum = deployed_skill.get("checksum", "")
|
|
575
625
|
|
|
576
626
|
if current_checksum != expected_checksum:
|
|
577
627
|
warnings.append(
|
|
578
|
-
f"
|
|
628
|
+
f"Required PM skill checksum mismatch: {required_skill} (file may be corrupted)"
|
|
579
629
|
)
|
|
580
|
-
|
|
630
|
+
corrupted_skills.append(required_skill)
|
|
581
631
|
|
|
582
|
-
# Check for available updates
|
|
632
|
+
# Check for available updates (bundled skills newer than deployed)
|
|
583
633
|
bundled_skills = {s["name"]: s for s in self._discover_bundled_pm_skills()}
|
|
584
634
|
for skill_name, bundled_skill in bundled_skills.items():
|
|
635
|
+
# Skip non-required skills
|
|
636
|
+
if skill_name not in REQUIRED_PM_SKILLS:
|
|
637
|
+
continue
|
|
638
|
+
|
|
585
639
|
# Find corresponding deployed skill
|
|
586
|
-
deployed_skill =
|
|
587
|
-
(s for s in deployed_skills if s["name"] == skill_name), None
|
|
588
|
-
)
|
|
640
|
+
deployed_skill = deployed_lookup.get(skill_name)
|
|
589
641
|
|
|
590
642
|
if not deployed_skill:
|
|
591
|
-
|
|
592
|
-
missing_skills.append(skill_name)
|
|
643
|
+
# Already tracked as missing
|
|
593
644
|
continue
|
|
594
645
|
|
|
595
646
|
# Check if checksums differ
|
|
@@ -597,23 +648,66 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
597
648
|
deployed_checksum = deployed_skill.get("checksum", "")
|
|
598
649
|
|
|
599
650
|
if bundled_checksum != deployed_checksum:
|
|
600
|
-
|
|
601
|
-
|
|
651
|
+
# Don't add to outdated_skills if already in corrupted_skills
|
|
652
|
+
if skill_name not in corrupted_skills:
|
|
653
|
+
warnings.append(f"PM skill update available: {skill_name}")
|
|
654
|
+
outdated_skills.append(skill_name)
|
|
655
|
+
|
|
656
|
+
# Auto-repair if enabled and issues found
|
|
657
|
+
repaired_skills = []
|
|
658
|
+
if auto_repair and (missing_skills or corrupted_skills):
|
|
659
|
+
self.logger.info(
|
|
660
|
+
f"Auto-repairing PM skills: {len(missing_skills)} missing, "
|
|
661
|
+
f"{len(corrupted_skills)} corrupted"
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# Deploy missing and corrupted skills
|
|
665
|
+
repair_result = self.deploy_pm_skills(project_dir, force=True)
|
|
602
666
|
|
|
603
|
-
|
|
667
|
+
if repair_result.success:
|
|
668
|
+
repaired_skills = repair_result.deployed
|
|
669
|
+
self.logger.info(f"Auto-repaired {len(repaired_skills)} PM skills")
|
|
604
670
|
|
|
671
|
+
# Remove repaired skills from missing/corrupted lists
|
|
672
|
+
missing_skills = [s for s in missing_skills if s not in repaired_skills]
|
|
673
|
+
corrupted_skills = [
|
|
674
|
+
s for s in corrupted_skills if s not in repaired_skills
|
|
675
|
+
]
|
|
676
|
+
|
|
677
|
+
# Update warnings
|
|
678
|
+
if repaired_skills:
|
|
679
|
+
warnings.append(
|
|
680
|
+
f"Auto-repaired {len(repaired_skills)} PM skills: {', '.join(repaired_skills)}"
|
|
681
|
+
)
|
|
682
|
+
else:
|
|
683
|
+
warnings.append(
|
|
684
|
+
f"Auto-repair failed: {len(repair_result.errors)} errors"
|
|
685
|
+
)
|
|
686
|
+
self.logger.error(
|
|
687
|
+
f"Auto-repair failed with errors: {repair_result.errors}"
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Determine verification status
|
|
691
|
+
verified = len(missing_skills) == 0 and len(corrupted_skills) == 0
|
|
692
|
+
|
|
693
|
+
# Build message
|
|
605
694
|
if verified:
|
|
606
|
-
|
|
695
|
+
if repaired_skills:
|
|
696
|
+
message = f"All PM skills verified (auto-repaired {len(repaired_skills)} skills)"
|
|
697
|
+
else:
|
|
698
|
+
message = "All PM skills verified and up-to-date"
|
|
607
699
|
else:
|
|
608
|
-
|
|
700
|
+
issue_count = len(missing_skills) + len(corrupted_skills)
|
|
701
|
+
message = f"{issue_count} PM skill issues found"
|
|
609
702
|
|
|
610
703
|
return VerificationResult(
|
|
611
704
|
verified=verified,
|
|
612
705
|
warnings=warnings,
|
|
613
706
|
missing_skills=missing_skills,
|
|
707
|
+
corrupted_skills=corrupted_skills,
|
|
614
708
|
outdated_skills=outdated_skills,
|
|
615
709
|
message=message,
|
|
616
|
-
skill_count=len(
|
|
710
|
+
skill_count=len(deployed_skills_data),
|
|
617
711
|
)
|
|
618
712
|
|
|
619
713
|
def get_deployed_skills(self, project_dir: Path) -> List[PMSkillInfo]:
|
|
@@ -636,10 +730,10 @@ class PMSkillsDeployerService(LoggerMixin):
|
|
|
636
730
|
skills = []
|
|
637
731
|
for skill_data in registry.get("skills", []):
|
|
638
732
|
skill_name = skill_data["name"]
|
|
639
|
-
deployed_path = deployment_dir /
|
|
733
|
+
deployed_path = deployment_dir / skill_name / "SKILL.md"
|
|
640
734
|
|
|
641
735
|
# Find source path (may not exist if bundled skills changed)
|
|
642
|
-
source_path = self.bundled_pm_skills_path /
|
|
736
|
+
source_path = self.bundled_pm_skills_path / skill_name / "SKILL.md"
|
|
643
737
|
|
|
644
738
|
skills.append(
|
|
645
739
|
PMSkillInfo(
|
|
@@ -1095,7 +1095,11 @@ class GitSkillSourceManager:
|
|
|
1095
1095
|
if removed_skills:
|
|
1096
1096
|
self.logger.info(
|
|
1097
1097
|
f"Removed {len(removed_skills)} orphaned skills not referenced by agents: {removed_skills[:10]}"
|
|
1098
|
-
+ (
|
|
1098
|
+
+ (
|
|
1099
|
+
f" (and {len(removed_skills) - 10} more)"
|
|
1100
|
+
if len(removed_skills) > 10
|
|
1101
|
+
else ""
|
|
1102
|
+
)
|
|
1099
1103
|
)
|
|
1100
1104
|
|
|
1101
1105
|
self.logger.info(
|