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,3220 +0,0 @@
1
- /**
2
- * Code Tree Component
3
- *
4
- * D3.js-based tree visualization for displaying AST-based code structure.
5
- * Shows modules, classes, functions, and methods with complexity-based coloring.
6
- * Provides real-time updates during code analysis.
7
- *
8
- * ===== CACHE CLEAR INSTRUCTIONS =====
9
- * If tree still moves/centers after update:
10
- * 1. Hard refresh: Ctrl+Shift+R (Windows/Linux) or Cmd+Shift+R (Mac)
11
- * 2. Or open DevTools (F12) → Network tab → Check "Disable cache"
12
- * 3. Or clear browser cache: Ctrl+Shift+Delete → Clear cached images and files
13
- *
14
- * Version: 2025-08-29T15:30:00Z - ALL CENTERING REMOVED
15
- * Last Update: Completely disabled tree centering/movement on node clicks
16
- */
17
-
18
- class CodeTree {
19
- constructor() {
20
- this.container = null;
21
- this.svg = null;
22
- this.treeData = null;
23
- this.root = null;
24
- this.treeLayout = null;
25
- this.treeGroup = null;
26
- this.nodes = new Map();
27
- this.stats = {
28
- files: 0,
29
- classes: 0,
30
- functions: 0,
31
- methods: 0,
32
- lines: 0
33
- };
34
- // Radial layout settings
35
- this.isRadialLayout = true; // Toggle for radial vs linear layout
36
- this.margin = {top: 20, right: 20, bottom: 20, left: 20};
37
- this.width = 960 - this.margin.left - this.margin.right;
38
- this.height = 600 - this.margin.top - this.margin.bottom;
39
- this.radius = Math.min(this.width, this.height) / 2;
40
- this.nodeId = 0;
41
- this.duration = 750;
42
- this.languageFilter = 'all';
43
- this.searchTerm = '';
44
- this.tooltip = null;
45
- this.initialized = false;
46
- this.analyzing = false;
47
- this.selectedNode = null;
48
- this.socket = null;
49
- this.autoDiscovered = false; // Track if auto-discovery has been done
50
- this.zoom = null; // Store zoom behavior
51
- this.activeNode = null; // Track currently active node
52
- this.loadingNodes = new Set(); // Track nodes that are loading
53
- this.bulkLoadMode = false; // Track bulk loading preference
54
- this.expandedPaths = new Set(); // Track which paths are expanded
55
- }
56
-
57
- /**
58
- * Initialize the code tree visualization
59
- */
60
- initialize() {
61
- if (this.initialized) {
62
- return;
63
- }
64
-
65
- this.container = document.getElementById('code-tree-container');
66
- if (!this.container) {
67
- console.error('Code tree container not found');
68
- return;
69
- }
70
-
71
- // Check if tab is visible
72
- const tabPanel = document.getElementById('code-tab');
73
- if (!tabPanel) {
74
- console.error('Code tab panel not found');
75
- return;
76
- }
77
-
78
- // Check if working directory is set
79
- const workingDir = this.getWorkingDirectory();
80
- if (!workingDir || workingDir === 'Loading...' || workingDir === 'Not selected') {
81
- this.showNoWorkingDirectoryMessage();
82
- this.initialized = true;
83
- return;
84
- }
85
-
86
- // Initialize always
87
- this.setupControls();
88
- this.initializeTreeData();
89
- this.subscribeToEvents();
90
-
91
- // Set initial status message
92
- const breadcrumbContent = document.getElementById('breadcrumb-content');
93
- if (breadcrumbContent && !this.analyzing) {
94
- this.updateActivityTicker('Loading project structure...', 'info');
95
- }
96
-
97
- // Only create visualization if tab is visible
98
- if (tabPanel.classList.contains('active')) {
99
- this.createVisualization();
100
- if (this.root && this.svg) {
101
- this.update(this.root);
102
- }
103
- // Auto-discover root level when tab is active
104
- this.autoDiscoverRootLevel();
105
- }
106
-
107
- this.initialized = true;
108
- }
109
-
110
- /**
111
- * Render visualization when tab becomes visible
112
- */
113
- renderWhenVisible() {
114
- // Check if working directory is set
115
- const workingDir = this.getWorkingDirectory();
116
- if (!workingDir || workingDir === 'Loading...' || workingDir === 'Not selected') {
117
- this.showNoWorkingDirectoryMessage();
118
- return;
119
- }
120
-
121
- // If no directory message is shown, remove it
122
- this.removeNoWorkingDirectoryMessage();
123
-
124
- if (!this.initialized) {
125
- this.initialize();
126
- return;
127
- }
128
-
129
- if (!this.svg) {
130
- this.createVisualization();
131
- if (this.svg && this.treeGroup) {
132
- this.update(this.root);
133
- }
134
- } else {
135
- // Force update with current data
136
- if (this.root && this.svg) {
137
- this.update(this.root);
138
- }
139
- }
140
-
141
- // Auto-discover root level if not done yet
142
- if (!this.autoDiscovered) {
143
- this.autoDiscoverRootLevel();
144
- }
145
- }
146
-
147
- /**
148
- * Set up control event handlers
149
- */
150
- setupControls() {
151
- // Remove analyze and cancel button handlers since they're no longer in the UI
152
-
153
- const languageFilter = document.getElementById('language-filter');
154
- if (languageFilter) {
155
- languageFilter.addEventListener('change', (e) => {
156
- this.languageFilter = e.target.value;
157
- this.filterTree();
158
- });
159
- }
160
-
161
- const searchBox = document.getElementById('code-search');
162
- if (searchBox) {
163
- searchBox.addEventListener('input', (e) => {
164
- this.searchTerm = e.target.value.toLowerCase();
165
- this.filterTree();
166
- });
167
- }
168
-
169
- const expandBtn = document.getElementById('code-expand-all');
170
- if (expandBtn) {
171
- expandBtn.addEventListener('click', () => this.expandAll());
172
- }
173
-
174
- const collapseBtn = document.getElementById('code-collapse-all');
175
- if (collapseBtn) {
176
- collapseBtn.addEventListener('click', () => this.collapseAll());
177
- }
178
-
179
- const resetZoomBtn = document.getElementById('code-reset-zoom');
180
- if (resetZoomBtn) {
181
- resetZoomBtn.addEventListener('click', () => this.resetZoom());
182
- }
183
-
184
- const toggleLegendBtn = document.getElementById('code-toggle-legend');
185
- if (toggleLegendBtn) {
186
- toggleLegendBtn.addEventListener('click', () => this.toggleLegend());
187
- }
188
-
189
- // Listen for working directory changes
190
- document.addEventListener('workingDirectoryChanged', (e) => {
191
- this.onWorkingDirectoryChanged(e.detail.directory);
192
- });
193
- }
194
-
195
- /**
196
- * Handle working directory change
197
- */
198
- onWorkingDirectoryChanged(newDirectory) {
199
- if (!newDirectory || newDirectory === 'Loading...' || newDirectory === 'Not selected') {
200
- // Show no directory message
201
- this.showNoWorkingDirectoryMessage();
202
- // Reset tree state
203
- this.autoDiscovered = false;
204
- this.analyzing = false;
205
- this.nodes.clear();
206
- this.stats = {
207
- files: 0,
208
- classes: 0,
209
- functions: 0,
210
- methods: 0,
211
- lines: 0
212
- };
213
- this.updateStats();
214
- return;
215
- }
216
-
217
- // Remove any no directory message
218
- this.removeNoWorkingDirectoryMessage();
219
-
220
- // Reset discovery state for new directory
221
- this.autoDiscovered = false;
222
- this.analyzing = false;
223
-
224
- // Clear existing data
225
- this.nodes.clear();
226
- this.stats = {
227
- files: 0,
228
- classes: 0,
229
- functions: 0,
230
- methods: 0,
231
- lines: 0
232
- };
233
-
234
- // Re-initialize with new directory
235
- this.initializeTreeData();
236
- if (this.svg) {
237
- this.update(this.root);
238
- }
239
-
240
- // Check if Code tab is currently active
241
- const tabPanel = document.getElementById('code-tab');
242
- if (tabPanel && tabPanel.classList.contains('active')) {
243
- // Auto-discover in the new directory
244
- this.autoDiscoverRootLevel();
245
- }
246
-
247
- this.updateStats();
248
- }
249
-
250
- /**
251
- * Show loading spinner
252
- */
253
- showLoading() {
254
- let loadingDiv = document.getElementById('code-tree-loading');
255
- if (!loadingDiv) {
256
- // Create loading element if it doesn't exist
257
- const container = document.getElementById('code-tree-container');
258
- if (container) {
259
- loadingDiv = document.createElement('div');
260
- loadingDiv.id = 'code-tree-loading';
261
- loadingDiv.innerHTML = `
262
- <div class="code-tree-spinner"></div>
263
- <div class="code-tree-loading-text">Analyzing code structure...</div>
264
- `;
265
- container.appendChild(loadingDiv);
266
- }
267
- }
268
- if (loadingDiv) {
269
- loadingDiv.classList.remove('hidden');
270
- }
271
- }
272
-
273
- /**
274
- * Hide loading spinner
275
- */
276
- hideLoading() {
277
- const loadingDiv = document.getElementById('code-tree-loading');
278
- if (loadingDiv) {
279
- loadingDiv.classList.add('hidden');
280
- }
281
- }
282
-
283
- /**
284
- * Create the D3.js visualization
285
- */
286
- createVisualization() {
287
- if (typeof d3 === 'undefined') {
288
- console.error('D3.js is not loaded');
289
- return;
290
- }
291
-
292
- const container = d3.select('#code-tree-container');
293
- container.selectAll('*').remove();
294
-
295
- // Add tree controls toolbar
296
- this.addTreeControls();
297
-
298
- // Add breadcrumb navigation
299
- this.addBreadcrumb();
300
-
301
- if (!container || !container.node()) {
302
- console.error('Code tree container not found');
303
- return;
304
- }
305
-
306
- // Calculate dimensions
307
- const containerNode = container.node();
308
- const containerWidth = containerNode.clientWidth || 960;
309
- const containerHeight = containerNode.clientHeight || 600;
310
-
311
- this.width = containerWidth - this.margin.left - this.margin.right;
312
- this.height = containerHeight - this.margin.top - this.margin.bottom;
313
- this.radius = Math.min(this.width, this.height) / 2;
314
-
315
- // Create SVG
316
- this.svg = container.append('svg')
317
- .attr('width', containerWidth)
318
- .attr('height', containerHeight);
319
-
320
- // Create tree group with appropriate centering
321
- const centerX = containerWidth / 2;
322
- const centerY = containerHeight / 2;
323
-
324
- // Different initial positioning for different layouts
325
- if (this.isRadialLayout) {
326
- // Radial: center in the middle of the canvas
327
- this.treeGroup = this.svg.append('g')
328
- .attr('transform', `translate(${centerX},${centerY})`);
329
- } else {
330
- // Linear: start from left with some margin
331
- this.treeGroup = this.svg.append('g')
332
- .attr('transform', `translate(${this.margin.left + 100},${centerY})`);
333
- }
334
-
335
- // Create tree layout with improved spacing
336
- if (this.isRadialLayout) {
337
- // Use d3.cluster for better radial distribution
338
- this.treeLayout = d3.cluster()
339
- .size([2 * Math.PI, this.radius - 100])
340
- .separation((a, b) => {
341
- // Enhanced separation for radial layout
342
- if (a.parent == b.parent) {
343
- // Base separation on tree depth for better spacing
344
- const depthFactor = Math.max(1, 4 - a.depth);
345
- // Increase spacing for nodes with many siblings
346
- const siblingCount = a.parent ? (a.parent.children?.length || 1) : 1;
347
- const siblingFactor = siblingCount > 5 ? 2 : (siblingCount > 3 ? 1.5 : 1);
348
- // More spacing at outer levels where circumference is larger
349
- const radiusFactor = 1 + (a.depth * 0.2);
350
- return (depthFactor * siblingFactor) / (a.depth || 1) * radiusFactor;
351
- } else {
352
- // Different parents - ensure enough space
353
- return 4 / (a.depth || 1);
354
- }
355
- });
356
- } else {
357
- // Linear layout with dynamic sizing based on node count
358
- // Use nodeSize for consistent spacing regardless of tree size
359
- this.treeLayout = d3.tree()
360
- .nodeSize([30, 200]) // Fixed spacing: 30px vertical, 200px horizontal
361
- .separation((a, b) => {
362
- // Consistent separation for linear layout
363
- if (a.parent == b.parent) {
364
- // Same parent - standard spacing
365
- return 1;
366
- } else {
367
- // Different parents - slightly more space
368
- return 1.5;
369
- }
370
- });
371
- }
372
-
373
- // DISABLED: All zoom behavior has been completely disabled to prevent tree movement
374
- // The tree should remain completely stationary - no zooming, panning, or centering allowed
375
- this.zoom = null; // Completely disable zoom behavior
376
-
377
- // Do NOT apply zoom behavior to SVG - this prevents all zoom/pan interactions
378
- // this.svg.call(this.zoom); // DISABLED
379
-
380
- console.log('[CodeTree] All zoom and pan behavior disabled - tree is now completely stationary');
381
-
382
- // Add controls overlay
383
- this.addVisualizationControls();
384
-
385
- // Create tooltip
386
- this.tooltip = d3.select('body').append('div')
387
- .attr('class', 'code-tree-tooltip')
388
- .style('opacity', 0)
389
- .style('position', 'absolute')
390
- .style('background', 'rgba(0, 0, 0, 0.8)')
391
- .style('color', 'white')
392
- .style('padding', '8px')
393
- .style('border-radius', '4px')
394
- .style('font-size', '12px')
395
- .style('pointer-events', 'none');
396
- }
397
-
398
- /**
399
- * Clear all D3 visualization elements
400
- */
401
- clearD3Visualization() {
402
- if (this.treeGroup) {
403
- // Remove all existing nodes and links
404
- this.treeGroup.selectAll('g.node').remove();
405
- this.treeGroup.selectAll('path.link').remove();
406
- }
407
- // Reset node ID counter for proper tracking
408
- this.nodeId = 0;
409
- }
410
-
411
- /**
412
- * Initialize tree data structure
413
- */
414
- initializeTreeData() {
415
- const workingDir = this.getWorkingDirectory();
416
- const dirName = workingDir ? workingDir.split('/').pop() || 'Project Root' : 'Project Root';
417
-
418
- // Use '.' as the root path for consistency with relative path handling
419
- // The actual working directory is retrieved via getWorkingDirectory() when needed
420
- this.treeData = {
421
- name: dirName,
422
- path: '.', // Always use '.' for root to simplify path handling
423
- type: 'root',
424
- children: [],
425
- loaded: false,
426
- expanded: true // Start expanded
427
- };
428
-
429
- if (typeof d3 !== 'undefined') {
430
- this.root = d3.hierarchy(this.treeData);
431
- this.root.x0 = this.height / 2;
432
- this.root.y0 = 0;
433
- }
434
- }
435
-
436
- /**
437
- * Subscribe to code analysis events
438
- */
439
- subscribeToEvents() {
440
- if (!this.socket) {
441
- if (window.socket) {
442
- this.socket = window.socket;
443
- this.setupEventHandlers();
444
- } else if (window.dashboard?.socketClient?.socket) {
445
- this.socket = window.dashboard.socketClient.socket;
446
- this.setupEventHandlers();
447
- } else if (window.socketClient?.socket) {
448
- this.socket = window.socketClient.socket;
449
- this.setupEventHandlers();
450
- }
451
- }
452
- }
453
-
454
- /**
455
- * Automatically discover root-level objects when tab opens
456
- */
457
- autoDiscoverRootLevel() {
458
- if (this.autoDiscovered || this.analyzing) {
459
- return;
460
- }
461
-
462
- // Update activity ticker
463
- this.updateActivityTicker('🔍 Discovering project structure...', 'info');
464
-
465
- // Get working directory
466
- const workingDir = this.getWorkingDirectory();
467
- if (!workingDir || workingDir === 'Loading...' || workingDir === 'Not selected') {
468
- console.warn('Cannot auto-discover: no working directory set');
469
- this.showNoWorkingDirectoryMessage();
470
- return;
471
- }
472
-
473
- // Ensure we have an absolute path
474
- if (!workingDir.startsWith('/') && !workingDir.match(/^[A-Z]:\\/)) {
475
- console.error('Working directory is not absolute:', workingDir);
476
- this.showNotification('Invalid working directory path', 'error');
477
- return;
478
- }
479
-
480
-
481
- this.autoDiscovered = true;
482
- this.analyzing = true;
483
-
484
- // Clear any existing nodes
485
- this.nodes.clear();
486
- this.stats = {
487
- files: 0,
488
- classes: 0,
489
- functions: 0,
490
- methods: 0,
491
- lines: 0
492
- };
493
-
494
- // Subscribe to events if not already done
495
- if (this.socket && !this.socket.hasListeners('code:node:found')) {
496
- this.setupEventHandlers();
497
- }
498
-
499
- // Update tree data with working directory as the root
500
- const dirName = workingDir.split('/').pop() || 'Project Root';
501
- this.treeData = {
502
- name: dirName,
503
- path: '.', // Use '.' for root to maintain consistency with relative path handling
504
- type: 'root',
505
- children: [],
506
- loaded: false,
507
- expanded: true // Start expanded to show discovered items
508
- };
509
-
510
- if (typeof d3 !== 'undefined') {
511
- this.root = d3.hierarchy(this.treeData);
512
- this.root.x0 = this.height / 2;
513
- this.root.y0 = 0;
514
- }
515
-
516
- // Update UI
517
- this.showLoading();
518
- this.updateBreadcrumb(`Discovering structure in ${dirName}...`, 'info');
519
-
520
- // Get selected languages from checkboxes
521
- const selectedLanguages = [];
522
- document.querySelectorAll('.language-checkbox:checked').forEach(cb => {
523
- selectedLanguages.push(cb.value);
524
- });
525
-
526
- // Get ignore patterns
527
- const ignorePatterns = document.getElementById('ignore-patterns')?.value || '';
528
-
529
- // Enhanced debug logging
530
-
531
- // Request top-level discovery with working directory
532
- const requestPayload = {
533
- path: workingDir, // Use working directory instead of '.'
534
- depth: 'top_level',
535
- languages: selectedLanguages,
536
- ignore_patterns: ignorePatterns,
537
- request_id: `discover_${Date.now()}` // Add request ID for tracking
538
- };
539
-
540
- // Sending top-level discovery request
541
-
542
- if (this.socket) {
543
- this.socket.emit('code:discover:top_level', requestPayload);
544
- }
545
-
546
- // Update stats display
547
- this.updateStats();
548
- }
549
-
550
- /**
551
- * Legacy analyzeCode method - redirects to auto-discovery
552
- */
553
- analyzeCode() {
554
- if (this.analyzing) {
555
- return;
556
- }
557
-
558
- // Redirect to auto-discovery
559
- this.autoDiscoverRootLevel();
560
- }
561
-
562
- /**
563
- * Cancel ongoing analysis - removed since we no longer have a cancel button
564
- */
565
- cancelAnalysis() {
566
- this.analyzing = false;
567
- this.hideLoading();
568
-
569
- if (this.socket) {
570
- this.socket.emit('code:analysis:cancel');
571
- }
572
- }
573
-
574
- /**
575
- * Add tree control toolbar with expand/collapse and other controls
576
- */
577
- addTreeControls() {
578
- const container = d3.select('#code-tree-container');
579
-
580
- // Remove any existing controls
581
- container.select('.tree-controls-toolbar').remove();
582
-
583
- const toolbar = container.append('div')
584
- .attr('class', 'tree-controls-toolbar');
585
-
586
- // Expand All button
587
- toolbar.append('button')
588
- .attr('class', 'tree-control-btn')
589
- .attr('title', 'Expand all loaded directories')
590
- .text('⊞')
591
- .on('click', () => this.expandAll());
592
-
593
- // Collapse All button
594
- toolbar.append('button')
595
- .attr('class', 'tree-control-btn')
596
- .attr('title', 'Collapse all directories')
597
- .text('⊟')
598
- .on('click', () => this.collapseAll());
599
-
600
- // Bulk Load Toggle
601
- toolbar.append('button')
602
- .attr('class', 'tree-control-btn')
603
- .attr('id', 'bulk-load-toggle')
604
- .attr('title', 'Toggle bulk loading (load 2 levels at once)')
605
- .text('↕')
606
- .on('click', () => this.toggleBulkLoad());
607
-
608
- // Layout Toggle
609
- toolbar.append('button')
610
- .attr('class', 'tree-control-btn')
611
- .attr('title', 'Toggle between radial and linear layouts')
612
- .text('◎')
613
- .on('click', () => this.toggleLayout());
614
-
615
- // Path Search
616
- const searchInput = toolbar.append('input')
617
- .attr('class', 'tree-control-btn')
618
- .attr('type', 'text')
619
- .attr('placeholder', 'Search...')
620
- .attr('title', 'Search for files and directories')
621
- .style('width', '120px')
622
- .style('text-align', 'left')
623
- .on('input', (event) => this.searchTree(event.target.value))
624
- .on('keydown', (event) => {
625
- if (event.key === 'Escape') {
626
- event.target.value = '';
627
- this.searchTree('');
628
- }
629
- });
630
- }
631
-
632
- /**
633
- * Add breadcrumb navigation
634
- */
635
- addBreadcrumb() {
636
- const container = d3.select('#code-tree-container');
637
-
638
- // Remove any existing breadcrumb
639
- container.select('.tree-breadcrumb').remove();
640
-
641
- const breadcrumb = container.append('div')
642
- .attr('class', 'tree-breadcrumb');
643
-
644
- const pathDiv = breadcrumb.append('div')
645
- .attr('class', 'breadcrumb-path')
646
- .attr('id', 'tree-breadcrumb-path');
647
-
648
- // Initialize with working directory
649
- this.updateBreadcrumbPath('/');
650
- }
651
-
652
- /**
653
- * Update breadcrumb path based on current navigation
654
- */
655
- updateBreadcrumbPath(currentPath) {
656
- const pathDiv = d3.select('#tree-breadcrumb-path');
657
- pathDiv.selectAll('*').remove();
658
-
659
- const workingDir = this.getWorkingDirectory();
660
- if (!workingDir || workingDir === 'Loading...' || workingDir === 'Not selected') {
661
- pathDiv.text('No project selected');
662
- return;
663
- }
664
-
665
- // Build path segments
666
- const segments = currentPath === '/' ?
667
- [workingDir.split('/').pop() || 'Root'] :
668
- currentPath.split('/').filter(s => s.length > 0);
669
-
670
- segments.forEach((segment, index) => {
671
- if (index > 0) {
672
- pathDiv.append('span')
673
- .attr('class', 'breadcrumb-separator')
674
- .text('/');
675
- }
676
-
677
- pathDiv.append('span')
678
- .attr('class', index === segments.length - 1 ? 'breadcrumb-segment current' : 'breadcrumb-segment')
679
- .text(segment)
680
- .on('click', () => {
681
- if (index < segments.length - 1) {
682
- // Navigate to parent path
683
- const parentPath = segments.slice(0, index + 1).join('/');
684
- this.navigateToPath(parentPath);
685
- }
686
- });
687
- });
688
- }
689
-
690
- /**
691
- * Expand all currently loaded directories
692
- */
693
- expandAll() {
694
- if (!this.root) return;
695
-
696
- const expandNode = (node) => {
697
- if (node.data.type === 'directory' && node.data.loaded === true) {
698
- if (node._children) {
699
- node.children = node._children;
700
- node._children = null;
701
- node.data.expanded = true;
702
- }
703
- }
704
- if (node.children) {
705
- node.children.forEach(expandNode);
706
- }
707
- };
708
-
709
- expandNode(this.root);
710
- this.update(this.root);
711
- this.showNotification('Expanded all loaded directories', 'success');
712
- }
713
-
714
- /**
715
- * Collapse all directories to root level
716
- */
717
- collapseAll() {
718
- if (!this.root) return;
719
-
720
- const collapseNode = (node) => {
721
- if (node.data.type === 'directory' && node.children) {
722
- node._children = node.children;
723
- node.children = null;
724
- node.data.expanded = false;
725
- }
726
- if (node._children) {
727
- node._children.forEach(collapseNode);
728
- }
729
- };
730
-
731
- collapseNode(this.root);
732
- this.update(this.root);
733
- this.showNotification('Collapsed all directories', 'info');
734
- }
735
-
736
- /**
737
- * Toggle bulk loading mode
738
- */
739
- toggleBulkLoad() {
740
- this.bulkLoadMode = !this.bulkLoadMode;
741
- const button = d3.select('#bulk-load-toggle');
742
-
743
- if (this.bulkLoadMode) {
744
- button.classed('active', true);
745
- this.showNotification('Bulk load enabled - will load 2 levels deep', 'info');
746
- } else {
747
- button.classed('active', false);
748
- this.showNotification('Bulk load disabled - load 1 level at a time', 'info');
749
- }
750
- }
751
-
752
- /**
753
- * Navigate to a specific path in the tree
754
- */
755
- navigateToPath(path) {
756
- // Implementation for navigating to a specific path
757
- // This would expand the tree to show the specified path
758
- this.updateBreadcrumbPath(path);
759
- this.showNotification(`Navigating to: ${path}`, 'info');
760
- }
761
-
762
- /**
763
- * Search the tree for matching files/directories
764
- */
765
- searchTree(query) {
766
- if (!this.root || !this.treeGroup) return;
767
-
768
- const searchTerm = query.toLowerCase().trim();
769
-
770
- // Clear previous search highlights
771
- this.treeGroup.selectAll('.code-node')
772
- .classed('search-match', false);
773
-
774
- if (!searchTerm) {
775
- return; // No search term, just clear highlights
776
- }
777
-
778
- // Find matching nodes
779
- const matchingNodes = [];
780
- const searchNode = (node) => {
781
- const name = (node.data.name || '').toLowerCase();
782
- const path = (node.data.path || '').toLowerCase();
783
-
784
- if (name.includes(searchTerm) || path.includes(searchTerm)) {
785
- matchingNodes.push(node);
786
- }
787
-
788
- if (node.children) {
789
- node.children.forEach(searchNode);
790
- }
791
- if (node._children) {
792
- node._children.forEach(searchNode);
793
- }
794
- };
795
-
796
- searchNode(this.root);
797
-
798
- // Highlight matching nodes
799
- if (matchingNodes.length > 0) {
800
- // Get all current nodes in the tree
801
- const allNodes = this.treeGroup.selectAll('.code-node').data();
802
-
803
- matchingNodes.forEach(matchNode => {
804
- // Find the corresponding DOM node
805
- const domNode = this.treeGroup.selectAll('.code-node')
806
- .filter(d => d.data.path === matchNode.data.path);
807
- domNode.classed('search-match', true);
808
-
809
- // Expand parent path to show the match
810
- this.expandPathToNode(matchNode);
811
- });
812
-
813
- this.showNotification(`Found ${matchingNodes.length} matches`, 'success');
814
-
815
- // Auto-center on first match if in radial layout - REMOVED
816
- // Centering functionality has been disabled to prevent unwanted repositioning
817
- // if (matchingNodes.length > 0 && this.isRadialLayout) {
818
- // this.centerOnNode ? this.centerOnNode(matchingNodes[0]) : this.centerOnNodeRadial(matchingNodes[0]);
819
- // }
820
- } else {
821
- this.showNotification('No matches found', 'info');
822
- }
823
- }
824
-
825
- /**
826
- * Expand the tree path to show a specific node
827
- */
828
- expandPathToNode(targetNode) {
829
- const pathToExpand = [];
830
- let current = targetNode.parent;
831
-
832
- // Build path from node to root
833
- while (current && current !== this.root) {
834
- pathToExpand.unshift(current);
835
- current = current.parent;
836
- }
837
-
838
- // Expand each node in the path
839
- pathToExpand.forEach(node => {
840
- if (node.data.type === 'directory' && node._children) {
841
- node.children = node._children;
842
- node._children = null;
843
- node.data.expanded = true;
844
- }
845
- });
846
-
847
- // Update the visualization if we expanded anything
848
- if (pathToExpand.length > 0) {
849
- this.update(this.root);
850
- }
851
- }
852
-
853
- /**
854
- * Create the events display area
855
- */
856
- createEventsDisplay() {
857
- let eventsContainer = document.getElementById('analysis-events');
858
- if (!eventsContainer) {
859
- const treeContainer = document.getElementById('code-tree-container');
860
- if (treeContainer) {
861
- eventsContainer = document.createElement('div');
862
- eventsContainer.id = 'analysis-events';
863
- eventsContainer.className = 'analysis-events';
864
- eventsContainer.style.display = 'none';
865
- treeContainer.appendChild(eventsContainer);
866
- }
867
- }
868
- }
869
-
870
- /**
871
- * Clear the events display
872
- */
873
- clearEventsDisplay() {
874
- const eventsContainer = document.getElementById('analysis-events');
875
- if (eventsContainer) {
876
- eventsContainer.innerHTML = '';
877
- eventsContainer.style.display = 'block';
878
- }
879
- }
880
-
881
- /**
882
- * Add an event to the display
883
- */
884
- addEventToDisplay(message, type = 'info') {
885
- const eventsContainer = document.getElementById('analysis-events');
886
- if (eventsContainer) {
887
- const eventEl = document.createElement('div');
888
- eventEl.className = 'analysis-event';
889
- eventEl.style.borderLeftColor = type === 'warning' ? '#f59e0b' :
890
- type === 'error' ? '#ef4444' : '#3b82f6';
891
-
892
- const timestamp = new Date().toLocaleTimeString();
893
- eventEl.innerHTML = `<span style="color: #718096;">[${timestamp}]</span> ${message}`;
894
-
895
- eventsContainer.appendChild(eventEl);
896
- // Auto-scroll to bottom
897
- eventsContainer.scrollTop = eventsContainer.scrollHeight;
898
- }
899
- }
900
-
901
- /**
902
- * Setup Socket.IO event handlers
903
- */
904
- setupEventHandlers() {
905
- if (!this.socket) return;
906
-
907
- // Analysis lifecycle events
908
- this.socket.on('code:analysis:accepted', (data) => this.onAnalysisAccepted(data));
909
- this.socket.on('code:analysis:queued', (data) => this.onAnalysisQueued(data));
910
- this.socket.on('code:analysis:start', (data) => this.onAnalysisStart(data));
911
- this.socket.on('code:analysis:complete', (data) => this.onAnalysisComplete(data));
912
- this.socket.on('code:analysis:cancelled', (data) => this.onAnalysisCancelled(data));
913
- this.socket.on('code:analysis:error', (data) => this.onAnalysisError(data));
914
-
915
- // Node discovery events
916
- this.socket.on('code:top_level:discovered', (data) => this.onTopLevelDiscovered(data));
917
- this.socket.on('code:directory:discovered', (data) => this.onDirectoryDiscovered(data));
918
- this.socket.on('code:file:discovered', (data) => this.onFileDiscovered(data));
919
- this.socket.on('code:file:analyzed', (data) => this.onFileAnalyzed(data));
920
- this.socket.on('code:node:found', (data) => this.onNodeFound(data));
921
-
922
- // Progress updates
923
- this.socket.on('code:analysis:progress', (data) => this.onProgressUpdate(data));
924
-
925
- // Lazy loading responses
926
- this.socket.on('code:directory:contents', (data) => {
927
- // Update the requested directory with its contents
928
- if (data.path) {
929
- // Convert absolute path back to relative path to match tree nodes
930
- let searchPath = data.path;
931
- const workingDir = this.getWorkingDirectory();
932
- if (workingDir && searchPath.startsWith(workingDir)) {
933
- // Remove working directory prefix to get relative path
934
- searchPath = searchPath.substring(workingDir.length).replace(/^\//, '');
935
- // If empty after removing prefix, it's the root
936
- if (!searchPath) {
937
- searchPath = '.';
938
- }
939
- }
940
-
941
- const node = this.findNodeByPath(searchPath);
942
- if (node && data.children) {
943
- // Find D3 node and remove loading pulse (use searchPath, not data.path)
944
- const d3Node = this.findD3NodeByPath(searchPath);
945
- if (d3Node && this.loadingNodes.has(searchPath)) {
946
- this.removeLoadingPulse(d3Node);
947
- this.loadingNodes.delete(searchPath); // Remove from loading set
948
- console.log('🎯 [SUBDIRECTORY LOADING] Successfully completed and removed from loading set:', searchPath);
949
- }
950
- node.children = data.children.map(child => {
951
- // Construct full path for child by combining parent path with child name
952
- // The backend now returns just the item name, not the full path
953
- let childPath;
954
- if (searchPath === '.' || searchPath === '') {
955
- // Root level - child path is just the name
956
- childPath = child.name || child.path;
957
- } else {
958
- // Subdirectory - combine parent path with child name
959
- // Use child.name (backend returns just the name) or fallback to child.path
960
- const childName = child.name || child.path;
961
- childPath = `${searchPath}/${childName}`;
962
- }
963
-
964
- return {
965
- ...child,
966
- path: childPath, // Override with constructed path
967
- loaded: child.type === 'directory' ? false : undefined,
968
- analyzed: child.type === 'file' ? false : undefined,
969
- expanded: false,
970
- children: []
971
- };
972
- });
973
- node.loaded = true;
974
- node.expanded = true; // Mark as expanded to show children
975
-
976
- // Update D3 hierarchy and make sure the node is expanded
977
- if (this.root && this.svg) {
978
- // Store old root to preserve expansion state
979
- const oldRoot = this.root;
980
-
981
- // Recreate hierarchy with updated data
982
- this.root = d3.hierarchy(this.treeData);
983
- this.root.x0 = this.height / 2;
984
- this.root.y0 = 0;
985
-
986
- // Preserve expansion state from old tree
987
- this.preserveExpansionState(oldRoot, this.root);
988
-
989
- // Find the D3 node again after hierarchy recreation
990
- const updatedD3Node = this.findD3NodeByPath(searchPath);
991
- if (updatedD3Node) {
992
- // Ensure children are visible (not collapsed)
993
- updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
994
- updatedD3Node._children = null;
995
- updatedD3Node.data.expanded = true;
996
- }
997
-
998
- this.update(this.root);
999
- }
1000
-
1001
- // Update stats based on discovered contents
1002
- if (data.stats) {
1003
- this.stats.files += data.stats.files || 0;
1004
- this.stats.directories += data.stats.directories || 0;
1005
- this.updateStats();
1006
- }
1007
-
1008
- this.updateBreadcrumb(`Loaded ${data.path}`, 'success');
1009
- this.hideLoading();
1010
- }
1011
- }
1012
- });
1013
-
1014
- // Top level discovery response
1015
- this.socket.on('code:top_level:discovered', (data) => {
1016
- if (data.items && Array.isArray(data.items)) {
1017
-
1018
- // Add discovered items to the root node
1019
- this.treeData.children = data.items.map(item => ({
1020
- name: item.name,
1021
- path: item.path,
1022
- type: item.type,
1023
- language: item.type === 'file' ? this.detectLanguage(item.path) : undefined,
1024
- size: item.size,
1025
- lines: item.lines,
1026
- loaded: item.type === 'directory' ? false : undefined,
1027
- analyzed: item.type === 'file' ? false : undefined,
1028
- expanded: false,
1029
- children: []
1030
- }));
1031
-
1032
- this.treeData.loaded = true;
1033
-
1034
- // Update stats
1035
- if (data.stats) {
1036
- this.stats = { ...this.stats, ...data.stats };
1037
- this.updateStats();
1038
- }
1039
-
1040
- // Update D3 hierarchy
1041
- if (typeof d3 !== 'undefined') {
1042
- // Clear any existing nodes before creating new ones
1043
- this.clearD3Visualization();
1044
-
1045
- // Create new hierarchy
1046
- this.root = d3.hierarchy(this.treeData);
1047
- this.root.x0 = this.height / 2;
1048
- this.root.y0 = 0;
1049
-
1050
- if (this.svg) {
1051
- this.update(this.root);
1052
- }
1053
- }
1054
-
1055
- this.analyzing = false;
1056
- this.hideLoading();
1057
- this.updateBreadcrumb(`Discovered ${data.items.length} root items`, 'success');
1058
- this.showNotification(`Found ${data.items.length} items in project root`, 'success');
1059
- }
1060
- });
1061
- }
1062
-
1063
- /**
1064
- * Handle analysis start event
1065
- */
1066
- onAnalysisStart(data) {
1067
- this.analyzing = true;
1068
- const message = data.message || 'Starting code analysis...';
1069
-
1070
- // Update activity ticker
1071
- this.updateActivityTicker('🚀 Starting analysis...', 'info');
1072
-
1073
- this.updateBreadcrumb(message, 'info');
1074
- this.addEventToDisplay(`🚀 ${message}`, 'info');
1075
-
1076
- // Initialize or clear the tree
1077
- if (!this.treeData || this.treeData.children.length === 0) {
1078
- this.initializeTreeData();
1079
- }
1080
-
1081
- // Reset stats
1082
- this.stats = {
1083
- files: 0,
1084
- classes: 0,
1085
- functions: 0,
1086
- methods: 0,
1087
- lines: 0
1088
- };
1089
- this.updateStats();
1090
- }
1091
-
1092
- /**
1093
- * Handle top-level discovery event (initial root directory scan)
1094
- */
1095
- onTopLevelDiscovered(data) {
1096
- // Received top-level discovery response
1097
-
1098
- // Update activity ticker
1099
- this.updateActivityTicker(`📁 Discovered ${(data.items || []).length} top-level items`, 'success');
1100
-
1101
- // Add to events display
1102
- this.addEventToDisplay(`📁 Found ${(data.items || []).length} top-level items in project root`, 'info');
1103
-
1104
- // The root node (with path '.') should receive the children
1105
- const rootNode = this.findNodeByPath('.');
1106
-
1107
- console.log('🔎 Looking for root node with path ".", found:', rootNode ? {
1108
- name: rootNode.name,
1109
- path: rootNode.path,
1110
- currentChildren: rootNode.children ? rootNode.children.length : 0
1111
- } : 'NOT FOUND');
1112
-
1113
- if (rootNode && data.items) {
1114
- console.log('🌳 Populating root node with children');
1115
-
1116
- // Update the root node with discovered children
1117
- rootNode.children = data.items.map(child => {
1118
- // Items at root level get their name as the path
1119
- const childPath = child.name;
1120
-
1121
- console.log(` Adding child: ${child.name} with path: ${childPath}`);
1122
-
1123
- return {
1124
- name: child.name,
1125
- path: childPath, // Just the name for top-level items
1126
- type: child.type,
1127
- loaded: child.type === 'directory' ? false : undefined,
1128
- analyzed: child.type === 'file' ? false : undefined,
1129
- expanded: false,
1130
- children: child.type === 'directory' ? [] : undefined,
1131
- size: child.size,
1132
- has_code: child.has_code
1133
- };
1134
- });
1135
-
1136
- rootNode.loaded = true;
1137
- rootNode.expanded = true;
1138
-
1139
- // Update D3 hierarchy and render
1140
- if (this.root && this.svg) {
1141
- // Recreate hierarchy with new data
1142
- this.root = d3.hierarchy(this.treeData);
1143
- this.root.x0 = this.height / 2;
1144
- this.root.y0 = 0;
1145
-
1146
- // Update the tree visualization
1147
- this.update(this.root);
1148
- }
1149
-
1150
- // Hide loading and show success
1151
- this.hideLoading();
1152
- this.updateBreadcrumb(`Discovered ${data.items.length} items`, 'success');
1153
- this.showNotification(`Found ${data.items.length} top-level items`, 'success');
1154
- } else {
1155
- console.error('❌ Could not find root node to populate');
1156
- this.showNotification('Failed to populate root directory', 'error');
1157
- }
1158
-
1159
- // Mark analysis as complete
1160
- this.analyzing = false;
1161
- }
1162
-
1163
- /**
1164
- * Handle directory discovered event
1165
- */
1166
- onDirectoryDiscovered(data) {
1167
- // Update activity ticker first
1168
- this.updateActivityTicker(`📁 Discovered: ${data.name || 'directory'}`);
1169
-
1170
- // Add to events display
1171
- this.addEventToDisplay(`📁 Found ${(data.children || []).length} items in: ${data.name || data.path}`, 'info');
1172
-
1173
- console.log('✅ [SUBDIRECTORY LOADING] Received directory discovery response:', {
1174
- path: data.path,
1175
- name: data.name,
1176
- childrenCount: (data.children || []).length,
1177
- children: (data.children || []).map(c => ({ name: c.name, type: c.type })),
1178
- workingDir: this.getWorkingDirectory(),
1179
- fullEventData: data
1180
- });
1181
-
1182
- // Convert absolute path back to relative path to match tree nodes
1183
- let searchPath = data.path;
1184
- const workingDir = this.getWorkingDirectory();
1185
- if (workingDir && searchPath.startsWith(workingDir)) {
1186
- // Remove working directory prefix to get relative path
1187
- searchPath = searchPath.substring(workingDir.length).replace(/^\//, '');
1188
- // If empty after removing prefix, it's the root
1189
- if (!searchPath) {
1190
- searchPath = '.';
1191
- }
1192
- }
1193
-
1194
- console.log('🔎 Searching for node with path:', searchPath);
1195
-
1196
- // Find the node that was clicked to trigger this discovery
1197
- const node = this.findNodeByPath(searchPath);
1198
-
1199
- // Located target node for expansion
1200
-
1201
- if (node && data.children) {
1202
- // Update the node with discovered children
1203
- node.children = data.children.map(child => {
1204
- // Construct full path for child by combining parent path with child name
1205
- // The backend now returns just the item name, not the full path
1206
- let childPath;
1207
- if (searchPath === '.' || searchPath === '') {
1208
- // Root level - child path is just the name
1209
- childPath = child.name || child.path;
1210
- } else {
1211
- // Subdirectory - combine parent path with child name
1212
- // Use child.name (backend returns just the name) or fallback to child.path
1213
- const childName = child.name || child.path;
1214
- childPath = `${searchPath}/${childName}`;
1215
- }
1216
-
1217
- return {
1218
- name: child.name,
1219
- path: childPath, // Use constructed path instead of child.path
1220
- type: child.type,
1221
- loaded: child.type === 'directory' ? false : undefined,
1222
- analyzed: child.type === 'file' ? false : undefined,
1223
- expanded: false,
1224
- children: child.type === 'directory' ? [] : undefined,
1225
- size: child.size,
1226
- has_code: child.has_code
1227
- };
1228
- });
1229
- node.loaded = true;
1230
- node.expanded = true;
1231
-
1232
- // Find D3 node and remove loading pulse (use searchPath, not data.path)
1233
- const d3Node = this.findD3NodeByPath(searchPath);
1234
- if (d3Node) {
1235
- // Remove loading animation
1236
- if (this.loadingNodes.has(searchPath)) {
1237
- this.removeLoadingPulse(d3Node);
1238
- this.loadingNodes.delete(searchPath); // Remove from loading set
1239
- console.log('🎯 [SUBDIRECTORY LOADING] Successfully completed and removed from loading set (hierarchy update):', searchPath);
1240
- }
1241
- }
1242
-
1243
- // Update D3 hierarchy and redraw with expanded node
1244
- if (this.root && this.svg) {
1245
- // Store old root to preserve expansion state
1246
- const oldRoot = this.root;
1247
-
1248
- // Recreate hierarchy with updated data
1249
- this.root = d3.hierarchy(this.treeData);
1250
-
1251
- // Restore positions for smooth animation
1252
- this.root.x0 = this.height / 2;
1253
- this.root.y0 = 0;
1254
-
1255
- // Preserve expansion state from old tree
1256
- this.preserveExpansionState(oldRoot, this.root);
1257
-
1258
- // Find the D3 node again after hierarchy recreation
1259
- const updatedD3Node = this.findD3NodeByPath(searchPath);
1260
- if (updatedD3Node && updatedD3Node.data.children && updatedD3Node.data.children.length > 0) {
1261
- // Ensure the node is expanded to show children
1262
- updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
1263
- updatedD3Node._children = null;
1264
- // Mark data as expanded
1265
- updatedD3Node.data.expanded = true;
1266
- }
1267
-
1268
- this.update(this.root);
1269
- }
1270
-
1271
- // Provide better feedback for empty vs populated directories
1272
- if (node.children.length === 0) {
1273
- this.updateBreadcrumb(`Empty directory: ${node.name}`, 'info');
1274
- this.showNotification(`Directory "${node.name}" is empty`, 'info');
1275
- } else {
1276
- this.updateBreadcrumb(`Loaded ${node.children.length} items from ${node.name}`, 'success');
1277
- this.showNotification(`Loaded ${node.children.length} items from "${node.name}"`, 'success');
1278
- }
1279
- this.updateStats();
1280
- } else if (!node) {
1281
- console.error('❌ [SUBDIRECTORY LOADING] Node not found for path:', {
1282
- searchPath,
1283
- originalPath: data.path,
1284
- workingDir: this.getWorkingDirectory(),
1285
- allTreePaths: this.getAllTreePaths(this.treeData)
1286
- });
1287
- this.showNotification(`Could not find directory "${searchPath}" in tree`, 'error');
1288
- this.logAllPaths(this.treeData);
1289
- } else if (node && !data.children) {
1290
- console.warn('⚠️ [SUBDIRECTORY LOADING] Directory response has no children:', {
1291
- path: data.path,
1292
- searchPath,
1293
- nodeExists: !!node,
1294
- dataKeys: Object.keys(data),
1295
- fullData: data
1296
- });
1297
- // This might be a top-level directory discovery
1298
- const pathParts = data.path ? data.path.split('/').filter(p => p) : [];
1299
- const isTopLevel = pathParts.length === 1;
1300
-
1301
- if (isTopLevel || data.forceAdd) {
1302
- const dirNode = {
1303
- name: data.name || pathParts[pathParts.length - 1] || 'Unknown',
1304
- path: data.path,
1305
- type: 'directory',
1306
- children: [],
1307
- loaded: false,
1308
- expanded: false,
1309
- stats: data.stats || {}
1310
- };
1311
-
1312
- this.addNodeToTree(dirNode, data.parent || '');
1313
- this.updateBreadcrumb(`Discovered: ${data.path}`, 'info');
1314
- }
1315
- }
1316
- }
1317
-
1318
- /**
1319
- * Handle file discovered event
1320
- */
1321
- onFileDiscovered(data) {
1322
- // Update activity ticker
1323
- const fileName = data.name || (data.path ? data.path.split('/').pop() : 'file');
1324
- this.updateActivityTicker(`📄 Found: ${fileName}`);
1325
-
1326
- // Add to events display
1327
- this.addEventToDisplay(`📄 Discovered: ${data.path || 'Unknown file'}`, 'info');
1328
-
1329
- const pathParts = data.path ? data.path.split('/').filter(p => p) : [];
1330
- const parentPath = pathParts.slice(0, -1).join('/');
1331
-
1332
- const fileNode = {
1333
- name: data.name || pathParts[pathParts.length - 1] || 'Unknown',
1334
- path: data.path,
1335
- type: 'file',
1336
- language: data.language || this.detectLanguage(data.path),
1337
- size: data.size || 0,
1338
- lines: data.lines || 0,
1339
- children: [],
1340
- analyzed: false
1341
- };
1342
-
1343
- this.addNodeToTree(fileNode, parentPath);
1344
- this.stats.files++;
1345
- this.updateStats();
1346
- this.updateBreadcrumb(`Found: ${data.path}`, 'info');
1347
- }
1348
-
1349
- /**
1350
- * Handle file analyzed event
1351
- */
1352
- onFileAnalyzed(data) {
1353
- // Remove loading pulse if this file was being analyzed
1354
- const d3Node = this.findD3NodeByPath(data.path);
1355
- if (d3Node && this.loadingNodes.has(data.path)) {
1356
- this.removeLoadingPulse(d3Node);
1357
- this.loadingNodes.delete(data.path); // Remove from loading set
1358
- }
1359
- // Update activity ticker
1360
- if (data.path) {
1361
- const fileName = data.path.split('/').pop();
1362
- this.updateActivityTicker(`🔍 Analyzed: ${fileName}`);
1363
- }
1364
-
1365
- const fileNode = this.findNodeByPath(data.path);
1366
- if (fileNode) {
1367
- fileNode.analyzed = true;
1368
- fileNode.complexity = data.complexity || 0;
1369
- fileNode.lines = data.lines || 0;
1370
-
1371
- // Add code elements as children
1372
- if (data.elements && Array.isArray(data.elements)) {
1373
- fileNode.children = data.elements.map(elem => ({
1374
- name: elem.name,
1375
- type: elem.type.toLowerCase(),
1376
- path: `${data.path}#${elem.name}`,
1377
- line: elem.line,
1378
- complexity: elem.complexity || 1,
1379
- docstring: elem.docstring || '',
1380
- children: elem.methods ? elem.methods.map(m => ({
1381
- name: m.name,
1382
- type: 'method',
1383
- path: `${data.path}#${elem.name}.${m.name}`,
1384
- line: m.line,
1385
- complexity: m.complexity || 1,
1386
- docstring: m.docstring || ''
1387
- })) : []
1388
- }));
1389
- }
1390
-
1391
- // Update stats
1392
- if (data.stats) {
1393
- this.stats.classes += data.stats.classes || 0;
1394
- this.stats.functions += data.stats.functions || 0;
1395
- this.stats.methods += data.stats.methods || 0;
1396
- this.stats.lines += data.stats.lines || 0;
1397
- }
1398
-
1399
- this.updateStats();
1400
- if (this.root) {
1401
- this.update(this.root);
1402
- }
1403
-
1404
- this.updateBreadcrumb(`Analyzed: ${data.path}`, 'success');
1405
- }
1406
- }
1407
-
1408
- /**
1409
- * Handle node found event
1410
- */
1411
- onNodeFound(data) {
1412
- // Add to events display with appropriate icon
1413
- const typeIcon = data.type === 'class' ? '🏛️' :
1414
- data.type === 'function' ? '⚡' :
1415
- data.type === 'method' ? '🔧' : '📦';
1416
- this.addEventToDisplay(`${typeIcon} Found ${data.type || 'node'}: ${data.name || 'Unknown'}`);
1417
-
1418
- // Extract node info
1419
- const nodeInfo = {
1420
- name: data.name || 'Unknown',
1421
- type: (data.type || 'unknown').toLowerCase(),
1422
- path: data.path || '',
1423
- line: data.line || 0,
1424
- complexity: data.complexity || 1,
1425
- docstring: data.docstring || ''
1426
- };
1427
-
1428
- // Map event types to our internal types
1429
- const typeMapping = {
1430
- 'class': 'class',
1431
- 'function': 'function',
1432
- 'method': 'method',
1433
- 'module': 'module',
1434
- 'file': 'file',
1435
- 'directory': 'directory'
1436
- };
1437
-
1438
- nodeInfo.type = typeMapping[nodeInfo.type] || nodeInfo.type;
1439
-
1440
- // Determine parent path
1441
- let parentPath = '';
1442
- if (data.parent_path) {
1443
- parentPath = data.parent_path;
1444
- } else if (data.file_path) {
1445
- parentPath = data.file_path;
1446
- } else if (nodeInfo.path.includes('/')) {
1447
- const parts = nodeInfo.path.split('/');
1448
- parts.pop();
1449
- parentPath = parts.join('/');
1450
- }
1451
-
1452
- // Update stats based on node type
1453
- switch(nodeInfo.type) {
1454
- case 'class':
1455
- this.stats.classes++;
1456
- break;
1457
- case 'function':
1458
- this.stats.functions++;
1459
- break;
1460
- case 'method':
1461
- this.stats.methods++;
1462
- break;
1463
- case 'file':
1464
- this.stats.files++;
1465
- break;
1466
- }
1467
-
1468
- // Add node to tree
1469
- this.addNodeToTree(nodeInfo, parentPath);
1470
- this.updateStats();
1471
-
1472
- // Show progress in breadcrumb
1473
- const elementType = nodeInfo.type.charAt(0).toUpperCase() + nodeInfo.type.slice(1);
1474
- this.updateBreadcrumb(`Found ${elementType}: ${nodeInfo.name}`, 'info');
1475
- }
1476
-
1477
- /**
1478
- * Handle progress update
1479
- */
1480
- onProgressUpdate(data) {
1481
- const progress = data.progress || 0;
1482
- const message = data.message || `Processing... ${progress}%`;
1483
-
1484
- this.updateBreadcrumb(message, 'info');
1485
-
1486
- // Update progress bar if it exists
1487
- const progressBar = document.querySelector('.code-tree-progress');
1488
- if (progressBar) {
1489
- progressBar.style.width = `${progress}%`;
1490
- }
1491
- }
1492
-
1493
- /**
1494
- * Handle analysis complete event
1495
- */
1496
- onAnalysisComplete(data) {
1497
- this.analyzing = false;
1498
- this.hideLoading();
1499
-
1500
- // Update activity ticker
1501
- this.updateActivityTicker('✅ Ready', 'success');
1502
-
1503
- // Add completion event
1504
- this.addEventToDisplay('✅ Analysis complete!', 'success');
1505
-
1506
- // Update tree visualization
1507
- if (this.root && this.svg) {
1508
- this.update(this.root);
1509
- }
1510
-
1511
- // Update stats from completion data
1512
- if (data.stats) {
1513
- this.stats = { ...this.stats, ...data.stats };
1514
- this.updateStats();
1515
- }
1516
-
1517
- const message = data.message || `Analysis complete: ${this.stats.files} files, ${this.stats.classes} classes, ${this.stats.functions} functions`;
1518
- this.updateBreadcrumb(message, 'success');
1519
- this.showNotification(message, 'success');
1520
- }
1521
-
1522
- /**
1523
- * Handle analysis error
1524
- */
1525
- onAnalysisError(data) {
1526
- this.analyzing = false;
1527
- this.hideLoading();
1528
-
1529
- const message = data.message || data.error || 'Analysis failed';
1530
- this.updateBreadcrumb(message, 'error');
1531
- this.showNotification(message, 'error');
1532
- }
1533
-
1534
- /**
1535
- * Handle analysis accepted
1536
- */
1537
- onAnalysisAccepted(data) {
1538
- const message = data.message || 'Analysis request accepted';
1539
- this.updateBreadcrumb(message, 'info');
1540
- }
1541
-
1542
- /**
1543
- * Handle analysis queued
1544
- */
1545
- onAnalysisQueued(data) {
1546
- const position = data.position || 0;
1547
- const message = `Analysis queued (position ${position})`;
1548
- this.updateBreadcrumb(message, 'warning');
1549
- this.showNotification(message, 'info');
1550
- }
1551
-
1552
- /**
1553
- * Handle INFO events for granular work tracking
1554
- */
1555
- onInfoEvent(data) {
1556
- // Log to console for debugging
1557
-
1558
- // Update breadcrumb for certain events
1559
- if (data.type && data.type.startsWith('discovery.')) {
1560
- // Discovery events
1561
- if (data.type === 'discovery.start') {
1562
- this.updateBreadcrumb(data.message, 'info');
1563
- } else if (data.type === 'discovery.complete') {
1564
- this.updateBreadcrumb(data.message, 'success');
1565
- // Show stats if available
1566
- if (data.stats) {
1567
- }
1568
- } else if (data.type === 'discovery.directory' || data.type === 'discovery.file') {
1569
- // Quick flash of discovery events
1570
- this.updateBreadcrumb(data.message, 'info');
1571
- }
1572
- } else if (data.type && data.type.startsWith('analysis.')) {
1573
- // Analysis events
1574
- if (data.type === 'analysis.start') {
1575
- this.updateBreadcrumb(data.message, 'info');
1576
- } else if (data.type === 'analysis.complete') {
1577
- this.updateBreadcrumb(data.message, 'success');
1578
- // Show stats if available
1579
- if (data.stats) {
1580
- const statsMsg = `Found: ${data.stats.classes || 0} classes, ${data.stats.functions || 0} functions, ${data.stats.methods || 0} methods`;
1581
- }
1582
- } else if (data.type === 'analysis.class' || data.type === 'analysis.function' || data.type === 'analysis.method') {
1583
- // Show found elements briefly
1584
- this.updateBreadcrumb(data.message, 'info');
1585
- } else if (data.type === 'analysis.parse') {
1586
- this.updateBreadcrumb(data.message, 'info');
1587
- }
1588
- } else if (data.type && data.type.startsWith('filter.')) {
1589
- // Filter events - optionally show in debug mode
1590
- if (window.debugMode || this.showFilterEvents) {
1591
- console.debug('[FILTER]', data.type, data.path, data.reason);
1592
- if (this.showFilterEvents) {
1593
- this.updateBreadcrumb(data.message, 'warning');
1594
- }
1595
- }
1596
- } else if (data.type && data.type.startsWith('cache.')) {
1597
- // Cache events
1598
- if (data.type === 'cache.hit') {
1599
- console.debug('[CACHE HIT]', data.file);
1600
- if (this.showCacheEvents) {
1601
- this.updateBreadcrumb(data.message, 'info');
1602
- }
1603
- } else if (data.type === 'cache.miss') {
1604
- console.debug('[CACHE MISS]', data.file);
1605
- }
1606
- }
1607
-
1608
- // Optionally add to an event log display if enabled
1609
- if (this.eventLogEnabled && data.message) {
1610
- this.addEventToDisplay(data);
1611
- }
1612
- }
1613
-
1614
- /**
1615
- * Add event to display log (if we have one)
1616
- */
1617
- addEventToDisplay(data) {
1618
- // Could be implemented to show events in a dedicated log area
1619
- // For now, just maintain a recent events list
1620
- if (!this.recentEvents) {
1621
- this.recentEvents = [];
1622
- }
1623
-
1624
- this.recentEvents.unshift({
1625
- timestamp: data.timestamp || new Date().toISOString(),
1626
- type: data.type,
1627
- message: data.message,
1628
- data: data
1629
- });
1630
-
1631
- // Keep only last 100 events
1632
- if (this.recentEvents.length > 100) {
1633
- this.recentEvents.pop();
1634
- }
1635
-
1636
- // Could update a UI element here if we had an event log display
1637
- }
1638
-
1639
- /**
1640
- * Handle analysis cancelled
1641
- */
1642
- onAnalysisCancelled(data) {
1643
- this.analyzing = false;
1644
- this.hideLoading();
1645
- const message = data.message || 'Analysis cancelled';
1646
- this.updateBreadcrumb(message, 'warning');
1647
- }
1648
-
1649
- /**
1650
- * Show notification toast
1651
- */
1652
- showNotification(message, type = 'info') {
1653
- const notification = document.createElement('div');
1654
- notification.className = `code-tree-notification ${type}`;
1655
- notification.textContent = message;
1656
-
1657
- // Change from appending to container to positioning absolutely within it
1658
- const container = document.getElementById('code-tree-container');
1659
- if (container) {
1660
- // Position relative to the container
1661
- notification.style.position = 'absolute';
1662
- notification.style.top = '10px';
1663
- notification.style.right = '10px';
1664
- notification.style.zIndex = '1000';
1665
-
1666
- // Ensure container is positioned
1667
- if (!container.style.position || container.style.position === 'static') {
1668
- container.style.position = 'relative';
1669
- }
1670
-
1671
- container.appendChild(notification);
1672
-
1673
- // Animate out after 3 seconds
1674
- setTimeout(() => {
1675
- notification.style.animation = 'slideOutRight 0.3s ease';
1676
- setTimeout(() => notification.remove(), 300);
1677
- }, 3000);
1678
- }
1679
- }
1680
-
1681
- /**
1682
- * Add node to tree structure
1683
- */
1684
- addNodeToTree(nodeInfo, parentPath = '') {
1685
- // CRITICAL: Validate that nodeInfo.path doesn't contain absolute paths
1686
- // The backend should only send relative paths now
1687
- if (nodeInfo.path && nodeInfo.path.startsWith('/')) {
1688
- console.error('Absolute path detected in node, skipping:', nodeInfo.path);
1689
- return;
1690
- }
1691
-
1692
- // Also validate parent path
1693
- if (parentPath && parentPath.startsWith('/')) {
1694
- console.error('Absolute path detected in parent, skipping:', parentPath);
1695
- return;
1696
- }
1697
-
1698
- // Find parent node
1699
- let parentNode = this.treeData;
1700
-
1701
- if (parentPath) {
1702
- parentNode = this.findNodeByPath(parentPath);
1703
- if (!parentNode) {
1704
- // CRITICAL: Do NOT create parent structure if it doesn't exist
1705
- // This prevents creating nodes above the working directory
1706
- console.warn('Parent node not found, skipping node creation:', parentPath);
1707
- console.warn('Attempted to add node:', nodeInfo);
1708
- return;
1709
- }
1710
- }
1711
-
1712
- // Check if node already exists
1713
- const existingNode = parentNode.children?.find(c =>
1714
- c.path === nodeInfo.path ||
1715
- (c.name === nodeInfo.name && c.type === nodeInfo.type)
1716
- );
1717
-
1718
- if (existingNode) {
1719
- // Update existing node
1720
- Object.assign(existingNode, nodeInfo);
1721
- return;
1722
- }
1723
-
1724
- // Add new node
1725
- if (!parentNode.children) {
1726
- parentNode.children = [];
1727
- }
1728
-
1729
- // Ensure the node has a children array
1730
- if (!nodeInfo.children) {
1731
- nodeInfo.children = [];
1732
- }
1733
-
1734
- parentNode.children.push(nodeInfo);
1735
-
1736
- // Store node reference for quick access
1737
- this.nodes.set(nodeInfo.path, nodeInfo);
1738
-
1739
- // Update tree if initialized
1740
- if (this.root && this.svg) {
1741
- // Recreate hierarchy with new data
1742
- this.root = d3.hierarchy(this.treeData);
1743
- this.root.x0 = this.height / 2;
1744
- this.root.y0 = 0;
1745
-
1746
- // Update only if we have a reasonable number of nodes to avoid performance issues
1747
- if (this.nodes.size < 1000) {
1748
- this.update(this.root);
1749
- } else if (this.nodes.size % 100 === 0) {
1750
- // Update every 100 nodes for large trees
1751
- this.update(this.root);
1752
- }
1753
- }
1754
- }
1755
-
1756
- /**
1757
- * Find node by path in tree
1758
- */
1759
- findNodeByPath(path, node = null) {
1760
- if (!node) {
1761
- node = this.treeData;
1762
- console.log('🔍 [SUBDIRECTORY LOADING] Starting search for path:', path);
1763
- }
1764
-
1765
- if (node.path === path) {
1766
- console.log('✅ [SUBDIRECTORY LOADING] Found node for path:', path);
1767
- return node;
1768
- }
1769
-
1770
- if (node.children) {
1771
- for (const child of node.children) {
1772
- const found = this.findNodeByPath(path, child);
1773
- if (found) {
1774
- return found;
1775
- }
1776
- }
1777
- }
1778
-
1779
- if (!node.parent && node === this.treeData) {
1780
- console.warn('❌ [SUBDIRECTORY LOADING] Path not found in tree:', path);
1781
- }
1782
- return null;
1783
- }
1784
-
1785
- /**
1786
- * Helper to log all paths in tree for debugging
1787
- */
1788
- logAllPaths(node, indent = '') {
1789
- console.log(`${indent}${node.path} (${node.name})`);
1790
- if (node.children) {
1791
- for (const child of node.children) {
1792
- this.logAllPaths(child, indent + ' ');
1793
- }
1794
- }
1795
- }
1796
-
1797
- /**
1798
- * Helper to collect all paths in tree for debugging
1799
- */
1800
- getAllTreePaths(node) {
1801
- const paths = [node.path];
1802
- if (node.children) {
1803
- for (const child of node.children) {
1804
- paths.push(...this.getAllTreePaths(child));
1805
- }
1806
- }
1807
- return paths;
1808
- }
1809
-
1810
- /**
1811
- * Find D3 hierarchy node by path
1812
- */
1813
- findD3NodeByPath(path) {
1814
- if (!this.root) return null;
1815
- return this.root.descendants().find(d => d.data.path === path);
1816
- }
1817
-
1818
- /**
1819
- * Preserve expansion state when recreating hierarchy
1820
- */
1821
- preserveExpansionState(oldRoot, newRoot) {
1822
- if (!oldRoot || !newRoot) return;
1823
-
1824
- // Create a map of expanded nodes from the old tree
1825
- const expansionMap = new Map();
1826
- oldRoot.descendants().forEach(node => {
1827
- if (node.data.expanded || (node.children && !node._children)) {
1828
- expansionMap.set(node.data.path, true);
1829
- }
1830
- });
1831
-
1832
- // Apply expansion state to new tree
1833
- newRoot.descendants().forEach(node => {
1834
- if (expansionMap.has(node.data.path)) {
1835
- node.children = node._children || node.children;
1836
- node._children = null;
1837
- node.data.expanded = true;
1838
- }
1839
- });
1840
- }
1841
-
1842
- /**
1843
- * Update statistics display
1844
- */
1845
- updateStats() {
1846
- // Update stats display - use correct IDs from HTML
1847
- const statsElements = {
1848
- 'file-count': this.stats.files,
1849
- 'class-count': this.stats.classes,
1850
- 'function-count': this.stats.functions,
1851
- 'line-count': this.stats.lines
1852
- };
1853
-
1854
- for (const [id, value] of Object.entries(statsElements)) {
1855
- const elem = document.getElementById(id);
1856
- if (elem) {
1857
- elem.textContent = value.toLocaleString();
1858
- }
1859
- }
1860
-
1861
- // Update progress text
1862
- const progressText = document.getElementById('code-progress-text');
1863
- if (progressText) {
1864
- const statusText = this.analyzing ?
1865
- `Analyzing... ${this.stats.files} files processed` :
1866
- `Ready - ${this.stats.files} files in tree`;
1867
- progressText.textContent = statusText;
1868
- }
1869
- }
1870
-
1871
- /**
1872
- * Update breadcrumb trail
1873
- */
1874
- updateBreadcrumb(message, type = 'info') {
1875
- const breadcrumbContent = document.getElementById('breadcrumb-content');
1876
- if (breadcrumbContent) {
1877
- breadcrumbContent.textContent = message;
1878
- breadcrumbContent.className = `breadcrumb-${type}`;
1879
- }
1880
- }
1881
-
1882
- /**
1883
- * Detect language from file extension
1884
- */
1885
- detectLanguage(filePath) {
1886
- const ext = filePath.split('.').pop().toLowerCase();
1887
- const languageMap = {
1888
- 'py': 'python',
1889
- 'js': 'javascript',
1890
- 'ts': 'typescript',
1891
- 'jsx': 'javascript',
1892
- 'tsx': 'typescript',
1893
- 'java': 'java',
1894
- 'cpp': 'cpp',
1895
- 'c': 'c',
1896
- 'cs': 'csharp',
1897
- 'rb': 'ruby',
1898
- 'go': 'go',
1899
- 'rs': 'rust',
1900
- 'php': 'php',
1901
- 'swift': 'swift',
1902
- 'kt': 'kotlin',
1903
- 'scala': 'scala',
1904
- 'r': 'r',
1905
- 'sh': 'bash',
1906
- 'ps1': 'powershell'
1907
- };
1908
- return languageMap[ext] || 'unknown';
1909
- }
1910
-
1911
- /**
1912
- * Add visualization controls for layout toggle
1913
- */
1914
- addVisualizationControls() {
1915
- const controls = this.svg.append('g')
1916
- .attr('class', 'viz-controls')
1917
- .attr('transform', 'translate(10, 10)');
1918
-
1919
- // Add layout toggle button
1920
- const toggleButton = controls.append('g')
1921
- .attr('class', 'layout-toggle')
1922
- .style('cursor', 'pointer')
1923
- .on('click', () => this.toggleLayout());
1924
-
1925
- toggleButton.append('rect')
1926
- .attr('width', 120)
1927
- .attr('height', 30)
1928
- .attr('rx', 5)
1929
- .attr('fill', '#3b82f6')
1930
- .attr('opacity', 0.8);
1931
-
1932
- toggleButton.append('text')
1933
- .attr('x', 60)
1934
- .attr('y', 20)
1935
- .attr('text-anchor', 'middle')
1936
- .attr('fill', 'white')
1937
- .style('font-size', '12px')
1938
- .text(this.isRadialLayout ? 'Switch to Linear' : 'Switch to Radial');
1939
- }
1940
-
1941
- /**
1942
- * Toggle between radial and linear layouts
1943
- */
1944
- toggleLayout() {
1945
- this.isRadialLayout = !this.isRadialLayout;
1946
- this.createVisualization();
1947
- if (this.root) {
1948
- this.update(this.root);
1949
- }
1950
- this.showNotification(
1951
- this.isRadialLayout ? 'Switched to radial layout' : 'Switched to linear layout',
1952
- 'info'
1953
- );
1954
- }
1955
-
1956
- /**
1957
- * Convert radial coordinates to Cartesian
1958
- */
1959
- radialPoint(x, y) {
1960
- return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
1961
- }
1962
-
1963
- /**
1964
- * Update D3 tree visualization
1965
- */
1966
- update(source) {
1967
- if (!this.treeLayout || !this.treeGroup || !source) {
1968
- return;
1969
- }
1970
-
1971
- // Compute the new tree layout
1972
- const treeData = this.treeLayout(this.root);
1973
- const nodes = treeData.descendants();
1974
- const links = treeData.descendants().slice(1);
1975
-
1976
- if (this.isRadialLayout) {
1977
- // Radial layout adjustments
1978
- nodes.forEach(d => {
1979
- // Store original x,y for transitions
1980
- if (d.x0 === undefined) {
1981
- d.x0 = d.x;
1982
- d.y0 = d.y;
1983
- }
1984
- });
1985
- } else {
1986
- // Linear layout with nodeSize doesn't need manual normalization
1987
- // The tree layout handles spacing automatically
1988
- }
1989
-
1990
- // Update nodes
1991
- const node = this.treeGroup.selectAll('g.node')
1992
- .data(nodes, d => d.id || (d.id = ++this.nodeId));
1993
-
1994
- // Enter new nodes
1995
- const nodeEnter = node.enter().append('g')
1996
- .attr('class', d => {
1997
- let classes = ['node', 'code-node'];
1998
- if (d.data.type === 'directory') {
1999
- classes.push('directory');
2000
- if (d.data.loaded === true && d.children) {
2001
- classes.push('expanded');
2002
- }
2003
- if (d.data.loaded === 'loading') {
2004
- classes.push('loading');
2005
- }
2006
- if (d.data.children && d.data.children.length === 0) {
2007
- classes.push('empty');
2008
- }
2009
- } else if (d.data.type === 'file') {
2010
- classes.push('file');
2011
- }
2012
- return classes.join(' ');
2013
- })
2014
- .attr('transform', d => {
2015
- if (this.isRadialLayout) {
2016
- const [x, y] = this.radialPoint(source.x0 || 0, source.y0 || 0);
2017
- return `translate(${x},${y})`;
2018
- } else {
2019
- return `translate(${source.y0},${source.x0})`;
2020
- }
2021
- })
2022
- .on('click', (event, d) => this.onNodeClick(event, d));
2023
-
2024
- // Add circles for nodes
2025
- nodeEnter.append('circle')
2026
- .attr('class', 'node-circle')
2027
- .attr('r', 1e-6)
2028
- .style('fill', d => this.getNodeColor(d))
2029
- .style('stroke', d => this.getNodeStrokeColor(d))
2030
- .style('stroke-width', d => d.data.type === 'directory' ? 2 : 1.5)
2031
- .style('cursor', 'pointer') // Add cursor pointer for visual feedback
2032
- .on('click', (event, d) => this.onNodeClick(event, d)) // CRITICAL FIX: Add click handler to circles
2033
- .on('mouseover', (event, d) => this.showTooltip(event, d))
2034
- .on('mouseout', () => this.hideTooltip());
2035
-
2036
- // Add expand/collapse icons for directories
2037
- nodeEnter.filter(d => d.data.type === 'directory')
2038
- .append('text')
2039
- .attr('class', 'expand-icon')
2040
- .attr('x', 0)
2041
- .attr('y', 0)
2042
- .attr('text-anchor', 'middle')
2043
- .attr('dominant-baseline', 'central')
2044
- .text(d => {
2045
- if (d.data.loaded === 'loading') return '⟳';
2046
- if (d.data.loaded === true && d.children) return '▼';
2047
- return '▶';
2048
- })
2049
- .style('font-size', '10px')
2050
- .style('pointer-events', 'none');
2051
-
2052
- // Add labels for nodes with smart positioning
2053
- nodeEnter.append('text')
2054
- .attr('class', 'node-label')
2055
- .attr('dy', '.35em')
2056
- .attr('x', d => {
2057
- if (this.isRadialLayout) {
2058
- // For radial layout, initial position
2059
- return 0;
2060
- } else {
2061
- // Linear layout: standard positioning
2062
- return d.children || d._children ? -13 : 13;
2063
- }
2064
- })
2065
- .attr('text-anchor', d => {
2066
- if (this.isRadialLayout) {
2067
- return 'start'; // Will be adjusted in update
2068
- } else {
2069
- // Linear layout: standard anchoring
2070
- return d.children || d._children ? 'end' : 'start';
2071
- }
2072
- })
2073
- .text(d => {
2074
- // Truncate long names
2075
- const maxLength = 20;
2076
- const name = d.data.name || '';
2077
- return name.length > maxLength ?
2078
- name.substring(0, maxLength - 3) + '...' : name;
2079
- })
2080
- .style('fill-opacity', 1e-6)
2081
- .style('font-size', '12px')
2082
- .style('font-family', '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif')
2083
- .style('text-shadow', '1px 1px 2px rgba(255,255,255,0.8), -1px -1px 2px rgba(255,255,255,0.8)')
2084
- .on('click', (event, d) => this.onNodeClick(event, d)) // CRITICAL FIX: Add click handler to labels
2085
- .style('cursor', 'pointer');
2086
-
2087
- // Add icons for node types (files only, directories use expand icons)
2088
- nodeEnter.filter(d => d.data.type !== 'directory')
2089
- .append('text')
2090
- .attr('class', 'node-icon')
2091
- .attr('dy', '.35em')
2092
- .attr('x', 0)
2093
- .attr('text-anchor', 'middle')
2094
- .text(d => this.getNodeIcon(d))
2095
- .style('font-size', '10px')
2096
- .style('fill', 'white')
2097
- .on('click', (event, d) => this.onNodeClick(event, d)) // CRITICAL FIX: Add click handler to file icons
2098
- .style('cursor', 'pointer');
2099
-
2100
- // Add item count badges for directories
2101
- nodeEnter.filter(d => d.data.type === 'directory' && d.data.children)
2102
- .append('text')
2103
- .attr('class', 'item-count-badge')
2104
- .attr('x', 12)
2105
- .attr('y', -8)
2106
- .attr('text-anchor', 'middle')
2107
- .text(d => {
2108
- const count = d.data.children ? d.data.children.length : 0;
2109
- return count > 0 ? count : '';
2110
- })
2111
- .style('font-size', '9px')
2112
- .style('opacity', 0.7)
2113
- .on('click', (event, d) => this.onNodeClick(event, d)) // CRITICAL FIX: Add click handler to count badges
2114
- .style('cursor', 'pointer');
2115
-
2116
- // Transition to new positions
2117
- const nodeUpdate = nodeEnter.merge(node);
2118
-
2119
- // CRITICAL FIX: Ensure ALL nodes (new and existing) have click handlers
2120
- // This fixes the issue where subdirectory clicks stop working after tree updates
2121
- nodeUpdate.on('click', (event, d) => this.onNodeClick(event, d));
2122
-
2123
- nodeUpdate.transition()
2124
- .duration(this.duration)
2125
- .attr('transform', d => {
2126
- if (this.isRadialLayout) {
2127
- const [x, y] = this.radialPoint(d.x, d.y);
2128
- return `translate(${x},${y})`;
2129
- } else {
2130
- return `translate(${d.y},${d.x})`;
2131
- }
2132
- });
2133
-
2134
- // Update node classes based on current state
2135
- nodeUpdate.attr('class', d => {
2136
- let classes = ['node', 'code-node'];
2137
- if (d.data.type === 'directory') {
2138
- classes.push('directory');
2139
- if (d.data.loaded === true && d.children) {
2140
- classes.push('expanded');
2141
- }
2142
- if (d.data.loaded === 'loading') {
2143
- classes.push('loading');
2144
- }
2145
- if (d.data.children && d.data.children.length === 0) {
2146
- classes.push('empty');
2147
- }
2148
- } else if (d.data.type === 'file') {
2149
- classes.push('file');
2150
- }
2151
- return classes.join(' ');
2152
- });
2153
-
2154
- nodeUpdate.select('circle.node-circle')
2155
- .attr('r', d => d.data.type === 'directory' ? 10 : 8)
2156
- .style('fill', d => this.getNodeColor(d))
2157
-
2158
- // Update expand/collapse icons
2159
- nodeUpdate.select('.expand-icon')
2160
- .text(d => {
2161
- if (d.data.loaded === 'loading') return '⟳';
2162
- if (d.data.loaded === true && d.children) return '▼';
2163
- return '▶';
2164
- });
2165
-
2166
- // Update item count badges
2167
- nodeUpdate.select('.item-count-badge')
2168
- .text(d => {
2169
- if (d.data.type !== 'directory') return '';
2170
- const count = d.data.children ? d.data.children.length : 0;
2171
- return count > 0 ? count : '';
2172
- })
2173
- .style('stroke', d => this.getNodeStrokeColor(d))
2174
- .attr('cursor', 'pointer');
2175
-
2176
- // Update text labels with proper rotation for radial layout
2177
- const isRadial = this.isRadialLayout; // Capture the layout type
2178
- nodeUpdate.select('text.node-label')
2179
- .style('fill-opacity', 1)
2180
- .style('fill', '#333')
2181
- .each(function(d) {
2182
- const selection = d3.select(this);
2183
-
2184
- if (isRadial) {
2185
- // For radial layout, apply rotation and positioning
2186
- const angle = (d.x * 180 / Math.PI) - 90; // Convert to degrees
2187
-
2188
- // Determine if text should be flipped (left side of circle)
2189
- const shouldFlip = angle > 90 || angle < -90;
2190
-
2191
- // Calculate text position and rotation
2192
- if (shouldFlip) {
2193
- // Text on left side - rotate 180 degrees to read properly
2194
- selection
2195
- .attr('transform', `rotate(${angle + 180})`)
2196
- .attr('x', -15) // Negative offset for flipped text
2197
- .attr('text-anchor', 'end')
2198
- .attr('dy', '.35em');
2199
- } else {
2200
- // Text on right side - normal orientation
2201
- selection
2202
- .attr('transform', `rotate(${angle})`)
2203
- .attr('x', 15) // Positive offset for normal text
2204
- .attr('text-anchor', 'start')
2205
- .attr('dy', '.35em');
2206
- }
2207
- } else {
2208
- // Linear layout - no rotation needed
2209
- selection
2210
- .attr('transform', null)
2211
- .attr('x', d.children || d._children ? -13 : 13)
2212
- .attr('text-anchor', d.children || d._children ? 'end' : 'start')
2213
- .attr('dy', '.35em');
2214
- }
2215
- });
2216
-
2217
- // Remove exiting nodes
2218
- const nodeExit = node.exit().transition()
2219
- .duration(this.duration)
2220
- .attr('transform', d => {
2221
- if (this.isRadialLayout) {
2222
- const [x, y] = this.radialPoint(source.x, source.y);
2223
- return `translate(${x},${y})`;
2224
- } else {
2225
- return `translate(${source.y},${source.x})`;
2226
- }
2227
- })
2228
- .remove();
2229
-
2230
- nodeExit.select('circle')
2231
- .attr('r', 1e-6);
2232
-
2233
- nodeExit.select('text.node-label')
2234
- .style('fill-opacity', 1e-6);
2235
-
2236
- nodeExit.select('text.node-icon')
2237
- .style('fill-opacity', 1e-6);
2238
-
2239
- // Update links
2240
- const link = this.treeGroup.selectAll('path.link')
2241
- .data(links, d => d.id);
2242
-
2243
- // Enter new links
2244
- const linkEnter = link.enter().insert('path', 'g')
2245
- .attr('class', 'link')
2246
- .attr('d', d => {
2247
- const o = {x: source.x0, y: source.y0};
2248
- return this.isRadialLayout ?
2249
- this.radialDiagonal(o, o) :
2250
- this.diagonal(o, o);
2251
- })
2252
- .style('fill', 'none')
2253
- .style('stroke', '#ccc')
2254
- .style('stroke-width', 2);
2255
-
2256
- // Transition to new positions
2257
- const linkUpdate = linkEnter.merge(link);
2258
-
2259
- linkUpdate.transition()
2260
- .duration(this.duration)
2261
- .attr('d', d => this.isRadialLayout ?
2262
- this.radialDiagonal(d, d.parent) :
2263
- this.diagonal(d, d.parent));
2264
-
2265
- // Remove exiting links
2266
- link.exit().transition()
2267
- .duration(this.duration)
2268
- .attr('d', d => {
2269
- const o = {x: source.x, y: source.y};
2270
- return this.isRadialLayout ?
2271
- this.radialDiagonal(o, o) :
2272
- this.diagonal(o, o);
2273
- })
2274
- .remove();
2275
-
2276
- // Store old positions for transition
2277
- nodes.forEach(d => {
2278
- d.x0 = d.x;
2279
- d.y0 = d.y;
2280
- });
2281
- }
2282
-
2283
- /**
2284
- * REMOVED: Center the view on a specific node (Linear layout)
2285
- * This method has been completely disabled to prevent unwanted tree movement.
2286
- * All centering functionality has been removed from the code tree.
2287
- */
2288
- centerOnNode(d) {
2289
- // Method disabled - no centering operations will be performed
2290
- console.log('[CodeTree] centerOnNode called but disabled - no centering will occur');
2291
- return;
2292
- }
2293
-
2294
- /**
2295
- * REMOVED: Center the view on a specific node (Radial layout)
2296
- * This method has been completely disabled to prevent unwanted tree movement.
2297
- * All centering functionality has been removed from the code tree.
2298
- */
2299
- centerOnNodeRadial(d) {
2300
- // Method disabled - no centering operations will be performed
2301
- console.log('[CodeTree] centerOnNodeRadial called but disabled - no centering will occur');
2302
- return;
2303
- }
2304
-
2305
- /**
2306
- * Highlight the active node with larger icon
2307
- */
2308
- highlightActiveNode(d) {
2309
- // Reset all nodes to normal size and clear parent context
2310
- // First clear classes on the selection
2311
- const allCircles = this.treeGroup.selectAll('circle.node-circle');
2312
- allCircles
2313
- .classed('active', false)
2314
- .classed('parent-context', false);
2315
-
2316
- // Then apply transition separately
2317
- allCircles
2318
- .transition()
2319
- .duration(300)
2320
- .attr('r', 8)
2321
- .style('stroke', null)
2322
- .style('stroke-width', null)
2323
- .style('opacity', null);
2324
-
2325
- // Reset all labels to normal
2326
- this.treeGroup.selectAll('text.node-label')
2327
- .style('font-weight', 'normal')
2328
- .style('font-size', '12px');
2329
-
2330
- // Find and increase size of clicked node - use data matching
2331
- // Make the size increase MUCH more dramatic: 8 -> 20 (2.5x the size)
2332
- const activeNodeCircle = this.treeGroup.selectAll('g.node')
2333
- .filter(node => node === d)
2334
- .select('circle.node-circle');
2335
-
2336
- // First set the class (not part of transition)
2337
- activeNodeCircle.classed('active', true);
2338
-
2339
- // Then apply the transition with styles - MUCH LARGER
2340
- activeNodeCircle
2341
- .transition()
2342
- .duration(300)
2343
- .attr('r', 20) // Much larger radius (2.5x)
2344
- .style('stroke', '#3b82f6')
2345
- .style('stroke-width', 5) // Thicker border
2346
- .style('filter', 'drop-shadow(0 0 15px rgba(59, 130, 246, 0.6))'); // Stronger glow effect
2347
-
2348
- // Also make the label bold
2349
- this.treeGroup.selectAll('g.node')
2350
- .filter(node => node === d)
2351
- .select('text.node-label')
2352
- .style('font-weight', 'bold')
2353
- .style('font-size', '14px'); // Slightly larger text
2354
-
2355
- // Store active node
2356
- this.activeNode = d;
2357
- }
2358
-
2359
- /**
2360
- * Add pulsing animation for loading state
2361
- */
2362
- addLoadingPulse(d) {
2363
- // Use consistent selection pattern
2364
- const node = this.treeGroup.selectAll('g.node')
2365
- .filter(node => node === d)
2366
- .select('circle.node-circle');
2367
-
2368
- // Add to loading set
2369
- this.loadingNodes.add(d.data.path);
2370
-
2371
- // Add pulsing class and orange color - separate operations
2372
- node.classed('loading-pulse', true);
2373
- node.style('fill', '#fb923c'); // Orange color for loading
2374
-
2375
- // Create pulse animation
2376
- const pulseAnimation = () => {
2377
- if (!this.loadingNodes.has(d.data.path)) return;
2378
-
2379
- node.transition()
2380
- .duration(600)
2381
- .attr('r', 14)
2382
- .style('opacity', 0.6)
2383
- .transition()
2384
- .duration(600)
2385
- .attr('r', 10)
2386
- .style('opacity', 1)
2387
- .on('end', () => {
2388
- if (this.loadingNodes.has(d.data.path)) {
2389
- pulseAnimation(); // Continue pulsing
2390
- }
2391
- });
2392
- };
2393
-
2394
- pulseAnimation();
2395
- }
2396
-
2397
- /**
2398
- * Remove pulsing animation when loading complete
2399
- */
2400
- removeLoadingPulse(d) {
2401
- // Remove from loading set
2402
- this.loadingNodes.delete(d.data.path);
2403
-
2404
- // Use consistent selection pattern
2405
- const node = this.treeGroup.selectAll('g.node')
2406
- .filter(node => node === d)
2407
- .select('circle.node-circle');
2408
-
2409
- // Clear class first
2410
- node.classed('loading-pulse', false);
2411
-
2412
- // Then interrupt and transition
2413
- node.interrupt() // Stop animation
2414
- .transition()
2415
- .duration(300)
2416
- .attr('r', this.activeNode === d ? 20 : 8) // Use 20 for active node
2417
- .style('opacity', 1)
2418
- .style('fill', d => this.getNodeColor(d)); // Restore original color
2419
- }
2420
-
2421
- /**
2422
- * Show parent node alongside for context
2423
- */
2424
- showWithParent(d) {
2425
- if (!d.parent) return;
2426
-
2427
- // Make parent more visible
2428
- const parentNode = this.treeGroup.selectAll('g.node')
2429
- .filter(node => node === d.parent);
2430
-
2431
- // Highlight parent with different style - separate class from styles
2432
- const parentCircle = parentNode.select('circle.node-circle');
2433
- parentCircle.classed('parent-context', true);
2434
- parentCircle
2435
- .style('stroke', '#10b981')
2436
- .style('stroke-width', 3)
2437
- .style('opacity', 0.8);
2438
-
2439
- // REMOVED: Radial zoom adjustment functionality
2440
- // This section previously adjusted zoom to show parent and clicked node together,
2441
- // but has been completely disabled to prevent unwanted tree movement/centering.
2442
- // Only visual highlighting of the parent remains active.
2443
-
2444
- // if (this.isRadialLayout && d.parent) {
2445
- // // All zoom.transform operations have been disabled
2446
- // // to prevent tree movement when nodes are clicked
2447
- // }
2448
- }
2449
-
2450
- /**
2451
- * Handle node click - implement lazy loading with enhanced visual feedback
2452
- */
2453
- onNodeClick(event, d) {
2454
- // Handle node click interaction
2455
-
2456
- // Check event parameter
2457
- if (event) {
2458
- try {
2459
- if (typeof event.stopPropagation === 'function') {
2460
- event.stopPropagation();
2461
- } else {
2462
- }
2463
- } catch (error) {
2464
- console.error('[CodeTree] ERROR calling stopPropagation:', error);
2465
- }
2466
- } else {
2467
- }
2468
-
2469
- // Check d parameter structure
2470
- if (!d) {
2471
- console.error('[CodeTree] ERROR: d is null/undefined, cannot continue');
2472
- return;
2473
- }
2474
-
2475
- if (!d.data) {
2476
- console.error('[CodeTree] ERROR: d.data is null/undefined, cannot continue');
2477
- return;
2478
- }
2479
-
2480
- // Node interaction detected
2481
-
2482
- // === PHASE 1: Immediate Visual Effects (Synchronous) ===
2483
- // These execute immediately before any async operations
2484
-
2485
-
2486
- // Center on clicked node (immediate visual effect) - REMOVED
2487
- // Centering functionality has been disabled to prevent unwanted repositioning
2488
- // when nodes are clicked. All other click functionality remains intact.
2489
- // try {
2490
- // if (this.isRadialLayout) {
2491
- // if (typeof this.centerOnNodeRadial === 'function') {
2492
- // this.centerOnNodeRadial(d);
2493
- // } else {
2494
- // console.error('[CodeTree] centerOnNodeRadial is not a function!');
2495
- // }
2496
- // } else {
2497
- // if (typeof this.centerOnNode === 'function') {
2498
- // this.centerOnNode(d);
2499
- // } else {
2500
- // console.error('[CodeTree] centerOnNode is not a function!');
2501
- // }
2502
- // }
2503
- // } catch (error) {
2504
- // console.error('[CodeTree] ERROR during centering:', error, error.stack);
2505
- // }
2506
-
2507
-
2508
- // Highlight with larger icon (immediate visual effect)
2509
- try {
2510
- if (typeof this.highlightActiveNode === 'function') {
2511
- this.highlightActiveNode(d);
2512
- } else {
2513
- console.error('[CodeTree] highlightActiveNode is not a function!');
2514
- }
2515
- } catch (error) {
2516
- console.error('[CodeTree] ERROR during highlightActiveNode:', error, error.stack);
2517
- }
2518
-
2519
-
2520
- // Show parent context (immediate visual effect)
2521
- try {
2522
- if (typeof this.showWithParent === 'function') {
2523
- this.showWithParent(d);
2524
- } else {
2525
- console.error('[CodeTree] showWithParent is not a function!');
2526
- }
2527
- } catch (error) {
2528
- console.error('[CodeTree] ERROR during showWithParent:', error, error.stack);
2529
- }
2530
-
2531
-
2532
- // Add pulsing animation immediately for directories
2533
-
2534
- if (d.data.type === 'directory' && !d.data.loaded) {
2535
- try {
2536
- if (typeof this.addLoadingPulse === 'function') {
2537
- this.addLoadingPulse(d);
2538
- } else {
2539
- console.error('[CodeTree] addLoadingPulse is not a function!');
2540
- }
2541
- } catch (error) {
2542
- console.error('[CodeTree] ERROR during addLoadingPulse:', error, error.stack);
2543
- }
2544
- } else {
2545
- }
2546
-
2547
-
2548
- // === PHASE 2: Prepare Data (Synchronous) ===
2549
-
2550
-
2551
- // Get selected languages from checkboxes
2552
- const selectedLanguages = [];
2553
- const checkboxes = document.querySelectorAll('.language-checkbox:checked');
2554
- checkboxes.forEach(cb => {
2555
- selectedLanguages.push(cb.value);
2556
- });
2557
-
2558
- // Get ignore patterns
2559
- const ignorePatternsElement = document.getElementById('ignore-patterns');
2560
- const ignorePatterns = ignorePatternsElement?.value || '';
2561
-
2562
-
2563
- // === PHASE 3: Async Operations (Delayed) ===
2564
- // Add a small delay to ensure visual effects are rendered first
2565
-
2566
- // For directories that haven't been loaded yet, request discovery
2567
- if (d.data.type === 'directory' && !d.data.loaded) {
2568
- // Prevent duplicate requests
2569
- if (this.loadingNodes.has(d.data.path)) {
2570
- this.showNotification(`Already loading: ${d.data.name}`, 'warning');
2571
- return;
2572
- }
2573
-
2574
- // Mark as loading immediately to prevent duplicate requests
2575
- d.data.loaded = 'loading';
2576
- this.loadingNodes.add(d.data.path);
2577
-
2578
- // Ensure path is absolute or relative to working directory
2579
- const fullPath = this.ensureFullPath(d.data.path);
2580
-
2581
- // CRITICAL DEBUG: Log directory loading attempt
2582
- console.log('🚀 [SUBDIRECTORY LOADING] Attempting to load:', {
2583
- originalPath: d.data.path,
2584
- fullPath: fullPath,
2585
- nodeType: d.data.type,
2586
- loaded: d.data.loaded,
2587
- hasSocket: !!this.socket,
2588
- workingDir: this.getWorkingDirectory()
2589
- });
2590
-
2591
- // Sending discovery request for child content
2592
-
2593
- // Store reference to the D3 node for later expansion
2594
- const clickedD3Node = d;
2595
-
2596
- // Delay the socket request to ensure visual effects are rendered
2597
- setTimeout(() => {
2598
-
2599
- // Request directory contents via Socket.IO
2600
- if (this.socket) {
2601
- console.log('📡 [SUBDIRECTORY LOADING] Emitting WebSocket request:', {
2602
- event: 'code:discover:directory',
2603
- data: {
2604
- path: fullPath,
2605
- depth: this.bulkLoadMode ? 2 : 1,
2606
- languages: selectedLanguages,
2607
- ignore_patterns: ignorePatterns
2608
- }
2609
- });
2610
-
2611
- this.socket.emit('code:discover:directory', {
2612
- path: fullPath,
2613
- depth: this.bulkLoadMode ? 2 : 1, // Load 2 levels if bulk mode enabled
2614
- languages: selectedLanguages,
2615
- ignore_patterns: ignorePatterns
2616
- });
2617
-
2618
- this.updateBreadcrumb(`Loading ${d.data.name}...`, 'info');
2619
- this.showNotification(`Loading directory: ${d.data.name}`, 'info');
2620
- } else {
2621
- console.error('❌ [SUBDIRECTORY LOADING] No WebSocket connection available!');
2622
- this.showNotification(`Cannot load directory: No connection`, 'error');
2623
- }
2624
- }, 100); // 100ms delay to ensure visual effects render first
2625
- }
2626
- // For files that haven't been analyzed, request analysis
2627
- else if (d.data.type === 'file' && !d.data.analyzed) {
2628
- // Only analyze files of selected languages
2629
- const fileLanguage = this.detectLanguage(d.data.path);
2630
- if (!selectedLanguages.includes(fileLanguage) && fileLanguage !== 'unknown') {
2631
- this.showNotification(`Skipping ${d.data.name} - ${fileLanguage} not selected`, 'warning');
2632
- return;
2633
- }
2634
-
2635
- // Add pulsing animation immediately
2636
- this.addLoadingPulse(d);
2637
-
2638
- // Mark as loading immediately
2639
- d.data.analyzed = 'loading';
2640
-
2641
- // Ensure path is absolute or relative to working directory
2642
- const fullPath = this.ensureFullPath(d.data.path);
2643
-
2644
- // Delay the socket request to ensure visual effects are rendered
2645
- setTimeout(() => {
2646
-
2647
- if (this.socket) {
2648
- this.socket.emit('code:analyze:file', {
2649
- path: fullPath
2650
- });
2651
-
2652
- this.updateBreadcrumb(`Analyzing ${d.data.name}...`, 'info');
2653
- this.showNotification(`Analyzing: ${d.data.name}`, 'info');
2654
- }
2655
- }, 100); // 100ms delay to ensure visual effects render first
2656
- }
2657
- // Toggle children visibility for already loaded nodes
2658
- else if (d.data.type === 'directory' && d.data.loaded === true) {
2659
- // Directory is loaded, toggle expansion
2660
- if (d.children) {
2661
- // Collapse - hide children
2662
- d._children = d.children;
2663
- d.children = null;
2664
- d.data.expanded = false;
2665
- } else if (d._children) {
2666
- // Expand - show children
2667
- d.children = d._children;
2668
- d._children = null;
2669
- d.data.expanded = true;
2670
- } else if (d.data.children && d.data.children.length > 0) {
2671
- // Children exist in data but not in D3 node, recreate hierarchy
2672
- this.root = d3.hierarchy(this.treeData);
2673
- const updatedD3Node = this.findD3NodeByPath(d.data.path);
2674
- if (updatedD3Node) {
2675
- updatedD3Node.children = updatedD3Node._children || updatedD3Node.children;
2676
- updatedD3Node._children = null;
2677
- updatedD3Node.data.expanded = true;
2678
- }
2679
- }
2680
- this.update(this.root);
2681
- }
2682
- // Also handle other nodes that might have children
2683
- else if (d.children || d._children) {
2684
- if (d.children) {
2685
- d._children = d.children;
2686
- d.children = null;
2687
- d.data.expanded = false;
2688
- } else {
2689
- d.children = d._children;
2690
- d._children = null;
2691
- d.data.expanded = true;
2692
- }
2693
- this.update(d);
2694
- } else {
2695
- }
2696
-
2697
- // Update selection
2698
- this.selectedNode = d;
2699
- try {
2700
- this.highlightNode(d);
2701
- } catch (error) {
2702
- console.error('[CodeTree] ERROR during highlightNode:', error);
2703
- }
2704
-
2705
- }
2706
-
2707
- /**
2708
- * Ensure path is absolute or relative to working directory
2709
- */
2710
- ensureFullPath(path) {
2711
- console.log('🔗 ensureFullPath called with:', path);
2712
-
2713
- if (!path) return path;
2714
-
2715
- // If already absolute, return as is
2716
- if (path.startsWith('/')) {
2717
- console.log(' → Already absolute, returning:', path);
2718
- return path;
2719
- }
2720
-
2721
- // Get working directory
2722
- const workingDir = this.getWorkingDirectory();
2723
- console.log(' → Working directory:', workingDir);
2724
-
2725
- if (!workingDir) {
2726
- console.log(' → No working directory, returning original:', path);
2727
- return path;
2728
- }
2729
-
2730
- // Special handling for root path
2731
- if (path === '.') {
2732
- console.log(' → Root path detected, returning working dir:', workingDir);
2733
- return workingDir;
2734
- }
2735
-
2736
- // If path equals working directory, return as is
2737
- if (path === workingDir) {
2738
- console.log(' → Path equals working directory, returning:', workingDir);
2739
- return workingDir;
2740
- }
2741
-
2742
- // Combine working directory with relative path
2743
- const result = `${workingDir}/${path}`.replace(/\/+/g, '/');
2744
- console.log(' → Combining with working dir, result:', result);
2745
- return result;
2746
- }
2747
-
2748
- /**
2749
- * Highlight selected node
2750
- */
2751
- highlightNode(node) {
2752
- // Remove previous highlights
2753
- this.treeGroup.selectAll('circle.node-circle')
2754
- .style('stroke-width', 2)
2755
- .classed('selected', false);
2756
-
2757
- // Highlight selected node
2758
- this.treeGroup.selectAll('circle.node-circle')
2759
- .filter(d => d === node)
2760
- .style('stroke-width', 4)
2761
- .classed('selected', true);
2762
- }
2763
-
2764
- /**
2765
- * Create diagonal path for links
2766
- */
2767
- diagonal(s, d) {
2768
- return `M ${s.y} ${s.x}
2769
- C ${(s.y + d.y) / 2} ${s.x},
2770
- ${(s.y + d.y) / 2} ${d.x},
2771
- ${d.y} ${d.x}`;
2772
- }
2773
-
2774
- /**
2775
- * Create radial diagonal path for links
2776
- */
2777
- radialDiagonal(s, d) {
2778
- const path = d3.linkRadial()
2779
- .angle(d => d.x)
2780
- .radius(d => d.y);
2781
- return path({source: s, target: d});
2782
- }
2783
-
2784
- /**
2785
- * Get node color based on type and complexity
2786
- */
2787
- getNodeColor(d) {
2788
- const type = d.data.type;
2789
- const complexity = d.data.complexity || 1;
2790
-
2791
- // Base colors by type
2792
- const baseColors = {
2793
- 'root': '#6B7280',
2794
- 'directory': '#3B82F6',
2795
- 'file': '#10B981',
2796
- 'module': '#8B5CF6',
2797
- 'class': '#F59E0B',
2798
- 'function': '#EF4444',
2799
- 'method': '#EC4899'
2800
- };
2801
-
2802
- const baseColor = baseColors[type] || '#6B7280';
2803
-
2804
- // Adjust brightness based on complexity (higher complexity = darker)
2805
- if (complexity > 10) {
2806
- return d3.color(baseColor).darker(0.5);
2807
- } else if (complexity > 5) {
2808
- return d3.color(baseColor).darker(0.25);
2809
- }
2810
-
2811
- return baseColor;
2812
- }
2813
-
2814
- /**
2815
- * Get node stroke color
2816
- */
2817
- getNodeStrokeColor(d) {
2818
- if (d.data.loaded === 'loading' || d.data.analyzed === 'loading') {
2819
- return '#FCD34D'; // Yellow for loading
2820
- }
2821
- if (d.data.type === 'directory' && !d.data.loaded) {
2822
- return '#94A3B8'; // Gray for unloaded
2823
- }
2824
- if (d.data.type === 'file' && !d.data.analyzed) {
2825
- return '#CBD5E1'; // Light gray for unanalyzed
2826
- }
2827
- return this.getNodeColor(d);
2828
- }
2829
-
2830
- /**
2831
- * Get icon for node type
2832
- */
2833
- getNodeIcon(d) {
2834
- const icons = {
2835
- 'root': '📦',
2836
- 'directory': '📁',
2837
- 'file': '📄',
2838
- 'module': '📦',
2839
- 'class': 'C',
2840
- 'function': 'ƒ',
2841
- 'method': 'm'
2842
- };
2843
- return icons[d.data.type] || '•';
2844
- }
2845
-
2846
- /**
2847
- * Show tooltip on hover
2848
- */
2849
- showTooltip(event, d) {
2850
- if (!this.tooltip) return;
2851
-
2852
- const info = [];
2853
- info.push(`<strong>${d.data.name}</strong>`);
2854
- info.push(`Type: ${d.data.type}`);
2855
-
2856
- if (d.data.language) {
2857
- info.push(`Language: ${d.data.language}`);
2858
- }
2859
- if (d.data.complexity) {
2860
- info.push(`Complexity: ${d.data.complexity}`);
2861
- }
2862
- if (d.data.lines) {
2863
- info.push(`Lines: ${d.data.lines}`);
2864
- }
2865
- if (d.data.path) {
2866
- info.push(`Path: ${d.data.path}`);
2867
- }
2868
-
2869
- // Special messages for lazy-loaded nodes
2870
- if (d.data.type === 'directory' && !d.data.loaded) {
2871
- info.push('<em>Click to explore contents</em>');
2872
- } else if (d.data.type === 'file' && !d.data.analyzed) {
2873
- info.push('<em>Click to analyze file</em>');
2874
- }
2875
-
2876
- this.tooltip.transition()
2877
- .duration(200)
2878
- .style('opacity', .9);
2879
-
2880
- this.tooltip.html(info.join('<br>'))
2881
- .style('left', (event.pageX + 10) + 'px')
2882
- .style('top', (event.pageY - 28) + 'px');
2883
- }
2884
-
2885
- /**
2886
- * Hide tooltip
2887
- */
2888
- hideTooltip() {
2889
- if (!this.tooltip) return;
2890
-
2891
- this.tooltip.transition()
2892
- .duration(500)
2893
- .style('opacity', 0);
2894
- }
2895
-
2896
- /**
2897
- * Filter tree based on language and search
2898
- */
2899
- filterTree() {
2900
- if (!this.root) return;
2901
-
2902
- // Apply filters
2903
- this.root.descendants().forEach(d => {
2904
- d.data._hidden = false;
2905
-
2906
- // Language filter
2907
- if (this.languageFilter !== 'all') {
2908
- if (d.data.type === 'file' && d.data.language !== this.languageFilter) {
2909
- d.data._hidden = true;
2910
- }
2911
- }
2912
-
2913
- // Search filter
2914
- if (this.searchTerm) {
2915
- if (!d.data.name.toLowerCase().includes(this.searchTerm)) {
2916
- d.data._hidden = true;
2917
- }
2918
- }
2919
- });
2920
-
2921
- // Update display
2922
- this.update(this.root);
2923
- }
2924
-
2925
- /**
2926
- * Expand all nodes in the tree
2927
- */
2928
- expandAll() {
2929
- if (!this.root) return;
2930
-
2931
- // Recursively expand all nodes
2932
- const expandRecursive = (node) => {
2933
- if (node._children) {
2934
- node.children = node._children;
2935
- node._children = null;
2936
- }
2937
- if (node.children) {
2938
- node.children.forEach(expandRecursive);
2939
- }
2940
- };
2941
-
2942
- expandRecursive(this.root);
2943
- this.update(this.root);
2944
- this.showNotification('All nodes expanded', 'info');
2945
- }
2946
-
2947
- /**
2948
- * Collapse all nodes in the tree
2949
- */
2950
- collapseAll() {
2951
- if (!this.root) return;
2952
-
2953
- // Recursively collapse all nodes except root
2954
- const collapseRecursive = (node) => {
2955
- if (node.children) {
2956
- node._children = node.children;
2957
- node.children = null;
2958
- }
2959
- if (node._children) {
2960
- node._children.forEach(collapseRecursive);
2961
- }
2962
- };
2963
-
2964
- this.root.children?.forEach(collapseRecursive);
2965
- this.update(this.root);
2966
- this.showNotification('All nodes collapsed', 'info');
2967
- }
2968
-
2969
- /**
2970
- * Reset zoom to fit the tree
2971
- */
2972
- resetZoom() {
2973
- // DISABLED: All zoom reset operations have been disabled to prevent tree centering/movement
2974
- // The tree should remain stationary and not center/move when interacting with nodes
2975
- console.log('[CodeTree] resetZoom called but disabled - no zoom reset will occur');
2976
- this.showNotification('Zoom reset disabled - tree remains stationary', 'info');
2977
- return;
2978
- }
2979
-
2980
- /**
2981
- * REMOVED: Focus on a specific node and its subtree
2982
- * This method has been completely disabled to prevent unwanted tree movement.
2983
- * All centering and focus functionality has been removed from the code tree.
2984
- */
2985
- focusOnNode(node) {
2986
- // Method disabled - no focusing/centering operations will be performed
2987
- console.log('[CodeTree] focusOnNode called but disabled - no focusing will occur');
2988
- return;
2989
-
2990
- // Update breadcrumb with focused path
2991
- const path = this.getNodePath(node);
2992
- this.updateBreadcrumb(`Focused: ${path}`, 'info');
2993
- }
2994
-
2995
- /**
2996
- * Get the full path of a node
2997
- */
2998
- getNodePath(node) {
2999
- const path = [];
3000
- let current = node;
3001
- while (current) {
3002
- if (current.data && current.data.name) {
3003
- path.unshift(current.data.name);
3004
- }
3005
- current = current.parent;
3006
- }
3007
- return path.join(' / ');
3008
- }
3009
-
3010
- /**
3011
- * Toggle legend visibility
3012
- */
3013
- toggleLegend() {
3014
- const legend = document.getElementById('tree-legend');
3015
- if (legend) {
3016
- if (legend.style.display === 'none') {
3017
- legend.style.display = 'block';
3018
- } else {
3019
- legend.style.display = 'none';
3020
- }
3021
- }
3022
- }
3023
-
3024
- /**
3025
- * Get the current working directory
3026
- */
3027
- getWorkingDirectory() {
3028
- // Try to get from dashboard's working directory manager
3029
- if (window.dashboard && window.dashboard.workingDirectoryManager) {
3030
- return window.dashboard.workingDirectoryManager.getCurrentWorkingDir();
3031
- }
3032
-
3033
- // Fallback to checking the DOM element
3034
- const workingDirPath = document.getElementById('working-dir-path');
3035
- if (workingDirPath) {
3036
- const pathText = workingDirPath.textContent.trim();
3037
- if (pathText && pathText !== 'Loading...' && pathText !== 'Not selected') {
3038
- return pathText;
3039
- }
3040
- }
3041
-
3042
- return null;
3043
- }
3044
-
3045
- /**
3046
- * Show a message when no working directory is selected
3047
- */
3048
- showNoWorkingDirectoryMessage() {
3049
- const container = document.getElementById('code-tree-container');
3050
- if (!container) return;
3051
-
3052
- // Remove any existing message
3053
- this.removeNoWorkingDirectoryMessage();
3054
-
3055
- // Hide loading if shown
3056
- this.hideLoading();
3057
-
3058
- // Create message element
3059
- const messageDiv = document.createElement('div');
3060
- messageDiv.id = 'no-working-dir-message';
3061
- messageDiv.className = 'no-working-dir-message';
3062
- messageDiv.innerHTML = `
3063
- <div class="message-icon">📁</div>
3064
- <h3>No Working Directory Selected</h3>
3065
- <p>Please select a working directory from the top menu to analyze code.</p>
3066
- <button id="select-working-dir-btn" class="btn btn-primary">
3067
- Select Working Directory
3068
- </button>
3069
- `;
3070
- messageDiv.style.cssText = `
3071
- text-align: center;
3072
- padding: 40px;
3073
- color: #666;
3074
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
3075
- `;
3076
-
3077
- // Style the message elements
3078
- const messageIcon = messageDiv.querySelector('.message-icon');
3079
- if (messageIcon) {
3080
- messageIcon.style.cssText = 'font-size: 48px; margin-bottom: 16px; opacity: 0.5;';
3081
- }
3082
-
3083
- const h3 = messageDiv.querySelector('h3');
3084
- if (h3) {
3085
- h3.style.cssText = 'margin: 16px 0; color: #333; font-size: 20px;';
3086
- }
3087
-
3088
- const p = messageDiv.querySelector('p');
3089
- if (p) {
3090
- p.style.cssText = 'margin: 16px 0; color: #666; font-size: 14px;';
3091
- }
3092
-
3093
- const button = messageDiv.querySelector('button');
3094
- if (button) {
3095
- button.style.cssText = `
3096
- margin-top: 20px;
3097
- padding: 10px 20px;
3098
- background: #3b82f6;
3099
- color: white;
3100
- border: none;
3101
- border-radius: 6px;
3102
- cursor: pointer;
3103
- font-size: 14px;
3104
- transition: background 0.2s;
3105
- `;
3106
- button.addEventListener('mouseenter', () => {
3107
- button.style.background = '#2563eb';
3108
- });
3109
- button.addEventListener('mouseleave', () => {
3110
- button.style.background = '#3b82f6';
3111
- });
3112
- button.addEventListener('click', () => {
3113
- // Trigger working directory selection
3114
- const changeDirBtn = document.getElementById('change-dir-btn');
3115
- if (changeDirBtn) {
3116
- changeDirBtn.click();
3117
- } else if (window.dashboard && window.dashboard.workingDirectoryManager) {
3118
- window.dashboard.workingDirectoryManager.showChangeDirDialog();
3119
- }
3120
- });
3121
- }
3122
-
3123
- container.appendChild(messageDiv);
3124
-
3125
- // Update breadcrumb
3126
- this.updateBreadcrumb('Please select a working directory', 'warning');
3127
- }
3128
-
3129
- /**
3130
- * Remove the no working directory message
3131
- */
3132
- removeNoWorkingDirectoryMessage() {
3133
- const message = document.getElementById('no-working-dir-message');
3134
- if (message) {
3135
- message.remove();
3136
- }
3137
- }
3138
-
3139
- /**
3140
- * Export tree data
3141
- */
3142
- exportTree() {
3143
- const exportData = {
3144
- timestamp: new Date().toISOString(),
3145
- workingDirectory: this.getWorkingDirectory(),
3146
- stats: this.stats,
3147
- tree: this.treeData
3148
- };
3149
-
3150
- const blob = new Blob([JSON.stringify(exportData, null, 2)],
3151
- {type: 'application/json'});
3152
- const url = URL.createObjectURL(blob);
3153
- const link = document.createElement('a');
3154
- link.href = url;
3155
- link.download = `code-tree-${Date.now()}.json`;
3156
- link.click();
3157
- URL.revokeObjectURL(url);
3158
-
3159
- this.showNotification('Tree exported successfully', 'success');
3160
- }
3161
-
3162
- /**
3163
- * Update activity ticker with real-time messages
3164
- */
3165
- updateActivityTicker(message, type = 'info') {
3166
- const breadcrumb = document.getElementById('breadcrumb-content');
3167
- if (breadcrumb) {
3168
- // Add spinning icon for loading states
3169
- const icon = type === 'info' && message.includes('...') ? '⟳ ' : '';
3170
- breadcrumb.innerHTML = `${icon}${message}`;
3171
- breadcrumb.className = `breadcrumb-${type}`;
3172
- }
3173
- }
3174
-
3175
- /**
3176
- * Update ticker message
3177
- */
3178
- updateTicker(message, type = 'info') {
3179
- const ticker = document.getElementById('code-tree-ticker');
3180
- if (ticker) {
3181
- ticker.textContent = message;
3182
- ticker.className = `ticker ticker-${type}`;
3183
-
3184
- // Auto-hide after 5 seconds for non-error messages
3185
- if (type !== 'error') {
3186
- setTimeout(() => {
3187
- ticker.style.opacity = '0';
3188
- setTimeout(() => {
3189
- ticker.style.opacity = '1';
3190
- ticker.textContent = '';
3191
- }, 300);
3192
- }, 5000);
3193
- }
3194
- }
3195
- }
3196
- }
3197
-
3198
- // Export for use in other modules
3199
- window.CodeTree = CodeTree;
3200
-
3201
- // Auto-initialize when DOM is ready
3202
- document.addEventListener('DOMContentLoaded', () => {
3203
- // Check if we're on a page with code tree container
3204
- if (document.getElementById('code-tree-container')) {
3205
- window.codeTree = new CodeTree();
3206
-
3207
- // Listen for tab changes to initialize when code tab is selected
3208
- document.addEventListener('click', (e) => {
3209
- if (e.target.matches('[data-tab="code"]')) {
3210
- setTimeout(() => {
3211
- if (window.codeTree && !window.codeTree.initialized) {
3212
- window.codeTree.initialize();
3213
- } else if (window.codeTree) {
3214
- window.codeTree.renderWhenVisible();
3215
- }
3216
- }, 100);
3217
- }
3218
- });
3219
- }
3220
- });/* Cache buster: 1756393851 */