claude-mpm 4.7.4__py3-none-any.whl → 4.18.2__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.
Files changed (308) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  4. claude_mpm/agents/BASE_PM.md +106 -1
  5. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
  7. claude_mpm/agents/agent_loader.py +17 -5
  8. claude_mpm/agents/frontmatter_validator.py +284 -253
  9. claude_mpm/agents/templates/README.md +465 -0
  10. claude_mpm/agents/templates/agent-manager.json +4 -1
  11. claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
  12. claude_mpm/agents/templates/api_qa.json +11 -2
  13. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  14. claude_mpm/agents/templates/clerk-ops.json +12 -2
  15. claude_mpm/agents/templates/code_analyzer.json +8 -2
  16. claude_mpm/agents/templates/content-agent.json +358 -0
  17. claude_mpm/agents/templates/dart_engineer.json +15 -2
  18. claude_mpm/agents/templates/data_engineer.json +15 -2
  19. claude_mpm/agents/templates/documentation.json +10 -2
  20. claude_mpm/agents/templates/engineer.json +21 -1
  21. claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
  22. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  23. claude_mpm/agents/templates/golang_engineer.json +270 -0
  24. claude_mpm/agents/templates/imagemagick.json +4 -1
  25. claude_mpm/agents/templates/java_engineer.json +346 -0
  26. claude_mpm/agents/templates/local_ops_agent.json +1227 -6
  27. claude_mpm/agents/templates/memory_manager.json +4 -1
  28. claude_mpm/agents/templates/nextjs_engineer.json +141 -133
  29. claude_mpm/agents/templates/ops.json +12 -2
  30. claude_mpm/agents/templates/php-engineer.json +270 -174
  31. claude_mpm/agents/templates/pm_examples.md +474 -0
  32. claude_mpm/agents/templates/pm_red_flags.md +240 -0
  33. claude_mpm/agents/templates/product_owner.json +338 -0
  34. claude_mpm/agents/templates/project_organizer.json +14 -4
  35. claude_mpm/agents/templates/prompt-engineer.json +13 -2
  36. claude_mpm/agents/templates/python_engineer.json +174 -81
  37. claude_mpm/agents/templates/qa.json +11 -2
  38. claude_mpm/agents/templates/react_engineer.json +16 -3
  39. claude_mpm/agents/templates/refactoring_engineer.json +12 -2
  40. claude_mpm/agents/templates/research.json +34 -21
  41. claude_mpm/agents/templates/response_format.md +583 -0
  42. claude_mpm/agents/templates/ruby-engineer.json +129 -192
  43. claude_mpm/agents/templates/rust_engineer.json +270 -0
  44. claude_mpm/agents/templates/security.json +10 -2
  45. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  46. claude_mpm/agents/templates/ticketing.json +10 -2
  47. claude_mpm/agents/templates/typescript_engineer.json +116 -125
  48. claude_mpm/agents/templates/validation_templates.md +312 -0
  49. claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
  50. claude_mpm/agents/templates/version_control.json +12 -2
  51. claude_mpm/agents/templates/web_qa.json +11 -2
  52. claude_mpm/agents/templates/web_ui.json +15 -2
  53. claude_mpm/cli/__init__.py +34 -614
  54. claude_mpm/cli/commands/agent_manager.py +25 -12
  55. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  56. claude_mpm/cli/commands/agents.py +235 -148
  57. claude_mpm/cli/commands/agents_detect.py +380 -0
  58. claude_mpm/cli/commands/agents_recommend.py +309 -0
  59. claude_mpm/cli/commands/aggregate.py +7 -3
  60. claude_mpm/cli/commands/analyze.py +9 -4
  61. claude_mpm/cli/commands/analyze_code.py +7 -2
  62. claude_mpm/cli/commands/auto_configure.py +570 -0
  63. claude_mpm/cli/commands/config.py +47 -13
  64. claude_mpm/cli/commands/configure.py +419 -1571
  65. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  66. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  67. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  68. claude_mpm/cli/commands/configure_models.py +18 -0
  69. claude_mpm/cli/commands/configure_navigation.py +167 -0
  70. claude_mpm/cli/commands/configure_paths.py +104 -0
  71. claude_mpm/cli/commands/configure_persistence.py +254 -0
  72. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  73. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  74. claude_mpm/cli/commands/configure_validators.py +73 -0
  75. claude_mpm/cli/commands/local_deploy.py +537 -0
  76. claude_mpm/cli/commands/memory.py +54 -20
  77. claude_mpm/cli/commands/mpm_init.py +585 -196
  78. claude_mpm/cli/commands/mpm_init_handler.py +37 -3
  79. claude_mpm/cli/commands/search.py +170 -4
  80. claude_mpm/cli/commands/upgrade.py +152 -0
  81. claude_mpm/cli/executor.py +202 -0
  82. claude_mpm/cli/helpers.py +105 -0
  83. claude_mpm/cli/interactive/__init__.py +3 -0
  84. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  85. claude_mpm/cli/parsers/__init__.py +7 -1
  86. claude_mpm/cli/parsers/agents_parser.py +9 -0
  87. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  88. claude_mpm/cli/parsers/base_parser.py +110 -3
  89. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  90. claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
  91. claude_mpm/cli/shared/output_formatters.py +28 -19
  92. claude_mpm/cli/startup.py +481 -0
  93. claude_mpm/cli/utils.py +52 -1
  94. claude_mpm/commands/mpm-agents-detect.md +168 -0
  95. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  96. claude_mpm/commands/mpm-agents.md +75 -1
  97. claude_mpm/commands/mpm-auto-configure.md +217 -0
  98. claude_mpm/commands/mpm-help.md +163 -0
  99. claude_mpm/commands/mpm-init.md +148 -3
  100. claude_mpm/commands/mpm-version.md +113 -0
  101. claude_mpm/commands/mpm.md +1 -0
  102. claude_mpm/config/agent_config.py +2 -2
  103. claude_mpm/config/model_config.py +428 -0
  104. claude_mpm/constants.py +1 -0
  105. claude_mpm/core/base_service.py +13 -12
  106. claude_mpm/core/enums.py +452 -0
  107. claude_mpm/core/factories.py +1 -1
  108. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  109. claude_mpm/core/interactive_session.py +9 -3
  110. claude_mpm/core/log_manager.py +2 -0
  111. claude_mpm/core/logging_config.py +6 -2
  112. claude_mpm/core/oneshot_session.py +8 -4
  113. claude_mpm/core/optimized_agent_loader.py +3 -3
  114. claude_mpm/core/output_style_manager.py +12 -192
  115. claude_mpm/core/service_registry.py +5 -1
  116. claude_mpm/core/types.py +2 -9
  117. claude_mpm/core/typing_utils.py +7 -6
  118. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  119. claude_mpm/dashboard/templates/index.html +3 -41
  120. claude_mpm/hooks/__init__.py +20 -0
  121. claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
  122. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  123. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
  124. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  125. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  126. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  127. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  128. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  129. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  130. claude_mpm/hooks/kuzu_memory_hook.py +37 -12
  131. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  132. claude_mpm/models/resume_log.py +340 -0
  133. claude_mpm/services/agents/__init__.py +18 -5
  134. claude_mpm/services/agents/auto_config_manager.py +796 -0
  135. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  136. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  137. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  138. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  139. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  140. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  141. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  142. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  143. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  144. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  145. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  146. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  147. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  148. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  149. claude_mpm/services/agents/local_template_manager.py +1 -1
  150. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  151. claude_mpm/services/agents/observers.py +547 -0
  152. claude_mpm/services/agents/recommender.py +568 -0
  153. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  154. claude_mpm/services/command_handler_service.py +11 -5
  155. claude_mpm/services/core/__init__.py +33 -1
  156. claude_mpm/services/core/interfaces/__init__.py +90 -3
  157. claude_mpm/services/core/interfaces/agent.py +184 -0
  158. claude_mpm/services/core/interfaces/health.py +172 -0
  159. claude_mpm/services/core/interfaces/model.py +281 -0
  160. claude_mpm/services/core/interfaces/process.py +372 -0
  161. claude_mpm/services/core/interfaces/project.py +121 -0
  162. claude_mpm/services/core/interfaces/restart.py +307 -0
  163. claude_mpm/services/core/interfaces/stability.py +260 -0
  164. claude_mpm/services/core/memory_manager.py +11 -24
  165. claude_mpm/services/core/models/__init__.py +79 -0
  166. claude_mpm/services/core/models/agent_config.py +381 -0
  167. claude_mpm/services/core/models/health.py +162 -0
  168. claude_mpm/services/core/models/process.py +235 -0
  169. claude_mpm/services/core/models/restart.py +302 -0
  170. claude_mpm/services/core/models/stability.py +264 -0
  171. claude_mpm/services/core/models/toolchain.py +306 -0
  172. claude_mpm/services/core/path_resolver.py +23 -7
  173. claude_mpm/services/diagnostics/__init__.py +2 -2
  174. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  175. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  176. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  177. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  178. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  179. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  180. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  181. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  182. claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
  183. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  184. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  185. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  186. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  187. claude_mpm/services/diagnostics/models.py +19 -24
  188. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  189. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  190. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  191. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  192. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  193. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  194. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  195. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  196. claude_mpm/services/local_ops/__init__.py +163 -0
  197. claude_mpm/services/local_ops/crash_detector.py +257 -0
  198. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  199. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  200. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  201. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  202. claude_mpm/services/local_ops/health_manager.py +430 -0
  203. claude_mpm/services/local_ops/log_monitor.py +396 -0
  204. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  205. claude_mpm/services/local_ops/process_manager.py +595 -0
  206. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  207. claude_mpm/services/local_ops/restart_manager.py +401 -0
  208. claude_mpm/services/local_ops/restart_policy.py +387 -0
  209. claude_mpm/services/local_ops/state_manager.py +372 -0
  210. claude_mpm/services/local_ops/unified_manager.py +600 -0
  211. claude_mpm/services/mcp_config_manager.py +9 -4
  212. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  213. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  214. claude_mpm/services/mcp_gateway/main.py +30 -0
  215. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
  216. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  217. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
  218. claude_mpm/services/mcp_service_verifier.py +1 -1
  219. claude_mpm/services/memory/failure_tracker.py +563 -0
  220. claude_mpm/services/memory_hook_service.py +165 -4
  221. claude_mpm/services/model/__init__.py +147 -0
  222. claude_mpm/services/model/base_provider.py +365 -0
  223. claude_mpm/services/model/claude_provider.py +412 -0
  224. claude_mpm/services/model/model_router.py +453 -0
  225. claude_mpm/services/model/ollama_provider.py +415 -0
  226. claude_mpm/services/monitor/daemon_manager.py +3 -2
  227. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  228. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  229. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  230. claude_mpm/services/monitor/server.py +2 -1
  231. claude_mpm/services/project/__init__.py +23 -0
  232. claude_mpm/services/project/detection_strategies.py +719 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  234. claude_mpm/services/self_upgrade_service.py +342 -0
  235. claude_mpm/services/session_management_service.py +3 -2
  236. claude_mpm/services/session_manager.py +205 -1
  237. claude_mpm/services/shared/async_service_base.py +16 -27
  238. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  239. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  240. claude_mpm/services/socketio/handlers/hook.py +13 -2
  241. claude_mpm/services/socketio/handlers/registry.py +4 -2
  242. claude_mpm/services/socketio/server/main.py +10 -8
  243. claude_mpm/services/subprocess_launcher_service.py +14 -5
  244. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  245. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  246. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  247. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  248. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  249. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  250. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  251. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  252. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  253. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  254. claude_mpm/services/unified/interfaces.py +3 -1
  255. claude_mpm/services/unified/unified_analyzer.py +14 -10
  256. claude_mpm/services/unified/unified_config.py +2 -1
  257. claude_mpm/services/unified/unified_deployment.py +9 -4
  258. claude_mpm/services/version_service.py +104 -1
  259. claude_mpm/skills/__init__.py +21 -0
  260. claude_mpm/skills/bundled/__init__.py +6 -0
  261. claude_mpm/skills/bundled/api-documentation.md +393 -0
  262. claude_mpm/skills/bundled/async-testing.md +571 -0
  263. claude_mpm/skills/bundled/code-review.md +143 -0
  264. claude_mpm/skills/bundled/database-migration.md +199 -0
  265. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  266. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  267. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  268. claude_mpm/skills/bundled/git-workflow.md +414 -0
  269. claude_mpm/skills/bundled/imagemagick.md +204 -0
  270. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  271. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  272. claude_mpm/skills/bundled/pdf.md +141 -0
  273. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  274. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  275. claude_mpm/skills/bundled/security-scanning.md +327 -0
  276. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  277. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  278. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  279. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  280. claude_mpm/skills/bundled/xlsx.md +157 -0
  281. claude_mpm/skills/registry.py +286 -0
  282. claude_mpm/skills/skill_manager.py +310 -0
  283. claude_mpm/storage/state_storage.py +15 -15
  284. claude_mpm/tools/code_tree_analyzer.py +177 -141
  285. claude_mpm/tools/code_tree_events.py +4 -2
  286. claude_mpm/utils/agent_dependency_loader.py +40 -20
  287. claude_mpm/utils/display_helper.py +260 -0
  288. claude_mpm/utils/git_analyzer.py +407 -0
  289. claude_mpm/utils/robust_installer.py +73 -19
  290. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
  291. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
  292. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  293. claude_mpm/dashboard/static/index-hub-backup.html +0 -713
  294. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  295. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  296. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  297. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  298. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  299. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  300. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  301. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  302. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  303. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  304. claude_mpm/services/project/analyzer_refactored.py +0 -450
  305. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  306. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  307. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  308. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,286 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ Learning Extraction Hook
4
+ =========================
5
+
6
+ Synthesizes learnings from failure-fix pairs and writes them to agent memory.
7
+
8
+ WHY: The final step in the failure-learning cycle is extracting actionable
9
+ learnings and persisting them to agent memory files. This hook completes the
10
+ cycle by taking failure-fix pairs and creating formatted learning entries.
11
+
12
+ DESIGN DECISION: This hook runs last (priority=89) after both failure and fix
13
+ detection. It uses template-based synthesis for MVP (no AI) and integrates
14
+ with AgentMemoryManager to write learnings to the appropriate memory files.
15
+
16
+ Integration points:
17
+ - Monitors for fix_detected metadata from FixDetectionHook
18
+ - Extracts learnings using FailureTracker
19
+ - Formats learnings as markdown
20
+ - Writes to agent memory files via AgentMemoryManager
21
+ """
22
+
23
+ import logging
24
+ from typing import Any
25
+
26
+ from claude_mpm.hooks.base_hook import (
27
+ BaseHook,
28
+ HookContext,
29
+ HookResult,
30
+ HookType,
31
+ )
32
+ from claude_mpm.services.memory.failure_tracker import get_failure_tracker
33
+
34
+ logger = logging.getLogger(__name__)
35
+
36
+
37
+ class LearningExtractionHook(BaseHook):
38
+ """Hook that extracts and persists learnings from failure-fix pairs.
39
+
40
+ WHY: Automatically converts failure-fix pairs into persistent learnings
41
+ stored in agent memory files. This completes the failure-learning cycle
42
+ without requiring manual intervention.
43
+
44
+ DESIGN DECISION: Priority 89 ensures this runs last in the chain:
45
+ 1. Failure detection (85) - detects failures
46
+ 2. Fix detection (87) - matches fixes with failures
47
+ 3. Learning extraction (89) - creates and persists learnings
48
+
49
+ MVP uses template-based learning synthesis. Future versions could use
50
+ AI to analyze git diffs, code changes, and generate richer learnings.
51
+ """
52
+
53
+ def __init__(self):
54
+ """Initialize the learning extraction hook."""
55
+ super().__init__(
56
+ name="learning_extraction",
57
+ priority=89, # Last in the chain, after fix detection
58
+ )
59
+ self.tracker = get_failure_tracker()
60
+ self._memory_manager = None
61
+
62
+ @property
63
+ def memory_manager(self):
64
+ """Lazy-load memory manager to avoid circular imports.
65
+
66
+ WHY: AgentMemoryManager may import hooks, so we lazy-load to prevent
67
+ circular dependency issues.
68
+
69
+ Returns:
70
+ AgentMemoryManager instance
71
+ """
72
+ if self._memory_manager is None:
73
+ try:
74
+ from claude_mpm.services.agents.memory.agent_memory_manager import (
75
+ get_memory_manager,
76
+ )
77
+
78
+ self._memory_manager = get_memory_manager()
79
+ except ImportError as e:
80
+ logger.error(f"Failed to import AgentMemoryManager: {e}")
81
+ raise
82
+
83
+ return self._memory_manager
84
+
85
+ def execute(self, context: HookContext) -> HookResult:
86
+ """Execute learning extraction from failure-fix pairs.
87
+
88
+ WHY: When a fix is detected, we have everything needed to extract a
89
+ learning: the original failure, the fix that resolved it, and the
90
+ context. This method synthesizes a learning and writes it to memory.
91
+
92
+ Args:
93
+ context: Hook context containing fix detection metadata
94
+
95
+ Returns:
96
+ HookResult with extraction results
97
+ """
98
+ try:
99
+ # Check if this is a fix detection event
100
+ metadata = context.metadata or {}
101
+ if not metadata.get("fix_detected"):
102
+ # Not a fix event, skip
103
+ return HookResult(success=True, modified=False)
104
+
105
+ # Extract failure and fix events from metadata
106
+ failure_event = metadata.get("failure_event")
107
+ fix_event = metadata.get("fix_event")
108
+
109
+ if not failure_event or not fix_event:
110
+ logger.warning("Fix detected but failure/fix events not in metadata")
111
+ return HookResult(success=True, modified=False)
112
+
113
+ # Extract learning from failure-fix pair
114
+ learning = self.tracker.extract_learning(
115
+ fix_event=fix_event,
116
+ failure_event=failure_event,
117
+ target_agent=self._determine_target_agent(context, failure_event),
118
+ )
119
+
120
+ # Format learning as markdown
121
+ learning_markdown = learning.to_markdown()
122
+
123
+ # Write to agent memory
124
+ success = self._write_to_memory(
125
+ agent_id=learning.target_agent, learning_text=learning_markdown
126
+ )
127
+
128
+ if success:
129
+ logger.info(
130
+ f"Learning extracted and saved for {learning.target_agent}: "
131
+ f"{learning.category}"
132
+ )
133
+ return HookResult(
134
+ success=True,
135
+ modified=False,
136
+ metadata={
137
+ "learning_extracted": True,
138
+ "target_agent": learning.target_agent,
139
+ "learning_category": learning.category,
140
+ },
141
+ )
142
+ logger.warning(
143
+ f"Failed to write learning to memory for {learning.target_agent}"
144
+ )
145
+ return HookResult(
146
+ success=True, # Don't fail the hook, just log warning
147
+ modified=False,
148
+ metadata={"learning_extracted": False},
149
+ )
150
+
151
+ except Exception as e:
152
+ logger.error(f"Error in learning extraction hook: {e}", exc_info=True)
153
+ return HookResult(success=False, error=str(e), modified=False)
154
+
155
+ def validate(self, context: HookContext) -> bool:
156
+ """Validate if this hook should run for the given context.
157
+
158
+ Args:
159
+ context: Hook context to validate
160
+
161
+ Returns:
162
+ True if hook should execute
163
+ """
164
+ if not super().validate(context):
165
+ return False
166
+
167
+ # Run for POST_DELEGATION events (after tool execution)
168
+ if context.hook_type != HookType.POST_DELEGATION:
169
+ return False
170
+
171
+ # Must have fix detection metadata
172
+ metadata = context.metadata or {}
173
+ return metadata.get("fix_detected", False)
174
+
175
+ def _determine_target_agent(self, context: HookContext, failure_event: Any) -> str:
176
+ """Determine which agent should receive the learning.
177
+
178
+ WHY: Learnings should go to the agent most likely to benefit from them.
179
+ This method applies routing logic to determine the best target.
180
+
181
+ Args:
182
+ context: Hook context
183
+ failure_event: The failure event
184
+
185
+ Returns:
186
+ Agent identifier (PM, engineer, qa, etc.)
187
+ """
188
+ # Try to get agent from context first
189
+ agent_type = (
190
+ context.data.get("agent_type")
191
+ or context.data.get("subagent_type")
192
+ or context.metadata.get("agent_type")
193
+ )
194
+ if agent_type:
195
+ return agent_type
196
+
197
+ # Check failure event context
198
+ if hasattr(failure_event, "context") and failure_event.context.get(
199
+ "agent_type"
200
+ ):
201
+ return failure_event.context["agent_type"]
202
+
203
+ # Fall back to task-based routing
204
+ if hasattr(failure_event, "task_type"):
205
+ task_type = failure_event.task_type
206
+ if task_type in ("test", "lint"):
207
+ return "qa"
208
+ if task_type in ("build", "install", "script") or task_type == "git":
209
+ return "engineer"
210
+
211
+ # Default to PM
212
+ return "PM"
213
+
214
+ def _write_to_memory(self, agent_id: str, learning_text: str) -> bool:
215
+ """Write learning to agent memory file.
216
+
217
+ WHY: Learnings must be persisted to memory files so agents can access
218
+ them in future sessions. This method uses AgentMemoryManager to handle
219
+ the actual file operations.
220
+
221
+ Args:
222
+ agent_id: Agent identifier
223
+ learning_text: Markdown-formatted learning
224
+
225
+ Returns:
226
+ True if write succeeded, False otherwise
227
+ """
228
+ try:
229
+ # Parse learning sections from markdown
230
+ learning_items = self._parse_learning_markdown(learning_text)
231
+
232
+ if not learning_items:
233
+ logger.warning(f"No learning items parsed from: {learning_text}")
234
+ return False
235
+
236
+ # Add to agent memory
237
+ return self.memory_manager.update_agent_memory(
238
+ agent_id=agent_id, new_items=learning_items
239
+ )
240
+
241
+ except Exception as e:
242
+ logger.error(f"Failed to write learning to memory for {agent_id}: {e}")
243
+ return False
244
+
245
+ def _parse_learning_markdown(self, learning_markdown: str) -> list:
246
+ """Parse learning markdown into list items for memory.
247
+
248
+ WHY: AgentMemoryManager expects a list of learning items. We need to
249
+ convert the markdown-formatted learning into individual list items.
250
+
251
+ Args:
252
+ learning_markdown: Markdown-formatted learning
253
+
254
+ Returns:
255
+ List of learning items
256
+ """
257
+ items = []
258
+
259
+ # Split by lines and extract bullet points
260
+ lines = learning_markdown.split("\n")
261
+
262
+ for line in lines:
263
+ line = line.strip()
264
+ if line.startswith("- **"):
265
+ # This is a learning item (e.g., "- **Problem**: ...")
266
+ items.append(line)
267
+ elif line.startswith("## "):
268
+ # This is a category header, skip it
269
+ continue
270
+
271
+ # If no items found, return the whole thing as a single item
272
+ if not items:
273
+ items = [f"- {learning_markdown.strip()}"]
274
+
275
+ return items
276
+
277
+
278
+ def get_learning_extraction_hook() -> LearningExtractionHook:
279
+ """Factory function to create learning extraction hook.
280
+
281
+ WHY: Provides consistent hook creation pattern used throughout the framework.
282
+
283
+ Returns:
284
+ Configured LearningExtractionHook instance
285
+ """
286
+ return LearningExtractionHook()
@@ -10,6 +10,7 @@ from dataclasses import dataclass
10
10
  from enum import Enum
11
11
  from typing import Dict, List, Optional, Tuple
12
12
 
13
+ from claude_mpm.core.enums import OperationResult
13
14
  from claude_mpm.core.logging_utils import get_logger
14
15
 
15
16
  logger = get_logger(__name__)
@@ -267,7 +268,7 @@ class InstructionReinforcementHook:
267
268
  if not self.violations:
268
269
  return {
269
270
  "total_violations": 0,
270
- "status": "COMPLIANT",
271
+ "status": OperationResult.SUCCESS,
271
272
  "message": "No PM delegation violations detected",
272
273
  }
273
274
 
@@ -276,7 +277,11 @@ class InstructionReinforcementHook:
276
277
  vtype = v.violation_type.value
277
278
  violation_types[vtype] = violation_types.get(vtype, 0) + 1
278
279
 
279
- status = "WARNING" if self.violation_count < 3 else "CRITICAL"
280
+ status = (
281
+ OperationResult.ERROR
282
+ if self.violation_count < 3
283
+ else OperationResult.FAILED
284
+ )
280
285
 
281
286
  return {
282
287
  "total_violations": self.violation_count,
@@ -0,0 +1,263 @@
1
+ """
2
+ Kuzu-Memory Pre-Delegation Enrichment Hook
3
+ ==========================================
4
+
5
+ Enriches agent delegation context with relevant memories from kuzu-memory
6
+ before the agent receives the task. This is the READ side of bidirectional
7
+ enrichment.
8
+
9
+ WHY: Agents need access to relevant historical knowledge when performing tasks.
10
+ This hook retrieves memories from kuzu-memory and injects them into the
11
+ delegation context.
12
+
13
+ DESIGN DECISIONS:
14
+ - Priority 10 to run early, before other context modifications
15
+ - Reuses KuzuMemoryHook's retrieval methods for consistency
16
+ - Injects memories as a dedicated section in agent context
17
+ - Falls back gracefully if kuzu-memory is not available
18
+ """
19
+
20
+ import logging
21
+ from typing import Any, Dict, Optional
22
+
23
+ from claude_mpm.hooks.base_hook import HookContext, HookResult, PreDelegationHook
24
+ from claude_mpm.hooks.kuzu_memory_hook import get_kuzu_memory_hook
25
+
26
+ logger = logging.getLogger(__name__)
27
+
28
+
29
+ class KuzuEnrichmentHook(PreDelegationHook):
30
+ """
31
+ Hook that enriches agent delegation context with kuzu-memory.
32
+
33
+ This hook:
34
+ 1. Extracts the task/prompt from delegation context
35
+ 2. Retrieves relevant memories from kuzu-memory
36
+ 3. Injects memories into agent context
37
+ 4. Formats memories for optimal agent understanding
38
+ """
39
+
40
+ def __init__(self):
41
+ """Initialize the kuzu-memory enrichment hook."""
42
+ super().__init__(name="kuzu_memory_enrichment", priority=10)
43
+
44
+ # Reuse the kuzu-memory hook instance for retrieval
45
+ self.kuzu_hook = get_kuzu_memory_hook()
46
+ self.enabled = self.kuzu_hook.enabled
47
+
48
+ if not self.enabled:
49
+ logger.info(
50
+ "Kuzu-memory enrichment hook disabled (kuzu-memory not available)"
51
+ )
52
+ else:
53
+ logger.info("Kuzu-memory enrichment hook enabled")
54
+
55
+ def validate(self, context: HookContext) -> bool:
56
+ """
57
+ Validate if hook should process this context.
58
+
59
+ Args:
60
+ context: Hook context to validate
61
+
62
+ Returns:
63
+ True if hook should execute
64
+ """
65
+ if not self.enabled:
66
+ return False
67
+
68
+ # Check base validation (enabled, correct hook type, has agent)
69
+ if not super().validate(context):
70
+ return False
71
+
72
+ # Must have agent and context data
73
+ if not context.data.get("agent"):
74
+ return False
75
+
76
+ return True
77
+
78
+ def execute(self, context: HookContext) -> HookResult:
79
+ """
80
+ Enrich delegation context with relevant memories.
81
+
82
+ Args:
83
+ context: Hook context containing delegation data
84
+
85
+ Returns:
86
+ HookResult with enriched context
87
+ """
88
+ if not self.enabled:
89
+ return HookResult(success=True, data=context.data, modified=False)
90
+
91
+ try:
92
+ # Extract query for memory retrieval
93
+ query = self._extract_query_from_context(context.data)
94
+
95
+ if not query:
96
+ logger.debug("No query extracted from context for memory retrieval")
97
+ return HookResult(success=True, data=context.data, modified=False)
98
+
99
+ # Retrieve relevant memories
100
+ memories = self.kuzu_hook._retrieve_memories(query)
101
+
102
+ if not memories:
103
+ logger.debug("No relevant memories found")
104
+ return HookResult(success=True, data=context.data, modified=False)
105
+
106
+ # Enrich context with memories
107
+ enriched_data = self._enrich_delegation_context(
108
+ context.data, memories, context.data.get("agent", "Agent")
109
+ )
110
+
111
+ logger.info(
112
+ f"Enriched delegation context with {len(memories)} memories for {context.data.get('agent')}"
113
+ )
114
+
115
+ return HookResult(
116
+ success=True,
117
+ data=enriched_data,
118
+ modified=True,
119
+ metadata={
120
+ "memories_added": len(memories),
121
+ "memory_source": "kuzu",
122
+ "agent": context.data.get("agent"),
123
+ },
124
+ )
125
+
126
+ except Exception as e:
127
+ logger.error(f"Error in kuzu enrichment hook: {e}")
128
+ # Don't fail the delegation if memory enrichment fails
129
+ return HookResult(
130
+ success=True,
131
+ data=context.data,
132
+ modified=False,
133
+ error=f"Memory enrichment failed: {e}",
134
+ )
135
+
136
+ def _extract_query_from_context(self, data: Dict[str, Any]) -> Optional[str]:
137
+ """
138
+ Extract query text for memory retrieval.
139
+
140
+ Args:
141
+ data: Delegation context data
142
+
143
+ Returns:
144
+ Query string or None
145
+ """
146
+ # Try various context fields
147
+ delegation_context = data.get("context", {})
148
+
149
+ # Handle string context
150
+ if isinstance(delegation_context, str):
151
+ return delegation_context
152
+
153
+ # Handle dict context
154
+ if isinstance(delegation_context, dict):
155
+ # Try common fields
156
+ for field in ["prompt", "task", "query", "user_request", "description"]:
157
+ if field in delegation_context:
158
+ value = delegation_context[field]
159
+ if isinstance(value, str):
160
+ return value
161
+
162
+ # If no specific field, join all string values
163
+ text_parts = [
164
+ str(v) for v in delegation_context.values() if isinstance(v, str)
165
+ ]
166
+ if text_parts:
167
+ return " ".join(text_parts)
168
+
169
+ # Fallback: try to get task or instruction directly
170
+ if "task" in data and isinstance(data["task"], str):
171
+ return data["task"]
172
+
173
+ if "instruction" in data and isinstance(data["instruction"], str):
174
+ return data["instruction"]
175
+
176
+ return None
177
+
178
+ def _enrich_delegation_context(
179
+ self, original_data: Dict[str, Any], memories: list, agent_name: str
180
+ ) -> Dict[str, Any]:
181
+ """
182
+ Enrich delegation context with memories.
183
+
184
+ Args:
185
+ original_data: Original delegation data
186
+ memories: Retrieved memories
187
+ agent_name: Name of the agent
188
+
189
+ Returns:
190
+ Enriched delegation data
191
+ """
192
+ # Format memories
193
+ memory_section = self._format_memory_section(memories, agent_name)
194
+
195
+ # Create enriched data
196
+ enriched_data = original_data.copy()
197
+
198
+ # Get existing context
199
+ delegation_context = enriched_data.get("context", {})
200
+ if isinstance(delegation_context, str):
201
+ delegation_context = {"prompt": delegation_context}
202
+
203
+ # Add memory section
204
+ if isinstance(delegation_context, dict):
205
+ # Prepend memory section to context
206
+ delegation_context["kuzu_memories"] = memory_section
207
+
208
+ # If there's a main prompt/task, prepend memory note
209
+ for field in ["prompt", "task", "instruction"]:
210
+ if field in delegation_context and isinstance(
211
+ delegation_context[field], str
212
+ ):
213
+ delegation_context[field] = (
214
+ f"{memory_section}\n\n{delegation_context[field]}"
215
+ )
216
+ break
217
+ else:
218
+ # If context is not dict, create new dict with memory
219
+ delegation_context = {
220
+ "kuzu_memories": memory_section,
221
+ "original_context": delegation_context,
222
+ }
223
+
224
+ enriched_data["context"] = delegation_context
225
+ enriched_data["_kuzu_enriched"] = True
226
+
227
+ return enriched_data
228
+
229
+ def _format_memory_section(self, memories: list, agent_name: str) -> str:
230
+ """
231
+ Format memories into a readable section.
232
+
233
+ Args:
234
+ memories: List of memory dictionaries
235
+ agent_name: Name of the agent
236
+
237
+ Returns:
238
+ Formatted memory section
239
+ """
240
+ memory_text = self.kuzu_hook._format_memories(memories)
241
+
242
+ return f"""
243
+ === RELEVANT KNOWLEDGE FROM KUZU MEMORY ===
244
+ {agent_name}, you have access to these relevant memories from the knowledge graph:
245
+
246
+ {memory_text}
247
+
248
+ INSTRUCTIONS: Review these memories before proceeding. Apply learned patterns and avoid known mistakes.
249
+ Use this knowledge to provide more informed and contextual responses.
250
+ ===========================================
251
+ """
252
+
253
+
254
+ # Create a singleton instance
255
+ _kuzu_enrichment_hook = None
256
+
257
+
258
+ def get_kuzu_enrichment_hook() -> KuzuEnrichmentHook:
259
+ """Get the singleton kuzu-memory enrichment hook instance."""
260
+ global _kuzu_enrichment_hook
261
+ if _kuzu_enrichment_hook is None:
262
+ _kuzu_enrichment_hook = KuzuEnrichmentHook()
263
+ return _kuzu_enrichment_hook
@@ -13,8 +13,9 @@ for structured memory storage with semantic search capabilities.
13
13
  DESIGN DECISIONS:
14
14
  - Priority 10 for early execution to enrich prompts before other hooks
15
15
  - Uses subprocess to call kuzu-memory directly for maximum compatibility
16
- - Graceful degradation if kuzu-memory is not installed
16
+ - Graceful degradation if kuzu-memory is not in PATH (though it's now required)
17
17
  - Automatic extraction and storage of important information
18
+ - kuzu-memory>=1.1.5 is now a REQUIRED dependency (moved from optional in v4.8.6)
18
19
  """
19
20
 
20
21
  import json
@@ -50,7 +51,10 @@ class KuzuMemoryHook(SubmitHook):
50
51
  self.enabled = self.kuzu_memory_cmd is not None
51
52
 
52
53
  if not self.enabled:
53
- logger.info("Kuzu-memory not found. Install with: pipx install kuzu-memory")
54
+ logger.warning(
55
+ "Kuzu-memory not found in PATH. As of v4.8.6, it's a required dependency. "
56
+ "Install with: pip install kuzu-memory>=1.1.5 or pipx install kuzu-memory"
57
+ )
54
58
  else:
55
59
  logger.info(f"Kuzu-memory integration enabled: {self.kuzu_memory_cmd}")
56
60
 
@@ -72,6 +76,10 @@ class KuzuMemoryHook(SubmitHook):
72
76
  1. Check pipx installation
73
77
  2. Check system PATH
74
78
  3. Return None if not found
79
+
80
+ NOTE: As of v4.8.6, kuzu-memory is a required dependency and should be
81
+ installed via pip. This method checks both pipx and system PATH for
82
+ backward compatibility.
75
83
  """
76
84
  # Check pipx installation
77
85
  pipx_path = (
@@ -157,9 +165,13 @@ class KuzuMemoryHook(SubmitHook):
157
165
  List of relevant memory dictionaries
158
166
  """
159
167
  try:
160
- # Use kuzu-memory recall command
168
+ # Type narrowing: ensure kuzu_memory_cmd is not None before using
169
+ if self.kuzu_memory_cmd is None:
170
+ return []
171
+
172
+ # Use kuzu-memory recall command (v1.2.7+ syntax)
161
173
  result = subprocess.run(
162
- [self.kuzu_memory_cmd, "recall", query, "--format", "json"],
174
+ [self.kuzu_memory_cmd, "memory", "recall", query, "--format", "json"],
163
175
  capture_output=True,
164
176
  text=True,
165
177
  timeout=5,
@@ -168,10 +180,21 @@ class KuzuMemoryHook(SubmitHook):
168
180
  )
169
181
 
170
182
  if result.returncode == 0 and result.stdout:
171
- memories = json.loads(result.stdout)
172
- return memories if isinstance(memories, list) else []
173
-
174
- except (subprocess.TimeoutExpired, json.JSONDecodeError, Exception) as e:
183
+ try:
184
+ # Parse JSON with strict=False to handle control characters
185
+ data = json.loads(result.stdout, strict=False)
186
+ # v1.2.7 returns dict with 'memories' key, not array
187
+ if isinstance(data, dict):
188
+ memories = data.get("memories", [])
189
+ else:
190
+ memories = data if isinstance(data, list) else []
191
+ return memories
192
+ except json.JSONDecodeError as e:
193
+ logger.warning(f"Failed to parse kuzu-memory JSON output: {e}")
194
+ logger.debug(f"Raw output: {result.stdout[:200]}")
195
+ return [] # Graceful fallback
196
+
197
+ except (subprocess.TimeoutExpired, Exception) as e:
175
198
  logger.debug(f"Memory retrieval failed: {e}")
176
199
 
177
200
  return []
@@ -255,12 +278,12 @@ Note: Use the memories above to provide more informed and contextual responses.
255
278
  Returns:
256
279
  True if storage was successful
257
280
  """
258
- if not self.enabled:
281
+ if not self.enabled or self.kuzu_memory_cmd is None:
259
282
  return False
260
283
 
261
284
  try:
262
- # Use kuzu-memory remember command (synchronous)
263
- cmd = [self.kuzu_memory_cmd, "remember", content]
285
+ # Use kuzu-memory store command (v1.2.7+ syntax)
286
+ cmd = [self.kuzu_memory_cmd, "memory", "store", content]
264
287
 
265
288
  # Execute store command in project directory
266
289
  result = subprocess.run(
@@ -273,8 +296,10 @@ Note: Use the memories above to provide more informed and contextual responses.
273
296
  )
274
297
 
275
298
  if result.returncode == 0:
276
- logger.debug(f"Stored memory: {content[:50]}...")
299
+ logger.debug(f"Stored memory in kuzu: {content[:50]}...")
277
300
  return True
301
+ logger.warning(f"Failed to store memory in kuzu: {result.stderr}")
302
+ return False
278
303
 
279
304
  except Exception as e:
280
305
  logger.error(f"Failed to store memory: {e}")