claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__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.

Files changed (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
@@ -18,6 +18,11 @@ from dataclasses import dataclass, field
18
18
  from datetime import datetime, timezone
19
19
  from typing import Any, Dict, List, Optional
20
20
 
21
+ # Privileged users who can push directly to main branch
22
+ # All other users must use feature branches and PRs
23
+ PRIVILEGED_GIT_USERS = ["bobmatnyc@users.noreply.github.com"]
24
+ PROTECTED_BRANCHES = ["main", "master"]
25
+
21
26
 
22
27
  @dataclass
23
28
  class GitBranchInfo:
@@ -101,6 +106,94 @@ class GitOperationsManager:
101
106
  if not self._is_git_repository():
102
107
  raise GitOperationError(f"Not a Git repository: {project_root}")
103
108
 
109
+ def _get_current_git_user(self) -> str:
110
+ """
111
+ Get the current Git user email.
112
+
113
+ Returns:
114
+ Git user email configured in repository or globally
115
+
116
+ Raises:
117
+ GitOperationError: If git user.email is not configured
118
+ """
119
+ try:
120
+ result = self._run_git_command(["config", "user.email"])
121
+ email = result.stdout.strip()
122
+ if not email:
123
+ raise GitOperationError(
124
+ "Git user.email is not configured. "
125
+ "Please configure it with: git config user.email 'your@email.com'"
126
+ )
127
+ return email
128
+ except GitOperationError as e:
129
+ raise GitOperationError(
130
+ "Git user.email is not configured. "
131
+ "Please configure it with: git config user.email 'your@email.com'"
132
+ ) from e
133
+
134
+ def _is_privileged_user(self) -> bool:
135
+ """
136
+ Check if the current Git user is privileged to push to protected branches.
137
+
138
+ Returns:
139
+ True if user email is in PRIVILEGED_GIT_USERS, False otherwise
140
+ """
141
+ try:
142
+ current_user = self._get_current_git_user()
143
+ return current_user in PRIVILEGED_GIT_USERS
144
+ except GitOperationError:
145
+ # If we can't determine user, assume not privileged
146
+ return False
147
+
148
+ def _enforce_branch_protection(
149
+ self, target_branch: str, operation: str
150
+ ) -> Optional[GitOperationResult]:
151
+ """
152
+ Enforce branch protection rules for protected branches.
153
+
154
+ Args:
155
+ target_branch: Branch being operated on
156
+ operation: Operation being performed (e.g., "push", "merge")
157
+
158
+ Returns:
159
+ GitOperationResult with error if protection violated, None if allowed
160
+ """
161
+ # Check if target branch is protected
162
+ if target_branch not in PROTECTED_BRANCHES:
163
+ return None
164
+
165
+ # Check if user is privileged
166
+ if self._is_privileged_user():
167
+ return None
168
+
169
+ # Get current user for error message
170
+ try:
171
+ current_user = self._get_current_git_user()
172
+ except GitOperationError:
173
+ current_user = "unknown"
174
+
175
+ # Build helpful error message
176
+ error_message = (
177
+ f"Direct {operation} to '{target_branch}' branch is restricted.\n"
178
+ f"Only {', '.join(PRIVILEGED_GIT_USERS)} can {operation} directly to protected branches.\n"
179
+ f"Current user: {current_user}\n\n"
180
+ f"Please use the feature branch workflow:\n"
181
+ f" 1. git checkout -b feature/your-feature-name\n"
182
+ f" 2. Make your changes and commit\n"
183
+ f" 3. git push -u origin feature/your-feature-name\n"
184
+ f" 4. Create a Pull Request on GitHub for review"
185
+ )
186
+
187
+ return GitOperationResult(
188
+ success=False,
189
+ operation=f"{operation}_branch_protection",
190
+ message=f"Branch protection: {operation} to '{target_branch}' denied",
191
+ error=error_message,
192
+ branch_before=self.get_current_branch(),
193
+ branch_after=self.get_current_branch(),
194
+ execution_time=0.0,
195
+ )
196
+
104
197
  def _is_git_repository(self) -> bool:
105
198
  """Check if the directory is a Git repository."""
106
199
  return self.git_dir.exists() and self.git_dir.is_dir()
@@ -503,6 +596,11 @@ class GitOperationsManager:
503
596
  start_time = datetime.now(timezone.utc)
504
597
  current_branch = self.get_current_branch()
505
598
 
599
+ # Enforce branch protection for target branch
600
+ protection_result = self._enforce_branch_protection(target_branch, "merge")
601
+ if protection_result:
602
+ return protection_result
603
+
506
604
  try:
507
605
  # Switch to target branch
508
606
  if current_branch != target_branch:
@@ -659,6 +757,11 @@ class GitOperationsManager:
659
757
  if not branch_name:
660
758
  branch_name = current_branch
661
759
 
760
+ # Enforce branch protection
761
+ protection_result = self._enforce_branch_protection(branch_name, "push")
762
+ if protection_result:
763
+ return protection_result
764
+
662
765
  try:
663
766
  # Build push command
664
767
  push_args = ["push"]
@@ -2,7 +2,9 @@
2
2
 
3
3
  import json
4
4
  from pathlib import Path
5
- from typing import Dict, List, Optional
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ import yaml
6
8
 
7
9
  from claude_mpm.core.logging_utils import get_logger
8
10
 
@@ -56,12 +58,79 @@ class SkillManager:
56
58
  if mapping_count > 0:
57
59
  logger.info(f"Loaded skill mappings for {mapping_count} agents")
58
60
 
61
+ def _get_pm_skills(self, project_dir: Optional[Path] = None) -> List[Dict[str, Any]]:
62
+ """Load PM skills from project's .claude-mpm/skills/pm/ directory.
63
+
64
+ PM skills are special required skills deployed per-project,
65
+ NOT fetched from the skills repository.
66
+
67
+ Args:
68
+ project_dir: Project directory. Defaults to current working directory.
69
+
70
+ Returns:
71
+ List of PM skill dictionaries with metadata
72
+ """
73
+ if project_dir is None:
74
+ project_dir = Path.cwd()
75
+
76
+ pm_skills_dir = project_dir / ".claude-mpm" / "skills" / "pm"
77
+
78
+ if not pm_skills_dir.exists():
79
+ logger.debug("PM skills directory not found")
80
+ return []
81
+
82
+ skills = []
83
+ for skill_dir in pm_skills_dir.iterdir():
84
+ if skill_dir.is_dir():
85
+ skill_file = skill_dir / "SKILL.md"
86
+ if skill_file.exists():
87
+ skill = self._load_pm_skill(skill_file)
88
+ if skill:
89
+ skills.append(skill)
90
+
91
+ if skills:
92
+ logger.debug(f"Loaded {len(skills)} PM skills from {pm_skills_dir}")
93
+
94
+ return skills
95
+
96
+ def _load_pm_skill(self, skill_file: Path) -> Optional[Dict[str, Any]]:
97
+ """Load a single PM skill from SKILL.md file.
98
+
99
+ Args:
100
+ skill_file: Path to SKILL.md file
101
+
102
+ Returns:
103
+ Dictionary with skill metadata and content, or None if failed
104
+ """
105
+ try:
106
+ content = skill_file.read_text(encoding='utf-8')
107
+
108
+ # Parse YAML frontmatter
109
+ if content.startswith('---'):
110
+ parts = content.split('---', 2)
111
+ if len(parts) >= 3:
112
+ metadata = yaml.safe_load(parts[1])
113
+ body = parts[2].strip()
114
+
115
+ return {
116
+ 'name': metadata.get('name', skill_file.parent.name),
117
+ 'version': metadata.get('version', '1.0.0'),
118
+ 'description': metadata.get('description', ''),
119
+ 'when_to_use': metadata.get('when_to_use', ''),
120
+ 'content': body,
121
+ 'is_pm_skill': True
122
+ }
123
+ except Exception as e:
124
+ logger.warning(f"Failed to load PM skill {skill_file}: {e}")
125
+
126
+ return None
127
+
59
128
  def get_agent_skills(self, agent_type: str) -> List[Skill]:
60
129
  """
61
- Get all skills for an agent (bundled + discovered).
130
+ Get all skills for an agent (bundled + discovered + PM skills if PM agent).
62
131
 
63
132
  Args:
64
- agent_type: Agent type/ID (e.g., 'engineer', 'python_engineer')
133
+ agent_type: Agent type/ID (e.g., 'engineer', 'python_engineer', 'pm')
65
134
 
66
135
  Returns:
67
136
  List of Skill objects for this agent
@@ -86,6 +155,26 @@ class SkillManager:
86
155
  if skill not in skills:
87
156
  skills.append(skill)
88
157
 
158
+ # Add PM skills for PM agent only
159
+ if agent_type.lower() in ('pm', 'project-manager', 'project_manager'):
160
+ pm_skill_dicts = self._get_pm_skills()
161
+ for pm_skill_dict in pm_skill_dicts:
162
+ # Convert PM skill dict to Skill object
163
+ pm_skill = Skill(
164
+ name=pm_skill_dict['name'],
165
+ path=Path.cwd() / ".claude-mpm" / "skills" / "pm" / pm_skill_dict['name'],
166
+ content=pm_skill_dict['content'],
167
+ source='pm', # Special source type for PM skills
168
+ version=pm_skill_dict['version'],
169
+ skill_id=pm_skill_dict['name'],
170
+ description=pm_skill_dict['description'],
171
+ agent_types=['pm', 'project-manager', 'project_manager']
172
+ )
173
+ skills.append(pm_skill)
174
+
175
+ if pm_skill_dicts:
176
+ logger.debug(f"Added {len(pm_skill_dicts)} PM skills for PM agent")
177
+
89
178
  return skills
90
179
 
91
180
  def enhance_agent_prompt(
@@ -658,12 +658,24 @@ class AgentDependencyLoader:
658
658
  "Robust installer not available, falling back to simple installation"
659
659
  )
660
660
  try:
661
- cmd = [sys.executable, "-m", "pip", "install"]
662
-
663
661
  # Check environment and add appropriate flags
664
662
  import os
665
663
  import sysconfig
666
664
 
665
+ # Check if in UV tool environment (no pip available)
666
+ uv_tool_dir = os.environ.get("UV_TOOL_DIR", "")
667
+ is_uv_tool = (
668
+ (uv_tool_dir and "claude-mpm" in uv_tool_dir)
669
+ or ".local/share/uv/tools/" in sys.executable
670
+ or "/uv/tools/" in sys.executable
671
+ )
672
+
673
+ if is_uv_tool:
674
+ cmd = ["uv", "pip", "install"]
675
+ logger.debug("Using 'uv pip install' for UV tool environment")
676
+ else:
677
+ cmd = [sys.executable, "-m", "pip", "install"]
678
+
667
679
  # Check if in virtualenv
668
680
  in_virtualenv = (
669
681
  (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
@@ -4,10 +4,14 @@ Agent filtering utilities for claude-mpm.
4
4
  WHY: This module provides centralized filtering logic to remove non-deployable
5
5
  agents (BASE_AGENT) and already-deployed agents from user-facing displays.
6
6
 
7
+ ARCHITECTURE:
8
+ - SOURCE: ~/.claude-mpm/cache/agents/ (git repository cache)
9
+ - DEPLOYMENT: .claude/agents/ (project-level deployment location)
10
+
7
11
  DESIGN DECISIONS:
8
12
  - BASE_AGENT is a build tool, not a deployable agent - filter everywhere
9
- - Deployed agent detection supports both new (.claude-mpm/agents/) and
10
- legacy (.claude/agents/)
13
+ - Deployed agent detection checks .claude/agents/ for all deployed agents
14
+ - Supports both virtual (.mpm_deployment_state) and physical (.md files) detection
11
15
  - Case-insensitive BASE_AGENT detection for robustness
12
16
  - Pure functions for easy testing and reuse
13
17
 
@@ -102,10 +106,11 @@ def get_deployed_agent_ids(project_dir: Optional[Path] = None) -> Set[str]:
102
106
 
103
107
  Design Rationale:
104
108
  - Primary detection: Virtual deployment state (.mpm_deployment_state)
105
- - Fallback detection: Physical .md files (.claude-mpm/agents/, .claude/agents/)
109
+ - Fallback detection: Physical .md files in .claude/agents/
106
110
  - Returns leaf names for consistent comparison with agent_id formats
107
111
  - Combines both detection methods for complete coverage
108
112
  - Graceful error handling for malformed or missing state files
113
+ - Only checks project-level deployment (simplified architecture)
109
114
 
110
115
  Related:
111
116
  - Fixes checkbox interface showing all agents as "○ [Available]" instead of "● [Installed]"
@@ -115,24 +120,17 @@ def get_deployed_agent_ids(project_dir: Optional[Path] = None) -> Set[str]:
115
120
  deployed = set()
116
121
 
117
122
  # Track if project_dir was explicitly provided
118
- explicit_project_dir = project_dir is not None
119
123
 
120
124
  if project_dir is None:
121
125
  project_dir = Path.cwd()
122
126
 
123
127
  # NEW: Check virtual deployment state (primary method)
124
128
  # This is the current deployment model used by Claude Code
129
+ # Only checking project-level deployment in simplified architecture
125
130
  deployment_state_paths = [
126
131
  project_dir / ".claude" / "agents" / ".mpm_deployment_state",
127
132
  ]
128
133
 
129
- # Only check user-level state if using default project directory
130
- # This prevents test isolation issues when explicit project_dir is provided
131
- if not explicit_project_dir:
132
- deployment_state_paths.append(
133
- Path.home() / ".claude" / "agents" / ".mpm_deployment_state"
134
- )
135
-
136
134
  for state_path in deployment_state_paths:
137
135
  if state_path.exists():
138
136
  try:
@@ -162,42 +160,17 @@ def get_deployed_agent_ids(project_dir: Optional[Path] = None) -> Set[str]:
162
160
  continue
163
161
 
164
162
  # EXISTING: Check physical .md files (fallback for backward compatibility)
165
- # Check new architecture
166
- new_agents_dir = project_dir / ".claude-mpm" / "agents"
167
- if new_agents_dir.exists():
168
- for file in new_agents_dir.glob("*.md"):
169
- if file.stem not in {"BASE-AGENT", ".DS_Store"}:
170
- deployed.add(file.stem)
171
-
172
- # Check legacy architecture
173
- legacy_agents_dir = project_dir / ".claude" / "agents"
174
- if legacy_agents_dir.exists():
175
- for file in legacy_agents_dir.glob("*.md"):
163
+ # Check project deployment location (.claude/agents/)
164
+ agents_dir = project_dir / ".claude" / "agents"
165
+ if agents_dir.exists():
166
+ for file in agents_dir.glob("*.md"):
176
167
  if file.stem not in {"BASE-AGENT", ".DS_Store"}:
177
168
  deployed.add(file.stem)
178
169
 
179
- # Check .claude/templates/ directory (where agents are actually deployed)
180
- templates_dir = project_dir / ".claude" / "templates"
181
- if templates_dir.exists():
182
- for file in templates_dir.glob("*.md"):
183
- if file.stem not in {
184
- "BASE-AGENT",
185
- ".DS_Store",
186
- "README",
187
- "circuit-breakers",
188
- }:
189
- # Skip template/example files
190
- if not any(x in file.stem for x in ["example", "template", "pm-"]):
191
- deployed.add(file.stem)
192
-
193
- # Check user-level directory only if using default project directory
194
- # This prevents test isolation issues when explicit project_dir is provided
195
- if not explicit_project_dir:
196
- user_agents_dir = Path.home() / ".claude" / "agents"
197
- if user_agents_dir.exists():
198
- for file in user_agents_dir.glob("*.md"):
199
- if file.stem not in {"BASE-AGENT", ".DS_Store"}:
200
- deployed.add(file.stem)
170
+ # NOTE: .claude/templates/ contains PM instruction templates, NOT deployed agents
171
+ # It should NOT be checked here. Agents are deployed to:
172
+ # - .mpm_deployment_state (virtual deployment)
173
+ # - .claude/agents/*.md (project deployment)
201
174
 
202
175
  return deployed
203
176
 
@@ -212,6 +212,9 @@ def ensure_claude_mpm_gitignore(project_dir: str = ".") -> dict:
212
212
  entries_to_add = [
213
213
  ".claude-mpm/",
214
214
  ".claude/agents/",
215
+ ".mcp.json",
216
+ ".claude.json",
217
+ ".claude/",
215
218
  ]
216
219
 
217
220
  added, existing = manager.ensure_entries(entries_to_add)
@@ -2,7 +2,7 @@
2
2
 
3
3
  WHY: Phase 3 of 1M-486 requires migrating from old single-tier deployment
4
4
  (~/.claude/agents/, ~/.claude/skills/) to new two-phase architecture:
5
- - Cache: ~/.claude-mpm/cache/remote-agents/, ~/.claude-mpm/cache/skills/
5
+ - Cache: ~/.claude-mpm/cache/agents/, ~/.claude-mpm/cache/skills/
6
6
  - Deployment: .claude-mpm/agents/, .claude-mpm/skills/
7
7
 
8
8
  DESIGN DECISIONS:
@@ -41,7 +41,7 @@ class MigrationUtility:
41
41
  self.old_agent_dir = Path.home() / ".claude" / "agents"
42
42
  self.old_skill_dir = Path.home() / ".claude" / "skills"
43
43
 
44
- self.new_agent_cache = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
44
+ self.new_agent_cache = Path.home() / ".claude-mpm" / "cache" / "agents"
45
45
  self.new_skill_cache = Path.home() / ".claude-mpm" / "cache" / "skills"
46
46
 
47
47
  def detect_old_locations(self) -> Dict[str, bool]:
@@ -80,7 +80,7 @@ class MigrationUtility:
80
80
  def migrate_agents(
81
81
  self, dry_run: bool = False, auto_confirm: bool = False
82
82
  ) -> Dict[str, any]:
83
- """Migrate agents from ~/.claude/agents/ to ~/.claude-mpm/cache/remote-agents/.
83
+ """Migrate agents from ~/.claude/agents/ to ~/.claude-mpm/cache/agents/.
84
84
 
85
85
  Args:
86
86
  dry_run: Preview migration without making changes
@@ -329,7 +329,7 @@ class MigrationUtility:
329
329
 
330
330
  warning += "\nThe deployment architecture has changed:\n"
331
331
  warning += " OLD: ~/.claude/agents/ (single-tier, global)\n"
332
- warning += " NEW: ~/.claude-mpm/cache/remote-agents/ → .claude-mpm/agents/ (two-phase, per-project)\n\n"
332
+ warning += " NEW: ~/.claude-mpm/cache/agents/ → .claude-mpm/agents/ (two-phase, per-project)\n\n"
333
333
 
334
334
  warning += "To migrate:\n"
335
335
  warning += " claude-mpm migrate\n\n"
@@ -82,6 +82,7 @@ class RobustPackageInstaller:
82
82
  self.attempts: List[InstallAttempt] = []
83
83
  self.success_cache: Dict[str, bool] = {}
84
84
  self.in_virtualenv = self._check_virtualenv()
85
+ self.is_uv_tool = self._check_uv_tool_installation()
85
86
  self.is_pep668_managed = self._check_pep668_managed()
86
87
  self.pep668_warning_shown = False
87
88
 
@@ -261,6 +262,36 @@ class RobustPackageInstaller:
261
262
 
262
263
  return False
263
264
 
265
+ def _check_uv_tool_installation(self) -> bool:
266
+ """
267
+ Check if running in UV tool environment (no pip available).
268
+
269
+ WHY: UV tool environments don't have pip installed. The executable
270
+ path typically contains ".local/share/uv/tools/" and the UV_TOOL_DIR
271
+ environment variable is set. In such environments, we need to use
272
+ 'uv pip' instead of 'python -m pip'.
273
+
274
+ Returns:
275
+ True if UV tool environment, False otherwise
276
+ """
277
+ import os
278
+
279
+ # Check UV_TOOL_DIR environment variable
280
+ uv_tool_dir = os.environ.get("UV_TOOL_DIR", "")
281
+ if uv_tool_dir and "claude-mpm" in uv_tool_dir:
282
+ logger.debug(f"UV tool environment detected via UV_TOOL_DIR: {uv_tool_dir}")
283
+ return True
284
+
285
+ # Check executable path for UV tool patterns
286
+ executable = sys.executable
287
+ if ".local/share/uv/tools/" in executable or "/uv/tools/" in executable:
288
+ logger.debug(
289
+ f"UV tool environment detected via executable path: {executable}"
290
+ )
291
+ return True
292
+
293
+ return False
294
+
264
295
  def _show_pep668_warning(self) -> None:
265
296
  """
266
297
  Show warning about PEP 668 managed environment.
@@ -301,7 +332,12 @@ class RobustPackageInstaller:
301
332
  Returns:
302
333
  Command as list of arguments
303
334
  """
304
- base_cmd = [sys.executable, "-m", "pip", "install"]
335
+ # UV tool environments don't have pip; use uv pip instead
336
+ if self.is_uv_tool:
337
+ base_cmd = ["uv", "pip", "install"]
338
+ logger.debug("Using 'uv pip install' for UV tool environment")
339
+ else:
340
+ base_cmd = [sys.executable, "-m", "pip", "install"]
305
341
 
306
342
  # Determine appropriate flags based on environment
307
343
  if self.in_virtualenv:
@@ -651,7 +687,12 @@ class RobustPackageInstaller:
651
687
  Tuple of (success, error_message)
652
688
  """
653
689
  try:
654
- cmd = [sys.executable, "-m", "pip", "install"]
690
+ # UV tool environments don't have pip; use uv pip instead
691
+ if self.is_uv_tool:
692
+ cmd = ["uv", "pip", "install"]
693
+ logger.debug("Using 'uv pip install' for batch installation")
694
+ else:
695
+ cmd = [sys.executable, "-m", "pip", "install"]
655
696
 
656
697
  # Add appropriate flags based on environment
657
698
  if self.in_virtualenv:
@@ -702,7 +743,10 @@ class RobustPackageInstaller:
702
743
 
703
744
  # Add environment status
704
745
  lines.append("")
705
- if self.in_virtualenv:
746
+ if self.is_uv_tool:
747
+ lines.append("✓ Environment: UV Tool Environment")
748
+ lines.append(" Using 'uv pip' command (pip not available)")
749
+ elif self.in_virtualenv:
706
750
  lines.append("✓ Environment: Virtual Environment (isolated)")
707
751
  lines.append(" No special pip flags needed")
708
752
  elif self.is_pep668_managed: