claude-mpm 4.7.4__py3-none-any.whl → 4.18.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (308) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +118 -0
  3. claude_mpm/agents/BASE_ENGINEER.md +286 -0
  4. claude_mpm/agents/BASE_PM.md +106 -1
  5. claude_mpm/agents/OUTPUT_STYLE.md +329 -11
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +397 -459
  7. claude_mpm/agents/agent_loader.py +17 -5
  8. claude_mpm/agents/frontmatter_validator.py +284 -253
  9. claude_mpm/agents/templates/README.md +465 -0
  10. claude_mpm/agents/templates/agent-manager.json +4 -1
  11. claude_mpm/agents/templates/agentic-coder-optimizer.json +13 -3
  12. claude_mpm/agents/templates/api_qa.json +11 -2
  13. claude_mpm/agents/templates/circuit_breakers.md +638 -0
  14. claude_mpm/agents/templates/clerk-ops.json +12 -2
  15. claude_mpm/agents/templates/code_analyzer.json +8 -2
  16. claude_mpm/agents/templates/content-agent.json +358 -0
  17. claude_mpm/agents/templates/dart_engineer.json +15 -2
  18. claude_mpm/agents/templates/data_engineer.json +15 -2
  19. claude_mpm/agents/templates/documentation.json +10 -2
  20. claude_mpm/agents/templates/engineer.json +21 -1
  21. claude_mpm/agents/templates/gcp_ops_agent.json +12 -2
  22. claude_mpm/agents/templates/git_file_tracking.md +584 -0
  23. claude_mpm/agents/templates/golang_engineer.json +270 -0
  24. claude_mpm/agents/templates/imagemagick.json +4 -1
  25. claude_mpm/agents/templates/java_engineer.json +346 -0
  26. claude_mpm/agents/templates/local_ops_agent.json +1227 -6
  27. claude_mpm/agents/templates/memory_manager.json +4 -1
  28. claude_mpm/agents/templates/nextjs_engineer.json +141 -133
  29. claude_mpm/agents/templates/ops.json +12 -2
  30. claude_mpm/agents/templates/php-engineer.json +270 -174
  31. claude_mpm/agents/templates/pm_examples.md +474 -0
  32. claude_mpm/agents/templates/pm_red_flags.md +240 -0
  33. claude_mpm/agents/templates/product_owner.json +338 -0
  34. claude_mpm/agents/templates/project_organizer.json +14 -4
  35. claude_mpm/agents/templates/prompt-engineer.json +13 -2
  36. claude_mpm/agents/templates/python_engineer.json +174 -81
  37. claude_mpm/agents/templates/qa.json +11 -2
  38. claude_mpm/agents/templates/react_engineer.json +16 -3
  39. claude_mpm/agents/templates/refactoring_engineer.json +12 -2
  40. claude_mpm/agents/templates/research.json +34 -21
  41. claude_mpm/agents/templates/response_format.md +583 -0
  42. claude_mpm/agents/templates/ruby-engineer.json +129 -192
  43. claude_mpm/agents/templates/rust_engineer.json +270 -0
  44. claude_mpm/agents/templates/security.json +10 -2
  45. claude_mpm/agents/templates/svelte-engineer.json +225 -0
  46. claude_mpm/agents/templates/ticketing.json +10 -2
  47. claude_mpm/agents/templates/typescript_engineer.json +116 -125
  48. claude_mpm/agents/templates/validation_templates.md +312 -0
  49. claude_mpm/agents/templates/vercel_ops_agent.json +12 -2
  50. claude_mpm/agents/templates/version_control.json +12 -2
  51. claude_mpm/agents/templates/web_qa.json +11 -2
  52. claude_mpm/agents/templates/web_ui.json +15 -2
  53. claude_mpm/cli/__init__.py +34 -614
  54. claude_mpm/cli/commands/agent_manager.py +25 -12
  55. claude_mpm/cli/commands/agent_state_manager.py +186 -0
  56. claude_mpm/cli/commands/agents.py +235 -148
  57. claude_mpm/cli/commands/agents_detect.py +380 -0
  58. claude_mpm/cli/commands/agents_recommend.py +309 -0
  59. claude_mpm/cli/commands/aggregate.py +7 -3
  60. claude_mpm/cli/commands/analyze.py +9 -4
  61. claude_mpm/cli/commands/analyze_code.py +7 -2
  62. claude_mpm/cli/commands/auto_configure.py +570 -0
  63. claude_mpm/cli/commands/config.py +47 -13
  64. claude_mpm/cli/commands/configure.py +419 -1571
  65. claude_mpm/cli/commands/configure_agent_display.py +261 -0
  66. claude_mpm/cli/commands/configure_behavior_manager.py +204 -0
  67. claude_mpm/cli/commands/configure_hook_manager.py +225 -0
  68. claude_mpm/cli/commands/configure_models.py +18 -0
  69. claude_mpm/cli/commands/configure_navigation.py +167 -0
  70. claude_mpm/cli/commands/configure_paths.py +104 -0
  71. claude_mpm/cli/commands/configure_persistence.py +254 -0
  72. claude_mpm/cli/commands/configure_startup_manager.py +646 -0
  73. claude_mpm/cli/commands/configure_template_editor.py +497 -0
  74. claude_mpm/cli/commands/configure_validators.py +73 -0
  75. claude_mpm/cli/commands/local_deploy.py +537 -0
  76. claude_mpm/cli/commands/memory.py +54 -20
  77. claude_mpm/cli/commands/mpm_init.py +585 -196
  78. claude_mpm/cli/commands/mpm_init_handler.py +37 -3
  79. claude_mpm/cli/commands/search.py +170 -4
  80. claude_mpm/cli/commands/upgrade.py +152 -0
  81. claude_mpm/cli/executor.py +202 -0
  82. claude_mpm/cli/helpers.py +105 -0
  83. claude_mpm/cli/interactive/__init__.py +3 -0
  84. claude_mpm/cli/interactive/skills_wizard.py +491 -0
  85. claude_mpm/cli/parsers/__init__.py +7 -1
  86. claude_mpm/cli/parsers/agents_parser.py +9 -0
  87. claude_mpm/cli/parsers/auto_configure_parser.py +245 -0
  88. claude_mpm/cli/parsers/base_parser.py +110 -3
  89. claude_mpm/cli/parsers/local_deploy_parser.py +227 -0
  90. claude_mpm/cli/parsers/mpm_init_parser.py +65 -5
  91. claude_mpm/cli/shared/output_formatters.py +28 -19
  92. claude_mpm/cli/startup.py +481 -0
  93. claude_mpm/cli/utils.py +52 -1
  94. claude_mpm/commands/mpm-agents-detect.md +168 -0
  95. claude_mpm/commands/mpm-agents-recommend.md +214 -0
  96. claude_mpm/commands/mpm-agents.md +75 -1
  97. claude_mpm/commands/mpm-auto-configure.md +217 -0
  98. claude_mpm/commands/mpm-help.md +163 -0
  99. claude_mpm/commands/mpm-init.md +148 -3
  100. claude_mpm/commands/mpm-version.md +113 -0
  101. claude_mpm/commands/mpm.md +1 -0
  102. claude_mpm/config/agent_config.py +2 -2
  103. claude_mpm/config/model_config.py +428 -0
  104. claude_mpm/constants.py +1 -0
  105. claude_mpm/core/base_service.py +13 -12
  106. claude_mpm/core/enums.py +452 -0
  107. claude_mpm/core/factories.py +1 -1
  108. claude_mpm/core/instruction_reinforcement_hook.py +2 -1
  109. claude_mpm/core/interactive_session.py +9 -3
  110. claude_mpm/core/log_manager.py +2 -0
  111. claude_mpm/core/logging_config.py +6 -2
  112. claude_mpm/core/oneshot_session.py +8 -4
  113. claude_mpm/core/optimized_agent_loader.py +3 -3
  114. claude_mpm/core/output_style_manager.py +12 -192
  115. claude_mpm/core/service_registry.py +5 -1
  116. claude_mpm/core/types.py +2 -9
  117. claude_mpm/core/typing_utils.py +7 -6
  118. claude_mpm/dashboard/static/js/dashboard.js +0 -14
  119. claude_mpm/dashboard/templates/index.html +3 -41
  120. claude_mpm/hooks/__init__.py +20 -0
  121. claude_mpm/hooks/claude_hooks/event_handlers.py +4 -2
  122. claude_mpm/hooks/claude_hooks/response_tracking.py +35 -1
  123. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +23 -2
  124. claude_mpm/hooks/failure_learning/__init__.py +60 -0
  125. claude_mpm/hooks/failure_learning/failure_detection_hook.py +235 -0
  126. claude_mpm/hooks/failure_learning/fix_detection_hook.py +217 -0
  127. claude_mpm/hooks/failure_learning/learning_extraction_hook.py +286 -0
  128. claude_mpm/hooks/instruction_reinforcement.py +7 -2
  129. claude_mpm/hooks/kuzu_enrichment_hook.py +263 -0
  130. claude_mpm/hooks/kuzu_memory_hook.py +37 -12
  131. claude_mpm/hooks/kuzu_response_hook.py +183 -0
  132. claude_mpm/models/resume_log.py +340 -0
  133. claude_mpm/services/agents/__init__.py +18 -5
  134. claude_mpm/services/agents/auto_config_manager.py +796 -0
  135. claude_mpm/services/agents/deployment/agent_configuration_manager.py +1 -1
  136. claude_mpm/services/agents/deployment/agent_record_service.py +1 -1
  137. claude_mpm/services/agents/deployment/agent_validator.py +17 -1
  138. claude_mpm/services/agents/deployment/async_agent_deployment.py +1 -1
  139. claude_mpm/services/agents/deployment/interface_adapter.py +3 -2
  140. claude_mpm/services/agents/deployment/local_template_deployment.py +1 -1
  141. claude_mpm/services/agents/deployment/pipeline/steps/agent_processing_step.py +7 -6
  142. claude_mpm/services/agents/deployment/pipeline/steps/base_step.py +7 -16
  143. claude_mpm/services/agents/deployment/pipeline/steps/configuration_step.py +4 -3
  144. claude_mpm/services/agents/deployment/pipeline/steps/target_directory_step.py +5 -3
  145. claude_mpm/services/agents/deployment/pipeline/steps/validation_step.py +6 -5
  146. claude_mpm/services/agents/deployment/refactored_agent_deployment_service.py +9 -6
  147. claude_mpm/services/agents/deployment/validation/__init__.py +3 -1
  148. claude_mpm/services/agents/deployment/validation/validation_result.py +1 -9
  149. claude_mpm/services/agents/local_template_manager.py +1 -1
  150. claude_mpm/services/agents/memory/agent_memory_manager.py +5 -2
  151. claude_mpm/services/agents/observers.py +547 -0
  152. claude_mpm/services/agents/recommender.py +568 -0
  153. claude_mpm/services/agents/registry/modification_tracker.py +5 -2
  154. claude_mpm/services/command_handler_service.py +11 -5
  155. claude_mpm/services/core/__init__.py +33 -1
  156. claude_mpm/services/core/interfaces/__init__.py +90 -3
  157. claude_mpm/services/core/interfaces/agent.py +184 -0
  158. claude_mpm/services/core/interfaces/health.py +172 -0
  159. claude_mpm/services/core/interfaces/model.py +281 -0
  160. claude_mpm/services/core/interfaces/process.py +372 -0
  161. claude_mpm/services/core/interfaces/project.py +121 -0
  162. claude_mpm/services/core/interfaces/restart.py +307 -0
  163. claude_mpm/services/core/interfaces/stability.py +260 -0
  164. claude_mpm/services/core/memory_manager.py +11 -24
  165. claude_mpm/services/core/models/__init__.py +79 -0
  166. claude_mpm/services/core/models/agent_config.py +381 -0
  167. claude_mpm/services/core/models/health.py +162 -0
  168. claude_mpm/services/core/models/process.py +235 -0
  169. claude_mpm/services/core/models/restart.py +302 -0
  170. claude_mpm/services/core/models/stability.py +264 -0
  171. claude_mpm/services/core/models/toolchain.py +306 -0
  172. claude_mpm/services/core/path_resolver.py +23 -7
  173. claude_mpm/services/diagnostics/__init__.py +2 -2
  174. claude_mpm/services/diagnostics/checks/agent_check.py +25 -24
  175. claude_mpm/services/diagnostics/checks/claude_code_check.py +24 -23
  176. claude_mpm/services/diagnostics/checks/common_issues_check.py +25 -24
  177. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -23
  178. claude_mpm/services/diagnostics/checks/filesystem_check.py +18 -17
  179. claude_mpm/services/diagnostics/checks/installation_check.py +30 -29
  180. claude_mpm/services/diagnostics/checks/instructions_check.py +20 -19
  181. claude_mpm/services/diagnostics/checks/mcp_check.py +50 -36
  182. claude_mpm/services/diagnostics/checks/mcp_services_check.py +38 -33
  183. claude_mpm/services/diagnostics/checks/monitor_check.py +23 -22
  184. claude_mpm/services/diagnostics/checks/startup_log_check.py +9 -8
  185. claude_mpm/services/diagnostics/diagnostic_runner.py +6 -5
  186. claude_mpm/services/diagnostics/doctor_reporter.py +28 -25
  187. claude_mpm/services/diagnostics/models.py +19 -24
  188. claude_mpm/services/infrastructure/monitoring/__init__.py +1 -1
  189. claude_mpm/services/infrastructure/monitoring/aggregator.py +12 -12
  190. claude_mpm/services/infrastructure/monitoring/base.py +5 -13
  191. claude_mpm/services/infrastructure/monitoring/network.py +7 -6
  192. claude_mpm/services/infrastructure/monitoring/process.py +13 -12
  193. claude_mpm/services/infrastructure/monitoring/resources.py +7 -6
  194. claude_mpm/services/infrastructure/monitoring/service.py +16 -15
  195. claude_mpm/services/infrastructure/resume_log_generator.py +439 -0
  196. claude_mpm/services/local_ops/__init__.py +163 -0
  197. claude_mpm/services/local_ops/crash_detector.py +257 -0
  198. claude_mpm/services/local_ops/health_checks/__init__.py +28 -0
  199. claude_mpm/services/local_ops/health_checks/http_check.py +224 -0
  200. claude_mpm/services/local_ops/health_checks/process_check.py +236 -0
  201. claude_mpm/services/local_ops/health_checks/resource_check.py +255 -0
  202. claude_mpm/services/local_ops/health_manager.py +430 -0
  203. claude_mpm/services/local_ops/log_monitor.py +396 -0
  204. claude_mpm/services/local_ops/memory_leak_detector.py +294 -0
  205. claude_mpm/services/local_ops/process_manager.py +595 -0
  206. claude_mpm/services/local_ops/resource_monitor.py +331 -0
  207. claude_mpm/services/local_ops/restart_manager.py +401 -0
  208. claude_mpm/services/local_ops/restart_policy.py +387 -0
  209. claude_mpm/services/local_ops/state_manager.py +372 -0
  210. claude_mpm/services/local_ops/unified_manager.py +600 -0
  211. claude_mpm/services/mcp_config_manager.py +9 -4
  212. claude_mpm/services/mcp_gateway/core/__init__.py +1 -2
  213. claude_mpm/services/mcp_gateway/core/base.py +18 -31
  214. claude_mpm/services/mcp_gateway/main.py +30 -0
  215. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +206 -32
  216. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +30 -28
  217. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +25 -5
  218. claude_mpm/services/mcp_service_verifier.py +1 -1
  219. claude_mpm/services/memory/failure_tracker.py +563 -0
  220. claude_mpm/services/memory_hook_service.py +165 -4
  221. claude_mpm/services/model/__init__.py +147 -0
  222. claude_mpm/services/model/base_provider.py +365 -0
  223. claude_mpm/services/model/claude_provider.py +412 -0
  224. claude_mpm/services/model/model_router.py +453 -0
  225. claude_mpm/services/model/ollama_provider.py +415 -0
  226. claude_mpm/services/monitor/daemon_manager.py +3 -2
  227. claude_mpm/services/monitor/handlers/dashboard.py +2 -1
  228. claude_mpm/services/monitor/handlers/hooks.py +2 -1
  229. claude_mpm/services/monitor/management/lifecycle.py +3 -2
  230. claude_mpm/services/monitor/server.py +2 -1
  231. claude_mpm/services/project/__init__.py +23 -0
  232. claude_mpm/services/project/detection_strategies.py +719 -0
  233. claude_mpm/services/project/toolchain_analyzer.py +581 -0
  234. claude_mpm/services/self_upgrade_service.py +342 -0
  235. claude_mpm/services/session_management_service.py +3 -2
  236. claude_mpm/services/session_manager.py +205 -1
  237. claude_mpm/services/shared/async_service_base.py +16 -27
  238. claude_mpm/services/shared/lifecycle_service_base.py +1 -14
  239. claude_mpm/services/socketio/handlers/__init__.py +5 -2
  240. claude_mpm/services/socketio/handlers/hook.py +13 -2
  241. claude_mpm/services/socketio/handlers/registry.py +4 -2
  242. claude_mpm/services/socketio/server/main.py +10 -8
  243. claude_mpm/services/subprocess_launcher_service.py +14 -5
  244. claude_mpm/services/unified/analyzer_strategies/code_analyzer.py +8 -7
  245. claude_mpm/services/unified/analyzer_strategies/dependency_analyzer.py +6 -5
  246. claude_mpm/services/unified/analyzer_strategies/performance_analyzer.py +8 -7
  247. claude_mpm/services/unified/analyzer_strategies/security_analyzer.py +7 -6
  248. claude_mpm/services/unified/analyzer_strategies/structure_analyzer.py +5 -4
  249. claude_mpm/services/unified/config_strategies/validation_strategy.py +13 -9
  250. claude_mpm/services/unified/deployment_strategies/cloud_strategies.py +10 -3
  251. claude_mpm/services/unified/deployment_strategies/local.py +6 -5
  252. claude_mpm/services/unified/deployment_strategies/utils.py +6 -5
  253. claude_mpm/services/unified/deployment_strategies/vercel.py +7 -6
  254. claude_mpm/services/unified/interfaces.py +3 -1
  255. claude_mpm/services/unified/unified_analyzer.py +14 -10
  256. claude_mpm/services/unified/unified_config.py +2 -1
  257. claude_mpm/services/unified/unified_deployment.py +9 -4
  258. claude_mpm/services/version_service.py +104 -1
  259. claude_mpm/skills/__init__.py +21 -0
  260. claude_mpm/skills/bundled/__init__.py +6 -0
  261. claude_mpm/skills/bundled/api-documentation.md +393 -0
  262. claude_mpm/skills/bundled/async-testing.md +571 -0
  263. claude_mpm/skills/bundled/code-review.md +143 -0
  264. claude_mpm/skills/bundled/database-migration.md +199 -0
  265. claude_mpm/skills/bundled/docker-containerization.md +194 -0
  266. claude_mpm/skills/bundled/express-local-dev.md +1429 -0
  267. claude_mpm/skills/bundled/fastapi-local-dev.md +1199 -0
  268. claude_mpm/skills/bundled/git-workflow.md +414 -0
  269. claude_mpm/skills/bundled/imagemagick.md +204 -0
  270. claude_mpm/skills/bundled/json-data-handling.md +223 -0
  271. claude_mpm/skills/bundled/nextjs-local-dev.md +807 -0
  272. claude_mpm/skills/bundled/pdf.md +141 -0
  273. claude_mpm/skills/bundled/performance-profiling.md +567 -0
  274. claude_mpm/skills/bundled/refactoring-patterns.md +180 -0
  275. claude_mpm/skills/bundled/security-scanning.md +327 -0
  276. claude_mpm/skills/bundled/systematic-debugging.md +473 -0
  277. claude_mpm/skills/bundled/test-driven-development.md +378 -0
  278. claude_mpm/skills/bundled/vite-local-dev.md +1061 -0
  279. claude_mpm/skills/bundled/web-performance-optimization.md +2305 -0
  280. claude_mpm/skills/bundled/xlsx.md +157 -0
  281. claude_mpm/skills/registry.py +286 -0
  282. claude_mpm/skills/skill_manager.py +310 -0
  283. claude_mpm/storage/state_storage.py +15 -15
  284. claude_mpm/tools/code_tree_analyzer.py +177 -141
  285. claude_mpm/tools/code_tree_events.py +4 -2
  286. claude_mpm/utils/agent_dependency_loader.py +40 -20
  287. claude_mpm/utils/display_helper.py +260 -0
  288. claude_mpm/utils/git_analyzer.py +407 -0
  289. claude_mpm/utils/robust_installer.py +73 -19
  290. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/METADATA +129 -12
  291. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/RECORD +295 -193
  292. claude_mpm/dashboard/static/css/code-tree.css +0 -1639
  293. claude_mpm/dashboard/static/index-hub-backup.html +0 -713
  294. claude_mpm/dashboard/static/js/components/code-tree/tree-breadcrumb.js +0 -353
  295. claude_mpm/dashboard/static/js/components/code-tree/tree-constants.js +0 -235
  296. claude_mpm/dashboard/static/js/components/code-tree/tree-search.js +0 -409
  297. claude_mpm/dashboard/static/js/components/code-tree/tree-utils.js +0 -435
  298. claude_mpm/dashboard/static/js/components/code-tree.js +0 -5869
  299. claude_mpm/dashboard/static/js/components/code-viewer.js +0 -1386
  300. claude_mpm/hooks/claude_hooks/hook_handler_eventbus.py +0 -425
  301. claude_mpm/hooks/claude_hooks/hook_handler_original.py +0 -1041
  302. claude_mpm/hooks/claude_hooks/hook_handler_refactored.py +0 -347
  303. claude_mpm/services/agents/deployment/agent_lifecycle_manager_refactored.py +0 -575
  304. claude_mpm/services/project/analyzer_refactored.py +0 -450
  305. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/WHEEL +0 -0
  306. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/entry_points.txt +0 -0
  307. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/licenses/LICENSE +0 -0
  308. {claude_mpm-4.7.4.dist-info → claude_mpm-4.18.2.dist-info}/top_level.txt +0 -0
@@ -1528,10 +1528,31 @@ class CodeTreeAnalyzer:
1528
1528
  if not path.exists() or not path.is_file():
1529
1529
  return {"error": f"Invalid file: {file_path}"}
1530
1530
 
1531
- # Get language first (needed for return statement)
1532
1531
  language = self._get_language(path)
1532
+ self._emit_analysis_start(path, language)
1533
1533
 
1534
- # Emit analysis start event
1534
+ # Check cache
1535
+ file_hash = self._get_file_hash(path)
1536
+ cache_key = f"{file_path}:{file_hash}"
1537
+
1538
+ if cache_key in self.cache:
1539
+ nodes = self.cache[cache_key]
1540
+ self._emit_cache_hit(path)
1541
+ filtered_nodes = self._filter_nodes(nodes)
1542
+ else:
1543
+ nodes, filtered_nodes, duration = self._analyze_and_cache_file(
1544
+ path, language, cache_key
1545
+ )
1546
+ self._emit_analysis_complete(path, filtered_nodes, duration)
1547
+
1548
+ # Prepare final data structures
1549
+ final_nodes = self._prepare_final_nodes(nodes, filtered_nodes)
1550
+ elements = self._convert_nodes_to_elements(final_nodes)
1551
+
1552
+ return self._build_result(file_path, language, final_nodes, elements)
1553
+
1554
+ def _emit_analysis_start(self, path: Path, language: str) -> None:
1555
+ """Emit analysis start event."""
1535
1556
  if self.emitter:
1536
1557
  from datetime import datetime
1537
1558
 
@@ -1546,179 +1567,194 @@ class CodeTreeAnalyzer:
1546
1567
  },
1547
1568
  )
1548
1569
 
1549
- # Check cache
1550
- file_hash = self._get_file_hash(path)
1551
- cache_key = f"{file_path}:{file_hash}"
1570
+ def _emit_cache_hit(self, path: Path) -> None:
1571
+ """Emit cache hit event."""
1572
+ if self.emitter:
1573
+ from datetime import datetime
1552
1574
 
1553
- if cache_key in self.cache:
1554
- nodes = self.cache[cache_key]
1555
- if self.emitter:
1556
- from datetime import datetime
1575
+ self.emitter.emit(
1576
+ "info",
1577
+ {
1578
+ "type": "cache.hit",
1579
+ "file": str(path),
1580
+ "message": f"Using cached analysis for {path.name}",
1581
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1582
+ },
1583
+ )
1557
1584
 
1558
- self.emitter.emit(
1559
- "info",
1560
- {
1561
- "type": "cache.hit",
1562
- "file": str(path),
1563
- "message": f"Using cached analysis for {path.name}",
1564
- "timestamp": datetime.now(timezone.utc).isoformat(),
1565
- },
1566
- )
1567
- else:
1568
- # Analyze file
1569
- if self.emitter:
1570
- from datetime import datetime
1585
+ def _emit_cache_miss(self, path: Path) -> None:
1586
+ """Emit cache miss event."""
1587
+ if self.emitter:
1588
+ from datetime import datetime
1571
1589
 
1572
- self.emitter.emit(
1573
- "info",
1574
- {
1575
- "type": "cache.miss",
1576
- "file": str(path),
1577
- "message": f"Cache miss, analyzing fresh: {path.name}",
1578
- "timestamp": datetime.now(timezone.utc).isoformat(),
1579
- },
1580
- )
1590
+ self.emitter.emit(
1591
+ "info",
1592
+ {
1593
+ "type": "cache.miss",
1594
+ "file": str(path),
1595
+ "message": f"Cache miss, analyzing fresh: {path.name}",
1596
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1597
+ },
1598
+ )
1581
1599
 
1582
- if language == "python":
1583
- analyzer = self.python_analyzer
1584
- elif language in {"javascript", "typescript"}:
1585
- analyzer = self.javascript_analyzer
1586
- else:
1587
- analyzer = self.generic_analyzer
1600
+ def _emit_parsing_start(self, path: Path) -> None:
1601
+ """Emit parsing start event."""
1602
+ if self.emitter:
1603
+ from datetime import datetime
1604
+
1605
+ self.emitter.emit(
1606
+ "info",
1607
+ {
1608
+ "type": "analysis.parse",
1609
+ "file": str(path),
1610
+ "message": f"Parsing file content: {path.name}",
1611
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1612
+ },
1613
+ )
1588
1614
 
1589
- start_time = time.time()
1615
+ def _emit_node_found(self, node: CodeNode, path: Path) -> None:
1616
+ """Emit node found event."""
1617
+ if self.emitter:
1618
+ from datetime import datetime
1590
1619
 
1591
- # Emit parsing event
1592
- if self.emitter:
1593
- from datetime import datetime
1620
+ self.emitter.emit(
1621
+ "info",
1622
+ {
1623
+ "type": f"analysis.{node.node_type}",
1624
+ "name": node.name,
1625
+ "file": str(path),
1626
+ "line_start": node.line_start,
1627
+ "complexity": node.complexity,
1628
+ "message": f"Found {node.node_type}: {node.name}",
1629
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1630
+ },
1631
+ )
1594
1632
 
1595
- self.emitter.emit(
1596
- "info",
1597
- {
1598
- "type": "analysis.parse",
1599
- "file": str(path),
1600
- "message": f"Parsing file content: {path.name}",
1601
- "timestamp": datetime.now(timezone.utc).isoformat(),
1602
- },
1603
- )
1633
+ def _emit_analysis_complete(
1634
+ self, path: Path, filtered_nodes: list, duration: float
1635
+ ) -> None:
1636
+ """Emit analysis complete event."""
1637
+ if not self.emitter:
1638
+ return
1604
1639
 
1605
- nodes = analyzer.analyze_file(path) if analyzer else []
1606
- duration = time.time() - start_time
1640
+ from datetime import datetime
1641
+
1642
+ stats = self._calculate_node_stats(filtered_nodes)
1643
+ self.emitter.emit(
1644
+ "info",
1645
+ {
1646
+ "type": "analysis.complete",
1647
+ "file": str(path),
1648
+ "stats": stats,
1649
+ "duration": duration,
1650
+ "message": f"Analysis complete: {stats['classes']} classes, {stats['functions']} functions, {stats['methods']} methods",
1651
+ "timestamp": datetime.now(timezone.utc).isoformat(),
1652
+ },
1653
+ )
1654
+ self.emitter.emit_file_analyzed(str(path), filtered_nodes, duration)
1607
1655
 
1608
- # Cache results
1609
- self.cache[cache_key] = nodes
1656
+ def _analyze_and_cache_file(
1657
+ self, path: Path, language: str, cache_key: str
1658
+ ) -> tuple:
1659
+ """Analyze file content and cache results."""
1660
+ self._emit_cache_miss(path)
1661
+ self._emit_parsing_start(path)
1610
1662
 
1611
- # Filter internal functions before emitting
1612
- filtered_nodes = []
1613
- classes_count = 0
1614
- functions_count = 0
1615
- methods_count = 0
1663
+ # Select analyzer based on language
1664
+ analyzer = self._select_analyzer(language)
1616
1665
 
1617
- for node in nodes:
1618
- # Only include main structural elements
1619
- if not self._is_internal_node(node):
1620
- # Emit found element event
1621
- if self.emitter:
1622
- from datetime import datetime
1666
+ # Perform analysis
1667
+ start_time = time.time()
1668
+ nodes = analyzer.analyze_file(path) if analyzer else []
1669
+ duration = time.time() - start_time
1623
1670
 
1624
- self.emitter.emit(
1625
- "info",
1626
- {
1627
- "type": f"analysis.{node.node_type}",
1628
- "name": node.name,
1629
- "file": str(path),
1630
- "line_start": node.line_start,
1631
- "complexity": node.complexity,
1632
- "message": f"Found {node.node_type}: {node.name}",
1633
- "timestamp": datetime.now(timezone.utc).isoformat(),
1634
- },
1635
- )
1671
+ # Cache results
1672
+ self.cache[cache_key] = nodes
1636
1673
 
1637
- # Count node types
1638
- if node.node_type == "class":
1639
- classes_count += 1
1640
- elif node.node_type == "function":
1641
- functions_count += 1
1642
- elif node.node_type == "method":
1643
- methods_count += 1
1644
-
1645
- filtered_nodes.append(
1646
- {
1647
- "name": node.name,
1648
- "type": node.node_type,
1649
- "line_start": node.line_start,
1650
- "line_end": node.line_end,
1651
- "complexity": node.complexity,
1652
- "has_docstring": node.has_docstring,
1653
- "signature": node.signature,
1654
- }
1655
- )
1674
+ # Filter and process nodes
1675
+ filtered_nodes = self._filter_and_emit_nodes(nodes, path)
1656
1676
 
1657
- # Emit analysis complete event with stats
1658
- if self.emitter:
1659
- from datetime import datetime
1677
+ return nodes, filtered_nodes, duration
1660
1678
 
1661
- self.emitter.emit(
1662
- "info",
1663
- {
1664
- "type": "analysis.complete",
1665
- "file": str(path),
1666
- "stats": {
1667
- "classes": classes_count,
1668
- "functions": functions_count,
1669
- "methods": methods_count,
1670
- "total_nodes": len(filtered_nodes),
1671
- },
1672
- "duration": duration,
1673
- "message": f"Analysis complete: {classes_count} classes, {functions_count} functions, {methods_count} methods",
1674
- "timestamp": datetime.now(timezone.utc).isoformat(),
1675
- },
1676
- )
1679
+ def _select_analyzer(self, language: str):
1680
+ """Select appropriate analyzer for language."""
1681
+ if language == "python":
1682
+ return self.python_analyzer
1683
+ if language in {"javascript", "typescript"}:
1684
+ return self.javascript_analyzer
1685
+ return self.generic_analyzer
1677
1686
 
1678
- self.emitter.emit_file_analyzed(file_path, filtered_nodes, duration)
1687
+ def _filter_nodes(self, nodes: list) -> list:
1688
+ """Filter nodes without emitting events."""
1689
+ return [self._node_to_dict(n) for n in nodes if not self._is_internal_node(n)]
1679
1690
 
1680
- # Prepare the nodes data
1681
- final_nodes = (
1682
- filtered_nodes
1683
- if "filtered_nodes" in locals()
1684
- else [
1685
- {
1686
- "name": n.name,
1687
- "type": n.node_type,
1688
- "line_start": n.line_start,
1689
- "line_end": n.line_end,
1690
- "complexity": n.complexity,
1691
- "has_docstring": n.has_docstring,
1692
- "signature": n.signature,
1693
- }
1694
- for n in nodes
1695
- if not self._is_internal_node(n)
1696
- ]
1697
- )
1691
+ def _filter_and_emit_nodes(self, nodes: list, path: Path) -> list:
1692
+ """Filter nodes and emit events for each."""
1693
+ filtered_nodes = []
1694
+ for node in nodes:
1695
+ if not self._is_internal_node(node):
1696
+ self._emit_node_found(node, path)
1697
+ filtered_nodes.append(self._node_to_dict(node))
1698
+ return filtered_nodes
1699
+
1700
+ def _node_to_dict(self, node: CodeNode) -> dict:
1701
+ """Convert CodeNode to dictionary."""
1702
+ return {
1703
+ "name": node.name,
1704
+ "type": node.node_type,
1705
+ "line_start": node.line_start,
1706
+ "line_end": node.line_end,
1707
+ "complexity": node.complexity,
1708
+ "has_docstring": node.has_docstring,
1709
+ "signature": node.signature,
1710
+ }
1711
+
1712
+ def _calculate_node_stats(self, filtered_nodes: list) -> dict:
1713
+ """Calculate statistics from filtered nodes."""
1714
+ classes_count = sum(1 for n in filtered_nodes if n["type"] == "class")
1715
+ functions_count = sum(1 for n in filtered_nodes if n["type"] == "function")
1716
+ methods_count = sum(1 for n in filtered_nodes if n["type"] == "method")
1717
+ return {
1718
+ "classes": classes_count,
1719
+ "functions": functions_count,
1720
+ "methods": methods_count,
1721
+ "total_nodes": len(filtered_nodes),
1722
+ }
1723
+
1724
+ def _prepare_final_nodes(self, nodes: list, filtered_nodes: list) -> list:
1725
+ """Prepare final nodes data structure."""
1726
+ if filtered_nodes:
1727
+ return filtered_nodes
1728
+ return [self._node_to_dict(n) for n in nodes if not self._is_internal_node(n)]
1698
1729
 
1699
- # Convert nodes to elements format for dashboard compatibility
1730
+ def _convert_nodes_to_elements(self, final_nodes: list) -> list:
1731
+ """Convert nodes to elements format for dashboard."""
1700
1732
  elements = []
1701
1733
  for node in final_nodes:
1702
1734
  element = {
1703
1735
  "name": node["name"],
1704
1736
  "type": node["type"],
1705
- "line": node["line_start"], # Dashboard expects 'line' not 'line_start'
1737
+ "line": node["line_start"],
1706
1738
  "complexity": node["complexity"],
1707
1739
  "signature": node.get("signature", ""),
1708
1740
  "has_docstring": node.get("has_docstring", False),
1709
1741
  }
1710
- # Add methods if it's a class (for expandable tree)
1711
1742
  if node["type"] == "class":
1712
- element["methods"] = [] # Could be populated with class methods
1743
+ element["methods"] = []
1713
1744
  elements.append(element)
1745
+ return elements
1714
1746
 
1747
+ def _build_result(
1748
+ self, file_path: str, language: str, final_nodes: list, elements: list
1749
+ ) -> dict:
1750
+ """Build final result dictionary."""
1715
1751
  return {
1716
1752
  "path": file_path,
1717
1753
  "language": language,
1718
- "nodes": final_nodes, # Keep for backward compatibility
1719
- "elements": elements, # Add for dashboard compatibility
1754
+ "nodes": final_nodes,
1755
+ "elements": elements,
1720
1756
  "complexity": sum(e["complexity"] for e in elements),
1721
- "lines": len(elements), # Simple line count approximation
1757
+ "lines": len(elements),
1722
1758
  "stats": {
1723
1759
  "classes": len([e for e in elements if e["type"] == "class"]),
1724
1760
  "functions": len([e for e in elements if e["type"] == "function"]),
@@ -22,6 +22,8 @@ from dataclasses import asdict, dataclass
22
22
  from datetime import datetime, timezone
23
23
  from typing import Any, Dict, List, Optional
24
24
 
25
+ from ..core.enums import OperationResult
26
+
25
27
  try:
26
28
  import socketio
27
29
 
@@ -268,7 +270,7 @@ class CodeTreeEventEmitter:
268
270
  "name": Path(file_path).name,
269
271
  "language": language or "unknown",
270
272
  "type": "file",
271
- "status": "analyzing",
273
+ "status": OperationResult.PENDING,
272
274
  },
273
275
  )
274
276
 
@@ -287,7 +289,7 @@ class CodeTreeEventEmitter:
287
289
  "nodes_count": nodes_count,
288
290
  "duration": duration,
289
291
  "type": "file",
290
- "status": "complete",
292
+ "status": OperationResult.COMPLETED,
291
293
  },
292
294
  )
293
295
 
@@ -14,7 +14,7 @@ import logging
14
14
  import subprocess
15
15
  import sys
16
16
  import time
17
- from typing import Dict, List, Optional, Set, Tuple
17
+ from typing import Any, Dict, List, Optional, Set, Tuple
18
18
 
19
19
  from packaging.requirements import InvalidRequirement, Requirement
20
20
 
@@ -72,7 +72,7 @@ class AgentDependencyLoader:
72
72
  Returns:
73
73
  Dictionary mapping agent IDs to their file paths
74
74
  """
75
- deployed_agents = {}
75
+ deployed_agents: Dict[str, Path] = {}
76
76
  claude_agents_dir = Path.cwd() / ".claude" / "agents"
77
77
 
78
78
  if not claude_agents_dir.exists():
@@ -85,7 +85,7 @@ class AgentDependencyLoader:
85
85
  deployed_agents[agent_id] = agent_file
86
86
  logger.debug(f"Found deployed agent: {agent_id}")
87
87
 
88
- logger.info(f"Discovered {len(deployed_agents)} deployed agents")
88
+ logger.debug(f"Discovered {len(deployed_agents)} deployed agents")
89
89
  self.deployed_agents = deployed_agents
90
90
  return deployed_agents
91
91
 
@@ -121,7 +121,7 @@ class AgentDependencyLoader:
121
121
  logger.warning(f"Failed to load config for {agent_id}: {e}")
122
122
 
123
123
  self.agent_dependencies = agent_dependencies
124
- logger.info(f"Loaded dependencies for {len(agent_dependencies)} agents")
124
+ logger.debug(f"Loaded dependencies for {len(agent_dependencies)} agents")
125
125
  return agent_dependencies
126
126
 
127
127
  def check_python_dependency(self, package_spec: str) -> Tuple[bool, Optional[str]]:
@@ -409,7 +409,7 @@ class AgentDependencyLoader:
409
409
  Returns:
410
410
  Analysis results including missing and satisfied dependencies
411
411
  """
412
- results = {
412
+ results: Dict[str, Any] = {
413
413
  "agents": {},
414
414
  "summary": {
415
415
  "total_agents": len(self.deployed_agents),
@@ -422,7 +422,7 @@ class AgentDependencyLoader:
422
422
  }
423
423
 
424
424
  for agent_id, deps in self.agent_dependencies.items():
425
- agent_result = {
425
+ agent_result: Dict[str, Dict[str, List[str]]] = {
426
426
  "python": {"satisfied": [], "missing": [], "outdated": []},
427
427
  "system": {"satisfied": [], "missing": []},
428
428
  }
@@ -476,8 +476,8 @@ class AgentDependencyLoader:
476
476
  """
477
477
  import sys
478
478
 
479
- compatible = []
480
- incompatible = []
479
+ compatible: List[str] = []
480
+ incompatible: List[str] = []
481
481
 
482
482
  for dep in dependencies:
483
483
  try:
@@ -593,20 +593,37 @@ class AgentDependencyLoader:
593
593
  try:
594
594
  cmd = [sys.executable, "-m", "pip", "install"]
595
595
 
596
- # Check for PEP 668 managed environment
596
+ # Check environment and add appropriate flags
597
+ import os
597
598
  import sysconfig
598
599
 
599
- stdlib_path = sysconfig.get_path("stdlib")
600
- marker_file = Path(stdlib_path) / "EXTERNALLY-MANAGED"
601
- parent_marker = marker_file.parent.parent / "EXTERNALLY-MANAGED"
600
+ # Check if in virtualenv
601
+ in_virtualenv = (
602
+ (hasattr(sys, "base_prefix") and sys.base_prefix != sys.prefix)
603
+ or (hasattr(sys, "real_prefix"))
604
+ or (os.environ.get("VIRTUAL_ENV") is not None)
605
+ )
602
606
 
603
- if marker_file.exists() or parent_marker.exists():
604
- logger.warning(
605
- "PEP 668 managed environment detected. "
606
- "Installing with --break-system-packages --user flags. "
607
- "Consider using a virtual environment instead."
608
- )
609
- cmd.extend(["--break-system-packages", "--user"])
607
+ if in_virtualenv:
608
+ # In virtualenv - no special flags needed
609
+ logger.debug("Installing in virtualenv (no special flags)")
610
+ else:
611
+ # Check for PEP 668 managed environment
612
+ stdlib_path = sysconfig.get_path("stdlib")
613
+ marker_file = Path(stdlib_path) / "EXTERNALLY-MANAGED"
614
+ parent_marker = marker_file.parent.parent / "EXTERNALLY-MANAGED"
615
+
616
+ if marker_file.exists() or parent_marker.exists():
617
+ logger.warning(
618
+ "PEP 668 managed environment detected. "
619
+ "Installing with --break-system-packages flag. "
620
+ "Consider using a virtual environment instead."
621
+ )
622
+ cmd.append("--break-system-packages")
623
+ else:
624
+ # Normal system Python - use --user
625
+ cmd.append("--user")
626
+ logger.debug("Installing with --user flag")
610
627
 
611
628
  cmd.extend(compatible)
612
629
 
@@ -946,13 +963,16 @@ class AgentDependencyLoader:
946
963
 
947
964
  def check_deployed_agent_dependencies(
948
965
  auto_install: bool = False, verbose: bool = False
949
- ) -> None:
966
+ ) -> int:
950
967
  """
951
968
  Check dependencies for currently deployed agents.
952
969
 
953
970
  Args:
954
971
  auto_install: If True, automatically install missing Python dependencies
955
972
  verbose: If True, enable verbose logging
973
+
974
+ Returns:
975
+ Status code: 0 if all dependencies satisfied, 1 if missing dependencies
956
976
  """
957
977
  if verbose:
958
978
  logging.getLogger().setLevel(logging.DEBUG)