claude-mpm 4.1.26__py3-none-any.whl → 4.24.0__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (845) hide show
  1. claude_mpm/BUILD_NUMBER +1 -1
  2. claude_mpm/VERSION +1 -1
  3. claude_mpm/__init__.py +20 -5
  4. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  5. claude_mpm/agents/BASE_DOCUMENTATION.md +53 -0
  6. claude_mpm/agents/BASE_ENGINEER.md +658 -0
  7. claude_mpm/agents/BASE_OPS.md +219 -0
  8. claude_mpm/agents/BASE_PM.md +420 -158
  9. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +787 -0
  10. claude_mpm/agents/BASE_QA.md +167 -0
  11. claude_mpm/agents/BASE_RESEARCH.md +53 -0
  12. claude_mpm/agents/OUTPUT_STYLE.md +299 -29
  13. claude_mpm/agents/PM_INSTRUCTIONS.md +1159 -0
  14. claude_mpm/agents/WORKFLOW.md +355 -191
  15. claude_mpm/agents/agent_loader.py +40 -10
  16. claude_mpm/agents/agent_loader_integration.py +3 -2
  17. claude_mpm/agents/async_agent_loader.py +3 -3
  18. claude_mpm/agents/base_agent_loader.py +11 -9
  19. claude_mpm/agents/frontmatter_validator.py +291 -251
  20. claude_mpm/agents/system_agent_config.py +3 -2
  21. claude_mpm/agents/templates/README.md +465 -0
  22. claude_mpm/agents/templates/agent-manager.json +7 -4
  23. claude_mpm/agents/templates/{agentic_coder_optimizer.json → agentic-coder-optimizer.json} +33 -7
  24. claude_mpm/agents/templates/api_qa.json +16 -4
  25. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  26. claude_mpm/agents/templates/clerk-ops.json +235 -0
  27. claude_mpm/agents/templates/code_analyzer.json +10 -4
  28. claude_mpm/agents/templates/content-agent.json +358 -0
  29. claude_mpm/agents/templates/dart_engineer.json +307 -0
  30. claude_mpm/agents/templates/data_engineer.json +87 -14
  31. claude_mpm/agents/templates/documentation.json +76 -13
  32. claude_mpm/agents/templates/engineer.json +43 -9
  33. claude_mpm/agents/templates/gcp_ops_agent.json +253 -0
  34. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  35. claude_mpm/agents/templates/golang_engineer.json +270 -0
  36. claude_mpm/agents/templates/imagemagick.json +5 -2
  37. claude_mpm/agents/templates/java_engineer.json +346 -0
  38. claude_mpm/agents/templates/javascript_engineer_agent.json +380 -0
  39. claude_mpm/agents/templates/local_ops_agent.json +1840 -0
  40. claude_mpm/agents/templates/logs/prompts/agent_engineer_20250901_010124_142.md +400 -0
  41. claude_mpm/agents/templates/memory_manager.json +6 -3
  42. claude_mpm/agents/templates/nextjs_engineer.json +285 -0
  43. claude_mpm/agents/templates/ops.json +14 -4
  44. claude_mpm/agents/templates/php-engineer.json +287 -0
  45. claude_mpm/agents/templates/pm_examples.md +474 -0
  46. claude_mpm/agents/templates/pm_red_flags.md +262 -0
  47. claude_mpm/agents/templates/product_owner.json +338 -0
  48. claude_mpm/agents/templates/project_organizer.json +19 -5
  49. claude_mpm/agents/templates/prompt-engineer.json +737 -0
  50. claude_mpm/agents/templates/python_engineer.json +387 -0
  51. claude_mpm/agents/templates/qa.json +25 -5
  52. claude_mpm/agents/templates/react_engineer.json +239 -0
  53. claude_mpm/agents/templates/refactoring_engineer.json +15 -5
  54. claude_mpm/agents/templates/research.json +46 -21
  55. claude_mpm/agents/templates/response_format.md +583 -0
  56. claude_mpm/agents/templates/ruby-engineer.json +280 -0
  57. claude_mpm/agents/templates/rust_engineer.json +275 -0
  58. claude_mpm/agents/templates/security.json +59 -10
  59. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  60. claude_mpm/agents/templates/tauri_engineer.json +274 -0
  61. claude_mpm/agents/templates/ticketing.json +16 -7
  62. claude_mpm/agents/templates/typescript_engineer.json +285 -0
  63. claude_mpm/agents/templates/validation_templates.md +312 -0
  64. claude_mpm/agents/templates/vercel_ops_agent.json +164 -33
  65. claude_mpm/agents/templates/version_control.json +16 -4
  66. claude_mpm/agents/templates/web_qa.json +167 -21
  67. claude_mpm/agents/templates/web_ui.json +18 -5
  68. claude_mpm/cli/__init__.py +38 -378
  69. claude_mpm/cli/commands/__init__.py +2 -0
  70. claude_mpm/cli/commands/agent_manager.py +675 -20
  71. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  72. claude_mpm/cli/commands/agents.py +722 -150
  73. claude_mpm/cli/commands/agents_detect.py +380 -0
  74. claude_mpm/cli/commands/agents_recommend.py +309 -0
  75. claude_mpm/cli/commands/aggregate.py +10 -6
  76. claude_mpm/cli/commands/analyze.py +15 -10
  77. claude_mpm/cli/commands/analyze_code.py +8 -4
  78. claude_mpm/cli/commands/auto_configure.py +570 -0
  79. claude_mpm/cli/commands/cleanup.py +12 -12
  80. claude_mpm/cli/commands/config.py +47 -13
  81. claude_mpm/cli/commands/configure.py +469 -1064
  82. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  83. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  84. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  85. claude_mpm/cli/commands/configure_models.py +18 -0
  86. claude_mpm/cli/commands/configure_navigation.py +167 -0
  87. claude_mpm/cli/commands/configure_paths.py +104 -0
  88. claude_mpm/cli/commands/configure_persistence.py +254 -0
  89. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  90. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  91. claude_mpm/cli/commands/configure_validators.py +73 -0
  92. claude_mpm/cli/commands/dashboard.py +50 -52
  93. claude_mpm/cli/commands/debug.py +7 -7
  94. claude_mpm/cli/commands/doctor.py +43 -7
  95. claude_mpm/cli/commands/info.py +3 -4
  96. claude_mpm/cli/commands/local_deploy.py +537 -0
  97. claude_mpm/cli/commands/mcp.py +17 -10
  98. claude_mpm/cli/commands/mcp_command_router.py +11 -0
  99. claude_mpm/cli/commands/mcp_config.py +154 -0
  100. claude_mpm/cli/commands/mcp_external_commands.py +249 -0
  101. claude_mpm/cli/commands/mcp_install_commands.py +101 -32
  102. claude_mpm/cli/commands/mcp_pipx_config.py +2 -2
  103. claude_mpm/cli/commands/mcp_setup_external.py +868 -0
  104. claude_mpm/cli/commands/memory.py +55 -21
  105. claude_mpm/cli/commands/monitor.py +160 -70
  106. claude_mpm/cli/commands/mpm_init/__init__.py +73 -0
  107. claude_mpm/cli/commands/mpm_init/core.py +525 -0
  108. claude_mpm/cli/commands/mpm_init/display.py +341 -0
  109. claude_mpm/cli/commands/mpm_init/git_activity.py +427 -0
  110. claude_mpm/cli/commands/mpm_init/modes.py +397 -0
  111. claude_mpm/cli/commands/mpm_init/prompts.py +442 -0
  112. claude_mpm/cli/commands/mpm_init_cli.py +396 -0
  113. claude_mpm/cli/commands/mpm_init_handler.py +114 -4
  114. claude_mpm/cli/commands/run.py +169 -42
  115. claude_mpm/cli/commands/search.py +458 -0
  116. claude_mpm/cli/commands/skills.py +488 -0
  117. claude_mpm/cli/commands/uninstall.py +176 -0
  118. claude_mpm/cli/commands/upgrade.py +152 -0
  119. claude_mpm/cli/commands/verify.py +119 -0
  120. claude_mpm/cli/executor.py +204 -0
  121. claude_mpm/cli/helpers.py +105 -0
  122. claude_mpm/cli/interactive/__init__.py +21 -0
  123. claude_mpm/cli/interactive/agent_wizard.py +962 -0
  124. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  125. claude_mpm/cli/parser.py +79 -2
  126. claude_mpm/cli/parsers/__init__.py +7 -1
  127. claude_mpm/cli/parsers/agent_manager_parser.py +161 -1
  128. claude_mpm/cli/parsers/agents_parser.py +116 -0
  129. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  130. claude_mpm/cli/parsers/base_parser.py +143 -3
  131. claude_mpm/cli/parsers/configure_parser.py +11 -15
  132. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  133. claude_mpm/cli/parsers/mcp_parser.py +15 -0
  134. claude_mpm/cli/parsers/monitor_parser.py +12 -2
  135. claude_mpm/cli/parsers/mpm_init_parser.py +179 -9
  136. claude_mpm/cli/parsers/run_parser.py +5 -0
  137. claude_mpm/cli/parsers/search_parser.py +245 -0
  138. claude_mpm/cli/parsers/skills_parser.py +137 -0
  139. claude_mpm/cli/shared/argument_patterns.py +20 -13
  140. claude_mpm/cli/shared/base_command.py +2 -2
  141. claude_mpm/cli/shared/output_formatters.py +28 -19
  142. claude_mpm/cli/startup.py +562 -0
  143. claude_mpm/cli/startup_logging.py +179 -13
  144. claude_mpm/cli/utils.py +53 -2
  145. claude_mpm/commands/mpm-agents-detect.md +168 -0
  146. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  147. claude_mpm/commands/mpm-agents.md +118 -8
  148. claude_mpm/commands/mpm-auto-configure.md +269 -0
  149. claude_mpm/commands/mpm-config.md +137 -14
  150. claude_mpm/commands/mpm-help.md +285 -5
  151. claude_mpm/commands/mpm-init.md +374 -15
  152. claude_mpm/commands/mpm-monitor.md +409 -0
  153. claude_mpm/commands/mpm-organize.md +295 -0
  154. claude_mpm/commands/mpm-resume.md +372 -0
  155. claude_mpm/commands/mpm-status.md +71 -9
  156. claude_mpm/commands/mpm-tickets.md +56 -7
  157. claude_mpm/commands/mpm-version.md +113 -0
  158. claude_mpm/commands/mpm.md +2 -0
  159. claude_mpm/config/agent_config.py +4 -4
  160. claude_mpm/config/experimental_features.py +7 -7
  161. claude_mpm/config/model_config.py +428 -0
  162. claude_mpm/config/paths.py +3 -2
  163. claude_mpm/config/socketio_config.py +3 -3
  164. claude_mpm/constants.py +15 -1
  165. claude_mpm/core/__init__.py +53 -17
  166. claude_mpm/core/agent_name_normalizer.py +3 -2
  167. claude_mpm/core/agent_registry.py +2 -2
  168. claude_mpm/core/agent_session_manager.py +10 -10
  169. claude_mpm/core/api_validator.py +330 -0
  170. claude_mpm/core/base_service.py +33 -23
  171. claude_mpm/core/cache.py +9 -9
  172. claude_mpm/core/claude_runner.py +19 -8
  173. claude_mpm/core/config.py +85 -8
  174. claude_mpm/core/config_aliases.py +7 -6
  175. claude_mpm/core/constants.py +65 -0
  176. claude_mpm/core/container.py +11 -5
  177. claude_mpm/core/enums.py +452 -0
  178. claude_mpm/core/error_handler.py +623 -0
  179. claude_mpm/core/factories.py +1 -1
  180. claude_mpm/core/file_utils.py +764 -0
  181. claude_mpm/core/framework/__init__.py +38 -0
  182. claude_mpm/core/framework/formatters/__init__.py +11 -0
  183. claude_mpm/core/framework/formatters/capability_generator.py +367 -0
  184. claude_mpm/core/framework/formatters/content_formatter.py +288 -0
  185. claude_mpm/core/framework/formatters/context_generator.py +185 -0
  186. claude_mpm/core/framework/loaders/__init__.py +13 -0
  187. claude_mpm/core/framework/loaders/agent_loader.py +210 -0
  188. claude_mpm/core/framework/loaders/file_loader.py +223 -0
  189. claude_mpm/core/framework/loaders/instruction_loader.py +161 -0
  190. claude_mpm/core/framework/loaders/packaged_loader.py +232 -0
  191. claude_mpm/core/framework/processors/__init__.py +11 -0
  192. claude_mpm/core/framework/processors/memory_processor.py +230 -0
  193. claude_mpm/core/framework/processors/metadata_processor.py +146 -0
  194. claude_mpm/core/framework/processors/template_processor.py +244 -0
  195. claude_mpm/core/framework_loader.py +321 -1631
  196. claude_mpm/core/hook_manager.py +8 -6
  197. claude_mpm/core/injectable_service.py +11 -8
  198. claude_mpm/core/instruction_reinforcement_hook.py +4 -3
  199. claude_mpm/core/interactive_session.py +55 -8
  200. claude_mpm/core/interfaces.py +56 -1
  201. claude_mpm/core/lazy.py +3 -3
  202. claude_mpm/core/log_manager.py +92 -23
  203. claude_mpm/core/logger.py +19 -14
  204. claude_mpm/core/logging_config.py +6 -2
  205. claude_mpm/core/logging_utils.py +520 -0
  206. claude_mpm/core/oneshot_session.py +51 -7
  207. claude_mpm/core/optimized_agent_loader.py +9 -9
  208. claude_mpm/core/optimized_startup.py +1 -1
  209. claude_mpm/core/output_style_manager.py +12 -192
  210. claude_mpm/core/pm_hook_interceptor.py +18 -12
  211. claude_mpm/core/service_registry.py +7 -3
  212. claude_mpm/core/session_manager.py +14 -12
  213. claude_mpm/core/shared/config_loader.py +1 -1
  214. claude_mpm/core/socketio_pool.py +15 -15
  215. claude_mpm/core/tool_access_control.py +3 -2
  216. claude_mpm/core/types.py +4 -11
  217. claude_mpm/core/typing_utils.py +7 -6
  218. claude_mpm/core/unified_agent_registry.py +115 -11
  219. claude_mpm/core/unified_config.py +6 -6
  220. claude_mpm/core/unified_paths.py +23 -20
  221. claude_mpm/dashboard/analysis_runner.py +4 -4
  222. claude_mpm/dashboard/api/simple_directory.py +261 -0
  223. claude_mpm/dashboard/react/components/DataInspector/DataInspector.module.css +188 -0
  224. claude_mpm/dashboard/react/components/EventViewer/EventViewer.module.css +156 -0
  225. claude_mpm/dashboard/react/components/shared/ConnectionStatus.module.css +38 -0
  226. claude_mpm/dashboard/react/components/shared/FilterBar.module.css +92 -0
  227. claude_mpm/dashboard/static/archive/activity_dashboard_fixed.html +248 -0
  228. claude_mpm/dashboard/static/archive/activity_dashboard_test.html +61 -0
  229. claude_mpm/dashboard/static/archive/test_activity_connection.html +179 -0
  230. claude_mpm/dashboard/static/archive/test_claude_tree_tab.html +68 -0
  231. claude_mpm/dashboard/static/archive/test_dashboard.html +409 -0
  232. claude_mpm/dashboard/static/archive/test_dashboard_fixed.html +519 -0
  233. claude_mpm/dashboard/static/archive/test_dashboard_verification.html +181 -0
  234. claude_mpm/dashboard/static/archive/test_file_data.html +315 -0
  235. claude_mpm/dashboard/static/archive/test_file_tree_empty_state.html +243 -0
  236. claude_mpm/dashboard/static/archive/test_file_tree_fix.html +234 -0
  237. claude_mpm/dashboard/static/archive/test_file_tree_rename.html +117 -0
  238. claude_mpm/dashboard/static/archive/test_file_tree_tab.html +115 -0
  239. claude_mpm/dashboard/static/archive/test_file_viewer.html +224 -0
  240. claude_mpm/dashboard/static/archive/test_final_activity.html +220 -0
  241. claude_mpm/dashboard/static/archive/test_tab_fix.html +139 -0
  242. claude_mpm/dashboard/static/built/assets/events.DjpNxWNo.css +1 -0
  243. claude_mpm/dashboard/static/built/components/activity-tree.js +1 -1
  244. claude_mpm/dashboard/static/built/components/agent-hierarchy.js +777 -0
  245. claude_mpm/dashboard/static/built/components/agent-inference.js +1 -1
  246. claude_mpm/dashboard/static/built/components/build-tracker.js +333 -0
  247. claude_mpm/dashboard/static/built/components/code-simple.js +857 -0
  248. claude_mpm/dashboard/static/built/components/code-tree/tree-breadcrumb.js +353 -0
  249. claude_mpm/dashboard/static/built/components/code-tree/tree-constants.js +235 -0
  250. claude_mpm/dashboard/static/built/components/code-tree/tree-search.js +409 -0
  251. claude_mpm/dashboard/static/built/components/code-tree/tree-utils.js +435 -0
  252. claude_mpm/dashboard/static/built/components/code-tree.js +1 -1
  253. claude_mpm/dashboard/static/built/components/code-viewer.js +1 -1
  254. claude_mpm/dashboard/static/built/components/connection-debug.js +654 -0
  255. claude_mpm/dashboard/static/built/components/diff-viewer.js +891 -0
  256. claude_mpm/dashboard/static/built/components/event-processor.js +1 -1
  257. claude_mpm/dashboard/static/built/components/event-viewer.js +1 -1
  258. claude_mpm/dashboard/static/built/components/export-manager.js +1 -1
  259. claude_mpm/dashboard/static/built/components/file-change-tracker.js +443 -0
  260. claude_mpm/dashboard/static/built/components/file-change-viewer.js +690 -0
  261. claude_mpm/dashboard/static/built/components/file-tool-tracker.js +1 -1
  262. claude_mpm/dashboard/static/built/components/file-viewer.js +2 -0
  263. claude_mpm/dashboard/static/built/components/module-viewer.js +1 -1
  264. claude_mpm/dashboard/static/built/components/nav-bar.js +145 -0
  265. claude_mpm/dashboard/static/built/components/page-structure.js +429 -0
  266. claude_mpm/dashboard/static/built/components/session-manager.js +1 -1
  267. claude_mpm/dashboard/static/built/components/unified-data-viewer.js +1 -1
  268. claude_mpm/dashboard/static/built/components/working-directory.js +1 -1
  269. claude_mpm/dashboard/static/built/connection-manager.js +536 -0
  270. claude_mpm/dashboard/static/built/dashboard.js +1 -1
  271. claude_mpm/dashboard/static/built/extension-error-handler.js +164 -0
  272. claude_mpm/dashboard/static/built/react/events.js +30 -0
  273. claude_mpm/dashboard/static/built/shared/dom-helpers.js +396 -0
  274. claude_mpm/dashboard/static/built/shared/event-bus.js +330 -0
  275. claude_mpm/dashboard/static/built/shared/event-filter-service.js +540 -0
  276. claude_mpm/dashboard/static/built/shared/logger.js +385 -0
  277. claude_mpm/dashboard/static/built/shared/page-structure.js +249 -0
  278. claude_mpm/dashboard/static/built/shared/tooltip-service.js +253 -0
  279. claude_mpm/dashboard/static/built/socket-client.js +1 -1
  280. claude_mpm/dashboard/static/built/tab-isolation-fix.js +185 -0
  281. claude_mpm/dashboard/static/css/dashboard.css +588 -6
  282. claude_mpm/dashboard/static/dist/assets/events.DjpNxWNo.css +1 -0
  283. claude_mpm/dashboard/static/dist/components/activity-tree.js +1 -1
  284. claude_mpm/dashboard/static/dist/components/agent-inference.js +1 -1
  285. claude_mpm/dashboard/static/dist/components/code-tree.js +1 -1
  286. claude_mpm/dashboard/static/dist/components/code-viewer.js +1 -1
  287. claude_mpm/dashboard/static/dist/components/event-processor.js +1 -1
  288. claude_mpm/dashboard/static/dist/components/event-viewer.js +1 -1
  289. claude_mpm/dashboard/static/dist/components/export-manager.js +1 -1
  290. claude_mpm/dashboard/static/dist/components/file-tool-tracker.js +1 -1
  291. claude_mpm/dashboard/static/dist/components/file-viewer.js +2 -0
  292. claude_mpm/dashboard/static/dist/components/module-viewer.js +1 -1
  293. claude_mpm/dashboard/static/dist/components/session-manager.js +1 -1
  294. claude_mpm/dashboard/static/dist/components/unified-data-viewer.js +1 -1
  295. claude_mpm/dashboard/static/dist/components/working-directory.js +1 -1
  296. claude_mpm/dashboard/static/dist/dashboard.js +1 -1
  297. claude_mpm/dashboard/static/dist/react/events.js +30 -0
  298. claude_mpm/dashboard/static/dist/socket-client.js +1 -1
  299. claude_mpm/dashboard/static/events.html +607 -0
  300. claude_mpm/dashboard/static/index.html +635 -0
  301. claude_mpm/dashboard/static/js/components/activity-tree.js +3 -17
  302. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +4 -1
  303. claude_mpm/dashboard/static/js/components/agent-inference.js +3 -0
  304. claude_mpm/dashboard/static/js/components/build-tracker.js +8 -0
  305. claude_mpm/dashboard/static/js/components/code-simple.js +857 -0
  306. claude_mpm/dashboard/static/js/components/diff-viewer.js +891 -0
  307. claude_mpm/dashboard/static/js/components/event-processor.js +3 -0
  308. claude_mpm/dashboard/static/js/components/event-viewer.js +39 -2
  309. claude_mpm/dashboard/static/js/components/export-manager.js +3 -0
  310. claude_mpm/dashboard/static/js/components/file-change-tracker.js +443 -0
  311. claude_mpm/dashboard/static/js/components/file-change-viewer.js +690 -0
  312. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +30 -10
  313. claude_mpm/dashboard/static/js/components/file-viewer.js +580 -0
  314. claude_mpm/dashboard/static/js/components/module-viewer.js +26 -0
  315. claude_mpm/dashboard/static/js/components/session-manager.js +7 -7
  316. claude_mpm/dashboard/static/js/components/socket-manager.js +4 -0
  317. claude_mpm/dashboard/static/js/components/ui-state-manager.js +356 -41
  318. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +455 -23
  319. claude_mpm/dashboard/static/js/components/working-directory.js +44 -9
  320. claude_mpm/dashboard/static/js/dashboard.js +245 -132
  321. claude_mpm/dashboard/static/js/shared/dom-helpers.js +396 -0
  322. claude_mpm/dashboard/static/js/shared/event-bus.js +330 -0
  323. claude_mpm/dashboard/static/js/shared/logger.js +385 -0
  324. claude_mpm/dashboard/static/js/shared/tooltip-service.js +253 -0
  325. claude_mpm/dashboard/static/js/socket-client.js +49 -22
  326. claude_mpm/dashboard/static/js/stores/dashboard-store.js +562 -0
  327. claude_mpm/dashboard/static/js/tab-isolation-fix.js +185 -0
  328. claude_mpm/dashboard/static/legacy/activity.html +736 -0
  329. claude_mpm/dashboard/static/legacy/agents.html +786 -0
  330. claude_mpm/dashboard/static/legacy/files.html +747 -0
  331. claude_mpm/dashboard/static/legacy/tools.html +831 -0
  332. claude_mpm/dashboard/static/monitors.html +431 -0
  333. claude_mpm/dashboard/static/production/events.html +659 -0
  334. claude_mpm/dashboard/static/production/main.html +698 -0
  335. claude_mpm/dashboard/static/production/monitors.html +483 -0
  336. claude_mpm/dashboard/static/socket.io.min.js +7 -0
  337. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +7 -0
  338. claude_mpm/dashboard/static/test-archive/dashboard.html +635 -0
  339. claude_mpm/dashboard/static/test-archive/debug-events.html +147 -0
  340. claude_mpm/dashboard/static/test-archive/test-navigation.html +256 -0
  341. claude_mpm/dashboard/static/test-archive/test-react-exports.html +180 -0
  342. claude_mpm/dashboard/static/test-archive/test_debug.html +25 -0
  343. claude_mpm/dashboard/templates/code_simple.html +153 -0
  344. claude_mpm/dashboard/templates/index.html +112 -109
  345. claude_mpm/experimental/cli_enhancements.py +4 -2
  346. claude_mpm/generators/agent_profile_generator.py +5 -3
  347. claude_mpm/hooks/__init__.py +37 -1
  348. claude_mpm/hooks/base_hook.py +5 -4
  349. claude_mpm/hooks/claude_hooks/connection_pool.py +4 -4
  350. claude_mpm/hooks/claude_hooks/event_handlers.py +21 -18
  351. claude_mpm/hooks/claude_hooks/hook_handler.py +29 -22
  352. claude_mpm/hooks/claude_hooks/installer.py +67 -22
  353. claude_mpm/hooks/claude_hooks/memory_integration.py +3 -3
  354. claude_mpm/hooks/claude_hooks/response_tracking.py +57 -17
  355. claude_mpm/hooks/claude_hooks/services/connection_manager.py +62 -64
  356. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +140 -76
  357. claude_mpm/hooks/claude_hooks/services/state_manager.py +11 -9
  358. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +3 -3
  359. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  360. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  361. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  362. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  363. claude_mpm/hooks/instruction_reinforcement.py +301 -0
  364. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  365. claude_mpm/hooks/kuzu_memory_hook.py +386 -0
  366. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  367. claude_mpm/hooks/memory_integration_hook.py +1 -1
  368. claude_mpm/hooks/session_resume_hook.py +121 -0
  369. claude_mpm/hooks/templates/pre_tool_use_simple.py +78 -0
  370. claude_mpm/hooks/templates/pre_tool_use_template.py +323 -0
  371. claude_mpm/hooks/tool_call_interceptor.py +8 -5
  372. claude_mpm/hooks/validation_hooks.py +3 -3
  373. claude_mpm/init.py +23 -4
  374. claude_mpm/models/agent_session.py +8 -6
  375. claude_mpm/models/resume_log.py +340 -0
  376. claude_mpm/scripts/claude-hook-handler.sh +33 -7
  377. claude_mpm/scripts/launch_monitor.py +85 -0
  378. claude_mpm/scripts/mcp_server.py +3 -5
  379. claude_mpm/scripts/mpm_doctor.py +3 -2
  380. claude_mpm/scripts/socketio_daemon.py +159 -512
  381. claude_mpm/services/__init__.py +144 -160
  382. claude_mpm/services/agents/__init__.py +18 -5
  383. claude_mpm/services/agents/agent_builder.py +13 -11
  384. claude_mpm/services/agents/auto_config_manager.py +796 -0
  385. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  386. claude_mpm/services/agents/deployment/agent_deployment.py +38 -15
  387. claude_mpm/services/agents/deployment/agent_discovery_service.py +125 -7
  388. claude_mpm/services/agents/deployment/agent_filesystem_manager.py +5 -5
  389. claude_mpm/services/agents/deployment/agent_format_converter.py +56 -12
  390. claude_mpm/services/agents/deployment/agent_lifecycle_manager.py +4 -2
  391. claude_mpm/services/agents/deployment/agent_operation_service.py +2 -2
  392. claude_mpm/services/agents/deployment/agent_record_service.py +4 -4
  393. claude_mpm/services/agents/deployment/agent_state_service.py +2 -2
  394. claude_mpm/services/agents/deployment/agent_template_builder.py +715 -47
  395. claude_mpm/services/agents/deployment/agent_validator.py +31 -7
  396. claude_mpm/services/agents/deployment/agent_version_manager.py +8 -5
  397. claude_mpm/services/agents/deployment/agent_versioning.py +1 -1
  398. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  399. claude_mpm/services/agents/deployment/deployment_config_loader.py +131 -7
  400. claude_mpm/services/agents/deployment/deployment_type_detector.py +10 -14
  401. claude_mpm/services/agents/deployment/deployment_wrapper.py +58 -0
  402. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  403. claude_mpm/services/agents/deployment/local_template_deployment.py +360 -0
  404. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +134 -38
  405. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +8 -7
  406. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  407. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  408. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +7 -5
  409. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  410. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  411. claude_mpm/services/agents/deployment/system_instructions_deployer.py +9 -6
  412. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  413. claude_mpm/services/agents/deployment/validation/template_validator.py +64 -44
  414. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  415. claude_mpm/services/agents/loading/agent_profile_loader.py +10 -9
  416. claude_mpm/services/agents/loading/base_agent_manager.py +16 -6
  417. claude_mpm/services/agents/loading/framework_agent_loader.py +2 -2
  418. claude_mpm/services/agents/local_template_manager.py +744 -0
  419. claude_mpm/services/agents/management/agent_capabilities_generator.py +3 -2
  420. claude_mpm/services/agents/management/agent_management_service.py +5 -5
  421. claude_mpm/services/agents/memory/agent_memory_manager.py +32 -29
  422. claude_mpm/services/agents/memory/content_manager.py +17 -9
  423. claude_mpm/services/agents/memory/memory_categorization_service.py +4 -2
  424. claude_mpm/services/agents/memory/memory_file_service.py +32 -6
  425. claude_mpm/services/agents/memory/memory_format_service.py +6 -4
  426. claude_mpm/services/agents/memory/memory_limits_service.py +4 -2
  427. claude_mpm/services/agents/memory/template_generator.py +3 -3
  428. claude_mpm/services/agents/observers.py +547 -0
  429. claude_mpm/services/agents/recommender.py +615 -0
  430. claude_mpm/services/agents/registry/deployed_agent_discovery.py +3 -3
  431. claude_mpm/services/agents/registry/modification_tracker.py +30 -19
  432. claude_mpm/services/async_session_logger.py +141 -98
  433. claude_mpm/services/claude_session_logger.py +82 -74
  434. claude_mpm/services/cli/agent_cleanup_service.py +5 -0
  435. claude_mpm/services/cli/agent_listing_service.py +5 -5
  436. claude_mpm/services/cli/agent_validation_service.py +3 -1
  437. claude_mpm/services/cli/memory_crud_service.py +12 -7
  438. claude_mpm/services/cli/memory_output_formatter.py +2 -2
  439. claude_mpm/services/cli/resume_service.py +617 -0
  440. claude_mpm/services/cli/session_manager.py +104 -13
  441. claude_mpm/services/cli/session_pause_manager.py +504 -0
  442. claude_mpm/services/cli/session_resume_helper.py +372 -0
  443. claude_mpm/services/cli/startup_checker.py +13 -10
  444. claude_mpm/services/cli/unified_dashboard_manager.py +439 -0
  445. claude_mpm/services/command_deployment_service.py +9 -7
  446. claude_mpm/services/command_handler_service.py +11 -5
  447. claude_mpm/services/core/__init__.py +33 -1
  448. claude_mpm/services/core/base.py +26 -11
  449. claude_mpm/services/core/interfaces/__init__.py +90 -3
  450. claude_mpm/services/core/interfaces/agent.py +184 -0
  451. claude_mpm/services/core/interfaces/health.py +172 -0
  452. claude_mpm/services/core/interfaces/model.py +281 -0
  453. claude_mpm/services/core/interfaces/process.py +372 -0
  454. claude_mpm/services/core/interfaces/project.py +121 -0
  455. claude_mpm/services/core/interfaces/restart.py +307 -0
  456. claude_mpm/services/core/interfaces/stability.py +260 -0
  457. claude_mpm/services/core/interfaces.py +56 -1
  458. claude_mpm/services/core/memory_manager.py +92 -47
  459. claude_mpm/services/core/models/__init__.py +79 -0
  460. claude_mpm/services/core/models/agent_config.py +384 -0
  461. claude_mpm/services/core/models/health.py +162 -0
  462. claude_mpm/services/core/models/process.py +239 -0
  463. claude_mpm/services/core/models/restart.py +302 -0
  464. claude_mpm/services/core/models/stability.py +264 -0
  465. claude_mpm/services/core/models/toolchain.py +306 -0
  466. claude_mpm/services/core/path_resolver.py +36 -14
  467. claude_mpm/services/diagnostics/__init__.py +2 -2
  468. claude_mpm/services/diagnostics/checks/__init__.py +4 -2
  469. claude_mpm/services/diagnostics/checks/agent_check.py +30 -32
  470. claude_mpm/services/diagnostics/checks/claude_code_check.py +270 -0
  471. claude_mpm/services/diagnostics/checks/common_issues_check.py +28 -27
  472. claude_mpm/services/diagnostics/checks/configuration_check.py +26 -25
  473. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  474. claude_mpm/services/diagnostics/checks/installation_check.py +165 -60
  475. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  476. claude_mpm/services/diagnostics/checks/mcp_check.py +57 -43
  477. claude_mpm/services/diagnostics/checks/mcp_services_check.py +1066 -0
  478. claude_mpm/services/diagnostics/checks/monitor_check.py +24 -23
  479. claude_mpm/services/diagnostics/checks/startup_log_check.py +14 -11
  480. claude_mpm/services/diagnostics/diagnostic_runner.py +22 -13
  481. claude_mpm/services/diagnostics/doctor_reporter.py +275 -47
  482. claude_mpm/services/diagnostics/models.py +37 -21
  483. claude_mpm/services/event_aggregator.py +5 -3
  484. claude_mpm/services/event_bus/direct_relay.py +8 -4
  485. claude_mpm/services/event_bus/event_bus.py +51 -9
  486. claude_mpm/services/event_bus/relay.py +33 -14
  487. claude_mpm/services/events/consumers/dead_letter.py +7 -5
  488. claude_mpm/services/events/core.py +5 -6
  489. claude_mpm/services/events/producers/hook.py +6 -6
  490. claude_mpm/services/events/producers/system.py +8 -8
  491. claude_mpm/services/exceptions.py +5 -5
  492. claude_mpm/services/framework_claude_md_generator/__init__.py +1 -1
  493. claude_mpm/services/framework_claude_md_generator/content_assembler.py +5 -5
  494. claude_mpm/services/framework_claude_md_generator/content_validator.py +2 -2
  495. claude_mpm/services/framework_claude_md_generator/deployment_manager.py +3 -3
  496. claude_mpm/services/framework_claude_md_generator/section_generators/__init__.py +2 -2
  497. claude_mpm/services/framework_claude_md_generator/version_manager.py +1 -1
  498. claude_mpm/services/hook_installer_service.py +506 -0
  499. claude_mpm/services/hook_service.py +5 -6
  500. claude_mpm/services/infrastructure/context_preservation.py +13 -11
  501. claude_mpm/services/infrastructure/daemon_manager.py +9 -9
  502. claude_mpm/services/infrastructure/logging.py +2 -2
  503. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  504. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  505. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  506. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  507. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  508. claude_mpm/services/infrastructure/monitoring/resources.py +8 -7
  509. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  510. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  511. claude_mpm/services/local_ops/__init__.py +165 -0
  512. claude_mpm/services/local_ops/crash_detector.py +257 -0
  513. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  514. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  515. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  516. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  517. claude_mpm/services/local_ops/health_manager.py +430 -0
  518. claude_mpm/services/local_ops/log_monitor.py +396 -0
  519. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  520. claude_mpm/services/local_ops/process_manager.py +595 -0
  521. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  522. claude_mpm/services/local_ops/restart_manager.py +401 -0
  523. claude_mpm/services/local_ops/restart_policy.py +387 -0
  524. claude_mpm/services/local_ops/state_manager.py +372 -0
  525. claude_mpm/services/local_ops/unified_manager.py +600 -0
  526. claude_mpm/services/mcp_config_manager.py +1612 -0
  527. claude_mpm/services/mcp_gateway/__init__.py +97 -93
  528. claude_mpm/services/mcp_gateway/auto_configure.py +43 -38
  529. claude_mpm/services/mcp_gateway/config/config_loader.py +3 -3
  530. claude_mpm/services/mcp_gateway/config/configuration.py +23 -4
  531. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  532. claude_mpm/services/mcp_gateway/core/base.py +20 -33
  533. claude_mpm/services/mcp_gateway/core/process_pool.py +585 -31
  534. claude_mpm/services/mcp_gateway/core/singleton_manager.py +2 -2
  535. claude_mpm/services/mcp_gateway/core/startup_verification.py +3 -3
  536. claude_mpm/services/mcp_gateway/main.py +90 -15
  537. claude_mpm/services/mcp_gateway/registry/service_registry.py +4 -2
  538. claude_mpm/services/mcp_gateway/registry/tool_registry.py +12 -9
  539. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +4 -4
  540. claude_mpm/services/mcp_gateway/server/stdio_server.py +9 -15
  541. claude_mpm/services/mcp_gateway/tools/__init__.py +14 -2
  542. claude_mpm/services/mcp_gateway/tools/base_adapter.py +15 -15
  543. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +10 -9
  544. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +654 -0
  545. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +36 -34
  546. claude_mpm/services/mcp_gateway/tools/hello_world.py +8 -8
  547. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +551 -0
  548. claude_mpm/services/mcp_gateway/utils/__init__.py +14 -0
  549. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +160 -0
  550. claude_mpm/services/mcp_gateway/utils/update_preferences.py +170 -0
  551. claude_mpm/services/mcp_service_verifier.py +729 -0
  552. claude_mpm/services/memory/builder.py +9 -8
  553. claude_mpm/services/memory/cache/shared_prompt_cache.py +2 -1
  554. claude_mpm/services/memory/cache/simple_cache.py +2 -2
  555. claude_mpm/services/memory/failure_tracker.py +578 -0
  556. claude_mpm/services/memory/indexed_memory.py +8 -8
  557. claude_mpm/services/memory/optimizer.py +8 -9
  558. claude_mpm/services/memory/router.py +3 -3
  559. claude_mpm/services/memory_hook_service.py +165 -4
  560. claude_mpm/services/model/__init__.py +147 -0
  561. claude_mpm/services/model/base_provider.py +365 -0
  562. claude_mpm/services/model/claude_provider.py +412 -0
  563. claude_mpm/services/model/model_router.py +453 -0
  564. claude_mpm/services/model/ollama_provider.py +415 -0
  565. claude_mpm/services/monitor/__init__.py +20 -0
  566. claude_mpm/services/monitor/daemon.py +671 -0
  567. claude_mpm/services/monitor/daemon_manager.py +963 -0
  568. claude_mpm/services/monitor/event_emitter.py +350 -0
  569. claude_mpm/services/monitor/handlers/__init__.py +21 -0
  570. claude_mpm/services/monitor/handlers/code_analysis.py +332 -0
  571. claude_mpm/services/monitor/handlers/dashboard.py +299 -0
  572. claude_mpm/services/monitor/handlers/file.py +264 -0
  573. claude_mpm/services/monitor/handlers/hooks.py +512 -0
  574. claude_mpm/services/monitor/management/__init__.py +18 -0
  575. claude_mpm/services/monitor/management/health.py +124 -0
  576. claude_mpm/services/monitor/management/lifecycle.py +724 -0
  577. claude_mpm/services/monitor/server.py +817 -0
  578. claude_mpm/services/monitor_build_service.py +2 -2
  579. claude_mpm/services/native_agent_converter.py +356 -0
  580. claude_mpm/services/orphan_detection.py +786 -0
  581. claude_mpm/services/port_manager.py +2 -2
  582. claude_mpm/services/project/__init__.py +23 -0
  583. claude_mpm/services/project/analyzer.py +3 -3
  584. claude_mpm/services/project/architecture_analyzer.py +5 -5
  585. claude_mpm/services/project/archive_manager.py +1045 -0
  586. claude_mpm/services/project/dependency_analyzer.py +4 -4
  587. claude_mpm/services/project/detection_strategies.py +719 -0
  588. claude_mpm/services/project/documentation_manager.py +553 -0
  589. claude_mpm/services/project/enhanced_analyzer.py +572 -0
  590. claude_mpm/services/project/metrics_collector.py +4 -4
  591. claude_mpm/services/project/project_organizer.py +1005 -0
  592. claude_mpm/services/project/registry.py +13 -7
  593. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  594. claude_mpm/services/project_port_allocator.py +596 -0
  595. claude_mpm/services/response_tracker.py +21 -10
  596. claude_mpm/services/runner_configuration_service.py +1 -0
  597. claude_mpm/services/self_upgrade_service.py +500 -0
  598. claude_mpm/services/session_management_service.py +7 -5
  599. claude_mpm/services/session_manager.py +380 -0
  600. claude_mpm/services/shared/__init__.py +2 -1
  601. claude_mpm/services/shared/async_service_base.py +16 -27
  602. claude_mpm/services/shared/config_service_base.py +17 -14
  603. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  604. claude_mpm/services/shared/service_factory.py +8 -5
  605. claude_mpm/services/socketio/client_proxy.py +60 -5
  606. claude_mpm/services/socketio/dashboard_server.py +361 -0
  607. claude_mpm/services/socketio/event_normalizer.py +10 -6
  608. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  609. claude_mpm/services/socketio/handlers/base.py +2 -2
  610. claude_mpm/services/socketio/handlers/code_analysis.py +90 -27
  611. claude_mpm/services/socketio/handlers/connection.py +21 -40
  612. claude_mpm/services/socketio/handlers/connection_handler.py +13 -10
  613. claude_mpm/services/socketio/handlers/file.py +46 -10
  614. claude_mpm/services/socketio/handlers/git.py +8 -8
  615. claude_mpm/services/socketio/handlers/hook.py +29 -17
  616. claude_mpm/services/socketio/handlers/registry.py +4 -2
  617. claude_mpm/services/socketio/monitor_client.py +364 -0
  618. claude_mpm/services/socketio/server/broadcaster.py +9 -7
  619. claude_mpm/services/socketio/server/connection_manager.py +2 -2
  620. claude_mpm/services/socketio/server/core.py +141 -4
  621. claude_mpm/services/socketio/server/eventbus_integration.py +20 -14
  622. claude_mpm/services/socketio/server/main.py +23 -21
  623. claude_mpm/services/socketio_client_manager.py +4 -4
  624. claude_mpm/services/subprocess_launcher_service.py +19 -15
  625. claude_mpm/services/system_instructions_service.py +2 -2
  626. claude_mpm/services/ticket_services/formatter_service.py +1 -1
  627. claude_mpm/services/ticket_services/validation_service.py +5 -5
  628. claude_mpm/services/unified/__init__.py +65 -0
  629. claude_mpm/services/unified/analyzer_strategies/__init__.py +44 -0
  630. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +518 -0
  631. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +680 -0
  632. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +903 -0
  633. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +746 -0
  634. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +733 -0
  635. claude_mpm/services/unified/config_strategies/__init__.py +175 -0
  636. claude_mpm/services/unified/config_strategies/config_schema.py +731 -0
  637. claude_mpm/services/unified/config_strategies/context_strategy.py +747 -0
  638. claude_mpm/services/unified/config_strategies/error_handling_strategy.py +1005 -0
  639. claude_mpm/services/unified/config_strategies/file_loader_strategy.py +881 -0
  640. claude_mpm/services/unified/config_strategies/unified_config_service.py +823 -0
  641. claude_mpm/services/unified/config_strategies/validation_strategy.py +1148 -0
  642. claude_mpm/services/unified/deployment_strategies/__init__.py +97 -0
  643. claude_mpm/services/unified/deployment_strategies/base.py +553 -0
  644. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +573 -0
  645. claude_mpm/services/unified/deployment_strategies/local.py +607 -0
  646. claude_mpm/services/unified/deployment_strategies/utils.py +667 -0
  647. claude_mpm/services/unified/deployment_strategies/vercel.py +475 -0
  648. claude_mpm/services/unified/interfaces.py +475 -0
  649. claude_mpm/services/unified/migration.py +509 -0
  650. claude_mpm/services/unified/strategies.py +534 -0
  651. claude_mpm/services/unified/unified_analyzer.py +542 -0
  652. claude_mpm/services/unified/unified_config.py +691 -0
  653. claude_mpm/services/unified/unified_deployment.py +470 -0
  654. claude_mpm/services/utility_service.py +6 -3
  655. claude_mpm/services/version_control/branch_strategy.py +2 -2
  656. claude_mpm/services/version_control/conflict_resolution.py +8 -4
  657. claude_mpm/services/version_control/git_operations.py +26 -24
  658. claude_mpm/services/version_control/semantic_versioning.py +14 -14
  659. claude_mpm/services/version_control/version_parser.py +14 -11
  660. claude_mpm/services/version_service.py +104 -1
  661. claude_mpm/skills/__init__.py +42 -0
  662. claude_mpm/skills/agent_skills_injector.py +324 -0
  663. claude_mpm/skills/bundled/LICENSE_ATTRIBUTIONS.md +79 -0
  664. claude_mpm/skills/bundled/__init__.py +6 -0
  665. claude_mpm/skills/bundled/api-documentation.md +393 -0
  666. claude_mpm/skills/bundled/async-testing.md +571 -0
  667. claude_mpm/skills/bundled/code-review.md +143 -0
  668. claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
  669. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
  670. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
  671. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
  672. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
  673. claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
  674. claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
  675. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
  676. claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
  677. claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
  678. claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
  679. claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
  680. claude_mpm/skills/bundled/database-migration.md +199 -0
  681. claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
  682. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
  683. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
  684. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
  685. claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
  686. claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
  687. claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
  688. claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
  689. claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
  690. claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
  691. claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
  692. claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
  693. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
  694. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
  695. claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
  696. claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
  697. claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
  698. claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
  699. claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
  700. claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
  701. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  702. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  703. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  704. claude_mpm/skills/bundled/git-workflow.md +414 -0
  705. claude_mpm/skills/bundled/imagemagick.md +204 -0
  706. claude_mpm/skills/bundled/infrastructure/env-manager/scripts/validate_env.py +576 -0
  707. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  708. claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
  709. claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
  710. claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
  711. claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
  712. claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
  713. claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
  714. claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
  715. claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
  716. claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
  717. claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
  718. claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
  719. claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
  720. claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
  721. claude_mpm/skills/bundled/main/mcp-builder/scripts/connections.py +157 -0
  722. claude_mpm/skills/bundled/main/mcp-builder/scripts/evaluation.py +425 -0
  723. claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
  724. claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
  725. claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
  726. claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
  727. claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
  728. claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
  729. claude_mpm/skills/bundled/main/skill-creator/scripts/init_skill.py +303 -0
  730. claude_mpm/skills/bundled/main/skill-creator/scripts/package_skill.py +113 -0
  731. claude_mpm/skills/bundled/main/skill-creator/scripts/quick_validate.py +72 -0
  732. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  733. claude_mpm/skills/bundled/pdf.md +141 -0
  734. claude_mpm/skills/bundled/performance-profiling.md +573 -0
  735. claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
  736. claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
  737. claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
  738. claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
  739. claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
  740. claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
  741. claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
  742. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  743. claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
  744. claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
  745. claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
  746. claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
  747. claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
  748. claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
  749. claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
  750. claude_mpm/skills/bundled/security-scanning.md +327 -0
  751. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  752. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  753. claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
  754. claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
  755. claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
  756. claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
  757. claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
  758. claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
  759. claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
  760. claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
  761. claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
  762. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
  763. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
  764. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
  765. claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
  766. claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
  767. claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
  768. claude_mpm/skills/bundled/testing/webapp-testing/examples/console_logging.py +35 -0
  769. claude_mpm/skills/bundled/testing/webapp-testing/examples/element_discovery.py +44 -0
  770. claude_mpm/skills/bundled/testing/webapp-testing/examples/static_html_automation.py +34 -0
  771. claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
  772. claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
  773. claude_mpm/skills/bundled/testing/webapp-testing/scripts/with_server.py +129 -0
  774. claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
  775. claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
  776. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  777. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  778. claude_mpm/skills/bundled/xlsx.md +157 -0
  779. claude_mpm/skills/registry.py +286 -0
  780. claude_mpm/skills/skill_manager.py +310 -0
  781. claude_mpm/skills/skills_registry.py +348 -0
  782. claude_mpm/skills/skills_service.py +739 -0
  783. claude_mpm/storage/state_storage.py +31 -31
  784. claude_mpm/tools/__main__.py +1 -1
  785. claude_mpm/tools/code_tree_analyzer/__init__.py +45 -0
  786. claude_mpm/tools/code_tree_analyzer/analysis.py +299 -0
  787. claude_mpm/tools/code_tree_analyzer/cache.py +131 -0
  788. claude_mpm/tools/code_tree_analyzer/core.py +380 -0
  789. claude_mpm/tools/code_tree_analyzer/discovery.py +403 -0
  790. claude_mpm/tools/code_tree_analyzer/events.py +168 -0
  791. claude_mpm/tools/code_tree_analyzer/gitignore.py +308 -0
  792. claude_mpm/tools/code_tree_analyzer/models.py +39 -0
  793. claude_mpm/tools/code_tree_analyzer/multilang_analyzer.py +224 -0
  794. claude_mpm/tools/code_tree_analyzer/python_analyzer.py +284 -0
  795. claude_mpm/tools/code_tree_builder.py +6 -6
  796. claude_mpm/tools/code_tree_events.py +14 -10
  797. claude_mpm/tools/socketio_debug.py +11 -11
  798. claude_mpm/utils/agent_dependency_loader.py +108 -27
  799. claude_mpm/utils/common.py +544 -0
  800. claude_mpm/utils/config_manager.py +12 -6
  801. claude_mpm/utils/database_connector.py +298 -0
  802. claude_mpm/utils/dependency_cache.py +2 -2
  803. claude_mpm/utils/dependency_strategies.py +15 -10
  804. claude_mpm/utils/display_helper.py +260 -0
  805. claude_mpm/utils/environment_context.py +4 -3
  806. claude_mpm/utils/error_handler.py +5 -3
  807. claude_mpm/utils/file_utils.py +13 -14
  808. claude_mpm/utils/git_analyzer.py +407 -0
  809. claude_mpm/utils/log_cleanup.py +627 -0
  810. claude_mpm/utils/path_operations.py +7 -4
  811. claude_mpm/utils/robust_installer.py +133 -24
  812. claude_mpm/utils/session_logging.py +2 -2
  813. claude_mpm/utils/subprocess_utils.py +9 -8
  814. claude_mpm/validation/agent_validator.py +6 -6
  815. claude_mpm/validation/frontmatter_validator.py +6 -6
  816. claude_mpm-4.24.0.dist-info/METADATA +675 -0
  817. claude_mpm-4.24.0.dist-info/RECORD +1018 -0
  818. {claude_mpm-4.1.26.dist-info → claude_mpm-4.24.0.dist-info}/entry_points.txt +1 -0
  819. claude_mpm/agents/INSTRUCTIONS.md +0 -261
  820. claude_mpm/agents/templates/agent-manager.md +0 -619
  821. claude_mpm/cli/commands/configure_tui.py +0 -1927
  822. claude_mpm/cli/commands/mpm_init.py +0 -594
  823. claude_mpm/cli/commands/socketio_monitor.py +0 -233
  824. claude_mpm/dashboard/static/css/code-tree.css +0 -1408
  825. claude_mpm/dashboard/static/js/components/code-tree.js +0 -3220
  826. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -480
  827. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  828. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1040
  829. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  830. claude_mpm/scripts/socketio_daemon_hardened.py +0 -937
  831. claude_mpm/scripts/socketio_daemon_wrapper.py +0 -78
  832. claude_mpm/scripts/socketio_server_manager.py +0 -349
  833. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  834. claude_mpm/services/cli/dashboard_launcher.py +0 -423
  835. claude_mpm/services/cli/socketio_manager.py +0 -537
  836. claude_mpm/services/diagnostics/checks/claude_desktop_check.py +0 -286
  837. claude_mpm/services/mcp_gateway/tools/ticket_tools.py +0 -645
  838. claude_mpm/services/mcp_gateway/tools/unified_ticket_tool.py +0 -602
  839. claude_mpm/services/project/analyzer_refactored.py +0 -450
  840. claude_mpm/tools/code_tree_analyzer.py +0 -1693
  841. claude_mpm-4.1.26.dist-info/METADATA +0 -332
  842. claude_mpm-4.1.26.dist-info/RECORD +0 -606
  843. {claude_mpm-4.1.26.dist-info → claude_mpm-4.24.0.dist-info}/WHEEL +0 -0
  844. {claude_mpm-4.1.26.dist-info → claude_mpm-4.24.0.dist-info}/licenses/LICENSE +0 -0
  845. {claude_mpm-4.1.26.dist-info → claude_mpm-4.24.0.dist-info}/top_level.txt +0 -0
@@ -1,1693 +0,0 @@
1
- #!/usr/bin/env python3
2
- """
3
- Code Tree Analyzer
4
- ==================
5
-
6
- WHY: Analyzes source code using AST to extract structure and metrics,
7
- supporting multiple languages and emitting incremental events for visualization.
8
-
9
- DESIGN DECISIONS:
10
- - Use Python's ast module for Python files
11
- - Use tree-sitter for multi-language support
12
- - Extract comprehensive metadata (complexity, docstrings, etc.)
13
- - Cache parsed results to avoid re-processing
14
- - Support incremental processing with checkpoints
15
- """
16
-
17
- import ast
18
- import hashlib
19
- import json
20
- import time
21
- from dataclasses import dataclass
22
- from pathlib import Path
23
- from typing import Any, Dict, List, Optional
24
-
25
- try:
26
- import pathspec
27
-
28
- PATHSPEC_AVAILABLE = True
29
- except ImportError:
30
- PATHSPEC_AVAILABLE = False
31
- pathspec = None
32
-
33
- try:
34
- import tree_sitter
35
- import tree_sitter_javascript
36
- import tree_sitter_python
37
- import tree_sitter_typescript
38
-
39
- TREE_SITTER_AVAILABLE = True
40
- except ImportError:
41
- TREE_SITTER_AVAILABLE = False
42
- tree_sitter = None
43
-
44
- from ..core.logging_config import get_logger
45
- from .code_tree_events import CodeNodeEvent, CodeTreeEventEmitter
46
-
47
-
48
- class GitignoreManager:
49
- """Manages .gitignore pattern matching for file filtering.
50
-
51
- WHY: Properly respecting .gitignore patterns ensures we don't analyze
52
- or display files that should be ignored in the repository.
53
- """
54
-
55
- # Default patterns that should always be ignored
56
- DEFAULT_PATTERNS = [
57
- ".git/",
58
- "__pycache__/",
59
- "*.pyc",
60
- "*.pyo",
61
- ".DS_Store",
62
- ".pytest_cache/",
63
- ".mypy_cache/",
64
- "dist/",
65
- "build/",
66
- "*.egg-info/",
67
- ".coverage",
68
- ".tox/",
69
- "htmlcov/",
70
- ".idea/",
71
- ".vscode/",
72
- "*.swp",
73
- "*.swo",
74
- "*~",
75
- "Thumbs.db",
76
- "node_modules/",
77
- ".venv/",
78
- "venv/",
79
- "env/",
80
- ".env",
81
- "*.log",
82
- ".ipynb_checkpoints/",
83
- "__MACOSX/",
84
- ".Spotlight-V100/",
85
- ".Trashes/",
86
- "desktop.ini",
87
- ]
88
-
89
- # Additional patterns to hide dotfiles (when enabled)
90
- DOTFILE_PATTERNS = [
91
- ".*", # All dotfiles
92
- ".*/", # All dot directories
93
- ]
94
-
95
- # Important files/directories to always show
96
- DOTFILE_EXCEPTIONS = {
97
- # Removed .gitignore from exceptions - it should be hidden by default
98
- ".env.example",
99
- ".env.sample",
100
- ".gitlab-ci.yml",
101
- ".travis.yml",
102
- ".dockerignore",
103
- ".editorconfig",
104
- ".eslintrc",
105
- ".prettierrc",
106
- # Removed .github from exceptions - it should be hidden by default
107
- }
108
-
109
- def __init__(self):
110
- """Initialize the GitignoreManager."""
111
- self.logger = get_logger(__name__)
112
- self._pathspec_cache: Dict[str, Any] = {}
113
- self._gitignore_cache: Dict[str, List[str]] = {}
114
- self._use_pathspec = PATHSPEC_AVAILABLE
115
-
116
- if not self._use_pathspec:
117
- self.logger.warning(
118
- "pathspec library not available - using basic pattern matching"
119
- )
120
-
121
- def get_ignore_patterns(self, working_dir: Path) -> List[str]:
122
- """Get all ignore patterns for a directory.
123
-
124
- Args:
125
- working_dir: The working directory to search for .gitignore files
126
-
127
- Returns:
128
- Combined list of ignore patterns from all sources
129
- """
130
- # Always include default patterns
131
- patterns = self.DEFAULT_PATTERNS.copy()
132
-
133
- # Don't add dotfile patterns here - handle them separately in should_ignore
134
- # This prevents exceptions from being overridden by the .* pattern
135
-
136
- # Find and parse .gitignore files
137
- gitignore_files = self._find_gitignore_files(working_dir)
138
- for gitignore_file in gitignore_files:
139
- patterns.extend(self._parse_gitignore(gitignore_file))
140
-
141
- return patterns
142
-
143
- def should_ignore(self, path: Path, working_dir: Path) -> bool:
144
- """Check if a path should be ignored based on patterns.
145
-
146
- Args:
147
- path: The path to check
148
- working_dir: The working directory (for relative path calculation)
149
-
150
- Returns:
151
- True if the path should be ignored
152
- """
153
- # Get the filename
154
- filename = path.name
155
-
156
- # 1. ALWAYS hide system files regardless of settings
157
- ALWAYS_HIDE = {".DS_Store", "Thumbs.db", ".pyc", ".pyo", ".pyd"}
158
- if filename in ALWAYS_HIDE or filename.endswith((".pyc", ".pyo", ".pyd")):
159
- return True
160
-
161
- # 2. Check dotfiles - ALWAYS filter them out (except exceptions)
162
- if filename.startswith("."):
163
- # Hide all dotfiles except those in the exceptions list
164
- # This means: return True (ignore) if NOT in exceptions
165
- return filename not in self.DOTFILE_EXCEPTIONS
166
-
167
- # Get or create PathSpec for this working directory
168
- pathspec_obj = self._get_pathspec(working_dir)
169
-
170
- if pathspec_obj:
171
- # Use pathspec for accurate matching
172
- try:
173
- rel_path = path.relative_to(working_dir)
174
- rel_path_str = str(rel_path)
175
-
176
- # For directories, also check with trailing slash
177
- if path.is_dir():
178
- return pathspec_obj.match_file(
179
- rel_path_str
180
- ) or pathspec_obj.match_file(rel_path_str + "/")
181
- return pathspec_obj.match_file(rel_path_str)
182
- except ValueError:
183
- # Path is outside working directory
184
- return False
185
- else:
186
- # Fallback to basic pattern matching
187
- return self._basic_should_ignore(path, working_dir)
188
-
189
- def _get_pathspec(self, working_dir: Path) -> Optional[Any]:
190
- """Get or create a PathSpec object for the working directory.
191
-
192
- Args:
193
- working_dir: The working directory
194
-
195
- Returns:
196
- PathSpec object or None if not available
197
- """
198
- if not self._use_pathspec:
199
- return None
200
-
201
- cache_key = str(working_dir)
202
- if cache_key not in self._pathspec_cache:
203
- patterns = self.get_ignore_patterns(working_dir)
204
- try:
205
- self._pathspec_cache[cache_key] = pathspec.PathSpec.from_lines(
206
- "gitwildmatch", patterns
207
- )
208
- except Exception as e:
209
- self.logger.warning(f"Failed to create PathSpec: {e}")
210
- return None
211
-
212
- return self._pathspec_cache[cache_key]
213
-
214
- def _find_gitignore_files(self, working_dir: Path) -> List[Path]:
215
- """Find all .gitignore files in the directory tree.
216
-
217
- Args:
218
- working_dir: The directory to search
219
-
220
- Returns:
221
- List of .gitignore file paths
222
- """
223
- gitignore_files = []
224
-
225
- # Check for .gitignore in working directory
226
- main_gitignore = working_dir / ".gitignore"
227
- if main_gitignore.exists():
228
- gitignore_files.append(main_gitignore)
229
-
230
- # Also check parent directories up to repository root
231
- current = working_dir
232
- while current != current.parent:
233
- parent_gitignore = current.parent / ".gitignore"
234
- if parent_gitignore.exists():
235
- gitignore_files.append(parent_gitignore)
236
-
237
- # Stop if we find a .git directory (repository root)
238
- if (current / ".git").exists():
239
- break
240
-
241
- current = current.parent
242
-
243
- return gitignore_files
244
-
245
- def _parse_gitignore(self, gitignore_path: Path) -> List[str]:
246
- """Parse a .gitignore file and return patterns.
247
-
248
- Args:
249
- gitignore_path: Path to .gitignore file
250
-
251
- Returns:
252
- List of patterns from the file
253
- """
254
- cache_key = str(gitignore_path)
255
-
256
- # Check cache
257
- if cache_key in self._gitignore_cache:
258
- return self._gitignore_cache[cache_key]
259
-
260
- patterns = []
261
- try:
262
- with open(gitignore_path, encoding="utf-8") as f:
263
- for line in f:
264
- line = line.strip()
265
- # Skip empty lines and comments
266
- if line and not line.startswith("#"):
267
- patterns.append(line)
268
-
269
- self._gitignore_cache[cache_key] = patterns
270
- except Exception as e:
271
- self.logger.warning(f"Failed to parse {gitignore_path}: {e}")
272
-
273
- return patterns
274
-
275
- def _basic_should_ignore(self, path: Path, working_dir: Path) -> bool:
276
- """Basic pattern matching fallback when pathspec is not available.
277
-
278
- Args:
279
- path: The path to check
280
- working_dir: The working directory
281
-
282
- Returns:
283
- True if the path should be ignored
284
- """
285
- path_str = str(path)
286
- path_name = path.name
287
-
288
- # 1. ALWAYS hide system files regardless of settings
289
- ALWAYS_HIDE = {".DS_Store", "Thumbs.db", ".pyc", ".pyo", ".pyd"}
290
- if path_name in ALWAYS_HIDE or path_name.endswith((".pyc", ".pyo", ".pyd")):
291
- return True
292
-
293
- # 2. Check dotfiles - ALWAYS filter them out (except exceptions)
294
- if path_name.startswith("."):
295
- # Only show if in exceptions list
296
- return path_name not in self.DOTFILE_EXCEPTIONS
297
-
298
- patterns = self.get_ignore_patterns(working_dir)
299
-
300
- for pattern in patterns:
301
- # Skip dotfile patterns since we already handled them above
302
- if pattern in [".*", ".*/"]:
303
- continue
304
-
305
- # Simple pattern matching
306
- if pattern.endswith("/"):
307
- # Directory pattern
308
- if path.is_dir() and path_name == pattern[:-1]:
309
- return True
310
- elif pattern.startswith("*."):
311
- # Extension pattern
312
- if path_name.endswith(pattern[1:]):
313
- return True
314
- elif "*" in pattern:
315
- # Wildcard pattern (simplified)
316
- import fnmatch
317
-
318
- if fnmatch.fnmatch(path_name, pattern):
319
- return True
320
- elif pattern in path_str:
321
- # Substring match
322
- return True
323
- elif path_name == pattern:
324
- # Exact match
325
- return True
326
-
327
- return False
328
-
329
- def clear_cache(self):
330
- """Clear all caches."""
331
- self._pathspec_cache.clear()
332
- self._gitignore_cache.clear()
333
-
334
-
335
- @dataclass
336
- class CodeNode:
337
- """Represents a node in the code tree."""
338
-
339
- file_path: str
340
- node_type: str
341
- name: str
342
- line_start: int
343
- line_end: int
344
- complexity: int = 0
345
- has_docstring: bool = False
346
- decorators: List[str] = None
347
- parent: Optional[str] = None
348
- children: List["CodeNode"] = None
349
- language: str = "python"
350
- signature: str = ""
351
- metrics: Dict[str, Any] = None
352
-
353
- def __post_init__(self):
354
- if self.decorators is None:
355
- self.decorators = []
356
- if self.children is None:
357
- self.children = []
358
- if self.metrics is None:
359
- self.metrics = {}
360
-
361
-
362
- class PythonAnalyzer:
363
- """Analyzes Python source code using AST.
364
-
365
- WHY: Python's built-in AST module provides rich structural information
366
- that we can leverage for detailed analysis.
367
- """
368
-
369
- def __init__(self, emitter: Optional[CodeTreeEventEmitter] = None):
370
- self.logger = get_logger(__name__)
371
- self.emitter = emitter
372
-
373
- def analyze_file(self, file_path: Path) -> List[CodeNode]:
374
- """Analyze a Python file and extract code structure.
375
-
376
- Args:
377
- file_path: Path to Python file
378
-
379
- Returns:
380
- List of code nodes found in the file
381
- """
382
- nodes = []
383
-
384
- try:
385
- with open(file_path, encoding="utf-8") as f:
386
- source = f.read()
387
-
388
- tree = ast.parse(source, filename=str(file_path))
389
- nodes = self._extract_nodes(tree, file_path, source)
390
-
391
- except SyntaxError as e:
392
- self.logger.warning(f"Syntax error in {file_path}: {e}")
393
- if self.emitter:
394
- self.emitter.emit_error(str(file_path), f"Syntax error: {e}")
395
- except Exception as e:
396
- self.logger.error(f"Error analyzing {file_path}: {e}")
397
- if self.emitter:
398
- self.emitter.emit_error(str(file_path), str(e))
399
-
400
- return nodes
401
-
402
- def _extract_nodes(
403
- self, tree: ast.AST, file_path: Path, source: str
404
- ) -> List[CodeNode]:
405
- """Extract code nodes from AST tree.
406
-
407
- Args:
408
- tree: AST tree
409
- file_path: Source file path
410
- source: Source code text
411
-
412
- Returns:
413
- List of extracted code nodes
414
- """
415
- nodes = []
416
- source.splitlines()
417
-
418
- class NodeVisitor(ast.NodeVisitor):
419
- def __init__(self, parent_name: Optional[str] = None):
420
- self.parent_name = parent_name
421
- self.current_class = None
422
-
423
- def visit_ClassDef(self, node):
424
- # Extract class information
425
- class_node = CodeNode(
426
- file_path=str(file_path),
427
- node_type="class",
428
- name=node.name,
429
- line_start=node.lineno,
430
- line_end=node.end_lineno or node.lineno,
431
- has_docstring=bool(ast.get_docstring(node)),
432
- decorators=[self._decorator_name(d) for d in node.decorator_list],
433
- parent=self.parent_name,
434
- complexity=self._calculate_complexity(node),
435
- signature=self._get_class_signature(node),
436
- )
437
-
438
- nodes.append(class_node)
439
-
440
- # Emit event if emitter is available
441
- if self.emitter:
442
- self.emitter.emit_node(
443
- CodeNodeEvent(
444
- file_path=str(file_path),
445
- node_type="class",
446
- name=node.name,
447
- line_start=node.lineno,
448
- line_end=node.end_lineno or node.lineno,
449
- complexity=class_node.complexity,
450
- has_docstring=class_node.has_docstring,
451
- decorators=class_node.decorators,
452
- parent=self.parent_name,
453
- children_count=len(node.body),
454
- )
455
- )
456
-
457
- # Visit class members
458
- old_class = self.current_class
459
- self.current_class = node.name
460
- for child in node.body:
461
- if isinstance(child, (ast.FunctionDef, ast.AsyncFunctionDef)):
462
- self.visit_FunctionDef(child, is_method=True)
463
- self.current_class = old_class
464
-
465
- def visit_FunctionDef(self, node, is_method=False):
466
- # Determine node type
467
- node_type = "method" if is_method else "function"
468
- parent = self.current_class if is_method else self.parent_name
469
-
470
- # Extract function information
471
- func_node = CodeNode(
472
- file_path=str(file_path),
473
- node_type=node_type,
474
- name=node.name,
475
- line_start=node.lineno,
476
- line_end=node.end_lineno or node.lineno,
477
- has_docstring=bool(ast.get_docstring(node)),
478
- decorators=[self._decorator_name(d) for d in node.decorator_list],
479
- parent=parent,
480
- complexity=self._calculate_complexity(node),
481
- signature=self._get_function_signature(node),
482
- )
483
-
484
- nodes.append(func_node)
485
-
486
- # Emit event if emitter is available
487
- if self.emitter:
488
- self.emitter.emit_node(
489
- CodeNodeEvent(
490
- file_path=str(file_path),
491
- node_type=node_type,
492
- name=node.name,
493
- line_start=node.lineno,
494
- line_end=node.end_lineno or node.lineno,
495
- complexity=func_node.complexity,
496
- has_docstring=func_node.has_docstring,
497
- decorators=func_node.decorators,
498
- parent=parent,
499
- children_count=0,
500
- )
501
- )
502
-
503
- def visit_AsyncFunctionDef(self, node):
504
- self.visit_FunctionDef(node)
505
-
506
- def _decorator_name(self, decorator):
507
- """Extract decorator name from AST node."""
508
- if isinstance(decorator, ast.Name):
509
- return decorator.id
510
- if isinstance(decorator, ast.Call):
511
- if isinstance(decorator.func, ast.Name):
512
- return decorator.func.id
513
- if isinstance(decorator.func, ast.Attribute):
514
- return decorator.func.attr
515
- return "unknown"
516
-
517
- def _calculate_complexity(self, node):
518
- """Calculate cyclomatic complexity of a node."""
519
- complexity = 1 # Base complexity
520
-
521
- for child in ast.walk(node):
522
- if isinstance(
523
- child, (ast.If, ast.While, ast.For, ast.ExceptHandler)
524
- ):
525
- complexity += 1
526
- elif isinstance(child, ast.BoolOp):
527
- complexity += len(child.values) - 1
528
-
529
- return complexity
530
-
531
- def _get_function_signature(self, node):
532
- """Extract function signature."""
533
- args = []
534
- for arg in node.args.args:
535
- args.append(arg.arg)
536
- return f"{node.name}({', '.join(args)})"
537
-
538
- def _get_class_signature(self, node):
539
- """Extract class signature."""
540
- bases = []
541
- for base in node.bases:
542
- if isinstance(base, ast.Name):
543
- bases.append(base.id)
544
- base_str = f"({', '.join(bases)})" if bases else ""
545
- return f"class {node.name}{base_str}"
546
-
547
- # Extract imports
548
- for node in ast.walk(tree):
549
- if isinstance(node, ast.Import):
550
- for alias in node.names:
551
- import_node = CodeNode(
552
- file_path=str(file_path),
553
- node_type="import",
554
- name=alias.name,
555
- line_start=node.lineno,
556
- line_end=node.end_lineno or node.lineno,
557
- signature=f"import {alias.name}",
558
- )
559
- nodes.append(import_node)
560
-
561
- elif isinstance(node, ast.ImportFrom):
562
- module = node.module or ""
563
- for alias in node.names:
564
- import_node = CodeNode(
565
- file_path=str(file_path),
566
- node_type="import",
567
- name=f"{module}.{alias.name}",
568
- line_start=node.lineno,
569
- line_end=node.end_lineno or node.lineno,
570
- signature=f"from {module} import {alias.name}",
571
- )
572
- nodes.append(import_node)
573
-
574
- # Visit all nodes
575
- visitor = NodeVisitor()
576
- visitor.emitter = self.emitter
577
- visitor.visit(tree)
578
-
579
- return nodes
580
-
581
-
582
- class MultiLanguageAnalyzer:
583
- """Analyzes multiple programming languages using tree-sitter.
584
-
585
- WHY: Tree-sitter provides consistent parsing across multiple languages,
586
- allowing us to support JavaScript, TypeScript, and other languages.
587
- """
588
-
589
- LANGUAGE_PARSERS = {
590
- "python": "tree_sitter_python",
591
- "javascript": "tree_sitter_javascript",
592
- "typescript": "tree_sitter_typescript",
593
- }
594
-
595
- def __init__(self, emitter: Optional[CodeTreeEventEmitter] = None):
596
- self.logger = get_logger(__name__)
597
- self.emitter = emitter
598
- self.parsers = {}
599
- self._init_parsers()
600
-
601
- def _init_parsers(self):
602
- """Initialize tree-sitter parsers for supported languages."""
603
- if not TREE_SITTER_AVAILABLE:
604
- self.logger.warning(
605
- "tree-sitter not available - multi-language support disabled"
606
- )
607
- return
608
-
609
- for lang, module_name in self.LANGUAGE_PARSERS.items():
610
- try:
611
- # Dynamic import of language module
612
- module = __import__(module_name)
613
- parser = tree_sitter.Parser()
614
- # Different tree-sitter versions have different APIs
615
- if hasattr(parser, "set_language"):
616
- parser.set_language(tree_sitter.Language(module.language()))
617
- else:
618
- # Newer API
619
- lang_obj = tree_sitter.Language(module.language())
620
- parser = tree_sitter.Parser(lang_obj)
621
- self.parsers[lang] = parser
622
- except (ImportError, AttributeError) as e:
623
- # Silently skip unavailable parsers - will fall back to basic file discovery
624
- self.logger.debug(f"Language parser not available for {lang}: {e}")
625
-
626
- def analyze_file(self, file_path: Path, language: str) -> List[CodeNode]:
627
- """Analyze a file using tree-sitter.
628
-
629
- Args:
630
- file_path: Path to source file
631
- language: Programming language
632
-
633
- Returns:
634
- List of code nodes found in the file
635
- """
636
- if language not in self.parsers:
637
- # No parser available - return empty list to fall back to basic discovery
638
- self.logger.debug(
639
- f"No parser available for language: {language}, using basic file discovery"
640
- )
641
- return []
642
-
643
- nodes = []
644
-
645
- try:
646
- with open(file_path, "rb") as f:
647
- source = f.read()
648
-
649
- parser = self.parsers[language]
650
- tree = parser.parse(source)
651
-
652
- # Extract nodes based on language
653
- if language in {"javascript", "typescript"}:
654
- nodes = self._extract_js_nodes(tree, file_path, source)
655
- else:
656
- nodes = self._extract_generic_nodes(tree, file_path, source, language)
657
-
658
- except Exception as e:
659
- self.logger.error(f"Error analyzing {file_path}: {e}")
660
- if self.emitter:
661
- self.emitter.emit_error(str(file_path), str(e))
662
-
663
- return nodes
664
-
665
- def _extract_js_nodes(self, tree, file_path: Path, source: bytes) -> List[CodeNode]:
666
- """Extract nodes from JavaScript/TypeScript files."""
667
- nodes = []
668
-
669
- def walk_tree(node, parent_name=None):
670
- if node.type == "class_declaration":
671
- # Extract class
672
- name_node = node.child_by_field_name("name")
673
- if name_node:
674
- class_node = CodeNode(
675
- file_path=str(file_path),
676
- node_type="class",
677
- name=source[name_node.start_byte : name_node.end_byte].decode(
678
- "utf-8"
679
- ),
680
- line_start=node.start_point[0] + 1,
681
- line_end=node.end_point[0] + 1,
682
- parent=parent_name,
683
- language="javascript",
684
- )
685
- nodes.append(class_node)
686
-
687
- if self.emitter:
688
- self.emitter.emit_node(
689
- CodeNodeEvent(
690
- file_path=str(file_path),
691
- node_type="class",
692
- name=class_node.name,
693
- line_start=class_node.line_start,
694
- line_end=class_node.line_end,
695
- parent=parent_name,
696
- language="javascript",
697
- )
698
- )
699
-
700
- elif node.type in (
701
- "function_declaration",
702
- "arrow_function",
703
- "method_definition",
704
- ):
705
- # Extract function
706
- name_node = node.child_by_field_name("name")
707
- if name_node:
708
- func_name = source[
709
- name_node.start_byte : name_node.end_byte
710
- ].decode("utf-8")
711
- func_node = CodeNode(
712
- file_path=str(file_path),
713
- node_type=(
714
- "function" if node.type != "method_definition" else "method"
715
- ),
716
- name=func_name,
717
- line_start=node.start_point[0] + 1,
718
- line_end=node.end_point[0] + 1,
719
- parent=parent_name,
720
- language="javascript",
721
- )
722
- nodes.append(func_node)
723
-
724
- if self.emitter:
725
- self.emitter.emit_node(
726
- CodeNodeEvent(
727
- file_path=str(file_path),
728
- node_type=func_node.node_type,
729
- name=func_name,
730
- line_start=func_node.line_start,
731
- line_end=func_node.line_end,
732
- parent=parent_name,
733
- language="javascript",
734
- )
735
- )
736
-
737
- # Recursively walk children
738
- for child in node.children:
739
- walk_tree(child, parent_name)
740
-
741
- walk_tree(tree.root_node)
742
- return nodes
743
-
744
- def _extract_generic_nodes(
745
- self, tree, file_path: Path, source: bytes, language: str
746
- ) -> List[CodeNode]:
747
- """Generic node extraction for other languages."""
748
- # Simple generic extraction - can be enhanced per language
749
- nodes = []
750
-
751
- def walk_tree(node):
752
- # Look for common patterns
753
- if "class" in node.type or "struct" in node.type:
754
- nodes.append(
755
- CodeNode(
756
- file_path=str(file_path),
757
- node_type="class",
758
- name=f"{node.type}_{node.start_point[0]}",
759
- line_start=node.start_point[0] + 1,
760
- line_end=node.end_point[0] + 1,
761
- language=language,
762
- )
763
- )
764
- elif "function" in node.type or "method" in node.type:
765
- nodes.append(
766
- CodeNode(
767
- file_path=str(file_path),
768
- node_type="function",
769
- name=f"{node.type}_{node.start_point[0]}",
770
- line_start=node.start_point[0] + 1,
771
- line_end=node.end_point[0] + 1,
772
- language=language,
773
- )
774
- )
775
-
776
- for child in node.children:
777
- walk_tree(child)
778
-
779
- walk_tree(tree.root_node)
780
- return nodes
781
-
782
-
783
- class CodeTreeAnalyzer:
784
- """Main analyzer that coordinates language-specific analyzers.
785
-
786
- WHY: Provides a unified interface for analyzing codebases with multiple
787
- languages, handling caching and incremental processing.
788
- """
789
-
790
- # Define code file extensions at class level for directory filtering
791
- CODE_EXTENSIONS = {
792
- ".py",
793
- ".js",
794
- ".jsx",
795
- ".ts",
796
- ".tsx",
797
- ".mjs", # Added missing extension
798
- ".cjs", # Added missing extension
799
- ".java",
800
- ".cpp",
801
- ".c",
802
- ".h",
803
- ".hpp",
804
- ".cs",
805
- ".go",
806
- ".rs",
807
- ".rb",
808
- ".php",
809
- ".swift",
810
- ".kt",
811
- ".scala",
812
- ".r",
813
- ".m",
814
- ".mm",
815
- ".sh",
816
- ".bash",
817
- ".zsh",
818
- ".fish",
819
- ".ps1",
820
- ".bat",
821
- ".cmd",
822
- ".sql",
823
- ".html",
824
- ".css",
825
- ".scss",
826
- ".sass",
827
- ".less",
828
- ".xml",
829
- ".json",
830
- ".yaml",
831
- ".yml",
832
- ".toml",
833
- ".ini",
834
- ".cfg",
835
- ".conf",
836
- ".md",
837
- ".rst",
838
- ".txt",
839
- }
840
-
841
- # File extensions to language mapping
842
- LANGUAGE_MAP = {
843
- ".py": "python",
844
- ".js": "javascript",
845
- ".jsx": "javascript",
846
- ".ts": "typescript",
847
- ".tsx": "typescript",
848
- ".mjs": "javascript",
849
- ".cjs": "javascript",
850
- }
851
-
852
- def __init__(
853
- self,
854
- emit_events: bool = True,
855
- cache_dir: Optional[Path] = None,
856
- emitter: Optional[CodeTreeEventEmitter] = None,
857
- ):
858
- """Initialize the code tree analyzer.
859
-
860
- Args:
861
- emit_events: Whether to emit Socket.IO events
862
- cache_dir: Directory for caching analysis results
863
- emitter: Optional event emitter to use (creates one if not provided)
864
- """
865
- self.logger = get_logger(__name__)
866
- self.emit_events = emit_events
867
- self.cache_dir = cache_dir or Path.home() / ".claude-mpm" / "code-cache"
868
-
869
- # Initialize gitignore manager (always filters dotfiles)
870
- self.gitignore_manager = GitignoreManager()
871
- self._last_working_dir = None
872
-
873
- # Use provided emitter or create one
874
- if emitter:
875
- self.emitter = emitter
876
- elif emit_events:
877
- self.emitter = CodeTreeEventEmitter(use_stdout=True)
878
- else:
879
- self.emitter = None
880
-
881
- # Initialize language analyzers
882
- self.python_analyzer = PythonAnalyzer(self.emitter)
883
- self.multi_lang_analyzer = MultiLanguageAnalyzer(self.emitter)
884
-
885
- # For JavaScript/TypeScript
886
- self.javascript_analyzer = self.multi_lang_analyzer
887
- self.generic_analyzer = self.multi_lang_analyzer
888
-
889
- # Cache for processed files
890
- self.cache = {}
891
- self._load_cache()
892
-
893
- def analyze_directory(
894
- self,
895
- directory: Path,
896
- languages: Optional[List[str]] = None,
897
- ignore_patterns: Optional[List[str]] = None,
898
- max_depth: Optional[int] = None,
899
- ) -> Dict[str, Any]:
900
- """Analyze a directory and build code tree.
901
-
902
- Args:
903
- directory: Directory to analyze
904
- languages: Languages to include (None for all)
905
- ignore_patterns: Patterns to ignore
906
- max_depth: Maximum directory depth
907
-
908
- Returns:
909
- Dictionary containing the code tree and statistics
910
- """
911
- if self.emitter:
912
- self.emitter.start()
913
-
914
- start_time = time.time()
915
- all_nodes = []
916
- files_processed = 0
917
- total_files = 0
918
-
919
- # Collect files to process
920
- files_to_process = []
921
- for ext, lang in self.LANGUAGE_MAP.items():
922
- if languages and lang not in languages:
923
- continue
924
-
925
- for file_path in directory.rglob(f"*{ext}"):
926
- # Use gitignore manager for filtering with directory as working dir
927
- if self.gitignore_manager.should_ignore(file_path, directory):
928
- continue
929
-
930
- # Also check additional patterns
931
- if ignore_patterns and any(
932
- p in str(file_path) for p in ignore_patterns
933
- ):
934
- continue
935
-
936
- # Check max depth
937
- if max_depth:
938
- depth = len(file_path.relative_to(directory).parts) - 1
939
- if depth > max_depth:
940
- continue
941
-
942
- files_to_process.append((file_path, lang))
943
-
944
- total_files = len(files_to_process)
945
-
946
- # Process files
947
- for file_path, language in files_to_process:
948
- # Check cache
949
- file_hash = self._get_file_hash(file_path)
950
- cache_key = f"{file_path}:{file_hash}"
951
-
952
- if cache_key in self.cache:
953
- nodes = self.cache[cache_key]
954
- self.logger.debug(f"Using cached results for {file_path}")
955
- else:
956
- # Emit file start event
957
- if self.emitter:
958
- self.emitter.emit_file_start(str(file_path), language)
959
-
960
- file_start = time.time()
961
-
962
- # Analyze based on language
963
- if language == "python":
964
- nodes = self.python_analyzer.analyze_file(file_path)
965
- else:
966
- nodes = self.multi_lang_analyzer.analyze_file(file_path, language)
967
-
968
- # If no nodes found and we have a valid language, emit basic file info
969
- if not nodes and language != "unknown":
970
- self.logger.debug(
971
- f"No AST nodes found for {file_path}, using basic discovery"
972
- )
973
-
974
- # Cache results
975
- self.cache[cache_key] = nodes
976
-
977
- # Emit file complete event
978
- if self.emitter:
979
- self.emitter.emit_file_complete(
980
- str(file_path), len(nodes), time.time() - file_start
981
- )
982
-
983
- all_nodes.extend(nodes)
984
- files_processed += 1
985
-
986
- # Emit progress
987
- if self.emitter and files_processed % 10 == 0:
988
- self.emitter.emit_progress(
989
- files_processed, total_files, f"Processing {file_path.name}"
990
- )
991
-
992
- # Build tree structure
993
- tree = self._build_tree(all_nodes, directory)
994
-
995
- # Calculate statistics
996
- duration = time.time() - start_time
997
- stats = {
998
- "files_processed": files_processed,
999
- "total_nodes": len(all_nodes),
1000
- "duration": duration,
1001
- "classes": sum(1 for n in all_nodes if n.node_type == "class"),
1002
- "functions": sum(
1003
- 1 for n in all_nodes if n.node_type in ("function", "method")
1004
- ),
1005
- "imports": sum(1 for n in all_nodes if n.node_type == "import"),
1006
- "languages": list(
1007
- {n.language for n in all_nodes if hasattr(n, "language")}
1008
- ),
1009
- "avg_complexity": (
1010
- sum(n.complexity for n in all_nodes) / len(all_nodes)
1011
- if all_nodes
1012
- else 0
1013
- ),
1014
- }
1015
-
1016
- # Save cache
1017
- self._save_cache()
1018
-
1019
- # Stop emitter
1020
- if self.emitter:
1021
- self.emitter.stop()
1022
-
1023
- return {"tree": tree, "nodes": all_nodes, "stats": stats}
1024
-
1025
- def _should_ignore(self, file_path: Path, patterns: Optional[List[str]]) -> bool:
1026
- """Check if file should be ignored.
1027
-
1028
- Uses GitignoreManager for proper pattern matching.
1029
- """
1030
- # Get the working directory (use parent for files, self for directories)
1031
- if file_path.is_file():
1032
- working_dir = file_path.parent
1033
- else:
1034
- # For directories during discovery, use the parent
1035
- working_dir = (
1036
- file_path.parent if file_path.parent != file_path else Path.cwd()
1037
- )
1038
-
1039
- # Use gitignore manager for checking
1040
- if self.gitignore_manager.should_ignore(file_path, working_dir):
1041
- return True
1042
-
1043
- # Also check any additional patterns provided
1044
- if patterns:
1045
- path_str = str(file_path)
1046
- return any(pattern in path_str for pattern in patterns)
1047
-
1048
- return False
1049
-
1050
- def _get_file_hash(self, file_path: Path) -> str:
1051
- """Get hash of file contents for caching."""
1052
- hasher = hashlib.md5()
1053
- with open(file_path, "rb") as f:
1054
- hasher.update(f.read())
1055
- return hasher.hexdigest()
1056
-
1057
- def _build_tree(self, nodes: List[CodeNode], root_dir: Path) -> Dict[str, Any]:
1058
- """Build hierarchical tree structure from flat nodes list."""
1059
- tree = {
1060
- "name": root_dir.name,
1061
- "type": "directory",
1062
- "path": str(root_dir),
1063
- "children": [],
1064
- }
1065
-
1066
- # Group nodes by file
1067
- files_map = {}
1068
- for node in nodes:
1069
- if node.file_path not in files_map:
1070
- files_map[node.file_path] = {
1071
- "name": Path(node.file_path).name,
1072
- "type": "file",
1073
- "path": node.file_path,
1074
- "children": [],
1075
- }
1076
-
1077
- # Add node to file
1078
- node_dict = {
1079
- "name": node.name,
1080
- "type": node.node_type,
1081
- "line_start": node.line_start,
1082
- "line_end": node.line_end,
1083
- "complexity": node.complexity,
1084
- "has_docstring": node.has_docstring,
1085
- "decorators": node.decorators,
1086
- "signature": node.signature,
1087
- }
1088
- files_map[node.file_path]["children"].append(node_dict)
1089
-
1090
- # Build directory structure
1091
- for file_path, file_node in files_map.items():
1092
- rel_path = Path(file_path).relative_to(root_dir)
1093
- parts = rel_path.parts
1094
-
1095
- current = tree
1096
- for part in parts[:-1]:
1097
- # Find or create directory
1098
- dir_node = None
1099
- for child in current["children"]:
1100
- if child["type"] == "directory" and child["name"] == part:
1101
- dir_node = child
1102
- break
1103
-
1104
- if not dir_node:
1105
- dir_node = {"name": part, "type": "directory", "children": []}
1106
- current["children"].append(dir_node)
1107
-
1108
- current = dir_node
1109
-
1110
- # Add file to current directory
1111
- current["children"].append(file_node)
1112
-
1113
- return tree
1114
-
1115
- def _load_cache(self):
1116
- """Load cache from disk."""
1117
- cache_file = self.cache_dir / "code_tree_cache.json"
1118
- if cache_file.exists():
1119
- try:
1120
- with open(cache_file) as f:
1121
- cache_data = json.load(f)
1122
- # Reconstruct CodeNode objects
1123
- for key, nodes_data in cache_data.items():
1124
- self.cache[key] = [
1125
- CodeNode(**node_data) for node_data in nodes_data
1126
- ]
1127
- self.logger.info(f"Loaded cache with {len(self.cache)} entries")
1128
- except Exception as e:
1129
- self.logger.warning(f"Failed to load cache: {e}")
1130
-
1131
- def _save_cache(self):
1132
- """Save cache to disk."""
1133
- self.cache_dir.mkdir(parents=True, exist_ok=True)
1134
- cache_file = self.cache_dir / "code_tree_cache.json"
1135
-
1136
- try:
1137
- # Convert CodeNode objects to dictionaries
1138
- cache_data = {}
1139
- for key, nodes in self.cache.items():
1140
- cache_data[key] = [
1141
- {
1142
- "file_path": n.file_path,
1143
- "node_type": n.node_type,
1144
- "name": n.name,
1145
- "line_start": n.line_start,
1146
- "line_end": n.line_end,
1147
- "complexity": n.complexity,
1148
- "has_docstring": n.has_docstring,
1149
- "decorators": n.decorators,
1150
- "parent": n.parent,
1151
- "language": n.language,
1152
- "signature": n.signature,
1153
- }
1154
- for n in nodes
1155
- ]
1156
-
1157
- with open(cache_file, "w") as f:
1158
- json.dump(cache_data, f, indent=2)
1159
-
1160
- self.logger.info(f"Saved cache with {len(self.cache)} entries")
1161
- except Exception as e:
1162
- self.logger.warning(f"Failed to save cache: {e}")
1163
-
1164
- def has_code_files(
1165
- self, directory: Path, depth: int = 5, current_depth: int = 0
1166
- ) -> bool:
1167
- """Check if directory contains code files up to 5 levels deep.
1168
-
1169
- Args:
1170
- directory: Directory to check
1171
- depth: Maximum depth to search
1172
- current_depth: Current recursion depth
1173
-
1174
- Returns:
1175
- True if directory contains code files within depth levels
1176
- """
1177
- if current_depth >= depth:
1178
- return False
1179
-
1180
- # Skip checking these directories entirely
1181
- SKIP_DIRS = {
1182
- "node_modules",
1183
- "__pycache__",
1184
- ".git",
1185
- ".venv",
1186
- "venv",
1187
- "dist",
1188
- "build",
1189
- ".tox",
1190
- "htmlcov",
1191
- ".pytest_cache",
1192
- ".mypy_cache",
1193
- "coverage",
1194
- ".idea",
1195
- ".vscode",
1196
- "env",
1197
- ".coverage",
1198
- "__MACOSX",
1199
- ".ipynb_checkpoints",
1200
- }
1201
- # Skip directories in the skip list or egg-info directories
1202
- if directory.name in SKIP_DIRS or directory.name.endswith(".egg-info"):
1203
- return False
1204
-
1205
- try:
1206
- for item in directory.iterdir():
1207
- # Skip hidden items in scan
1208
- if item.name.startswith("."):
1209
- continue
1210
-
1211
- if item.is_file():
1212
- # Check if it's a code file
1213
- ext = item.suffix.lower()
1214
- if ext in self.CODE_EXTENSIONS:
1215
- return True
1216
- elif item.is_dir() and current_depth < depth - 1:
1217
- # Skip egg-info directories in the recursive check too
1218
- if item.name.endswith(".egg-info"):
1219
- continue
1220
- if self.has_code_files(item, depth, current_depth + 1):
1221
- return True
1222
-
1223
- except (PermissionError, OSError):
1224
- pass
1225
-
1226
- return False
1227
-
1228
- def discover_top_level(
1229
- self, directory: Path, ignore_patterns: Optional[List[str]] = None
1230
- ) -> Dict[str, Any]:
1231
- """Discover only top-level directories and files for lazy loading.
1232
-
1233
- Args:
1234
- directory: Root directory to discover
1235
- ignore_patterns: Patterns to ignore
1236
-
1237
- Returns:
1238
- Dictionary with top-level structure
1239
- """
1240
- # CRITICAL FIX: Use the directory parameter as the base for relative paths
1241
- # NOT the current working directory. This ensures we only show items
1242
- # within the requested directory, not parent directories.
1243
- working_dir = Path(directory).absolute()
1244
-
1245
- # Emit discovery start event
1246
- if self.emitter:
1247
- from datetime import datetime
1248
-
1249
- self.emitter.emit(
1250
- "info",
1251
- {
1252
- "type": "discovery.start",
1253
- "action": "scanning_directory",
1254
- "path": str(directory),
1255
- "message": f"Starting discovery of {directory.name}",
1256
- "timestamp": datetime.now().isoformat(),
1257
- },
1258
- )
1259
-
1260
- result = {
1261
- "path": str(directory),
1262
- "name": directory.name,
1263
- "type": "directory",
1264
- "children": [],
1265
- }
1266
-
1267
- try:
1268
- # Clear cache if working directory changed
1269
- if self._last_working_dir != directory:
1270
- self.gitignore_manager.clear_cache()
1271
- self._last_working_dir = directory
1272
-
1273
- # Get immediate children only (no recursion)
1274
- files_count = 0
1275
- dirs_count = 0
1276
- ignored_count = 0
1277
-
1278
- for item in directory.iterdir():
1279
- # Use gitignore manager for filtering with the directory as working dir
1280
- if self.gitignore_manager.should_ignore(item, directory):
1281
- if self.emitter:
1282
- from datetime import datetime
1283
-
1284
- self.emitter.emit(
1285
- "info",
1286
- {
1287
- "type": "filter.gitignore",
1288
- "path": str(item),
1289
- "reason": "gitignore pattern",
1290
- "message": f"Ignored by gitignore: {item.name}",
1291
- "timestamp": datetime.now().isoformat(),
1292
- },
1293
- )
1294
- ignored_count += 1
1295
- continue
1296
-
1297
- # Also check additional patterns if provided
1298
- if ignore_patterns and any(p in str(item) for p in ignore_patterns):
1299
- if self.emitter:
1300
- from datetime import datetime
1301
-
1302
- self.emitter.emit(
1303
- "info",
1304
- {
1305
- "type": "filter.pattern",
1306
- "path": str(item),
1307
- "reason": "custom pattern",
1308
- "message": f"Ignored by pattern: {item.name}",
1309
- "timestamp": datetime.now().isoformat(),
1310
- },
1311
- )
1312
- ignored_count += 1
1313
- continue
1314
-
1315
- if item.is_dir():
1316
- # Check if directory contains code files (recursively checking subdirectories)
1317
- # Important: We want to include directories even if they only have code
1318
- # in subdirectories (like src/claude_mpm/*.py)
1319
- if not self.has_code_files(item, depth=5):
1320
- if self.emitter:
1321
- from datetime import datetime
1322
-
1323
- self.emitter.emit(
1324
- "info",
1325
- {
1326
- "type": "filter.no_code",
1327
- "path": str(item.name),
1328
- "reason": "no code files",
1329
- "message": f"Skipped directory without code: {item.name}",
1330
- "timestamp": datetime.now().isoformat(),
1331
- },
1332
- )
1333
- ignored_count += 1
1334
- continue
1335
-
1336
- # Directory - return just the item name
1337
- # The frontend will construct the full path by combining parent path with child name
1338
- path_str = item.name
1339
-
1340
- # Emit directory found event
1341
- if self.emitter:
1342
- from datetime import datetime
1343
-
1344
- self.emitter.emit(
1345
- "info",
1346
- {
1347
- "type": "discovery.directory",
1348
- "path": str(item),
1349
- "message": f"Found directory: {item.name}",
1350
- "timestamp": datetime.now().isoformat(),
1351
- },
1352
- )
1353
- dirs_count += 1
1354
-
1355
- child = {
1356
- "path": path_str,
1357
- "name": item.name,
1358
- "type": "directory",
1359
- "discovered": False,
1360
- "children": [],
1361
- }
1362
- result["children"].append(child)
1363
-
1364
- if self.emitter:
1365
- self.emitter.emit_directory_discovered(path_str, [])
1366
-
1367
- elif item.is_file():
1368
- # Check if it's a supported code file or a special file we want to show
1369
- if item.suffix in self.supported_extensions or item.name in [
1370
- ".gitignore",
1371
- ".env.example",
1372
- ".env.sample",
1373
- ]:
1374
- # File - mark for lazy analysis
1375
- language = self._get_language(item)
1376
-
1377
- # File path should be just the item name
1378
- # The frontend will construct the full path by combining parent path with child name
1379
- path_str = item.name
1380
-
1381
- # Emit file found event
1382
- if self.emitter:
1383
- from datetime import datetime
1384
-
1385
- self.emitter.emit(
1386
- "info",
1387
- {
1388
- "type": "discovery.file",
1389
- "path": str(item),
1390
- "language": language,
1391
- "size": item.stat().st_size,
1392
- "message": f"Found file: {item.name} ({language})",
1393
- "timestamp": datetime.now().isoformat(),
1394
- },
1395
- )
1396
- files_count += 1
1397
-
1398
- child = {
1399
- "path": path_str,
1400
- "name": item.name,
1401
- "type": "file",
1402
- "language": language,
1403
- "size": item.stat().st_size,
1404
- "analyzed": False,
1405
- }
1406
- result["children"].append(child)
1407
-
1408
- if self.emitter:
1409
- self.emitter.emit_file_discovered(
1410
- path_str, language, item.stat().st_size
1411
- )
1412
-
1413
- except PermissionError as e:
1414
- self.logger.warning(f"Permission denied accessing {directory}: {e}")
1415
- if self.emitter:
1416
- self.emitter.emit_error(str(directory), f"Permission denied: {e}")
1417
-
1418
- # Emit discovery complete event with stats
1419
- if self.emitter:
1420
- from datetime import datetime
1421
-
1422
- self.emitter.emit(
1423
- "info",
1424
- {
1425
- "type": "discovery.complete",
1426
- "path": str(directory),
1427
- "stats": {
1428
- "files": files_count,
1429
- "directories": dirs_count,
1430
- "ignored": ignored_count,
1431
- },
1432
- "message": f"Discovery complete: {files_count} files, {dirs_count} directories, {ignored_count} ignored",
1433
- "timestamp": datetime.now().isoformat(),
1434
- },
1435
- )
1436
-
1437
- return result
1438
-
1439
- def discover_directory(
1440
- self, dir_path: str, ignore_patterns: Optional[List[str]] = None
1441
- ) -> Dict[str, Any]:
1442
- """Discover contents of a specific directory for lazy loading.
1443
-
1444
- Args:
1445
- dir_path: Directory path to discover
1446
- ignore_patterns: Patterns to ignore
1447
-
1448
- Returns:
1449
- Dictionary with directory contents
1450
- """
1451
- directory = Path(dir_path)
1452
- if not directory.exists() or not directory.is_dir():
1453
- return {"error": f"Invalid directory: {dir_path}"}
1454
-
1455
- # Clear cache if working directory changed
1456
- if self._last_working_dir != directory.parent:
1457
- self.gitignore_manager.clear_cache()
1458
- self._last_working_dir = directory.parent
1459
-
1460
- # The discover_top_level method will emit all the INFO events
1461
- result = self.discover_top_level(directory, ignore_patterns)
1462
- return result
1463
-
1464
- def analyze_file(self, file_path: str) -> Dict[str, Any]:
1465
- """Analyze a specific file and return its AST structure.
1466
-
1467
- Args:
1468
- file_path: Path to file to analyze
1469
-
1470
- Returns:
1471
- Dictionary with file analysis results
1472
- """
1473
- path = Path(file_path)
1474
- if not path.exists() or not path.is_file():
1475
- return {"error": f"Invalid file: {file_path}"}
1476
-
1477
- # Get language first (needed for return statement)
1478
- language = self._get_language(path)
1479
-
1480
- # Emit analysis start event
1481
- if self.emitter:
1482
- from datetime import datetime
1483
-
1484
- self.emitter.emit(
1485
- "info",
1486
- {
1487
- "type": "analysis.start",
1488
- "file": str(path),
1489
- "language": language,
1490
- "message": f"Analyzing: {path.name}",
1491
- "timestamp": datetime.now().isoformat(),
1492
- },
1493
- )
1494
-
1495
- # Check cache
1496
- file_hash = self._get_file_hash(path)
1497
- cache_key = f"{file_path}:{file_hash}"
1498
-
1499
- if cache_key in self.cache:
1500
- nodes = self.cache[cache_key]
1501
- if self.emitter:
1502
- from datetime import datetime
1503
-
1504
- self.emitter.emit(
1505
- "info",
1506
- {
1507
- "type": "cache.hit",
1508
- "file": str(path),
1509
- "message": f"Using cached analysis for {path.name}",
1510
- "timestamp": datetime.now().isoformat(),
1511
- },
1512
- )
1513
- else:
1514
- # Analyze file
1515
- if self.emitter:
1516
- from datetime import datetime
1517
-
1518
- self.emitter.emit(
1519
- "info",
1520
- {
1521
- "type": "cache.miss",
1522
- "file": str(path),
1523
- "message": f"Cache miss, analyzing fresh: {path.name}",
1524
- "timestamp": datetime.now().isoformat(),
1525
- },
1526
- )
1527
-
1528
- if language == "python":
1529
- analyzer = self.python_analyzer
1530
- elif language == "javascript" or language == "typescript":
1531
- analyzer = self.javascript_analyzer
1532
- else:
1533
- analyzer = self.generic_analyzer
1534
-
1535
- start_time = time.time()
1536
-
1537
- # Emit parsing event
1538
- if self.emitter:
1539
- from datetime import datetime
1540
-
1541
- self.emitter.emit(
1542
- "info",
1543
- {
1544
- "type": "analysis.parse",
1545
- "file": str(path),
1546
- "message": f"Parsing file content: {path.name}",
1547
- "timestamp": datetime.now().isoformat(),
1548
- },
1549
- )
1550
-
1551
- nodes = analyzer.analyze_file(path) if analyzer else []
1552
- duration = time.time() - start_time
1553
-
1554
- # Cache results
1555
- self.cache[cache_key] = nodes
1556
-
1557
- # Filter internal functions before emitting
1558
- filtered_nodes = []
1559
- classes_count = 0
1560
- functions_count = 0
1561
- methods_count = 0
1562
-
1563
- for node in nodes:
1564
- # Only include main structural elements
1565
- if not self._is_internal_node(node):
1566
- # Emit found element event
1567
- if self.emitter:
1568
- from datetime import datetime
1569
-
1570
- self.emitter.emit(
1571
- "info",
1572
- {
1573
- "type": f"analysis.{node.node_type}",
1574
- "name": node.name,
1575
- "file": str(path),
1576
- "line_start": node.line_start,
1577
- "complexity": node.complexity,
1578
- "message": f"Found {node.node_type}: {node.name}",
1579
- "timestamp": datetime.now().isoformat(),
1580
- },
1581
- )
1582
-
1583
- # Count node types
1584
- if node.node_type == "class":
1585
- classes_count += 1
1586
- elif node.node_type == "function":
1587
- functions_count += 1
1588
- elif node.node_type == "method":
1589
- methods_count += 1
1590
-
1591
- filtered_nodes.append(
1592
- {
1593
- "name": node.name,
1594
- "type": node.node_type,
1595
- "line_start": node.line_start,
1596
- "line_end": node.line_end,
1597
- "complexity": node.complexity,
1598
- "has_docstring": node.has_docstring,
1599
- "signature": node.signature,
1600
- }
1601
- )
1602
-
1603
- # Emit analysis complete event with stats
1604
- if self.emitter:
1605
- from datetime import datetime
1606
-
1607
- self.emitter.emit(
1608
- "info",
1609
- {
1610
- "type": "analysis.complete",
1611
- "file": str(path),
1612
- "stats": {
1613
- "classes": classes_count,
1614
- "functions": functions_count,
1615
- "methods": methods_count,
1616
- "total_nodes": len(filtered_nodes),
1617
- },
1618
- "duration": duration,
1619
- "message": f"Analysis complete: {classes_count} classes, {functions_count} functions, {methods_count} methods",
1620
- "timestamp": datetime.now().isoformat(),
1621
- },
1622
- )
1623
-
1624
- self.emitter.emit_file_analyzed(file_path, filtered_nodes, duration)
1625
-
1626
- return {
1627
- "path": file_path,
1628
- "language": language,
1629
- "nodes": (
1630
- filtered_nodes
1631
- if "filtered_nodes" in locals()
1632
- else [
1633
- {
1634
- "name": n.name,
1635
- "type": n.node_type,
1636
- "line_start": n.line_start,
1637
- "line_end": n.line_end,
1638
- "complexity": n.complexity,
1639
- "has_docstring": n.has_docstring,
1640
- "signature": n.signature,
1641
- }
1642
- for n in nodes
1643
- if not self._is_internal_node(n)
1644
- ]
1645
- ),
1646
- }
1647
-
1648
- def _is_internal_node(self, node: CodeNode) -> bool:
1649
- """Check if node is an internal function that should be filtered."""
1650
- # Filter patterns for internal functions
1651
- internal_patterns = [
1652
- "handle", # Event handlers
1653
- "on_", # Event callbacks
1654
- "_", # Private methods
1655
- "get_", # Simple getters
1656
- "set_", # Simple setters
1657
- "__", # Python magic methods
1658
- ]
1659
-
1660
- name_lower = node.name.lower()
1661
-
1662
- # Don't filter classes or important public methods
1663
- if node.node_type == "class":
1664
- return False
1665
-
1666
- # Check patterns
1667
- for pattern in internal_patterns:
1668
- if name_lower.startswith(pattern):
1669
- # Exception: include __init__ methods
1670
- if node.name == "__init__":
1671
- return False
1672
- return True
1673
-
1674
- return False
1675
-
1676
- @property
1677
- def supported_extensions(self):
1678
- """Get list of supported file extensions."""
1679
- return {".py", ".js", ".jsx", ".ts", ".tsx", ".mjs", ".cjs"}
1680
-
1681
- def _get_language(self, file_path: Path) -> str:
1682
- """Determine language from file extension."""
1683
- ext = file_path.suffix.lower()
1684
- language_map = {
1685
- ".py": "python",
1686
- ".js": "javascript",
1687
- ".jsx": "javascript",
1688
- ".ts": "typescript",
1689
- ".tsx": "typescript",
1690
- ".mjs": "javascript",
1691
- ".cjs": "javascript",
1692
- }
1693
- return language_map.get(ext, "unknown")