claude-mpm 5.1.8__py3-none-any.whl → 5.4.22__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 (191) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/{PM_INSTRUCTIONS_TEACH.md → CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md} +721 -41
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +290 -34
  5. claude_mpm/agents/agent_loader.py +13 -44
  6. claude_mpm/agents/frontmatter_validator.py +68 -0
  7. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  8. claude_mpm/cli/__main__.py +4 -0
  9. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  10. claude_mpm/cli/commands/agent_state_manager.py +8 -17
  11. claude_mpm/cli/commands/agents.py +169 -31
  12. claude_mpm/cli/commands/auto_configure.py +210 -25
  13. claude_mpm/cli/commands/config.py +88 -2
  14. claude_mpm/cli/commands/configure.py +1111 -161
  15. claude_mpm/cli/commands/configure_agent_display.py +15 -6
  16. claude_mpm/cli/commands/mpm_init/core.py +160 -46
  17. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  18. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  19. claude_mpm/cli/commands/skills.py +214 -189
  20. claude_mpm/cli/commands/summarize.py +413 -0
  21. claude_mpm/cli/executor.py +11 -3
  22. claude_mpm/cli/parsers/agents_parser.py +54 -9
  23. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  24. claude_mpm/cli/parsers/base_parser.py +5 -0
  25. claude_mpm/cli/parsers/config_parser.py +153 -83
  26. claude_mpm/cli/parsers/skills_parser.py +3 -2
  27. claude_mpm/cli/startup.py +550 -94
  28. claude_mpm/commands/mpm-config.md +265 -0
  29. claude_mpm/commands/mpm-help.md +14 -95
  30. claude_mpm/commands/mpm-organize.md +500 -0
  31. claude_mpm/config/agent_sources.py +27 -0
  32. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  33. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  34. claude_mpm/core/framework_loader.py +4 -2
  35. claude_mpm/core/logger.py +13 -0
  36. claude_mpm/core/output_style_manager.py +173 -43
  37. claude_mpm/core/socketio_pool.py +3 -3
  38. claude_mpm/core/unified_agent_registry.py +134 -16
  39. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  40. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  41. claude_mpm/hooks/claude_hooks/hook_handler.py +6 -0
  42. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  43. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  44. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  45. claude_mpm/hooks/claude_hooks/services/connection_manager.py +4 -0
  46. claude_mpm/hooks/memory_integration_hook.py +46 -1
  47. claude_mpm/init.py +0 -19
  48. claude_mpm/models/agent_definition.py +7 -0
  49. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  50. claude_mpm/scripts/launch_monitor.py +93 -13
  51. claude_mpm/scripts/start_activity_logging.py +0 -0
  52. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  53. claude_mpm/services/agents/agent_review_service.py +280 -0
  54. claude_mpm/services/agents/deployment/agent_discovery_service.py +2 -3
  55. claude_mpm/services/agents/deployment/agent_template_builder.py +4 -2
  56. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +188 -12
  57. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +531 -55
  58. claude_mpm/services/agents/git_source_manager.py +34 -0
  59. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  60. claude_mpm/services/agents/sources/git_source_sync_service.py +8 -1
  61. claude_mpm/services/agents/toolchain_detector.py +10 -6
  62. claude_mpm/services/analysis/__init__.py +11 -1
  63. claude_mpm/services/analysis/clone_detector.py +1030 -0
  64. claude_mpm/services/command_deployment_service.py +81 -10
  65. claude_mpm/services/event_bus/config.py +3 -1
  66. claude_mpm/services/git/git_operations_service.py +93 -8
  67. claude_mpm/services/monitor/daemon.py +9 -2
  68. claude_mpm/services/monitor/daemon_manager.py +39 -3
  69. claude_mpm/services/monitor/server.py +225 -19
  70. claude_mpm/services/self_upgrade_service.py +120 -12
  71. claude_mpm/services/skills/__init__.py +3 -0
  72. claude_mpm/services/skills/git_skill_source_manager.py +32 -2
  73. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  74. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  75. claude_mpm/services/skills_deployer.py +126 -9
  76. claude_mpm/services/socketio/event_normalizer.py +15 -1
  77. claude_mpm/services/socketio/server/core.py +160 -21
  78. claude_mpm/services/version_control/git_operations.py +103 -0
  79. claude_mpm/utils/agent_filters.py +17 -44
  80. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/METADATA +47 -84
  81. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/RECORD +86 -176
  82. claude_mpm-5.4.22.dist-info/entry_points.txt +5 -0
  83. claude_mpm-5.4.22.dist-info/licenses/LICENSE +94 -0
  84. claude_mpm-5.4.22.dist-info/licenses/LICENSE-FAQ.md +153 -0
  85. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  86. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  87. claude_mpm/agents/BASE_ENGINEER.md +0 -658
  88. claude_mpm/agents/BASE_OPS.md +0 -219
  89. claude_mpm/agents/BASE_PM.md +0 -480
  90. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  91. claude_mpm/agents/BASE_QA.md +0 -167
  92. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  93. claude_mpm/agents/base_agent.json +0 -31
  94. claude_mpm/agents/base_agent_loader.py +0 -601
  95. claude_mpm/cli/commands/agents_detect.py +0 -380
  96. claude_mpm/cli/commands/agents_recommend.py +0 -309
  97. claude_mpm/cli/ticket_cli.py +0 -35
  98. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  99. claude_mpm/commands/mpm-agents-detect.md +0 -177
  100. claude_mpm/commands/mpm-agents-list.md +0 -131
  101. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  102. claude_mpm/commands/mpm-config-view.md +0 -150
  103. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  104. claude_mpm/dashboard/analysis_runner.py +0 -455
  105. claude_mpm/dashboard/index.html +0 -13
  106. claude_mpm/dashboard/open_dashboard.py +0 -66
  107. claude_mpm/dashboard/static/css/activity.css +0 -1958
  108. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  109. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  110. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  111. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  112. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  113. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  114. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  115. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  116. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  117. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  118. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  119. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  120. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  121. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  122. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  123. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  124. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  125. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  126. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  127. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  128. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  129. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  130. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  131. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  132. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  133. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  134. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  135. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  136. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  137. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  138. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  139. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  140. claude_mpm/dashboard/templates/code_simple.html +0 -153
  141. claude_mpm/dashboard/templates/index.html +0 -606
  142. claude_mpm/dashboard/test_dashboard.html +0 -372
  143. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-313.pyc +0 -0
  144. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-313.pyc +0 -0
  145. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-313.pyc +0 -0
  146. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-313.pyc +0 -0
  147. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-313.pyc +0 -0
  148. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-313.pyc +0 -0
  149. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-313.pyc +0 -0
  150. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-313.pyc +0 -0
  151. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-313.pyc +0 -0
  152. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-313.pyc +0 -0
  153. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-313.pyc +0 -0
  154. claude_mpm/scripts/mcp_server.py +0 -75
  155. claude_mpm/scripts/mcp_wrapper.py +0 -39
  156. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  157. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  158. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  159. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  160. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  161. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  162. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  163. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  164. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  165. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  166. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  167. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  168. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  169. claude_mpm/services/mcp_gateway/main.py +0 -589
  170. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  171. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  172. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  173. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  174. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  175. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  176. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  177. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  178. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  179. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  180. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  181. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  182. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  183. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  184. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  185. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  186. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  187. claude_mpm-5.1.8.dist-info/entry_points.txt +0 -10
  188. claude_mpm-5.1.8.dist-info/licenses/LICENSE +0 -21
  189. /claude_mpm/agents/{OUTPUT_STYLE.md → CLAUDE_MPM_OUTPUT_STYLE.md} +0 -0
  190. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.dist-info}/WHEEL +0 -0
  191. {claude_mpm-5.1.8.dist-info → claude_mpm-5.4.22.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"]