claude-mpm 5.4.22__py3-none-any.whl → 5.6.34__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 (487) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT.md +164 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  4. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
  5. claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
  6. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
  7. claude_mpm/agents/MEMORY.md +1 -1
  8. claude_mpm/agents/PM_INSTRUCTIONS.md +374 -1257
  9. claude_mpm/agents/WORKFLOW.md +6 -253
  10. claude_mpm/agents/agent_loader.py +1 -1
  11. claude_mpm/agents/base_agent.json +31 -0
  12. claude_mpm/agents/frontmatter_validator.py +2 -2
  13. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  14. claude_mpm/cli/__init__.py +5 -1
  15. claude_mpm/cli/commands/agent_state_manager.py +10 -10
  16. claude_mpm/cli/commands/agents.py +11 -13
  17. claude_mpm/cli/commands/agents_reconcile.py +197 -0
  18. claude_mpm/cli/commands/auto_configure.py +4 -4
  19. claude_mpm/cli/commands/autotodos.py +566 -0
  20. claude_mpm/cli/commands/commander.py +216 -0
  21. claude_mpm/cli/commands/configure.py +621 -22
  22. claude_mpm/cli/commands/configure_agent_display.py +12 -0
  23. claude_mpm/cli/commands/hook_errors.py +60 -60
  24. claude_mpm/cli/commands/monitor.py +2 -2
  25. claude_mpm/cli/commands/mpm_init/core.py +72 -0
  26. claude_mpm/cli/commands/postmortem.py +1 -1
  27. claude_mpm/cli/commands/profile.py +276 -0
  28. claude_mpm/cli/commands/run.py +35 -3
  29. claude_mpm/cli/commands/skill_source.py +51 -2
  30. claude_mpm/cli/commands/skills.py +182 -32
  31. claude_mpm/cli/executor.py +130 -16
  32. claude_mpm/cli/interactive/__init__.py +10 -0
  33. claude_mpm/cli/interactive/agent_wizard.py +32 -52
  34. claude_mpm/cli/interactive/questionary_styles.py +65 -0
  35. claude_mpm/cli/interactive/skill_selector.py +481 -0
  36. claude_mpm/cli/parsers/base_parser.py +83 -1
  37. claude_mpm/cli/parsers/commander_parser.py +116 -0
  38. claude_mpm/cli/parsers/profile_parser.py +147 -0
  39. claude_mpm/cli/parsers/run_parser.py +10 -0
  40. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  41. claude_mpm/cli/parsers/skills_parser.py +2 -3
  42. claude_mpm/cli/startup.py +690 -386
  43. claude_mpm/cli/startup_display.py +74 -6
  44. claude_mpm/cli/startup_logging.py +2 -2
  45. claude_mpm/cli/utils.py +7 -3
  46. claude_mpm/commander/__init__.py +78 -0
  47. claude_mpm/commander/adapters/__init__.py +60 -0
  48. claude_mpm/commander/adapters/auggie.py +260 -0
  49. claude_mpm/commander/adapters/base.py +288 -0
  50. claude_mpm/commander/adapters/claude_code.py +392 -0
  51. claude_mpm/commander/adapters/codex.py +237 -0
  52. claude_mpm/commander/adapters/communication.py +366 -0
  53. claude_mpm/commander/adapters/example_usage.py +310 -0
  54. claude_mpm/commander/adapters/mpm.py +389 -0
  55. claude_mpm/commander/adapters/registry.py +204 -0
  56. claude_mpm/commander/api/__init__.py +16 -0
  57. claude_mpm/commander/api/app.py +121 -0
  58. claude_mpm/commander/api/errors.py +133 -0
  59. claude_mpm/commander/api/routes/__init__.py +8 -0
  60. claude_mpm/commander/api/routes/events.py +184 -0
  61. claude_mpm/commander/api/routes/inbox.py +171 -0
  62. claude_mpm/commander/api/routes/messages.py +148 -0
  63. claude_mpm/commander/api/routes/projects.py +271 -0
  64. claude_mpm/commander/api/routes/sessions.py +226 -0
  65. claude_mpm/commander/api/routes/work.py +296 -0
  66. claude_mpm/commander/api/schemas.py +186 -0
  67. claude_mpm/commander/chat/__init__.py +7 -0
  68. claude_mpm/commander/chat/cli.py +146 -0
  69. claude_mpm/commander/chat/commands.py +96 -0
  70. claude_mpm/commander/chat/repl.py +310 -0
  71. claude_mpm/commander/config.py +51 -0
  72. claude_mpm/commander/config_loader.py +115 -0
  73. claude_mpm/commander/core/__init__.py +10 -0
  74. claude_mpm/commander/core/block_manager.py +325 -0
  75. claude_mpm/commander/core/response_manager.py +323 -0
  76. claude_mpm/commander/daemon.py +603 -0
  77. claude_mpm/commander/env_loader.py +59 -0
  78. claude_mpm/commander/events/__init__.py +26 -0
  79. claude_mpm/commander/events/manager.py +332 -0
  80. claude_mpm/commander/frameworks/__init__.py +12 -0
  81. claude_mpm/commander/frameworks/base.py +146 -0
  82. claude_mpm/commander/frameworks/claude_code.py +58 -0
  83. claude_mpm/commander/frameworks/mpm.py +62 -0
  84. claude_mpm/commander/inbox/__init__.py +16 -0
  85. claude_mpm/commander/inbox/dedup.py +128 -0
  86. claude_mpm/commander/inbox/inbox.py +224 -0
  87. claude_mpm/commander/inbox/models.py +70 -0
  88. claude_mpm/commander/instance_manager.py +450 -0
  89. claude_mpm/commander/llm/__init__.py +6 -0
  90. claude_mpm/commander/llm/openrouter_client.py +167 -0
  91. claude_mpm/commander/llm/summarizer.py +70 -0
  92. claude_mpm/commander/memory/__init__.py +45 -0
  93. claude_mpm/commander/memory/compression.py +347 -0
  94. claude_mpm/commander/memory/embeddings.py +230 -0
  95. claude_mpm/commander/memory/entities.py +310 -0
  96. claude_mpm/commander/memory/example_usage.py +290 -0
  97. claude_mpm/commander/memory/integration.py +325 -0
  98. claude_mpm/commander/memory/search.py +381 -0
  99. claude_mpm/commander/memory/store.py +657 -0
  100. claude_mpm/commander/models/__init__.py +18 -0
  101. claude_mpm/commander/models/events.py +121 -0
  102. claude_mpm/commander/models/project.py +162 -0
  103. claude_mpm/commander/models/work.py +214 -0
  104. claude_mpm/commander/parsing/__init__.py +20 -0
  105. claude_mpm/commander/parsing/extractor.py +132 -0
  106. claude_mpm/commander/parsing/output_parser.py +270 -0
  107. claude_mpm/commander/parsing/patterns.py +100 -0
  108. claude_mpm/commander/persistence/__init__.py +11 -0
  109. claude_mpm/commander/persistence/event_store.py +274 -0
  110. claude_mpm/commander/persistence/state_store.py +309 -0
  111. claude_mpm/commander/persistence/work_store.py +164 -0
  112. claude_mpm/commander/polling/__init__.py +13 -0
  113. claude_mpm/commander/polling/event_detector.py +104 -0
  114. claude_mpm/commander/polling/output_buffer.py +49 -0
  115. claude_mpm/commander/polling/output_poller.py +153 -0
  116. claude_mpm/commander/project_session.py +268 -0
  117. claude_mpm/commander/proxy/__init__.py +12 -0
  118. claude_mpm/commander/proxy/formatter.py +89 -0
  119. claude_mpm/commander/proxy/output_handler.py +191 -0
  120. claude_mpm/commander/proxy/relay.py +155 -0
  121. claude_mpm/commander/registry.py +410 -0
  122. claude_mpm/commander/runtime/__init__.py +10 -0
  123. claude_mpm/commander/runtime/executor.py +191 -0
  124. claude_mpm/commander/runtime/monitor.py +346 -0
  125. claude_mpm/commander/session/__init__.py +6 -0
  126. claude_mpm/commander/session/context.py +81 -0
  127. claude_mpm/commander/session/manager.py +59 -0
  128. claude_mpm/commander/tmux_orchestrator.py +361 -0
  129. claude_mpm/commander/web/__init__.py +1 -0
  130. claude_mpm/commander/work/__init__.py +30 -0
  131. claude_mpm/commander/work/executor.py +207 -0
  132. claude_mpm/commander/work/queue.py +405 -0
  133. claude_mpm/commander/workflow/__init__.py +27 -0
  134. claude_mpm/commander/workflow/event_handler.py +241 -0
  135. claude_mpm/commander/workflow/notifier.py +146 -0
  136. claude_mpm/commands/mpm-config.md +20 -249
  137. claude_mpm/commands/mpm-doctor.md +16 -21
  138. claude_mpm/commands/mpm-help.md +12 -205
  139. claude_mpm/commands/mpm-init.md +88 -506
  140. claude_mpm/commands/mpm-monitor.md +22 -401
  141. claude_mpm/commands/mpm-organize.md +70 -442
  142. claude_mpm/commands/mpm-postmortem.md +13 -107
  143. claude_mpm/commands/mpm-session-resume.md +20 -363
  144. claude_mpm/commands/mpm-status.md +13 -69
  145. claude_mpm/commands/mpm-ticket-view.md +60 -495
  146. claude_mpm/commands/mpm-version.md +13 -107
  147. claude_mpm/commands/mpm.md +8 -0
  148. claude_mpm/config/agent_presets.py +8 -7
  149. claude_mpm/config/skill_sources.py +16 -0
  150. claude_mpm/constants.py +1 -0
  151. claude_mpm/core/claude_runner.py +154 -2
  152. claude_mpm/core/config.py +37 -26
  153. claude_mpm/core/config_constants.py +74 -9
  154. claude_mpm/core/constants.py +56 -12
  155. claude_mpm/core/framework/loaders/agent_loader.py +1 -1
  156. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  157. claude_mpm/core/hook_manager.py +51 -3
  158. claude_mpm/core/interactive_session.py +12 -11
  159. claude_mpm/core/logger.py +26 -9
  160. claude_mpm/core/logging_utils.py +39 -13
  161. claude_mpm/core/network_config.py +148 -0
  162. claude_mpm/core/oneshot_session.py +7 -6
  163. claude_mpm/core/optimized_startup.py +61 -0
  164. claude_mpm/core/output_style_manager.py +66 -18
  165. claude_mpm/core/shared/config_loader.py +3 -1
  166. claude_mpm/core/socketio_pool.py +47 -15
  167. claude_mpm/core/unified_agent_registry.py +1 -1
  168. claude_mpm/core/unified_config.py +54 -8
  169. claude_mpm/core/unified_paths.py +95 -90
  170. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/1WZnGYqX.js +24 -0
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/67pF3qNn.js +1 -0
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/6RxdMKe4.js +1 -0
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/8cZrfX0h.js +60 -0
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/9a6T2nm-.js +7 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B443AUzu.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B8AwtY2H.js +1 -0
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BF15LAsF.js +1 -0
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BRcwIQNr.js +4 -0
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BSNlmTZj.js +1 -0
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BV6nKitt.js +43 -0
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BViJ8lZt.js +128 -0
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BcQ-Q0FE.js +1 -0
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bpyvgze_.js +30 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C3rbW_a-.js +1 -0
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C8WYN38h.js +1 -0
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C9I8FlXH.js +61 -0
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIQcWgO2.js +36 -0
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIctN7YN.js +7 -0
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CKrS_JZW.js +145 -0
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CR6P9C4A.js +89 -0
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRRR9MD_.js +2 -0
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CSXtMOf0.js +1 -0
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CT-sbxSk.js +1 -0
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWm6DJsp.js +1 -0
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CpqQ1Kzn.js +1 -0
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D2nGpDRe.js +1 -0
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9iCMida.js +267 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9ykgMoY.js +10 -0
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DL2Ldur1.js +1 -0
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DPfltzjH.js +165 -0
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DR8nis88.js +2 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUliQN2b.js +1 -0
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DXlhR01x.js +122 -0
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D_lyTybS.js +1 -0
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DngoTTgh.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DqkmHtDC.js +220 -0
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DsDh8EYs.js +1 -0
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DypDmXgd.js +139 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
  220. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/IPYC-LnN.js +162 -0
  221. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  222. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JpevfAFt.js +68 -0
  223. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/NqQ1dWOy.js +1 -0
  224. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/R8CEIRAd.js +2 -0
  225. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Zxy7qc-l.js +64 -0
  226. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  227. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/qtd3IeO4.js +15 -0
  228. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ulBFON_C.js +65 -0
  229. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/wQVh1CoA.js +10 -0
  230. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.Dr7t0z2J.js +2 -0
  231. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  232. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.RgBboRvH.js +1 -0
  233. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DG-KkbDf.js +1 -0
  234. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  235. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  236. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  237. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  238. claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
  239. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
  240. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
  241. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
  242. claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
  243. claude_mpm/experimental/cli_enhancements.py +2 -1
  244. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  245. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  246. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  247. claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
  248. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  249. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  250. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  251. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  252. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  253. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  254. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  255. claude_mpm/hooks/claude_hooks/event_handlers.py +527 -136
  256. claude_mpm/hooks/claude_hooks/hook_handler.py +313 -99
  257. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  258. claude_mpm/hooks/claude_hooks/installer.py +206 -36
  259. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  260. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  261. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  262. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  263. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  264. claude_mpm/hooks/claude_hooks/services/__pycache__/container.cpython-311.pyc +0 -0
  265. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  266. claude_mpm/hooks/claude_hooks/services/__pycache__/protocols.cpython-311.pyc +0 -0
  267. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  268. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  269. claude_mpm/hooks/claude_hooks/services/connection_manager.py +67 -32
  270. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  271. claude_mpm/hooks/claude_hooks/services/container.py +310 -0
  272. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  273. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  274. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  275. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  276. claude_mpm/hooks/session_resume_hook.py +89 -1
  277. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  278. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  279. claude_mpm/init.py +276 -0
  280. claude_mpm/models/git_repository.py +3 -3
  281. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  282. claude_mpm/services/agents/agent_builder.py +3 -3
  283. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  284. claude_mpm/services/agents/agent_selection_service.py +2 -2
  285. claude_mpm/services/agents/cache_git_manager.py +7 -7
  286. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  287. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -2
  288. claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
  289. claude_mpm/services/agents/deployment/agent_template_builder.py +39 -19
  290. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  291. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  292. claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
  293. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  294. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +169 -26
  295. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +101 -75
  296. claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
  297. claude_mpm/services/agents/git_source_manager.py +23 -4
  298. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  299. claude_mpm/services/agents/recommender.py +5 -3
  300. claude_mpm/services/agents/single_tier_deployment_service.py +6 -6
  301. claude_mpm/services/agents/sources/git_source_sync_service.py +121 -10
  302. claude_mpm/services/agents/startup_sync.py +27 -4
  303. claude_mpm/services/cli/__init__.py +3 -0
  304. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  305. claude_mpm/services/cli/session_resume_helper.py +10 -2
  306. claude_mpm/services/command_deployment_service.py +44 -26
  307. claude_mpm/services/delegation_detector.py +175 -0
  308. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  309. claude_mpm/services/diagnostics/checks/agent_sources_check.py +31 -1
  310. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  311. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  312. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  313. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  314. claude_mpm/services/diagnostics/models.py +14 -1
  315. claude_mpm/services/event_log.py +325 -0
  316. claude_mpm/services/git/git_operations_service.py +8 -8
  317. claude_mpm/services/hook_installer_service.py +77 -8
  318. claude_mpm/services/infrastructure/__init__.py +4 -0
  319. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  320. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  321. claude_mpm/services/monitor/daemon_manager.py +15 -4
  322. claude_mpm/services/monitor/management/lifecycle.py +15 -3
  323. claude_mpm/services/monitor/server.py +571 -11
  324. claude_mpm/services/pm_skills_deployer.py +884 -0
  325. claude_mpm/services/profile_manager.py +337 -0
  326. claude_mpm/services/skills/git_skill_source_manager.py +281 -20
  327. claude_mpm/services/skills/selective_skill_deployer.py +211 -46
  328. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  329. claude_mpm/services/skills_deployer.py +192 -70
  330. claude_mpm/services/socketio/dashboard_server.py +1 -0
  331. claude_mpm/services/socketio/event_normalizer.py +37 -6
  332. claude_mpm/services/socketio/handlers/hook.py +14 -7
  333. claude_mpm/services/socketio/server/core.py +262 -123
  334. claude_mpm/services/socketio/server/main.py +12 -4
  335. claude_mpm/skills/__init__.py +2 -1
  336. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  337. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  338. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  339. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  340. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  341. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  342. claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
  343. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  344. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  345. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  346. claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
  347. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  348. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  349. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  350. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  351. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  352. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  353. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  354. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  355. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  356. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  357. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  358. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  359. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  360. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  361. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  362. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  363. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  364. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  365. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  366. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  367. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  368. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  369. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  370. claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
  371. claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
  372. claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
  373. claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
  374. claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
  375. claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
  376. claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
  377. claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
  378. claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
  379. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  380. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  381. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  382. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  383. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  384. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  385. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  386. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  387. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  388. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  389. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  390. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  391. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  392. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  393. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  394. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  395. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  396. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  397. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  398. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  399. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  400. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  401. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  402. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  403. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  404. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  405. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  406. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  407. claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
  408. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  409. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  410. claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
  411. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  412. claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
  413. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  414. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  415. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  416. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  417. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  418. claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
  419. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  420. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  421. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  422. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  423. claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
  424. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  425. claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
  426. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  427. claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
  428. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  429. claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
  430. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  431. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  432. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  433. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  434. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  435. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  436. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  437. claude_mpm/skills/bundled/security-scanning.md +112 -0
  438. claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
  439. claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
  440. claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
  441. claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
  442. claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
  443. claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
  444. claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
  445. claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
  446. claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
  447. claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
  448. claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
  449. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  450. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  451. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  452. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  453. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  454. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  455. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  456. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  457. claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
  458. claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
  459. claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
  460. claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
  461. claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
  462. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  463. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  464. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  465. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  466. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  467. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  468. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  469. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  470. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  471. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  472. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  473. claude_mpm/skills/registry.py +295 -90
  474. claude_mpm/skills/skill_manager.py +98 -3
  475. claude_mpm/templates/.pre-commit-config.yaml +112 -0
  476. claude_mpm/utils/agent_dependency_loader.py +115 -4
  477. claude_mpm/utils/agent_filters.py +1 -1
  478. claude_mpm/utils/migration.py +4 -4
  479. claude_mpm/utils/robust_installer.py +86 -21
  480. claude_mpm-5.6.34.dist-info/METADATA +393 -0
  481. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/RECORD +486 -145
  482. claude_mpm-5.4.22.dist-info/METADATA +0 -996
  483. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/WHEEL +0 -0
  484. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/entry_points.txt +0 -0
  485. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE +0 -0
  486. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  487. {claude_mpm-5.4.22.dist-info → claude_mpm-5.6.34.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py CHANGED
@@ -10,7 +10,6 @@ Part of cli/__init__.py refactoring to reduce file size and improve modularity.
10
10
 
11
11
  import os
12
12
  import sys
13
- import warnings
14
13
  from pathlib import Path
15
14
 
16
15
 
@@ -35,13 +34,13 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
35
34
  installer = HookInstaller()
36
35
 
37
36
  # Show brief status (hooks sync is fast)
38
- if not quiet:
37
+ if not quiet and sys.stdout.isatty():
39
38
  print("Syncing Claude Code hooks...", end=" ", flush=True)
40
39
 
41
40
  # Reinstall hooks (force=True ensures update)
42
41
  success = installer.install_hooks(force=True)
43
42
 
44
- if not quiet:
43
+ if not quiet and sys.stdout.isatty():
45
44
  if success:
46
45
  print("✓")
47
46
  else:
@@ -50,7 +49,7 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
50
49
  return success
51
50
 
52
51
  except Exception as e:
53
- if not quiet:
52
+ if not quiet and sys.stdout.isatty():
54
53
  print("(error)")
55
54
  # Log but don't fail startup
56
55
  from ..core.logger import get_logger
@@ -60,43 +59,79 @@ def sync_hooks_on_startup(quiet: bool = False) -> bool:
60
59
  return False
61
60
 
62
61
 
63
- def check_legacy_cache() -> None:
64
- """Check for legacy cache/agents/ directory and warn user.
62
+ def cleanup_legacy_agent_cache() -> None:
63
+ """Remove legacy hierarchical agent cache directories.
64
+
65
+ WHY: Old agent cache used category-based directory structure directly in cache.
66
+ New structure uses remote source paths. This cleanup prevents confusion from
67
+ stale cache directories.
68
+
69
+ Old structure (removed):
70
+ ~/.claude-mpm/cache/agents/engineer/
71
+ ~/.claude-mpm/cache/agents/ops/
72
+ ~/.claude-mpm/cache/agents/qa/
73
+ ...
65
74
 
66
- WHY: cache/agents/ is deprecated in favor of cache/remote-agents/.
67
- Research confirmed that cache/remote-agents/ is the canonical location
68
- with 26 active code references, while cache/agents/ has only 7 legacy references.
75
+ New structure (kept):
76
+ ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents/...
69
77
 
70
- DESIGN DECISIONS:
71
- - Non-blocking warning: Doesn't stop execution, just informs user
72
- - Migration guidance: Provides clear path to migrate
73
- - One-time check: Only warns if legacy cache contains files
78
+ DESIGN DECISION: Runs early in startup before agent deployment to ensure
79
+ clean cache state. Removes only known legacy directories to avoid deleting
80
+ user data.
74
81
  """
75
- home = Path.home()
76
- legacy_cache = home / ".claude-mpm" / "cache" / "agents"
77
- canonical_cache = home / ".claude-mpm" / "cache" / "remote-agents"
78
- migration_marker = home / ".claude-mpm" / "cache" / ".migrated_to_remote_agents"
82
+ import shutil
83
+ from pathlib import Path
79
84
 
80
- # Skip if already migrated or no legacy cache
81
- if migration_marker.exists() or not legacy_cache.exists():
82
- return
85
+ from ..core.logger import get_logger
86
+
87
+ logger = get_logger("startup")
83
88
 
84
- # Check if legacy cache has actual agent files
85
- legacy_files = list(legacy_cache.glob("*.md")) + list(legacy_cache.glob("*.json"))
86
- if not legacy_files:
89
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
90
+ if not cache_dir.exists():
87
91
  return
88
92
 
89
- # Only warn if canonical cache doesn't exist (indicating unmigrated system)
90
- if not canonical_cache.exists():
91
- warnings.warn(
92
- f"\n⚠️ DEPRECATION: Legacy cache directory detected\n"
93
- f" Location: {legacy_cache}\n"
94
- f" Files found: {len(legacy_files)}\n\n"
95
- f"The 'cache/agents/' directory is deprecated. Please migrate to 'cache/remote-agents/'.\n"
96
- f"Run: python scripts/migrate_cache_to_remote_agents.py\n",
97
- DeprecationWarning,
98
- stacklevel=2,
99
- )
93
+ # Known legacy category directories (from old hierarchical structure)
94
+ legacy_dirs = [
95
+ "claude-mpm",
96
+ "documentation",
97
+ "engineer",
98
+ "ops",
99
+ "qa",
100
+ "security",
101
+ "universal",
102
+ ]
103
+
104
+ removed = []
105
+
106
+ # Remove legacy category directories
107
+ for dir_name in legacy_dirs:
108
+ legacy_path = cache_dir / dir_name
109
+ if legacy_path.exists() and legacy_path.is_dir():
110
+ try:
111
+ shutil.rmtree(legacy_path)
112
+ removed.append(dir_name)
113
+ except Exception as e:
114
+ logger.debug(f"Failed to remove legacy directory {dir_name}: {e}")
115
+
116
+ # Also remove stray BASE-AGENT.md in cache root
117
+ base_agent = cache_dir / "BASE-AGENT.md"
118
+ if base_agent.exists():
119
+ try:
120
+ base_agent.unlink()
121
+ removed.append("BASE-AGENT.md")
122
+ except Exception as e:
123
+ logger.debug(f"Failed to remove BASE-AGENT.md: {e}")
124
+
125
+ if removed:
126
+ logger.info(f"Cleaned up legacy agent cache: {', '.join(removed)}")
127
+
128
+
129
+ def check_legacy_cache() -> None:
130
+ """Deprecated: Legacy cache checking is no longer needed.
131
+
132
+ This function is kept for backward compatibility but does nothing.
133
+ All agent cache operations now use the standardized cache/agents/ directory.
134
+ """
100
135
 
101
136
 
102
137
  def setup_early_environment(argv):
@@ -126,7 +161,25 @@ def setup_early_environment(argv):
126
161
  # CRITICAL: Suppress ALL logging by default
127
162
  # This catches all loggers (claude_mpm.*, service.*, framework_loader, etc.)
128
163
  # This will be overridden by setup_mcp_server_logging() based on user preference
129
- logging.getLogger().setLevel(logging.CRITICAL + 1) # Root logger catches everything
164
+ root_logger = logging.getLogger()
165
+ root_logger.setLevel(logging.CRITICAL + 1) # Root logger catches everything
166
+ root_logger.handlers = [] # Remove any handlers
167
+
168
+ # Also suppress common module loggers explicitly to prevent handler leakage
169
+ for logger_name in [
170
+ "claude_mpm",
171
+ "path_resolver",
172
+ "file_loader",
173
+ "framework_loader",
174
+ "service",
175
+ "instruction_loader",
176
+ "agent_loader",
177
+ "startup",
178
+ ]:
179
+ module_logger = logging.getLogger(logger_name)
180
+ module_logger.setLevel(logging.CRITICAL + 1)
181
+ module_logger.handlers = []
182
+ module_logger.propagate = False
130
183
 
131
184
  # Process argv
132
185
  if argv is None:
@@ -156,7 +209,17 @@ def should_skip_background_services(args, processed_argv):
156
209
  skip_commands = ["--version", "-v", "--help", "-h"]
157
210
  return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
158
211
  hasattr(args, "command")
159
- and args.command in ["info", "doctor", "config", "mcp", "configure"]
212
+ and args.command
213
+ in [
214
+ "info",
215
+ "doctor",
216
+ "config",
217
+ "mcp",
218
+ "configure",
219
+ "hook-errors",
220
+ "autotodos",
221
+ "commander",
222
+ ]
160
223
  )
161
224
 
162
225
 
@@ -199,7 +262,7 @@ def deploy_bundled_skills():
199
262
  if not skills_config.get("auto_deploy", True):
200
263
  # Auto-deploy disabled, skip silently
201
264
  return
202
- except Exception:
265
+ except Exception: # nosec B110
203
266
  # If config loading fails, assume auto-deploy is enabled (default)
204
267
  pass
205
268
 
@@ -217,11 +280,13 @@ def deploy_bundled_skills():
217
280
  if deployment_result.get("deployed"):
218
281
  # Show simple feedback for deployed skills
219
282
  deployed_count = len(deployment_result["deployed"])
220
- print(f"✓ Bundled skills ready ({deployed_count} deployed)", flush=True)
283
+ if sys.stdout.isatty():
284
+ print(f"✓ Bundled skills ready ({deployed_count} deployed)", flush=True)
221
285
  logger.info(f"Skills: Deployed {deployed_count} skill(s)")
222
286
  elif not deployment_result.get("errors"):
223
287
  # No deployment needed, skills already present
224
- print("✓ Bundled skills ready", flush=True)
288
+ if sys.stdout.isatty():
289
+ print("✓ Bundled skills ready", flush=True)
225
290
 
226
291
  if deployment_result.get("errors"):
227
292
  logger.warning(
@@ -255,7 +320,8 @@ def discover_and_link_runtime_skills():
255
320
 
256
321
  discover_skills()
257
322
  # Show simple success feedback
258
- print("✓ Runtime skills linked", flush=True)
323
+ if sys.stdout.isatty():
324
+ print("✓ Runtime skills linked", flush=True)
259
325
  except Exception as e:
260
326
  # Import logger here to avoid circular imports
261
327
  from ..core.logger import get_logger
@@ -273,63 +339,62 @@ def deploy_output_style_on_startup():
273
339
  communication without emojis and exclamation points. Styles are project-specific
274
340
  to allow different projects to have different communication styles.
275
341
 
276
- DESIGN DECISION: This is non-blocking and idempotent. Deploys to project-level
277
- directory (.claude/settings/output-styles/) instead of user-level to maintain
278
- project isolation.
342
+ DESIGN DECISION: This is non-blocking and idempotent. Deploys to user-level
343
+ directory (~/.claude/output-styles/) which is the official Claude Code location
344
+ for custom output styles.
279
345
 
280
- Deploys two styles:
281
- - claude-mpm-style.md (professional mode)
346
+ Deploys all styles:
347
+ - claude-mpm.md (professional mode)
282
348
  - claude-mpm-teacher.md (teaching mode)
349
+ - claude-mpm-research.md (research mode - for codebase analysis)
283
350
  """
284
351
  try:
285
- import shutil
286
- from pathlib import Path
352
+ from ..core.output_style_manager import OutputStyleManager
287
353
 
288
- # Source files (in framework package)
289
- package_dir = Path(__file__).parent.parent / "agents"
290
- professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
291
- teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
354
+ # Initialize the output style manager
355
+ manager = OutputStyleManager()
292
356
 
293
- # Target directory (PROJECT-LEVEL, not user-level)
294
- project_dir = Path.cwd()
295
- output_styles_dir = project_dir / ".claude" / "settings" / "output-styles"
296
- professional_target = output_styles_dir / "claude-mpm-style.md"
297
- teacher_target = output_styles_dir / "claude-mpm-teacher.md"
298
-
299
- # Create directory if it doesn't exist
300
- output_styles_dir.mkdir(parents=True, exist_ok=True)
301
-
302
- # Check if already deployed (both files exist and have content)
303
- already_deployed = (
304
- professional_target.exists()
305
- and teacher_target.exists()
306
- and professional_target.stat().st_size > 0
307
- and teacher_target.stat().st_size > 0
308
- )
357
+ # Check if Claude Code version supports output styles (>= 1.0.83)
358
+ if not manager.supports_output_styles():
359
+ # Skip deployment for older versions
360
+ # The manager will fall back to injecting content directly
361
+ return
309
362
 
310
- if already_deployed:
363
+ # Check if all styles are already deployed and up-to-date
364
+ all_up_to_date = True
365
+ for style_config in manager.styles.values():
366
+ source_path = style_config["source"]
367
+ target_path = style_config["target"]
368
+
369
+ if not (
370
+ target_path.exists()
371
+ and source_path.exists()
372
+ and target_path.stat().st_size == source_path.stat().st_size
373
+ ):
374
+ all_up_to_date = False
375
+ break
376
+
377
+ if all_up_to_date:
311
378
  # Show feedback that output styles are ready
312
- print("✓ Output styles ready", flush=True)
379
+ if sys.stdout.isatty():
380
+ print("✓ Output styles ready", flush=True)
313
381
  return
314
382
 
315
- # Deploy both styles
316
- deployed_count = 0
317
- if professional_source.exists():
318
- shutil.copy2(professional_source, professional_target)
319
- deployed_count += 1
383
+ # Deploy all styles using the manager
384
+ results = manager.deploy_all_styles(activate_default=True)
320
385
 
321
- if teacher_source.exists():
322
- shutil.copy2(teacher_source, teacher_target)
323
- deployed_count += 1
386
+ # Count successful deployments
387
+ deployed_count = sum(1 for success in results.values() if success)
324
388
 
325
389
  if deployed_count > 0:
326
- print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
390
+ if sys.stdout.isatty():
391
+ print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
327
392
  else:
328
- # Source files missing - log but don't fail
393
+ # Deployment failed - log but don't fail startup
329
394
  from ..core.logger import get_logger
330
395
 
331
396
  logger = get_logger("cli")
332
- logger.debug("Output style source files not found")
397
+ logger.debug("Failed to deploy any output styles")
333
398
 
334
399
  except Exception as e:
335
400
  # Non-critical - log but don't fail startup
@@ -418,7 +483,95 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
418
483
  return removed_count
419
484
 
420
485
 
421
- def sync_remote_agents_on_startup():
486
+ def _save_deployment_state_after_reconciliation(
487
+ agent_result, project_path: Path
488
+ ) -> None:
489
+ """Save deployment state after reconciliation to prevent duplicate deployment.
490
+
491
+ WHY: After perform_startup_reconciliation() deploys agents to .claude/agents/,
492
+ we need to save a deployment state file so that ClaudeRunner.setup_agents()
493
+ can detect agents are already deployed and skip redundant deployment.
494
+
495
+ This prevents the "✓ Deployed 31 native agents" duplicate deployment that
496
+ occurs when setup_agents() doesn't know reconciliation already ran.
497
+
498
+ Args:
499
+ agent_result: DeploymentResult from perform_startup_reconciliation()
500
+ project_path: Project root directory
501
+
502
+ DESIGN DECISION: Use same state file format as ClaudeRunner._save_deployment_state()
503
+ Located at: .claude-mpm/cache/deployment_state.json
504
+
505
+ State file format:
506
+ {
507
+ "version": "5.6.13",
508
+ "agent_count": 15,
509
+ "deployment_hash": "sha256:...",
510
+ "deployed_at": 1234567890.123
511
+ }
512
+ """
513
+ import hashlib
514
+ import json
515
+ import time
516
+
517
+ from ..core.logger import get_logger
518
+
519
+ logger = get_logger("cli")
520
+
521
+ try:
522
+ # Get version from package
523
+ from claude_mpm import __version__
524
+
525
+ # Path to state file (matches ClaudeRunner._get_deployment_state_path())
526
+ state_file = project_path / ".claude-mpm" / "cache" / "deployment_state.json"
527
+ agents_dir = project_path / ".claude" / "agents"
528
+
529
+ # Count deployed agents
530
+ if agents_dir.exists():
531
+ agent_count = len(list(agents_dir.glob("*.md")))
532
+ else:
533
+ agent_count = 0
534
+
535
+ # Calculate deployment hash (matches ClaudeRunner._calculate_deployment_hash())
536
+ # CRITICAL: Must match exact hash algorithm used in ClaudeRunner
537
+ # Hashes filename + file content (not mtime) for consistency
538
+ deployment_hash = ""
539
+ if agents_dir.exists():
540
+ agent_files = sorted(agents_dir.glob("*.md"))
541
+ hash_obj = hashlib.sha256()
542
+ for agent_file in agent_files:
543
+ # Include filename and content in hash (matches ClaudeRunner)
544
+ hash_obj.update(agent_file.name.encode())
545
+ try:
546
+ hash_obj.update(agent_file.read_bytes())
547
+ except Exception as e:
548
+ logger.debug(f"Error reading {agent_file} for hash: {e}")
549
+
550
+ deployment_hash = hash_obj.hexdigest()
551
+
552
+ # Create state data
553
+ state_data = {
554
+ "version": __version__,
555
+ "agent_count": agent_count,
556
+ "deployment_hash": deployment_hash,
557
+ "deployed_at": time.time(),
558
+ }
559
+
560
+ # Ensure directory exists
561
+ state_file.parent.mkdir(parents=True, exist_ok=True)
562
+
563
+ # Write state file
564
+ state_file.write_text(json.dumps(state_data, indent=2))
565
+ logger.debug(
566
+ f"Saved deployment state after reconciliation: {agent_count} agents"
567
+ )
568
+
569
+ except Exception as e:
570
+ # Non-critical error - log but don't fail startup
571
+ logger.debug(f"Failed to save deployment state: {e}")
572
+
573
+
574
+ def sync_remote_agents_on_startup(force_sync: bool = False):
422
575
  """
423
576
  Synchronize agent templates from remote sources on startup.
424
577
 
@@ -434,18 +587,46 @@ def sync_remote_agents_on_startup():
434
587
  1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
435
588
  2. Deploy agents to ~/.claude/agents/ - Phase 2 progress bar
436
589
  3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
437
- 4. Log deployment results
590
+ 4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
591
+ 5. Log deployment results
592
+
593
+ Args:
594
+ force_sync: Force download even if cache is fresh (bypasses ETag).
438
595
  """
439
- # Check for legacy cache and warn user if found
596
+ # DEPRECATED: Legacy warning - no-op function, kept for compatibility
440
597
  check_legacy_cache()
441
598
 
442
599
  try:
443
- from ..services.agents.deployment.agent_deployment import AgentDeploymentService
600
+ # Load active profile if configured
601
+ # Get project root (where .claude-mpm exists)
602
+ from pathlib import Path
603
+
604
+ from ..core.shared.config_loader import ConfigLoader
444
605
  from ..services.agents.startup_sync import sync_agents_on_startup
606
+ from ..services.profile_manager import ProfileManager
445
607
  from ..utils.progress import ProgressBar
446
608
 
609
+ project_root = Path.cwd()
610
+
611
+ profile_manager = ProfileManager(project_dir=project_root)
612
+ config_loader = ConfigLoader()
613
+ main_config = config_loader.load_main_config()
614
+ active_profile = main_config.get("active_profile")
615
+
616
+ if active_profile:
617
+ success = profile_manager.load_profile(active_profile)
618
+ if success:
619
+ summary = profile_manager.get_filtering_summary()
620
+ from ..core.logger import get_logger
621
+
622
+ logger = get_logger("cli")
623
+ logger.info(
624
+ f"Profile '{active_profile}' active: "
625
+ f"{summary['enabled_agents_count']} agents enabled"
626
+ )
627
+
447
628
  # Phase 1: Sync files from Git sources
448
- result = sync_agents_on_startup()
629
+ result = sync_agents_on_startup(force_refresh=force_sync)
449
630
 
450
631
  # Only proceed with deployment if sync was enabled and ran
451
632
  if result.get("enabled") and result.get("sources_synced", 0) > 0:
@@ -468,185 +649,95 @@ def sync_remote_agents_on_startup():
468
649
  logger.warning(f"Agent sync completed with {len(errors)} errors")
469
650
 
470
651
  # Phase 2: Deploy agents from cache to ~/.claude/agents/
471
- # This mirrors the skills deployment pattern (lines 371-407)
652
+ # Use reconciliation service to respect configuration.yaml settings
472
653
  try:
473
- # Initialize deployment service
474
- deployment_service = AgentDeploymentService()
475
-
476
- # Count agents in cache to show accurate progress
477
654
  from pathlib import Path
478
655
 
479
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
480
- agent_count = 0
481
-
482
- if cache_dir.exists():
483
- # Count MD files in cache (agent markdown files from Git)
484
- # BUGFIX: Only count files in agent directories, not docs/templates/READMEs
485
- # Valid agent paths must contain "/agents/" or be in root-level category dirs
486
- # Exclude PM templates, BASE-AGENT, and documentation files
487
- pm_templates = {
488
- "base-agent.md",
489
- "circuit_breakers.md",
490
- "pm_examples.md",
491
- "pm_red_flags.md",
492
- "research_gate_examples.md",
493
- "response_format.md",
494
- "ticket_completeness_examples.md",
495
- "validation_templates.md",
496
- "git_file_tracking.md",
497
- }
498
- # Documentation files to exclude (by filename)
499
- doc_files = {
500
- "readme.md",
501
- "changelog.md",
502
- "contributing.md",
503
- "implementation-summary.md",
504
- "reorganization-plan.md",
505
- "auto-deploy-index.md",
506
- }
507
-
508
- # Find all markdown files
509
- all_md_files = list(cache_dir.rglob("*.md"))
510
-
511
- # Filter to only agent files:
512
- # 1. Must have "/agents/" in path (from git repos)
513
- # 2. Must not be in PM templates or doc files
514
- # 3. Exclude BASE-AGENT.md which is not a deployable agent
515
- # 4. Exclude build artifacts (dist/, build/, .cache/) to prevent double-counting
516
- agent_files = [
517
- f
518
- for f in all_md_files
519
- if (
520
- # Must be in an agent directory (from git repos like bobmatnyc/claude-mpm-agents/agents/)
521
- "/agents/" in str(f)
522
- # Exclude PM templates, doc files, and BASE-AGENT
523
- and f.name.lower() not in pm_templates
524
- and f.name.lower() not in doc_files
525
- and f.name.lower() != "base-agent.md"
526
- # Exclude build artifacts (prevents double-counting source + built files)
527
- and not any(
528
- part in str(f).split("/")
529
- for part in ["dist", "build", ".cache"]
530
- )
531
- )
532
- ]
533
- agent_count = len(agent_files)
534
-
535
- if agent_count > 0:
536
- # Deploy agents to project-level directory where Claude Code expects them
537
- deploy_target = Path.cwd() / ".claude" / "agents"
538
- deployment_result = deployment_service.deploy_agents(
539
- target_dir=deploy_target,
540
- force_rebuild=False, # Only deploy if versions differ
541
- deployment_mode="update", # Version-aware updates
656
+ from ..core.unified_config import UnifiedConfig
657
+ from ..services.agents.deployment.startup_reconciliation import (
658
+ perform_startup_reconciliation,
659
+ )
660
+
661
+ # Load configuration
662
+ unified_config = UnifiedConfig()
663
+
664
+ # Override with profile settings if active
665
+ if active_profile and profile_manager.active_profile:
666
+ # Get enabled agents from profile (returns Set[str])
667
+ profile_enabled_agents = (
668
+ profile_manager.active_profile.get_enabled_agents()
669
+ )
670
+ # Update config with profile's enabled list (convert Set to List)
671
+ unified_config.agents.enabled = list(profile_enabled_agents)
672
+ logger.info(
673
+ f"Profile '{active_profile}': Using {len(profile_enabled_agents)} enabled agents"
542
674
  )
543
675
 
544
- # Get actual counts from deployment result (reflects configured agents)
545
- deployed = len(deployment_result.get("deployed", []))
546
- updated = len(deployment_result.get("updated", []))
547
- skipped = len(deployment_result.get("skipped", []))
548
- total_configured = deployed + updated + skipped
549
-
550
- # FALLBACK: If deployment result doesn't track skipped agents (async path),
551
- # count existing agents in target directory as "already deployed"
552
- # This ensures accurate reporting when agents are already up-to-date
553
- if total_configured == 0 and deploy_target.exists():
554
- existing_agents = list(deploy_target.glob("*.md"))
555
- # Filter out non-agent files (e.g., README.md, INSTRUCTIONS.md)
556
- agent_count_in_target = len(
557
- [
558
- f
559
- for f in existing_agents
560
- if not f.name.startswith(("README", "INSTRUCTIONS"))
561
- ]
562
- )
563
- if agent_count_in_target > 0:
564
- # All agents already deployed - count them as skipped
565
- skipped = agent_count_in_target
566
- total_configured = agent_count_in_target
676
+ # Perform reconciliation to deploy configured agents
677
+ project_path = Path.cwd()
678
+ agent_result, _skill_result = perform_startup_reconciliation(
679
+ project_path=project_path, config=unified_config, silent=False
680
+ )
567
681
 
568
- # Create progress bar with actual configured agent count (not raw file count)
682
+ # Display results with progress bar
683
+ total_operations = (
684
+ len(agent_result.deployed)
685
+ + len(agent_result.removed)
686
+ + len(agent_result.unchanged)
687
+ )
688
+
689
+ if total_operations > 0:
569
690
  deploy_progress = ProgressBar(
570
- total=total_configured if total_configured > 0 else 1,
691
+ total=total_operations,
571
692
  prefix="Deploying agents",
572
693
  show_percentage=True,
573
694
  show_counter=True,
574
695
  )
575
-
576
- # Update progress bar to completion
577
- deploy_progress.update(
578
- total_configured if total_configured > 0 else 1
696
+ deploy_progress.update(total_operations)
697
+
698
+ # Build summary message
699
+ deployed = len(agent_result.deployed)
700
+ removed = len(agent_result.removed)
701
+ unchanged = len(agent_result.unchanged)
702
+
703
+ summary_parts = []
704
+ if deployed > 0:
705
+ summary_parts.append(f"{deployed} new")
706
+ if removed > 0:
707
+ summary_parts.append(f"{removed} removed")
708
+ if unchanged > 0:
709
+ summary_parts.append(f"{unchanged} unchanged")
710
+
711
+ summary = f"Complete: {', '.join(summary_parts)}"
712
+ deploy_progress.finish(summary)
713
+
714
+ # Display errors if any
715
+ if agent_result.errors:
716
+ logger.warning(
717
+ f"Agent deployment completed with {len(agent_result.errors)} errors"
579
718
  )
719
+ print("\n⚠️ Agent Deployment Errors:")
720
+ max_errors_to_show = 10
721
+ errors_to_display = agent_result.errors[:max_errors_to_show]
580
722
 
581
- # Cleanup orphaned agents (ours but no longer deployed)
582
- # Get list of deployed agent filenames (what should remain)
583
- deployed_filenames = []
584
- for agent_name in deployment_result.get("deployed", []):
585
- deployed_filenames.append(f"{agent_name}.md")
586
- for agent_name in deployment_result.get("updated", []):
587
- deployed_filenames.append(f"{agent_name}.md")
588
- for agent_name in deployment_result.get("skipped", []):
589
- deployed_filenames.append(f"{agent_name}.md")
590
-
591
- # Run cleanup and get count of removed agents
592
- removed = _cleanup_orphaned_agents(
593
- deploy_target, deployed_filenames
594
- )
723
+ for error in errors_to_display:
724
+ print(f" - {error}")
595
725
 
596
- # Show total configured agents (deployed + updated + already existing)
597
- # Include repo count for context and removed count if any
598
- if deployed > 0 or updated > 0:
599
- if removed > 0:
600
- deploy_progress.finish(
601
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
602
- f"{removed} removed ({total_configured} configured from {agent_count} in repo)"
603
- )
604
- else:
605
- deploy_progress.finish(
606
- f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
607
- f"({total_configured} configured from {agent_count} in repo)"
608
- )
609
- elif removed > 0:
610
- deploy_progress.finish(
611
- f"Complete: {total_configured} agents ready - all unchanged, "
612
- f"{removed} removed ({agent_count} available in repo)"
613
- )
614
- else:
615
- deploy_progress.finish(
616
- f"Complete: {total_configured} agents ready - all unchanged "
617
- f"({agent_count} available in repo)"
618
- )
726
+ if len(agent_result.errors) > max_errors_to_show:
727
+ remaining = len(agent_result.errors) - max_errors_to_show
728
+ print(f" ... and {remaining} more error(s)")
619
729
 
620
- # Display deployment errors to user (not just logs)
621
- deploy_errors = deployment_result.get("errors", [])
622
- if deploy_errors:
623
- # Log for debugging
624
- logger.warning(
625
- f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
626
- )
627
-
628
- # Display errors to user with clear formatting
629
- print("\n⚠️ Agent Deployment Errors:")
630
-
631
- # Show first 10 errors to avoid overwhelming output
632
- max_errors_to_show = 10
633
- errors_to_display = deploy_errors[:max_errors_to_show]
634
-
635
- for error in errors_to_display:
636
- # Format error message for readability
637
- # Errors typically come as strings like "agent.md: Error message"
638
- print(f" - {error}")
639
-
640
- # If more errors exist, show count
641
- if len(deploy_errors) > max_errors_to_show:
642
- remaining = len(deploy_errors) - max_errors_to_show
643
- print(f" ... and {remaining} more error(s)")
730
+ print(
731
+ f"\n❌ Failed to deploy {len(agent_result.errors)} agent(s). "
732
+ "Please check the error messages above."
733
+ )
734
+ print(" Run with --verbose for detailed error information.\n")
644
735
 
645
- # Show summary message
646
- print(
647
- f"\n❌ Failed to deploy {len(deploy_errors)} agent(s). Please check the error messages above."
648
- )
649
- print(" Run with --verbose for detailed error information.\n")
736
+ # Save deployment state to prevent duplicate deployment in ClaudeRunner
737
+ # This ensures setup_agents() skips deployment since we already reconciled
738
+ _save_deployment_state_after_reconciliation(
739
+ agent_result=agent_result, project_path=project_path
740
+ )
650
741
 
651
742
  except Exception as e:
652
743
  # Deployment failure shouldn't block startup
@@ -655,6 +746,11 @@ def sync_remote_agents_on_startup():
655
746
  logger = get_logger("cli")
656
747
  logger.warning(f"Failed to deploy agents from cache: {e}")
657
748
 
749
+ # Phase 4: Cleanup legacy agent cache directories (after sync/deployment)
750
+ # CRITICAL: This must run AFTER sync completes because sync may recreate
751
+ # legacy directories. Running cleanup here ensures they're removed.
752
+ cleanup_legacy_agent_cache()
753
+
658
754
  except Exception as e:
659
755
  # Non-critical - log but don't fail startup
660
756
  from ..core.logger import get_logger
@@ -663,8 +759,14 @@ def sync_remote_agents_on_startup():
663
759
  logger.debug(f"Failed to sync remote agents: {e}")
664
760
  # Continue execution - agent sync failure shouldn't block startup
665
761
 
762
+ # Cleanup legacy cache even if sync failed
763
+ try:
764
+ cleanup_legacy_agent_cache()
765
+ except Exception: # nosec B110
766
+ pass # Ignore cleanup errors
666
767
 
667
- def sync_remote_skills_on_startup():
768
+
769
+ def sync_remote_skills_on_startup(force_sync: bool = False):
668
770
  """
669
771
  Synchronize skill templates from remote sources on startup.
670
772
 
@@ -679,13 +781,19 @@ def sync_remote_skills_on_startup():
679
781
  1. Sync all enabled Git sources (download/cache files) - Phase 1 progress bar
680
782
  2. Scan deployed agents for skill requirements → save to configuration.yaml
681
783
  3. Resolve which skills to deploy (user_defined vs agent_referenced)
682
- 4. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
683
- 5. Log deployment results with source indication
784
+ 4. Apply profile filtering if active
785
+ 5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
786
+ 6. Log deployment results with source indication
787
+
788
+ Args:
789
+ force_sync: Force download even if cache is fresh (bypasses ETag).
684
790
  """
685
791
  try:
686
792
  from pathlib import Path
687
793
 
688
794
  from ..config.skill_sources import SkillSourceConfiguration
795
+ from ..core.shared.config_loader import ConfigLoader
796
+ from ..services.profile_manager import ProfileManager
689
797
  from ..services.skills.git_skill_source_manager import GitSkillSourceManager
690
798
  from ..services.skills.selective_skill_deployer import (
691
799
  get_required_skills_from_agents,
@@ -694,6 +802,28 @@ def sync_remote_skills_on_startup():
694
802
  )
695
803
  from ..utils.progress import ProgressBar
696
804
 
805
+ # Load active profile if configured
806
+ # Get project root (where .claude-mpm exists)
807
+ project_root = Path.cwd()
808
+
809
+ profile_manager = ProfileManager(project_dir=project_root)
810
+ config_loader = ConfigLoader()
811
+ main_config = config_loader.load_main_config()
812
+ active_profile = main_config.get("active_profile")
813
+
814
+ if active_profile:
815
+ success = profile_manager.load_profile(active_profile)
816
+ if success:
817
+ from ..core.logger import get_logger
818
+
819
+ logger = get_logger("cli")
820
+ summary = profile_manager.get_filtering_summary()
821
+ logger.info(
822
+ f"Profile '{active_profile}' active: "
823
+ f"{summary['enabled_skills_count']} skills enabled, "
824
+ f"{summary['disabled_patterns_count']} patterns disabled"
825
+ )
826
+
697
827
  config = SkillSourceConfiguration()
698
828
  manager = GitSkillSourceManager(config)
699
829
 
@@ -762,7 +892,7 @@ def sync_remote_skills_on_startup():
762
892
 
763
893
  # Sync all sources with progress callback
764
894
  results = manager.sync_all_sources(
765
- force=False, progress_callback=sync_progress.update
895
+ force=force_sync, progress_callback=sync_progress.update
766
896
  )
767
897
 
768
898
  # Finish sync progress bar with clear breakdown
@@ -782,108 +912,191 @@ def sync_remote_skills_on_startup():
782
912
 
783
913
  # Phase 2: Scan agents and save to configuration.yaml
784
914
  # This step populates configuration.yaml with agent-referenced skills
785
- if results["synced_count"] > 0:
786
- agents_dir = Path.cwd() / ".claude" / "agents"
915
+ # CRITICAL: Always scan agents to populate agent_referenced, even when using cached skills.
916
+ # Without this, skill_filter=None causes ALL skills to deploy and NO cleanup to run.
917
+ agents_dir = Path.cwd() / ".claude" / "agents"
918
+
919
+ # Scan agents for skill requirements (ALWAYS run to ensure cleanup works)
920
+ agent_skills = get_required_skills_from_agents(agents_dir)
921
+ logger.info(
922
+ f"Agent scan found {len(agent_skills)} unique skills across deployed agents"
923
+ )
787
924
 
788
- # Scan agents for skill requirements
789
- agent_skills = get_required_skills_from_agents(agents_dir)
925
+ # Save to project-level configuration.yaml
926
+ project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
927
+ save_agent_skills_to_config(list(agent_skills), project_config_path)
928
+ logger.debug(
929
+ f"Saved {len(agent_skills)} agent-referenced skills to {project_config_path}"
930
+ )
790
931
 
791
- # Save to project-level configuration.yaml
792
- project_config_path = Path.cwd() / ".claude-mpm" / "configuration.yaml"
793
- save_agent_skills_to_config(list(agent_skills), project_config_path)
932
+ # Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
933
+ skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
794
934
 
795
- # Phase 3: Resolve which skills to deploy (user_defined or agent_referenced)
796
- skills_to_deploy, skill_source = get_skills_to_deploy(project_config_path)
935
+ # CRITICAL DEBUG: Log deployment resolution to diagnose cleanup issues
936
+ if skills_to_deploy:
937
+ logger.info(
938
+ f"Resolved {len(skills_to_deploy)} skills from {skill_source} (cleanup will run)"
939
+ )
940
+ else:
941
+ logger.warning(
942
+ f"No skills resolved from {skill_source} - will deploy ALL skills WITHOUT cleanup! "
943
+ f"This may indicate agent_referenced is empty in configuration.yaml."
944
+ )
797
945
 
798
- # Get all skills to determine counts
799
- all_skills = manager.get_all_skills()
800
- total_skill_count = len(all_skills)
946
+ # Phase 4: Apply profile filtering if active
947
+ if active_profile and profile_manager.active_profile:
948
+ # Filter skills based on profile
949
+ if skills_to_deploy:
950
+ # Filter the resolved skill list
951
+ original_count = len(skills_to_deploy)
952
+ filtered_skills = [
953
+ skill
954
+ for skill in skills_to_deploy
955
+ if profile_manager.is_skill_enabled(skill)
956
+ ]
957
+ filtered_count = original_count - len(filtered_skills)
958
+
959
+ # SAFEGUARD: Warn if all skills were filtered out (misconfiguration)
960
+ if not filtered_skills and original_count > 0:
961
+ logger.warning(
962
+ f"Profile '{active_profile}' filtered ALL {original_count} skills. "
963
+ f"This may indicate a naming mismatch in the profile."
964
+ )
965
+ elif filtered_count > 0:
966
+ logger.info(
967
+ f"Profile '{active_profile}' filtered {filtered_count} skills "
968
+ f"({len(filtered_skills)} remaining)"
969
+ )
801
970
 
802
- # Determine skill count based on resolution
803
- skill_count = (
804
- len(skills_to_deploy) if skills_to_deploy else total_skill_count
971
+ skills_to_deploy = filtered_skills
972
+ skill_source = f"{skill_source} + profile filtered"
973
+ else:
974
+ # No explicit skill list - filter from all available
975
+ all_skills = manager.get_all_skills()
976
+ filtered_skills = [
977
+ skill["name"]
978
+ for skill in all_skills
979
+ if profile_manager.is_skill_enabled(skill["name"])
980
+ ]
981
+ skills_to_deploy = filtered_skills
982
+ skill_source = "profile filtered"
983
+ logger.info(
984
+ f"Profile '{active_profile}': "
985
+ f"{len(filtered_skills)} skills enabled from {len(all_skills)} available"
986
+ )
987
+
988
+ # Get all skills to determine counts
989
+ all_skills = manager.get_all_skills()
990
+ total_skill_count = len(all_skills)
991
+
992
+ # Determine skill count based on resolution
993
+ skill_count = len(skills_to_deploy) if skills_to_deploy else total_skill_count
994
+
995
+ if skill_count > 0:
996
+ # Deploy skills with resolved filter
997
+ # Deploy ONLY to project directory (not user-level)
998
+ # DESIGN DECISION: Project-level deployment keeps skills isolated per project,
999
+ # avoiding pollution of user's global ~/.claude/skills/ directory.
1000
+
1001
+ # Deploy to project-local directory with cleanup
1002
+ deployment_result = manager.deploy_skills(
1003
+ target_dir=Path.cwd() / ".claude" / "skills",
1004
+ force=force_sync,
1005
+ # CRITICAL FIX: Empty list should mean "deploy no skills", not "deploy all"
1006
+ # When skills_to_deploy is [], we want skill_filter=set() NOT skill_filter=None
1007
+ # None means "no filtering" (deploy all), empty set means "filter to nothing"
1008
+ skill_filter=set(skills_to_deploy)
1009
+ if skills_to_deploy is not None
1010
+ else None,
805
1011
  )
806
1012
 
807
- if skill_count > 0:
808
- # Deploy skills with resolved filter
809
- # Deploy to project directory (like agents), not user directory
810
- deployment_result = manager.deploy_skills(
811
- target_dir=Path.cwd() / ".claude" / "skills",
812
- force=False,
813
- skill_filter=set(skills_to_deploy) if skills_to_deploy else None,
1013
+ # REMOVED: User-level deployment (lines 1068-1074)
1014
+ # Reason: Skills should be project-specific, not user-global.
1015
+ # Claude Code can read from project-level .claude/skills/ directory.
1016
+
1017
+ # Get actual counts from deployment result (use project-local for display)
1018
+ deployed = deployment_result.get("deployed_count", 0)
1019
+ skipped = deployment_result.get("skipped_count", 0)
1020
+ filtered = deployment_result.get("filtered_count", 0)
1021
+ removed = deployment_result.get("removed_count", 0)
1022
+ total_available = deployed + skipped
1023
+
1024
+ # Only show progress bar if there are skills to deploy
1025
+ if total_available > 0:
1026
+ deploy_progress = ProgressBar(
1027
+ total=total_available,
1028
+ prefix="Deploying skill directories",
1029
+ show_percentage=True,
1030
+ show_counter=True,
1031
+ )
1032
+ # Update progress bar to completion
1033
+ deploy_progress.update(total_available)
1034
+ else:
1035
+ # No skills to deploy - create dummy progress for message only
1036
+ deploy_progress = ProgressBar(
1037
+ total=1,
1038
+ prefix="Deploying skill directories",
1039
+ show_percentage=False,
1040
+ show_counter=False,
814
1041
  )
1042
+ deploy_progress.update(1)
815
1043
 
816
- # Get actual counts from deployment result
817
- deployed = deployment_result.get("deployed_count", 0)
818
- skipped = deployment_result.get("skipped_count", 0)
819
- filtered = deployment_result.get("filtered_count", 0)
820
- total_available = deployed + skipped
1044
+ # Show total available skills (deployed + already existing)
1045
+ # Include source indication (user_defined vs agent_referenced)
1046
+ # Note: total_skill_count is from cache, total_available is what's deployed/needed
1047
+ source_label = (
1048
+ "user override" if skill_source == "user_defined" else "from agents"
1049
+ )
821
1050
 
822
- # Only show progress bar if there are skills to deploy
823
- if total_available > 0:
824
- deploy_progress = ProgressBar(
825
- total=total_available,
826
- prefix="Deploying skill directories",
827
- show_percentage=True,
828
- show_counter=True,
829
- )
830
- # Update progress bar to completion
831
- deploy_progress.update(total_available)
832
- else:
833
- # No skills to deploy - create dummy progress for message only
834
- deploy_progress = ProgressBar(
835
- total=1,
836
- prefix="Deploying skill directories",
837
- show_percentage=False,
838
- show_counter=False,
839
- )
840
- deploy_progress.update(1)
1051
+ # Build finish message with cleanup info
1052
+ if deployed > 0 or removed > 0:
1053
+ parts = []
1054
+ if deployed > 0:
1055
+ parts.append(f"{deployed} new")
1056
+ if skipped > 0:
1057
+ parts.append(f"{skipped} unchanged")
1058
+ if removed > 0:
1059
+ parts.append(f"{removed} removed")
841
1060
 
842
- # Show total available skills (deployed + already existing)
843
- # Include source indication (user_defined vs agent_referenced)
844
- # Note: total_skill_count is from the repo, total_available is what's deployed/needed
845
- source_label = (
846
- "user override" if skill_source == "user_defined" else "from agents"
847
- )
1061
+ status = ", ".join(parts)
848
1062
 
849
- if deployed > 0:
850
- if filtered > 0:
851
- deploy_progress.finish(
852
- f"Complete: {deployed} new, {skipped} unchanged "
853
- f"({total_available} {source_label}, {filtered} available in repo)"
854
- )
855
- else:
856
- deploy_progress.finish(
857
- f"Complete: {deployed} new, {skipped} unchanged "
858
- f"({total_available} skills {source_label} from {total_skill_count} in repo)"
859
- )
860
- elif filtered > 0:
861
- # Skills filtered means agents require fewer skills than available
1063
+ if filtered > 0:
862
1064
  deploy_progress.finish(
863
- f"No skills needed ({source_label}, {total_skill_count} available in repo)"
1065
+ f"Complete: {status} ({total_available} {source_label}, {filtered} files in cache)"
864
1066
  )
865
1067
  else:
866
1068
  deploy_progress.finish(
867
- f"Complete: {total_available} skills {source_label} "
868
- f"({total_skill_count} available in repo)"
1069
+ f"Complete: {status} ({total_available} skills {source_label} from {total_skill_count} files in cache)"
869
1070
  )
1071
+ elif filtered > 0:
1072
+ # Skills filtered means agents require fewer skills than available
1073
+ deploy_progress.finish(
1074
+ f"No skills needed ({source_label}, {total_skill_count} files in cache)"
1075
+ )
1076
+ else:
1077
+ # No changes - all skills already deployed
1078
+ msg = f"Complete: {total_available} skills {source_label}"
1079
+ if removed > 0:
1080
+ msg += f", {removed} removed"
1081
+ msg += f" ({total_skill_count} files in cache)"
1082
+ deploy_progress.finish(msg)
1083
+
1084
+ # Log deployment errors if any
1085
+ from ..core.logger import get_logger
870
1086
 
871
- # Log deployment errors if any
872
- from ..core.logger import get_logger
873
-
874
- logger = get_logger("cli")
1087
+ logger = get_logger("cli")
875
1088
 
876
- errors = deployment_result.get("errors", [])
877
- if errors:
878
- logger.warning(
879
- f"Skill deployment completed with {len(errors)} errors: {errors}"
880
- )
1089
+ errors = deployment_result.get("errors", [])
1090
+ if errors:
1091
+ logger.warning(
1092
+ f"Skill deployment completed with {len(errors)} errors: {errors}"
1093
+ )
881
1094
 
882
- # Log sync errors if any
883
- if results["failed_count"] > 0:
884
- logger.warning(
885
- f"Skill sync completed with {results['failed_count']} failures"
886
- )
1095
+ # Log sync errors if any
1096
+ if results["failed_count"] > 0:
1097
+ logger.warning(
1098
+ f"Skill sync completed with {results['failed_count']} failures"
1099
+ )
887
1100
 
888
1101
  except Exception as e:
889
1102
  # Non-critical - log but don't fail startup
@@ -921,7 +1134,7 @@ def show_agent_summary():
921
1134
  installed_count = len(agent_files)
922
1135
 
923
1136
  # Count available agents in cache (from remote sources)
924
- cache_dir = Path.home() / ".claude-mpm" / "cache" / "remote-agents"
1137
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
925
1138
  available_count = 0
926
1139
  if cache_dir.exists():
927
1140
  # Use same filtering logic as agent deployment (lines 486-533 in startup.py)
@@ -966,7 +1179,7 @@ def show_agent_summary():
966
1179
  # Display summary if we have agents
967
1180
  if installed_count > 0 or available_count > 0:
968
1181
  print(
969
- f"✓ Agents: {installed_count} installed / {available_count} available",
1182
+ f"✓ Agents: {installed_count} deployed / {max(0, available_count - installed_count)} cached",
970
1183
  flush=True,
971
1184
  )
972
1185
 
@@ -983,61 +1196,64 @@ def show_skill_summary():
983
1196
  Display skill availability summary on startup.
984
1197
 
985
1198
  WHY: Users should see at a glance how many skills are deployed and available
986
- from collections, similar to the agent summary.
1199
+ from cache, similar to the agent summary showing "X deployed / Y cached".
987
1200
 
988
- DESIGN DECISION: Fast, non-blocking check that counts skills from deployment
989
- directory and collection repos. Shows "X installed (Y available)" format.
1201
+ DESIGN DECISION: Fast, non-blocking check that counts skills from:
1202
+ - Deployed skills: PROJECT-level .claude/skills/ directory
1203
+ - Cached skills: ~/.claude-mpm/cache/skills/ directory (from remote sources)
1204
+
1205
+ Shows format: "✓ Skills: X deployed / Y cached"
990
1206
  Failures are silent to avoid blocking startup.
991
1207
  """
992
1208
  try:
993
1209
  from pathlib import Path
994
1210
 
995
- # Count deployed skills (installed)
996
- skills_dir = Path.home() / ".claude" / "skills"
997
- installed_count = 0
998
- if skills_dir.exists():
1211
+ # Count deployed skills (PROJECT-level, not user-level)
1212
+ project_skills_dir = Path.cwd() / ".claude" / "skills"
1213
+ deployed_count = 0
1214
+ if project_skills_dir.exists():
999
1215
  # Count directories with SKILL.md (excludes collection repos)
1000
1216
  # Exclude collection directories (obra-superpowers, etc.)
1001
1217
  skill_dirs = [
1002
1218
  d
1003
- for d in skills_dir.iterdir()
1219
+ for d in project_skills_dir.iterdir()
1004
1220
  if d.is_dir()
1005
1221
  and (d / "SKILL.md").exists()
1006
1222
  and not (d / ".git").exists() # Exclude collection repos
1007
1223
  ]
1008
- installed_count = len(skill_dirs)
1224
+ deployed_count = len(skill_dirs)
1009
1225
 
1010
- # Count available skills in collections
1011
- available_count = 0
1012
- if skills_dir.exists():
1013
- # Scan all collection directories (those with .git)
1014
- for collection_dir in skills_dir.iterdir():
1015
- if (
1016
- not collection_dir.is_dir()
1017
- or not (collection_dir / ".git").exists()
1018
- ):
1226
+ # Count cached skills (from remote sources, not deployed yet)
1227
+ # This matches the agent summary pattern: deployed vs cached
1228
+ cache_dir = Path.home() / ".claude-mpm" / "cache" / "skills"
1229
+ cached_count = 0
1230
+ if cache_dir.exists():
1231
+ # Scan all repository directories in cache
1232
+ # Cache structure: ~/.claude-mpm/cache/skills/{owner}/{repo}/...
1233
+ for repo_dir in cache_dir.rglob("*"):
1234
+ if not repo_dir.is_dir():
1019
1235
  continue
1020
1236
 
1021
- # Count skill directories in this collection
1237
+ # Count skill directories (those with SKILL.md)
1022
1238
  # Skills can be nested in: skills/category/skill-name/SKILL.md
1023
1239
  # or in flat structure: skill-name/SKILL.md
1024
- for root, dirs, files in os.walk(collection_dir):
1240
+ for root, dirs, files in os.walk(repo_dir):
1025
1241
  if "SKILL.md" in files:
1026
- # Exclude build artifacts and hidden directories (within the collection)
1027
- # Get relative path from collection_dir to avoid excluding based on .claude parent
1242
+ # Exclude build artifacts and hidden directories
1028
1243
  root_path = Path(root)
1029
- relative_parts = root_path.relative_to(collection_dir).parts
1030
1244
  if not any(
1031
1245
  part.startswith(".")
1032
1246
  or part in ["dist", "build", "__pycache__"]
1033
- for part in relative_parts
1247
+ for part in root_path.parts
1034
1248
  ):
1035
- available_count += 1
1249
+ cached_count += 1
1036
1250
 
1037
- # Display summary if we have skills
1038
- if installed_count > 0 or available_count > 0:
1251
+ # Display summary using agent summary format: "X deployed / Y cached"
1252
+ # Only show non-deployed cached skills (subtract deployed from cached)
1253
+ non_deployed_cached = max(0, cached_count - deployed_count)
1254
+ if deployed_count > 0 or non_deployed_cached > 0:
1039
1255
  print(
1040
- f"✓ Skills: {installed_count} installed ({available_count} available)",
1256
+ f"✓ Skills: {deployed_count} deployed / {non_deployed_cached} cached",
1041
1257
  flush=True,
1042
1258
  )
1043
1259
 
@@ -1049,6 +1265,86 @@ def show_skill_summary():
1049
1265
  logger.debug(f"Failed to generate skill summary: {e}")
1050
1266
 
1051
1267
 
1268
+ def verify_and_show_pm_skills():
1269
+ """Verify PM skills and display status with enhanced validation.
1270
+
1271
+ WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
1272
+ framework knowledge is unavailable at startup. Enhanced validation
1273
+ checks all required skills exist, are not corrupted, and auto-repairs
1274
+ if needed.
1275
+
1276
+ Shows deployment status:
1277
+ - "✓ PM skills: 8/8 verified" if all required skills are valid
1278
+ - "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
1279
+ - Non-blocking but visible warning if auto-repair fails
1280
+ """
1281
+ try:
1282
+ from pathlib import Path
1283
+
1284
+ from ..services.pm_skills_deployer import (
1285
+ REQUIRED_PM_SKILLS,
1286
+ PMSkillsDeployerService,
1287
+ )
1288
+
1289
+ deployer = PMSkillsDeployerService()
1290
+ project_dir = Path.cwd()
1291
+
1292
+ # Verify with auto-repair enabled
1293
+ result = deployer.verify_pm_skills(project_dir, auto_repair=True)
1294
+
1295
+ if result.verified:
1296
+ # Show verified status with count
1297
+ total_required = len(REQUIRED_PM_SKILLS)
1298
+ if sys.stdout.isatty():
1299
+ print(
1300
+ f"✓ PM skills: {total_required}/{total_required} verified",
1301
+ flush=True,
1302
+ )
1303
+ else:
1304
+ # Show warning with details
1305
+ missing_count = len(result.missing_skills)
1306
+ corrupted_count = len(result.corrupted_skills)
1307
+
1308
+ # Build status message
1309
+ issues = []
1310
+ if missing_count > 0:
1311
+ issues.append(f"{missing_count} missing")
1312
+ if corrupted_count > 0:
1313
+ issues.append(f"{corrupted_count} corrupted")
1314
+
1315
+ status = ", ".join(issues)
1316
+
1317
+ # Check if auto-repair was attempted
1318
+ if "Auto-repaired" in result.message:
1319
+ # Auto-repair succeeded
1320
+ total_required = len(REQUIRED_PM_SKILLS)
1321
+ if sys.stdout.isatty():
1322
+ print(
1323
+ f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
1324
+ flush=True,
1325
+ )
1326
+ else:
1327
+ # Auto-repair failed or not attempted
1328
+ if sys.stdout.isatty():
1329
+ print(f"⚠ PM skills: {status}", flush=True)
1330
+
1331
+ # Log warnings for debugging
1332
+ from ..core.logger import get_logger
1333
+
1334
+ logger = get_logger("cli")
1335
+ for warning in result.warnings:
1336
+ logger.warning(f"PM skills: {warning}")
1337
+
1338
+ except ImportError:
1339
+ # PM skills deployer not available - skip silently
1340
+ pass
1341
+ except Exception as e:
1342
+ from ..core.logger import get_logger
1343
+
1344
+ logger = get_logger("cli")
1345
+ logger.debug(f"PM skills verification failed: {e}")
1346
+
1347
+
1052
1348
  def auto_install_chrome_devtools_on_startup():
1053
1349
  """
1054
1350
  Automatically install chrome-devtools-mcp on startup if enabled.
@@ -1072,7 +1368,7 @@ def auto_install_chrome_devtools_on_startup():
1072
1368
  if not chrome_devtools_config.get("auto_install", True):
1073
1369
  # Auto-install disabled, skip silently
1074
1370
  return
1075
- except Exception:
1371
+ except Exception: # nosec B110
1076
1372
  # If config loading fails, assume auto-install is enabled (default)
1077
1373
  pass
1078
1374
 
@@ -1090,7 +1386,7 @@ def auto_install_chrome_devtools_on_startup():
1090
1386
  # Continue execution - chrome-devtools installation failure shouldn't block startup
1091
1387
 
1092
1388
 
1093
- def run_background_services():
1389
+ def run_background_services(force_sync: bool = False):
1094
1390
  """
1095
1391
  Initialize all background services on startup.
1096
1392
 
@@ -1101,6 +1397,9 @@ def run_background_services():
1101
1397
  explicitly requests them via agent-manager commands. This prevents unwanted
1102
1398
  file creation in project .claude/ directories.
1103
1399
  See: SystemInstructionsDeployer and agent_deployment.py line 504-509
1400
+
1401
+ Args:
1402
+ force_sync: Force download even if cache is fresh (bypasses ETag).
1104
1403
  """
1105
1404
  # Sync hooks early to ensure up-to-date configuration
1106
1405
  # RATIONALE: Hooks should be synced before other services to fix stale configs
@@ -1111,7 +1410,9 @@ def run_background_services():
1111
1410
  check_mcp_auto_configuration()
1112
1411
  verify_mcp_gateway_startup()
1113
1412
  check_for_updates_async()
1114
- sync_remote_agents_on_startup() # Sync agents from remote sources
1413
+ sync_remote_agents_on_startup(
1414
+ force_sync=force_sync
1415
+ ) # Sync agents from remote sources
1115
1416
  show_agent_summary() # Display agent counts after deployment
1116
1417
 
1117
1418
  # Skills deployment order (precedence: remote > bundled)
@@ -1120,9 +1421,12 @@ def run_background_services():
1120
1421
  # 3. Discover and link runtime skills (user-added skills)
1121
1422
  # This ensures remote skills take precedence over bundled skills when names conflict
1122
1423
  deploy_bundled_skills() # Base layer: package-bundled skills
1123
- sync_remote_skills_on_startup() # Override layer: Git-based skills (takes precedence)
1424
+ sync_remote_skills_on_startup(
1425
+ force_sync=force_sync
1426
+ ) # Override layer: Git-based skills (takes precedence)
1124
1427
  discover_and_link_runtime_skills() # Discovery: user-added skills
1125
1428
  show_skill_summary() # Display skill counts after deployment
1429
+ verify_and_show_pm_skills() # PM skills verification and status
1126
1430
 
1127
1431
  deploy_output_style_on_startup()
1128
1432
 
@@ -1221,7 +1525,9 @@ def check_mcp_auto_configuration():
1221
1525
  from ..services.mcp_gateway.auto_configure import check_and_configure_mcp
1222
1526
 
1223
1527
  # Show progress feedback - this operation can take 10+ seconds
1224
- print("Checking MCP configuration...", end="", flush=True)
1528
+ # Only show progress message in TTY mode to avoid interfering with Claude Code's status display
1529
+ if sys.stdout.isatty():
1530
+ print("Checking MCP configuration...", end="", flush=True)
1225
1531
 
1226
1532
  # This function handles all the logic:
1227
1533
  # - Checks if already configured
@@ -1232,11 +1538,17 @@ def check_mcp_auto_configuration():
1232
1538
  check_and_configure_mcp()
1233
1539
 
1234
1540
  # Clear the "Checking..." message by overwriting with spaces
1235
- print("\r" + " " * 30 + "\r", end="", flush=True)
1541
+ # Only use carriage return clearing if stdout is a real TTY
1542
+ if sys.stdout.isatty():
1543
+ print("\r" + " " * 30 + "\r", end="", flush=True)
1544
+ # In non-TTY mode, don't print anything - the "Checking..." message will just remain on its line
1236
1545
 
1237
1546
  except Exception as e:
1238
1547
  # Clear progress message on error
1239
- print("\r" + " " * 30 + "\r", end="", flush=True)
1548
+ # Only use carriage return clearing if stdout is a real TTY
1549
+ if sys.stdout.isatty():
1550
+ print("\r" + " " * 30 + "\r", end="", flush=True)
1551
+ # In non-TTY mode, don't print anything - the "Checking..." message will just remain on its line
1240
1552
 
1241
1553
  # Non-critical - log but don't fail
1242
1554
  from ..core.logger import get_logger
@@ -1256,18 +1568,10 @@ def verify_mcp_gateway_startup():
1256
1568
  DESIGN DECISION: This is non-blocking - failures are logged but don't prevent
1257
1569
  startup to ensure claude-mpm remains functional even if MCP gateway has issues.
1258
1570
  """
1259
- # Quick verification of MCP services installation
1260
- try:
1261
- from ..core.logger import get_logger
1262
- from ..services.mcp_service_verifier import verify_mcp_services_on_startup
1263
-
1264
- logger = get_logger("mcp_verify")
1265
- all_ok, message = verify_mcp_services_on_startup()
1266
- if not all_ok:
1267
- logger.warning(message)
1268
- except Exception:
1269
- # Non-critical - continue with startup
1270
- pass
1571
+ # DISABLED: MCP service verification removed - Claude Code handles MCP natively
1572
+ # The previous check warned about missing MCP services, but users should configure
1573
+ # MCP servers through Claude Code's native MCP management, not through claude-mpm.
1574
+ # See: https://docs.anthropic.com/en/docs/claude-code/mcp
1271
1575
 
1272
1576
  try:
1273
1577
  import asyncio
@@ -1327,7 +1631,7 @@ def verify_mcp_gateway_startup():
1327
1631
  loop.run_until_complete(
1328
1632
  asyncio.gather(*pending, return_exceptions=True)
1329
1633
  )
1330
- except Exception:
1634
+ except Exception: # nosec B110
1331
1635
  pass # Ignore cleanup errors
1332
1636
  finally:
1333
1637
  loop.close()
@@ -1421,7 +1725,7 @@ def check_for_updates_async():
1421
1725
 
1422
1726
  logger = get_logger("upgrade_check")
1423
1727
  logger.debug(f"Update check failed (non-critical): {e}")
1424
- except Exception:
1728
+ except Exception: # nosec B110
1425
1729
  pass # Avoid any errors in error handling
1426
1730
  finally:
1427
1731
  # Properly clean up event loop
@@ -1436,7 +1740,7 @@ def check_for_updates_async():
1436
1740
  loop.run_until_complete(
1437
1741
  asyncio.gather(*pending, return_exceptions=True)
1438
1742
  )
1439
- except Exception:
1743
+ except Exception: # nosec B110
1440
1744
  pass # Ignore cleanup errors
1441
1745
  finally:
1442
1746
  loop.close()