moai-adk 0.25.4__py3-none-any.whl โ†’ 0.32.8__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 moai-adk might be problematic. Click here for more details.

Files changed (378) hide show
  1. moai_adk/__init__.py +2 -5
  2. moai_adk/__main__.py +114 -82
  3. moai_adk/cli/__init__.py +6 -1
  4. moai_adk/cli/commands/__init__.py +1 -3
  5. moai_adk/cli/commands/analyze.py +5 -16
  6. moai_adk/cli/commands/doctor.py +6 -18
  7. moai_adk/cli/commands/init.py +56 -125
  8. moai_adk/cli/commands/language.py +14 -35
  9. moai_adk/cli/commands/status.py +9 -15
  10. moai_adk/cli/commands/update.py +1555 -190
  11. moai_adk/cli/prompts/init_prompts.py +112 -56
  12. moai_adk/cli/spec_status.py +263 -0
  13. moai_adk/cli/ui/__init__.py +44 -0
  14. moai_adk/cli/ui/progress.py +422 -0
  15. moai_adk/cli/ui/prompts.py +389 -0
  16. moai_adk/cli/ui/theme.py +129 -0
  17. moai_adk/cli/worktree/__init__.py +27 -0
  18. moai_adk/cli/worktree/__main__.py +31 -0
  19. moai_adk/cli/worktree/cli.py +672 -0
  20. moai_adk/cli/worktree/exceptions.py +89 -0
  21. moai_adk/cli/worktree/manager.py +490 -0
  22. moai_adk/cli/worktree/models.py +65 -0
  23. moai_adk/cli/worktree/registry.py +128 -0
  24. moai_adk/core/PHASE2_OPTIMIZATIONS.md +467 -0
  25. moai_adk/core/analysis/session_analyzer.py +17 -56
  26. moai_adk/core/claude_integration.py +26 -54
  27. moai_adk/core/command_helpers.py +10 -10
  28. moai_adk/core/comprehensive_monitoring_system.py +1183 -0
  29. moai_adk/core/config/auto_spec_config.py +5 -11
  30. moai_adk/core/config/migration.py +19 -9
  31. moai_adk/core/config/unified.py +436 -0
  32. moai_adk/core/context_manager.py +6 -12
  33. moai_adk/core/enterprise_features.py +1404 -0
  34. moai_adk/core/error_recovery_system.py +725 -112
  35. moai_adk/core/event_driven_hook_system.py +1371 -0
  36. moai_adk/core/git/__init__.py +8 -0
  37. moai_adk/core/git/branch_manager.py +3 -11
  38. moai_adk/core/git/checkpoint.py +1 -3
  39. moai_adk/core/git/conflict_detector.py +413 -0
  40. moai_adk/core/git/manager.py +91 -1
  41. moai_adk/core/hooks/post_tool_auto_spec_completion.py +56 -80
  42. moai_adk/core/input_validation_middleware.py +1006 -0
  43. moai_adk/core/integration/engine.py +6 -18
  44. moai_adk/core/integration/integration_tester.py +10 -9
  45. moai_adk/core/integration/utils.py +1 -1
  46. moai_adk/core/issue_creator.py +10 -28
  47. moai_adk/core/jit_context_loader.py +956 -0
  48. moai_adk/core/jit_enhanced_hook_manager.py +1987 -0
  49. moai_adk/core/language_config_resolver.py +485 -0
  50. moai_adk/core/language_validator.py +28 -41
  51. moai_adk/core/mcp/setup.py +15 -12
  52. moai_adk/core/merge/__init__.py +9 -0
  53. moai_adk/core/merge/analyzer.py +481 -0
  54. moai_adk/core/migration/alfred_to_moai_migrator.py +383 -0
  55. moai_adk/core/migration/backup_manager.py +78 -9
  56. moai_adk/core/migration/custom_element_scanner.py +358 -0
  57. moai_adk/core/migration/file_migrator.py +8 -17
  58. moai_adk/core/migration/interactive_checkbox_ui.py +488 -0
  59. moai_adk/core/migration/selective_restorer.py +470 -0
  60. moai_adk/core/migration/template_utils.py +74 -0
  61. moai_adk/core/migration/user_selection_ui.py +338 -0
  62. moai_adk/core/migration/version_detector.py +6 -10
  63. moai_adk/core/migration/version_migrator.py +3 -3
  64. moai_adk/core/performance/cache_system.py +8 -10
  65. moai_adk/core/phase_optimized_hook_scheduler.py +879 -0
  66. moai_adk/core/project/checker.py +2 -4
  67. moai_adk/core/project/detector.py +1 -3
  68. moai_adk/core/project/initializer.py +135 -23
  69. moai_adk/core/project/phase_executor.py +54 -81
  70. moai_adk/core/project/validator.py +6 -12
  71. moai_adk/core/quality/trust_checker.py +9 -27
  72. moai_adk/core/realtime_monitoring_dashboard.py +1724 -0
  73. moai_adk/core/robust_json_parser.py +611 -0
  74. moai_adk/core/rollback_manager.py +73 -148
  75. moai_adk/core/session_manager.py +10 -26
  76. moai_adk/core/skill_loading_system.py +579 -0
  77. moai_adk/core/spec/confidence_scoring.py +31 -100
  78. moai_adk/core/spec/ears_template_engine.py +351 -286
  79. moai_adk/core/spec/quality_validator.py +35 -69
  80. moai_adk/core/spec_status_manager.py +64 -74
  81. moai_adk/core/template/backup.py +45 -20
  82. moai_adk/core/template/config.py +112 -39
  83. moai_adk/core/template/merger.py +11 -19
  84. moai_adk/core/template/processor.py +253 -149
  85. moai_adk/core/template_engine.py +73 -40
  86. moai_adk/core/template_variable_synchronizer.py +417 -0
  87. moai_adk/core/unified_permission_manager.py +745 -0
  88. moai_adk/core/user_behavior_analytics.py +851 -0
  89. moai_adk/core/version_sync.py +429 -0
  90. moai_adk/foundation/__init__.py +56 -0
  91. moai_adk/foundation/backend.py +1027 -0
  92. moai_adk/foundation/database.py +1115 -0
  93. moai_adk/foundation/devops.py +1585 -0
  94. moai_adk/foundation/ears.py +431 -0
  95. moai_adk/foundation/frontend.py +870 -0
  96. moai_adk/foundation/git/commit_templates.py +4 -12
  97. moai_adk/foundation/git.py +376 -0
  98. moai_adk/foundation/langs.py +484 -0
  99. moai_adk/foundation/ml_ops.py +1162 -0
  100. moai_adk/foundation/testing.py +1524 -0
  101. moai_adk/foundation/trust/trust_principles.py +23 -72
  102. moai_adk/foundation/trust/validation_checklist.py +57 -162
  103. moai_adk/project/__init__.py +0 -0
  104. moai_adk/project/configuration.py +1084 -0
  105. moai_adk/project/documentation.py +566 -0
  106. moai_adk/project/schema.py +447 -0
  107. moai_adk/statusline/alfred_detector.py +1 -3
  108. moai_adk/statusline/config.py +13 -4
  109. moai_adk/statusline/enhanced_output_style_detector.py +23 -15
  110. moai_adk/statusline/main.py +51 -15
  111. moai_adk/statusline/renderer.py +104 -48
  112. moai_adk/statusline/update_checker.py +3 -9
  113. moai_adk/statusline/version_reader.py +140 -46
  114. moai_adk/templates/.claude/agents/moai/ai-nano-banana.md +549 -0
  115. moai_adk/templates/.claude/agents/moai/builder-agent.md +445 -0
  116. moai_adk/templates/.claude/agents/moai/builder-command.md +1132 -0
  117. moai_adk/templates/.claude/agents/moai/builder-skill.md +601 -0
  118. moai_adk/templates/.claude/agents/moai/expert-backend.md +831 -0
  119. moai_adk/templates/.claude/agents/moai/expert-database.md +774 -0
  120. moai_adk/templates/.claude/agents/moai/expert-debug.md +396 -0
  121. moai_adk/templates/.claude/agents/moai/expert-devops.md +711 -0
  122. moai_adk/templates/.claude/agents/moai/expert-frontend.md +666 -0
  123. moai_adk/templates/.claude/agents/moai/expert-security.md +474 -0
  124. moai_adk/templates/.claude/agents/moai/expert-uiux.md +1038 -0
  125. moai_adk/templates/.claude/agents/moai/manager-claude-code.md +429 -0
  126. moai_adk/templates/.claude/agents/moai/manager-docs.md +570 -0
  127. moai_adk/templates/.claude/agents/moai/manager-git.md +937 -0
  128. moai_adk/templates/.claude/agents/moai/manager-project.md +891 -0
  129. moai_adk/templates/.claude/agents/moai/manager-quality.md +598 -0
  130. moai_adk/templates/.claude/agents/moai/manager-spec.md +713 -0
  131. moai_adk/templates/.claude/agents/moai/manager-strategy.md +600 -0
  132. moai_adk/templates/.claude/agents/moai/manager-tdd.md +603 -0
  133. moai_adk/templates/.claude/agents/moai/mcp-context7.md +369 -0
  134. moai_adk/templates/.claude/agents/moai/mcp-figma.md +1567 -0
  135. moai_adk/templates/.claude/agents/moai/mcp-notion.md +749 -0
  136. moai_adk/templates/.claude/agents/moai/mcp-playwright.md +427 -0
  137. moai_adk/templates/.claude/agents/moai/mcp-sequential-thinking.md +994 -0
  138. moai_adk/templates/.claude/commands/moai/0-project.md +1143 -0
  139. moai_adk/templates/.claude/commands/moai/1-plan.md +1435 -0
  140. moai_adk/templates/.claude/commands/moai/2-run.md +883 -0
  141. moai_adk/templates/.claude/commands/moai/3-sync.md +993 -0
  142. moai_adk/templates/.claude/commands/moai/9-feedback.md +314 -0
  143. moai_adk/templates/.claude/hooks/__init__.py +8 -0
  144. moai_adk/templates/.claude/hooks/moai/__init__.py +8 -0
  145. moai_adk/templates/.claude/hooks/moai/lib/__init__.py +85 -0
  146. moai_adk/templates/.claude/hooks/moai/lib/checkpoint.py +244 -0
  147. moai_adk/templates/.claude/hooks/moai/lib/common.py +131 -0
  148. moai_adk/templates/.claude/hooks/moai/lib/config_manager.py +446 -0
  149. moai_adk/templates/.claude/hooks/moai/lib/config_validator.py +639 -0
  150. moai_adk/templates/.claude/hooks/moai/lib/example_config.json +104 -0
  151. moai_adk/templates/.claude/hooks/moai/lib/git_operations_manager.py +590 -0
  152. moai_adk/templates/.claude/hooks/moai/lib/language_validator.py +317 -0
  153. moai_adk/templates/.claude/hooks/moai/lib/models.py +102 -0
  154. moai_adk/templates/.claude/hooks/moai/lib/path_utils.py +28 -0
  155. moai_adk/templates/.claude/hooks/moai/lib/project.py +768 -0
  156. moai_adk/templates/.claude/hooks/moai/lib/test_hooks_improvements.py +443 -0
  157. moai_adk/templates/.claude/hooks/moai/lib/timeout.py +160 -0
  158. moai_adk/templates/.claude/hooks/moai/lib/unified_timeout_manager.py +530 -0
  159. moai_adk/templates/.claude/hooks/moai/session_end__auto_cleanup.py +862 -0
  160. moai_adk/templates/.claude/hooks/moai/session_start__show_project_info.py +921 -0
  161. moai_adk/templates/.claude/output-styles/moai/r2d2.md +380 -0
  162. moai_adk/templates/.claude/output-styles/moai/yoda.md +338 -0
  163. moai_adk/templates/.claude/settings.json +172 -0
  164. moai_adk/templates/.claude/skills/moai-docs-generation/SKILL.md +247 -0
  165. moai_adk/templates/.claude/skills/moai-docs-generation/modules/README.md +44 -0
  166. moai_adk/templates/.claude/skills/moai-docs-generation/modules/api-documentation.md +130 -0
  167. moai_adk/templates/.claude/skills/moai-docs-generation/modules/code-documentation.md +152 -0
  168. moai_adk/templates/.claude/skills/moai-docs-generation/modules/multi-format-output.md +178 -0
  169. moai_adk/templates/.claude/skills/moai-docs-generation/modules/user-guides.md +147 -0
  170. moai_adk/templates/.claude/skills/moai-domain-backend/SKILL.md +319 -0
  171. moai_adk/templates/.claude/skills/moai-domain-database/SKILL.md +320 -0
  172. moai_adk/templates/.claude/skills/moai-domain-database/modules/README.md +53 -0
  173. moai_adk/templates/.claude/skills/moai-domain-database/modules/mongodb.md +231 -0
  174. moai_adk/templates/.claude/skills/moai-domain-database/modules/postgresql.md +169 -0
  175. moai_adk/templates/.claude/skills/moai-domain-database/modules/redis.md +262 -0
  176. moai_adk/templates/.claude/skills/moai-domain-frontend/SKILL.md +496 -0
  177. moai_adk/templates/.claude/skills/moai-domain-uiux/SKILL.md +453 -0
  178. moai_adk/templates/.claude/skills/moai-domain-uiux/examples.md +560 -0
  179. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/accessibility-wcag.md +260 -0
  180. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/component-architecture.md +228 -0
  181. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/design-system-tokens.md +405 -0
  182. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/icon-libraries.md +401 -0
  183. moai_adk/templates/.claude/skills/moai-domain-uiux/modules/theming-system.md +373 -0
  184. moai_adk/templates/.claude/skills/moai-domain-uiux/reference.md +243 -0
  185. moai_adk/templates/.claude/skills/moai-formats-data/SKILL.md +491 -0
  186. moai_adk/templates/.claude/skills/moai-formats-data/modules/README.md +98 -0
  187. moai_adk/templates/.claude/skills/moai-formats-data/modules/SKILL-MODULARIZATION-TEMPLATE.md +278 -0
  188. moai_adk/templates/.claude/skills/moai-formats-data/modules/caching-performance.md +459 -0
  189. moai_adk/templates/.claude/skills/moai-formats-data/modules/data-validation.md +485 -0
  190. moai_adk/templates/.claude/skills/moai-formats-data/modules/json-optimization.md +374 -0
  191. moai_adk/templates/.claude/skills/moai-formats-data/modules/toon-encoding.md +308 -0
  192. moai_adk/templates/.claude/skills/moai-foundation-claude/SKILL.md +201 -0
  193. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/best-practices-checklist.md +616 -0
  194. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-custom-slash-commands-official.md +729 -0
  195. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-hooks-official.md +560 -0
  196. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-iam-official.md +635 -0
  197. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-memory-official.md +543 -0
  198. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-settings-official.md +663 -0
  199. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-skills-official.md +113 -0
  200. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/claude-code-sub-agents-official.md +238 -0
  201. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/complete-configuration-guide.md +175 -0
  202. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-examples.md +1674 -0
  203. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/skill-formatting-guide.md +729 -0
  204. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-examples.md +1513 -0
  205. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-formatting-guide.md +1086 -0
  206. moai_adk/templates/.claude/skills/moai-foundation-claude/reference/sub-agents/sub-agent-integration-patterns.md +1100 -0
  207. moai_adk/templates/.claude/skills/moai-foundation-context/SKILL.md +438 -0
  208. moai_adk/templates/.claude/skills/moai-foundation-core/SKILL.md +515 -0
  209. moai_adk/templates/.claude/skills/moai-foundation-core/modules/README.md +296 -0
  210. moai_adk/templates/.claude/skills/moai-foundation-core/modules/agents-reference.md +346 -0
  211. moai_adk/templates/.claude/skills/moai-foundation-core/modules/commands-reference.md +432 -0
  212. moai_adk/templates/.claude/skills/moai-foundation-core/modules/delegation-patterns.md +757 -0
  213. moai_adk/templates/.claude/skills/moai-foundation-core/modules/execution-rules.md +687 -0
  214. moai_adk/templates/.claude/skills/moai-foundation-core/modules/modular-system.md +665 -0
  215. moai_adk/templates/.claude/skills/moai-foundation-core/modules/progressive-disclosure.md +649 -0
  216. moai_adk/templates/.claude/skills/moai-foundation-core/modules/spec-first-tdd.md +864 -0
  217. moai_adk/templates/.claude/skills/moai-foundation-core/modules/token-optimization.md +708 -0
  218. moai_adk/templates/.claude/skills/moai-foundation-core/modules/trust-5-framework.md +981 -0
  219. moai_adk/templates/.claude/skills/moai-foundation-quality/SKILL.md +362 -0
  220. moai_adk/templates/.claude/skills/moai-foundation-quality/examples.md +1232 -0
  221. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/best-practices.md +261 -0
  222. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/integration-patterns.md +194 -0
  223. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/proactive-analysis.md +229 -0
  224. moai_adk/templates/.claude/skills/moai-foundation-quality/modules/trust5-validation.md +169 -0
  225. moai_adk/templates/.claude/skills/moai-foundation-quality/reference.md +1266 -0
  226. moai_adk/templates/.claude/skills/moai-foundation-quality/scripts/quality-gate.sh +668 -0
  227. moai_adk/templates/.claude/skills/moai-foundation-quality/templates/github-actions-quality.yml +481 -0
  228. moai_adk/templates/.claude/skills/moai-foundation-quality/templates/quality-config.yaml +519 -0
  229. moai_adk/templates/.claude/skills/moai-integration-mcp/SKILL.md +352 -0
  230. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/README.md +52 -0
  231. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/error-handling.md +334 -0
  232. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/integration-patterns.md +310 -0
  233. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/security-authentication.md +256 -0
  234. moai_adk/templates/.claude/skills/moai-integration-mcp/modules/server-architecture.md +253 -0
  235. moai_adk/templates/.claude/skills/moai-lang-unified/README.md +133 -0
  236. moai_adk/templates/.claude/skills/moai-lang-unified/SKILL.md +296 -0
  237. moai_adk/templates/.claude/skills/moai-lang-unified/examples.md +1269 -0
  238. moai_adk/templates/.claude/skills/moai-lang-unified/reference.md +331 -0
  239. moai_adk/templates/.claude/skills/moai-library-mermaid/SKILL.md +298 -0
  240. moai_adk/templates/.claude/skills/moai-library-mermaid/advanced-patterns.md +465 -0
  241. moai_adk/templates/.claude/skills/moai-library-mermaid/examples.md +270 -0
  242. moai_adk/templates/.claude/skills/moai-library-mermaid/optimization.md +440 -0
  243. moai_adk/templates/.claude/skills/moai-library-mermaid/reference.md +228 -0
  244. moai_adk/templates/.claude/skills/moai-library-nextra/SKILL.md +316 -0
  245. moai_adk/templates/.claude/skills/moai-library-nextra/advanced-patterns.md +336 -0
  246. moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-deployment-patterns.md +182 -0
  247. moai_adk/templates/.claude/skills/moai-library-nextra/modules/advanced-patterns.md +17 -0
  248. moai_adk/templates/.claude/skills/moai-library-nextra/modules/configuration.md +57 -0
  249. moai_adk/templates/.claude/skills/moai-library-nextra/modules/content-architecture-optimization.md +162 -0
  250. moai_adk/templates/.claude/skills/moai-library-nextra/modules/deployment.md +52 -0
  251. moai_adk/templates/.claude/skills/moai-library-nextra/modules/framework-core-configuration.md +186 -0
  252. moai_adk/templates/.claude/skills/moai-library-nextra/modules/i18n-setup.md +55 -0
  253. moai_adk/templates/.claude/skills/moai-library-nextra/modules/mdx-components.md +52 -0
  254. moai_adk/templates/.claude/skills/moai-library-nextra/optimization.md +303 -0
  255. moai_adk/templates/.claude/skills/moai-library-shadcn/SKILL.md +370 -0
  256. moai_adk/templates/.claude/skills/moai-library-shadcn/examples.md +575 -0
  257. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/advanced-patterns.md +394 -0
  258. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/optimization.md +278 -0
  259. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-components.md +457 -0
  260. moai_adk/templates/.claude/skills/moai-library-shadcn/modules/shadcn-theming.md +373 -0
  261. moai_adk/templates/.claude/skills/moai-library-shadcn/reference.md +74 -0
  262. moai_adk/templates/.claude/skills/moai-platform-baas/README.md +186 -0
  263. moai_adk/templates/.claude/skills/moai-platform-baas/SKILL.md +290 -0
  264. moai_adk/templates/.claude/skills/moai-platform-baas/examples.md +1225 -0
  265. moai_adk/templates/.claude/skills/moai-platform-baas/reference.md +567 -0
  266. moai_adk/templates/.claude/skills/moai-platform-baas/scripts/provider-selector.py +323 -0
  267. moai_adk/templates/.claude/skills/moai-platform-baas/templates/stack-config.yaml +204 -0
  268. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/SKILL.md +446 -0
  269. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/advanced-patterns.md +379 -0
  270. moai_adk/templates/.claude/skills/moai-workflow-jit-docs/optimization.md +286 -0
  271. moai_adk/templates/.claude/skills/moai-workflow-project/README.md +190 -0
  272. moai_adk/templates/.claude/skills/moai-workflow-project/SKILL.md +387 -0
  273. moai_adk/templates/.claude/skills/moai-workflow-project/__init__.py +520 -0
  274. moai_adk/templates/.claude/skills/moai-workflow-project/complete_workflow_demo_fixed.py +574 -0
  275. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_project_setup.py +317 -0
  276. moai_adk/templates/.claude/skills/moai-workflow-project/examples/complete_workflow_demo.py +663 -0
  277. moai_adk/templates/.claude/skills/moai-workflow-project/examples/config-migration-example.json +190 -0
  278. moai_adk/templates/.claude/skills/moai-workflow-project/examples/question-examples.json +135 -0
  279. moai_adk/templates/.claude/skills/moai-workflow-project/examples/quick_start.py +196 -0
  280. moai_adk/templates/.claude/skills/moai-workflow-project/modules/__init__.py +17 -0
  281. moai_adk/templates/.claude/skills/moai-workflow-project/modules/advanced-patterns.md +158 -0
  282. moai_adk/templates/.claude/skills/moai-workflow-project/modules/ask_user_integration.py +340 -0
  283. moai_adk/templates/.claude/skills/moai-workflow-project/modules/batch_questions.py +713 -0
  284. moai_adk/templates/.claude/skills/moai-workflow-project/modules/config_manager.py +538 -0
  285. moai_adk/templates/.claude/skills/moai-workflow-project/modules/documentation_manager.py +1336 -0
  286. moai_adk/templates/.claude/skills/moai-workflow-project/modules/language_initializer.py +730 -0
  287. moai_adk/templates/.claude/skills/moai-workflow-project/modules/migration_manager.py +608 -0
  288. moai_adk/templates/.claude/skills/moai-workflow-project/modules/template_optimizer.py +1005 -0
  289. moai_adk/templates/.claude/skills/moai-workflow-project/schemas/config-schema.json +316 -0
  290. moai_adk/templates/.claude/skills/moai-workflow-project/schemas/tab_schema.json +1362 -0
  291. moai_adk/templates/.claude/skills/moai-workflow-project/templates/config-template.json +71 -0
  292. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/product-template.md +44 -0
  293. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/structure-template.md +48 -0
  294. moai_adk/templates/.claude/skills/moai-workflow-project/templates/doc-templates/tech-template.md +71 -0
  295. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/config-manager-setup.json +109 -0
  296. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/language-initializer.json +228 -0
  297. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/menu-project-config.json +130 -0
  298. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/project-batch-questions.json +97 -0
  299. moai_adk/templates/.claude/skills/moai-workflow-project/templates/question-templates/spec-workflow-setup.json +150 -0
  300. moai_adk/templates/.claude/skills/moai-workflow-project/test_integration_simple.py +436 -0
  301. moai_adk/templates/.claude/skills/moai-workflow-templates/SKILL.md +374 -0
  302. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/code-templates.md +124 -0
  303. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/feedback-templates.md +100 -0
  304. moai_adk/templates/.claude/skills/moai-workflow-templates/modules/template-optimizer.md +138 -0
  305. moai_adk/templates/.claude/skills/moai-workflow-testing/LICENSE.txt +202 -0
  306. moai_adk/templates/.claude/skills/moai-workflow-testing/SKILL.md +453 -0
  307. moai_adk/templates/.claude/skills/moai-workflow-testing/advanced-patterns.md +576 -0
  308. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/ai-powered-testing.py +294 -0
  309. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/console_logging.py +35 -0
  310. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/element_discovery.py +40 -0
  311. moai_adk/templates/.claude/skills/moai-workflow-testing/examples/static_html_automation.py +34 -0
  312. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/README.md +220 -0
  313. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/ai-debugging.md +845 -0
  314. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/automated-code-review.md +1416 -0
  315. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/performance-optimization.md +1234 -0
  316. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/smart-refactoring.md +1243 -0
  317. moai_adk/templates/.claude/skills/moai-workflow-testing/modules/tdd-context7.md +1260 -0
  318. moai_adk/templates/.claude/skills/moai-workflow-testing/optimization.md +505 -0
  319. moai_adk/templates/.claude/skills/moai-workflow-testing/reference/playwright-best-practices.md +57 -0
  320. moai_adk/templates/.claude/skills/moai-workflow-testing/scripts/with_server.py +218 -0
  321. moai_adk/templates/.claude/skills/moai-workflow-testing/templates/alfred-integration.md +376 -0
  322. moai_adk/templates/.claude/skills/moai-workflow-testing/workflows/enterprise-testing-workflow.py +571 -0
  323. moai_adk/templates/.claude/skills/moai-worktree/SKILL.md +410 -0
  324. moai_adk/templates/.claude/skills/moai-worktree/examples.md +606 -0
  325. moai_adk/templates/.claude/skills/moai-worktree/modules/integration-patterns.md +982 -0
  326. moai_adk/templates/.claude/skills/moai-worktree/modules/parallel-development.md +778 -0
  327. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-commands.md +646 -0
  328. moai_adk/templates/.claude/skills/moai-worktree/modules/worktree-management.md +782 -0
  329. moai_adk/templates/.claude/skills/moai-worktree/reference.md +357 -0
  330. moai_adk/templates/.git-hooks/pre-commit +103 -41
  331. moai_adk/templates/.git-hooks/pre-push +116 -21
  332. moai_adk/templates/.github/workflows/ci-universal.yml +513 -0
  333. moai_adk/templates/.github/workflows/security-secrets-check.yml +179 -0
  334. moai_adk/templates/.gitignore +184 -44
  335. moai_adk/templates/.mcp.json +7 -9
  336. moai_adk/templates/.moai/cache/personalization.json +10 -0
  337. moai_adk/templates/.moai/config/config.yaml +344 -0
  338. moai_adk/templates/.moai/config/presets/manual.yaml +28 -0
  339. moai_adk/templates/.moai/config/presets/personal.yaml +30 -0
  340. moai_adk/templates/.moai/config/presets/team.yaml +33 -0
  341. moai_adk/templates/.moai/config/questions/_schema.yaml +79 -0
  342. moai_adk/templates/.moai/config/questions/tab1-user.yaml +108 -0
  343. moai_adk/templates/.moai/config/questions/tab2-project.yaml +122 -0
  344. moai_adk/templates/.moai/config/questions/tab3-git.yaml +542 -0
  345. moai_adk/templates/.moai/config/questions/tab4-quality.yaml +167 -0
  346. moai_adk/templates/.moai/config/questions/tab5-system.yaml +152 -0
  347. moai_adk/templates/.moai/config/sections/git-strategy.yaml +40 -0
  348. moai_adk/templates/.moai/config/sections/language.yaml +11 -0
  349. moai_adk/templates/.moai/config/sections/project.yaml +13 -0
  350. moai_adk/templates/.moai/config/sections/quality.yaml +15 -0
  351. moai_adk/templates/.moai/config/sections/system.yaml +14 -0
  352. moai_adk/templates/.moai/config/sections/user.yaml +5 -0
  353. moai_adk/templates/.moai/config/statusline-config.yaml +86 -0
  354. moai_adk/templates/.moai/scripts/setup-glm.py +136 -0
  355. moai_adk/templates/CLAUDE.md +382 -501
  356. moai_adk/utils/__init__.py +24 -1
  357. moai_adk/utils/banner.py +7 -10
  358. moai_adk/utils/common.py +16 -30
  359. moai_adk/utils/link_validator.py +4 -12
  360. moai_adk/utils/safe_file_reader.py +2 -6
  361. moai_adk/utils/timeout.py +160 -0
  362. moai_adk/utils/toon_utils.py +256 -0
  363. moai_adk/version.py +22 -0
  364. moai_adk-0.32.8.dist-info/METADATA +2478 -0
  365. moai_adk-0.32.8.dist-info/RECORD +396 -0
  366. {moai_adk-0.25.4.dist-info โ†’ moai_adk-0.32.8.dist-info}/WHEEL +1 -1
  367. {moai_adk-0.25.4.dist-info โ†’ moai_adk-0.32.8.dist-info}/entry_points.txt +1 -0
  368. moai_adk/cli/commands/backup.py +0 -82
  369. moai_adk/cli/commands/improve_user_experience.py +0 -348
  370. moai_adk/cli/commands/migrate.py +0 -158
  371. moai_adk/cli/commands/validate_links.py +0 -118
  372. moai_adk/templates/.github/workflows/moai-gitflow.yml +0 -413
  373. moai_adk/templates/.github/workflows/moai-release-create.yml +0 -100
  374. moai_adk/templates/.github/workflows/moai-release-pipeline.yml +0 -188
  375. moai_adk/utils/user_experience.py +0 -531
  376. moai_adk-0.25.4.dist-info/METADATA +0 -2279
  377. moai_adk-0.25.4.dist-info/RECORD +0 -112
  378. {moai_adk-0.25.4.dist-info โ†’ moai_adk-0.32.8.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1987 @@
1
+ """
2
+ JIT-Enhanced Hook Manager
3
+
4
+ Integrates Phase 2 JIT Context Loading System with Claude Code hook infrastructure
5
+ to provide intelligent, phase-aware hook execution with optimal performance.
6
+
7
+ Key Features:
8
+ - Phase-based hook optimization
9
+ - JIT context loading for hooks
10
+ - Intelligent skill filtering for hook operations
11
+ - Dynamic token budget management
12
+ - Real-time performance monitoring
13
+ - Smart caching and invalidation
14
+ """
15
+
16
+ import asyncio
17
+ import inspect
18
+ import json
19
+ import logging
20
+ import threading
21
+ import time
22
+ from collections import defaultdict
23
+ from dataclasses import dataclass, field
24
+ from datetime import datetime, timedelta
25
+ from enum import Enum
26
+ from pathlib import Path
27
+ from typing import Any, Callable, Dict, List, Optional, Set, Tuple
28
+
29
+ # Import JIT Context Loading System from Phase 2
30
+ try:
31
+ from .jit_context_loader import (
32
+ ContextCache as _ImportedContextCache,
33
+ )
34
+ from .jit_context_loader import (
35
+ JITContextLoader as _ImportedJITContextLoader,
36
+ )
37
+ from .jit_context_loader import (
38
+ Phase as _ImportedPhase,
39
+ )
40
+ from .jit_context_loader import (
41
+ TokenBudgetManager as _ImportedTokenBudgetManager,
42
+ )
43
+
44
+ JITContextLoader = _ImportedJITContextLoader
45
+ ContextCache = _ImportedContextCache
46
+ TokenBudgetManager = _ImportedTokenBudgetManager
47
+ Phase = _ImportedPhase
48
+ _JIT_AVAILABLE = True
49
+ except ImportError:
50
+ _JIT_AVAILABLE = False
51
+
52
+ # Fallback for environments where JIT system might not be available
53
+ class JITContextLoader: # type: ignore[no-redef]
54
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
55
+ pass
56
+
57
+ class ContextCache: # type: ignore[no-redef]
58
+ def __init__(self, max_size: int = 100, max_memory_mb: int = 50) -> None:
59
+ self.max_size = max_size
60
+ self.max_memory_mb = max_memory_mb
61
+ self.hits = 0
62
+ self.misses = 0
63
+ self.cache: dict[Any, Any] = {}
64
+
65
+ def get(self, key: Any) -> Any:
66
+ self.misses += 1
67
+ return None
68
+
69
+ def put(self, key: Any, value: Any, token_count: int = 0) -> None:
70
+ pass
71
+
72
+ def clear(self) -> None:
73
+ pass
74
+
75
+ def get_stats(self) -> dict[str, Any]:
76
+ return {"hits": self.hits, "misses": self.misses}
77
+
78
+ class TokenBudgetManager: # type: ignore[no-redef]
79
+ def __init__(self, *args: Any, **kwargs: Any) -> None:
80
+ pass
81
+
82
+ # Create Phase enum for hook system (fallback)
83
+ class Phase(Enum): # type: ignore[no-redef]
84
+ SPEC = "SPEC"
85
+ RED = "RED"
86
+ GREEN = "GREEN"
87
+ REFACTOR = "REFACTOR"
88
+ SYNC = "SYNC"
89
+ DEBUG = "DEBUG"
90
+ PLANNING = "PLANNING"
91
+
92
+
93
+ class HookEvent(Enum):
94
+ """Hook event types from Claude Code"""
95
+
96
+ SESSION_START = "SessionStart"
97
+ SESSION_END = "SessionEnd"
98
+ USER_PROMPT_SUBMIT = "UserPromptSubmit"
99
+ PRE_TOOL_USE = "PreToolUse"
100
+ POST_TOOL_USE = "PostToolUse"
101
+ SUBAGENT_START = "SubagentStart"
102
+ SUBAGENT_STOP = "SubagentStop"
103
+
104
+
105
+ class HookPriority(Enum):
106
+ """Hook execution priority levels"""
107
+
108
+ CRITICAL = 1 # System-critical hooks (security, validation)
109
+ HIGH = 2 # High-impact hooks (performance optimization)
110
+ NORMAL = 3 # Standard hooks (logging, cleanup)
111
+ LOW = 4 # Optional hooks (analytics, metrics)
112
+
113
+
114
+ @dataclass
115
+ class HookMetadata:
116
+ """Metadata for a hook execution"""
117
+
118
+ hook_path: str
119
+ event_type: HookEvent
120
+ priority: HookPriority
121
+ estimated_execution_time_ms: float = 0.0
122
+ last_execution_time: Optional[datetime] = None
123
+ success_rate: float = 1.0
124
+ phase_relevance: Dict[Phase, float] = field(default_factory=dict)
125
+ token_cost_estimate: int = 0
126
+ dependencies: Set[str] = field(default_factory=set)
127
+ parallel_safe: bool = True
128
+
129
+
130
+ @dataclass
131
+ class HookExecutionResult:
132
+ """Result of hook execution"""
133
+
134
+ hook_path: str
135
+ success: bool
136
+ execution_time_ms: float
137
+ token_usage: int
138
+ output: Any
139
+ error_message: Optional[str] = None
140
+ metadata: Dict[str, Any] = field(default_factory=dict)
141
+
142
+
143
+ @dataclass
144
+ class CircuitBreakerState:
145
+ """Circuit breaker state for hook resilience"""
146
+
147
+ failure_count: int = 0
148
+ last_failure_time: Optional[datetime] = None
149
+ state: str = "CLOSED" # CLOSED, OPEN, HALF_OPEN
150
+ success_threshold: int = 5
151
+ failure_threshold: int = 3
152
+ timeout_seconds: int = 60
153
+
154
+
155
+ class CircuitBreaker:
156
+ """Circuit breaker pattern for failing hooks"""
157
+
158
+ def __init__(
159
+ self,
160
+ failure_threshold: int = 3,
161
+ timeout_seconds: int = 60,
162
+ success_threshold: int = 5,
163
+ ):
164
+ self.failure_threshold = failure_threshold
165
+ self.timeout_seconds = timeout_seconds
166
+ self.success_threshold = success_threshold
167
+ self.state = CircuitBreakerState(
168
+ failure_threshold=failure_threshold,
169
+ timeout_seconds=timeout_seconds,
170
+ success_threshold=success_threshold,
171
+ )
172
+
173
+ async def call(self, func: Callable, *args, **kwargs) -> Any:
174
+ """Execute function with circuit breaker protection"""
175
+ if self.state.state == "OPEN":
176
+ if self._should_attempt_reset():
177
+ self.state.state = "HALF_OPEN"
178
+ else:
179
+ raise Exception("Circuit breaker is OPEN - call blocked")
180
+
181
+ try:
182
+ result = await func(*args, **kwargs) if inspect.iscoroutinefunction(func) else func(*args, **kwargs)
183
+ self._on_success()
184
+ return result
185
+ except Exception as e:
186
+ self._on_failure()
187
+ raise e
188
+
189
+ def _should_attempt_reset(self) -> bool:
190
+ """Check if circuit breaker should attempt reset"""
191
+ if self.state.last_failure_time is None:
192
+ return False
193
+ return datetime.now() - self.state.last_failure_time > timedelta(seconds=self.timeout_seconds)
194
+
195
+ def _on_success(self) -> None:
196
+ """Handle successful call"""
197
+ self.state.failure_count = 0
198
+ if self.state.state == "HALF_OPEN":
199
+ self.state.success_threshold -= 1
200
+ if self.state.success_threshold <= 0:
201
+ self.state.state = "CLOSED"
202
+ self.state.success_threshold = 5
203
+
204
+ def _on_failure(self) -> None:
205
+ """Handle failed call"""
206
+ self.state.failure_count += 1
207
+ self.state.last_failure_time = datetime.now()
208
+
209
+ if self.state.failure_count >= self.failure_threshold:
210
+ self.state.state = "OPEN"
211
+
212
+
213
+ class HookResultCache:
214
+ """Advanced result caching with TTL and invalidation"""
215
+
216
+ def __init__(self, max_size: int = 1000, default_ttl_seconds: int = 300):
217
+ self.max_size = max_size
218
+ self.default_ttl_seconds = default_ttl_seconds
219
+ self._cache: Dict[str, Tuple[Any, datetime, int]] = {} # key -> (value, expiry, access_count)
220
+ self._access_times: Dict[str, datetime] = {}
221
+ self._lock = threading.RLock()
222
+
223
+ def get(self, key: str) -> Optional[Any]:
224
+ """Get cached value if valid"""
225
+ with self._lock:
226
+ if key not in self._cache:
227
+ return None
228
+
229
+ value, expiry, access_count = self._cache[key]
230
+
231
+ # Check TTL
232
+ if datetime.now() > expiry:
233
+ del self._cache[key]
234
+ if key in self._access_times:
235
+ del self._access_times[key]
236
+ return None
237
+
238
+ # Update access
239
+ self._cache[key] = (value, expiry, access_count + 1)
240
+ self._access_times[key] = datetime.now()
241
+
242
+ return value
243
+
244
+ def put(self, key: str, value: Any, ttl_seconds: Optional[int] = None) -> None:
245
+ """Cache value with TTL"""
246
+ with self._lock:
247
+ # Enforce size limit
248
+ if len(self._cache) >= self.max_size:
249
+ self._evict_lru()
250
+
251
+ ttl = ttl_seconds or self.default_ttl_seconds
252
+ expiry = datetime.now() + timedelta(seconds=ttl)
253
+ self._cache[key] = (value, expiry, 1)
254
+ self._access_times[key] = datetime.now()
255
+
256
+ def invalidate(self, pattern: Optional[str] = None) -> None:
257
+ """Invalidate cache entries"""
258
+ with self._lock:
259
+ if pattern is None:
260
+ self._cache.clear()
261
+ self._access_times.clear()
262
+ else:
263
+ keys_to_remove = [key for key in self._cache.keys() if pattern in key]
264
+ for key in keys_to_remove:
265
+ del self._cache[key]
266
+ if key in self._access_times:
267
+ del self._access_times[key]
268
+
269
+ def _evict_lru(self) -> None:
270
+ """Evict least recently used item"""
271
+ if not self._access_times:
272
+ return
273
+
274
+ lru_key = min(self._access_times.keys(), key=lambda k: self._access_times[k])
275
+ del self._cache[lru_key]
276
+ del self._access_times[lru_key]
277
+
278
+ def get_stats(self) -> Dict[str, Any]:
279
+ """Get cache statistics"""
280
+ with self._lock:
281
+ return {
282
+ "size": len(self._cache),
283
+ "max_size": self.max_size,
284
+ "utilization": len(self._cache) / self.max_size,
285
+ }
286
+
287
+
288
+ class ConnectionPool:
289
+ """Connection pooling for MCP servers and external resources"""
290
+
291
+ def __init__(self, max_connections: int = 10, connection_timeout_seconds: int = 30):
292
+ self.max_connections = max_connections
293
+ self.connection_timeout_seconds = connection_timeout_seconds
294
+ self._pools: Dict[str, List] = defaultdict(list)
295
+ self._active_connections: Dict[str, int] = defaultdict(int)
296
+ self._lock = threading.Lock()
297
+
298
+ async def get_connection(self, pool_name: str, connection_factory: Callable) -> Any:
299
+ """Get connection from pool or create new one"""
300
+ with self._lock:
301
+ # Check pool for available connection
302
+ pool = self._pools[pool_name]
303
+ if pool:
304
+ connection = pool.pop()
305
+ self._active_connections[pool_name] += 1
306
+ return connection
307
+
308
+ # Check if we can create new connection
309
+ if self._active_connections[pool_name] >= self.max_connections:
310
+ raise Exception(f"Connection pool '{pool_name}' is full")
311
+
312
+ self._active_connections[pool_name] += 1
313
+
314
+ # Create new connection outside of lock
315
+ try:
316
+ connection = (
317
+ await connection_factory() if inspect.iscoroutinefunction(connection_factory) else connection_factory()
318
+ )
319
+ return connection
320
+ except Exception:
321
+ with self._lock:
322
+ self._active_connections[pool_name] -= 1
323
+ raise
324
+
325
+ def return_connection(self, pool_name: str, connection: Any) -> None:
326
+ """Return connection to pool"""
327
+ with self._lock:
328
+ pool = self._pools[pool_name]
329
+ if len(pool) < self.max_connections and connection is not None:
330
+ pool.append(connection)
331
+ self._active_connections[pool_name] -= 1
332
+
333
+ def get_pool_stats(self) -> Dict[str, Any]:
334
+ """Get connection pool statistics"""
335
+ with self._lock:
336
+ return {
337
+ "pools": {
338
+ name: {
339
+ "available": len(pool),
340
+ "active": self._active_connections[name],
341
+ "total": len(pool) + self._active_connections[name],
342
+ }
343
+ for name, pool in self._pools.items()
344
+ }
345
+ }
346
+
347
+
348
+ class RetryPolicy:
349
+ """Exponential backoff retry policy"""
350
+
351
+ def __init__(
352
+ self,
353
+ max_retries: int = 3,
354
+ base_delay_ms: float = 100,
355
+ max_delay_ms: float = 5000,
356
+ backoff_factor: float = 2.0,
357
+ ):
358
+ self.max_retries = max_retries
359
+ self.base_delay_ms = base_delay_ms
360
+ self.max_delay_ms = max_delay_ms
361
+ self.backoff_factor = backoff_factor
362
+
363
+ async def execute_with_retry(self, func: Callable, *args, **kwargs) -> Any:
364
+ """Execute function with retry policy"""
365
+ last_exception = None
366
+
367
+ for attempt in range(self.max_retries + 1):
368
+ try:
369
+ result = await func(*args, **kwargs) if inspect.iscoroutinefunction(func) else func(*args, **kwargs)
370
+ return result
371
+ except Exception as e:
372
+ last_exception = e
373
+
374
+ if attempt < self.max_retries:
375
+ delay_ms = min(
376
+ self.base_delay_ms * (self.backoff_factor**attempt),
377
+ self.max_delay_ms,
378
+ )
379
+ await asyncio.sleep(delay_ms / 1000.0)
380
+ else:
381
+ break
382
+
383
+ raise last_exception
384
+
385
+
386
+ @dataclass
387
+ class ResourceUsageMetrics:
388
+ """Resource usage metrics for monitoring"""
389
+
390
+ cpu_usage_percent: float = 0.0
391
+ memory_usage_mb: float = 0.0
392
+ disk_io_mb: float = 0.0
393
+ network_io_mb: float = 0.0
394
+ open_files: int = 0
395
+ thread_count: int = 0
396
+
397
+
398
+ @dataclass
399
+ class HookPerformanceMetrics:
400
+ """Performance metrics for hook system"""
401
+
402
+ total_executions: int = 0
403
+ successful_executions: int = 0
404
+ average_execution_time_ms: float = 0.0
405
+ total_token_usage: int = 0
406
+ cache_hits: int = 0
407
+ cache_misses: int = 0
408
+ phase_distribution: Dict[Phase, int] = field(default_factory=dict)
409
+ event_type_distribution: Dict[HookEvent, int] = field(default_factory=dict)
410
+ circuit_breaker_trips: int = 0
411
+ retry_attempts: int = 0
412
+ resource_usage: ResourceUsageMetrics = field(default_factory=ResourceUsageMetrics)
413
+
414
+
415
+ class ResourceMonitor:
416
+ """Resource usage monitoring for hook system"""
417
+
418
+ def __init__(self):
419
+ self._baseline_metrics = self._get_current_metrics()
420
+ self._peak_usage = ResourceUsageMetrics()
421
+
422
+ def get_current_metrics(self) -> ResourceUsageMetrics:
423
+ """Get current resource usage metrics"""
424
+ import os
425
+
426
+ import psutil
427
+
428
+ try:
429
+ process = psutil.Process(os.getpid())
430
+
431
+ # Memory usage
432
+ memory_info = process.memory_info()
433
+ memory_mb = memory_info.rss / 1024 / 1024
434
+
435
+ # CPU usage (average across all cores)
436
+ cpu_percent = process.cpu_percent()
437
+
438
+ # Thread count
439
+ thread_count = process.num_threads()
440
+
441
+ # Open file descriptors
442
+ try:
443
+ open_files = process.num_fds()
444
+ except (AttributeError, psutil.AccessDenied):
445
+ open_files = 0
446
+
447
+ # Update peak usage
448
+ self._peak_usage.memory_usage_mb = max(self._peak_usage.memory_usage_mb, memory_mb)
449
+ self._peak_usage.cpu_usage_percent = max(self._peak_usage.cpu_usage_percent, cpu_percent)
450
+ self._peak_usage.thread_count = max(self._peak_usage.thread_count, thread_count)
451
+ self._peak_usage.open_files = max(self._peak_usage.open_files, open_files)
452
+
453
+ return ResourceUsageMetrics(
454
+ cpu_usage_percent=cpu_percent,
455
+ memory_usage_mb=memory_mb,
456
+ thread_count=thread_count,
457
+ open_files=open_files,
458
+ )
459
+ except Exception:
460
+ return ResourceUsageMetrics()
461
+
462
+ def _get_current_metrics(self) -> ResourceUsageMetrics:
463
+ """Get baseline metrics for comparison"""
464
+ return self.get_current_metrics()
465
+
466
+ def get_peak_metrics(self) -> ResourceUsageMetrics:
467
+ """Get peak resource usage metrics"""
468
+ return self._peak_usage
469
+
470
+
471
+ class HealthChecker:
472
+ """Health monitoring and check endpoints for hook system"""
473
+
474
+ def __init__(self, hook_manager: "JITEnhancedHookManager"):
475
+ self.hook_manager = hook_manager
476
+ self._last_health_check = datetime.now()
477
+ self._health_status = "healthy"
478
+
479
+ async def check_system_health(self) -> Dict[str, Any]:
480
+ """Perform comprehensive health check"""
481
+ checks: Dict[str, Dict[str, Any]] = {}
482
+ health_report: Dict[str, Any] = {
483
+ "status": "healthy",
484
+ "timestamp": datetime.now().isoformat(),
485
+ "checks": checks,
486
+ }
487
+
488
+ try:
489
+ # Check hook registry
490
+ checks["hook_registry"] = {
491
+ "status": ("healthy" if len(self.hook_manager._hook_registry) > 0 else "warning"),
492
+ "registered_hooks": len(self.hook_manager._hook_registry),
493
+ "events_supported": len(self.hook_manager._hooks_by_event),
494
+ }
495
+
496
+ # Check cache health
497
+ cache_stats = self.hook_manager._advanced_cache.get_stats()
498
+ checks["cache"] = {
499
+ "status": "healthy",
500
+ "size": cache_stats["size"],
501
+ "utilization": cache_stats["utilization"],
502
+ "max_size": cache_stats["max_size"],
503
+ }
504
+
505
+ # Check connection pools
506
+ pool_stats = self.hook_manager._connection_pool.get_pool_stats()
507
+ checks["connection_pools"] = {
508
+ "status": "healthy",
509
+ "pools": pool_stats["pools"],
510
+ }
511
+
512
+ # Check circuit breakers
513
+ tripped_breakers = [
514
+ name for name, cb in self.hook_manager._circuit_breakers.items() if cb.state.state == "OPEN"
515
+ ]
516
+
517
+ checks["circuit_breakers"] = {
518
+ "status": "healthy" if len(tripped_breakers) == 0 else "degraded",
519
+ "total_breakers": len(self.hook_manager._circuit_breakers),
520
+ "tripped_breakers": len(tripped_breakers),
521
+ "tripped_breaker_names": tripped_breakers,
522
+ }
523
+
524
+ # Check resource usage
525
+ resource_metrics = self.hook_manager._resource_monitor.get_current_metrics()
526
+ checks["resource_usage"] = {
527
+ "status": "healthy",
528
+ "memory_mb": resource_metrics.memory_usage_mb,
529
+ "cpu_percent": resource_metrics.cpu_usage_percent,
530
+ "thread_count": resource_metrics.thread_count,
531
+ }
532
+
533
+ # Overall status determination
534
+ statuses = [check["status"] for check in checks.values()]
535
+ if "unhealthy" in statuses:
536
+ health_report["status"] = "unhealthy"
537
+ elif "degraded" in statuses or "warning" in statuses:
538
+ health_report["status"] = "degraded"
539
+
540
+ self._health_status = health_report["status"]
541
+ self._last_health_check = datetime.now()
542
+
543
+ except Exception as e:
544
+ health_report["status"] = "unhealthy"
545
+ health_report["error"] = str(e)
546
+
547
+ return health_report
548
+
549
+ def get_health_status(self) -> str:
550
+ """Get current health status"""
551
+ return self._health_status
552
+
553
+
554
+ class PerformanceAnomalyDetector:
555
+ """Detect performance anomalies in hook execution"""
556
+
557
+ def __init__(self, sensitivity_factor: float = 2.0):
558
+ self.sensitivity_factor = sensitivity_factor
559
+ self._performance_history: Dict[str, List[float]] = defaultdict(list)
560
+
561
+ def detect_anomaly(self, hook_path: str, execution_time_ms: float) -> Optional[Dict[str, Any]]:
562
+ """Detect if execution time is anomalous"""
563
+ history = self._performance_history[hook_path]
564
+
565
+ if len(history) < 5:
566
+ # Not enough data for detection
567
+ self._performance_history[hook_path].append(execution_time_ms)
568
+ return None
569
+
570
+ # Calculate statistics
571
+ mean_time = sum(history) / len(history)
572
+ variance = sum((x - mean_time) ** 2 for x in history) / len(history)
573
+ std_dev = variance**0.5
574
+
575
+ # Check for anomaly
576
+ if abs(execution_time_ms - mean_time) > (self.sensitivity_factor * std_dev):
577
+ anomaly_type = "slow" if execution_time_ms > mean_time else "fast"
578
+ return {
579
+ "hook_path": hook_path,
580
+ "anomaly_type": anomaly_type,
581
+ "execution_time_ms": execution_time_ms,
582
+ "mean_time_ms": mean_time,
583
+ "std_dev_ms": std_dev,
584
+ "deviation_factor": abs(execution_time_ms - mean_time) / std_dev,
585
+ "severity": ("high" if abs(execution_time_ms - mean_time) > (3 * std_dev) else "medium"),
586
+ }
587
+
588
+ # Update history (keep last 50 entries)
589
+ history.append(execution_time_ms)
590
+ if len(history) > 50:
591
+ history.pop(0)
592
+
593
+ return None
594
+
595
+
596
+ class JITEnhancedHookManager:
597
+ """
598
+ Enhanced Hook Manager with JIT Context Loading System integration
599
+
600
+ Provides intelligent hook execution with phase-aware optimization,
601
+ token budget management, performance monitoring, and reliability patterns.
602
+ """
603
+
604
+ def __init__(
605
+ self,
606
+ hooks_directory: Optional[Path] = None,
607
+ cache_directory: Optional[Path] = None,
608
+ max_concurrent_hooks: int = 5,
609
+ enable_performance_monitoring: bool = True,
610
+ cache_ttl_seconds: int = 300,
611
+ circuit_breaker_threshold: int = 3,
612
+ max_retries: int = 3,
613
+ connection_pool_size: int = 10,
614
+ ):
615
+ """Initialize JIT-Enhanced Hook Manager with Phase 2 optimizations
616
+
617
+ Args:
618
+ hooks_directory: Directory containing hook files
619
+ cache_directory: Directory for hook cache and performance data
620
+ max_concurrent_hooks: Maximum number of hooks to execute concurrently
621
+ enable_performance_monitoring: Enable detailed performance tracking
622
+ cache_ttl_seconds: Default TTL for cached results
623
+ circuit_breaker_threshold: Failure threshold for circuit breaker
624
+ max_retries: Maximum retry attempts for failed hooks
625
+ connection_pool_size: Size of connection pool for external resources
626
+ """
627
+ self.hooks_directory = hooks_directory or Path.cwd() / ".claude" / "hooks"
628
+ self.cache_directory = cache_directory or Path.cwd() / ".moai" / "cache" / "hooks"
629
+ self.max_concurrent_hooks = max_concurrent_hooks
630
+ self.enable_performance_monitoring = enable_performance_monitoring
631
+
632
+ # Initialize JIT Context Loading System
633
+ self.jit_loader = JITContextLoader()
634
+
635
+ # Initialize Phase 2 optimizations
636
+ self._initialize_phase2_optimizations(
637
+ cache_ttl_seconds,
638
+ circuit_breaker_threshold,
639
+ max_retries,
640
+ connection_pool_size,
641
+ )
642
+
643
+ # Initialize caches and metadata storage
644
+ self._initialize_caches()
645
+
646
+ # Performance tracking
647
+ self.metrics = HookPerformanceMetrics()
648
+ self._performance_lock = threading.Lock()
649
+ self._resource_monitor = ResourceMonitor()
650
+
651
+ # Hook registry with metadata
652
+ self._hook_registry: Dict[str, HookMetadata] = {}
653
+ self._hooks_by_event: Dict[HookEvent, List[str]] = {}
654
+ self._circuit_breakers: Dict[str, CircuitBreaker] = {}
655
+ self._retry_policies: Dict[str, RetryPolicy] = {}
656
+
657
+ # Initialize hook registry
658
+ self._discover_hooks()
659
+
660
+ # Setup health monitoring
661
+ self._health_checker = HealthChecker(self)
662
+ self._logger = logging.getLogger(__name__)
663
+
664
+ def _initialize_phase2_optimizations(
665
+ self,
666
+ cache_ttl_seconds: int,
667
+ circuit_breaker_threshold: int,
668
+ max_retries: int,
669
+ connection_pool_size: int,
670
+ ) -> None:
671
+ """Initialize Phase 2 optimization components"""
672
+ # Advanced result cache with TTL
673
+ self._advanced_cache = HookResultCache(max_size=1000, default_ttl_seconds=cache_ttl_seconds)
674
+
675
+ # Connection pooling for MCP servers and external resources
676
+ self._connection_pool = ConnectionPool(max_connections=connection_pool_size)
677
+
678
+ # Circuit breaker and retry policies
679
+ self.circuit_breaker_threshold = circuit_breaker_threshold
680
+ self.max_retries = max_retries
681
+
682
+ # Performance profiling
683
+ self._execution_profiles: Dict[str, List[float]] = defaultdict(list)
684
+ self._anomaly_detector = PerformanceAnomalyDetector()
685
+
686
+ def _initialize_caches(self) -> None:
687
+ """Initialize cache directories and data structures"""
688
+ self.cache_directory.mkdir(parents=True, exist_ok=True)
689
+
690
+ # Initialize hook result cache
691
+ self._result_cache = ContextCache(max_size=100, max_memory_mb=50)
692
+
693
+ # Initialize metadata cache
694
+ self._metadata_cache: Dict[str, Dict[str, Any]] = {}
695
+
696
+ # Performance log file
697
+ self._performance_log_path = self.cache_directory / "performance.jsonl"
698
+
699
+ def _discover_hooks(self) -> None:
700
+ """Discover and register all available hooks"""
701
+ if not self.hooks_directory.exists():
702
+ return
703
+
704
+ for hook_file in self.hooks_directory.rglob("*.py"):
705
+ if hook_file.name.startswith("__") or hook_file.name.startswith("lib/"):
706
+ continue
707
+
708
+ hook_path_str = str(hook_file.relative_to(self.hooks_directory))
709
+
710
+ # Extract event type from filename
711
+ event_type = self._extract_event_type_from_filename(hook_file.name)
712
+ if event_type:
713
+ self._register_hook(hook_path_str, event_type)
714
+
715
+ def _extract_event_type_from_filename(self, filename: str) -> Optional[HookEvent]:
716
+ """Extract hook event type from filename pattern"""
717
+ filename_lower = filename.lower()
718
+
719
+ if "session_start" in filename_lower:
720
+ return HookEvent.SESSION_START
721
+ elif "session_end" in filename_lower:
722
+ return HookEvent.SESSION_END
723
+ elif "pre_tool" in filename_lower or "pretool" in filename_lower:
724
+ return HookEvent.PRE_TOOL_USE
725
+ elif "post_tool" in filename_lower or "posttool" in filename_lower:
726
+ return HookEvent.POST_TOOL_USE
727
+ elif "subagent_start" in filename_lower:
728
+ return HookEvent.SUBAGENT_START
729
+ elif "subagent_stop" in filename_lower:
730
+ return HookEvent.SUBAGENT_STOP
731
+ else:
732
+ return None
733
+
734
+ def _register_hook(self, hook_path: str, event_type: HookEvent) -> None:
735
+ """Register a hook with metadata"""
736
+ # Generate metadata based on hook characteristics
737
+ metadata = HookMetadata(
738
+ hook_path=hook_path,
739
+ event_type=event_type,
740
+ priority=self._determine_hook_priority(hook_path, event_type),
741
+ estimated_execution_time_ms=self._estimate_execution_time(hook_path),
742
+ phase_relevance=self._determine_phase_relevance(hook_path, event_type),
743
+ token_cost_estimate=self._estimate_token_cost(hook_path),
744
+ parallel_safe=self._is_parallel_safe(hook_path),
745
+ )
746
+
747
+ self._hook_registry[hook_path] = metadata
748
+
749
+ if event_type not in self._hooks_by_event:
750
+ self._hooks_by_event[event_type] = []
751
+ self._hooks_by_event[event_type].append(hook_path)
752
+
753
+ def _determine_hook_priority(self, hook_path: str, event_type: HookEvent) -> HookPriority:
754
+ """Determine hook priority based on its characteristics"""
755
+ filename = hook_path.lower()
756
+
757
+ # Security and validation hooks are critical
758
+ if any(keyword in filename for keyword in ["security", "validation", "health_check"]):
759
+ return HookPriority.CRITICAL
760
+
761
+ # Performance optimization hooks are high priority
762
+ if any(keyword in filename for keyword in ["performance", "optimizer", "jit"]):
763
+ return HookPriority.HIGH
764
+
765
+ # Cleanup and logging hooks are normal priority
766
+ if any(keyword in filename for keyword in ["cleanup", "log", "tracker"]):
767
+ return HookPriority.NORMAL
768
+
769
+ # Analytics and metrics are low priority
770
+ if any(keyword in filename for keyword in ["analytics", "metrics", "stats"]):
771
+ return HookPriority.LOW
772
+
773
+ # Default priority based on event type
774
+ if event_type == HookEvent.PRE_TOOL_USE:
775
+ return HookPriority.HIGH # Pre-execution validation is important
776
+ elif event_type == HookEvent.SESSION_START:
777
+ return HookPriority.NORMAL
778
+ else:
779
+ return HookPriority.NORMAL
780
+
781
+ def _estimate_execution_time(self, hook_path: str) -> float:
782
+ """Estimate hook execution time based on historical data and characteristics"""
783
+ # Check cache for historical execution time
784
+ cache_key = f"exec_time:{hook_path}"
785
+ if cache_key in self._metadata_cache:
786
+ cached_time = self._metadata_cache[cache_key].get("avg_time_ms")
787
+ if cached_time:
788
+ return cached_time
789
+
790
+ # Estimate based on hook characteristics
791
+ filename = hook_path.lower()
792
+
793
+ # Hooks with git operations tend to be slower
794
+ if "git" in filename:
795
+ return 200.0 # 200ms estimate for git operations
796
+
797
+ # Hooks with network operations are slower
798
+ if any(keyword in filename for keyword in ["fetch", "api", "network"]):
799
+ return 500.0 # 500ms estimate for network operations
800
+
801
+ # Hooks with file I/O are moderate
802
+ if any(keyword in filename for keyword in ["read", "write", "parse"]):
803
+ return 50.0 # 50ms estimate for file I/O
804
+
805
+ # Simple hooks are fast
806
+ return 10.0 # 10ms estimate for simple operations
807
+
808
+ def _determine_phase_relevance(self, hook_path: str, event_type: HookEvent) -> Dict[Phase, float]:
809
+ """Determine hook relevance to different development phases"""
810
+ filename = hook_path.lower()
811
+ relevance = {}
812
+
813
+ # Default relevance for all phases
814
+ default_relevance = 0.5
815
+
816
+ # SPEC phase relevance
817
+ if any(keyword in filename for keyword in ["spec", "plan", "design", "requirement"]):
818
+ relevance[Phase.SPEC] = 1.0
819
+ else:
820
+ relevance[Phase.SPEC] = default_relevance
821
+
822
+ # RED phase relevance (testing)
823
+ if any(keyword in filename for keyword in ["test", "red", "tdd", "assert"]):
824
+ relevance[Phase.RED] = 1.0
825
+ else:
826
+ relevance[Phase.RED] = default_relevance
827
+
828
+ # GREEN phase relevance (implementation)
829
+ if any(keyword in filename for keyword in ["implement", "code", "green", "build"]):
830
+ relevance[Phase.GREEN] = 1.0
831
+ else:
832
+ relevance[Phase.GREEN] = default_relevance
833
+
834
+ # REFACTOR phase relevance
835
+ if any(keyword in filename for keyword in ["refactor", "optimize", "improve", "clean"]):
836
+ relevance[Phase.REFACTOR] = 1.0
837
+ else:
838
+ relevance[Phase.REFACTOR] = default_relevance
839
+
840
+ # SYNC phase relevance (documentation)
841
+ if any(keyword in filename for keyword in ["sync", "doc", "document", "deploy"]):
842
+ relevance[Phase.SYNC] = 1.0
843
+ else:
844
+ relevance[Phase.SYNC] = default_relevance
845
+
846
+ # DEBUG phase relevance
847
+ if any(keyword in filename for keyword in ["debug", "error", "troubleshoot", "log"]):
848
+ relevance[Phase.DEBUG] = 1.0
849
+ else:
850
+ relevance[Phase.DEBUG] = default_relevance
851
+
852
+ # PLANNING phase relevance
853
+ if any(keyword in filename for keyword in ["plan", "analysis", "strategy"]):
854
+ relevance[Phase.PLANNING] = 1.0
855
+ else:
856
+ relevance[Phase.PLANNING] = default_relevance
857
+
858
+ return relevance
859
+
860
+ def _estimate_token_cost(self, hook_path: str) -> int:
861
+ """Estimate token cost for hook execution"""
862
+ # Base token cost for any hook
863
+ base_cost = 100
864
+
865
+ # Additional cost based on hook characteristics
866
+ filename = hook_path.lower()
867
+
868
+ if any(keyword in filename for keyword in ["analysis", "report", "generate"]):
869
+ base_cost += 500 # Higher cost for analysis/generation
870
+ elif any(keyword in filename for keyword in ["log", "simple", "basic"]):
871
+ base_cost += 50 # Lower cost for simple operations
872
+
873
+ return base_cost
874
+
875
+ def _is_parallel_safe(self, hook_path: str) -> bool:
876
+ """Determine if hook can be executed in parallel"""
877
+ filename = hook_path.lower()
878
+
879
+ # Hooks that modify shared state are not parallel safe
880
+ if any(keyword in filename for keyword in ["write", "modify", "update", "delete"]):
881
+ return False
882
+
883
+ # Hooks with external dependencies might not be parallel safe
884
+ if any(keyword in filename for keyword in ["database", "network", "api"]):
885
+ return False
886
+
887
+ # Most hooks are parallel safe by default
888
+ return True
889
+
890
+ async def execute_hooks(
891
+ self,
892
+ event_type: HookEvent,
893
+ context: Dict[str, Any],
894
+ user_input: Optional[str] = None,
895
+ phase: Optional[Phase] = None,
896
+ max_total_execution_time_ms: float = 15000.0,
897
+ ) -> List[HookExecutionResult]:
898
+ """Execute hooks for a specific event with JIT optimization
899
+
900
+ Args:
901
+ event_type: Type of hook event
902
+ context: Execution context data
903
+ user_input: User input for phase detection
904
+ phase: Current development phase (if known)
905
+ max_total_execution_time_ms: Maximum total execution time for all hooks
906
+
907
+ Returns:
908
+ List of hook execution results
909
+ """
910
+ start_time = time.time()
911
+
912
+ # Detect phase if not provided
913
+ if phase is None and user_input:
914
+ try:
915
+ phase = self.jit_loader.phase_detector.detect_phase(user_input)
916
+ except AttributeError:
917
+ # Fallback if JIT loader doesn't have phase detector
918
+ phase = Phase.SPEC
919
+
920
+ # Get relevant hooks for this event
921
+ hook_paths = self._hooks_by_event.get(event_type, [])
922
+
923
+ # Filter and prioritize hooks based on phase and performance
924
+ prioritized_hooks = self._prioritize_hooks(hook_paths, phase)
925
+
926
+ # Load optimized context using JIT system
927
+ optimized_context = await self._load_optimized_context(event_type, context, phase, prioritized_hooks)
928
+
929
+ # Execute hooks with optimization
930
+ results = await self._execute_hooks_optimized(prioritized_hooks, optimized_context, max_total_execution_time_ms)
931
+
932
+ # Update performance metrics
933
+ if self.enable_performance_monitoring:
934
+ self._update_performance_metrics(event_type, phase, results, start_time)
935
+
936
+ return results
937
+
938
+ def _prioritize_hooks(self, hook_paths: List[str], phase: Optional[Phase]) -> List[Tuple[str, float]]:
939
+ """Prioritize hooks based on phase relevance and performance characteristics
940
+
941
+ Args:
942
+ hook_paths: List of hook file paths
943
+ phase: Current development phase
944
+
945
+ Returns:
946
+ List of (hook_path, priority_score) tuples sorted by priority
947
+ """
948
+ hook_priorities = []
949
+
950
+ for hook_path in hook_paths:
951
+ metadata = self._hook_registry.get(hook_path)
952
+ if not metadata:
953
+ continue
954
+
955
+ # Calculate priority score
956
+ priority_score = 0.0
957
+
958
+ # Base priority (lower number = higher priority)
959
+ priority_score += metadata.priority.value * 10
960
+
961
+ # Phase relevance bonus
962
+ if phase and phase in metadata.phase_relevance:
963
+ relevance = metadata.phase_relevance[phase]
964
+ priority_score -= relevance * 5 # Higher relevance = lower score (higher priority)
965
+
966
+ # Performance penalty (slower hooks get lower priority)
967
+ priority_score += metadata.estimated_execution_time_ms / 100
968
+
969
+ # Success rate bonus (more reliable hooks get higher priority)
970
+ if metadata.success_rate < 0.9:
971
+ priority_score += 5 # Penalize unreliable hooks
972
+
973
+ hook_priorities.append((hook_path, priority_score))
974
+
975
+ # Sort by priority score (lower is better)
976
+ hook_priorities.sort(key=lambda x: x[1])
977
+
978
+ return hook_priorities
979
+
980
+ async def _load_optimized_context(
981
+ self,
982
+ event_type: HookEvent,
983
+ context: Dict[str, Any],
984
+ phase: Optional[Phase],
985
+ prioritized_hooks: List[Tuple[str, float]],
986
+ ) -> Dict[str, Any]:
987
+ """Load optimized context using JIT system for hook execution
988
+
989
+ Args:
990
+ event_type: Hook event type
991
+ context: Original context
992
+ phase: Current development phase
993
+ prioritized_hooks: List of prioritized hooks
994
+
995
+ Returns:
996
+ Optimized context with relevant information
997
+ """
998
+ # Create synthetic user input for context loading
999
+ synthetic_input = f"Hook execution for {event_type.value}"
1000
+ if phase:
1001
+ synthetic_input += f" during {phase.value} phase"
1002
+
1003
+ # Load context using JIT system
1004
+ try:
1005
+ jit_context, context_metrics = await self.jit_loader.load_context(
1006
+ user_input=synthetic_input, context=context
1007
+ )
1008
+ except (TypeError, AttributeError):
1009
+ # Fallback to basic context if JIT loader interface is different
1010
+ jit_context = context.copy()
1011
+
1012
+ # Add hook-specific context
1013
+ optimized_context = jit_context.copy()
1014
+ optimized_context.update(
1015
+ {
1016
+ "hook_event_type": event_type.value,
1017
+ "hook_phase": phase.value if phase else None,
1018
+ "hook_execution_mode": "optimized",
1019
+ "prioritized_hooks": [hook_path for hook_path, _ in prioritized_hooks[:5]], # Top 5 hooks
1020
+ }
1021
+ )
1022
+
1023
+ return optimized_context
1024
+
1025
+ async def _execute_hooks_optimized(
1026
+ self,
1027
+ prioritized_hooks: List[Tuple[str, float]],
1028
+ context: Dict[str, Any],
1029
+ max_total_execution_time_ms: float,
1030
+ ) -> List[HookExecutionResult]:
1031
+ """Execute hooks with optimization and time management
1032
+
1033
+ Args:
1034
+ prioritized_hooks: List of (hook_path, priority_score) tuples
1035
+ context: Optimized execution context
1036
+ max_total_execution_time_ms: Maximum total execution time
1037
+
1038
+ Returns:
1039
+ List of hook execution results
1040
+ """
1041
+ results = []
1042
+ remaining_time = max_total_execution_time_ms
1043
+
1044
+ # Separate hooks into parallel-safe and sequential
1045
+ parallel_hooks = []
1046
+ sequential_hooks = []
1047
+
1048
+ for hook_path, _ in prioritized_hooks:
1049
+ metadata = self._hook_registry.get(hook_path)
1050
+ if metadata and metadata.parallel_safe:
1051
+ parallel_hooks.append(hook_path)
1052
+ else:
1053
+ sequential_hooks.append(hook_path)
1054
+
1055
+ # Execute parallel hooks first (faster)
1056
+ if parallel_hooks and remaining_time > 0:
1057
+ parallel_results = await self._execute_hooks_parallel(parallel_hooks, context, remaining_time)
1058
+ results.extend(parallel_results)
1059
+
1060
+ # Update remaining time
1061
+ total_parallel_time = sum(r.execution_time_ms for r in parallel_results)
1062
+ remaining_time -= total_parallel_time
1063
+
1064
+ # Execute sequential hooks with remaining time
1065
+ if sequential_hooks and remaining_time > 0:
1066
+ sequential_results = await self._execute_hooks_sequential(sequential_hooks, context, remaining_time)
1067
+ results.extend(sequential_results)
1068
+
1069
+ return results
1070
+
1071
+ async def _execute_hooks_parallel(
1072
+ self, hook_paths: List[str], context: Dict[str, Any], max_total_time_ms: float
1073
+ ) -> List[HookExecutionResult]:
1074
+ """Execute hooks in parallel with time management"""
1075
+ results = []
1076
+
1077
+ # Create semaphore to limit concurrent executions
1078
+ semaphore = asyncio.Semaphore(self.max_concurrent_hooks)
1079
+
1080
+ async def execute_single_hook(hook_path: str) -> Optional[HookExecutionResult]:
1081
+ async with semaphore:
1082
+ try:
1083
+ return await self._execute_single_hook(hook_path, context)
1084
+ except Exception as e:
1085
+ return HookExecutionResult(
1086
+ hook_path=hook_path,
1087
+ success=False,
1088
+ execution_time_ms=0.0,
1089
+ token_usage=0,
1090
+ output=None,
1091
+ error_message=str(e),
1092
+ )
1093
+
1094
+ # Execute hooks with timeout
1095
+ tasks = [execute_single_hook(hook_path) for hook_path in hook_paths]
1096
+
1097
+ try:
1098
+ # Wait for all hooks with total timeout
1099
+ completed_results = await asyncio.wait_for(
1100
+ asyncio.gather(*tasks, return_exceptions=True),
1101
+ timeout=max_total_time_ms / 1000.0,
1102
+ )
1103
+
1104
+ for result in completed_results:
1105
+ if isinstance(result, HookExecutionResult):
1106
+ results.append(result)
1107
+ elif isinstance(result, Exception):
1108
+ # Handle exceptions
1109
+ error_result = HookExecutionResult(
1110
+ hook_path="unknown",
1111
+ success=False,
1112
+ execution_time_ms=0.0,
1113
+ token_usage=0,
1114
+ output=None,
1115
+ error_message=str(result),
1116
+ )
1117
+ results.append(error_result)
1118
+
1119
+ except asyncio.TimeoutError:
1120
+ # Some hooks didn't complete in time
1121
+ pass
1122
+
1123
+ return results
1124
+
1125
+ async def _execute_hooks_sequential(
1126
+ self, hook_paths: List[str], context: Dict[str, Any], max_total_time_ms: float
1127
+ ) -> List[HookExecutionResult]:
1128
+ """Execute hooks sequentially with time management"""
1129
+ results = []
1130
+ remaining_time = max_total_time_ms
1131
+
1132
+ for hook_path in hook_paths:
1133
+ if remaining_time <= 0:
1134
+ break
1135
+
1136
+ try:
1137
+ result = await self._execute_single_hook(hook_path, context)
1138
+ results.append(result)
1139
+
1140
+ # Update remaining time
1141
+ execution_time = result.execution_time_ms
1142
+ remaining_time -= execution_time
1143
+
1144
+ except Exception as e:
1145
+ error_result = HookExecutionResult(
1146
+ hook_path=hook_path,
1147
+ success=False,
1148
+ execution_time_ms=0.0,
1149
+ token_usage=0,
1150
+ output=None,
1151
+ error_message=str(e),
1152
+ )
1153
+ results.append(error_result)
1154
+
1155
+ return results
1156
+
1157
+ async def _execute_single_hook(self, hook_path: str, context: Dict[str, Any]) -> HookExecutionResult:
1158
+ """Execute a single hook with Phase 2 optimizations
1159
+
1160
+ Args:
1161
+ hook_path: Path to hook file
1162
+ context: Execution context
1163
+
1164
+ Returns:
1165
+ Hook execution result with enhanced monitoring and reliability
1166
+ """
1167
+ start_time = time.time()
1168
+ full_hook_path = self.hooks_directory / hook_path
1169
+
1170
+ try:
1171
+ # Get metadata for the hook
1172
+ metadata = self._hook_registry.get(hook_path)
1173
+ if not metadata:
1174
+ raise ValueError(f"Hook metadata not found for {hook_path}")
1175
+
1176
+ # Initialize circuit breaker and retry policy for this hook if needed
1177
+ if hook_path not in self._circuit_breakers:
1178
+ self._circuit_breakers[hook_path] = CircuitBreaker(
1179
+ failure_threshold=self.circuit_breaker_threshold,
1180
+ timeout_seconds=60,
1181
+ success_threshold=5,
1182
+ )
1183
+ self._retry_policies[hook_path] = RetryPolicy(
1184
+ max_retries=self.max_retries, base_delay_ms=100, max_delay_ms=5000
1185
+ )
1186
+
1187
+ circuit_breaker = self._circuit_breakers[hook_path]
1188
+ retry_policy = self._retry_policies[hook_path]
1189
+
1190
+ # Check advanced cache first
1191
+ cache_key = f"hook_result:{hook_path}:{hash(str(context))}"
1192
+ cached_result = self._advanced_cache.get(cache_key)
1193
+ if cached_result:
1194
+ if cached_result.success:
1195
+ with self._performance_lock:
1196
+ self.metrics.cache_hits += 1
1197
+ return cached_result
1198
+
1199
+ # Execute with circuit breaker protection and retry logic
1200
+ async def execute_hook_with_retry():
1201
+ return await self._execute_hook_subprocess(full_hook_path, context, metadata)
1202
+
1203
+ # Apply circuit breaker and retry pattern
1204
+ try:
1205
+ result = await circuit_breaker.call(retry_policy.execute_with_retry, execute_hook_with_retry)
1206
+ except Exception as e:
1207
+ # Circuit breaker is OPEN or all retries exhausted
1208
+ execution_time = (time.time() - start_time) * 1000
1209
+ with self._performance_lock:
1210
+ if circuit_breaker.state.state == "OPEN":
1211
+ self.metrics.circuit_breaker_trips += 1
1212
+
1213
+ self._logger.warning(f"Hook {hook_path} failed due to circuit breaker: {str(e)}")
1214
+
1215
+ return HookExecutionResult(
1216
+ hook_path=hook_path,
1217
+ success=False,
1218
+ execution_time_ms=execution_time,
1219
+ token_usage=0,
1220
+ output=None,
1221
+ error_message=f"Circuit breaker OPEN: {str(e)}",
1222
+ metadata={"circuit_breaker_state": circuit_breaker.state.state},
1223
+ )
1224
+
1225
+ # Update resource usage metrics
1226
+ current_resources = self._resource_monitor.get_current_metrics()
1227
+ with self._performance_lock:
1228
+ self.metrics.resource_usage = current_resources
1229
+
1230
+ # Performance anomaly detection
1231
+ anomaly = self._anomaly_detector.detect_anomaly(hook_path, result.execution_time_ms)
1232
+ if anomaly:
1233
+ self._logger.warning(f"Performance anomaly detected for {hook_path}: {anomaly}")
1234
+ result.metadata["performance_anomaly"] = anomaly
1235
+
1236
+ # Cache successful results with TTL based on hook characteristics
1237
+ if result.success:
1238
+ cache_ttl = self._determine_cache_ttl(hook_path, metadata)
1239
+ self._advanced_cache.put(cache_key, result, ttl_seconds=cache_ttl)
1240
+
1241
+ # Update cache statistics
1242
+ with self._performance_lock:
1243
+ self.metrics.cache_misses += 1
1244
+
1245
+ # Update execution profile for performance monitoring
1246
+ self._execution_profiles[hook_path].append(result.execution_time_ms)
1247
+ if len(self._execution_profiles[hook_path]) > 100:
1248
+ self._execution_profiles[hook_path].pop(0)
1249
+
1250
+ # Update metadata
1251
+ self._update_hook_metadata(hook_path, result)
1252
+
1253
+ return result
1254
+
1255
+ except Exception as e:
1256
+ execution_time = (time.time() - start_time) * 1000
1257
+ self._logger.error(f"Unexpected error executing hook {hook_path}: {str(e)}")
1258
+
1259
+ return HookExecutionResult(
1260
+ hook_path=hook_path,
1261
+ success=False,
1262
+ execution_time_ms=execution_time,
1263
+ token_usage=0,
1264
+ output=None,
1265
+ error_message=f"Unexpected error: {str(e)}",
1266
+ )
1267
+
1268
+ def _determine_cache_ttl(self, hook_path: str, metadata: HookMetadata) -> int:
1269
+ """Determine optimal cache TTL based on hook characteristics"""
1270
+ filename = hook_path.lower()
1271
+
1272
+ # Hooks that fetch external data should have shorter TTL
1273
+ if any(keyword in filename for keyword in ["fetch", "api", "network", "git"]):
1274
+ return 60 # 1 minute
1275
+
1276
+ # Hooks that read static files can have longer TTL
1277
+ if any(keyword in filename for keyword in ["read", "parse", "analyze"]):
1278
+ return 1800 # 30 minutes
1279
+
1280
+ # Hooks that write or modify data should have very short TTL
1281
+ if any(keyword in filename for keyword in ["write", "modify", "update", "create"]):
1282
+ return 30 # 30 seconds
1283
+
1284
+ # Default TTL
1285
+ return 300 # 5 minutes
1286
+
1287
+ async def _execute_hook_subprocess(
1288
+ self, hook_path: Path, context: Dict[str, Any], metadata: HookMetadata
1289
+ ) -> HookExecutionResult:
1290
+ """Execute hook in isolated subprocess
1291
+
1292
+ Args:
1293
+ hook_path: Full path to hook file
1294
+ context: Execution context
1295
+ metadata: Hook metadata
1296
+
1297
+ Returns:
1298
+ Hook execution result
1299
+ """
1300
+ start_time = time.time()
1301
+
1302
+ try:
1303
+ # Prepare input for hook
1304
+ hook_input = json.dumps(context)
1305
+
1306
+ # Execute hook with timeout
1307
+ timeout_seconds = max(1.0, metadata.estimated_execution_time_ms / 1000.0)
1308
+
1309
+ process = await asyncio.create_subprocess_exec(
1310
+ "uv",
1311
+ "run",
1312
+ str(hook_path),
1313
+ stdin=asyncio.subprocess.PIPE,
1314
+ stdout=asyncio.subprocess.PIPE,
1315
+ stderr=asyncio.subprocess.PIPE,
1316
+ cwd=Path.cwd(),
1317
+ )
1318
+
1319
+ try:
1320
+ stdout, stderr = await asyncio.wait_for(
1321
+ process.communicate(input=hook_input.encode()),
1322
+ timeout=timeout_seconds,
1323
+ )
1324
+ except asyncio.TimeoutError:
1325
+ process.kill()
1326
+ await process.wait()
1327
+ raise TimeoutError(f"Hook execution timed out after {timeout_seconds}s")
1328
+
1329
+ execution_time_ms = (time.time() - start_time) * 1000
1330
+ success = process.returncode == 0
1331
+
1332
+ # Parse output
1333
+ output = None
1334
+ if stdout:
1335
+ try:
1336
+ output = json.loads(stdout.decode())
1337
+ except json.JSONDecodeError:
1338
+ output = stdout.decode()
1339
+
1340
+ error_message = None
1341
+ if stderr:
1342
+ error_message = stderr.decode().strip()
1343
+ elif process.returncode != 0:
1344
+ error_message = f"Hook exited with code {process.returncode}"
1345
+
1346
+ return HookExecutionResult(
1347
+ hook_path=str(hook_path.relative_to(self.hooks_directory)),
1348
+ success=success,
1349
+ execution_time_ms=execution_time_ms,
1350
+ token_usage=metadata.token_cost_estimate,
1351
+ output=output,
1352
+ error_message=error_message,
1353
+ )
1354
+
1355
+ except Exception as e:
1356
+ execution_time_ms = (time.time() - start_time) * 1000
1357
+
1358
+ return HookExecutionResult(
1359
+ hook_path=str(hook_path.relative_to(self.hooks_directory)),
1360
+ success=False,
1361
+ execution_time_ms=execution_time_ms,
1362
+ token_usage=metadata.token_cost_estimate,
1363
+ output=None,
1364
+ error_message=str(e),
1365
+ )
1366
+
1367
+ def _update_hook_metadata(self, hook_path: str, result: HookExecutionResult) -> None:
1368
+ """Update hook metadata based on execution result"""
1369
+ metadata = self._hook_registry.get(hook_path)
1370
+ if not metadata:
1371
+ return
1372
+
1373
+ # Update execution time estimate
1374
+ cache_key = f"exec_time:{hook_path}"
1375
+ if cache_key not in self._metadata_cache:
1376
+ self._metadata_cache[cache_key] = {"count": 0, "total_time": 0.0}
1377
+
1378
+ cache_entry = self._metadata_cache[cache_key]
1379
+ cache_entry["count"] += 1
1380
+ cache_entry["total_time"] += result.execution_time_ms
1381
+ cache_entry["avg_time_ms"] = cache_entry["total_time"] / cache_entry["count"]
1382
+
1383
+ # Update success rate
1384
+ metadata.success_rate = (metadata.success_rate * 0.8) + (1.0 if result.success else 0.0) * 0.2
1385
+ metadata.last_execution_time = datetime.now()
1386
+
1387
+ def _update_performance_metrics(
1388
+ self,
1389
+ event_type: HookEvent,
1390
+ phase: Optional[Phase],
1391
+ results: List[HookExecutionResult],
1392
+ start_time: float,
1393
+ ) -> None:
1394
+ """Update performance metrics"""
1395
+ with self._performance_lock:
1396
+ self.metrics.total_executions += len(results)
1397
+ self.metrics.successful_executions += sum(1 for r in results if r.success)
1398
+
1399
+ total_execution_time = sum(r.execution_time_ms for r in results)
1400
+ self.metrics.average_execution_time_ms = (self.metrics.average_execution_time_ms * 0.9) + (
1401
+ total_execution_time / len(results) * 0.1
1402
+ )
1403
+
1404
+ self.metrics.total_token_usage += sum(r.token_usage for r in results)
1405
+
1406
+ if phase:
1407
+ self.metrics.phase_distribution[phase] = self.metrics.phase_distribution.get(phase, 0) + 1
1408
+
1409
+ self.metrics.event_type_distribution[event_type] = (
1410
+ self.metrics.event_type_distribution.get(event_type, 0) + 1
1411
+ )
1412
+
1413
+ # Log performance data
1414
+ self._log_performance_data(event_type, phase, results, start_time)
1415
+
1416
+ def _log_performance_data(
1417
+ self,
1418
+ event_type: HookEvent,
1419
+ phase: Optional[Phase],
1420
+ results: List[HookExecutionResult],
1421
+ start_time: float,
1422
+ ) -> None:
1423
+ """Log performance data to file"""
1424
+ log_entry = {
1425
+ "timestamp": datetime.now().isoformat(),
1426
+ "event_type": event_type.value,
1427
+ "phase": phase.value if phase else None,
1428
+ "total_hooks": len(results),
1429
+ "successful_hooks": sum(1 for r in results if r.success),
1430
+ "total_execution_time_ms": sum(r.execution_time_ms for r in results),
1431
+ "total_token_usage": sum(r.token_usage for r in results),
1432
+ "system_time_ms": (time.time() - start_time) * 1000,
1433
+ "results": [
1434
+ {
1435
+ "hook_path": r.hook_path,
1436
+ "success": r.success,
1437
+ "execution_time_ms": r.execution_time_ms,
1438
+ "token_usage": r.token_usage,
1439
+ "error_message": r.error_message,
1440
+ }
1441
+ for r in results
1442
+ ],
1443
+ }
1444
+
1445
+ try:
1446
+ with open(self._performance_log_path, "a") as f:
1447
+ f.write(json.dumps(log_entry) + "\n")
1448
+ except Exception:
1449
+ pass # Silently fail on logging
1450
+
1451
+ def get_performance_metrics(self) -> HookPerformanceMetrics:
1452
+ """Get comprehensive performance metrics with Phase 2 enhancements"""
1453
+ with self._performance_lock:
1454
+ # Get advanced cache stats
1455
+ self._advanced_cache.get_stats()
1456
+
1457
+ # Calculate performance profiles summary
1458
+ self._calculate_performance_summary()
1459
+
1460
+ # Get peak resource usage
1461
+ self._resource_monitor.get_peak_metrics()
1462
+
1463
+ return HookPerformanceMetrics(
1464
+ total_executions=self.metrics.total_executions,
1465
+ successful_executions=self.metrics.successful_executions,
1466
+ average_execution_time_ms=self.metrics.average_execution_time_ms,
1467
+ total_token_usage=self.metrics.total_token_usage,
1468
+ cache_hits=self.metrics.cache_hits,
1469
+ cache_misses=self.metrics.cache_misses,
1470
+ phase_distribution=self.metrics.phase_distribution.copy(),
1471
+ event_type_distribution=self.metrics.event_type_distribution.copy(),
1472
+ circuit_breaker_trips=self.metrics.circuit_breaker_trips,
1473
+ retry_attempts=self.metrics.retry_attempts,
1474
+ resource_usage=ResourceUsageMetrics(
1475
+ cpu_usage_percent=self.metrics.resource_usage.cpu_usage_percent,
1476
+ memory_usage_mb=self.metrics.resource_usage.memory_usage_mb,
1477
+ disk_io_mb=self.metrics.resource_usage.disk_io_mb,
1478
+ network_io_mb=self.metrics.resource_usage.network_io_mb,
1479
+ open_files=self.metrics.resource_usage.open_files,
1480
+ thread_count=self.metrics.resource_usage.thread_count,
1481
+ ),
1482
+ )
1483
+
1484
+ def _calculate_performance_summary(self) -> Dict[str, Any]:
1485
+ """Calculate detailed performance summary"""
1486
+ hook_performance: Dict[str, Dict[str, Any]] = {}
1487
+ summary: Dict[str, Any] = {
1488
+ "hook_performance": hook_performance,
1489
+ "cache_efficiency": 0.0,
1490
+ "overall_health": "healthy",
1491
+ }
1492
+
1493
+ # Calculate cache efficiency
1494
+ total_cache_requests = self.metrics.cache_hits + self.metrics.cache_misses
1495
+ if total_cache_requests > 0:
1496
+ summary["cache_efficiency"] = self.metrics.cache_hits / total_cache_requests
1497
+
1498
+ # Calculate per-hook performance statistics
1499
+ for hook_path, execution_times in self._execution_profiles.items():
1500
+ if execution_times:
1501
+ hook_performance[hook_path] = {
1502
+ "avg_time_ms": sum(execution_times) / len(execution_times),
1503
+ "min_time_ms": min(execution_times),
1504
+ "max_time_ms": max(execution_times),
1505
+ "execution_count": len(execution_times),
1506
+ "std_dev_ms": self._calculate_std_dev(execution_times),
1507
+ }
1508
+
1509
+ # Determine overall health
1510
+ success_rate = self.metrics.successful_executions / max(self.metrics.total_executions, 1)
1511
+ if success_rate < 0.9:
1512
+ summary["overall_health"] = "degraded"
1513
+ elif success_rate < 0.7:
1514
+ summary["overall_health"] = "unhealthy"
1515
+
1516
+ return summary
1517
+
1518
+ def _calculate_std_dev(self, values: List[float]) -> float:
1519
+ """Calculate standard deviation"""
1520
+ if len(values) < 2:
1521
+ return 0.0
1522
+ mean = sum(values) / len(values)
1523
+ variance = sum((x - mean) ** 2 for x in values) / len(values)
1524
+ return variance**0.5
1525
+
1526
+ async def get_system_health_report(self) -> Dict[str, Any]:
1527
+ """Get comprehensive health report"""
1528
+ return await self._health_checker.check_system_health()
1529
+
1530
+ def get_connection_pool_stats(self) -> Dict[str, Any]:
1531
+ """Get connection pool statistics"""
1532
+ return self._connection_pool.get_pool_stats()
1533
+
1534
+ def get_advanced_cache_stats(self) -> Dict[str, Any]:
1535
+ """Get advanced cache statistics"""
1536
+ return self._advanced_cache.get_stats()
1537
+
1538
+ def get_circuit_breaker_status(self) -> Dict[str, Any]:
1539
+ """Get circuit breaker status for all hooks"""
1540
+ return {
1541
+ hook_path: {
1542
+ "state": cb.state.state,
1543
+ "failure_count": cb.state.failure_count,
1544
+ "last_failure_time": (cb.state.last_failure_time.isoformat() if cb.state.last_failure_time else None),
1545
+ "success_threshold": cb.state.success_threshold,
1546
+ }
1547
+ for hook_path, cb in self._circuit_breakers.items()
1548
+ }
1549
+
1550
+ def get_hook_recommendations(
1551
+ self, event_type: Optional[HookEvent] = None, phase: Optional[Phase] = None
1552
+ ) -> Dict[str, Any]:
1553
+ """Get recommendations for hook optimization
1554
+
1555
+ Args:
1556
+ event_type: Specific event type to analyze
1557
+ phase: Specific phase to analyze
1558
+
1559
+ Returns:
1560
+ Dictionary with optimization recommendations
1561
+ """
1562
+ recommendations: Dict[str, List[Any]] = {
1563
+ "slow_hooks": [],
1564
+ "unreliable_hooks": [],
1565
+ "phase_mismatched_hooks": [],
1566
+ "optimization_suggestions": [],
1567
+ }
1568
+
1569
+ # Analyze hook performance
1570
+ for hook_path, metadata in self._hook_registry.items():
1571
+ if event_type and metadata.event_type != event_type:
1572
+ continue
1573
+
1574
+ # Check for slow hooks
1575
+ if metadata.estimated_execution_time_ms > 200:
1576
+ recommendations["slow_hooks"].append(
1577
+ {
1578
+ "hook_path": hook_path,
1579
+ "estimated_time_ms": metadata.estimated_execution_time_ms,
1580
+ "suggestion": "Consider optimizing or making this hook parallel-safe",
1581
+ }
1582
+ )
1583
+
1584
+ # Check for unreliable hooks
1585
+ if metadata.success_rate < 0.8:
1586
+ recommendations["unreliable_hooks"].append(
1587
+ {
1588
+ "hook_path": hook_path,
1589
+ "success_rate": metadata.success_rate,
1590
+ "suggestion": "Review error handling and improve reliability",
1591
+ }
1592
+ )
1593
+
1594
+ # Check for phase mismatch
1595
+ if phase:
1596
+ relevance = metadata.phase_relevance.get(phase, 0.0)
1597
+ if relevance < 0.3:
1598
+ recommendations["phase_mismatched_hooks"].append(
1599
+ {
1600
+ "hook_path": hook_path,
1601
+ "phase": phase.value,
1602
+ "relevance": relevance,
1603
+ "suggestion": "This hook may not be relevant for the current phase",
1604
+ }
1605
+ )
1606
+
1607
+ # Generate optimization suggestions
1608
+ if recommendations["slow_hooks"]:
1609
+ recommendations["optimization_suggestions"].append(
1610
+ "Consider implementing caching for frequently executed slow hooks"
1611
+ )
1612
+
1613
+ if recommendations["unreliable_hooks"]:
1614
+ recommendations["optimization_suggestions"].append(
1615
+ "Add retry logic and better error handling for unreliable hooks"
1616
+ )
1617
+
1618
+ if recommendations["phase_mismatched_hooks"]:
1619
+ recommendations["optimization_suggestions"].append(
1620
+ "Use phase-based hook filtering to skip irrelevant hooks"
1621
+ )
1622
+
1623
+ return recommendations
1624
+
1625
+ async def cleanup(self) -> None:
1626
+ """Enhanced cleanup with Phase 2 resource management"""
1627
+ try:
1628
+ # Save comprehensive performance metrics and state
1629
+ metrics_file = self.cache_directory / "metrics.json"
1630
+ state_file = self.cache_directory / "state.json"
1631
+
1632
+ # Get current metrics
1633
+ current_metrics = self.get_performance_metrics()
1634
+ health_report = await self.get_system_health_report()
1635
+
1636
+ metrics_data = {
1637
+ "timestamp": datetime.now().isoformat(),
1638
+ "phase": "phase_2_optimized",
1639
+ "metrics": {
1640
+ "total_executions": current_metrics.total_executions,
1641
+ "successful_executions": current_metrics.successful_executions,
1642
+ "average_execution_time_ms": current_metrics.average_execution_time_ms,
1643
+ "total_token_usage": current_metrics.total_token_usage,
1644
+ "cache_hits": current_metrics.cache_hits,
1645
+ "cache_misses": current_metrics.cache_misses,
1646
+ "circuit_breaker_trips": current_metrics.circuit_breaker_trips,
1647
+ "retry_attempts": current_metrics.retry_attempts,
1648
+ "resource_usage": current_metrics.resource_usage.__dict__,
1649
+ "phase_distribution": {k.value: v for k, v in current_metrics.phase_distribution.items()},
1650
+ "event_type_distribution": {k.value: v for k, v in current_metrics.event_type_distribution.items()},
1651
+ },
1652
+ "health_status": health_report,
1653
+ "cache_stats": self.get_advanced_cache_stats(),
1654
+ "connection_pool_stats": self.get_connection_pool_stats(),
1655
+ "circuit_breaker_status": self.get_circuit_breaker_status(),
1656
+ "hook_metadata": {
1657
+ hook_path: {
1658
+ "estimated_execution_time_ms": metadata.estimated_execution_time_ms,
1659
+ "success_rate": metadata.success_rate,
1660
+ "last_execution_time": (
1661
+ metadata.last_execution_time.isoformat() if metadata.last_execution_time else None
1662
+ ),
1663
+ "priority": metadata.priority.value,
1664
+ "parallel_safe": metadata.parallel_safe,
1665
+ "token_cost_estimate": metadata.token_cost_estimate,
1666
+ }
1667
+ for hook_path, metadata in self._hook_registry.items()
1668
+ },
1669
+ "performance_profiles": {
1670
+ hook_path: {
1671
+ "execution_times": times[-10:], # Keep last 10 execution times
1672
+ "avg_time_ms": sum(times) / len(times) if times else 0,
1673
+ "count": len(times),
1674
+ }
1675
+ for hook_path, times in self._execution_profiles.items()
1676
+ if times
1677
+ },
1678
+ }
1679
+
1680
+ # Save metrics
1681
+ with open(metrics_file, "w") as f:
1682
+ json.dump(metrics_data, f, indent=2)
1683
+
1684
+ # Save state for recovery
1685
+ state_data = {
1686
+ "timestamp": datetime.now().isoformat(),
1687
+ "circuit_breaker_states": self.get_circuit_breaker_status(),
1688
+ "cache_config": {
1689
+ "max_size": self._advanced_cache.max_size,
1690
+ "default_ttl_seconds": self._advanced_cache.default_ttl_seconds,
1691
+ },
1692
+ "connection_pool_config": {
1693
+ "max_connections": self._connection_pool.max_connections,
1694
+ "connection_timeout_seconds": self._connection_pool.connection_timeout_seconds,
1695
+ },
1696
+ "optimization_config": {
1697
+ "circuit_breaker_threshold": self.circuit_breaker_threshold,
1698
+ "max_retries": self.max_retries,
1699
+ },
1700
+ }
1701
+
1702
+ with open(state_file, "w") as f:
1703
+ json.dump(state_data, f, indent=2)
1704
+
1705
+ except Exception as e:
1706
+ if hasattr(self, "_logger"):
1707
+ self._logger.error(f"Error during cleanup: {str(e)}")
1708
+
1709
+ finally:
1710
+ # Clear all caches and resources
1711
+ try:
1712
+ self._advanced_cache.invalidate() # Clear all cache entries
1713
+ self._metadata_cache.clear()
1714
+
1715
+ # Reset circuit breakers
1716
+ for circuit_breaker in self._circuit_breakers.values():
1717
+ circuit_breaker.state.state = "CLOSED"
1718
+ circuit_breaker.state.failure_count = 0
1719
+ circuit_breaker.state.last_failure_time = None
1720
+
1721
+ # Clear execution profiles
1722
+ self._execution_profiles.clear()
1723
+
1724
+ except Exception as e:
1725
+ if hasattr(self, "_logger"):
1726
+ self._logger.error(f"Error during cache cleanup: {str(e)}")
1727
+
1728
+ # Log peak resource usage for monitoring
1729
+ try:
1730
+ peak_resources = self._resource_monitor.get_peak_metrics()
1731
+ if hasattr(self, "_logger"):
1732
+ self._logger.info(
1733
+ f"Peak resource usage - Memory: {peak_resources.memory_usage_mb:.1f}MB, "
1734
+ f"CPU: {peak_resources.cpu_usage_percent:.1f}%, "
1735
+ f"Threads: {peak_resources.thread_count}"
1736
+ )
1737
+ except Exception:
1738
+ pass # Ignore cleanup logging errors
1739
+
1740
+
1741
+ # Global instance for easy access
1742
+ _jit_hook_manager: Optional[JITEnhancedHookManager] = None
1743
+
1744
+
1745
+ def get_jit_hook_manager() -> JITEnhancedHookManager:
1746
+ """Get or create global JIT hook manager instance"""
1747
+ global _jit_hook_manager
1748
+ if _jit_hook_manager is None:
1749
+ _jit_hook_manager = JITEnhancedHookManager()
1750
+ return _jit_hook_manager
1751
+
1752
+
1753
+ # Convenience functions for common hook operations
1754
+ async def execute_session_start_hooks(
1755
+ context: Dict[str, Any], user_input: Optional[str] = None
1756
+ ) -> List[HookExecutionResult]:
1757
+ """Execute SessionStart hooks with JIT optimization"""
1758
+ manager = get_jit_hook_manager()
1759
+ return await manager.execute_hooks(HookEvent.SESSION_START, context, user_input=user_input)
1760
+
1761
+
1762
+ async def execute_pre_tool_hooks(
1763
+ context: Dict[str, Any], user_input: Optional[str] = None
1764
+ ) -> List[HookExecutionResult]:
1765
+ """Execute PreToolUse hooks with JIT optimization"""
1766
+ manager = get_jit_hook_manager()
1767
+ return await manager.execute_hooks(HookEvent.PRE_TOOL_USE, context, user_input=user_input)
1768
+
1769
+
1770
+ async def execute_session_end_hooks(
1771
+ context: Dict[str, Any], user_input: Optional[str] = None
1772
+ ) -> List[HookExecutionResult]:
1773
+ """Execute SessionEnd hooks with JIT optimization"""
1774
+ manager = get_jit_hook_manager()
1775
+ return await manager.execute_hooks(HookEvent.SESSION_END, context, user_input=user_input)
1776
+
1777
+
1778
+ def get_hook_performance_metrics() -> HookPerformanceMetrics:
1779
+ """Get current hook performance metrics"""
1780
+ manager = get_jit_hook_manager()
1781
+ return manager.get_performance_metrics()
1782
+
1783
+
1784
+ def get_hook_optimization_recommendations(
1785
+ event_type: Optional[HookEvent] = None, phase: Optional[Phase] = None
1786
+ ) -> Dict[str, Any]:
1787
+ """Get hook optimization recommendations"""
1788
+ manager = get_jit_hook_manager()
1789
+ return manager.get_hook_recommendations(event_type, phase)
1790
+
1791
+
1792
+ # Phase 2 convenience functions for enhanced monitoring and control
1793
+
1794
+
1795
+ async def get_system_health() -> Dict[str, Any]:
1796
+ """Get comprehensive system health report"""
1797
+ manager = get_jit_hook_manager()
1798
+ return await manager.get_system_health_report()
1799
+
1800
+
1801
+ def get_connection_pool_info() -> Dict[str, Any]:
1802
+ """Get connection pool statistics and status"""
1803
+ manager = get_jit_hook_manager()
1804
+ return manager.get_connection_pool_stats()
1805
+
1806
+
1807
+ def get_cache_performance() -> Dict[str, Any]:
1808
+ """Get advanced cache performance metrics"""
1809
+ manager = get_jit_hook_manager()
1810
+ return manager.get_advanced_cache_stats()
1811
+
1812
+
1813
+ def get_circuit_breaker_info() -> Dict[str, Any]:
1814
+ """Get circuit breaker status for all hooks"""
1815
+ manager = get_jit_hook_manager()
1816
+ return manager.get_circuit_breaker_status()
1817
+
1818
+
1819
+ def invalidate_hook_cache(pattern: Optional[str] = None) -> None:
1820
+ """Invalidate hook cache entries"""
1821
+ manager = get_jit_hook_manager()
1822
+ manager._advanced_cache.invalidate(pattern)
1823
+
1824
+
1825
+ def reset_circuit_breakers(hook_path: Optional[str] = None) -> None:
1826
+ """Reset circuit breakers (specific hook or all)"""
1827
+ manager = get_jit_hook_manager()
1828
+
1829
+ if hook_path:
1830
+ if hook_path in manager._circuit_breakers:
1831
+ cb = manager._circuit_breakers[hook_path]
1832
+ cb.state.state = "CLOSED"
1833
+ cb.state.failure_count = 0
1834
+ cb.state.last_failure_time = None
1835
+ else:
1836
+ for cb in manager._circuit_breakers.values():
1837
+ cb.state.state = "CLOSED"
1838
+ cb.state.failure_count = 0
1839
+ cb.state.last_failure_time = None
1840
+
1841
+
1842
+ async def optimize_hook_system() -> Dict[str, Any]:
1843
+ """Run system optimization and return recommendations"""
1844
+ manager = get_jit_hook_manager()
1845
+
1846
+ # Get current health and metrics
1847
+ health_report = await manager.get_system_health_report()
1848
+ metrics = manager.get_performance_metrics()
1849
+ manager.get_advanced_cache_stats()
1850
+
1851
+ # Generate optimization recommendations
1852
+ optimization_report = {
1853
+ "timestamp": datetime.now().isoformat(),
1854
+ "health_status": health_report["status"],
1855
+ "performance_summary": {
1856
+ "success_rate": metrics.successful_executions / max(metrics.total_executions, 1),
1857
+ "average_execution_time_ms": metrics.average_execution_time_ms,
1858
+ "cache_efficiency": metrics.cache_hits / max(metrics.cache_hits + metrics.cache_misses, 1),
1859
+ "circuit_breaker_trips": metrics.circuit_breaker_trips,
1860
+ },
1861
+ "recommendations": [],
1862
+ }
1863
+
1864
+ # Performance recommendations
1865
+ if metrics.average_execution_time_ms > 500:
1866
+ optimization_report["recommendations"].append("Consider optimizing slow hooks or increasing parallel execution")
1867
+
1868
+ if metrics.cache_hits / max(metrics.cache_hits + metrics.cache_misses, 1) < 0.3:
1869
+ optimization_report["recommendations"].append(
1870
+ "Cache hit rate is low - consider increasing TTL or reviewing cache strategy"
1871
+ )
1872
+
1873
+ if metrics.circuit_breaker_trips > 5:
1874
+ optimization_report["recommendations"].append(
1875
+ "High circuit breaker activity detected - review hook reliability"
1876
+ )
1877
+
1878
+ # Resource recommendations
1879
+ if metrics.resource_usage.memory_usage_mb > 500:
1880
+ optimization_report["recommendations"].append(
1881
+ "High memory usage - consider reducing cache size or optimizing resource usage"
1882
+ )
1883
+
1884
+ if health_report["status"] != "healthy":
1885
+ optimization_report["recommendations"].append(
1886
+ f"System health is {health_report['status']} - review health checks"
1887
+ )
1888
+
1889
+ return optimization_report
1890
+
1891
+
1892
+ if __name__ == "__main__":
1893
+ # Example usage and testing with Phase 2 optimizations
1894
+ async def test_phase2_optimizations():
1895
+ """Test Phase 2 performance optimizations and reliability features"""
1896
+ print("๐Ÿš€ Testing Phase 2 JIT-Enhanced Hook Manager Optimizations")
1897
+ print("=" * 60)
1898
+
1899
+ # Initialize with Phase 2 optimizations
1900
+ manager = JITEnhancedHookManager(
1901
+ cache_ttl_seconds=300,
1902
+ circuit_breaker_threshold=3,
1903
+ max_retries=2,
1904
+ connection_pool_size=5,
1905
+ )
1906
+
1907
+ try:
1908
+ # Test hook execution with advanced features
1909
+ context = {"test": True, "user": "test_user", "session_id": "test_session"}
1910
+
1911
+ print("\n๐Ÿ“Š Testing Hook Execution with Phase 2 Optimizations:")
1912
+ results = await manager.execute_hooks(
1913
+ HookEvent.SESSION_START,
1914
+ context,
1915
+ user_input="Testing Phase 2 JIT enhanced hook system",
1916
+ )
1917
+
1918
+ print(f"Executed {len(results)} hooks")
1919
+ for result in results:
1920
+ status = "โœ“" if result.success else "โœ—"
1921
+ print(f" {result.hook_path}: {status} ({result.execution_time_ms:.1f}ms)")
1922
+ if result.metadata.get("performance_anomaly"):
1923
+ anomaly = result.metadata["performance_anomaly"]
1924
+ print(f" โš ๏ธ Performance anomaly: {anomaly['anomaly_type']} ({anomaly['severity']})")
1925
+
1926
+ # Show Phase 2 enhanced metrics
1927
+ print("\n๐Ÿ“ˆ Phase 2 Performance Metrics:")
1928
+ metrics = manager.get_performance_metrics()
1929
+ print(f" Total executions: {metrics.total_executions}")
1930
+ print(f" Success rate: {metrics.successful_executions}/{metrics.total_executions}")
1931
+ print(f" Avg execution time: {metrics.average_execution_time_ms:.1f}ms")
1932
+ print(f" Cache hits: {metrics.cache_hits}, misses: {metrics.cache_misses}")
1933
+ print(f" Circuit breaker trips: {metrics.circuit_breaker_trips}")
1934
+ print(f" Retry attempts: {metrics.retry_attempts}")
1935
+ print(f" Memory usage: {metrics.resource_usage.memory_usage_mb:.1f}MB")
1936
+ print(f" CPU usage: {metrics.resource_usage.cpu_usage_percent:.1f}%")
1937
+
1938
+ # Test health monitoring
1939
+ print("\n๐Ÿฅ System Health Check:")
1940
+ health_report = await manager.get_system_health_report()
1941
+ print(f" Overall status: {health_report['status']}")
1942
+ for check_name, check_data in health_report["checks"].items():
1943
+ status_icon = "โœ“" if check_data["status"] == "healthy" else "โš ๏ธ"
1944
+ print(f" {check_name}: {status_icon} {check_data['status']}")
1945
+
1946
+ # Test cache performance
1947
+ print("\n๐Ÿ’พ Cache Performance:")
1948
+ cache_stats = manager.get_advanced_cache_stats()
1949
+ print(f" Cache size: {cache_stats['size']}/{cache_stats['max_size']}")
1950
+ print(f" Utilization: {cache_stats['utilization']:.1%}")
1951
+
1952
+ # Test circuit breaker status
1953
+ print("\nโšก Circuit Breaker Status:")
1954
+ cb_status = manager.get_circuit_breaker_status()
1955
+ for hook_name, cb_data in cb_status.items():
1956
+ print(f" {hook_name}: {cb_data['state']} ({cb_data['failure_count']} failures)")
1957
+
1958
+ # Test system optimization
1959
+ print("\n๐Ÿ”ง System Optimization:")
1960
+ optimization_report = await optimize_hook_system()
1961
+ print(f" Health status: {optimization_report['health_status']}")
1962
+ print(f" Success rate: {optimization_report['performance_summary']['success_rate']:.1%}")
1963
+ print(f" Cache efficiency: {optimization_report['performance_summary']['cache_efficiency']:.1%}")
1964
+
1965
+ if optimization_report["recommendations"]:
1966
+ print(" Recommendations:")
1967
+ for rec in optimization_report["recommendations"]:
1968
+ print(f" โ€ข {rec}")
1969
+ else:
1970
+ print(" โœ“ No optimization recommendations needed")
1971
+
1972
+ print("\nโœ… Phase 2 optimizations test completed successfully!")
1973
+
1974
+ except Exception as e:
1975
+ print(f"\nโŒ Test failed: {str(e)}")
1976
+ import traceback
1977
+
1978
+ traceback.print_exc()
1979
+
1980
+ finally:
1981
+ # Enhanced cleanup with Phase 2 features
1982
+ print("\n๐Ÿงน Cleaning up Phase 2 resources...")
1983
+ await manager.cleanup()
1984
+ print("โœ… Cleanup completed")
1985
+
1986
+ # Run Phase 2 test
1987
+ asyncio.run(test_phase2_optimizations())