claude-mpm 5.0.9__py3-none-any.whl → 5.4.41__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of claude-mpm might be problematic. Click here for more details.

Files changed (263) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +468 -468
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +70 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +431 -45
  12. claude_mpm/cli/__init__.py +0 -1
  13. claude_mpm/cli/__main__.py +4 -0
  14. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  15. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  16. claude_mpm/cli/commands/agents.py +175 -37
  17. claude_mpm/cli/commands/auto_configure.py +723 -236
  18. claude_mpm/cli/commands/config.py +88 -2
  19. claude_mpm/cli/commands/configure.py +1262 -157
  20. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  21. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  22. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  23. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  24. claude_mpm/cli/commands/postmortem.py +1 -1
  25. claude_mpm/cli/commands/profile.py +277 -0
  26. claude_mpm/cli/commands/skills.py +214 -189
  27. claude_mpm/cli/commands/summarize.py +413 -0
  28. claude_mpm/cli/executor.py +21 -3
  29. claude_mpm/cli/interactive/agent_wizard.py +85 -10
  30. claude_mpm/cli/parsers/agents_parser.py +54 -9
  31. claude_mpm/cli/parsers/auto_configure_parser.py +13 -138
  32. claude_mpm/cli/parsers/base_parser.py +12 -0
  33. claude_mpm/cli/parsers/config_parser.py +153 -83
  34. claude_mpm/cli/parsers/profile_parser.py +148 -0
  35. claude_mpm/cli/parsers/skills_parser.py +3 -2
  36. claude_mpm/cli/startup.py +879 -149
  37. claude_mpm/commands/mpm-config.md +28 -0
  38. claude_mpm/commands/mpm-doctor.md +9 -22
  39. claude_mpm/commands/mpm-help.md +5 -287
  40. claude_mpm/commands/mpm-init.md +81 -507
  41. claude_mpm/commands/mpm-monitor.md +15 -402
  42. claude_mpm/commands/mpm-organize.md +120 -0
  43. claude_mpm/commands/mpm-postmortem.md +6 -108
  44. claude_mpm/commands/mpm-session-resume.md +12 -363
  45. claude_mpm/commands/mpm-status.md +5 -69
  46. claude_mpm/commands/mpm-ticket-view.md +52 -495
  47. claude_mpm/commands/mpm-version.md +5 -107
  48. claude_mpm/config/agent_sources.py +27 -0
  49. claude_mpm/core/config.py +2 -4
  50. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  51. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  52. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  53. claude_mpm/core/framework_loader.py +4 -2
  54. claude_mpm/core/logger.py +13 -0
  55. claude_mpm/core/optimized_startup.py +59 -0
  56. claude_mpm/core/output_style_manager.py +173 -43
  57. claude_mpm/core/shared/config_loader.py +1 -1
  58. claude_mpm/core/socketio_pool.py +3 -3
  59. claude_mpm/core/unified_agent_registry.py +134 -16
  60. claude_mpm/core/unified_config.py +22 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  74. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  75. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  76. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  77. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  78. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  85. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  86. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  87. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  88. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  89. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  90. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  91. claude_mpm/hooks/claude_hooks/memory_integration.py +28 -0
  92. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  97. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  98. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  99. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  100. claude_mpm/hooks/memory_integration_hook.py +46 -1
  101. claude_mpm/init.py +63 -19
  102. claude_mpm/models/agent_definition.py +7 -0
  103. claude_mpm/models/git_repository.py +3 -3
  104. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  105. claude_mpm/scripts/launch_monitor.py +93 -13
  106. claude_mpm/scripts/start_activity_logging.py +0 -0
  107. claude_mpm/services/agents/agent_builder.py +3 -3
  108. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  109. claude_mpm/services/agents/agent_review_service.py +280 -0
  110. claude_mpm/services/agents/cache_git_manager.py +6 -6
  111. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  112. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  113. claude_mpm/services/agents/deployment/agent_template_builder.py +5 -3
  114. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  115. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +320 -29
  116. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +546 -68
  117. claude_mpm/services/agents/git_source_manager.py +36 -2
  118. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  119. claude_mpm/services/agents/recommender.py +5 -3
  120. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  121. claude_mpm/services/agents/sources/git_source_sync_service.py +13 -6
  122. claude_mpm/services/agents/startup_sync.py +22 -2
  123. claude_mpm/services/agents/toolchain_detector.py +10 -6
  124. claude_mpm/services/analysis/__init__.py +11 -1
  125. claude_mpm/services/analysis/clone_detector.py +1030 -0
  126. claude_mpm/services/command_deployment_service.py +81 -10
  127. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  128. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  129. claude_mpm/services/event_bus/config.py +3 -1
  130. claude_mpm/services/git/git_operations_service.py +101 -16
  131. claude_mpm/services/monitor/daemon.py +9 -2
  132. claude_mpm/services/monitor/daemon_manager.py +39 -3
  133. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  134. claude_mpm/services/monitor/server.py +698 -22
  135. claude_mpm/services/pm_skills_deployer.py +676 -0
  136. claude_mpm/services/profile_manager.py +331 -0
  137. claude_mpm/services/project/project_organizer.py +4 -0
  138. claude_mpm/services/self_upgrade_service.py +120 -12
  139. claude_mpm/services/skills/__init__.py +3 -0
  140. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  141. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  142. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  143. claude_mpm/services/skills_deployer.py +126 -9
  144. claude_mpm/services/socketio/dashboard_server.py +1 -0
  145. claude_mpm/services/socketio/event_normalizer.py +51 -6
  146. claude_mpm/services/socketio/server/core.py +386 -108
  147. claude_mpm/services/version_control/git_operations.py +103 -0
  148. claude_mpm/skills/skill_manager.py +92 -3
  149. claude_mpm/utils/agent_dependency_loader.py +14 -2
  150. claude_mpm/utils/agent_filters.py +17 -44
  151. claude_mpm/utils/gitignore.py +3 -0
  152. claude_mpm/utils/migration.py +4 -4
  153. claude_mpm/utils/robust_installer.py +47 -3
  154. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/METADATA +57 -87
  155. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/RECORD +160 -211
  156. claude_mpm-5.4.41.dist-info/entry_points.txt +5 -0
  157. claude_mpm-5.4.41.dist-info/licenses/LICENSE +94 -0
  158. claude_mpm-5.4.41.dist-info/licenses/LICENSE-FAQ.md +153 -0
  159. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  160. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  161. claude_mpm/agents/BASE_OPS.md +0 -219
  162. claude_mpm/agents/BASE_PM.md +0 -480
  163. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  164. claude_mpm/agents/BASE_QA.md +0 -167
  165. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  166. claude_mpm/agents/base_agent_loader.py +0 -601
  167. claude_mpm/cli/commands/agents_detect.py +0 -380
  168. claude_mpm/cli/commands/agents_recommend.py +0 -309
  169. claude_mpm/cli/ticket_cli.py +0 -35
  170. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  171. claude_mpm/commands/mpm-agents-detect.md +0 -177
  172. claude_mpm/commands/mpm-agents-list.md +0 -131
  173. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  174. claude_mpm/commands/mpm-config-view.md +0 -150
  175. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  176. claude_mpm/dashboard/analysis_runner.py +0 -455
  177. claude_mpm/dashboard/index.html +0 -13
  178. claude_mpm/dashboard/open_dashboard.py +0 -66
  179. claude_mpm/dashboard/static/css/activity.css +0 -1958
  180. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  181. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  182. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  183. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  184. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  185. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  186. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  187. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  188. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  189. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  190. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  191. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  192. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  193. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  194. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  195. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  196. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  197. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  198. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  199. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  200. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  201. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  202. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  203. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  204. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  205. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  206. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  207. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  208. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  209. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  210. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  211. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  212. claude_mpm/dashboard/templates/code_simple.html +0 -153
  213. claude_mpm/dashboard/templates/index.html +0 -606
  214. claude_mpm/dashboard/test_dashboard.html +0 -372
  215. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  216. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  217. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  218. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  219. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  220. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  221. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  222. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  223. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  224. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  225. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  226. claude_mpm/scripts/mcp_server.py +0 -75
  227. claude_mpm/scripts/mcp_wrapper.py +0 -39
  228. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  229. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  230. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  231. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  232. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  233. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  234. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  235. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  236. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  237. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  238. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  239. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  240. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  241. claude_mpm/services/mcp_gateway/main.py +0 -589
  242. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  243. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  244. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  245. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  246. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  247. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  248. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  249. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  250. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  251. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  252. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  253. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  254. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  255. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  256. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  257. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  258. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  259. claude_mpm-5.0.9.dist-info/entry_points.txt +0 -10
  260. claude_mpm-5.0.9.dist-info/licenses/LICENSE +0 -21
  261. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  262. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/WHEEL +0 -0
  263. {claude_mpm-5.0.9.dist-info → claude_mpm-5.4.41.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,481 @@
1
+ """
2
+ Project Knowledge Extractor for Enhanced /mpm-init Update Mode
3
+ ==============================================================
4
+
5
+ This module extracts project knowledge from multiple sources:
6
+ - Git history (architectural decisions, tech stack changes, workflows)
7
+ - Session logs (.claude-mpm/responses/*.json)
8
+ - Memory files (.claude-mpm/memories/*.md)
9
+
10
+ Used to enhance CLAUDE.md updates with accumulated project insights.
11
+
12
+ Author: Claude MPM Development Team
13
+ Created: 2025-12-13
14
+ """
15
+
16
+ import json
17
+ import re
18
+ import subprocess
19
+ from collections import Counter
20
+ from datetime import datetime, timedelta, timezone
21
+ from pathlib import Path
22
+ from typing import Any, Dict, List
23
+
24
+ from claude_mpm.core.logging_utils import get_logger
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ class ProjectKnowledgeExtractor:
30
+ """Extract project knowledge from git, logs, and memory files."""
31
+
32
+ def __init__(self, project_path: Path):
33
+ """
34
+ Initialize knowledge extractor.
35
+
36
+ Args:
37
+ project_path: Path to the project root directory
38
+ """
39
+ self.project_path = project_path
40
+ self.claude_mpm_dir = project_path / ".claude-mpm"
41
+ self.is_git_repo = (project_path / ".git").is_dir()
42
+
43
+ def extract_all(self, days: int = 90) -> Dict[str, Any]:
44
+ """
45
+ Extract knowledge from all sources.
46
+
47
+ Args:
48
+ days: Number of days to analyze git history (default: 90)
49
+
50
+ Returns:
51
+ Dict containing all extracted knowledge
52
+ """
53
+ return {
54
+ "git_insights": self.extract_from_git(days),
55
+ "log_insights": self.extract_from_logs(),
56
+ "memory_insights": self.extract_from_memory(),
57
+ }
58
+
59
+ def extract_from_git(self, days: int = 90) -> Dict[str, Any]:
60
+ """
61
+ Extract insights from git history.
62
+
63
+ Focus on:
64
+ - Architectural patterns from commit messages
65
+ - Tech stack changes (new dependencies, migrations)
66
+ - Common workflows (build, test, deploy patterns)
67
+ - Hot files (frequently modified = important)
68
+
69
+ Args:
70
+ days: Number of days to analyze
71
+
72
+ Returns:
73
+ Dict with git insights including patterns, workflows, tech changes
74
+ """
75
+ if not self.is_git_repo:
76
+ return {
77
+ "available": False,
78
+ "message": "Not a git repository",
79
+ }
80
+
81
+ insights = {
82
+ "available": True,
83
+ "architectural_decisions": [],
84
+ "tech_stack_changes": [],
85
+ "workflow_patterns": [],
86
+ "hot_files": [],
87
+ }
88
+
89
+ try:
90
+ # Get recent commits
91
+ since_date = (datetime.now(timezone.utc) - timedelta(days=days)).strftime(
92
+ "%Y-%m-%d"
93
+ )
94
+
95
+ # Get commit messages with file stats
96
+ result = subprocess.run(
97
+ [
98
+ "git",
99
+ "log",
100
+ f"--since={since_date}",
101
+ "--pretty=format:%s|||%b",
102
+ "--stat",
103
+ "--no-merges",
104
+ ],
105
+ cwd=self.project_path,
106
+ capture_output=True,
107
+ text=True,
108
+ check=False,
109
+ )
110
+
111
+ if result.returncode == 0 and result.stdout:
112
+ insights["architectural_decisions"] = (
113
+ self._extract_architectural_patterns(result.stdout)
114
+ )
115
+ insights["tech_stack_changes"] = self._extract_tech_changes(
116
+ result.stdout
117
+ )
118
+ insights["workflow_patterns"] = self._extract_workflow_patterns(
119
+ result.stdout
120
+ )
121
+
122
+ # Get file change frequency
123
+ freq_result = subprocess.run(
124
+ [
125
+ "git",
126
+ "log",
127
+ f"--since={since_date}",
128
+ "--pretty=format:",
129
+ "--name-only",
130
+ "--no-merges",
131
+ ],
132
+ cwd=self.project_path,
133
+ capture_output=True,
134
+ text=True,
135
+ check=False,
136
+ )
137
+
138
+ if freq_result.returncode == 0 and freq_result.stdout:
139
+ insights["hot_files"] = self._identify_hot_files(freq_result.stdout)
140
+
141
+ except Exception as e:
142
+ logger.warning(f"Failed to extract git insights: {e}")
143
+ insights["error"] = str(e)
144
+
145
+ return insights
146
+
147
+ def extract_from_logs(self) -> Dict[str, Any]:
148
+ """
149
+ Extract learnings from session logs.
150
+
151
+ Parse .claude-mpm/responses/*.json for:
152
+ - pm_summary fields with completed work
153
+ - tasks arrays showing what was built
154
+ - stop_event data with context
155
+
156
+ Returns:
157
+ Dict with extracted learnings from session logs
158
+ """
159
+ insights = {
160
+ "available": False,
161
+ "learnings": [],
162
+ "completed_tasks": [],
163
+ "common_patterns": [],
164
+ }
165
+
166
+ responses_dir = self.claude_mpm_dir / "responses"
167
+ if not responses_dir.exists():
168
+ return insights
169
+
170
+ insights["available"] = True
171
+
172
+ try:
173
+ # Find all JSON response files
174
+ json_files = list(responses_dir.glob("*.json"))
175
+
176
+ for json_file in json_files[:50]: # Limit to 50 most recent
177
+ try:
178
+ with open(json_file, encoding="utf-8") as f:
179
+ data = json.load(f)
180
+
181
+ # Extract PM summaries
182
+ if data.get("pm_summary"):
183
+ insights["learnings"].append(
184
+ {
185
+ "source": "pm_summary",
186
+ "timestamp": json_file.stem,
187
+ "content": data["pm_summary"],
188
+ }
189
+ )
190
+
191
+ # Extract task information
192
+ if "tasks" in data and isinstance(data["tasks"], list):
193
+ for task in data["tasks"]:
194
+ if isinstance(task, dict) and "description" in task:
195
+ insights["completed_tasks"].append(task["description"])
196
+
197
+ # Extract stop event context
198
+ if "stop_event" in data and isinstance(data["stop_event"], dict):
199
+ stop_event = data["stop_event"]
200
+ if "context" in stop_event:
201
+ insights["learnings"].append(
202
+ {
203
+ "source": "stop_event",
204
+ "timestamp": json_file.stem,
205
+ "content": stop_event["context"],
206
+ }
207
+ )
208
+
209
+ except Exception as e:
210
+ logger.debug(f"Failed to parse {json_file}: {e}")
211
+ continue
212
+
213
+ # Identify common patterns in completed tasks
214
+ if insights["completed_tasks"]:
215
+ insights["common_patterns"] = self._identify_task_patterns(
216
+ insights["completed_tasks"]
217
+ )
218
+
219
+ except Exception as e:
220
+ logger.warning(f"Failed to extract log insights: {e}")
221
+ insights["error"] = str(e)
222
+
223
+ return insights
224
+
225
+ def extract_from_memory(self) -> Dict[str, Any]:
226
+ """
227
+ Extract accumulated knowledge from memory files.
228
+
229
+ Parse .claude-mpm/memories/*.md for:
230
+ - Project Architecture sections
231
+ - Implementation Guidelines
232
+ - Common Mistakes to Avoid
233
+ - Current Technical Context
234
+
235
+ Returns:
236
+ Dict with extracted memory insights
237
+ """
238
+ insights = {
239
+ "available": False,
240
+ "architectural_knowledge": [],
241
+ "implementation_guidelines": [],
242
+ "common_mistakes": [],
243
+ "technical_context": [],
244
+ }
245
+
246
+ memories_dir = self.claude_mpm_dir / "memories"
247
+ if not memories_dir.exists():
248
+ return insights
249
+
250
+ insights["available"] = True
251
+
252
+ try:
253
+ # Find all markdown memory files (exclude README)
254
+ memory_files = [
255
+ f for f in memories_dir.glob("*.md") if f.name != "README.md"
256
+ ]
257
+
258
+ for memory_file in memory_files:
259
+ try:
260
+ with open(memory_file, encoding="utf-8") as f:
261
+ content = f.read()
262
+
263
+ agent_name = memory_file.stem.replace("_memories", "")
264
+
265
+ # Parse memory sections
266
+ sections = self._parse_memory_sections(content)
267
+
268
+ # Extract by section type
269
+ if "Project Architecture" in sections:
270
+ insights["architectural_knowledge"].extend(
271
+ self._extract_memory_items(
272
+ sections["Project Architecture"], agent_name
273
+ )
274
+ )
275
+
276
+ if "Implementation Guidelines" in sections:
277
+ insights["implementation_guidelines"].extend(
278
+ self._extract_memory_items(
279
+ sections["Implementation Guidelines"], agent_name
280
+ )
281
+ )
282
+
283
+ if "Common Mistakes to Avoid" in sections:
284
+ insights["common_mistakes"].extend(
285
+ self._extract_memory_items(
286
+ sections["Common Mistakes to Avoid"], agent_name
287
+ )
288
+ )
289
+
290
+ if "Current Technical Context" in sections:
291
+ insights["technical_context"].extend(
292
+ self._extract_memory_items(
293
+ sections["Current Technical Context"], agent_name
294
+ )
295
+ )
296
+
297
+ except Exception as e:
298
+ logger.debug(f"Failed to parse {memory_file}: {e}")
299
+ continue
300
+
301
+ except Exception as e:
302
+ logger.warning(f"Failed to extract memory insights: {e}")
303
+ insights["error"] = str(e)
304
+
305
+ return insights
306
+
307
+ # Private helper methods
308
+
309
+ def _extract_architectural_patterns(self, git_log: str) -> List[str]:
310
+ """Extract architectural decisions from commit messages."""
311
+ patterns = []
312
+
313
+ # Patterns indicating architectural changes
314
+ arch_keywords = [
315
+ r"add(?:ed)?\s+(\w+\s+(?:pattern|architecture|design))",
316
+ r"refactor(?:ed)?\s+to\s+(\w+)",
317
+ r"migrat(?:e|ed)\s+(?:to|from)\s+(\w+)",
318
+ r"implement(?:ed)?\s+(\w+\s+(?:pattern|architecture))",
319
+ r"introduc(?:e|ed)\s+(\w+\s+(?:layer|service|handler))",
320
+ ]
321
+
322
+ for pattern in arch_keywords:
323
+ matches = re.finditer(pattern, git_log, re.IGNORECASE)
324
+ for match in matches:
325
+ decision = match.group(1).strip()
326
+ if decision and decision not in patterns:
327
+ patterns.append(decision)
328
+
329
+ return patterns[:15] # Limit to top 15
330
+
331
+ def _extract_tech_changes(self, git_log: str) -> List[str]:
332
+ """Extract tech stack changes from commit messages."""
333
+ changes = []
334
+
335
+ # Patterns indicating tech stack changes
336
+ tech_keywords = [
337
+ r"add(?:ed)?\s+(?:dependency|package|library):\s*(\w+)",
338
+ r"upgrad(?:e|ed)\s+(\w+)\s+(?:to|from)",
339
+ r"switch(?:ed)?\s+(?:to|from)\s+(\w+)",
340
+ r"replac(?:e|ed)\s+(\w+)\s+with\s+(\w+)",
341
+ r"remov(?:e|ed)\s+(\w+)\s+dependency",
342
+ ]
343
+
344
+ for pattern in tech_keywords:
345
+ matches = re.finditer(pattern, git_log, re.IGNORECASE)
346
+ for match in matches:
347
+ # Get first captured group
348
+ change = match.group(1).strip() if match.group(1) else ""
349
+ if change and change not in changes:
350
+ changes.append(change)
351
+
352
+ return changes[:15] # Limit to top 15
353
+
354
+ def _extract_workflow_patterns(self, git_log: str) -> List[str]:
355
+ """Extract common workflows from commit messages."""
356
+ workflows = []
357
+
358
+ # Patterns indicating workflows
359
+ workflow_keywords = [
360
+ r"(?:build|test|deploy|lint|format):\s+(.+?)(?:\n|$)",
361
+ r"add(?:ed)?\s+(?:script|command)\s+(?:for|to)\s+(.+?)(?:\n|$)",
362
+ r"automat(?:e|ed)\s+(.+?)(?:\n|$)",
363
+ r"(?:ci|cd):\s+(.+?)(?:\n|$)",
364
+ ]
365
+
366
+ for pattern in workflow_keywords:
367
+ matches = re.finditer(pattern, git_log, re.IGNORECASE)
368
+ for match in matches:
369
+ workflow = match.group(1).strip()
370
+ if workflow and len(workflow) < 100 and workflow not in workflows:
371
+ workflows.append(workflow)
372
+
373
+ return workflows[:10] # Limit to top 10
374
+
375
+ def _identify_hot_files(self, file_list: str) -> List[Dict[str, Any]]:
376
+ """Identify frequently modified files (hot spots)."""
377
+ # Count file modifications
378
+ files = [f.strip() for f in file_list.split("\n") if f.strip()]
379
+ file_counts = Counter(files)
380
+
381
+ # Return top 20 most modified files
382
+ hot_files = []
383
+ for file_path, count in file_counts.most_common(20):
384
+ # Skip certain files
385
+ if any(
386
+ skip in file_path
387
+ for skip in [".lock", "package-lock", "poetry.lock", ".min."]
388
+ ):
389
+ continue
390
+
391
+ hot_files.append(
392
+ {
393
+ "path": file_path,
394
+ "modifications": count,
395
+ }
396
+ )
397
+
398
+ return hot_files
399
+
400
+ def _identify_task_patterns(self, tasks: List[str]) -> List[str]:
401
+ """Identify common patterns in completed tasks."""
402
+ # Extract common words/phrases
403
+ words = []
404
+ for task in tasks:
405
+ # Extract keywords (simple approach)
406
+ task_words = re.findall(r"\b[a-z]{4,}\b", task.lower())
407
+ words.extend(task_words)
408
+
409
+ # Count word frequency
410
+ word_counts = Counter(words)
411
+
412
+ # Return top 10 most common (excluding stopwords)
413
+ stopwords = {
414
+ "with",
415
+ "from",
416
+ "this",
417
+ "that",
418
+ "have",
419
+ "been",
420
+ "were",
421
+ "will",
422
+ "their",
423
+ "about",
424
+ }
425
+ patterns = [
426
+ word
427
+ for word, _count in word_counts.most_common(20)
428
+ if word not in stopwords
429
+ ]
430
+
431
+ return patterns[:10]
432
+
433
+ def _parse_memory_sections(self, content: str) -> Dict[str, str]:
434
+ """Parse markdown memory file into sections."""
435
+ sections = {}
436
+ current_section = None
437
+ current_content = []
438
+
439
+ for line in content.split("\n"):
440
+ # Check for section headers (## Section Name)
441
+ if line.startswith("## "):
442
+ # Save previous section
443
+ if current_section:
444
+ sections[current_section] = "\n".join(current_content).strip()
445
+
446
+ # Start new section
447
+ current_section = line[3:].strip()
448
+ current_content = []
449
+ elif current_section:
450
+ current_content.append(line)
451
+
452
+ # Save last section
453
+ if current_section:
454
+ sections[current_section] = "\n".join(current_content).strip()
455
+
456
+ return sections
457
+
458
+ def _extract_memory_items(self, section_content: str, agent_name: str) -> List[str]:
459
+ """Extract individual items from memory section."""
460
+ items = []
461
+
462
+ # Split by bullet points or numbered lists
463
+ lines = section_content.split("\n")
464
+ for line in lines:
465
+ line = line.strip()
466
+
467
+ # Match bullet points or numbered lists
468
+ if line.startswith(("-", "*", "•")) or re.match(r"^\d+\.", line):
469
+ # Remove bullet/number
470
+ item = re.sub(r"^[-*•]\s*", "", line)
471
+ item = re.sub(r"^\d+\.\s*", "", item)
472
+ item = item.strip()
473
+
474
+ if item:
475
+ # Prefix with agent name for context
476
+ items.append(f"[{agent_name}] {item}")
477
+
478
+ return items
479
+
480
+
481
+ __all__ = ["ProjectKnowledgeExtractor"]