claude-mpm 5.1.9__py3-none-any.whl → 5.4.48__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 (248) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/__init__.py +4 -0
  3. claude_mpm/agents/BASE_AGENT.md +164 -0
  4. claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +1 -1
  5. claude_mpm/agents/MEMORY.md +1 -1
  6. claude_mpm/agents/PM_INSTRUCTIONS.md +843 -900
  7. claude_mpm/agents/WORKFLOW.md +5 -254
  8. claude_mpm/agents/agent_loader.py +13 -44
  9. claude_mpm/agents/base_agent.json +1 -1
  10. claude_mpm/agents/frontmatter_validator.py +2 -2
  11. claude_mpm/agents/templates/circuit-breakers.md +138 -1
  12. claude_mpm/cli/__main__.py +4 -0
  13. claude_mpm/cli/chrome_devtools_installer.py +175 -0
  14. claude_mpm/cli/commands/agent_state_manager.py +18 -27
  15. claude_mpm/cli/commands/agents.py +9 -40
  16. claude_mpm/cli/commands/auto_configure.py +210 -25
  17. claude_mpm/cli/commands/config.py +88 -2
  18. claude_mpm/cli/commands/configure.py +1098 -159
  19. claude_mpm/cli/commands/configure_agent_display.py +25 -6
  20. claude_mpm/cli/commands/mpm_init/core.py +225 -46
  21. claude_mpm/cli/commands/mpm_init/knowledge_extractor.py +481 -0
  22. claude_mpm/cli/commands/mpm_init/prompts.py +280 -0
  23. claude_mpm/cli/commands/postmortem.py +1 -1
  24. claude_mpm/cli/commands/profile.py +277 -0
  25. claude_mpm/cli/commands/skills.py +218 -197
  26. claude_mpm/cli/commands/summarize.py +413 -0
  27. claude_mpm/cli/executor.py +21 -3
  28. claude_mpm/cli/interactive/agent_wizard.py +2 -2
  29. claude_mpm/cli/parsers/agents_parser.py +0 -9
  30. claude_mpm/cli/parsers/auto_configure_parser.py +0 -138
  31. claude_mpm/cli/parsers/base_parser.py +12 -0
  32. claude_mpm/cli/parsers/config_parser.py +153 -83
  33. claude_mpm/cli/parsers/profile_parser.py +148 -0
  34. claude_mpm/cli/parsers/skills_parser.py +0 -5
  35. claude_mpm/cli/startup.py +876 -149
  36. claude_mpm/commands/mpm-config.md +28 -0
  37. claude_mpm/commands/mpm-doctor.md +9 -22
  38. claude_mpm/commands/mpm-help.md +5 -287
  39. claude_mpm/commands/mpm-init.md +81 -507
  40. claude_mpm/commands/mpm-monitor.md +15 -402
  41. claude_mpm/commands/mpm-organize.md +120 -0
  42. claude_mpm/commands/mpm-postmortem.md +6 -108
  43. claude_mpm/commands/mpm-session-resume.md +12 -363
  44. claude_mpm/commands/mpm-status.md +5 -69
  45. claude_mpm/commands/mpm-ticket-view.md +52 -495
  46. claude_mpm/commands/mpm-version.md +5 -107
  47. claude_mpm/config/agent_sources.py +27 -0
  48. claude_mpm/core/config.py +2 -4
  49. claude_mpm/core/framework/formatters/content_formatter.py +3 -13
  50. claude_mpm/core/framework/loaders/agent_loader.py +8 -5
  51. claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
  52. claude_mpm/core/framework_loader.py +4 -2
  53. claude_mpm/core/logger.py +13 -0
  54. claude_mpm/core/optimized_startup.py +59 -0
  55. claude_mpm/core/shared/config_loader.py +1 -1
  56. claude_mpm/core/socketio_pool.py +3 -3
  57. claude_mpm/core/unified_agent_registry.py +5 -15
  58. claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
  59. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +1 -0
  60. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +1 -0
  61. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +1 -0
  62. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +1 -0
  63. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CWc5urbQ.js +1 -0
  64. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +2 -0
  65. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DjhvlsAc.js +1 -0
  66. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/N4qtv3Hx.js +2 -0
  67. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uj46x2Wr.js +1 -0
  68. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +2 -0
  69. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +1 -0
  70. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/0.CAGBuiOw.js +1 -0
  71. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +1 -0
  72. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +10 -0
  73. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -0
  74. claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
  75. claude_mpm/dashboard/static/svelte-build/index.html +36 -0
  76. claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
  77. claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
  78. claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
  79. claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
  80. claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
  81. claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
  82. claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
  83. claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
  84. claude_mpm/hooks/claude_hooks/correlation_manager.py +60 -0
  85. claude_mpm/hooks/claude_hooks/event_handlers.py +211 -78
  86. claude_mpm/hooks/claude_hooks/hook_handler.py +155 -1
  87. claude_mpm/hooks/claude_hooks/installer.py +33 -10
  88. claude_mpm/hooks/claude_hooks/memory_integration.py +26 -9
  89. claude_mpm/hooks/claude_hooks/response_tracking.py +2 -3
  90. claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
  91. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
  92. claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
  93. claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
  94. claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
  95. claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
  96. claude_mpm/hooks/claude_hooks/services/connection_manager.py +30 -6
  97. claude_mpm/hooks/kuzu_memory_hook.py +5 -5
  98. claude_mpm/hooks/memory_integration_hook.py +46 -1
  99. claude_mpm/init.py +63 -19
  100. claude_mpm/models/git_repository.py +3 -3
  101. claude_mpm/scripts/claude-hook-handler.sh +58 -18
  102. claude_mpm/scripts/launch_monitor.py +93 -13
  103. claude_mpm/services/agents/agent_builder.py +3 -3
  104. claude_mpm/services/agents/agent_recommendation_service.py +278 -0
  105. claude_mpm/services/agents/agent_review_service.py +280 -0
  106. claude_mpm/services/agents/cache_git_manager.py +6 -6
  107. claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
  108. claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -5
  109. claude_mpm/services/agents/deployment/agent_format_converter.py +23 -13
  110. claude_mpm/services/agents/deployment/agent_template_builder.py +32 -20
  111. claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
  112. claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
  113. claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
  114. claude_mpm/services/agents/deployment/multi_source_deployment_service.py +247 -35
  115. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +392 -87
  116. claude_mpm/services/agents/git_source_manager.py +53 -4
  117. claude_mpm/services/agents/loading/base_agent_manager.py +1 -13
  118. claude_mpm/services/agents/recommender.py +5 -3
  119. claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
  120. claude_mpm/services/agents/sources/git_source_sync_service.py +120 -7
  121. claude_mpm/services/agents/startup_sync.py +22 -2
  122. claude_mpm/services/agents/toolchain_detector.py +10 -6
  123. claude_mpm/services/analysis/__init__.py +11 -1
  124. claude_mpm/services/analysis/clone_detector.py +1030 -0
  125. claude_mpm/services/command_deployment_service.py +81 -10
  126. claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
  127. claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
  128. claude_mpm/services/event_bus/config.py +3 -1
  129. claude_mpm/services/git/git_operations_service.py +101 -16
  130. claude_mpm/services/monitor/daemon.py +9 -2
  131. claude_mpm/services/monitor/daemon_manager.py +39 -3
  132. claude_mpm/services/monitor/management/lifecycle.py +8 -1
  133. claude_mpm/services/monitor/server.py +698 -22
  134. claude_mpm/services/pm_skills_deployer.py +711 -0
  135. claude_mpm/services/profile_manager.py +331 -0
  136. claude_mpm/services/self_upgrade_service.py +120 -12
  137. claude_mpm/services/skills/__init__.py +3 -0
  138. claude_mpm/services/skills/git_skill_source_manager.py +130 -2
  139. claude_mpm/services/skills/selective_skill_deployer.py +704 -0
  140. claude_mpm/services/skills/skill_to_agent_mapper.py +406 -0
  141. claude_mpm/services/skills_deployer.py +127 -9
  142. claude_mpm/services/socketio/dashboard_server.py +1 -0
  143. claude_mpm/services/socketio/event_normalizer.py +51 -6
  144. claude_mpm/services/socketio/server/core.py +386 -108
  145. claude_mpm/services/version_control/git_operations.py +103 -0
  146. claude_mpm/skills/skill_manager.py +92 -3
  147. claude_mpm/utils/agent_dependency_loader.py +14 -2
  148. claude_mpm/utils/agent_filters.py +17 -44
  149. claude_mpm/utils/migration.py +4 -4
  150. claude_mpm/utils/robust_installer.py +47 -3
  151. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/METADATA +53 -87
  152. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/RECORD +157 -197
  153. claude_mpm-5.4.48.dist-info/entry_points.txt +5 -0
  154. claude_mpm-5.4.48.dist-info/licenses/LICENSE +94 -0
  155. claude_mpm-5.4.48.dist-info/licenses/LICENSE-FAQ.md +153 -0
  156. claude_mpm/agents/BASE_AGENT_TEMPLATE.md +0 -292
  157. claude_mpm/agents/BASE_DOCUMENTATION.md +0 -53
  158. claude_mpm/agents/BASE_OPS.md +0 -219
  159. claude_mpm/agents/BASE_PM.md +0 -480
  160. claude_mpm/agents/BASE_PROMPT_ENGINEER.md +0 -787
  161. claude_mpm/agents/BASE_QA.md +0 -167
  162. claude_mpm/agents/BASE_RESEARCH.md +0 -53
  163. claude_mpm/agents/base_agent_loader.py +0 -601
  164. claude_mpm/cli/commands/agents_detect.py +0 -380
  165. claude_mpm/cli/commands/agents_recommend.py +0 -309
  166. claude_mpm/cli/ticket_cli.py +0 -35
  167. claude_mpm/commands/mpm-agents-auto-configure.md +0 -278
  168. claude_mpm/commands/mpm-agents-detect.md +0 -177
  169. claude_mpm/commands/mpm-agents-list.md +0 -131
  170. claude_mpm/commands/mpm-agents-recommend.md +0 -223
  171. claude_mpm/commands/mpm-config-view.md +0 -150
  172. claude_mpm/commands/mpm-ticket-organize.md +0 -304
  173. claude_mpm/dashboard/analysis_runner.py +0 -455
  174. claude_mpm/dashboard/index.html +0 -13
  175. claude_mpm/dashboard/open_dashboard.py +0 -66
  176. claude_mpm/dashboard/static/css/activity.css +0 -1958
  177. claude_mpm/dashboard/static/css/connection-status.css +0 -370
  178. claude_mpm/dashboard/static/css/dashboard.css +0 -4701
  179. claude_mpm/dashboard/static/js/components/activity-tree.js +0 -1871
  180. claude_mpm/dashboard/static/js/components/agent-hierarchy.js +0 -777
  181. claude_mpm/dashboard/static/js/components/agent-inference.js +0 -956
  182. claude_mpm/dashboard/static/js/components/build-tracker.js +0 -333
  183. claude_mpm/dashboard/static/js/components/code-simple.js +0 -857
  184. claude_mpm/dashboard/static/js/components/connection-debug.js +0 -654
  185. claude_mpm/dashboard/static/js/components/diff-viewer.js +0 -891
  186. claude_mpm/dashboard/static/js/components/event-processor.js +0 -542
  187. claude_mpm/dashboard/static/js/components/event-viewer.js +0 -1155
  188. claude_mpm/dashboard/static/js/components/export-manager.js +0 -368
  189. claude_mpm/dashboard/static/js/components/file-change-tracker.js +0 -443
  190. claude_mpm/dashboard/static/js/components/file-change-viewer.js +0 -690
  191. claude_mpm/dashboard/static/js/components/file-tool-tracker.js +0 -724
  192. claude_mpm/dashboard/static/js/components/file-viewer.js +0 -580
  193. claude_mpm/dashboard/static/js/components/hud-library-loader.js +0 -211
  194. claude_mpm/dashboard/static/js/components/hud-manager.js +0 -671
  195. claude_mpm/dashboard/static/js/components/hud-visualizer.js +0 -1718
  196. claude_mpm/dashboard/static/js/components/module-viewer.js +0 -2764
  197. claude_mpm/dashboard/static/js/components/session-manager.js +0 -579
  198. claude_mpm/dashboard/static/js/components/socket-manager.js +0 -368
  199. claude_mpm/dashboard/static/js/components/ui-state-manager.js +0 -749
  200. claude_mpm/dashboard/static/js/components/unified-data-viewer.js +0 -1824
  201. claude_mpm/dashboard/static/js/components/working-directory.js +0 -920
  202. claude_mpm/dashboard/static/js/connection-manager.js +0 -536
  203. claude_mpm/dashboard/static/js/dashboard.js +0 -1914
  204. claude_mpm/dashboard/static/js/extension-error-handler.js +0 -164
  205. claude_mpm/dashboard/static/js/socket-client.js +0 -1474
  206. claude_mpm/dashboard/static/js/tab-isolation-fix.js +0 -185
  207. claude_mpm/dashboard/static/socket.io.min.js +0 -7
  208. claude_mpm/dashboard/static/socket.io.v4.8.1.backup.js +0 -7
  209. claude_mpm/dashboard/templates/code_simple.html +0 -153
  210. claude_mpm/dashboard/templates/index.html +0 -606
  211. claude_mpm/dashboard/test_dashboard.html +0 -372
  212. claude_mpm/scripts/mcp_server.py +0 -75
  213. claude_mpm/scripts/mcp_wrapper.py +0 -39
  214. claude_mpm/services/mcp_gateway/__init__.py +0 -159
  215. claude_mpm/services/mcp_gateway/auto_configure.py +0 -369
  216. claude_mpm/services/mcp_gateway/config/__init__.py +0 -17
  217. claude_mpm/services/mcp_gateway/config/config_loader.py +0 -296
  218. claude_mpm/services/mcp_gateway/config/config_schema.py +0 -243
  219. claude_mpm/services/mcp_gateway/config/configuration.py +0 -429
  220. claude_mpm/services/mcp_gateway/core/__init__.py +0 -43
  221. claude_mpm/services/mcp_gateway/core/base.py +0 -312
  222. claude_mpm/services/mcp_gateway/core/exceptions.py +0 -253
  223. claude_mpm/services/mcp_gateway/core/interfaces.py +0 -443
  224. claude_mpm/services/mcp_gateway/core/process_pool.py +0 -977
  225. claude_mpm/services/mcp_gateway/core/singleton_manager.py +0 -315
  226. claude_mpm/services/mcp_gateway/core/startup_verification.py +0 -316
  227. claude_mpm/services/mcp_gateway/main.py +0 -589
  228. claude_mpm/services/mcp_gateway/registry/__init__.py +0 -12
  229. claude_mpm/services/mcp_gateway/registry/service_registry.py +0 -412
  230. claude_mpm/services/mcp_gateway/registry/tool_registry.py +0 -489
  231. claude_mpm/services/mcp_gateway/server/__init__.py +0 -15
  232. claude_mpm/services/mcp_gateway/server/mcp_gateway.py +0 -414
  233. claude_mpm/services/mcp_gateway/server/stdio_handler.py +0 -372
  234. claude_mpm/services/mcp_gateway/server/stdio_server.py +0 -712
  235. claude_mpm/services/mcp_gateway/tools/__init__.py +0 -36
  236. claude_mpm/services/mcp_gateway/tools/base_adapter.py +0 -485
  237. claude_mpm/services/mcp_gateway/tools/document_summarizer.py +0 -789
  238. claude_mpm/services/mcp_gateway/tools/external_mcp_services.py +0 -654
  239. claude_mpm/services/mcp_gateway/tools/health_check_tool.py +0 -456
  240. claude_mpm/services/mcp_gateway/tools/hello_world.py +0 -551
  241. claude_mpm/services/mcp_gateway/tools/kuzu_memory_service.py +0 -555
  242. claude_mpm/services/mcp_gateway/utils/__init__.py +0 -14
  243. claude_mpm/services/mcp_gateway/utils/package_version_checker.py +0 -160
  244. claude_mpm/services/mcp_gateway/utils/update_preferences.py +0 -170
  245. claude_mpm-5.1.9.dist-info/entry_points.txt +0 -10
  246. claude_mpm-5.1.9.dist-info/licenses/LICENSE +0 -21
  247. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/WHEEL +0 -0
  248. {claude_mpm-5.1.9.dist-info → claude_mpm-5.4.48.dist-info}/top_level.txt +0 -0
@@ -1,724 +0,0 @@
1
- /**
2
- * File and Tool Tracker Module
3
- *
4
- * Tracks file operations and tool calls by pairing pre/post events and maintaining
5
- * organized collections for the files and tools tabs. Provides analysis of
6
- * tool execution patterns and file operation history.
7
- *
8
- * WHY: Extracted from main dashboard to isolate complex event pairing logic
9
- * that groups related events into meaningful operations. This provides better
10
- * maintainability for the intricate logic of matching tool events with their results.
11
- *
12
- * DESIGN DECISION: Uses intelligent correlation strategy for tool calls that:
13
- * - Separates pre_tool and post_tool events first
14
- * - Correlates based on temporal proximity, parameter similarity, and context
15
- * - Handles timing differences between pre/post events (tools can run for minutes)
16
- * - Prevents duplicate tool entries by ensuring each tool call appears once
17
- * - Supports both paired and orphaned events for comprehensive tracking
18
- */
19
- class FileToolTracker {
20
- constructor(agentInference, workingDirectoryManager) {
21
- this.agentInference = agentInference;
22
- this.workingDirectoryManager = workingDirectoryManager;
23
-
24
- // File tracking for files tab
25
- this.fileOperations = new Map(); // Map of file paths to operations
26
-
27
- // Tool call tracking for tools tab
28
- this.toolCalls = new Map(); // Map of tool call keys to paired pre/post events
29
-
30
- console.log('File-tool tracker initialized');
31
- }
32
-
33
- /**
34
- * Update file operations from events
35
- * @param {Array} events - Events to process
36
- */
37
- updateFileOperations(events) {
38
- // Clear existing data
39
- this.fileOperations.clear();
40
-
41
- console.log('updateFileOperations - processing', events.length, 'events');
42
-
43
- // Group events by session and timestamp to match pre/post pairs
44
- const eventPairs = new Map(); // Key: session_id + timestamp + tool_name
45
- let fileOperationCount = 0;
46
-
47
- // First pass: collect all tool events and group them
48
- events.forEach((event, index) => {
49
- const isFileOp = this.isFileOperation(event);
50
- if (isFileOp) fileOperationCount++;
51
-
52
- if (index < 5) { // Debug first 5 events with more detail
53
- console.log(`Event ${index}:`, {
54
- type: event.type,
55
- subtype: event.subtype,
56
- tool_name: event.tool_name,
57
- tool_parameters: event.tool_parameters,
58
- isFileOp: isFileOp
59
- });
60
- }
61
-
62
- if (isFileOp) {
63
- const toolName = event.tool_name || (event.data && event.data.tool_name);
64
- const sessionId = event.session_id || (event.data && event.data.session_id) || 'unknown';
65
- const eventKey = `${sessionId}_${toolName}_${Math.floor(new Date(event.timestamp).getTime() / 1000)}`; // Group by second
66
-
67
- if (!eventPairs.has(eventKey)) {
68
- eventPairs.set(eventKey, {
69
- pre_event: null,
70
- post_event: null,
71
- tool_name: toolName,
72
- session_id: sessionId
73
- });
74
- }
75
-
76
- const pair = eventPairs.get(eventKey);
77
- if (event.subtype === 'pre_tool' || (event.type === 'hook' && event.subtype && !event.subtype.includes('post'))) {
78
- pair.pre_event = event;
79
- } else if (event.subtype === 'post_tool' || (event.subtype && event.subtype.includes('post'))) {
80
- pair.post_event = event;
81
- } else {
82
- // For events without clear pre/post distinction, treat as both
83
- pair.pre_event = event;
84
- pair.post_event = event;
85
- }
86
- }
87
- });
88
-
89
- console.log('updateFileOperations - found', fileOperationCount, 'file operations in', eventPairs.size, 'event pairs');
90
-
91
- // Second pass: extract file paths and operations from paired events
92
- eventPairs.forEach((pair, key) => {
93
- const filePath = this.extractFilePathFromPair(pair);
94
-
95
- if (filePath) {
96
- console.log('File operation detected for:', filePath, 'from pair:', key);
97
-
98
- if (!this.fileOperations.has(filePath)) {
99
- this.fileOperations.set(filePath, {
100
- path: filePath,
101
- operations: [],
102
- lastOperation: null
103
- });
104
- }
105
-
106
- const fileData = this.fileOperations.get(filePath);
107
- const operation = this.getFileOperationFromPair(pair);
108
- const timestamp = pair.post_event?.timestamp || pair.pre_event?.timestamp;
109
-
110
- const agentInfo = this.extractAgentFromPair(pair);
111
- const workingDirectory = this.workingDirectoryManager.extractWorkingDirectoryFromPair(pair);
112
-
113
- fileData.operations.push({
114
- operation: operation,
115
- timestamp: timestamp,
116
- agent: agentInfo.name,
117
- confidence: agentInfo.confidence,
118
- sessionId: pair.session_id,
119
- details: this.getFileOperationDetailsFromPair(pair),
120
- workingDirectory: workingDirectory
121
- });
122
- fileData.lastOperation = timestamp;
123
- } else {
124
- console.log('No file path found for pair:', key, pair);
125
- }
126
- });
127
-
128
- console.log('updateFileOperations - final result:', this.fileOperations.size, 'file operations');
129
- if (this.fileOperations.size > 0) {
130
- console.log('File operations map:', Array.from(this.fileOperations.entries()));
131
- }
132
- }
133
-
134
- /**
135
- * Update tool calls from events - pairs pre/post tool events into complete tool calls
136
- * @param {Array} events - Events to process
137
- */
138
- updateToolCalls(events) {
139
- // Clear existing data
140
- this.toolCalls.clear();
141
-
142
- console.log('updateToolCalls - processing', events.length, 'events');
143
-
144
- // Improved correlation strategy: collect events first, then correlate intelligently
145
- const preToolEvents = [];
146
- const postToolEvents = [];
147
- let toolOperationCount = 0;
148
-
149
- // First pass: separate pre_tool and post_tool events
150
- events.forEach((event, index) => {
151
- const isToolOp = this.isToolOperation(event);
152
- if (isToolOp) toolOperationCount++;
153
-
154
- if (index < 5) { // Debug first 5 events with more detail
155
- console.log(`Tool Event ${index}:`, {
156
- type: event.type,
157
- subtype: event.subtype,
158
- tool_name: event.tool_name,
159
- tool_parameters: event.tool_parameters,
160
- isToolOp: isToolOp
161
- });
162
- }
163
-
164
- if (isToolOp) {
165
- if (event.subtype === 'pre_tool' || (event.type === 'hook' && event.subtype && !event.subtype.includes('post'))) {
166
- preToolEvents.push(event);
167
- } else if (event.subtype === 'post_tool' || (event.subtype && event.subtype.includes('post'))) {
168
- postToolEvents.push(event);
169
- } else {
170
- // For events without clear pre/post distinction, treat as standalone
171
- preToolEvents.push(event);
172
- postToolEvents.push(event);
173
- }
174
- }
175
- });
176
-
177
- console.log('updateToolCalls - found', toolOperationCount, 'tool operations:', preToolEvents.length, 'pre_tool,', postToolEvents.length, 'post_tool');
178
-
179
- // Second pass: correlate pre_tool events with post_tool events
180
- const toolCallPairs = new Map();
181
- const usedPostEvents = new Set();
182
-
183
- preToolEvents.forEach((preEvent, preIndex) => {
184
- const toolName = preEvent.tool_name || (preEvent.data && preEvent.data.tool_name);
185
- const sessionId = preEvent.session_id || (preEvent.data && preEvent.data.session_id) || 'unknown';
186
- const preTimestamp = new Date(preEvent.timestamp).getTime();
187
-
188
- // Create a base pair for this pre_tool event
189
- const pairKey = `${sessionId}_${toolName}_${preIndex}_${preTimestamp}`;
190
- const pair = {
191
- pre_event: preEvent,
192
- post_event: null,
193
- tool_name: toolName,
194
- session_id: sessionId,
195
- operation_type: preEvent.operation_type || 'tool_execution',
196
- timestamp: preEvent.timestamp,
197
- duration_ms: null,
198
- success: null,
199
- exit_code: null,
200
- result_summary: null,
201
- agent_type: null,
202
- agent_confidence: null
203
- };
204
-
205
- // Get agent info from pre_event
206
- const agentInfo = this.extractAgentFromEvent(preEvent);
207
- pair.agent_type = agentInfo.name;
208
- pair.agent_confidence = agentInfo.confidence;
209
-
210
- // Try to find matching post_tool event
211
- let bestMatchIndex = -1;
212
- let bestMatchScore = -1;
213
- const maxTimeDiffMs = 300000; // 5 minutes max time difference
214
-
215
- postToolEvents.forEach((postEvent, postIndex) => {
216
- // Skip already used post events
217
- if (usedPostEvents.has(postIndex)) return;
218
-
219
- // Must match tool name and session
220
- const postToolName = postEvent.tool_name || (postEvent.data && postEvent.data.tool_name);
221
- const postSessionId = postEvent.session_id || (postEvent.data && postEvent.data.session_id) || 'unknown';
222
- if (postToolName !== toolName || postSessionId !== sessionId) return;
223
-
224
- const postTimestamp = new Date(postEvent.timestamp).getTime();
225
- const timeDiff = Math.abs(postTimestamp - preTimestamp);
226
-
227
- // Post event should generally come after pre event (or very close)
228
- const isTemporallyValid = postTimestamp >= preTimestamp - 1000; // Allow 1s clock skew
229
-
230
- // Calculate correlation score (higher is better)
231
- let score = 0;
232
- if (isTemporallyValid && timeDiff <= maxTimeDiffMs) {
233
- score = 1000 - (timeDiff / 1000); // Prefer closer timestamps
234
-
235
- // Boost score for parameter similarity (if available)
236
- if (this.compareToolParameters(preEvent, postEvent)) {
237
- score += 500;
238
- }
239
-
240
- // Boost score for same working directory
241
- if (preEvent.working_directory && postEvent.working_directory &&
242
- preEvent.working_directory === postEvent.working_directory) {
243
- score += 100;
244
- }
245
- }
246
-
247
- if (score > bestMatchScore) {
248
- bestMatchScore = score;
249
- bestMatchIndex = postIndex;
250
- }
251
- });
252
-
253
- // If we found a good match, pair them
254
- if (bestMatchIndex >= 0 && bestMatchScore > 0) {
255
- const postEvent = postToolEvents[bestMatchIndex];
256
- pair.post_event = postEvent;
257
- pair.duration_ms = postEvent.duration_ms;
258
- pair.success = postEvent.success;
259
- pair.exit_code = postEvent.exit_code;
260
- pair.result_summary = postEvent.result_summary;
261
-
262
- usedPostEvents.add(bestMatchIndex);
263
- console.log(`Paired pre_tool ${toolName} at ${preEvent.timestamp} with post_tool at ${postEvent.timestamp} (score: ${bestMatchScore})`);
264
- } else {
265
- console.log(`No matching post_tool found for ${toolName} at ${preEvent.timestamp} (still running or orphaned)`);
266
- }
267
-
268
- toolCallPairs.set(pairKey, pair);
269
- });
270
-
271
- // Third pass: handle any orphaned post_tool events (shouldn't happen but be safe)
272
- postToolEvents.forEach((postEvent, postIndex) => {
273
- if (usedPostEvents.has(postIndex)) return;
274
-
275
- const toolName = postEvent.tool_name || (postEvent.data && postEvent.data.tool_name);
276
- console.log('Orphaned post_tool event found:', toolName, 'at', postEvent.timestamp);
277
-
278
- const sessionId = postEvent.session_id || (postEvent.data && postEvent.data.session_id) || 'unknown';
279
- const postTimestamp = new Date(postEvent.timestamp).getTime();
280
-
281
- const pairKey = `orphaned_${sessionId}_${toolName}_${postIndex}_${postTimestamp}`;
282
- const pair = {
283
- pre_event: null,
284
- post_event: postEvent,
285
- tool_name: toolName,
286
- session_id: sessionId,
287
- operation_type: 'tool_execution',
288
- timestamp: postEvent.timestamp,
289
- duration_ms: postEvent.duration_ms,
290
- success: postEvent.success,
291
- exit_code: postEvent.exit_code,
292
- result_summary: postEvent.result_summary,
293
- agent_type: null,
294
- agent_confidence: null
295
- };
296
-
297
- const agentInfo = this.extractAgentFromEvent(postEvent);
298
- pair.agent_type = agentInfo.name;
299
- pair.agent_confidence = agentInfo.confidence;
300
-
301
- toolCallPairs.set(pairKey, pair);
302
- });
303
-
304
- // Store the correlated tool calls
305
- this.toolCalls = toolCallPairs;
306
-
307
- console.log('updateToolCalls - final result:', this.toolCalls.size, 'tool calls');
308
- if (this.toolCalls.size > 0) {
309
- console.log('Tool calls map keys:', Array.from(this.toolCalls.keys()));
310
- }
311
- }
312
-
313
- /**
314
- * Check if event is a tool operation
315
- * @param {Object} event - Event to check
316
- * @returns {boolean} - True if tool operation
317
- */
318
- isToolOperation(event) {
319
- // Tool operations have tool_name - be more inclusive about event types
320
- // Check both top-level and data.tool_name for compatibility
321
- const hasToolName = event.tool_name || (event.data && event.data.tool_name);
322
-
323
- // Accept multiple event types that might contain tool operations
324
- const validEventTypes = ['hook', 'tool_use', 'tool', 'agent', 'response'];
325
- const isValidEventType = validEventTypes.includes(event.type) ||
326
- (event.type && event.type.includes('tool'));
327
-
328
- // Check for tool-related subtypes or any indication this is a tool operation
329
- const isToolSubtype = event.subtype === 'pre_tool' ||
330
- event.subtype === 'post_tool' ||
331
- (event.subtype && typeof event.subtype === 'string' && event.subtype.includes('tool')) ||
332
- event.type === 'tool_use' ||
333
- event.type === 'tool';
334
-
335
- // If it has a tool_name and either a valid event type or tool subtype, it's a tool operation
336
- return hasToolName && (isValidEventType || isToolSubtype);
337
- }
338
-
339
- /**
340
- * Check if event is a file operation
341
- * @param {Object} event - Event to check
342
- * @returns {boolean} - True if file operation
343
- */
344
- isFileOperation(event) {
345
- // File operations are events with file-related tools - be more inclusive
346
- // Check both top-level and data for tool_name
347
- let toolName = event.tool_name || (event.data && event.data.tool_name) || '';
348
-
349
- // If no tool name, not a file operation
350
- if (!toolName) {
351
- return false;
352
- }
353
-
354
- toolName = toolName.toLowerCase();
355
-
356
- // Check case-insensitively since tool names can come in different cases
357
- const fileTools = ['read', 'write', 'edit', 'grep', 'multiedit', 'glob', 'ls', 'bash', 'notebookedit'];
358
-
359
- // Get tool parameters from either location
360
- const toolParams = event.tool_parameters || (event.data && event.data.tool_parameters);
361
-
362
- // Also check if Bash commands involve file operations
363
- if (toolName === 'bash' && toolParams) {
364
- const command = toolParams.command || '';
365
- // Check for common file operations in bash commands
366
- if (command.match(/\b(cat|less|more|head|tail|touch|mv|cp|rm|mkdir|ls|find)\b/)) {
367
- return true;
368
- }
369
- }
370
-
371
- // If it's a file tool, it's a file operation regardless of event type
372
- return fileTools.includes(toolName);
373
- }
374
-
375
- /**
376
- * Extract file path from event
377
- * @param {Object} event - Event to extract from
378
- * @returns {string|null} - File path or null
379
- */
380
- extractFilePath(event) {
381
- // Debug logging for file path extraction
382
- const fileTools = ['Read', 'Write', 'Edit', 'MultiEdit', 'NotebookEdit'];
383
- const toolName = event.tool_name || (event.data && event.data.tool_name);
384
-
385
- if (fileTools.includes(toolName)) {
386
- console.log('Extracting file path from event:', {
387
- tool_name: toolName,
388
- has_tool_parameters_top: !!event.tool_parameters,
389
- has_tool_parameters_data: !!(event.data && event.data.tool_parameters),
390
- tool_parameters: event.tool_parameters,
391
- data_tool_parameters: event.data?.tool_parameters
392
- });
393
- }
394
-
395
- // Try various locations where file path might be stored
396
- // Check top-level tool_parameters first (after transformation)
397
- if (event.tool_parameters?.file_path) return event.tool_parameters.file_path;
398
- if (event.tool_parameters?.path) return event.tool_parameters.path;
399
- if (event.tool_parameters?.notebook_path) return event.tool_parameters.notebook_path;
400
-
401
- // Check in data object as fallback
402
- if (event.data?.tool_parameters?.file_path) return event.data.tool_parameters.file_path;
403
- if (event.data?.tool_parameters?.path) return event.data.tool_parameters.path;
404
- if (event.data?.tool_parameters?.notebook_path) return event.data.tool_parameters.notebook_path;
405
- if (event.file_path) return event.file_path;
406
- if (event.path) return event.path;
407
-
408
- // For Glob tool, use the pattern as a pseudo-path
409
- if (event.tool_name?.toLowerCase() === 'glob' && event.tool_parameters?.pattern) {
410
- return `[glob] ${event.tool_parameters.pattern}`;
411
- }
412
-
413
- // For Bash commands, try to extract file paths from the command
414
- if (event.tool_name?.toLowerCase() === 'bash' && event.tool_parameters?.command) {
415
- const command = event.tool_parameters.command;
416
-
417
- // Enhanced regex to handle commands with flags
418
- // Match command followed by optional flags (starting with -) and then the file path
419
- // Patterns to handle:
420
- // 1. tail -50 /path/to/file
421
- // 2. head -n 100 /path/to/file
422
- // 3. cat /path/to/file
423
- // 4. grep -r "pattern" /path/to/file
424
- const fileMatch = command.match(/(?:cat|less|more|head|tail|touch|mv|cp|rm|mkdir|ls|find|echo.*>|sed|awk|grep)(?:\s+-[a-zA-Z0-9]+)*(?:\s+[0-9]+)*\s+([^\s;|&]+)/);
425
-
426
- // If first match might be a flag, try a more specific pattern
427
- if (fileMatch && fileMatch[1]) {
428
- const possiblePath = fileMatch[1];
429
- // Check if it's actually a flag (starts with -)
430
- if (possiblePath.startsWith('-')) {
431
- // Try alternative pattern that skips all flags
432
- const altMatch = command.match(/(?:cat|less|more|head|tail|touch|mv|cp|rm|mkdir|ls|find|echo.*>|sed|awk|grep)(?:\s+-[^\s]+)*\s+([^-][^\s;|&]*)/);
433
- if (altMatch && altMatch[1]) {
434
- return altMatch[1];
435
- }
436
- }
437
- return possiblePath;
438
- }
439
- }
440
-
441
- return null;
442
- }
443
-
444
- /**
445
- * Extract file path from event pair
446
- * @param {Object} pair - Event pair object
447
- * @returns {string|null} - File path or null
448
- */
449
- extractFilePathFromPair(pair) {
450
- // Try pre_event first, then post_event
451
- let filePath = null;
452
-
453
- if (pair.pre_event) {
454
- filePath = this.extractFilePath(pair.pre_event);
455
- }
456
-
457
- if (!filePath && pair.post_event) {
458
- filePath = this.extractFilePath(pair.post_event);
459
- }
460
-
461
- return filePath;
462
- }
463
-
464
- /**
465
- * Get file operation type from event
466
- * @param {Object} event - Event to analyze
467
- * @returns {string} - Operation type
468
- */
469
- getFileOperation(event) {
470
- if (!event.tool_name) return 'unknown';
471
-
472
- const toolName = event.tool_name.toLowerCase();
473
- switch (toolName) {
474
- case 'read': return 'read';
475
- case 'write': return 'write';
476
- case 'edit': return 'edit';
477
- case 'multiedit': return 'edit';
478
- case 'notebookedit': return 'edit';
479
- case 'grep': return 'search';
480
- case 'glob': return 'search';
481
- case 'ls': return 'list';
482
- case 'bash':
483
- // Check bash command for file operation type
484
- const command = event.tool_parameters?.command || '';
485
- if (command.match(/\b(cat|less|more|head|tail)\b/)) return 'read';
486
- if (command.match(/\b(touch|echo.*>|tee)\b/)) return 'write';
487
- if (command.match(/\b(sed|awk)\b/)) return 'edit';
488
- if (command.match(/\b(grep|find)\b/)) return 'search';
489
- if (command.match(/\b(ls|dir)\b/)) return 'list';
490
- if (command.match(/\b(mv|cp)\b/)) return 'copy/move';
491
- if (command.match(/\b(rm|rmdir)\b/)) return 'delete';
492
- if (command.match(/\b(mkdir)\b/)) return 'create';
493
- return 'bash';
494
- default: return toolName;
495
- }
496
- }
497
-
498
- /**
499
- * Get file operation from event pair
500
- * @param {Object} pair - Event pair object
501
- * @returns {string} - Operation type
502
- */
503
- getFileOperationFromPair(pair) {
504
- // Try pre_event first, then post_event
505
- if (pair.pre_event) {
506
- return this.getFileOperation(pair.pre_event);
507
- }
508
-
509
- if (pair.post_event) {
510
- return this.getFileOperation(pair.post_event);
511
- }
512
-
513
- return 'unknown';
514
- }
515
-
516
- /**
517
- * Extract agent information from event pair
518
- * @param {Object} pair - Event pair object
519
- * @returns {Object} - Agent info with name and confidence
520
- */
521
- extractAgentFromPair(pair) {
522
- // Try to get agent info from inference system first
523
- const event = pair.pre_event || pair.post_event;
524
- if (event && this.agentInference) {
525
- const inference = this.agentInference.getInferredAgentForEvent(event);
526
- if (inference) {
527
- return {
528
- name: inference.agentName || 'Unknown',
529
- confidence: inference.confidence || 'unknown'
530
- };
531
- }
532
- }
533
-
534
- // Fallback to direct event properties
535
- const agentName = event?.agent_type || event?.subagent_type ||
536
- pair.pre_event?.agent_type || pair.post_event?.agent_type || 'PM';
537
-
538
- return {
539
- name: agentName,
540
- confidence: 'direct'
541
- };
542
- }
543
-
544
- /**
545
- * Get detailed operation information from event pair
546
- * @param {Object} pair - Event pair object
547
- * @returns {Object} - Operation details
548
- */
549
- getFileOperationDetailsFromPair(pair) {
550
- const details = {};
551
-
552
- // Extract details from pre_event (parameters)
553
- if (pair.pre_event) {
554
- const params = pair.pre_event.tool_parameters || pair.pre_event.data?.tool_parameters || {};
555
- details.parameters = params;
556
- details.tool_input = pair.pre_event.tool_input;
557
- }
558
-
559
- // Extract details from post_event (results)
560
- if (pair.post_event) {
561
- details.result = pair.post_event.result;
562
- details.success = pair.post_event.success;
563
- details.error = pair.post_event.error;
564
- details.exit_code = pair.post_event.exit_code;
565
- details.duration_ms = pair.post_event.duration_ms;
566
- }
567
-
568
- return details;
569
- }
570
-
571
- /**
572
- * Get file operations map
573
- * @returns {Map} - File operations map
574
- */
575
- getFileOperations() {
576
- return this.fileOperations;
577
- }
578
-
579
- /**
580
- * Get tool calls map
581
- * @returns {Map} - Tool calls map
582
- */
583
- getToolCalls() {
584
- return this.toolCalls;
585
- }
586
-
587
- /**
588
- * Get tool calls as array for unique instance view
589
- * Each entry represents a unique tool call instance
590
- * @returns {Array} - Array of [key, toolCall] pairs
591
- */
592
- getToolCallsArray() {
593
- return Array.from(this.toolCalls.entries());
594
- }
595
-
596
- /**
597
- * Get file operations for a specific file
598
- * @param {string} filePath - File path
599
- * @returns {Object|null} - File operations data or null
600
- */
601
- getFileOperationsForFile(filePath) {
602
- return this.fileOperations.get(filePath) || null;
603
- }
604
-
605
- /**
606
- * Get tool call by key
607
- * @param {string} key - Tool call key
608
- * @returns {Object|null} - Tool call data or null
609
- */
610
- getToolCall(key) {
611
- return this.toolCalls.get(key) || null;
612
- }
613
-
614
- /**
615
- * Clear all tracking data
616
- */
617
- clear() {
618
- this.fileOperations.clear();
619
- this.toolCalls.clear();
620
- console.log('File-tool tracker cleared');
621
- }
622
-
623
- /**
624
- * Get statistics about tracked operations
625
- * @returns {Object} - Statistics
626
- */
627
- getStatistics() {
628
- return {
629
- fileOperations: this.fileOperations.size,
630
- toolCalls: this.toolCalls.size,
631
- uniqueFiles: this.fileOperations.size,
632
- totalFileOperations: Array.from(this.fileOperations.values())
633
- .reduce((sum, data) => sum + data.operations.length, 0)
634
- };
635
- }
636
-
637
- /**
638
- * Compare tool parameters between pre_tool and post_tool events
639
- * to determine if they're likely from the same tool call
640
- * @param {Object} preEvent - Pre-tool event
641
- * @param {Object} postEvent - Post-tool event
642
- * @returns {boolean} - True if parameters suggest same tool call
643
- */
644
- compareToolParameters(preEvent, postEvent) {
645
- // Extract parameters from both events
646
- const preParams = preEvent.tool_parameters || preEvent.data?.tool_parameters || {};
647
- const postParams = postEvent.tool_parameters || postEvent.data?.tool_parameters || {};
648
-
649
- // If no parameters in either event, can't compare meaningfully
650
- if (Object.keys(preParams).length === 0 && Object.keys(postParams).length === 0) {
651
- return false; // No boost for empty parameters
652
- }
653
-
654
- // Compare key parameters that are likely to be the same
655
- const importantParams = ['file_path', 'path', 'pattern', 'command', 'notebook_path'];
656
- let matchedParams = 0;
657
- let totalComparableParams = 0;
658
-
659
- importantParams.forEach(param => {
660
- const preValue = preParams[param];
661
- const postValue = postParams[param];
662
-
663
- if (preValue !== undefined || postValue !== undefined) {
664
- totalComparableParams++;
665
- if (preValue === postValue) {
666
- matchedParams++;
667
- }
668
- }
669
- });
670
-
671
- // If we found comparable parameters, check if most match
672
- if (totalComparableParams > 0) {
673
- return (matchedParams / totalComparableParams) >= 0.8; // 80% parameter match threshold
674
- }
675
-
676
- // If no important parameters to compare, check if the parameter structure is similar
677
- const preKeys = Object.keys(preParams).sort();
678
- const postKeys = Object.keys(postParams).sort();
679
-
680
- if (preKeys.length === 0 && postKeys.length === 0) {
681
- return false;
682
- }
683
-
684
- // Simple structural similarity check
685
- if (preKeys.length === postKeys.length) {
686
- const keyMatches = preKeys.filter(key => postKeys.includes(key)).length;
687
- return keyMatches >= Math.max(1, preKeys.length * 0.5); // At least 50% key overlap
688
- }
689
-
690
- return false;
691
- }
692
-
693
- /**
694
- * Extract agent information from event using inference system
695
- * @param {Object} event - Event to extract agent from
696
- * @returns {Object} - Agent info with name and confidence
697
- */
698
- extractAgentFromEvent(event) {
699
- if (this.agentInference) {
700
- const inference = this.agentInference.getInferredAgentForEvent(event);
701
- if (inference) {
702
- return {
703
- name: inference.agentName || 'Unknown',
704
- confidence: inference.confidence || 'unknown'
705
- };
706
- }
707
- }
708
-
709
- // Fallback to direct event properties
710
- const agentName = event.agent_type || event.subagent_type ||
711
- event.data?.agent_type || event.data?.subagent_type || 'PM';
712
-
713
- return {
714
- name: agentName,
715
- confidence: 'direct'
716
- };
717
- }
718
- }
719
- // ES6 Module export
720
- export { FileToolTracker };
721
- export default FileToolTracker;
722
-
723
- // Make FileToolTracker globally available for dist/dashboard.js
724
- window.FileToolTracker = FileToolTracker;