dev3000 0.0.156 → 0.0.159

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (516) hide show
  1. package/README.md +3 -1
  2. package/dist/cdp-monitor.d.ts +2 -3
  3. package/dist/cdp-monitor.d.ts.map +1 -1
  4. package/dist/cdp-monitor.js +5 -11
  5. package/dist/cdp-monitor.js.map +1 -1
  6. package/dist/cli.js +182 -161
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/cloud-check-pr.js +2 -1
  9. package/dist/commands/cloud-check-pr.js.map +1 -1
  10. package/dist/commands/cloud-fix.d.ts +1 -2
  11. package/dist/commands/cloud-fix.d.ts.map +1 -1
  12. package/dist/commands/cloud-fix.js +49 -685
  13. package/dist/commands/cloud-fix.js.map +1 -1
  14. package/dist/components/PackageSelector.d.ts +3 -2
  15. package/dist/components/PackageSelector.d.ts.map +1 -1
  16. package/dist/components/PackageSelector.js +9 -4
  17. package/dist/components/PackageSelector.js.map +1 -1
  18. package/dist/dev-environment.d.ts +9 -40
  19. package/dist/dev-environment.d.ts.map +1 -1
  20. package/dist/dev-environment.js +245 -542
  21. package/dist/dev-environment.js.map +1 -1
  22. package/dist/skills/d3k/SKILL.md +35 -2
  23. package/dist/src/tui-interface-impl.tsx +2 -12
  24. package/dist/tui-interface-impl.d.ts +0 -1
  25. package/dist/tui-interface-impl.d.ts.map +1 -1
  26. package/dist/tui-interface-impl.js +3 -7
  27. package/dist/tui-interface-impl.js.map +1 -1
  28. package/dist/tui-interface-opentui.d.ts +0 -1
  29. package/dist/tui-interface-opentui.d.ts.map +1 -1
  30. package/dist/tui-interface-opentui.js +26 -10
  31. package/dist/tui-interface-opentui.js.map +1 -1
  32. package/dist/tui-interface.d.ts +0 -1
  33. package/dist/tui-interface.d.ts.map +1 -1
  34. package/dist/tui-interface.js.map +1 -1
  35. package/dist/utils/agent-browser.d.ts.map +1 -1
  36. package/dist/utils/agent-browser.js +18 -13
  37. package/dist/utils/agent-browser.js.map +1 -1
  38. package/dist/utils/agent-selection.d.ts +1 -0
  39. package/dist/utils/agent-selection.d.ts.map +1 -1
  40. package/dist/utils/agent-selection.js +26 -1
  41. package/dist/utils/agent-selection.js.map +1 -1
  42. package/dist/utils/d3k-dir.d.ts +3 -0
  43. package/dist/utils/d3k-dir.d.ts.map +1 -0
  44. package/dist/utils/d3k-dir.js +12 -0
  45. package/dist/utils/d3k-dir.js.map +1 -0
  46. package/dist/utils/skill-installer.d.ts +7 -7
  47. package/dist/utils/skill-installer.d.ts.map +1 -1
  48. package/dist/utils/skill-installer.js +131 -18
  49. package/dist/utils/skill-installer.js.map +1 -1
  50. package/dist/utils/user-config.d.ts +0 -1
  51. package/dist/utils/user-config.d.ts.map +1 -1
  52. package/dist/utils/user-config.js +0 -13
  53. package/dist/utils/user-config.js.map +1 -1
  54. package/package.json +9 -29
  55. package/src/tui-interface-impl.tsx +2 -12
  56. package/dist/commands/cloud-fix-workflow.d.ts +0 -16
  57. package/dist/commands/cloud-fix-workflow.d.ts.map +0 -1
  58. package/dist/commands/cloud-fix-workflow.js +0 -153
  59. package/dist/commands/cloud-fix-workflow.js.map +0 -1
  60. package/dist/commands/restart.d.ts +0 -8
  61. package/dist/commands/restart.d.ts.map +0 -1
  62. package/dist/commands/restart.js +0 -92
  63. package/dist/commands/restart.js.map +0 -1
  64. package/dist/utils/mcp-configs.d.ts +0 -6
  65. package/dist/utils/mcp-configs.d.ts.map +0 -1
  66. package/dist/utils/mcp-configs.js +0 -54
  67. package/dist/utils/mcp-configs.js.map +0 -1
  68. package/mcp-server/.next/BUILD_ID +0 -1
  69. package/mcp-server/.next/app-path-routes-manifest.json +0 -22
  70. package/mcp-server/.next/build/chunks/[root-of-the-server]__25374c4f._.js +0 -500
  71. package/mcp-server/.next/build/chunks/[root-of-the-server]__25374c4f._.js.map +0 -11
  72. package/mcp-server/.next/build/chunks/[root-of-the-server]__6e020478._.js +0 -441
  73. package/mcp-server/.next/build/chunks/[root-of-the-server]__6e020478._.js.map +0 -7
  74. package/mcp-server/.next/build/chunks/[root-of-the-server]__c438ef56._.js +0 -205
  75. package/mcp-server/.next/build/chunks/[root-of-the-server]__c438ef56._.js.map +0 -8
  76. package/mcp-server/.next/build/chunks/[root-of-the-server]__c7ae8543._.js +0 -500
  77. package/mcp-server/.next/build/chunks/[root-of-the-server]__c7ae8543._.js.map +0 -11
  78. package/mcp-server/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_7988927e._.js +0 -13
  79. package/mcp-server/.next/build/chunks/[turbopack-node]_transforms_postcss_ts_7988927e._.js.map +0 -5
  80. package/mcp-server/.next/build/chunks/[turbopack-node]_transforms_webpack-loaders_ts_1efa112f._.js +0 -12
  81. package/mcp-server/.next/build/chunks/[turbopack-node]_transforms_webpack-loaders_ts_1efa112f._.js.map +0 -5
  82. package/mcp-server/.next/build/chunks/[turbopack]_runtime.js +0 -795
  83. package/mcp-server/.next/build/chunks/[turbopack]_runtime.js.map +0 -10
  84. package/mcp-server/.next/build/chunks/node_modules__bun_19755e4f._.js +0 -6758
  85. package/mcp-server/.next/build/chunks/node_modules__bun_19755e4f._.js.map +0 -47
  86. package/mcp-server/.next/build/package.json +0 -1
  87. package/mcp-server/.next/build/postcss.js +0 -6
  88. package/mcp-server/.next/build/postcss.js.map +0 -5
  89. package/mcp-server/.next/build/webpack-loaders.js +0 -6
  90. package/mcp-server/.next/build/webpack-loaders.js.map +0 -5
  91. package/mcp-server/.next/build-manifest.json +0 -20
  92. package/mcp-server/.next/export-marker.json +0 -6
  93. package/mcp-server/.next/fallback-build-manifest.json +0 -12
  94. package/mcp-server/.next/images-manifest.json +0 -66
  95. package/mcp-server/.next/next-minimal-server.js.nft.json +0 -1
  96. package/mcp-server/.next/next-server.js.nft.json +0 -1
  97. package/mcp-server/.next/package.json +0 -1
  98. package/mcp-server/.next/prerender-manifest.json +0 -85
  99. package/mcp-server/.next/required-server-files.js +0 -164
  100. package/mcp-server/.next/required-server-files.json +0 -164
  101. package/mcp-server/.next/routes-manifest.json +0 -171
  102. package/mcp-server/.next/server/app/_global-error/page/app-paths-manifest.json +0 -3
  103. package/mcp-server/.next/server/app/_global-error/page/build-manifest.json +0 -17
  104. package/mcp-server/.next/server/app/_global-error/page/next-font-manifest.json +0 -6
  105. package/mcp-server/.next/server/app/_global-error/page/react-loadable-manifest.json +0 -1
  106. package/mcp-server/.next/server/app/_global-error/page/server-reference-manifest.json +0 -4
  107. package/mcp-server/.next/server/app/_global-error/page.js +0 -10
  108. package/mcp-server/.next/server/app/_global-error/page.js.map +0 -5
  109. package/mcp-server/.next/server/app/_global-error/page.js.nft.json +0 -1
  110. package/mcp-server/.next/server/app/_global-error/page_client-reference-manifest.js +0 -2
  111. package/mcp-server/.next/server/app/_global-error.html +0 -2
  112. package/mcp-server/.next/server/app/_global-error.meta +0 -15
  113. package/mcp-server/.next/server/app/_global-error.rsc +0 -12
  114. package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +0 -5
  115. package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +0 -12
  116. package/mcp-server/.next/server/app/_global-error.segments/_head.segment.rsc +0 -5
  117. package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +0 -4
  118. package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +0 -1
  119. package/mcp-server/.next/server/app/_not-found/page/app-paths-manifest.json +0 -3
  120. package/mcp-server/.next/server/app/_not-found/page/build-manifest.json +0 -17
  121. package/mcp-server/.next/server/app/_not-found/page/next-font-manifest.json +0 -6
  122. package/mcp-server/.next/server/app/_not-found/page/react-loadable-manifest.json +0 -1
  123. package/mcp-server/.next/server/app/_not-found/page/server-reference-manifest.json +0 -4
  124. package/mcp-server/.next/server/app/_not-found/page.js +0 -13
  125. package/mcp-server/.next/server/app/_not-found/page.js.map +0 -5
  126. package/mcp-server/.next/server/app/_not-found/page.js.nft.json +0 -1
  127. package/mcp-server/.next/server/app/_not-found/page_client-reference-manifest.js +0 -2
  128. package/mcp-server/.next/server/app/_not-found.html +0 -1
  129. package/mcp-server/.next/server/app/_not-found.meta +0 -16
  130. package/mcp-server/.next/server/app/_not-found.rsc +0 -14
  131. package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +0 -14
  132. package/mcp-server/.next/server/app/_not-found.segments/_head.segment.rsc +0 -5
  133. package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +0 -6
  134. package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +0 -5
  135. package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +0 -4
  136. package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +0 -2
  137. package/mcp-server/.next/server/app/api/jank/[session]/route/app-paths-manifest.json +0 -3
  138. package/mcp-server/.next/server/app/api/jank/[session]/route/build-manifest.json +0 -11
  139. package/mcp-server/.next/server/app/api/jank/[session]/route/server-reference-manifest.json +0 -4
  140. package/mcp-server/.next/server/app/api/jank/[session]/route.js +0 -10
  141. package/mcp-server/.next/server/app/api/jank/[session]/route.js.map +0 -5
  142. package/mcp-server/.next/server/app/api/jank/[session]/route.js.nft.json +0 -1
  143. package/mcp-server/.next/server/app/api/jank/[session]/route_client-reference-manifest.js +0 -2
  144. package/mcp-server/.next/server/app/api/logs/append/route/app-paths-manifest.json +0 -3
  145. package/mcp-server/.next/server/app/api/logs/append/route/build-manifest.json +0 -11
  146. package/mcp-server/.next/server/app/api/logs/append/route/server-reference-manifest.json +0 -4
  147. package/mcp-server/.next/server/app/api/logs/append/route.js +0 -6
  148. package/mcp-server/.next/server/app/api/logs/append/route.js.map +0 -5
  149. package/mcp-server/.next/server/app/api/logs/append/route.js.nft.json +0 -1
  150. package/mcp-server/.next/server/app/api/logs/append/route_client-reference-manifest.js +0 -2
  151. package/mcp-server/.next/server/app/api/logs/head/route/app-paths-manifest.json +0 -3
  152. package/mcp-server/.next/server/app/api/logs/head/route/build-manifest.json +0 -11
  153. package/mcp-server/.next/server/app/api/logs/head/route/server-reference-manifest.json +0 -4
  154. package/mcp-server/.next/server/app/api/logs/head/route.js +0 -6
  155. package/mcp-server/.next/server/app/api/logs/head/route.js.map +0 -5
  156. package/mcp-server/.next/server/app/api/logs/head/route.js.nft.json +0 -1
  157. package/mcp-server/.next/server/app/api/logs/head/route_client-reference-manifest.js +0 -2
  158. package/mcp-server/.next/server/app/api/logs/list/route/app-paths-manifest.json +0 -3
  159. package/mcp-server/.next/server/app/api/logs/list/route/build-manifest.json +0 -11
  160. package/mcp-server/.next/server/app/api/logs/list/route/server-reference-manifest.json +0 -4
  161. package/mcp-server/.next/server/app/api/logs/list/route.js +0 -6
  162. package/mcp-server/.next/server/app/api/logs/list/route.js.map +0 -5
  163. package/mcp-server/.next/server/app/api/logs/list/route.js.nft.json +0 -1
  164. package/mcp-server/.next/server/app/api/logs/list/route_client-reference-manifest.js +0 -2
  165. package/mcp-server/.next/server/app/api/logs/rotate/route/app-paths-manifest.json +0 -3
  166. package/mcp-server/.next/server/app/api/logs/rotate/route/build-manifest.json +0 -11
  167. package/mcp-server/.next/server/app/api/logs/rotate/route/server-reference-manifest.json +0 -4
  168. package/mcp-server/.next/server/app/api/logs/rotate/route.js +0 -8
  169. package/mcp-server/.next/server/app/api/logs/rotate/route.js.map +0 -5
  170. package/mcp-server/.next/server/app/api/logs/rotate/route.js.nft.json +0 -1
  171. package/mcp-server/.next/server/app/api/logs/rotate/route_client-reference-manifest.js +0 -2
  172. package/mcp-server/.next/server/app/api/logs/stream/route/app-paths-manifest.json +0 -3
  173. package/mcp-server/.next/server/app/api/logs/stream/route/build-manifest.json +0 -11
  174. package/mcp-server/.next/server/app/api/logs/stream/route/server-reference-manifest.json +0 -4
  175. package/mcp-server/.next/server/app/api/logs/stream/route.js +0 -6
  176. package/mcp-server/.next/server/app/api/logs/stream/route.js.map +0 -5
  177. package/mcp-server/.next/server/app/api/logs/stream/route.js.nft.json +0 -1
  178. package/mcp-server/.next/server/app/api/logs/stream/route_client-reference-manifest.js +0 -2
  179. package/mcp-server/.next/server/app/api/logs/tail/route/app-paths-manifest.json +0 -3
  180. package/mcp-server/.next/server/app/api/logs/tail/route/build-manifest.json +0 -11
  181. package/mcp-server/.next/server/app/api/logs/tail/route/server-reference-manifest.json +0 -4
  182. package/mcp-server/.next/server/app/api/logs/tail/route.js +0 -6
  183. package/mcp-server/.next/server/app/api/logs/tail/route.js.map +0 -5
  184. package/mcp-server/.next/server/app/api/logs/tail/route.js.nft.json +0 -1
  185. package/mcp-server/.next/server/app/api/logs/tail/route_client-reference-manifest.js +0 -2
  186. package/mcp-server/.next/server/app/api/orchestrator/route/app-paths-manifest.json +0 -3
  187. package/mcp-server/.next/server/app/api/orchestrator/route/build-manifest.json +0 -11
  188. package/mcp-server/.next/server/app/api/orchestrator/route/server-reference-manifest.json +0 -4
  189. package/mcp-server/.next/server/app/api/orchestrator/route.js +0 -9
  190. package/mcp-server/.next/server/app/api/orchestrator/route.js.map +0 -5
  191. package/mcp-server/.next/server/app/api/orchestrator/route.js.nft.json +0 -1
  192. package/mcp-server/.next/server/app/api/orchestrator/route_client-reference-manifest.js +0 -2
  193. package/mcp-server/.next/server/app/api/screenshots/[filename]/route/app-paths-manifest.json +0 -3
  194. package/mcp-server/.next/server/app/api/screenshots/[filename]/route/build-manifest.json +0 -11
  195. package/mcp-server/.next/server/app/api/screenshots/[filename]/route/server-reference-manifest.json +0 -4
  196. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js +0 -8
  197. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js.map +0 -5
  198. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js.nft.json +0 -1
  199. package/mcp-server/.next/server/app/api/screenshots/[filename]/route_client-reference-manifest.js +0 -2
  200. package/mcp-server/.next/server/app/api/screenshots/capture/route/app-paths-manifest.json +0 -3
  201. package/mcp-server/.next/server/app/api/screenshots/capture/route/build-manifest.json +0 -11
  202. package/mcp-server/.next/server/app/api/screenshots/capture/route/server-reference-manifest.json +0 -4
  203. package/mcp-server/.next/server/app/api/screenshots/capture/route.js +0 -9
  204. package/mcp-server/.next/server/app/api/screenshots/capture/route.js.map +0 -5
  205. package/mcp-server/.next/server/app/api/screenshots/capture/route.js.nft.json +0 -1
  206. package/mcp-server/.next/server/app/api/screenshots/capture/route_client-reference-manifest.js +0 -2
  207. package/mcp-server/.next/server/app/api/screenshots/clear/route/app-paths-manifest.json +0 -3
  208. package/mcp-server/.next/server/app/api/screenshots/clear/route/build-manifest.json +0 -11
  209. package/mcp-server/.next/server/app/api/screenshots/clear/route/server-reference-manifest.json +0 -4
  210. package/mcp-server/.next/server/app/api/screenshots/clear/route.js +0 -8
  211. package/mcp-server/.next/server/app/api/screenshots/clear/route.js.map +0 -5
  212. package/mcp-server/.next/server/app/api/screenshots/clear/route.js.nft.json +0 -1
  213. package/mcp-server/.next/server/app/api/screenshots/clear/route_client-reference-manifest.js +0 -2
  214. package/mcp-server/.next/server/app/api/screenshots/list/route/app-paths-manifest.json +0 -3
  215. package/mcp-server/.next/server/app/api/screenshots/list/route/build-manifest.json +0 -11
  216. package/mcp-server/.next/server/app/api/screenshots/list/route/server-reference-manifest.json +0 -4
  217. package/mcp-server/.next/server/app/api/screenshots/list/route.js +0 -8
  218. package/mcp-server/.next/server/app/api/screenshots/list/route.js.map +0 -5
  219. package/mcp-server/.next/server/app/api/screenshots/list/route.js.nft.json +0 -1
  220. package/mcp-server/.next/server/app/api/screenshots/list/route_client-reference-manifest.js +0 -2
  221. package/mcp-server/.next/server/app/api/teams/route/app-paths-manifest.json +0 -3
  222. package/mcp-server/.next/server/app/api/teams/route/build-manifest.json +0 -11
  223. package/mcp-server/.next/server/app/api/teams/route/server-reference-manifest.json +0 -4
  224. package/mcp-server/.next/server/app/api/teams/route.js +0 -8
  225. package/mcp-server/.next/server/app/api/teams/route.js.map +0 -5
  226. package/mcp-server/.next/server/app/api/teams/route.js.nft.json +0 -1
  227. package/mcp-server/.next/server/app/api/teams/route_client-reference-manifest.js +0 -2
  228. package/mcp-server/.next/server/app/api/tools/route/app-paths-manifest.json +0 -3
  229. package/mcp-server/.next/server/app/api/tools/route/build-manifest.json +0 -11
  230. package/mcp-server/.next/server/app/api/tools/route/server-reference-manifest.json +0 -4
  231. package/mcp-server/.next/server/app/api/tools/route.js +0 -8
  232. package/mcp-server/.next/server/app/api/tools/route.js.map +0 -5
  233. package/mcp-server/.next/server/app/api/tools/route.js.nft.json +0 -1
  234. package/mcp-server/.next/server/app/api/tools/route_client-reference-manifest.js +0 -2
  235. package/mcp-server/.next/server/app/index.html +0 -1
  236. package/mcp-server/.next/server/app/index.meta +0 -14
  237. package/mcp-server/.next/server/app/index.rsc +0 -18
  238. package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +0 -9
  239. package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +0 -18
  240. package/mcp-server/.next/server/app/index.segments/_head.segment.rsc +0 -5
  241. package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +0 -6
  242. package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +0 -2
  243. package/mcp-server/.next/server/app/logs/page/app-paths-manifest.json +0 -3
  244. package/mcp-server/.next/server/app/logs/page/build-manifest.json +0 -17
  245. package/mcp-server/.next/server/app/logs/page/next-font-manifest.json +0 -6
  246. package/mcp-server/.next/server/app/logs/page/react-loadable-manifest.json +0 -1
  247. package/mcp-server/.next/server/app/logs/page/server-reference-manifest.json +0 -4
  248. package/mcp-server/.next/server/app/logs/page.js +0 -16
  249. package/mcp-server/.next/server/app/logs/page.js.map +0 -5
  250. package/mcp-server/.next/server/app/logs/page.js.nft.json +0 -1
  251. package/mcp-server/.next/server/app/logs/page_client-reference-manifest.js +0 -2
  252. package/mcp-server/.next/server/app/mcp/route/app-paths-manifest.json +0 -3
  253. package/mcp-server/.next/server/app/mcp/route/build-manifest.json +0 -11
  254. package/mcp-server/.next/server/app/mcp/route/server-reference-manifest.json +0 -4
  255. package/mcp-server/.next/server/app/mcp/route.js +0 -11
  256. package/mcp-server/.next/server/app/mcp/route.js.map +0 -5
  257. package/mcp-server/.next/server/app/mcp/route.js.nft.json +0 -1
  258. package/mcp-server/.next/server/app/mcp/route_client-reference-manifest.js +0 -2
  259. package/mcp-server/.next/server/app/page/app-paths-manifest.json +0 -3
  260. package/mcp-server/.next/server/app/page/build-manifest.json +0 -17
  261. package/mcp-server/.next/server/app/page/next-font-manifest.json +0 -6
  262. package/mcp-server/.next/server/app/page/react-loadable-manifest.json +0 -1
  263. package/mcp-server/.next/server/app/page/server-reference-manifest.json +0 -4
  264. package/mcp-server/.next/server/app/page.js +0 -15
  265. package/mcp-server/.next/server/app/page.js.map +0 -5
  266. package/mcp-server/.next/server/app/page.js.nft.json +0 -1
  267. package/mcp-server/.next/server/app/page_client-reference-manifest.js +0 -2
  268. package/mcp-server/.next/server/app/video/[session]/page/app-paths-manifest.json +0 -3
  269. package/mcp-server/.next/server/app/video/[session]/page/build-manifest.json +0 -17
  270. package/mcp-server/.next/server/app/video/[session]/page/next-font-manifest.json +0 -6
  271. package/mcp-server/.next/server/app/video/[session]/page/react-loadable-manifest.json +0 -1
  272. package/mcp-server/.next/server/app/video/[session]/page/server-reference-manifest.json +0 -4
  273. package/mcp-server/.next/server/app/video/[session]/page.js +0 -15
  274. package/mcp-server/.next/server/app/video/[session]/page.js.map +0 -5
  275. package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +0 -1
  276. package/mcp-server/.next/server/app/video/[session]/page_client-reference-manifest.js +0 -2
  277. package/mcp-server/.next/server/app-paths-manifest.json +0 -22
  278. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_[filename]_route_actions_4f8d6e37.js +0 -3
  279. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_[filename]_route_actions_4f8d6e37.js.map +0 -1
  280. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_capture_route_actions_4034f26c.js +0 -3
  281. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_capture_route_actions_4034f26c.js.map +0 -1
  282. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_clear_route_actions_e26206f4.js +0 -3
  283. package/mcp-server/.next/server/chunks/250ae__next-internal_server_app_api_screenshots_clear_route_actions_e26206f4.js.map +0 -1
  284. package/mcp-server/.next/server/chunks/[externals]__0e1fd2ca._.js +0 -3
  285. package/mcp-server/.next/server/chunks/[externals]__0e1fd2ca._.js.map +0 -1
  286. package/mcp-server/.next/server/chunks/[externals]_next_dist_2a398dc7._.js +0 -3
  287. package/mcp-server/.next/server/chunks/[externals]_next_dist_2a398dc7._.js.map +0 -1
  288. package/mcp-server/.next/server/chunks/[externals]_node:crypto_c20cce38._.js +0 -3
  289. package/mcp-server/.next/server/chunks/[externals]_node:crypto_c20cce38._.js.map +0 -1
  290. package/mcp-server/.next/server/chunks/[root-of-the-server]__1dca9894._.js +0 -3
  291. package/mcp-server/.next/server/chunks/[root-of-the-server]__1dca9894._.js.map +0 -1
  292. package/mcp-server/.next/server/chunks/[root-of-the-server]__21541e6b._.js +0 -11
  293. package/mcp-server/.next/server/chunks/[root-of-the-server]__21541e6b._.js.map +0 -1
  294. package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js +0 -17
  295. package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js.map +0 -1
  296. package/mcp-server/.next/server/chunks/[root-of-the-server]__377f76d7._.js +0 -7
  297. package/mcp-server/.next/server/chunks/[root-of-the-server]__377f76d7._.js.map +0 -1
  298. package/mcp-server/.next/server/chunks/[root-of-the-server]__454b0d3c._.js +0 -7
  299. package/mcp-server/.next/server/chunks/[root-of-the-server]__454b0d3c._.js.map +0 -1
  300. package/mcp-server/.next/server/chunks/[root-of-the-server]__69e6dfb7._.js +0 -3
  301. package/mcp-server/.next/server/chunks/[root-of-the-server]__69e6dfb7._.js.map +0 -1
  302. package/mcp-server/.next/server/chunks/[root-of-the-server]__6baff21e._.js +0 -4
  303. package/mcp-server/.next/server/chunks/[root-of-the-server]__6baff21e._.js.map +0 -1
  304. package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js +0 -3
  305. package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js.map +0 -1
  306. package/mcp-server/.next/server/chunks/[root-of-the-server]__7049acd5._.js +0 -3
  307. package/mcp-server/.next/server/chunks/[root-of-the-server]__7049acd5._.js.map +0 -1
  308. package/mcp-server/.next/server/chunks/[root-of-the-server]__73c9cc46._.js +0 -3
  309. package/mcp-server/.next/server/chunks/[root-of-the-server]__73c9cc46._.js.map +0 -1
  310. package/mcp-server/.next/server/chunks/[root-of-the-server]__78991125._.js +0 -3
  311. package/mcp-server/.next/server/chunks/[root-of-the-server]__78991125._.js.map +0 -1
  312. package/mcp-server/.next/server/chunks/[root-of-the-server]__7ae828c6._.js +0 -3
  313. package/mcp-server/.next/server/chunks/[root-of-the-server]__7ae828c6._.js.map +0 -1
  314. package/mcp-server/.next/server/chunks/[root-of-the-server]__94946101._.js +0 -3
  315. package/mcp-server/.next/server/chunks/[root-of-the-server]__94946101._.js.map +0 -1
  316. package/mcp-server/.next/server/chunks/[root-of-the-server]__99274dd8._.js +0 -4
  317. package/mcp-server/.next/server/chunks/[root-of-the-server]__99274dd8._.js.map +0 -1
  318. package/mcp-server/.next/server/chunks/[root-of-the-server]__9c4c7095._.js +0 -3
  319. package/mcp-server/.next/server/chunks/[root-of-the-server]__9c4c7095._.js.map +0 -1
  320. package/mcp-server/.next/server/chunks/[root-of-the-server]__b56464d6._.js +0 -7
  321. package/mcp-server/.next/server/chunks/[root-of-the-server]__b56464d6._.js.map +0 -1
  322. package/mcp-server/.next/server/chunks/[root-of-the-server]__b698502d._.js +0 -3
  323. package/mcp-server/.next/server/chunks/[root-of-the-server]__b698502d._.js.map +0 -1
  324. package/mcp-server/.next/server/chunks/[root-of-the-server]__b86e20b6._.js +0 -3
  325. package/mcp-server/.next/server/chunks/[root-of-the-server]__b86e20b6._.js.map +0 -1
  326. package/mcp-server/.next/server/chunks/[root-of-the-server]__c8cf5b23._.js +0 -3
  327. package/mcp-server/.next/server/chunks/[root-of-the-server]__c8cf5b23._.js.map +0 -1
  328. package/mcp-server/.next/server/chunks/[root-of-the-server]__e6a83e60._.js +0 -4
  329. package/mcp-server/.next/server/chunks/[root-of-the-server]__e6a83e60._.js.map +0 -1
  330. package/mcp-server/.next/server/chunks/[root-of-the-server]__ee139f8a._.js +0 -3
  331. package/mcp-server/.next/server/chunks/[root-of-the-server]__ee139f8a._.js.map +0 -1
  332. package/mcp-server/.next/server/chunks/[turbopack]_runtime.js +0 -795
  333. package/mcp-server/.next/server/chunks/[turbopack]_runtime.js.map +0 -10
  334. package/mcp-server/.next/server/chunks/edd96_next_80752ad3._.js +0 -3
  335. package/mcp-server/.next/server/chunks/edd96_next_80752ad3._.js.map +0 -1
  336. package/mcp-server/.next/server/chunks/edd96_next_dist_esm_build_templates_app-route_f51c5640.js +0 -3
  337. package/mcp-server/.next/server/chunks/edd96_next_dist_esm_build_templates_app-route_f51c5640.js.map +0 -1
  338. package/mcp-server/.next/server/chunks/edd96_next_dist_fa47e982._.js +0 -6
  339. package/mcp-server/.next/server/chunks/edd96_next_dist_fa47e982._.js.map +0 -1
  340. package/mcp-server/.next/server/chunks/edd96_next_ef93dda6._.js +0 -14
  341. package/mcp-server/.next/server/chunks/edd96_next_ef93dda6._.js.map +0 -1
  342. package/mcp-server/.next/server/chunks/mcp-server_70405c2f._.js +0 -3
  343. package/mcp-server/.next/server/chunks/mcp-server_70405c2f._.js.map +0 -1
  344. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_jank_[session]_route_actions_3b2b275b.js +0 -3
  345. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_jank_[session]_route_actions_3b2b275b.js.map +0 -1
  346. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_append_route_actions_bc66060f.js +0 -3
  347. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_append_route_actions_bc66060f.js.map +0 -1
  348. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_head_route_actions_1152480c.js +0 -3
  349. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_head_route_actions_1152480c.js.map +0 -1
  350. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_list_route_actions_b9e24400.js +0 -3
  351. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_list_route_actions_b9e24400.js.map +0 -1
  352. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_rotate_route_actions_76075d08.js +0 -3
  353. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_rotate_route_actions_76075d08.js.map +0 -1
  354. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_stream_route_actions_16e5c553.js +0 -3
  355. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_stream_route_actions_16e5c553.js.map +0 -1
  356. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_tail_route_actions_55440150.js +0 -3
  357. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_logs_tail_route_actions_55440150.js.map +0 -1
  358. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_orchestrator_route_actions_c6fba9ec.js +0 -3
  359. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_orchestrator_route_actions_c6fba9ec.js.map +0 -1
  360. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_screenshots_list_route_actions_acfa57bd.js +0 -3
  361. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_screenshots_list_route_actions_acfa57bd.js.map +0 -1
  362. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_teams_route_actions_aaa1c876.js +0 -3
  363. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_teams_route_actions_aaa1c876.js.map +0 -1
  364. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_tools_route_actions_007f3c7c.js +0 -3
  365. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_tools_route_actions_007f3c7c.js.map +0 -1
  366. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_mcp_route_actions_7f7b5be4.js +0 -3
  367. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_mcp_route_actions_7f7b5be4.js.map +0 -1
  368. package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js +0 -185
  369. package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js.map +0 -1
  370. package/mcp-server/.next/server/chunks/mcp-server_instrumentation_node_ts_32271e34._.js +0 -3
  371. package/mcp-server/.next/server/chunks/mcp-server_instrumentation_node_ts_32271e34._.js.map +0 -1
  372. package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js +0 -3
  373. package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js.map +0 -1
  374. package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js +0 -3
  375. package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js.map +0 -1
  376. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__250a4cf3._.js +0 -3
  377. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__250a4cf3._.js.map +0 -1
  378. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__41e244ae._.js +0 -3
  379. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__41e244ae._.js.map +0 -1
  380. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__4a45fb1f._.js +0 -4
  381. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__4a45fb1f._.js.map +0 -1
  382. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__4feaccaf._.js +0 -3
  383. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__4feaccaf._.js.map +0 -1
  384. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__6d9fa861._.js +0 -3
  385. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__6d9fa861._.js.map +0 -1
  386. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__9913ce94._.js +0 -3
  387. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__9913ce94._.js.map +0 -1
  388. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__9923da5e._.js +0 -3
  389. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__9923da5e._.js.map +0 -1
  390. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__a8fcf205._.js +0 -3
  391. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__a8fcf205._.js.map +0 -1
  392. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__b17d4048._.js +0 -3
  393. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__b17d4048._.js.map +0 -1
  394. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__dcf84f77._.js +0 -3
  395. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__dcf84f77._.js.map +0 -1
  396. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__df4ed844._.js +0 -3
  397. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__df4ed844._.js.map +0 -1
  398. package/mcp-server/.next/server/chunks/ssr/[turbopack]_runtime.js +0 -795
  399. package/mcp-server/.next/server/chunks/ssr/[turbopack]_runtime.js.map +0 -10
  400. package/mcp-server/.next/server/chunks/ssr/_213c874b._.js +0 -4
  401. package/mcp-server/.next/server/chunks/ssr/_213c874b._.js.map +0 -1
  402. package/mcp-server/.next/server/chunks/ssr/_69be9abe._.js +0 -4
  403. package/mcp-server/.next/server/chunks/ssr/_69be9abe._.js.map +0 -1
  404. package/mcp-server/.next/server/chunks/ssr/_b38781f1._.js +0 -4
  405. package/mcp-server/.next/server/chunks/ssr/_b38781f1._.js.map +0 -1
  406. package/mcp-server/.next/server/chunks/ssr/_edba94b0._.js +0 -8
  407. package/mcp-server/.next/server/chunks/ssr/_edba94b0._.js.map +0 -1
  408. package/mcp-server/.next/server/chunks/ssr/_f478416d._.js +0 -8
  409. package/mcp-server/.next/server/chunks/ssr/_f478416d._.js.map +0 -1
  410. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_22d4e869._.js +0 -4
  411. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_22d4e869._.js.map +0 -1
  412. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_329c4a9b._.js +0 -10
  413. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_329c4a9b._.js.map +0 -1
  414. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_6cceb2cd._.js +0 -3
  415. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_6cceb2cd._.js.map +0 -1
  416. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_861297ac._.js +0 -6
  417. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_861297ac._.js.map +0 -1
  418. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_85c7e922._.js +0 -3
  419. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_85c7e922._.js.map +0 -1
  420. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_forbidden_0eb1cacd.js +0 -3
  421. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_forbidden_0eb1cacd.js.map +0 -1
  422. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_global-error_e64e654b.js +0 -3
  423. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_global-error_e64e654b.js.map +0 -1
  424. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_unauthorized_06e7b5f5.js +0 -3
  425. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_client_components_builtin_unauthorized_06e7b5f5.js.map +0 -1
  426. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_esm_build_templates_app-page_f82c7ca4.js +0 -4
  427. package/mcp-server/.next/server/chunks/ssr/edd96_next_dist_esm_build_templates_app-page_f82c7ca4.js.map +0 -1
  428. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app__global-error_page_actions_404453e7.js +0 -3
  429. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app__global-error_page_actions_404453e7.js.map +0 -1
  430. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app__not-found_page_actions_c1864427.js +0 -3
  431. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app__not-found_page_actions_c1864427.js.map +0 -1
  432. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_logs_page_actions_71542ba9.js +0 -3
  433. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_logs_page_actions_71542ba9.js.map +0 -1
  434. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_page_actions_a5ee4758.js +0 -3
  435. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_page_actions_a5ee4758.js.map +0 -1
  436. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_video_[session]_page_actions_a6aab323.js +0 -3
  437. package/mcp-server/.next/server/chunks/ssr/mcp-server__next-internal_server_app_video_[session]_page_actions_a6aab323.js.map +0 -1
  438. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_layout_tsx_afa41767._.js +0 -3
  439. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_layout_tsx_afa41767._.js.map +0 -1
  440. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js +0 -3
  441. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js.map +0 -1
  442. package/mcp-server/.next/server/chunks/ssr/mcp-server_components_dark-mode-toggle_tsx_f31dd15d._.js +0 -3
  443. package/mcp-server/.next/server/chunks/ssr/mcp-server_components_dark-mode-toggle_tsx_f31dd15d._.js.map +0 -1
  444. package/mcp-server/.next/server/chunks/ssr/node_modules__bun_39d5fbaf._.js +0 -3
  445. package/mcp-server/.next/server/chunks/ssr/node_modules__bun_39d5fbaf._.js.map +0 -1
  446. package/mcp-server/.next/server/chunks/ssr/node_modules__bun_d6d37386._.js +0 -5
  447. package/mcp-server/.next/server/chunks/ssr/node_modules__bun_d6d37386._.js.map +0 -1
  448. package/mcp-server/.next/server/functions-config-manifest.json +0 -4
  449. package/mcp-server/.next/server/instrumentation.js +0 -4
  450. package/mcp-server/.next/server/instrumentation.js.nft.json +0 -1
  451. package/mcp-server/.next/server/interception-route-rewrite-manifest.js +0 -1
  452. package/mcp-server/.next/server/middleware-build-manifest.js +0 -21
  453. package/mcp-server/.next/server/middleware-manifest.json +0 -6
  454. package/mcp-server/.next/server/next-font-manifest.js +0 -1
  455. package/mcp-server/.next/server/next-font-manifest.json +0 -6
  456. package/mcp-server/.next/server/pages/404.html +0 -1
  457. package/mcp-server/.next/server/pages/500.html +0 -2
  458. package/mcp-server/.next/server/pages-manifest.json +0 -4
  459. package/mcp-server/.next/server/server-reference-manifest.js +0 -1
  460. package/mcp-server/.next/server/server-reference-manifest.json +0 -5
  461. package/mcp-server/.next/static/HxwUjR9e01QAp7lf9kRjn/_buildManifest.js +0 -11
  462. package/mcp-server/.next/static/HxwUjR9e01QAp7lf9kRjn/_clientMiddlewareManifest.json +0 -1
  463. package/mcp-server/.next/static/HxwUjR9e01QAp7lf9kRjn/_ssgManifest.js +0 -1
  464. package/mcp-server/.next/static/chunks/10099c90a1ca89ea.js +0 -4
  465. package/mcp-server/.next/static/chunks/10c04cb580a9beec.js +0 -1
  466. package/mcp-server/.next/static/chunks/13606014ef33124c.js +0 -2
  467. package/mcp-server/.next/static/chunks/39e9bdcc541cc428.js +0 -1
  468. package/mcp-server/.next/static/chunks/3f3f8e7d16ba3bf4.js +0 -1
  469. package/mcp-server/.next/static/chunks/4785978304fb9e19.js +0 -1
  470. package/mcp-server/.next/static/chunks/59cdeaf92a780e96.css +0 -1
  471. package/mcp-server/.next/static/chunks/5df77c9395248155.js +0 -1
  472. package/mcp-server/.next/static/chunks/807aef58565dccb1.js +0 -1
  473. package/mcp-server/.next/static/chunks/a6dad97d9634a72d.js +0 -1
  474. package/mcp-server/.next/static/chunks/a6dad97d9634a72d.js.map +0 -1
  475. package/mcp-server/.next/static/chunks/b8eb42a9560f7980.js +0 -3
  476. package/mcp-server/.next/static/chunks/ea7b53054294e7bb.js +0 -3
  477. package/mcp-server/.next/static/chunks/ec58e1c556f5d0fa.js +0 -3
  478. package/mcp-server/.next/static/chunks/turbopack-b64e111cadf03885.js +0 -4
  479. package/mcp-server/app/api/jank/[session]/route.ts +0 -344
  480. package/mcp-server/app/api/logs/append/route.ts +0 -82
  481. package/mcp-server/app/api/logs/head/route.ts +0 -32
  482. package/mcp-server/app/api/logs/list/route.ts +0 -71
  483. package/mcp-server/app/api/logs/rotate/route.ts +0 -48
  484. package/mcp-server/app/api/logs/stream/route.ts +0 -61
  485. package/mcp-server/app/api/logs/tail/route.ts +0 -32
  486. package/mcp-server/app/api/orchestrator/route.ts +0 -73
  487. package/mcp-server/app/api/screenshots/[filename]/route.ts +0 -61
  488. package/mcp-server/app/api/screenshots/capture/route.ts +0 -137
  489. package/mcp-server/app/api/screenshots/clear/route.ts +0 -44
  490. package/mcp-server/app/api/screenshots/list/route.ts +0 -22
  491. package/mcp-server/app/api/teams/route.ts +0 -86
  492. package/mcp-server/app/api/tools/route.ts +0 -92
  493. package/mcp-server/app/globals.css +0 -124
  494. package/mcp-server/app/layout.tsx +0 -23
  495. package/mcp-server/app/logs/LogsClient.infinite-loop.test.tsx +0 -127
  496. package/mcp-server/app/logs/LogsClient.test.ts +0 -416
  497. package/mcp-server/app/logs/LogsClient.tsx +0 -1967
  498. package/mcp-server/app/logs/page.tsx +0 -150
  499. package/mcp-server/app/logs/utils.ts +0 -151
  500. package/mcp-server/app/mcp/client-manager.ts +0 -346
  501. package/mcp-server/app/mcp/route.ts +0 -765
  502. package/mcp-server/app/mcp/tools.ts +0 -4158
  503. package/mcp-server/app/page.tsx +0 -399
  504. package/mcp-server/app/video/[session]/page.tsx +0 -239
  505. package/mcp-server/next-env.d.ts +0 -6
  506. package/mcp-server/package.json +0 -48
  507. package/mcp-server/postcss.config.mjs +0 -5
  508. package/mcp-server/public/favicon-16.svg +0 -4
  509. package/mcp-server/public/favicon-180.png +0 -0
  510. package/mcp-server/public/favicon-64.svg +0 -4
  511. package/mcp-server/public/favicon-preview.html +0 -67
  512. package/mcp-server/public/favicon.ico +0 -0
  513. package/mcp-server/public/favicon.svg +0 -4
  514. package/mcp-server/public/screenshots/test.txt +0 -1
  515. package/mcp-server/start-production.mjs +0 -101
  516. package/mcp-server/tsconfig.json +0 -28
@@ -1,4158 +0,0 @@
1
- import { exec, spawn } from "child_process"
2
- import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync } from "fs"
3
- import { homedir, tmpdir } from "os"
4
- import { join } from "path"
5
- import pixelmatch from "pixelmatch"
6
- import { PNG } from "pngjs"
7
- import { promisify } from "util"
8
- import { WebSocket } from "ws"
9
-
10
- const execAsync = promisify(exec)
11
-
12
- /**
13
- * Detect if we're in a sandbox environment (Vercel Sandbox, Docker, etc.)
14
- * where lsof and other system utilities may not be available.
15
- */
16
- function isInSandbox(): boolean {
17
- return (
18
- process.env.VERCEL_SANDBOX === "1" ||
19
- process.env.VERCEL === "1" ||
20
- existsSync("/.dockerenv") ||
21
- existsSync("/run/.containerenv")
22
- )
23
- }
24
-
25
- // Tool descriptions
26
- export const TOOL_DESCRIPTIONS = {
27
- // Meta-description for MCP coordination - Claude should see this when listing tools
28
- _mcp_coordination:
29
- "**dev3000 is an MCP orchestrator for web development.** For browser automation, use the d3k CLI:\n```bash\nd3k agent-browser --cdp 9222 <command>\n```\nThis connects to d3k's existing Chrome browser. NEVER install Playwright or Chromium - d3k already has a browser open.\n\ndev3000 provides unified error context from server logs + browser console + network, automatic screenshots on errors, and framework-aware diagnostics (Next.js, Svelte).",
30
-
31
- fix_my_app:
32
- "Diagnoses application errors from dev3000 logs. Returns a prioritized list of issues requiring fixes.\n\n**CRITICAL: You MUST use this tool in a loop until all errors are resolved:**\n\n```\nwhile (errors exist) {\n 1. DIAGNOSE: Call fix_my_app to get current errors\n 2. FIX: Implement a fix for the highest-priority error\n 3. VERIFY: Call fix_my_app again to confirm the error is gone\n 4. REPEAT: Continue until no errors remain\n}\n```\n\n**This tool does NOT fix anything automatically.** It returns diagnostic data. You must:\n- Read the error output\n- Investigate and fix each issue\n- Call this tool again to verify your fix worked\n- Keep looping until the app is healthy\n\n**What it analyzes:**\n• Server logs, browser console, network requests\n• Categorizes: build errors, server crashes, browser errors, network issues, warnings\n• Prioritizes by severity (fix build errors first, then server, then browser, etc.)\n• Shows user interactions that triggered each error\n\n**Parameters:**\n• focusArea: 'build', 'runtime', 'network', 'ui', 'performance', or 'all' (default)\n• mode: 'snapshot' (current state), 'bisect' (before/after comparison), 'monitor' (continuous)\n• timeRangeMinutes: How far back to analyze (default: 10)\n• createPR: If true, creates a PR branch for the highest-priority issue\n\n**Framework support:** Auto-detects Next.js for framework-specific analysis.\n\n**Attribution for commits/PRs:**\n```\nGenerated with Claude Code using d3k (https://d3k.dev)\nCo-Authored-By: Claude <noreply@anthropic.com>\n```",
33
-
34
- // execute_browser_action removed - use agent_browser_action instead
35
-
36
- analyze_visual_diff:
37
- "Compares two screenshots and returns analysis instructions for identifying visual differences.\n\n**What it provides:**\n• Instructions to load both images for comparison\n• Context about what visual changes to look for\n• Guidance on identifying layout shift causes\n\n**Use cases:**\n• Analyzing before/after frames from CLS detection\n• Identifying elements that appeared, moved, or resized\n• Debugging visual regressions",
38
-
39
- find_component_source:
40
- "Maps a DOM element to its React component source code location.\n\n**How it works:**\n1. Inspects the element via Chrome DevTools Protocol\n2. Extracts the React component function source\n3. Identifies unique code patterns (JSX, classNames, etc.)\n4. Returns grep patterns to locate the source file\n\n**Use cases:**\n• Finding which file contains a specific UI element\n• Locating components responsible for layout shifts\n• Tracing DOM elements back to source code",
41
-
42
- restart_dev_server:
43
- "⚠️ DANGEROUS: Restarts the development server. This is RARELY needed and often causes d3k to crash.\n\n**BEFORE using this tool, consider:**\n• HMR handles most code changes automatically - just wait a moment\n• Browser reload (Cmd+R) clears most cached state\n• 'use cache' in Next.js auto-invalidates when source files change\n• Restarting often fails and causes d3k to exit, disrupting the user's session\n\n**Only use for:**\n• Changes to next.config.js/ts (rare)\n• Changes to middleware.ts (rare)\n• Changes to .env files (rare)\n\n**DO NOT use for:**\n• Clearing RSC cache (HMR handles this)\n• Testing if a fix worked (just reload the page)\n• Any regular code changes\n\n**Ask the user first** before using this tool. They may prefer to manually restart d3k themselves.",
44
-
45
- crawl_app:
46
- "Discovers URLs in the application by crawling links from the homepage.\n\n**Parameters:**\n• depth: How many link levels to follow (1, 2, 3, or 'all')\n• limit: Max links per page (default: 3)\n\n**Behavior:**\n• Starts at localhost homepage\n• Follows same-origin links only\n• Deduplicates discovered URLs\n• Returns list of all found pages\n\n**Use cases:**\n• Discovering all routes before running diagnostics\n• Site-wide testing coverage\n• Verifying all pages load without errors",
47
-
48
- get_skill:
49
- "Get the content of a d3k/Claude Code skill. Skills are prompt templates that provide specialized instructions for specific tasks.\n\n**Parameters:**\n• name: The skill name (e.g., 'vercel-design-guidelines', 'd3k')\n\n**Returns:**\nThe full SKILL.md content which contains instructions on how to perform the skill's task.\n\n**Available skills:**\n• vercel-design-guidelines - Audit web interfaces against Vercel's design guidelines\n• d3k - Core d3k development assistant skill\n\n**Use cases:**\n• Loading design guidelines audit instructions\n• Getting specialized workflow instructions",
50
-
51
- // agent_browser_action: REMOVED - Use d3k CLI instead: `d3k agent-browser --cdp 9222 <command>`
52
- // This avoids browser installation issues and connects to d3k's existing Chrome
53
- _deprecated_agent_browser_action:
54
- "REMOVED - Use d3k CLI: `d3k agent-browser --cdp 9222 <command>` instead. This connects to d3k's existing Chrome browser without needing to install anything."
55
- }
56
-
57
- // Types
58
- export interface Session {
59
- projectName: string
60
- startTime: string
61
- logFilePath: string
62
- sessionFile: string
63
- lastModified: Date
64
- }
65
-
66
- export interface FixMyAppParams {
67
- projectName?: string
68
- focusArea?: string
69
- mode?: "snapshot" | "bisect" | "monitor"
70
- waitForUserInteraction?: boolean
71
- timeRangeMinutes?: number
72
- includeTimestampInstructions?: boolean
73
- integrateNextjs?: boolean
74
- integrateChromeDevtools?: boolean
75
- returnRawData?: boolean
76
- createPR?: boolean // Create a PR for the highest priority issue
77
- }
78
-
79
- export interface CreateIntegratedWorkflowParams {
80
- availableMcps?: string[] // Optional - will auto-discover if not provided
81
- focusArea?: string
82
- errorContext?: string
83
- }
84
-
85
- // ExecuteBrowserActionParams removed - use AgentBrowserActionParams instead
86
-
87
- export interface GetMcpCapabilitiesParams {
88
- mcpName?: string // Optional - if not provided, shows all available MCPs
89
- }
90
-
91
- // Structured data types for raw data output
92
- export interface ErrorWithInteractions {
93
- timestamp: string
94
- category: string
95
- message: string
96
- interactions: string[]
97
- severity: "critical" | "error" | "warning"
98
- }
99
-
100
- export interface CodeFix {
101
- file: string
102
- line?: number
103
- description: string
104
- code: string
105
- reason: string
106
- }
107
-
108
- export interface McpFunctionSuggestion {
109
- function: string
110
- params?: Record<string, unknown>
111
- reason: string
112
- priority: "high" | "medium" | "low"
113
- }
114
-
115
- export interface WorkflowPhase {
116
- name: string
117
- description: string
118
- actions: Array<{
119
- mcp: string
120
- function: string
121
- params?: Record<string, unknown>
122
- reason: string
123
- }>
124
- estimatedTime: string
125
- }
126
-
127
- export interface StructuredAnalysisResult {
128
- errors: ErrorWithInteractions[]
129
- fixes: CodeFix[]
130
- suggestedIntegrations: {
131
- nextjs?: McpFunctionSuggestion[]
132
- chrome?: McpFunctionSuggestion[]
133
- }
134
- workflowPlan?: {
135
- phase1: WorkflowPhase
136
- phase2: WorkflowPhase
137
- phase3: WorkflowPhase
138
- }
139
- summary: {
140
- totalErrors: number
141
- criticalErrors: number
142
- hasIntegrations: boolean
143
- estimatedFixTime: string
144
- }
145
- }
146
-
147
- export interface PrioritizedError {
148
- error: string
149
- category: "build" | "server" | "browser" | "network" | "warning"
150
- severity: "critical" | "error" | "warning"
151
- priorityScore: number
152
- interactions: string[]
153
- timestamp?: string
154
- suggestedFix?: string
155
- }
156
-
157
- // Helper functions
158
-
159
- /**
160
- * Calculate priority score for an error
161
- * Higher score = higher priority to fix
162
- *
163
- * Scoring system:
164
- * - Build errors: 1000+ (blocks development)
165
- * - Server errors: 500+ (affects functionality)
166
- * - Browser errors: 300+ (user-facing issues)
167
- * - Network errors: 200+ (intermittent issues)
168
- * - Warnings: 100+ (nice to fix)
169
- *
170
- * Additional modifiers:
171
- * - Multiple occurrences: +50 per occurrence
172
- * - Recent (last minute): +100
173
- * - Has user interactions: +50 (reproducible)
174
- */
175
- function calculateErrorPriority(
176
- errorLine: string,
177
- category: PrioritizedError["category"],
178
- interactions: string[],
179
- allErrors: string[]
180
- ): number {
181
- let score = 0
182
-
183
- // Base score by category
184
- if (category === "build") {
185
- score = 1000
186
- } else if (category === "server") {
187
- score = 500
188
- } else if (category === "browser") {
189
- score = 300
190
- } else if (category === "network") {
191
- score = 200
192
- } else if (category === "warning") {
193
- score = 100
194
- }
195
-
196
- // Severity multipliers
197
- if (/CRITICAL|FATAL|crashed/i.test(errorLine)) {
198
- score *= 2
199
- } else if (/ERROR|Exception|FAIL/i.test(errorLine)) {
200
- score *= 1.5
201
- }
202
-
203
- // Count occurrences of similar errors
204
- // Strip ANSI escape codes before constructing regex pattern (fixes #74)
205
- // biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape codes require control characters
206
- const cleanLine = errorLine.replace(/\x1b\[[0-9;]*m/g, "")
207
- // Escape regex special characters, then replace digit sequences with \d+
208
- const escapedLine = cleanLine.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
209
- const errorPattern = escapedLine.replace(/\d+/g, "\\d+").substring(0, 100)
210
- let occurrences = 0
211
- try {
212
- occurrences = allErrors.filter((e) => new RegExp(errorPattern).test(e)).length
213
- } catch {
214
- // If regex still fails for some reason, just count exact matches
215
- occurrences = allErrors.filter((e) => e.includes(cleanLine.substring(0, 50))).length
216
- }
217
- if (occurrences > 1) {
218
- score += (occurrences - 1) * 50
219
- }
220
-
221
- // Boost if has interactions (reproducible)
222
- if (interactions.length > 0) {
223
- score += 50
224
- }
225
-
226
- // Boost if recent (within last minute)
227
- const timestampMatch = errorLine.match(/\[(\d{2}):(\d{2}):(\d{2})\.\d{3}\]/)
228
- if (timestampMatch) {
229
- const now = new Date()
230
- const errorTime = new Date()
231
- errorTime.setHours(parseInt(timestampMatch[1], 10))
232
- errorTime.setMinutes(parseInt(timestampMatch[2], 10))
233
- errorTime.setSeconds(parseInt(timestampMatch[3], 10))
234
-
235
- const ageMinutes = (now.getTime() - errorTime.getTime()) / 1000 / 60
236
- if (ageMinutes < 1) {
237
- score += 100
238
- }
239
- }
240
-
241
- return score
242
- }
243
-
244
- /**
245
- * Find the single highest priority error from categorized errors
246
- */
247
- function findHighestPriorityError(
248
- categorizedErrors: {
249
- serverErrors: string[]
250
- browserErrors: string[]
251
- buildErrors: string[]
252
- networkErrors: string[]
253
- warnings: string[]
254
- },
255
- allErrors: string[],
256
- logLines: string[]
257
- ): PrioritizedError | null {
258
- const prioritizedErrors: PrioritizedError[] = []
259
-
260
- // Helper to find interactions before an error
261
- const findInteractions = (errorLine: string): string[] => {
262
- const errorIndex = logLines.indexOf(errorLine)
263
- if (errorIndex === -1) return []
264
-
265
- const interactions: string[] = []
266
- for (let i = errorIndex - 1; i >= Math.max(0, errorIndex - 20) && interactions.length < 5; i--) {
267
- if (
268
- logLines[i].includes("[INTERACTION]") ||
269
- logLines[i].includes("[NAVIGATION]") ||
270
- logLines[i].includes("[PAGE]")
271
- ) {
272
- interactions.unshift(logLines[i])
273
- }
274
- }
275
- return interactions
276
- }
277
-
278
- // Process build errors
279
- for (const error of categorizedErrors.buildErrors) {
280
- const interactions = findInteractions(error)
281
- prioritizedErrors.push({
282
- error,
283
- category: "build",
284
- severity: "critical",
285
- priorityScore: calculateErrorPriority(error, "build", interactions, allErrors),
286
- interactions
287
- })
288
- }
289
-
290
- // Process server errors
291
- for (const error of categorizedErrors.serverErrors) {
292
- const interactions = findInteractions(error)
293
- const severity: PrioritizedError["severity"] = /CRITICAL|FATAL/i.test(error) ? "critical" : "error"
294
- prioritizedErrors.push({
295
- error,
296
- category: "server",
297
- severity,
298
- priorityScore: calculateErrorPriority(error, "server", interactions, allErrors),
299
- interactions
300
- })
301
- }
302
-
303
- // Process browser errors
304
- for (const error of categorizedErrors.browserErrors) {
305
- const interactions = findInteractions(error)
306
- const severity: PrioritizedError["severity"] = /CRITICAL|FATAL/i.test(error) ? "critical" : "error"
307
- prioritizedErrors.push({
308
- error,
309
- category: "browser",
310
- severity,
311
- priorityScore: calculateErrorPriority(error, "browser", interactions, allErrors),
312
- interactions
313
- })
314
- }
315
-
316
- // Process network errors
317
- for (const error of categorizedErrors.networkErrors) {
318
- const interactions = findInteractions(error)
319
- prioritizedErrors.push({
320
- error,
321
- category: "network",
322
- severity: "error",
323
- priorityScore: calculateErrorPriority(error, "network", interactions, allErrors),
324
- interactions
325
- })
326
- }
327
-
328
- // Process warnings (only if no errors found)
329
- if (prioritizedErrors.length === 0) {
330
- for (const error of categorizedErrors.warnings) {
331
- const interactions = findInteractions(error)
332
- prioritizedErrors.push({
333
- error,
334
- category: "warning",
335
- severity: "warning",
336
- priorityScore: calculateErrorPriority(error, "warning", interactions, allErrors),
337
- interactions
338
- })
339
- }
340
- }
341
-
342
- // Sort by priority score (highest first)
343
- prioritizedErrors.sort((a, b) => b.priorityScore - a.priorityScore)
344
-
345
- return prioritizedErrors[0] || null
346
- }
347
-
348
- /**
349
- * Create a PR for the highest priority issue
350
- */
351
- async function createPRForIssue(prioritizedError: PrioritizedError, _projectName: string): Promise<string> {
352
- try {
353
- // Extract error details for PR title and body
354
- const errorType = prioritizedError.category.toUpperCase()
355
- const errorMessage = prioritizedError.error
356
- .replace(/\[[^\]]+\]/g, "") // Remove timestamps and tags
357
- .trim()
358
- .substring(0, 100)
359
-
360
- const prTitle = `Fix: ${errorType} - ${errorMessage}`
361
-
362
- // Build PR body
363
- const prBody = `## 🐛 Bug Fix - ${prioritizedError.category} Error
364
-
365
- **Priority Score:** ${prioritizedError.priorityScore} (${prioritizedError.severity})
366
-
367
- ### Error Details
368
- \`\`\`
369
- ${prioritizedError.error}
370
- \`\`\`
371
-
372
- ${
373
- prioritizedError.interactions.length > 0
374
- ? `### Reproduction Steps
375
- The error occurred after these user interactions:
376
- ${prioritizedError.interactions.map((i, idx) => `${idx + 1}. ${i}`).join("\n")}
377
-
378
- ### Verification
379
- After implementing the fix, verify by:
380
- 1. Replaying the same interactions using \`execute_browser_action\`
381
- 2. Confirming the error no longer appears in logs
382
- 3. Checking that functionality works as expected
383
- `
384
- : ""
385
- }
386
-
387
- ### Suggested Fix
388
- This PR addresses the ${prioritizedError.severity}-level ${prioritizedError.category} error detected by dev3000.
389
-
390
- ${prioritizedError.suggestedFix || "Please analyze the error and implement the appropriate fix."}
391
-
392
- ---
393
- 🤖 Generated with [dev3000](https://github.com/vercel-labs/dev3000) - AI-powered debugging
394
- `
395
-
396
- // Create a new branch
397
- const branchName = `fix/${prioritizedError.category}-${Date.now()}`
398
-
399
- // Use execAsync to run git and gh commands
400
- await execAsync(`git checkout -b ${branchName}`)
401
-
402
- // Create the PR using gh
403
- await execAsync(`gh pr create --title "${prTitle}" --body "${prBody}" --head ${branchName}`)
404
-
405
- return `✅ Created PR: ${prTitle}\n\nBranch: ${branchName}\n\nNext steps:\n1. Implement the fix in your code\n2. Commit and push changes\n3. PR is ready for review!`
406
- } catch (error) {
407
- return `❌ Failed to create PR: ${error instanceof Error ? error.message : String(error)}\n\nYou can manually create a PR with the error details above.`
408
- }
409
- }
410
-
411
- // Helper functions
412
- export function findActiveSessions(): Session[] {
413
- const sessionDir = join(homedir(), ".d3k")
414
- if (!existsSync(sessionDir)) {
415
- return []
416
- }
417
-
418
- try {
419
- // Look for session.json files in subdirectories (new format)
420
- // e.g., ~/.d3k/dev3000-www-935beb/session.json
421
- const entries = readdirSync(sessionDir, { withFileTypes: true })
422
- const sessionFiles: string[] = []
423
-
424
- for (const entry of entries) {
425
- if (entry.isDirectory()) {
426
- const sessionFile = join(sessionDir, entry.name, "session.json")
427
- if (existsSync(sessionFile)) {
428
- sessionFiles.push(sessionFile)
429
- }
430
- }
431
- }
432
-
433
- const files = sessionFiles
434
- .map((filePath) => {
435
- try {
436
- const content = JSON.parse(readFileSync(filePath, "utf-8"))
437
- const stat = statSync(filePath)
438
- return {
439
- ...content,
440
- sessionFile: filePath,
441
- lastModified: stat.mtime
442
- }
443
- } catch {
444
- return null
445
- }
446
- })
447
- .filter((session): session is NonNullable<typeof session> => {
448
- // Check if the process is still running by checking the PID
449
- if (!session || !session.pid) {
450
- return false
451
- }
452
- try {
453
- process.kill(session.pid, 0) // Signal 0 just checks if process exists
454
- return true // Process is still running
455
- } catch {
456
- return false // Process is not running
457
- }
458
- })
459
- .sort((a, b) => new Date(b.startTime).getTime() - new Date(a.startTime).getTime())
460
-
461
- return files
462
- } catch (_error) {
463
- return []
464
- }
465
- }
466
-
467
- export function getLogPath(projectName?: string): string | null {
468
- // If explicit project name provided, look it up
469
- if (projectName) {
470
- const sessions = findActiveSessions()
471
- const session = sessions.find((s) => s.projectName === projectName)
472
- if (session) {
473
- // Return the log path even if file doesn't exist yet
474
- // (it will be created when logs start, especially in sandbox environments)
475
- return session.logFilePath
476
- }
477
- }
478
-
479
- // Fall back to environment variable
480
- const envPath = process.env.LOG_FILE_PATH
481
- if (envPath) {
482
- // Return the path even if file doesn't exist yet
483
- return envPath
484
- }
485
-
486
- // If no project specified and no env var, show available sessions
487
- return null
488
- }
489
-
490
- // Main tool implementations
491
- export async function fixMyApp({
492
- projectName,
493
- focusArea = "all",
494
- mode = "snapshot",
495
- waitForUserInteraction = false,
496
- timeRangeMinutes = 10,
497
- includeTimestampInstructions = true,
498
- integrateNextjs = false,
499
- integrateChromeDevtools = false,
500
- returnRawData = false,
501
- createPR = false
502
- }: FixMyAppParams): Promise<{ content: Array<{ type: "text"; text: string }> }> {
503
- // 🎯 MCP ORCHESTRATION: Check which downstream MCPs are available
504
- const { getMCPClientManager } = await import("./client-manager")
505
- const clientManager = getMCPClientManager()
506
- const connectedMCPs = clientManager.getConnectedMCPs()
507
-
508
- const hasNextjsDev = connectedMCPs.includes("nextjs-dev")
509
- const hasChromeDevtools = connectedMCPs.includes("chrome-devtools")
510
-
511
- if (connectedMCPs.length > 0) {
512
- logToDevFile(`Fix My App: Connected to downstream MCPs: ${connectedMCPs.join(", ")}`)
513
- }
514
-
515
- // Auto-detect integration flags based on connected MCPs
516
- if (hasNextjsDev && integrateNextjs === false) {
517
- integrateNextjs = true
518
- }
519
- if (hasChromeDevtools && integrateChromeDevtools === false) {
520
- integrateChromeDevtools = true
521
- }
522
-
523
- // Legacy delegation check (keeping for backwards compatibility)
524
- const canDelegateNextjs = await canDelegateToNextjs()
525
- if (canDelegateNextjs) {
526
- logToDevFile(`Fix My App: Recommending dev3000-nextjs-dev MCP for Next.js-specific analysis`)
527
- }
528
- let logPath = getLogPath(projectName)
529
- if (!logPath) {
530
- const sessions = findActiveSessions()
531
- if (sessions.length === 0) {
532
- return {
533
- content: [
534
- {
535
- type: "text",
536
- text: "❌ No active dev3000 sessions found. Make sure dev3000 is running!"
537
- }
538
- ]
539
- }
540
- }
541
-
542
- // Auto-select if there's only one session
543
- logToDevFile(`fix_my_app: Found ${sessions.length} sessions`)
544
- if (sessions.length === 1) {
545
- projectName = sessions[0].projectName
546
- logPath = getLogPath(projectName)
547
- logToDevFile(`fix_my_app: Auto-selected single session: ${projectName}, logPath: ${logPath}`)
548
-
549
- // If still no log path after auto-select, return error
550
- if (!logPath) {
551
- return {
552
- content: [
553
- {
554
- type: "text",
555
- text: `❌ Could not find log file for project "${projectName}". The session may not be properly initialized yet.`
556
- }
557
- ]
558
- }
559
- }
560
- } else {
561
- const sessionList = sessions
562
- .map((s) => `• ${s.projectName} (started ${new Date(s.startTime).toLocaleString()})`)
563
- .join("\n")
564
-
565
- return {
566
- content: [
567
- {
568
- type: "text",
569
- text: `🔍 Found ${sessions.length} dev3000 sessions. Please specify which project to fix:\n${sessionList}\n\n💡 Use: projectName: "your-project-name" parameter`
570
- }
571
- ]
572
- }
573
- }
574
- }
575
-
576
- const results: string[] = []
577
-
578
- // Mode-specific handling
579
- if (mode === "bisect" && waitForUserInteraction) {
580
- const startTime = new Date().toISOString()
581
- results.push("🕐 **TIMESTAMP BISECT MODE ACTIVATED**")
582
- results.push(`📍 Start Time: ${startTime}`)
583
- results.push("")
584
- results.push("🎯 **NOW INTERACT WITH YOUR APP TO REPRODUCE THE ISSUE!**")
585
- results.push("• Click buttons, navigate, submit forms, etc.")
586
- results.push("• Reproduce the exact error scenario")
587
- results.push("• When done, run this tool again WITHOUT waitForUserInteraction")
588
- results.push("")
589
- results.push("💡 I'll analyze everything that happens between these timestamps!")
590
-
591
- return {
592
- content: [{ type: "text", text: results.join("\n") }]
593
- }
594
- }
595
-
596
- try {
597
- // Check if log file exists before reading
598
- if (!existsSync(logPath)) {
599
- results.push("📋 Log file doesn't exist yet. The dev server may still be starting up.")
600
- results.push("💡 Wait a few seconds for the server to generate logs, then try again.")
601
- return {
602
- content: [{ type: "text", text: results.join("\n") }]
603
- }
604
- }
605
-
606
- const content = readFileSync(logPath, "utf-8")
607
- const logLines = content.trim().split("\n").filter(Boolean)
608
-
609
- if (logLines.length === 0) {
610
- results.push("📋 Log file is empty. Make sure your app is running and generating logs.")
611
- return {
612
- content: [{ type: "text", text: results.join("\n") }]
613
- }
614
- }
615
-
616
- results.push(`🔍 **FIX MY APP ANALYSIS** - Mode: ${mode.toUpperCase()}`)
617
- results.push(`📁 Log file: ${logPath}`)
618
- results.push(`📊 Total log entries: ${logLines.length}`)
619
- results.push("")
620
-
621
- // Time-based filtering
622
- const now = new Date()
623
- const cutoffTime = new Date(now.getTime() - timeRangeMinutes * 60 * 1000)
624
-
625
- // Comprehensive error patterns
626
- const errorPatterns = [
627
- /ERROR/i,
628
- /FAIL/i,
629
- /Exception/i,
630
- /CRITICAL/i,
631
- /FATAL/i,
632
- /crashed/i,
633
- /undefined/i,
634
- /null reference/i,
635
- /cannot read/i,
636
- /cannot find/i,
637
- /not found/i,
638
- /timeout/i,
639
- /refused/i,
640
- /denied/i,
641
- /unauthorized/i,
642
- /404/,
643
- /500/,
644
- /503/,
645
- /WARN/i,
646
- /WARNING/i,
647
- /deprecated/i,
648
- /slow/i,
649
- /retry/i,
650
- /RUNTIME\.ERROR/,
651
- /hydration.*mismatch/i,
652
- /Uncaught/i,
653
- /throwOnHydrationMismatch/i
654
- ]
655
-
656
- // Filter logs by time range (replaces get_logs_between_timestamps)
657
- const timeFilteredLines = logLines.filter((line) => {
658
- // Try ISO format first (e.g., 2025-09-23T22:03:55.068Z)
659
- const isoMatch = line.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\]/)
660
- if (isoMatch) {
661
- const logTime = new Date(isoMatch[1])
662
- return logTime >= cutoffTime
663
- }
664
-
665
- // Try time-only format (e.g., 15:04:03.987)
666
- const timeMatch = line.match(/\[(\d{2}):(\d{2}):(\d{2})\.(\d{3})\]/)
667
- if (timeMatch) {
668
- // For time-only format, assume it's from today
669
- const now = new Date()
670
- const logTime = new Date(
671
- now.getFullYear(),
672
- now.getMonth(),
673
- now.getDate(),
674
- parseInt(timeMatch[1], 10),
675
- parseInt(timeMatch[2], 10),
676
- parseInt(timeMatch[3], 10),
677
- parseInt(timeMatch[4], 10)
678
- )
679
-
680
- // If the time is in the future (e.g., log shows 15:04 but now is 14:00),
681
- // assume it was from yesterday
682
- if (logTime > now) {
683
- logTime.setDate(logTime.getDate() - 1)
684
- }
685
-
686
- return logTime >= cutoffTime
687
- }
688
-
689
- // If no timestamp found, include the line (better to show more than miss errors)
690
- return true
691
- })
692
-
693
- // Extract ALL error types (replaces multiple error detection tools)
694
- const allErrors = timeFilteredLines.filter((line) => {
695
- return errorPatterns.some((pattern) => pattern.test(line))
696
- })
697
-
698
- // Extract react-scan performance data
699
- const reactScanLines = timeFilteredLines.filter(
700
- (line) => line.includes("react-scan") || line.includes("ReactScan") || line.includes("React render")
701
- )
702
-
703
- // Parse react-scan performance metrics
704
- const reactScanMetrics = {
705
- unnecessaryRenders: reactScanLines.filter(
706
- (line) => line.includes("unnecessary") || line.includes("re-render") || line.includes("wasted")
707
- ),
708
- slowComponents: reactScanLines.filter(
709
- (line) => line.includes("slow") || line.includes("performance") || /\d+ms/.test(line)
710
- ),
711
- totalRenders: reactScanLines.filter((line) => line.includes("render")).length
712
- }
713
-
714
- // Filter out framework noise (unfixable warnings from Next.js, React, etc.)
715
- const frameworkNoisePatterns = [
716
- /link rel=preload.*must have.*valid.*as/i, // Next.js font optimization warning - not actionable
717
- /next\/font/i, // Next.js font-related warnings
718
- /automatically generated/i, // Auto-generated code warnings
719
- /\[NETWORK\].*\b(200|201|204|304)\b\s+(OK|Created|No Content|Not Modified)/i // Successful HTTP responses - not errors
720
- ]
721
-
722
- const actionableErrors = allErrors.filter((line) => {
723
- return !frameworkNoisePatterns.some((pattern) => pattern.test(line))
724
- })
725
-
726
- // Categorize errors for better analysis
727
- const categorizedErrors = {
728
- serverErrors: actionableErrors.filter(
729
- (line) => line.includes("[SERVER]") && (line.includes("ERROR") || line.includes("Exception"))
730
- ),
731
- browserErrors: actionableErrors.filter(
732
- (line) =>
733
- line.includes("[BROWSER]") &&
734
- (line.includes("ERROR") || line.includes("CONSOLE ERROR") || line.includes("RUNTIME.ERROR"))
735
- ),
736
- buildErrors: actionableErrors.filter(
737
- (line) => line.includes("Failed to compile") || line.includes("Type error") || line.includes("Build failed")
738
- ),
739
- networkErrors: actionableErrors.filter((line) => {
740
- // Exclude successful status codes
741
- if (/\b(200|201|204|304)\b/.test(line)) return false
742
- return line.includes("NETWORK") || line.includes("404") || line.includes("500") || line.includes("timeout")
743
- }),
744
- warnings: actionableErrors.filter(
745
- (line) => /WARN|WARNING|deprecated/i.test(line) && !/ERROR|Exception|FAIL/i.test(line)
746
- )
747
- }
748
-
749
- const totalErrors = actionableErrors.length
750
- const criticalErrors = totalErrors - categorizedErrors.warnings.length
751
-
752
- // Also check for any errors in the entire log file (not just time filtered)
753
- const allLogErrors = logLines.filter((line) => {
754
- return errorPatterns.some((pattern) => pattern.test(line))
755
- })
756
- const recentErrorsOutsideTimeRange = allLogErrors.length > totalErrors
757
-
758
- // Helper function to find preceding interaction events for any error
759
- const findInteractionsBeforeError = (errorLine: string, allLines: string[]): string[] => {
760
- const errorIndex = allLines.indexOf(errorLine)
761
- if (errorIndex === -1) return []
762
-
763
- const interactions: string[] = []
764
- // Look back up to 20 lines or 5 interactions
765
- for (let i = errorIndex - 1; i >= Math.max(0, errorIndex - 20) && interactions.length < 5; i--) {
766
- if (
767
- allLines[i].includes("[INTERACTION]") ||
768
- allLines[i].includes("[NAVIGATION]") ||
769
- allLines[i].includes("[PAGE]")
770
- ) {
771
- interactions.unshift(allLines[i])
772
- }
773
- }
774
- return interactions
775
- }
776
-
777
- if (totalErrors === 0 && !recentErrorsOutsideTimeRange) {
778
- results.push(`No errors found in last ${timeRangeMinutes} minutes.`)
779
- results.push("Application appears healthy.")
780
-
781
- if (includeTimestampInstructions && mode !== "monitor") {
782
- results.push("")
783
- results.push("Options:")
784
- results.push("• Use mode='bisect' to compare before/after states during testing")
785
- results.push("• Use mode='monitor' for continuous monitoring")
786
- results.push("• Increase timeRangeMinutes to analyze a longer period")
787
- }
788
- } else if (totalErrors === 0 && recentErrorsOutsideTimeRange) {
789
- results.push(
790
- `No errors in last ${timeRangeMinutes} minutes, but found ${allLogErrors.length} errors in full log.`
791
- )
792
- results.push("")
793
- results.push("Older errors (outside time range):")
794
- // Show last 5 errors from the full log with their interactions
795
- allLogErrors.slice(-5).forEach((error) => {
796
- const interactions = findInteractionsBeforeError(error, logLines)
797
- if (interactions.length > 0) {
798
- results.push(" Preceding interactions:")
799
- for (const interaction of interactions) {
800
- results.push(` ${interaction}`)
801
- }
802
- }
803
- results.push(` - ${error}`)
804
- results.push("")
805
- })
806
- results.push("To analyze these errors, increase timeRangeMinutes (e.g., timeRangeMinutes=60)")
807
- } else {
808
- results.push(
809
- `**${totalErrors} ISSUES DETECTED** (${criticalErrors} critical, ${categorizedErrors.warnings.length} warnings)`
810
- )
811
- results.push("")
812
- results.push("**ACTION REQUIRED:** Fix the highest-priority error below, then call fix_my_app again to verify.")
813
- results.push("")
814
-
815
- // Show categorized errors with their preceding interactions
816
- if (categorizedErrors.serverErrors.length > 0) {
817
- results.push("SERVER ERRORS:")
818
- categorizedErrors.serverErrors.slice(-5).forEach((error) => {
819
- const interactions = findInteractionsBeforeError(error, logLines)
820
- if (interactions.length > 0) {
821
- results.push(" Preceding interactions:")
822
- for (const interaction of interactions) {
823
- results.push(` ${interaction}`)
824
- }
825
- }
826
- results.push(` - ${error}`)
827
- results.push("")
828
- })
829
- }
830
-
831
- if (categorizedErrors.browserErrors.length > 0) {
832
- results.push("BROWSER/CONSOLE ERRORS:")
833
- categorizedErrors.browserErrors.slice(-5).forEach((error) => {
834
- const interactions = findInteractionsBeforeError(error, logLines)
835
- if (interactions.length > 0) {
836
- results.push(" Preceding interactions:")
837
- for (const interaction of interactions) {
838
- results.push(` ${interaction}`)
839
- }
840
- }
841
- results.push(` - ${error}`)
842
- results.push("")
843
- })
844
- }
845
-
846
- if (categorizedErrors.buildErrors.length > 0) {
847
- results.push("BUILD/COMPILATION ERRORS:")
848
- categorizedErrors.buildErrors.slice(-5).forEach((error) => {
849
- const interactions = findInteractionsBeforeError(error, logLines)
850
- if (interactions.length > 0) {
851
- results.push(" Preceding interactions:")
852
- for (const interaction of interactions) {
853
- results.push(` ${interaction}`)
854
- }
855
- }
856
- results.push(` - ${error}`)
857
- results.push("")
858
- })
859
- }
860
-
861
- if (categorizedErrors.networkErrors.length > 0) {
862
- results.push("NETWORK/API ERRORS:")
863
- categorizedErrors.networkErrors.slice(-5).forEach((error) => {
864
- const interactions = findInteractionsBeforeError(error, logLines)
865
- if (interactions.length > 0) {
866
- results.push(" Preceding interactions:")
867
- for (const interaction of interactions) {
868
- results.push(` ${interaction}`)
869
- }
870
- }
871
- results.push(` - ${error}`)
872
- results.push("")
873
- })
874
- }
875
-
876
- if (categorizedErrors.warnings.length > 0 && focusArea === "all") {
877
- results.push(`WARNINGS (${categorizedErrors.warnings.length} found, showing recent):`)
878
- results.push(categorizedErrors.warnings.slice(-3).join("\n"))
879
- results.push("")
880
- }
881
-
882
- // Show the diagnose-fix-verify loop
883
- results.push("---")
884
- results.push("**NEXT: Fix the highest-priority issue, then call fix_my_app again to verify.**")
885
- results.push("")
886
- results.push("Keep calling fix_my_app after each fix until no errors remain.")
887
-
888
- // Add integration-aware suggestions
889
- if (integrateNextjs || integrateChromeDevtools) {
890
- // Log that integrations are being used in fix analysis
891
- const activeIntegrations = []
892
- if (integrateNextjs) activeIntegrations.push("Next.js")
893
- if (integrateChromeDevtools) activeIntegrations.push("Chrome DevTools")
894
- logToDevFile(
895
- `Fix Analysis: Using active MCP integrations [${activeIntegrations.join(", ")}] for enhanced error analysis`,
896
- projectName
897
- )
898
-
899
- results.push("")
900
- results.push("**Available MCP integrations:**")
901
-
902
- if (integrateNextjs) {
903
- results.push("")
904
- results.push("Next.js MCP (nextjs-dev):")
905
- const nextjsSuggestions = await generateNextjsSuggestions(allErrors.join(" "))
906
- nextjsSuggestions.forEach((suggestion) => {
907
- const params = suggestion.params
908
- ? `(${Object.entries(suggestion.params)
909
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
910
- .join(", ")})`
911
- : "()"
912
- results.push(` • nextjs-dev.${suggestion.function}${params}`)
913
- results.push(` Reason: ${suggestion.reason}`)
914
- })
915
-
916
- if (categorizedErrors.serverErrors.length > 0) {
917
- results.push(" • Check Next.js build/runtime logs for SSR/hydration issues")
918
- }
919
- }
920
-
921
- if (integrateChromeDevtools) {
922
- results.push("")
923
- results.push("Chrome DevTools MCP (chrome-devtools):")
924
- const chromeSuggestions = await generateChromeDevtoolsSuggestions(allErrors.join(" "))
925
- chromeSuggestions.forEach((suggestion) => {
926
- const params = suggestion.params
927
- ? `(${Object.entries(suggestion.params)
928
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
929
- .join(", ")})`
930
- : "()"
931
- results.push(` • chrome-devtools.${suggestion.function}${params}`)
932
- results.push(` Reason: ${suggestion.reason}`)
933
- })
934
-
935
- if (categorizedErrors.browserErrors.length > 0) {
936
- results.push(" • Use DOM inspection for UI issues")
937
- }
938
- if (categorizedErrors.networkErrors.length > 0) {
939
- results.push(" • Inspect network requests for detailed error context")
940
- }
941
- }
942
- }
943
-
944
- // Find the single highest priority error and optionally create a PR
945
- const highestPriorityError = findHighestPriorityError(categorizedErrors, actionableErrors, logLines)
946
-
947
- if (highestPriorityError) {
948
- results.push("")
949
- results.push("---")
950
- results.push("**HIGHEST PRIORITY ISSUE (fix this first):**")
951
- results.push(`Priority Score: ${highestPriorityError.priorityScore}`)
952
- results.push(`Category: ${highestPriorityError.category.toUpperCase()}`)
953
- results.push(`Severity: ${highestPriorityError.severity.toUpperCase()}`)
954
- results.push("")
955
- results.push("Error:")
956
- results.push(` ${highestPriorityError.error}`)
957
-
958
- if (highestPriorityError.interactions.length > 0) {
959
- results.push("")
960
- results.push("Reproduction steps:")
961
- highestPriorityError.interactions.forEach((interaction, idx) => {
962
- results.push(` ${idx + 1}. ${interaction}`)
963
- })
964
- }
965
-
966
- // Create PR if requested
967
- if (createPR) {
968
- results.push("")
969
- results.push("Creating PR branch for this issue...")
970
- const prResult = await createPRForIssue(highestPriorityError, projectName || "")
971
- results.push(prResult)
972
- } else {
973
- results.push("")
974
- results.push("To create a PR branch for this issue, run: fix_my_app(createPR=true)")
975
- }
976
- }
977
- }
978
-
979
- // Extract screenshot information (replaces get_recent_screenshots)
980
- const screenshotLines = logLines.filter(
981
- (line) => line.includes("[SCREENSHOT]") || line.includes("Screenshot captured")
982
- )
983
- if (screenshotLines.length > 0) {
984
- results.push("")
985
- results.push(`📸 **SCREENSHOTS CAPTURED** (${screenshotLines.length} total):`)
986
- screenshotLines.slice(-5).forEach((line) => {
987
- const match = line.match(/Screenshot captured: (.+)$/)
988
- if (match) {
989
- results.push(`• ${match[1]}`)
990
- }
991
- })
992
- results.push("")
993
- results.push("💡 **TIP**: Use analyze_visual_diff tool to compare screenshots and identify changes")
994
- results.push(" (Advanced: screenshots are also accessible via curl if needed)")
995
- }
996
-
997
- // Jank/Layout Shift Detection (from ScreencastManager passive captures)
998
- if (focusArea === "performance" || focusArea === "all") {
999
- const jankResult = await detectJankFromScreenshots(projectName)
1000
- if (jankResult.detections.length > 0) {
1001
- // Get MCP port for video viewer URL
1002
- const sessionInfo = findActiveSessions().find((s) => s.projectName === projectName)
1003
- const mcpPort = sessionInfo ? sessionInfo.sessionFile.match(/"mcpPort":\s*"(\d+)"/)?.[1] || "3684" : "3684"
1004
- const videoUrl = `http://localhost:${mcpPort}/video/${jankResult.sessionId}`
1005
-
1006
- results.push("")
1007
-
1008
- if (jankResult.realCLS) {
1009
- results.push(
1010
- `🚨 **LAYOUT SHIFT DETECTED** (${jankResult.detections.length} ${jankResult.detections.length === 1 ? "shift" : "shifts"} during page load):`
1011
- )
1012
- } else {
1013
- results.push(
1014
- `🚨 **LOADING JANK DETECTED** (${jankResult.detections.length} layout ${jankResult.detections.length === 1 ? "shift" : "shifts"} found):`
1015
- )
1016
- }
1017
-
1018
- const triggerLabel =
1019
- jankResult.captureTrigger === "navigation"
1020
- ? "Navigation complete"
1021
- : jankResult.captureTrigger === "load"
1022
- ? "Load complete"
1023
- : "View all frames"
1024
- results.push(`📹 **${triggerLabel}**: ${videoUrl}`)
1025
- results.push(`🎞️ **Session ID**: ${jankResult.sessionId} (${jankResult.totalFrames} frames)`)
1026
- results.push("")
1027
-
1028
- jankResult.detections.forEach((jank) => {
1029
- const emoji = jank.severity === "high" ? "🔴" : jank.severity === "medium" ? "🟡" : "🟢"
1030
-
1031
- if (jank.uxImpact) {
1032
- results.push(`${emoji} **${jank.timeSinceStart}ms** - ${jank.element}`)
1033
- results.push(` ${jank.uxImpact}`)
1034
- } else {
1035
- results.push(
1036
- `${emoji} **${jank.timeSinceStart}ms**: ${jank.visualDiff.toFixed(1)}% of screen changed (${jank.severity} severity)`
1037
- )
1038
- }
1039
-
1040
- // Include Before/After frame URLs if available
1041
- if (jank.beforeFrameUrl && jank.afterFrameUrl) {
1042
- results.push(` 📸 Before: ${jank.beforeFrameUrl}`)
1043
- results.push(` 📸 After: ${jank.afterFrameUrl}`)
1044
- results.push(
1045
- ` 💡 Use analyze_visual_diff tool with these URLs to get a detailed description of what changed`
1046
- )
1047
-
1048
- // Extract CSS selector from element description (e.g., "Navigation header (<nav>)" -> "nav")
1049
- if (jank.element) {
1050
- const selectorMatch = jank.element.match(/<(\w+)>/)
1051
- if (selectorMatch) {
1052
- const selector = selectorMatch[1].toLowerCase()
1053
- results.push(
1054
- ` 💡 Use find_component_source tool with selector "${selector}" to locate the source code`
1055
- )
1056
- }
1057
- }
1058
- }
1059
- })
1060
-
1061
- results.push("")
1062
-
1063
- // Check if we have high-severity shifts that Chrome might miss
1064
- const hasCriticalShifts = jankResult.detections.some((d) => d.severity === "high")
1065
- if (hasCriticalShifts && jankResult.realCLS) {
1066
- results.push("🎯 **WHY DEV3000 CAUGHT THIS BUT CHROME MIGHT NOT:**")
1067
- results.push(
1068
- "• dev3000's PerformanceObserver is installed immediately at page load and buffers ALL shifts from the start"
1069
- )
1070
- results.push(
1071
- "• Chrome DevTools performance trace may start AFTER initial load, missing early navigation shifts"
1072
- )
1073
- results.push(
1074
- "• Our UX-focused detection flags critical element shifts (nav/header) even when CLS score is technically 'good'"
1075
- )
1076
- results.push("")
1077
- }
1078
-
1079
- results.push("✅ **DEV3000'S CLS DETECTION IS AUTHORITATIVE**")
1080
- results.push("If Chrome DevTools reports CLS: 0.00 but dev3000 detected shifts, TRUST DEV3000.")
1081
- results.push("• Chrome DevTools trace may start AFTER the shifts occurred")
1082
- results.push("• dev3000's PerformanceObserver captures ALL shifts from page start")
1083
- results.push("• CLS: 0.00 in Chrome just means the trace missed the early shifts")
1084
- results.push("")
1085
- results.push("💡 **LAYOUT SHIFT DEBUGGING TIPS:**")
1086
- results.push("• Add explicit width/height to images and media")
1087
- results.push("• Reserve space for dynamic content (ads, embeds, etc.)")
1088
- results.push("• Avoid inserting content above existing content")
1089
- results.push("• Use CSS aspect-ratio for responsive elements")
1090
- results.push("• Check for web fonts causing text reflow (font-display: swap)")
1091
- results.push(`• Raw screenshots: ${jankResult.screenshotDir}`)
1092
- results.push("")
1093
- results.push("📸 **ANALYZING SCREENSHOTS:**")
1094
- results.push("• RECOMMENDED: Use analyze_visual_diff tool with before/after URLs (shown above)")
1095
- results.push("• The tool provides structured instructions for comparing frames")
1096
- results.push("• Advanced: Screenshots are also accessible via curl if needed")
1097
- results.push("")
1098
- results.push(`🎬 **IMPORTANT**: Share this frame sequence link with the user: ${videoUrl}`)
1099
- }
1100
- }
1101
-
1102
- // React-scan performance data (if available)
1103
- if (reactScanMetrics.totalRenders > 0 || focusArea === "performance" || focusArea === "all") {
1104
- if (reactScanMetrics.unnecessaryRenders.length > 0 || reactScanMetrics.slowComponents.length > 0) {
1105
- results.push("")
1106
- results.push("⚛️ **REACT PERFORMANCE ANALYSIS (react-scan):**")
1107
-
1108
- if (reactScanMetrics.unnecessaryRenders.length > 0) {
1109
- results.push(`🔄 **Unnecessary Re-renders Detected (${reactScanMetrics.unnecessaryRenders.length}):**`)
1110
- reactScanMetrics.unnecessaryRenders.slice(-5).forEach((line) => {
1111
- results.push(`• ${line}`)
1112
- })
1113
- results.push("")
1114
- }
1115
-
1116
- if (reactScanMetrics.slowComponents.length > 0) {
1117
- results.push(`🐌 **Slow Components Found (${reactScanMetrics.slowComponents.length}):**`)
1118
- reactScanMetrics.slowComponents.slice(-5).forEach((line) => {
1119
- results.push(`• ${line}`)
1120
- })
1121
- results.push("")
1122
- }
1123
-
1124
- results.push("💡 **REACT OPTIMIZATION TIPS:**")
1125
- results.push("• Use React.memo() for components with expensive renders")
1126
- results.push("• Use useMemo/useCallback to prevent unnecessary re-renders")
1127
- results.push("• Check for unstable prop references (objects/arrays created in render)")
1128
- results.push("• Consider using React DevTools Profiler for deeper analysis")
1129
- }
1130
- }
1131
-
1132
- // Performance insights (if no errors but looking at performance)
1133
- if (totalErrors === 0 && focusArea === "all") {
1134
- const performanceLines = logLines.filter((line) => line.includes("took") && line.includes("ms"))
1135
- if (performanceLines.length > 0) {
1136
- results.push("")
1137
- results.push("⚡ **PERFORMANCE INSIGHTS:**")
1138
- performanceLines.slice(-5).forEach((line) => {
1139
- results.push(`• ${line}`)
1140
- })
1141
- }
1142
- }
1143
-
1144
- // Return structured data if requested
1145
- if (returnRawData) {
1146
- logToDevFile(
1147
- `Structured Output: Returning structured data for Claude orchestration with ${totalErrors} errors and ${integrateNextjs || integrateChromeDevtools ? "active" : "no"} integrations`,
1148
- projectName
1149
- )
1150
- const structuredErrors: ErrorWithInteractions[] = allErrors.map((error) => {
1151
- const interactions = findInteractionsBeforeError(error, logLines)
1152
- const category = categorizedErrors.serverErrors.includes(error)
1153
- ? "server"
1154
- : categorizedErrors.browserErrors.includes(error)
1155
- ? "browser"
1156
- : categorizedErrors.buildErrors.includes(error)
1157
- ? "build"
1158
- : categorizedErrors.networkErrors.includes(error)
1159
- ? "network"
1160
- : categorizedErrors.warnings.includes(error)
1161
- ? "warning"
1162
- : "general"
1163
-
1164
- const severity = categorizedErrors.warnings.includes(error)
1165
- ? ("warning" as const)
1166
- : error.includes("CRITICAL") || error.includes("FATAL") || error.includes("crashed")
1167
- ? ("critical" as const)
1168
- : ("error" as const)
1169
-
1170
- // Extract timestamp from error line
1171
- const timestampMatch =
1172
- error.match(/\[(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)\]/) ||
1173
- error.match(/\[(\d{2}:\d{2}:\d{2}\.\d{3})\]/)
1174
- const timestamp = timestampMatch ? timestampMatch[1] : new Date().toISOString()
1175
-
1176
- return {
1177
- timestamp,
1178
- category,
1179
- message: error,
1180
- interactions,
1181
- severity
1182
- }
1183
- })
1184
-
1185
- const structuredFixes: CodeFix[] = []
1186
-
1187
- // Generate intelligent fix suggestions based on error patterns
1188
- structuredErrors.forEach((error) => {
1189
- if (error.category === "hydration" || error.message.includes("hydration")) {
1190
- structuredFixes.push({
1191
- file: "pages/_app.js or components/[component].tsx",
1192
- description: "Fix hydration mismatch",
1193
- code: `// Ensure server and client render the same content
1194
- // Use useEffect for client-only logic
1195
- useEffect(() => {
1196
- // Client-only code here
1197
- }, [])`,
1198
- reason: "Hydration errors occur when server and client render different content"
1199
- })
1200
- }
1201
-
1202
- if (error.message.includes("TypeError") || error.message.includes("undefined")) {
1203
- structuredFixes.push({
1204
- file: "Identify from stack trace in error message",
1205
- description: "Add null/undefined checks",
1206
- code: `// Add defensive programming checks
1207
- if (data && data.property) {
1208
- // Safe to use data.property
1209
- }
1210
- // Or use optional chaining
1211
- const value = data?.property?.nestedProperty`,
1212
- reason: "Prevent TypeError by checking for undefined/null values"
1213
- })
1214
- }
1215
-
1216
- if (error.message.includes("404") || error.message.includes("not found")) {
1217
- structuredFixes.push({
1218
- file: "routing configuration or API endpoints",
1219
- description: "Fix missing route or resource",
1220
- code: `// Check route configuration
1221
- // Ensure API endpoint exists
1222
- // Verify file paths are correct`,
1223
- reason: "404 errors indicate missing resources or incorrect paths"
1224
- })
1225
- }
1226
- })
1227
-
1228
- const suggestedIntegrations: StructuredAnalysisResult["suggestedIntegrations"] = {}
1229
-
1230
- if (integrateNextjs) {
1231
- suggestedIntegrations.nextjs = await generateNextjsSuggestions(allErrors.join(" "))
1232
- }
1233
-
1234
- if (integrateChromeDevtools) {
1235
- suggestedIntegrations.chrome = await generateChromeDevtoolsSuggestions(allErrors.join(" "))
1236
- }
1237
-
1238
- // Create workflow plan if integrations are available
1239
- let workflowPlan: StructuredAnalysisResult["workflowPlan"]
1240
-
1241
- if (integrateNextjs || integrateChromeDevtools) {
1242
- workflowPlan = {
1243
- phase1: {
1244
- name: "Data Collection",
1245
- description: "Parallel data gathering across all available MCPs",
1246
- actions: [
1247
- {
1248
- mcp: "dev3000",
1249
- function: "fix_my_app",
1250
- params: { focusArea, integrateNextjs, integrateChromeDevtools, returnRawData: true },
1251
- reason: "Get comprehensive error analysis with interaction data"
1252
- }
1253
- ],
1254
- estimatedTime: "2-3 minutes"
1255
- },
1256
- phase2: {
1257
- name: "Deep Analysis",
1258
- description: "Cross-MCP correlation and targeted investigation",
1259
- actions: [
1260
- {
1261
- mcp: "dev3000",
1262
- function: "fix_my_app",
1263
- params: { mode: "bisect" },
1264
- reason: "Regression analysis if needed"
1265
- }
1266
- ],
1267
- estimatedTime: "3-5 minutes"
1268
- },
1269
- phase3: {
1270
- name: "Fix & Verify",
1271
- description: "Implementation and verification across all layers",
1272
- actions: [
1273
- {
1274
- mcp: "dev3000",
1275
- function: "execute_browser_action",
1276
- reason: "Replay interactions to verify fixes"
1277
- }
1278
- ],
1279
- estimatedTime: "5-10 minutes"
1280
- }
1281
- }
1282
-
1283
- // Add Next.js actions to workflow
1284
- if (integrateNextjs && suggestedIntegrations.nextjs) {
1285
- workflowPlan.phase1.actions.push(
1286
- ...suggestedIntegrations.nextjs
1287
- .filter((s) => s.priority === "high")
1288
- .map((s) => ({
1289
- mcp: "nextjs-dev",
1290
- function: s.function,
1291
- params: s.params,
1292
- reason: s.reason
1293
- }))
1294
- )
1295
-
1296
- workflowPlan.phase3.actions.push({
1297
- mcp: "nextjs-dev",
1298
- function: "check_build_status",
1299
- reason: "Verify build success after fixes"
1300
- })
1301
- }
1302
-
1303
- // Add Chrome actions to workflow
1304
- if (integrateChromeDevtools && suggestedIntegrations.chrome) {
1305
- workflowPlan.phase1.actions.push(
1306
- ...suggestedIntegrations.chrome
1307
- .filter((s) => s.priority === "high")
1308
- .map((s) => ({
1309
- mcp: "chrome-devtools",
1310
- function: s.function,
1311
- params: s.params,
1312
- reason: s.reason
1313
- }))
1314
- )
1315
- }
1316
- }
1317
-
1318
- const structuredResult: StructuredAnalysisResult = {
1319
- errors: structuredErrors,
1320
- fixes: structuredFixes,
1321
- suggestedIntegrations,
1322
- workflowPlan,
1323
- summary: {
1324
- totalErrors: totalErrors,
1325
- criticalErrors: criticalErrors,
1326
- hasIntegrations: integrateNextjs || integrateChromeDevtools,
1327
- estimatedFixTime: calculateEstimatedTime(totalErrors, integrateNextjs || integrateChromeDevtools)
1328
- }
1329
- }
1330
-
1331
- return {
1332
- content: [{ type: "text", text: JSON.stringify(structuredResult, null, 2) }]
1333
- }
1334
- }
1335
-
1336
- // Add augmented analysis suggestions when specialized MCPs are available
1337
- const canDelegateChrome = await canDelegateToChromeDevtools("profile_performance")
1338
-
1339
- if (canDelegateNextjs || canDelegateChrome) {
1340
- results.push("")
1341
- results.push("🔗 **AUGMENTED ANALYSIS AVAILABLE**")
1342
- results.push("")
1343
- results.push("dev3000 provided the core log analysis above. For deeper insights, consider also gathering:")
1344
- results.push("")
1345
-
1346
- if (canDelegateNextjs) {
1347
- results.push("**Next.js Framework Analysis:**")
1348
- const dynamicNextjsSuggestions = await generateNextjsSuggestions(allErrors.join(" "))
1349
- dynamicNextjsSuggestions.slice(0, 3).forEach((suggestion) => {
1350
- results.push(`• \`dev3000-nextjs-dev:${suggestion.function}()\` - ${suggestion.reason}`)
1351
- })
1352
- results.push("")
1353
- }
1354
-
1355
- if (canDelegateChrome) {
1356
- results.push("**Browser-Side Analysis:**")
1357
- const dynamicChromeSuggestions = await generateChromeDevtoolsSuggestions(allErrors.join(" "))
1358
- dynamicChromeSuggestions.slice(0, 3).forEach((suggestion) => {
1359
- results.push(`• \`dev3000-chrome-devtools:${suggestion.function}()\` - ${suggestion.reason}`)
1360
- })
1361
- results.push("")
1362
- }
1363
-
1364
- results.push(
1365
- "💡 **Best approach:** Use dev3000's log analysis as your foundation, then gather specific additional data as needed for a complete picture."
1366
- )
1367
- }
1368
-
1369
- return {
1370
- content: [{ type: "text", text: results.join("\n") }]
1371
- }
1372
- } catch (error) {
1373
- return {
1374
- content: [
1375
- {
1376
- type: "text",
1377
- text: `Error analyzing logs: ${error instanceof Error ? error.message : String(error)}`
1378
- }
1379
- ]
1380
- }
1381
- }
1382
- }
1383
-
1384
- // Dynamic MCP capability discovery and filtering
1385
- interface McpCapability {
1386
- function: string
1387
- description?: string
1388
- parameters?: Record<string, unknown>
1389
- category: "advanced" | "basic"
1390
- reason: string
1391
- }
1392
-
1393
- interface McpSchemaCache {
1394
- timestamp: number
1395
- capabilities: McpCapability[]
1396
- }
1397
-
1398
- // Cache for discovered MCP capabilities (5 minute TTL)
1399
- const MCP_CAPABILITY_CACHE = new Map<string, McpSchemaCache>()
1400
- const CAPABILITY_CACHE_TTL = 5 * 60 * 1000 // 5 minutes
1401
-
1402
- // Keywords that indicate advanced capabilities (vs basic automation)
1403
- const ADVANCED_CAPABILITY_KEYWORDS = {
1404
- chrome: [
1405
- "inspect",
1406
- "debug",
1407
- "profile",
1408
- "performance",
1409
- "console",
1410
- "devtools",
1411
- "breakpoint",
1412
- "intercept",
1413
- "storage",
1414
- "memory",
1415
- "trace"
1416
- ],
1417
- nextjs: ["build", "hydration", "ssr", "routing", "analyze", "debug", "render", "middleware", "optimization"]
1418
- }
1419
-
1420
- // Basic capabilities that dev3000 handles well (should not suggest these)
1421
- const DEV3000_BASIC_CAPABILITIES = [
1422
- "screenshot",
1423
- "navigate",
1424
- "click",
1425
- "type",
1426
- "scroll",
1427
- "evaluate",
1428
- "simple_script",
1429
- "get_logs",
1430
- "basic_build_status",
1431
- "simple_error_check"
1432
- ]
1433
-
1434
- /**
1435
- * Dynamically discover MCP capabilities by introspecting their schemas
1436
- */
1437
- async function discoverMcpCapabilities(mcpName: string): Promise<McpCapability[]> {
1438
- const cacheKey = mcpName
1439
- const cached = MCP_CAPABILITY_CACHE.get(cacheKey)
1440
-
1441
- // Return cached capabilities if still fresh
1442
- if (cached && Date.now() - cached.timestamp < CAPABILITY_CACHE_TTL) {
1443
- logToDevFile(
1444
- `Capability Discovery: Using cached capabilities for ${mcpName} (${cached.capabilities.length} functions)`
1445
- )
1446
- return cached.capabilities
1447
- }
1448
-
1449
- logToDevFile(`Capability Discovery: Fetching fresh capabilities for ${mcpName}`)
1450
-
1451
- try {
1452
- // Method 1: Try to get MCP schema via tools/list request (MCP protocol standard)
1453
- const capabilities = await introspectMcpTools(mcpName)
1454
-
1455
- if (capabilities.length > 0) {
1456
- // Cache the results
1457
- MCP_CAPABILITY_CACHE.set(cacheKey, {
1458
- timestamp: Date.now(),
1459
- capabilities
1460
- })
1461
-
1462
- logToDevFile(`Capability Discovery: Successfully discovered ${capabilities.length} capabilities for ${mcpName}`)
1463
- return capabilities
1464
- }
1465
-
1466
- // Method 2: Fallback to checking available function names from logs/errors
1467
- const fallbackCapabilities = await inferCapabilitiesFromLogs(mcpName)
1468
-
1469
- // Cache even fallback results to avoid repeated failures
1470
- MCP_CAPABILITY_CACHE.set(cacheKey, {
1471
- timestamp: Date.now(),
1472
- capabilities: fallbackCapabilities
1473
- })
1474
-
1475
- logToDevFile(
1476
- `Capability Discovery: Using fallback inference for ${mcpName} (${fallbackCapabilities.length} functions)`
1477
- )
1478
- return fallbackCapabilities
1479
- } catch (error) {
1480
- logToDevFile(`Capability Discovery: Failed to discover capabilities for ${mcpName} - ${error}`)
1481
- return []
1482
- }
1483
- }
1484
-
1485
- /**
1486
- * Introspect MCP tools using the standard tools/list request
1487
- */
1488
- async function introspectMcpTools(mcpName: string): Promise<McpCapability[]> {
1489
- // For stdio MCPs, we can try to discover their capabilities by checking Claude's cache directory
1490
- // which often contains MCP schema information or error logs that reveal function names
1491
-
1492
- try {
1493
- const cacheDir = `/Users/${process.env.USER}/Library/Caches/claude-cli-nodejs`
1494
- const { readdirSync, existsSync, readFileSync } = await import("fs")
1495
-
1496
- if (!existsSync(cacheDir)) return []
1497
-
1498
- const cacheDirs = readdirSync(cacheDir)
1499
- const projectDir = cacheDirs.find((dir) => dir.includes(process.cwd().replace(/\//g, "-")))
1500
-
1501
- if (!projectDir) return []
1502
-
1503
- const mcpLogDir = `${cacheDir}/${projectDir}/mcp-logs-${mcpName}`
1504
- if (!existsSync(mcpLogDir)) return []
1505
-
1506
- // Look for schema information in MCP logs
1507
- const logFiles = readdirSync(mcpLogDir)
1508
- const capabilities: McpCapability[] = []
1509
-
1510
- for (const logFile of logFiles.slice(-5)) {
1511
- // Check recent logs only
1512
- try {
1513
- const logPath = `${mcpLogDir}/${logFile}`
1514
- const logContent = readFileSync(logPath, "utf8")
1515
-
1516
- // Parse log content for function definitions, tool lists, or schema information
1517
- const discoveredFunctions = extractFunctionsFromLog(logContent, mcpName)
1518
- capabilities.push(...discoveredFunctions)
1519
- } catch (_error) {
1520
- // Skip files that can't be read
1521
- }
1522
- }
1523
-
1524
- return deduplicateCapabilities(capabilities)
1525
- } catch (error) {
1526
- logToDevFile(`MCP Introspection: Failed to introspect ${mcpName} - ${error}`)
1527
- return []
1528
- }
1529
- }
1530
-
1531
- /**
1532
- * Extract function names and descriptions from MCP log content
1533
- */
1534
- function extractFunctionsFromLog(logContent: string, mcpName: string): McpCapability[] {
1535
- const capabilities: McpCapability[] = []
1536
- const mcpType: "chrome" | "nextjs" = mcpName.includes("chrome")
1537
- ? "chrome"
1538
- : mcpName.includes("nextjs")
1539
- ? "nextjs"
1540
- : "chrome" // default to chrome if unknown
1541
- const advancedKeywords = ADVANCED_CAPABILITY_KEYWORDS[mcpType]
1542
-
1543
- // Look for function definitions in various formats
1544
- const patterns = [
1545
- // JSON-RPC method calls: {"method": "tools/list", "result": {"tools": [{"name": "function_name", "description": "..."}]}}
1546
- /"name":\s*"([^"]+)"/g,
1547
- // Function call patterns: functionName(params)
1548
- /(\w+)\s*\([^)]*\)/g,
1549
- // Tool definition patterns: tool: function_name
1550
- /tool:\s*(\w+)/g,
1551
- // Error messages that reveal function names: "Unknown function: function_name"
1552
- /unknown function[:\s]+(\w+)/gi,
1553
- // Function export patterns: exports.function_name
1554
- /exports\.(\w+)/g
1555
- ]
1556
-
1557
- for (const pattern of patterns) {
1558
- let match: RegExpExecArray | null = pattern.exec(logContent)
1559
- while (match !== null) {
1560
- const functionName = match[1]
1561
-
1562
- // Skip if this is a basic capability that dev3000 handles
1563
- if (DEV3000_BASIC_CAPABILITIES.some((basic) => functionName.toLowerCase().includes(basic))) {
1564
- match = pattern.exec(logContent)
1565
- continue
1566
- }
1567
-
1568
- // Determine if this is an advanced capability
1569
- const isAdvanced = advancedKeywords.some((keyword) => functionName.toLowerCase().includes(keyword))
1570
-
1571
- // Generate reason based on function name and MCP type
1572
- const reason = generateCapabilityReason(functionName, mcpType, isAdvanced)
1573
-
1574
- capabilities.push({
1575
- function: functionName,
1576
- description: undefined, // Will be filled from actual description if available
1577
- category: isAdvanced ? "advanced" : "basic",
1578
- reason
1579
- })
1580
-
1581
- match = pattern.exec(logContent)
1582
- }
1583
- }
1584
-
1585
- return capabilities
1586
- }
1587
-
1588
- /**
1589
- * Generate intelligent reason text for a discovered capability
1590
- */
1591
- function generateCapabilityReason(functionName: string, mcpType: string, isAdvanced: boolean): string {
1592
- const name = functionName.toLowerCase()
1593
-
1594
- // Chrome DevTools specific reasons
1595
- if (mcpType === "chrome") {
1596
- if (name.includes("inspect")) return "Deep DOM inspection with DevTools-level detail"
1597
- if (name.includes("console")) return "Direct browser console access and manipulation"
1598
- if (name.includes("debug") || name.includes("breakpoint"))
1599
- return "JavaScript debugging with breakpoints and call stack"
1600
- if (name.includes("profile") || name.includes("performance")) return "Advanced performance profiling and analysis"
1601
- if (name.includes("network") || name.includes("request")) return "Network request interception and analysis"
1602
- if (name.includes("storage")) return "Browser storage manipulation (cookies, localStorage, etc.)"
1603
- if (name.includes("trace") || name.includes("memory")) return "Memory usage and execution tracing"
1604
- }
1605
-
1606
- // Next.js specific reasons
1607
- if (mcpType === "nextjs") {
1608
- if (name.includes("build")) return "Advanced Next.js build system analysis"
1609
- if (name.includes("hydration")) return "Client-server hydration debugging and analysis"
1610
- if (name.includes("ssr") || name.includes("render")) return "Server-side rendering debugging"
1611
- if (name.includes("route") || name.includes("routing")) return "Next.js routing system inspection and debugging"
1612
- if (name.includes("middleware")) return "Next.js middleware analysis and debugging"
1613
- if (name.includes("optimization") || name.includes("performance"))
1614
- return "Next.js-specific performance optimization"
1615
- }
1616
-
1617
- // Generic advanced vs basic
1618
- if (isAdvanced) {
1619
- return `Advanced ${mcpType} capability beyond dev3000's basic automation`
1620
- }
1621
-
1622
- return `${mcpType} capability for specialized analysis`
1623
- }
1624
-
1625
- /**
1626
- * Infer capabilities from error patterns and log analysis when direct introspection fails
1627
- */
1628
- async function inferCapabilitiesFromLogs(mcpName: string): Promise<McpCapability[]> {
1629
- // This is a fallback when we can't directly introspect the MCP
1630
- // We'll return commonly expected capabilities based on the MCP type
1631
-
1632
- const mcpType = mcpName.includes("chrome") ? "chrome" : mcpName.includes("nextjs") ? "nextjs" : "unknown"
1633
- const capabilities: McpCapability[] = []
1634
-
1635
- if (mcpType === "chrome") {
1636
- // Common chrome-devtools capabilities that are likely to exist
1637
- const commonChromeFunctions = [
1638
- "inspect_element",
1639
- "access_console",
1640
- "start_performance_profile",
1641
- "intercept_requests",
1642
- "set_breakpoint",
1643
- "take_screenshot",
1644
- "get_dom_snapshot",
1645
- "modify_storage",
1646
- "execute_script"
1647
- ]
1648
-
1649
- for (const func of commonChromeFunctions) {
1650
- capabilities.push({
1651
- function: func,
1652
- category: DEV3000_BASIC_CAPABILITIES.includes(func) ? "basic" : "advanced",
1653
- reason: generateCapabilityReason(func, mcpType, true)
1654
- })
1655
- }
1656
- }
1657
-
1658
- if (mcpType === "nextjs") {
1659
- // Common nextjs-dev capabilities that are likely to exist
1660
- const commonNextjsFunctions = [
1661
- "analyze_build_process",
1662
- "debug_server_rendering",
1663
- "debug_hydration",
1664
- "inspect_routing",
1665
- "analyze_next_performance",
1666
- "get_build_info",
1667
- "check_build_status",
1668
- "get_server_logs"
1669
- ]
1670
-
1671
- for (const func of commonNextjsFunctions) {
1672
- capabilities.push({
1673
- function: func,
1674
- category: DEV3000_BASIC_CAPABILITIES.includes(func) ? "basic" : "advanced",
1675
- reason: generateCapabilityReason(func, mcpType, true)
1676
- })
1677
- }
1678
- }
1679
-
1680
- logToDevFile(`Capability Inference: Generated ${capabilities.length} inferred capabilities for ${mcpName}`)
1681
- return capabilities
1682
- }
1683
-
1684
- /**
1685
- * Remove duplicate capabilities while preserving the most detailed ones
1686
- */
1687
- function deduplicateCapabilities(capabilities: McpCapability[]): McpCapability[] {
1688
- const seen = new Map<string, McpCapability>()
1689
-
1690
- for (const capability of capabilities) {
1691
- const existing = seen.get(capability.function)
1692
-
1693
- // Keep the one with more information (description, better reason, etc.)
1694
- if (
1695
- !existing ||
1696
- (capability.description && !existing.description) ||
1697
- capability.reason.length > existing.reason.length
1698
- ) {
1699
- seen.set(capability.function, capability)
1700
- }
1701
- }
1702
-
1703
- return Array.from(seen.values())
1704
- }
1705
-
1706
- /**
1707
- * Check if chrome-devtools MCP is available and get its capabilities
1708
- */
1709
- async function canDelegateToChromeDevtools(action?: string): Promise<boolean> {
1710
- try {
1711
- // Check if MCP is available
1712
- const availableMcps = await discoverAvailableMcps()
1713
- if (!availableMcps.includes("dev3000-chrome-devtools")) {
1714
- return false
1715
- }
1716
-
1717
- // If no specific action, just return availability
1718
- if (!action) return true
1719
-
1720
- // Get dynamic capabilities
1721
- const capabilities = await discoverMcpCapabilities("dev3000-chrome-devtools")
1722
-
1723
- // Check if the MCP has relevant capabilities for the action
1724
- const hasRelevantCapability = capabilities.some(
1725
- (cap) => cap.function.toLowerCase().includes(action.toLowerCase()) || cap.category === "advanced" // Any advanced capability indicates delegation worthiness
1726
- )
1727
-
1728
- return hasRelevantCapability
1729
- } catch (error) {
1730
- logToDevFile(`Chrome DevTools delegation check failed: ${error}`)
1731
- return false
1732
- }
1733
- }
1734
-
1735
- /**
1736
- * Check if nextjs-dev MCP is available and get its capabilities
1737
- */
1738
- async function canDelegateToNextjs(): Promise<boolean> {
1739
- try {
1740
- // Check if MCP is available
1741
- const availableMcps = await discoverAvailableMcps()
1742
- if (!availableMcps.includes("dev3000-nextjs-dev")) {
1743
- return false
1744
- }
1745
-
1746
- // Get dynamic capabilities to verify it has useful functions
1747
- const capabilities = await discoverMcpCapabilities("dev3000-nextjs-dev")
1748
-
1749
- // Return true if we found any advanced Next.js capabilities
1750
- return capabilities.some((cap) => cap.category === "advanced")
1751
- } catch (error) {
1752
- logToDevFile(`NextJS delegation check failed: ${error}`)
1753
- return false
1754
- }
1755
- }
1756
-
1757
- /**
1758
- * Delegate browser action to chrome-devtools MCP
1759
- */
1760
- async function _delegateToChromeDevtools(
1761
- action: string,
1762
- params: Record<string, unknown>
1763
- ): Promise<{ content: Array<{ type: "text"; text: string }> }> {
1764
- // Get dynamic capabilities from chrome-devtools MCP
1765
- const capabilities = await discoverMcpCapabilities("dev3000-chrome-devtools")
1766
-
1767
- // Find a relevant capability for this action
1768
- const relevantCap = capabilities.find(
1769
- (cap) =>
1770
- cap.function.toLowerCase().includes(action.toLowerCase()) ||
1771
- cap.description?.toLowerCase().includes(action.toLowerCase())
1772
- )
1773
-
1774
- if (!relevantCap) {
1775
- throw new Error(`Action ${action} cannot be delegated to chrome-devtools`)
1776
- }
1777
-
1778
- return {
1779
- content: [
1780
- {
1781
- type: "text",
1782
- text: `🔗 **ADVANCED BROWSER DEBUGGING AVAILABLE**
1783
-
1784
- For advanced debugging capabilities, use the \`dev3000-chrome-devtools\` MCP:
1785
-
1786
- \`\`\`
1787
- dev3000-chrome-devtools:${relevantCap.function}(${JSON.stringify(params, null, 2)})
1788
- \`\`\`
1789
-
1790
- 🎯 **Why use chrome-devtools for this:** ${relevantCap.reason}
1791
-
1792
- 💡 **When to use each tool:**
1793
- • **dev3000**: Basic browser automation (screenshots, navigation, clicks, simple scripts)
1794
- • **dev3000-chrome-devtools**: Advanced debugging (DOM inspection, breakpoints, performance profiling, network interception)
1795
-
1796
- ⚡ **Both tools share the same Chrome instance** - no conflicts or duplicate browsers`
1797
- }
1798
- ]
1799
- }
1800
- }
1801
-
1802
- /**
1803
- * Delegate to nextjs-dev MCP with suggested functions
1804
- */
1805
- async function _delegateToNextjs(): Promise<{ content: Array<{ type: "text"; text: string }> }> {
1806
- // Get dynamic capabilities from nextjs-dev MCP
1807
- const capabilities = await discoverMcpCapabilities("dev3000-nextjs-dev")
1808
-
1809
- const availableFunctions = capabilities
1810
- .map((cap) => `• \`dev3000-nextjs-dev:${cap.function}()\` - ${cap.reason}`)
1811
- .join("\n")
1812
-
1813
- return {
1814
- content: [
1815
- {
1816
- type: "text",
1817
- text: `🔗 **ADVANCED NEXT.JS ANALYSIS AVAILABLE**
1818
-
1819
- For Next.js-specific advanced analysis, use the \`dev3000-nextjs-dev\` MCP:
1820
-
1821
- **Available Advanced Functions:**
1822
- ${availableFunctions}
1823
-
1824
- 💡 **When to use each tool:**
1825
- • **dev3000**: General log analysis, basic error detection, simple build monitoring
1826
- • **dev3000-nextjs-dev**: Advanced Next.js debugging (SSR issues, hydration problems, build system analysis, routing inspection)
1827
-
1828
- ⚡ **Best of both worlds:** Use dev3000 for general monitoring and nextjs-dev for framework-specific deep dives`
1829
- }
1830
- ]
1831
- }
1832
- }
1833
-
1834
- // executeBrowserAction removed - use executeAgentBrowserAction directly
1835
- // The legacy execute_browser_action MCP tool has been removed
1836
- // All browser automation now goes through agent_browser_action
1837
-
1838
- // MCP Integration and Workflow Orchestration Functions
1839
-
1840
- /**
1841
- * Known MCP patterns for process detection
1842
- */
1843
- const KNOWN_MCP_PATTERNS = {
1844
- "nextjs-dev": [
1845
- "nextjs-dev",
1846
- "nextjs-dev-mcp",
1847
- "@modelcontextprotocol/server-nextjs-dev",
1848
- "mcp-server-nextjs-dev",
1849
- "nextjs-mcp"
1850
- ],
1851
- "chrome-devtools": [
1852
- "chrome-devtools",
1853
- "chrome-devtools-mcp",
1854
- "@modelcontextprotocol/server-chrome-devtools",
1855
- "mcp-server-chrome-devtools",
1856
- "chrome-mcp"
1857
- ]
1858
- }
1859
-
1860
- /**
1861
- * Standard MCP ports to try pinging
1862
- */
1863
- const STANDARD_MCP_PORTS = {
1864
- "nextjs-dev": [3001, 3002, 8080, 8081],
1865
- "chrome-devtools": [9222, 9223, 9224, 3003]
1866
- }
1867
-
1868
- /**
1869
- * Detect running processes that match known MCP patterns
1870
- */
1871
- async function detectMcpProcesses(): Promise<string[]> {
1872
- const detectedMcps: string[] = []
1873
-
1874
- try {
1875
- // Get running processes on different platforms
1876
- const platform = process.platform
1877
- let psCommand: string
1878
-
1879
- if (platform === "darwin" || platform === "linux") {
1880
- psCommand = "ps aux"
1881
- } else if (platform === "win32") {
1882
- psCommand = "tasklist"
1883
- } else {
1884
- logToDevFile("MCP Discovery: Unsupported platform for process detection")
1885
- return []
1886
- }
1887
-
1888
- const { stdout } = await execAsync(psCommand)
1889
- const processes = stdout.toLowerCase()
1890
-
1891
- // Check for each known MCP pattern
1892
- for (const [mcpName, patterns] of Object.entries(KNOWN_MCP_PATTERNS)) {
1893
- for (const pattern of patterns) {
1894
- if (processes.includes(pattern.toLowerCase())) {
1895
- if (!detectedMcps.includes(mcpName)) {
1896
- detectedMcps.push(mcpName)
1897
- logToDevFile(`MCP Discovery: Found ${mcpName} MCP via process detection [${pattern}]`)
1898
- }
1899
- break
1900
- }
1901
- }
1902
- }
1903
- } catch (error) {
1904
- logToDevFile(`MCP Discovery: Process detection failed - ${error instanceof Error ? error.message : String(error)}`)
1905
- }
1906
-
1907
- return detectedMcps
1908
- }
1909
-
1910
- /**
1911
- * Try to ping MCP services on standard ports
1912
- */
1913
- async function pingMcpPorts(): Promise<string[]> {
1914
- const detectedMcps: string[] = []
1915
-
1916
- for (const [mcpName, ports] of Object.entries(STANDARD_MCP_PORTS)) {
1917
- for (const port of ports) {
1918
- try {
1919
- // Try HTTP health check first
1920
- const response = await fetch(`http://localhost:${port}/health`, {
1921
- method: "GET",
1922
- signal: AbortSignal.timeout(2000)
1923
- })
1924
-
1925
- if (response.ok) {
1926
- detectedMcps.push(mcpName)
1927
- logToDevFile(`MCP Discovery: Found ${mcpName} MCP via HTTP ping on port ${port}`)
1928
- break
1929
- }
1930
- } catch {
1931
- // Try WebSocket connection for MCP protocol
1932
- try {
1933
- const ws = new WebSocket(`ws://localhost:${port}`)
1934
- await new Promise((resolve, reject) => {
1935
- const timeout = setTimeout(() => {
1936
- ws.close()
1937
- reject(new Error("timeout"))
1938
- }, 1000)
1939
-
1940
- ws.on("open", () => {
1941
- clearTimeout(timeout)
1942
- ws.close()
1943
- detectedMcps.push(mcpName)
1944
- logToDevFile(`MCP Discovery: Found ${mcpName} MCP via WebSocket ping on port ${port}`)
1945
- resolve(null)
1946
- })
1947
-
1948
- ws.on("error", () => {
1949
- clearTimeout(timeout)
1950
- reject(new Error("connection failed"))
1951
- })
1952
- })
1953
- break
1954
- } catch {}
1955
- }
1956
- }
1957
- }
1958
-
1959
- return detectedMcps
1960
- }
1961
-
1962
- /**
1963
- * Comprehensive MCP discovery using multiple methods
1964
- */
1965
- export async function discoverAvailableMcps(projectName?: string): Promise<string[]> {
1966
- logToDevFile("MCP Discovery: Starting proactive MCP discovery", projectName)
1967
-
1968
- const discoveredMcps = new Set<string>()
1969
-
1970
- // Method 1: Process detection
1971
- const processDetected = await detectMcpProcesses()
1972
- for (const mcp of processDetected) {
1973
- discoveredMcps.add(mcp)
1974
- }
1975
-
1976
- // Method 2: Check for dev3000-configured MCPs by testing their functionality
1977
- try {
1978
- // Test if dev3000-chrome-devtools MCP is working by checking Claude logs
1979
- const cacheDir = `/Users/${process.env.USER}/Library/Caches/claude-cli-nodejs`
1980
- const { readdirSync, existsSync } = await import("fs")
1981
-
1982
- if (existsSync(cacheDir)) {
1983
- const cacheDirs = readdirSync(cacheDir)
1984
- const projectDir = cacheDirs.find((dir) => dir.includes(process.cwd().replace(/\//g, "-")))
1985
-
1986
- if (projectDir) {
1987
- const projectCacheDir = `${cacheDir}/${projectDir}`
1988
-
1989
- // Check for chrome-devtools MCP logs
1990
- const chromeDevtoolsLogDir = `${projectCacheDir}/mcp-logs-dev3000-chrome-devtools`
1991
- if (existsSync(chromeDevtoolsLogDir)) {
1992
- const chromeDevtoolsLogs = readdirSync(chromeDevtoolsLogDir)
1993
- if (chromeDevtoolsLogs.length > 0) {
1994
- discoveredMcps.add("dev3000-chrome-devtools")
1995
- logToDevFile("MCP Discovery: Found dev3000-chrome-devtools via Claude cache logs", projectName)
1996
- }
1997
- }
1998
-
1999
- // Check for nextjs-dev MCP logs
2000
- const nextjsDevLogDir = `${projectCacheDir}/mcp-logs-dev3000-nextjs-dev`
2001
- if (existsSync(nextjsDevLogDir)) {
2002
- const nextjsDevLogs = readdirSync(nextjsDevLogDir)
2003
- if (nextjsDevLogs.length > 0) {
2004
- discoveredMcps.add("dev3000-nextjs-dev")
2005
- logToDevFile("MCP Discovery: Found dev3000-nextjs-dev via Claude cache logs", projectName)
2006
- }
2007
- }
2008
- }
2009
- }
2010
- } catch (_error) {
2011
- logToDevFile("MCP Discovery: Claude cache check failed, falling back to port detection", projectName)
2012
- }
2013
-
2014
- // Method 3: Port pinging (fallback)
2015
- if (discoveredMcps.size === 0) {
2016
- logToDevFile("MCP Discovery: No MCPs found via process or cache detection, trying port pinging", projectName)
2017
- const portDetected = await pingMcpPorts()
2018
- for (const mcp of portDetected) {
2019
- discoveredMcps.add(mcp)
2020
- }
2021
- }
2022
-
2023
- const finalMcps = Array.from(discoveredMcps)
2024
-
2025
- if (finalMcps.length > 0) {
2026
- logToDevFile(`MCP Discovery: Successfully discovered MCPs [${finalMcps.join(", ")}]`, projectName)
2027
- } else {
2028
- logToDevFile("MCP Discovery: No MCPs detected - will run in standalone mode", projectName)
2029
- }
2030
-
2031
- return finalMcps
2032
- }
2033
-
2034
- /**
2035
- * Get and display MCP capabilities for debugging and inspection
2036
- */
2037
- export async function getMcpCapabilities({
2038
- mcpName
2039
- }: GetMcpCapabilitiesParams = {}): Promise<{ content: Array<{ type: "text"; text: string }> }> {
2040
- const results: string[] = []
2041
-
2042
- results.push("🔍 **MCP CAPABILITY INSPECTOR**")
2043
- results.push("")
2044
-
2045
- try {
2046
- // Discover available MCPs if no specific one requested
2047
- const availableMcps = await discoverAvailableMcps()
2048
-
2049
- if (availableMcps.length === 0) {
2050
- results.push("❌ **NO MCPs DETECTED**")
2051
- results.push("No dev3000-chrome-devtools or dev3000-nextjs-dev MCPs found.")
2052
- results.push("")
2053
- results.push("💡 **To enable enhanced capabilities:**")
2054
- results.push("• Ensure Chrome DevTools MCP is configured: `dev3000-chrome-devtools`")
2055
- results.push("• Ensure Next.js Dev MCP is configured: `dev3000-nextjs-dev`")
2056
- results.push("• Check that Claude Code has MCPs properly configured")
2057
-
2058
- return {
2059
- content: [{ type: "text", text: results.join("\n") }]
2060
- }
2061
- }
2062
-
2063
- results.push(`✅ **DISCOVERED MCPs:** ${availableMcps.join(", ")}`)
2064
- results.push("")
2065
-
2066
- // Filter to specific MCP if requested
2067
- const mcpsToInspect = mcpName ? availableMcps.filter((name) => name.includes(mcpName)) : availableMcps
2068
-
2069
- if (mcpsToInspect.length === 0 && mcpName) {
2070
- results.push(`❌ **MCP NOT FOUND:** ${mcpName}`)
2071
- results.push(`Available MCPs: ${availableMcps.join(", ")}`)
2072
-
2073
- return {
2074
- content: [{ type: "text", text: results.join("\n") }]
2075
- }
2076
- }
2077
-
2078
- // Inspect capabilities for each MCP
2079
- for (const mcp of mcpsToInspect) {
2080
- results.push(`📋 **${mcp.toUpperCase()} CAPABILITIES:**`)
2081
- results.push("")
2082
-
2083
- const capabilities = await discoverMcpCapabilities(mcp)
2084
-
2085
- if (capabilities.length === 0) {
2086
- results.push(" ❌ No capabilities discovered")
2087
- results.push(" 💡 This might indicate the MCP is not properly configured or accessible")
2088
- results.push("")
2089
- continue
2090
- }
2091
-
2092
- // Group by category
2093
- const advanced = capabilities.filter((cap) => cap.category === "advanced")
2094
- const basic = capabilities.filter((cap) => cap.category === "basic")
2095
-
2096
- results.push(` 🚀 **ADVANCED CAPABILITIES** (${advanced.length} functions):`)
2097
- if (advanced.length > 0) {
2098
- advanced.forEach((cap) => {
2099
- results.push(` • \`${cap.function}()\` - ${cap.reason}`)
2100
- })
2101
- } else {
2102
- results.push(" No advanced capabilities discovered")
2103
- }
2104
- results.push("")
2105
-
2106
- results.push(` ⚙️ **BASIC CAPABILITIES** (${basic.length} functions):`)
2107
- if (basic.length > 0) {
2108
- basic.forEach((cap) => {
2109
- results.push(` • \`${cap.function}()\` - ${cap.reason}`)
2110
- })
2111
- } else {
2112
- results.push(" No basic capabilities discovered")
2113
- }
2114
- results.push("")
2115
-
2116
- // Cache info
2117
- const cached = MCP_CAPABILITY_CACHE.get(mcp)
2118
- if (cached) {
2119
- const age = Date.now() - cached.timestamp
2120
- const ageMinutes = Math.floor(age / 60000)
2121
- results.push(` 📝 **CACHE INFO:** Discovered ${ageMinutes} minutes ago`)
2122
- if (age > CAPABILITY_CACHE_TTL * 0.8) {
2123
- results.push(" ⚠️ Cache will refresh soon on next use")
2124
- }
2125
- }
2126
- results.push("")
2127
- }
2128
-
2129
- // Summary
2130
- const totalCapabilities = mcpsToInspect.reduce(async (accPromise, mcp) => {
2131
- const acc = await accPromise
2132
- const caps = await discoverMcpCapabilities(mcp)
2133
- return acc + caps.length
2134
- }, Promise.resolve(0))
2135
-
2136
- results.push("🎯 **AUGMENTED DELEGATION STATUS:**")
2137
- results.push(`• Total discovered capabilities: ${await totalCapabilities}`)
2138
- results.push(
2139
- `• MCPs with advanced capabilities: ${
2140
- mcpsToInspect.filter(async (mcp) => {
2141
- const caps = await discoverMcpCapabilities(mcp)
2142
- return caps.some((cap) => cap.category === "advanced")
2143
- }).length
2144
- }`
2145
- )
2146
- results.push("• Dynamic discovery: ✅ Active (updates automatically)")
2147
- results.push("• Cache TTL: 5 minutes")
2148
- results.push("")
2149
- results.push("💡 **These capabilities are automatically suggested in dev3000's enhanced responses!**")
2150
-
2151
- return {
2152
- content: [{ type: "text", text: results.join("\n") }]
2153
- }
2154
- } catch (error) {
2155
- results.push(`❌ **ERROR INSPECTING CAPABILITIES:** ${error instanceof Error ? error.message : String(error)}`)
2156
- results.push("")
2157
- results.push("💡 **Troubleshooting:**")
2158
- results.push("• Check that MCPs are properly configured in Claude Code")
2159
- results.push("• Verify dev3000 can access Claude cache directories")
2160
-
2161
- return {
2162
- content: [{ type: "text", text: results.join("\n") }]
2163
- }
2164
- }
2165
- }
2166
-
2167
- /**
2168
- * Detect if pixel changes represent a layout shift (elements moving) vs content change (images loading)
2169
- *
2170
- * Key distinction:
2171
- * - Layout shifts: Elements move to new positions (top region changes while bottom stays same)
2172
- * - Content changes: Same regions change in-place (image loads with pixels appearing)
2173
- */
2174
- function detectLayoutShiftVsContentChange(
2175
- prevPng: PNG,
2176
- currPng: PNG
2177
- ): { isLayoutShift: boolean; shiftScore: number; isOverlayNoise: boolean } {
2178
- const width = prevPng.width
2179
- const height = prevPng.height
2180
-
2181
- // Track changes at row-level for detecting correlated shifts
2182
- const rowChangeCounts = new Array(height).fill(0)
2183
-
2184
- // Count changed pixels per row (for correlation analysis)
2185
- for (let y = 0; y < height; y++) {
2186
- for (let x = 0; x < width; x++) {
2187
- const idx = (width * y + x) << 2
2188
- const rDiff = Math.abs(prevPng.data[idx] - currPng.data[idx])
2189
- const gDiff = Math.abs(prevPng.data[idx + 1] - currPng.data[idx + 1])
2190
- const bDiff = Math.abs(prevPng.data[idx + 2] - currPng.data[idx + 2])
2191
-
2192
- if (rDiff > 30 || gDiff > 30 || bDiff > 30) {
2193
- rowChangeCounts[y]++
2194
- }
2195
- }
2196
- }
2197
-
2198
- // Calculate percentage of pixels changed per row
2199
- const rowChangePercents = rowChangeCounts.map((count) => (count / width) * 100)
2200
-
2201
- // Detect consecutive rows with high change (indicates shift boundary)
2202
- // True CLS: Many consecutive rows change together (content moved as a block)
2203
- let maxConsecutiveHighChangeRows = 0
2204
- let currentConsecutive = 0
2205
-
2206
- for (let i = 0; i < height; i++) {
2207
- if (rowChangePercents[i] > 50) {
2208
- // >50% of row changed
2209
- currentConsecutive++
2210
- maxConsecutiveHighChangeRows = Math.max(maxConsecutiveHighChangeRows, currentConsecutive)
2211
- } else {
2212
- currentConsecutive = 0
2213
- }
2214
- }
2215
-
2216
- // Detect isolated hotspots (fixed/absolute overlay noise)
2217
- // Pattern: low change → spike → low change (element appearing in place)
2218
- let isolatedHotspots = 0
2219
- const windowSize = 5
2220
-
2221
- for (let i = windowSize; i < height - windowSize; i++) {
2222
- // Calculate average change in windows before, during, and after
2223
- const before = rowChangePercents.slice(i - windowSize, i).reduce((a, b) => a + b, 0) / windowSize
2224
- const during = rowChangePercents[i]
2225
- const after = rowChangePercents.slice(i + 1, i + windowSize + 1).reduce((a, b) => a + b, 0) / windowSize
2226
-
2227
- // Isolated spike: calm before/after, high during
2228
- if (before < 10 && during > 60 && after < 10) {
2229
- isolatedHotspots++
2230
- }
2231
- }
2232
-
2233
- // Detect narrow fixed elements (toolbars, indicators)
2234
- // Pattern: Many rows with LOW percentage change (5-25%) = narrow element across many rows
2235
- // This catches toolbars/indicators that are thin but tall
2236
- let narrowChangeRows = 0
2237
- for (let i = 0; i < height; i++) {
2238
- // Low but consistent change (narrow element)
2239
- if (rowChangePercents[i] > 5 && rowChangePercents[i] < 25) {
2240
- narrowChangeRows++
2241
- }
2242
- }
2243
-
2244
- // If many rows have narrow changes, this is likely a fixed toolbar/sidebar
2245
- const hasNarrowFixedElement = narrowChangeRows > height * 0.3 // >30% of rows have narrow changes
2246
-
2247
- // Calculate band-based metrics for backward compatibility
2248
- const bandHeight = Math.floor(height / 8)
2249
- const bands = Array(8).fill(0)
2250
-
2251
- for (let y = 0; y < height; y++) {
2252
- const bandIndex = Math.min(Math.floor(y / bandHeight), 7)
2253
- bands[bandIndex] += rowChangeCounts[y]
2254
- }
2255
-
2256
- const pixelsPerBand = width * bandHeight
2257
- const bandPercentages = bands.map((count) => (count / pixelsPerBand) * 100)
2258
- const topBandChange = (bandPercentages[0] + bandPercentages[1]) / 2
2259
- const bottomBandChange = (bandPercentages[6] + bandPercentages[7]) / 2
2260
-
2261
- // Calculate variance to detect if changes are uniform (shift) or scattered (overlay)
2262
- const meanChange = bandPercentages.reduce((a, b) => a + b, 0) / bandPercentages.length
2263
- const variance = bandPercentages.reduce((sum, val) => sum + (val - meanChange) ** 2, 0) / bandPercentages.length
2264
-
2265
- // Determine if this is a layout shift or overlay noise
2266
- // True layout shift indicators:
2267
- // 1. Many consecutive rows changed (>20 rows = significant shift)
2268
- // 2. Top heavy change pattern (topBandChange > bottomBandChange)
2269
- // 3. Low variance (uniform change across bands)
2270
- // 4. Few isolated hotspots
2271
-
2272
- const hasConsecutiveShift = maxConsecutiveHighChangeRows > 20
2273
- const hasTopHeavyPattern = topBandChange > 5 && bottomBandChange < 2 && topBandChange > bottomBandChange * 2
2274
- const hasUniformChange = variance < 200 && meanChange > 10
2275
- const hasIsolatedHotspots = isolatedHotspots >= 3
2276
-
2277
- // Overlay noise indicators:
2278
- // 1. High variance (scattered changes)
2279
- // 2. Multiple isolated hotspots
2280
- // 3. Few consecutive rows changed
2281
- // 4. Narrow fixed element (toolbar/indicator pattern)
2282
- const isOverlayNoise =
2283
- hasNarrowFixedElement || // Narrow element like toolbar
2284
- (hasIsolatedHotspots && !hasConsecutiveShift && (variance > 500 || meanChange < 10))
2285
-
2286
- // Layout shift: Either consecutive shift pattern OR traditional top-heavy pattern
2287
- // But NOT if it looks like overlay noise
2288
- const isLayoutShift = !isOverlayNoise && (hasConsecutiveShift || hasTopHeavyPattern || hasUniformChange)
2289
-
2290
- // Calculate shift score
2291
- const totalChanged = bands.reduce((sum, count) => sum + count, 0)
2292
- const totalPixels = width * height
2293
- const shiftScore = (totalChanged / totalPixels) * 0.1
2294
-
2295
- return { isLayoutShift, shiftScore, isOverlayNoise }
2296
- }
2297
-
2298
- /**
2299
- * Detect jank/layout shifts by comparing screenshots from ScreencastManager
2300
- * Returns array of jank detections with timing and visual impact data
2301
- */
2302
- async function detectJankFromScreenshots(_projectName?: string): Promise<{
2303
- detections: Array<{
2304
- timestamp: string
2305
- timeSinceStart: number
2306
- visualDiff: number
2307
- severity: "low" | "medium" | "high"
2308
- element?: string
2309
- clsScore?: number
2310
- uxImpact?: string
2311
- beforeFrameUrl?: string
2312
- afterFrameUrl?: string
2313
- }>
2314
- sessionId: string
2315
- totalFrames: number
2316
- screenshotDir: string
2317
- realCLS?: { score: number; grade: string }
2318
- captureTrigger?: "navigation" | "load"
2319
- }> {
2320
- const screenshotDir = process.env.SCREENSHOT_DIR || join(tmpdir(), "dev3000-mcp-deps", "public", "screenshots")
2321
-
2322
- if (!existsSync(screenshotDir)) {
2323
- return { detections: [], sessionId: "", totalFrames: 0, screenshotDir }
2324
- }
2325
-
2326
- // Find the most recent screencast session (files like 2025-10-06T01-54-45Z-jank-*.png)
2327
- const files = readdirSync(screenshotDir)
2328
- .filter((f) => f.includes("-jank-") && f.endsWith(".png"))
2329
- .sort()
2330
- .reverse()
2331
-
2332
- if (files.length === 0) {
2333
- return { detections: [], sessionId: "", totalFrames: 0, screenshotDir }
2334
- }
2335
-
2336
- // Get the most recent session ID (timestamp prefix)
2337
- const latestSessionId = files[0].split("-jank-")[0]
2338
- const sessionFiles = files
2339
- .filter((f) => f.startsWith(latestSessionId))
2340
- .sort((a, b) => {
2341
- // Extract timestamp (e.g., "28ms" from "2025-10-06T01-54-45Z-jank-28ms.png")
2342
- const aTime = parseInt(a.match(/-(\d+)ms\.png$/)?.[1] || "0", 10)
2343
- const bTime = parseInt(b.match(/-(\d+)ms\.png$/)?.[1] || "0", 10)
2344
- return aTime - bTime
2345
- })
2346
-
2347
- if (sessionFiles.length < 2) {
2348
- return { detections: [], sessionId: latestSessionId, totalFrames: sessionFiles.length, screenshotDir }
2349
- }
2350
-
2351
- // Try to read real CLS data from metadata
2352
- const metadataPath = join(screenshotDir, `${latestSessionId}-metadata.json`)
2353
- let realCLSData:
2354
- | {
2355
- score: number
2356
- grade: string
2357
- shifts: Array<{
2358
- score: number
2359
- timestamp: number
2360
- sources?: Array<{ node?: string; position?: string | null }>
2361
- }>
2362
- }
2363
- | undefined
2364
- let captureTrigger: "navigation" | "load" | undefined
2365
-
2366
- if (existsSync(metadataPath)) {
2367
- try {
2368
- const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"))
2369
- // Capture the trigger type for use in output messages
2370
- captureTrigger = metadata.captureTrigger
2371
- // Set realCLSData even if there are zero shifts - this tells us Chrome ran and found nothing
2372
- if (metadata.layoutShifts !== undefined) {
2373
- realCLSData = {
2374
- score: metadata.totalCLS || 0,
2375
- grade: metadata.clsGrade || "unknown",
2376
- shifts: metadata.layoutShifts
2377
- }
2378
- }
2379
- } catch {
2380
- // Ignore metadata read errors
2381
- }
2382
- }
2383
-
2384
- const jankDetections: Array<{
2385
- timestamp: string
2386
- timeSinceStart: number
2387
- visualDiff: number
2388
- severity: "low" | "medium" | "high"
2389
- element?: string
2390
- clsScore?: number
2391
- uxImpact?: string
2392
- beforeFrameUrl?: string
2393
- afterFrameUrl?: string
2394
- }> = []
2395
-
2396
- // Parse log file to extract Before/After frame URLs for each CLS event
2397
- const frameUrlMap: Map<number, { before: string; after: string }> = new Map()
2398
- try {
2399
- const logPath = getLogPath(_projectName)
2400
- if (logPath && existsSync(logPath)) {
2401
- const logContent = readFileSync(logPath, "utf-8")
2402
- const lines = logContent.split("\n")
2403
-
2404
- // Look for CLS entries with Before/After URLs
2405
- // Format: [BROWSER] [CDP] CLS #N (score: X, time: Yms):
2406
- // [BROWSER] [CDP] - <ELEMENT> shifted... (variable number of these)
2407
- // [BROWSER] [CDP] Before: http://...
2408
- // [BROWSER] [CDP] After: http://...
2409
- for (let i = 0; i < lines.length; i++) {
2410
- const clsMatch = lines[i].match(/\[CDP\] CLS #\d+ \(score: [\d.]+, time: (\d+)ms\):/)
2411
- if (clsMatch) {
2412
- const timestamp = parseInt(clsMatch[1], 10)
2413
- // Look ahead for Before and After URLs (scan next 10 lines for them)
2414
- let beforeUrl: string | null = null
2415
- let afterUrl: string | null = null
2416
-
2417
- for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
2418
- if (!beforeUrl) {
2419
- const beforeMatch = lines[j].match(/Before:\s+(http:\/\/\S+)/)
2420
- if (beforeMatch) beforeUrl = beforeMatch[1]
2421
- }
2422
- if (!afterUrl) {
2423
- const afterMatch = lines[j].match(/After:\s+(http:\/\/\S+)/)
2424
- if (afterMatch) afterUrl = afterMatch[1]
2425
- }
2426
- // Stop if we found both
2427
- if (beforeUrl && afterUrl) {
2428
- frameUrlMap.set(timestamp, {
2429
- before: beforeUrl,
2430
- after: afterUrl
2431
- })
2432
- break
2433
- }
2434
- }
2435
- }
2436
- }
2437
- }
2438
- } catch (_error) {
2439
- // Ignore log parsing errors
2440
- }
2441
-
2442
- // If we have real CLS data from Chrome's PerformanceObserver, trust it completely
2443
- if (realCLSData) {
2444
- // If Chrome says there are no shifts, validate with pixel diff as backup
2445
- // Chrome's PerformanceObserver can miss very fast hydration shifts
2446
- if (realCLSData.shifts.length === 0) {
2447
- // Run pixel diff validation on early frames (first 1500ms) to catch hydration issues
2448
- const earlyFrames = sessionFiles.filter((f) => {
2449
- const timeMatch = f.match(/-(\d+)ms\.png$/)
2450
- const time = timeMatch ? parseInt(timeMatch[1], 10) : 0
2451
- return time < 1500 // Hydration window
2452
- })
2453
-
2454
- let foundHydrationShift = false
2455
-
2456
- // Only check consecutive early frames
2457
- for (let i = 1; i < earlyFrames.length && i < 10; i++) {
2458
- const prevFile = join(screenshotDir, earlyFrames[i - 1])
2459
- const currFile = join(screenshotDir, earlyFrames[i])
2460
-
2461
- try {
2462
- const prevPng = PNG.sync.read(readFileSync(prevFile))
2463
- const currPng = PNG.sync.read(readFileSync(currFile))
2464
-
2465
- if (prevPng.width !== currPng.width || prevPng.height !== currPng.height) {
2466
- continue
2467
- }
2468
-
2469
- // Detect if this is a layout shift vs content change vs overlay noise
2470
- const shiftAnalysis = detectLayoutShiftVsContentChange(prevPng, currPng)
2471
-
2472
- // Skip if this looks like overlay noise (fixed/absolute elements like Next.js dev indicator or Vercel toolbar)
2473
- if (shiftAnalysis.isOverlayNoise) {
2474
- logToDevFile(
2475
- `Pixel Diff Hydration: Skipping frame ${i} - detected overlay noise (fixed/absolute elements), not true CLS`
2476
- )
2477
- continue
2478
- }
2479
-
2480
- // If we detect a true layout shift (not just content loading or overlay noise), flag it
2481
- if (shiftAnalysis.isLayoutShift) {
2482
- foundHydrationShift = true
2483
- const timeMatch = earlyFrames[i].match(/-(\d+)ms\.png$/)
2484
- const timeSinceStart = timeMatch ? parseInt(timeMatch[1], 10) : 0
2485
-
2486
- logToDevFile(
2487
- `Pixel Diff Hydration: Detected true layout shift at ${timeSinceStart}ms (score: ${shiftAnalysis.shiftScore.toFixed(4)})`
2488
- )
2489
-
2490
- const mcpPort = process.env.MCP_PORT || "3684"
2491
- jankDetections.push({
2492
- timestamp: `${timeSinceStart}ms`,
2493
- timeSinceStart,
2494
- visualDiff: shiftAnalysis.shiftScore * 100,
2495
- severity: "high", // Hydration shifts are always high severity
2496
- element: "Hydration-related element",
2497
- clsScore: shiftAnalysis.shiftScore,
2498
- uxImpact: "🚨 CRITICAL: Fast hydration shift detected - Chrome's observer missed this early shift",
2499
- beforeFrameUrl: `http://localhost:${mcpPort}/api/screenshots/${earlyFrames[i - 1]}`,
2500
- afterFrameUrl: `http://localhost:${mcpPort}/api/screenshots/${earlyFrames[i]}`
2501
- })
2502
- }
2503
- } catch {
2504
- // Skip frames that can't be compared
2505
- }
2506
- }
2507
-
2508
- // If we found hydration shifts, return them with a note
2509
- if (foundHydrationShift) {
2510
- return {
2511
- detections: jankDetections,
2512
- sessionId: latestSessionId,
2513
- totalFrames: sessionFiles.length,
2514
- screenshotDir,
2515
- realCLS: { score: 0.05, grade: "good" } // Estimate CLS for hydration shifts
2516
- }
2517
- }
2518
-
2519
- // Chrome is correct - no shifts detected
2520
- return {
2521
- detections: [],
2522
- sessionId: latestSessionId,
2523
- totalFrames: sessionFiles.length,
2524
- screenshotDir,
2525
- realCLS: { score: 0, grade: realCLSData.grade }
2526
- }
2527
- }
2528
-
2529
- // Process actual layout shifts detected by Chrome
2530
- // Trust Chrome's Layout Instability API - BUT ONLY if we can identify the culprit element
2531
- // and verify it's not a fixed/absolute positioned overlay
2532
- realCLSData.shifts.forEach((shift) => {
2533
- const element = shift.sources?.[0]?.node || "unknown"
2534
- const position = shift.sources?.[0]?.position
2535
-
2536
- // FILTER: Skip shifts where we couldn't identify the element
2537
- // Chrome sometimes reports CLS for fixed overlays but fails to identify the element
2538
- if (!shift.sources?.[0] || element === "unknown" || position === null || position === undefined) {
2539
- logToDevFile(
2540
- `Chrome CLS: Skipping unidentified shift (score: ${shift.score.toFixed(4)}) - cannot verify if it's a true CLS or fixed overlay noise`
2541
- )
2542
- return // Skip this shift - can't verify it's real
2543
- }
2544
-
2545
- // FILTER: Skip fixed/absolute positioned elements - these are overlays, not true CLS
2546
- if (position === "fixed" || position === "absolute") {
2547
- logToDevFile(
2548
- `Chrome CLS: Filtering out ${element} shift (position: ${position}) - fixed/absolute elements don't cause true layout shifts`
2549
- )
2550
- return // Skip this shift
2551
- }
2552
-
2553
- const isCriticalElement = ["NAV", "HEADER", "BUTTON", "A"].includes(element.toUpperCase())
2554
- const isDuringLoad = shift.timestamp < 1000 // First second
2555
-
2556
- // Make element names more descriptive
2557
- const elementDescriptions: Record<string, string> = {
2558
- NAV: "Navigation header (<nav>)",
2559
- HEADER: "Page header (<header>)",
2560
- BUTTON: "Button (<button>)",
2561
- A: "Link (<a>)"
2562
- }
2563
- const elementDisplay = elementDescriptions[element.toUpperCase()] || element
2564
-
2565
- // UX impact assessment (not just CLS score!)
2566
- let severity: "low" | "medium" | "high" = "low"
2567
- let uxImpact = "Minor visual adjustment"
2568
-
2569
- if (isCriticalElement && isDuringLoad) {
2570
- severity = "high"
2571
- uxImpact = `🚨 CRITICAL: ${elementDisplay} shifted during initial load - highly visible and disruptive to user interaction`
2572
- } else if (isCriticalElement) {
2573
- severity = "medium"
2574
- uxImpact = `⚠️ ${elementDisplay} shifted - affects navigation/interaction`
2575
- } else if (isDuringLoad) {
2576
- severity = "medium"
2577
- uxImpact = "Shift during page load - may cause mis-clicks"
2578
- }
2579
-
2580
- // Look up Before/After URLs for this shift timestamp
2581
- const roundedTimestamp = Math.round(shift.timestamp)
2582
- const frameUrls = frameUrlMap.get(roundedTimestamp)
2583
-
2584
- jankDetections.push({
2585
- timestamp: `${shift.timestamp.toFixed(0)}ms`,
2586
- timeSinceStart: roundedTimestamp,
2587
- visualDiff: shift.score * 100, // Convert to percentage-like scale
2588
- severity,
2589
- element: elementDisplay,
2590
- clsScore: shift.score,
2591
- uxImpact,
2592
- beforeFrameUrl: frameUrls?.before,
2593
- afterFrameUrl: frameUrls?.after
2594
- })
2595
- })
2596
-
2597
- return {
2598
- detections: jankDetections,
2599
- sessionId: latestSessionId,
2600
- totalFrames: sessionFiles.length,
2601
- screenshotDir,
2602
- realCLS: { score: realCLSData.score, grade: realCLSData.grade }
2603
- }
2604
- }
2605
-
2606
- // Fallback to pixel-diff if no real CLS data (old behavior)
2607
-
2608
- // Compare each frame with the previous frame
2609
- for (let i = 1; i < sessionFiles.length; i++) {
2610
- const prevFile = join(screenshotDir, sessionFiles[i - 1])
2611
- const currFile = join(screenshotDir, sessionFiles[i])
2612
-
2613
- try {
2614
- const prevPng = PNG.sync.read(readFileSync(prevFile))
2615
- const currPng = PNG.sync.read(readFileSync(currFile))
2616
-
2617
- // Ensure same dimensions
2618
- if (prevPng.width !== currPng.width || prevPng.height !== currPng.height) {
2619
- continue
2620
- }
2621
-
2622
- const diff = new PNG({ width: prevPng.width, height: prevPng.height })
2623
- const numDiffPixels = pixelmatch(prevPng.data, currPng.data, diff.data, prevPng.width, prevPng.height, {
2624
- threshold: 0.1
2625
- })
2626
-
2627
- const totalPixels = prevPng.width * prevPng.height
2628
- const diffPercentage = (numDiffPixels / totalPixels) * 100
2629
-
2630
- // Consider it jank if more than 1% of pixels changed (layout shift threshold)
2631
- if (diffPercentage > 1) {
2632
- const timeMatch = sessionFiles[i].match(/-(\d+)ms\.png$/)
2633
- const timeSinceStart = timeMatch ? parseInt(timeMatch[1], 10) : 0
2634
-
2635
- jankDetections.push({
2636
- timestamp: latestSessionId,
2637
- timeSinceStart,
2638
- visualDiff: diffPercentage,
2639
- severity: diffPercentage > 10 ? "high" : diffPercentage > 5 ? "medium" : "low"
2640
- })
2641
- }
2642
- } catch {
2643
- // Skip frames that can't be compared
2644
- }
2645
- }
2646
-
2647
- return {
2648
- detections: jankDetections,
2649
- sessionId: latestSessionId,
2650
- totalFrames: sessionFiles.length,
2651
- screenshotDir,
2652
- captureTrigger
2653
- }
2654
- }
2655
-
2656
- /**
2657
- * Log MCP-related events to the project-specific D3K log file (NOT main project log)
2658
- * This prevents Claude from seeing dev3000's orchestration logs as application errors
2659
- */
2660
- function logToDevFile(message: string, projectName?: string) {
2661
- try {
2662
- // Write to project-specific D3K log instead of main project log
2663
- const homeDir = process.env.HOME || process.env.USERPROFILE
2664
- if (!homeDir) return
2665
-
2666
- const debugLogDir = join(homeDir, ".d3k", "logs")
2667
- if (!existsSync(debugLogDir)) {
2668
- mkdirSync(debugLogDir, { recursive: true })
2669
- }
2670
-
2671
- // Use project name from parameter or try to detect from current session
2672
- const actualProjectName = projectName || getCurrentProjectName()
2673
- if (!actualProjectName) return
2674
-
2675
- const d3kLogFile = join(debugLogDir, `dev3000-${actualProjectName}-d3k.log`)
2676
- const timestamp = new Date().toISOString()
2677
- const logEntry = `[${timestamp}] [D3K] ${message}\n`
2678
- appendFileSync(d3kLogFile, logEntry)
2679
- } catch (_error) {
2680
- // Silently fail to avoid breaking MCP functionality
2681
- }
2682
- }
2683
-
2684
- /**
2685
- * Get current project name from active sessions
2686
- */
2687
- function getCurrentProjectName(): string | null {
2688
- try {
2689
- const homeDir = process.env.HOME || process.env.USERPROFILE
2690
- if (!homeDir) return null
2691
-
2692
- const sessionDir = join(homeDir, ".d3k")
2693
- if (!existsSync(sessionDir)) return null
2694
-
2695
- // Find the most recent session file
2696
- const sessionFiles = readdirSync(sessionDir).filter((file) => file.endsWith(".json"))
2697
- if (sessionFiles.length === 0) return null
2698
-
2699
- // Use the first session file's project name (could be improved to find the "current" one)
2700
- const sessionFile = join(sessionDir, sessionFiles[0])
2701
- const sessionData = JSON.parse(readFileSync(sessionFile, "utf8"))
2702
- return sessionData.projectName || null
2703
- } catch {
2704
- return null
2705
- }
2706
- }
2707
-
2708
- /**
2709
- * Detect available MCPs and set integration flags
2710
- */
2711
- export function detectMcpIntegrations(
2712
- availableMcps: string[],
2713
- projectName?: string
2714
- ): {
2715
- integrateNextjs: boolean
2716
- integrateChromeDevtools: boolean
2717
- } {
2718
- const integrateNextjs = availableMcps.includes("nextjs-dev")
2719
- const integrateChromeDevtools = availableMcps.includes("chrome-devtools")
2720
-
2721
- // Log MCP detection results
2722
- if (availableMcps.length > 0) {
2723
- logToDevFile(`MCP Detection: Available MCPs [${availableMcps.join(", ")}]`, projectName)
2724
-
2725
- const integrations: string[] = []
2726
- if (integrateNextjs) integrations.push("Next.js")
2727
- if (integrateChromeDevtools) integrations.push("Chrome DevTools")
2728
-
2729
- if (integrations.length > 0) {
2730
- logToDevFile(`MCP Integration: Activated integrations [${integrations.join(", ")}]`, projectName)
2731
- } else {
2732
- logToDevFile("MCP Integration: No compatible MCPs detected - running in standalone mode", projectName)
2733
- }
2734
- } else {
2735
- logToDevFile("MCP Detection: No MCPs provided - running in standalone mode", projectName)
2736
- }
2737
-
2738
- return {
2739
- integrateNextjs,
2740
- integrateChromeDevtools
2741
- }
2742
- }
2743
-
2744
- /**
2745
- * Calculate estimated time based on available tools and error complexity
2746
- */
2747
- export function calculateEstimatedTime(errorCount: number, hasIntegrations: boolean): string {
2748
- const baseTime = Math.min(errorCount * 2, 20) // 2 minutes per error, max 20 minutes
2749
- const integrationBonus = hasIntegrations ? 0.5 : 1 // 50% faster with integrations
2750
- const totalMinutes = Math.ceil(baseTime * integrationBonus)
2751
-
2752
- if (totalMinutes <= 5) return `${totalMinutes} minutes`
2753
- if (totalMinutes <= 60) return `${totalMinutes} minutes`
2754
- return `${Math.ceil(totalMinutes / 60)} hours`
2755
- }
2756
-
2757
- /**
2758
- * Generate dynamic Next.js specific MCP function suggestions based on discovered capabilities
2759
- */
2760
- export async function generateNextjsSuggestions(errorContext?: string): Promise<McpFunctionSuggestion[]> {
2761
- try {
2762
- // Get dynamic capabilities from the MCP
2763
- const capabilities = await discoverMcpCapabilities("dev3000-nextjs-dev")
2764
-
2765
- // Filter for advanced capabilities and create suggestions
2766
- const suggestions: McpFunctionSuggestion[] = capabilities
2767
- .filter((cap) => cap.category === "advanced")
2768
- .slice(0, 8) // Limit to most relevant suggestions
2769
- .map((cap) => ({
2770
- function: cap.function,
2771
- reason: cap.reason,
2772
- priority: determinePriority(cap.function, errorContext) as "high" | "medium" | "low"
2773
- }))
2774
-
2775
- logToDevFile(
2776
- `Dynamic Suggestions: Generated ${suggestions.length} Next.js suggestions from ${capabilities.length} discovered capabilities`
2777
- )
2778
-
2779
- return suggestions
2780
- } catch (error) {
2781
- logToDevFile(`Dynamic Suggestions: Failed to generate Next.js suggestions - ${error}`)
2782
-
2783
- // Fallback to basic suggestions if discovery fails
2784
- return [
2785
- {
2786
- function: "analyze_build_process",
2787
- reason: "Advanced Next.js build system analysis",
2788
- priority: "high"
2789
- },
2790
- {
2791
- function: "debug_server_rendering",
2792
- reason: "Server-side rendering debugging",
2793
- priority: "high"
2794
- }
2795
- ]
2796
- }
2797
- }
2798
-
2799
- /**
2800
- * Generate dynamic Chrome DevTools specific MCP function suggestions based on discovered capabilities
2801
- */
2802
- export async function generateChromeDevtoolsSuggestions(errorContext?: string): Promise<McpFunctionSuggestion[]> {
2803
- try {
2804
- // Get dynamic capabilities from the MCP
2805
- const capabilities = await discoverMcpCapabilities("dev3000-chrome-devtools")
2806
-
2807
- // Filter for advanced capabilities and create suggestions
2808
- const suggestions: McpFunctionSuggestion[] = capabilities
2809
- .filter((cap) => cap.category === "advanced")
2810
- .slice(0, 8) // Limit to most relevant suggestions
2811
- .map((cap) => ({
2812
- function: cap.function,
2813
- reason: cap.reason,
2814
- priority: determinePriority(cap.function, errorContext) as "high" | "medium" | "low"
2815
- }))
2816
-
2817
- logToDevFile(
2818
- `Dynamic Suggestions: Generated ${suggestions.length} Chrome DevTools suggestions from ${capabilities.length} discovered capabilities`
2819
- )
2820
-
2821
- return suggestions
2822
- } catch (error) {
2823
- logToDevFile(`Dynamic Suggestions: Failed to generate Chrome DevTools suggestions - ${error}`)
2824
-
2825
- // Fallback to basic suggestions if discovery fails
2826
- return [
2827
- {
2828
- function: "inspect_element",
2829
- reason: "Deep DOM inspection with DevTools-level detail",
2830
- priority: "high"
2831
- },
2832
- {
2833
- function: "access_console",
2834
- reason: "Direct browser console access and manipulation",
2835
- priority: "high"
2836
- }
2837
- ]
2838
- }
2839
- }
2840
-
2841
- /**
2842
- * Determine priority of a capability based on error context and function relevance
2843
- */
2844
- function determinePriority(functionName: string, errorContext?: string): "high" | "medium" | "low" {
2845
- const name = functionName.toLowerCase()
2846
- const context = errorContext?.toLowerCase() || ""
2847
-
2848
- // High priority matches - function directly relates to error context
2849
- const highPriorityPatterns = [
2850
- { pattern: /hydration/, keywords: ["hydration", "ssr", "render"] },
2851
- { pattern: /build|compile/, keywords: ["build", "compile", "analyze"] },
2852
- { pattern: /network|fetch|api/, keywords: ["network", "request", "intercept", "performance"] },
2853
- { pattern: /console|error/, keywords: ["console", "error", "debug"] },
2854
- { pattern: /click|interaction/, keywords: ["dom", "element", "inspect"] }
2855
- ]
2856
-
2857
- for (const { pattern, keywords } of highPriorityPatterns) {
2858
- if (pattern.test(context) && keywords.some((keyword) => name.includes(keyword))) {
2859
- return "high"
2860
- }
2861
- }
2862
-
2863
- // Medium priority - advanced debugging capabilities
2864
- const mediumPriorityKeywords = ["debug", "profile", "analyze", "trace", "inspect"]
2865
- if (mediumPriorityKeywords.some((keyword) => name.includes(keyword))) {
2866
- return "medium"
2867
- }
2868
-
2869
- // Low priority - basic or less critical functions
2870
- return "low"
2871
- }
2872
-
2873
- /**
2874
- * Create integrated workflow with 3-phase debugging plan
2875
- */
2876
- export async function createIntegratedWorkflow({
2877
- availableMcps,
2878
- focusArea = "all",
2879
- errorContext
2880
- }: CreateIntegratedWorkflowParams): Promise<{ content: Array<{ type: "text"; text: string }> }> {
2881
- const results: string[] = []
2882
-
2883
- // Log workflow creation
2884
- logToDevFile(
2885
- `Workflow Creation: Creating integrated workflow with focus area [${focusArea}]${errorContext ? `, error context [${errorContext}]` : ""}`
2886
- )
2887
-
2888
- // Use provided MCPs or discover them proactively
2889
- let finalMcps: string[] = availableMcps || []
2890
- if (!availableMcps || availableMcps.length === 0) {
2891
- logToDevFile("Workflow Creation: No MCPs provided, starting proactive discovery")
2892
- finalMcps = await discoverAvailableMcps()
2893
- }
2894
-
2895
- // Detect available integrations
2896
- const { integrateNextjs, integrateChromeDevtools } = detectMcpIntegrations(finalMcps)
2897
-
2898
- results.push("🎼 **INTELLIGENT DEBUGGING ORCHESTRATOR**")
2899
- results.push(`🔍 Available MCPs: ${finalMcps.length > 0 ? finalMcps.join(", ") : "none (will attempt discovery)"}`)
2900
- results.push(
2901
- `⚡ Integrations: ${integrateNextjs ? "✅ Next.js" : "❌ Next.js"} | ${integrateChromeDevtools ? "✅ Chrome DevTools" : "❌ Chrome DevTools"}`
2902
- )
2903
-
2904
- if (errorContext) {
2905
- results.push(`🎯 Error Context: ${errorContext}`)
2906
- }
2907
- results.push("")
2908
-
2909
- // Generate MCP-specific suggestions
2910
- const nextjsSuggestions = integrateNextjs ? await generateNextjsSuggestions(errorContext) : []
2911
- const chromeSuggestions = integrateChromeDevtools ? await generateChromeDevtoolsSuggestions(errorContext) : []
2912
-
2913
- if (!integrateNextjs && !integrateChromeDevtools) {
2914
- results.push("⚠️ **NO INTEGRATIONS DETECTED**")
2915
- results.push("Running in standalone mode. For enhanced debugging:")
2916
- results.push("• Add 'nextjs-dev' MCP for Next.js-specific analysis")
2917
- results.push("• Add 'chrome-devtools' MCP for browser inspection")
2918
- results.push("")
2919
- results.push("💡 **STANDALONE WORKFLOW:**")
2920
- results.push("1. Use fix_my_app(mode='snapshot') to analyze current issues")
2921
- results.push("2. Use execute_browser_action to reproduce and verify fixes")
2922
- results.push("3. Implement suggested code fixes")
2923
-
2924
- return {
2925
- content: [{ type: "text", text: results.join("\n") }]
2926
- }
2927
- }
2928
-
2929
- // Create 3-phase integrated workflow
2930
- results.push("🎪 **3-PHASE INTEGRATED WORKFLOW**")
2931
- results.push("")
2932
-
2933
- // Phase 1: Parallel Data Collection
2934
- results.push("🕐 **PHASE 1: PARALLEL DATA COLLECTION** (2-3 minutes)")
2935
- results.push("Execute these functions in parallel across all available MCPs:")
2936
- results.push("")
2937
-
2938
- results.push("📊 **dev3000 (this MCP):**")
2939
- results.push(
2940
- `• fix_my_app(focusArea='${focusArea}', integrateNextjs=${integrateNextjs}, integrateChromeDevtools=${integrateChromeDevtools}, returnRawData=true)`
2941
- )
2942
- results.push(" → Get comprehensive error analysis with interaction data")
2943
- results.push("")
2944
-
2945
- if (integrateNextjs) {
2946
- results.push("⚛️ **nextjs-dev MCP:**")
2947
- nextjsSuggestions
2948
- .filter((s) => s.priority === "high")
2949
- .forEach((suggestion) => {
2950
- const params = suggestion.params
2951
- ? `(${Object.entries(suggestion.params)
2952
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
2953
- .join(", ")})`
2954
- : "()"
2955
- results.push(`• ${suggestion.function}${params}`)
2956
- results.push(` → ${suggestion.reason}`)
2957
- })
2958
- results.push("")
2959
- }
2960
-
2961
- if (integrateChromeDevtools) {
2962
- results.push("🌐 **chrome-devtools MCP:**")
2963
- chromeSuggestions
2964
- .filter((s) => s.priority === "high")
2965
- .forEach((suggestion) => {
2966
- const params = suggestion.params
2967
- ? `(${Object.entries(suggestion.params)
2968
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
2969
- .join(", ")})`
2970
- : "()"
2971
- results.push(`• ${suggestion.function}${params}`)
2972
- results.push(` → ${suggestion.reason}`)
2973
- })
2974
- results.push("")
2975
- }
2976
-
2977
- // Phase 2: Deep Analysis
2978
- results.push("🕑 **PHASE 2: DEEP TARGETED ANALYSIS** (3-5 minutes)")
2979
- results.push("Based on Phase 1 findings, execute these functions sequentially:")
2980
- results.push("")
2981
-
2982
- results.push("🔗 **Cross-MCP Correlation:**")
2983
- results.push("• Compare dev3000 interaction data with browser console errors")
2984
- if (integrateNextjs) {
2985
- results.push("• Correlate dev3000 server errors with Next.js build/runtime logs")
2986
- results.push("• Match interaction timestamps with Next.js request handling")
2987
- }
2988
- results.push("• Identify root cause by combining all data sources")
2989
- results.push("")
2990
-
2991
- results.push("🎯 **Targeted Deep Dive:**")
2992
- results.push("• Use fix_my_app(mode='bisect') for regression analysis if needed")
2993
- if (integrateChromeDevtools) {
2994
- chromeSuggestions
2995
- .filter((s) => s.priority === "medium")
2996
- .forEach((suggestion) => {
2997
- const params = suggestion.params
2998
- ? `(${Object.entries(suggestion.params)
2999
- .map(([k, v]) => `${k}=${JSON.stringify(v)}`)
3000
- .join(", ")})`
3001
- : "()"
3002
- results.push(`• ${suggestion.function}${params} - ${suggestion.reason}`)
3003
- })
3004
- }
3005
- results.push("")
3006
-
3007
- // Phase 3: Fix Implementation & Verification
3008
- results.push("🕒 **PHASE 3: FIX IMPLEMENTATION & VERIFICATION** (5-10 minutes)")
3009
- results.push("Orchestrated fix implementation with cross-MCP verification:")
3010
- results.push("")
3011
-
3012
- results.push("🔧 **Implementation:**")
3013
- results.push("• Apply code fixes identified by dev3000 error analysis")
3014
- if (integrateNextjs) {
3015
- results.push("• Address Next.js-specific issues (hydration, build, etc.)")
3016
- }
3017
- results.push("• Use dev3000's interaction data to create comprehensive test scenarios")
3018
- results.push("")
3019
-
3020
- results.push("✅ **Verification Workflow:**")
3021
- results.push("• Use execute_browser_action to replay exact user interactions that caused errors")
3022
- if (integrateChromeDevtools) {
3023
- results.push("• Use chrome-devtools to monitor console for error resolution")
3024
- results.push("• Take before/after screenshots to verify UI fixes")
3025
- }
3026
- if (integrateNextjs) {
3027
- results.push("• Use nextjs-dev to verify build success and runtime stability")
3028
- }
3029
- results.push("• Re-run fix_my_app to confirm error resolution")
3030
- results.push("")
3031
-
3032
- // Integration Benefits
3033
- results.push("🚀 **INTEGRATION BENEFITS:**")
3034
-
3035
- if (integrateNextjs && integrateChromeDevtools) {
3036
- results.push("🎯 **Triple-Stack Coverage:**")
3037
- results.push("• dev3000: AI-powered error correlation + interaction replay")
3038
- results.push("• nextjs-dev: Framework-specific server-side analysis")
3039
- results.push("• chrome-devtools: Precise browser state inspection")
3040
- results.push("• Combined: Complete full-stack debugging with 90%+ issue resolution")
3041
- results.push("")
3042
- results.push("⚡ **Expected Results:**")
3043
- results.push("• 3x faster debugging vs using tools individually")
3044
- results.push("• AI-powered error correlation across all layers")
3045
- results.push("• Systematic fix verification workflow")
3046
- results.push("• Comprehensive interaction-based testing")
3047
- } else if (integrateNextjs) {
3048
- results.push("🎯 **Server-Side Enhanced Coverage:**")
3049
- results.push("• dev3000: Client error analysis + interaction data")
3050
- results.push("• nextjs-dev: Server-side logs and build analysis")
3051
- results.push("• Combined: Full-stack Next.js debugging coverage")
3052
- } else if (integrateChromeDevtools) {
3053
- results.push("🎯 **Browser-Enhanced Coverage:**")
3054
- results.push("• dev3000: Error detection + interaction replay")
3055
- results.push("• chrome-devtools: Detailed browser state inspection")
3056
- results.push("• Combined: Complete client-side debugging workflow")
3057
- }
3058
-
3059
- const estimatedTime = calculateEstimatedTime(5, integrateNextjs || integrateChromeDevtools) // Assume 5 errors for estimation
3060
- results.push("")
3061
- results.push(`⏱️ **ESTIMATED TOTAL TIME:** ${estimatedTime}`)
3062
- results.push(`🎼 **dev3000 orchestrates ${finalMcps.length} MCPs for maximum debugging power!**`)
3063
-
3064
- return {
3065
- content: [{ type: "text", text: results.join("\n") }]
3066
- }
3067
- }
3068
-
3069
- /**
3070
- * Visual diff analyzer - provides instructions for Claude to load and compare two images
3071
- */
3072
- export async function analyzeVisualDiff(params: {
3073
- beforeImageUrl: string
3074
- afterImageUrl: string
3075
- context?: string
3076
- }): Promise<{ content: Array<{ type: "text"; text: string }> }> {
3077
- const { beforeImageUrl, afterImageUrl, context } = params
3078
-
3079
- const results: string[] = []
3080
-
3081
- results.push("🔍 **VISUAL DIFF ANALYSIS**")
3082
- results.push("")
3083
- results.push("To analyze the visual differences between these two screenshots:")
3084
- results.push("")
3085
- results.push("**Step 1: Fetch and analyze the BEFORE image**")
3086
- results.push(`Use WebFetch with URL: \`${beforeImageUrl}\``)
3087
- results.push(`Prompt: "Describe this screenshot in detail, focusing on layout and visible elements"`)
3088
- results.push("")
3089
- results.push("**Step 2: Fetch and analyze the AFTER image**")
3090
- results.push(`Use WebFetch with URL: \`${afterImageUrl}\``)
3091
- results.push(`Prompt: "Describe this screenshot in detail, focusing on layout and visible elements"`)
3092
- results.push("")
3093
- results.push("**Step 3: Compare and describe the differences**")
3094
-
3095
- if (context) {
3096
- results.push(`Focus on: ${context}`)
3097
- } else {
3098
- results.push("Look for:")
3099
- results.push("• Elements that appeared or disappeared")
3100
- results.push("• Elements that moved or changed position")
3101
- results.push("• Elements that changed size or style")
3102
- results.push("• New content that pushed existing content")
3103
- }
3104
-
3105
- results.push("")
3106
- results.push("**Step 4: Identify the layout shift cause**")
3107
- results.push("Describe what visual change occurred that caused the layout shift.")
3108
- results.push("Be specific about:")
3109
- results.push("• Which element(s) changed")
3110
- results.push("• What appeared/moved/resized")
3111
- results.push("• Why this caused other elements to shift")
3112
-
3113
- return {
3114
- content: [{ type: "text", text: results.join("\n") }]
3115
- }
3116
- }
3117
-
3118
- export async function findComponentSource(params: {
3119
- selector: string
3120
- projectName?: string
3121
- }): Promise<{ content: Array<{ type: "text"; text: string }> }> {
3122
- const { selector } = params
3123
-
3124
- try {
3125
- const sessions = findActiveSessions()
3126
- if (sessions.length === 0) {
3127
- return {
3128
- content: [
3129
- {
3130
- type: "text",
3131
- text: "❌ **NO ACTIVE SESSIONS**\n\nNo active dev3000 sessions found. Make sure your app is running with dev3000."
3132
- }
3133
- ]
3134
- }
3135
- }
3136
-
3137
- const sessionData = JSON.parse(readFileSync(sessions[0].sessionFile, "utf-8"))
3138
- let cdpUrl = sessionData.cdpUrl
3139
-
3140
- if (!cdpUrl) {
3141
- try {
3142
- const response = await fetch("http://localhost:9222/json")
3143
- const pages = await response.json()
3144
- const activePage = pages.find(
3145
- (page: { type: string; url: string }) => page.type === "page" && !page.url.startsWith("chrome://")
3146
- )
3147
- if (activePage) {
3148
- cdpUrl = activePage.webSocketDebuggerUrl
3149
- }
3150
- } catch {
3151
- return {
3152
- content: [
3153
- {
3154
- type: "text",
3155
- text: "❌ **NO CDP CONNECTION**\n\nFailed to find Chrome DevTools Protocol URL."
3156
- }
3157
- ]
3158
- }
3159
- }
3160
- }
3161
-
3162
- if (!cdpUrl) {
3163
- return {
3164
- content: [
3165
- {
3166
- type: "text",
3167
- text: "❌ **NO CDP CONNECTION**\n\nNo Chrome DevTools Protocol URL found."
3168
- }
3169
- ]
3170
- }
3171
- }
3172
-
3173
- // Execute the component extraction script
3174
- const extractScript = `
3175
- (function() {
3176
- try {
3177
- const element = document.querySelector(${JSON.stringify(selector)});
3178
- if (!element) {
3179
- return { error: "Element not found with selector: ${selector}" };
3180
- }
3181
-
3182
- // Try to find React Fiber
3183
- const fiberKey = Object.keys(element).find(k => k.startsWith("__reactFiber$"));
3184
- if (!fiberKey) {
3185
- return { error: "No React internals found - element may not be a React component" };
3186
- }
3187
-
3188
- const fiber = element[fiberKey];
3189
- let componentFunction = null;
3190
- let componentName = "Unknown";
3191
-
3192
- // Walk up the fiber tree to find a function component
3193
- let current = fiber;
3194
- let depth = 0;
3195
-
3196
- while (current && depth < 10) {
3197
- if (typeof current.type === 'function') {
3198
- componentFunction = current.type;
3199
- componentName = current.type.name || current.type.displayName || "Anonymous";
3200
- break;
3201
- }
3202
- current = current.return;
3203
- depth++;
3204
- }
3205
-
3206
- if (!componentFunction) {
3207
- return { error: "Could not find component function in fiber tree" };
3208
- }
3209
-
3210
- // Get the source code
3211
- const sourceCode = componentFunction.toString();
3212
-
3213
- return {
3214
- success: true,
3215
- componentName,
3216
- sourceCode
3217
- };
3218
- } catch (error) {
3219
- return { error: error.message };
3220
- }
3221
- })()
3222
- `
3223
-
3224
- const result = await new Promise<unknown>((resolve, reject) => {
3225
- const ws = new WebSocket(cdpUrl)
3226
- let evalId: number | null = null
3227
- let resolved = false
3228
-
3229
- const timeout = setTimeout(() => {
3230
- if (!resolved) {
3231
- resolved = true
3232
- ws.close()
3233
- reject(new Error("CDP evaluation timeout after 5 seconds"))
3234
- }
3235
- }, 5000)
3236
-
3237
- ws.on("open", async () => {
3238
- try {
3239
- ws.send(JSON.stringify({ id: 1, method: "Target.getTargets", params: {} }))
3240
-
3241
- let messageId = 2
3242
-
3243
- ws.on("message", async (data) => {
3244
- const message = JSON.parse(data.toString())
3245
-
3246
- if (message.id === 1) {
3247
- // Check for CDP protocol errors (e.g., "Not allowed" in sandboxed environments)
3248
- if (message.error) {
3249
- clearTimeout(timeout)
3250
- resolved = true
3251
- ws.close()
3252
- reject(
3253
- new Error(
3254
- `Browser protocol error: ${message.error.message || JSON.stringify(message.error)}. This may occur in sandboxed browser environments where certain CDP commands are restricted.`
3255
- )
3256
- )
3257
- return
3258
- }
3259
-
3260
- const pageTarget = message.result?.targetInfos?.find((t: Record<string, unknown>) => t.type === "page")
3261
- if (!pageTarget) {
3262
- clearTimeout(timeout)
3263
- resolved = true
3264
- ws.close()
3265
- reject(new Error("No page targets found"))
3266
- return
3267
- }
3268
-
3269
- ws.send(
3270
- JSON.stringify({
3271
- id: messageId++,
3272
- method: "Target.attachToTarget",
3273
- params: { targetId: pageTarget.targetId, flatten: true }
3274
- })
3275
- )
3276
- return
3277
- }
3278
-
3279
- if (message.method === "Target.attachedToTarget") {
3280
- evalId = messageId++
3281
- ws.send(
3282
- JSON.stringify({
3283
- id: evalId,
3284
- method: "Runtime.evaluate",
3285
- params: { expression: extractScript, returnByValue: true }
3286
- })
3287
- )
3288
- return
3289
- }
3290
-
3291
- if (evalId !== null && message.id === evalId) {
3292
- clearTimeout(timeout)
3293
- resolved = true
3294
- ws.close()
3295
- if (message.error) {
3296
- reject(new Error(message.error.message))
3297
- } else {
3298
- const value = message.result?.result?.value
3299
- resolve(value)
3300
- }
3301
- }
3302
- })
3303
-
3304
- ws.on("error", (err) => {
3305
- clearTimeout(timeout)
3306
- if (!resolved) {
3307
- resolved = true
3308
- reject(err)
3309
- }
3310
- })
3311
- } catch (error) {
3312
- clearTimeout(timeout)
3313
- resolved = true
3314
- ws.close()
3315
- reject(error)
3316
- }
3317
- })
3318
-
3319
- ws.on("error", (err) => {
3320
- clearTimeout(timeout)
3321
- if (!resolved) {
3322
- resolved = true
3323
- reject(err)
3324
- }
3325
- })
3326
- })
3327
-
3328
- const evalResult = result as
3329
- | { error: string }
3330
- | {
3331
- success: true
3332
- componentName: string
3333
- sourceCode: string
3334
- }
3335
-
3336
- if ("error" in evalResult) {
3337
- return {
3338
- content: [
3339
- {
3340
- type: "text",
3341
- text: `❌ **ERROR EXTRACTING COMPONENT**\n\n${evalResult.error}\n\n💡 **TIPS:**\n• Make sure the selector matches an element on the page\n• Ensure the element is rendered by a React component\n• Try a simpler selector like 'nav' or '.header'`
3342
- }
3343
- ]
3344
- }
3345
- }
3346
-
3347
- if (!evalResult.success) {
3348
- return {
3349
- content: [
3350
- {
3351
- type: "text",
3352
- text: "❌ **FAILED TO EXTRACT COMPONENT**\n\nUnexpected result format."
3353
- }
3354
- ]
3355
- }
3356
- }
3357
-
3358
- // Extract unique patterns from the source code
3359
- const { componentName, sourceCode } = evalResult
3360
- const patterns: string[] = []
3361
-
3362
- // Look for unique JSX patterns (excluding common ones like <div>, <span>)
3363
- const jsxPattern = /<([A-Z][a-zA-Z0-9]*)/g
3364
- const customComponents = new Set<string>()
3365
- let jsxMatch = jsxPattern.exec(sourceCode)
3366
-
3367
- while (jsxMatch !== null) {
3368
- customComponents.add(jsxMatch[1])
3369
- jsxMatch = jsxPattern.exec(sourceCode)
3370
- }
3371
-
3372
- // Look for unique className patterns
3373
- const classNamePattern = /className=["']([^"']+)["']/g
3374
- const classNames = new Set<string>()
3375
- let classNameMatch = classNamePattern.exec(sourceCode)
3376
-
3377
- while (classNameMatch !== null) {
3378
- classNames.add(classNameMatch[1])
3379
- classNameMatch = classNamePattern.exec(sourceCode)
3380
- }
3381
-
3382
- // Build search patterns
3383
- const lines: string[] = []
3384
- lines.push("🔍 **COMPONENT SOURCE FINDER**")
3385
- lines.push("")
3386
- lines.push(`**Selector:** \`${selector}\``)
3387
- lines.push(`**Component:** ${componentName}`)
3388
- lines.push("")
3389
-
3390
- if (componentName !== "Anonymous") {
3391
- patterns.push(`function ${componentName}`)
3392
- patterns.push(`const ${componentName} =`)
3393
- patterns.push(`export default function ${componentName}`)
3394
- }
3395
-
3396
- // Add unique component references
3397
- if (customComponents.size > 0) {
3398
- const uniqueComponents = Array.from(customComponents).filter(
3399
- (name) => !["Fragment", "Suspense", "ErrorBoundary"].includes(name)
3400
- )
3401
- if (uniqueComponents.length > 0) {
3402
- patterns.push(`<${uniqueComponents[0]}`)
3403
- }
3404
- }
3405
-
3406
- // Add unique classNames
3407
- if (classNames.size > 0) {
3408
- const firstClassName = Array.from(classNames)[0]
3409
- patterns.push(`className="${firstClassName}"`)
3410
- }
3411
-
3412
- if (patterns.length === 0) {
3413
- lines.push("⚠️ **NO UNIQUE PATTERNS FOUND**")
3414
- lines.push("")
3415
- lines.push("The component source code doesn't contain distinctive patterns to search for.")
3416
- lines.push("You may need to manually search for the component.")
3417
- } else {
3418
- lines.push("📍 **SEARCH PATTERNS**")
3419
- lines.push("")
3420
- lines.push("Use these grep patterns to find the source file:")
3421
- lines.push("")
3422
-
3423
- for (const pattern of patterns.slice(0, 3)) {
3424
- lines.push(`\`\`\``)
3425
- lines.push(`grep -r "${pattern.replace(/"/g, '\\"')}" .`)
3426
- lines.push(`\`\`\``)
3427
- lines.push("")
3428
- }
3429
-
3430
- lines.push("💡 **TIP:** Start with the first pattern. If it returns multiple results, try combining patterns.")
3431
- }
3432
-
3433
- // Show a preview of the source code
3434
- const preview = sourceCode.substring(0, 300)
3435
- lines.push("")
3436
- lines.push("**Source Code Preview:**")
3437
- lines.push("```javascript")
3438
- lines.push(`${preview}...`)
3439
- lines.push("```")
3440
-
3441
- return {
3442
- content: [{ type: "text", text: lines.join("\n") }]
3443
- }
3444
- } catch (error) {
3445
- return {
3446
- content: [
3447
- {
3448
- type: "text",
3449
- text: `❌ **ERROR**\n\n${error instanceof Error ? error.message : String(error)}`
3450
- }
3451
- ]
3452
- }
3453
- }
3454
- }
3455
-
3456
- /**
3457
- * Restart the development server while preserving logs and monitoring
3458
- */
3459
- export async function restartDevServer(params: {
3460
- projectName?: string
3461
- }): Promise<{ content: Array<{ type: "text"; text: string }> }> {
3462
- const { projectName } = params
3463
-
3464
- try {
3465
- // Find active session
3466
- const sessions = findActiveSessions()
3467
- if (sessions.length === 0) {
3468
- return {
3469
- content: [
3470
- {
3471
- type: "text",
3472
- text: "❌ **NO ACTIVE SESSIONS**\n\nNo active dev3000 sessions found. Make sure your app is running with dev3000."
3473
- }
3474
- ]
3475
- }
3476
- }
3477
-
3478
- // Use specified project or first available session
3479
- let targetSession = sessions[0]
3480
- if (projectName) {
3481
- const found = sessions.find((s) => s.projectName === projectName)
3482
- if (found) {
3483
- targetSession = found
3484
- }
3485
- }
3486
-
3487
- const sessionData = JSON.parse(readFileSync(targetSession.sessionFile, "utf-8"))
3488
- const appPort = sessionData.appPort
3489
- const serverCommand = sessionData.serverCommand
3490
- const cwd = sessionData.cwd
3491
-
3492
- if (!appPort) {
3493
- return {
3494
- content: [
3495
- {
3496
- type: "text",
3497
- text: "❌ **NO APP PORT FOUND**\n\nSession file doesn't contain app port information."
3498
- }
3499
- ]
3500
- }
3501
- }
3502
-
3503
- if (!serverCommand) {
3504
- return {
3505
- content: [
3506
- {
3507
- type: "text",
3508
- text: "❌ **NO SERVER COMMAND FOUND**\n\nSession file doesn't contain the original server command. This session may have been created with an older version of dev3000."
3509
- }
3510
- ]
3511
- }
3512
- }
3513
-
3514
- logToDevFile(
3515
- `Restart Dev Server: Starting restart for project [${targetSession.projectName}] on port ${appPort} with command [${serverCommand}]`
3516
- )
3517
-
3518
- // Check if nextjs-dev MCP is available
3519
- const availableMcps = await discoverAvailableMcps(targetSession.projectName)
3520
- const hasNextjsDev = availableMcps.includes("nextjs-dev")
3521
-
3522
- logToDevFile(`Restart Dev Server: Has nextjs-dev MCP: ${hasNextjsDev}`)
3523
-
3524
- // Try nextjs-dev MCP first if available
3525
- if (hasNextjsDev) {
3526
- try {
3527
- logToDevFile("Restart Dev Server: Attempting to use nextjs-dev MCP restart")
3528
-
3529
- // Check if nextjs-dev has restart capability
3530
- const capabilities = await getMcpCapabilities({ mcpName: "nextjs-dev" })
3531
- const capabilitiesText =
3532
- capabilities.content[0] && "text" in capabilities.content[0] ? capabilities.content[0].text : ""
3533
-
3534
- if (capabilitiesText.includes("restart") || capabilitiesText.includes("reload")) {
3535
- logToDevFile("Restart Dev Server: nextjs-dev MCP has restart capability, delegating")
3536
-
3537
- return {
3538
- content: [
3539
- {
3540
- type: "text",
3541
- text: "✅ **DELEGATING TO NEXTJS-DEV MCP**\n\nThe nextjs-dev MCP has restart capabilities. Please use the nextjs-dev MCP restart tool directly for better integration with Next.js."
3542
- }
3543
- ]
3544
- }
3545
- }
3546
-
3547
- logToDevFile("Restart Dev Server: nextjs-dev MCP doesn't have restart capability, falling back")
3548
- } catch (error) {
3549
- logToDevFile(`Restart Dev Server: Failed to check nextjs-dev capabilities - ${error}`)
3550
- }
3551
- }
3552
-
3553
- // Fallback: Use dev3000's own restart mechanism
3554
- logToDevFile("Restart Dev Server: Using dev3000 restart mechanism")
3555
-
3556
- // In sandbox environments, lsof doesn't exist - skip process killing
3557
- if (isInSandbox()) {
3558
- logToDevFile("Restart Dev Server: Skipping lsof-based kill in sandbox environment")
3559
- return {
3560
- content: [
3561
- {
3562
- type: "text",
3563
- text: `⚠️ **RESTART NOT SUPPORTED IN SANDBOX**\n\nDev server restart is not supported in sandbox environments (Vercel Sandbox, Docker containers).\n\nThe \`lsof\` utility needed for process management is not available.\n\n💡 If running in Vercel Sandbox, the dev server is managed by the sandbox infrastructure.`
3564
- }
3565
- ]
3566
- }
3567
- }
3568
-
3569
- // Kill processes on the app port
3570
- const killCommand = `lsof -ti :${appPort} | xargs kill 2>/dev/null || true`
3571
- logToDevFile(`Restart Dev Server: Executing kill command: ${killCommand}`)
3572
-
3573
- try {
3574
- await execAsync(killCommand)
3575
- logToDevFile("Restart Dev Server: Kill command executed successfully")
3576
- } catch (error) {
3577
- logToDevFile(`Restart Dev Server: Kill command failed (may be ok) - ${error}`)
3578
- }
3579
-
3580
- // Wait for clean shutdown
3581
- await new Promise((resolve) => setTimeout(resolve, 2000))
3582
-
3583
- // Check if port is now free
3584
- const checkCommand = `lsof -ti :${appPort}`
3585
- let portFree = false
3586
- try {
3587
- const { stdout } = await execAsync(checkCommand)
3588
- portFree = stdout.trim() === ""
3589
- logToDevFile(`Restart Dev Server: Port check result - free: ${portFree}`)
3590
- } catch {
3591
- // Command failed means no process on port (port is free)
3592
- portFree = true
3593
- logToDevFile("Restart Dev Server: Port is free (lsof returned no results)")
3594
- }
3595
-
3596
- if (!portFree) {
3597
- return {
3598
- content: [
3599
- {
3600
- type: "text",
3601
- text: `⚠️ **PORT STILL IN USE**\n\nFailed to free port ${appPort}. There may be a process that couldn't be killed.\n\nTry manually killing the process:\n\`\`\`bash\nlsof -ti :${appPort} | xargs kill -9\n\`\`\``
3602
- }
3603
- ]
3604
- }
3605
- }
3606
-
3607
- logToDevFile("Restart Dev Server: Port is now free, spawning new server process")
3608
-
3609
- // Spawn new server process
3610
- try {
3611
- const serverProcess = spawn(serverCommand, {
3612
- stdio: "inherit", // Inherit stdio so output goes to dev3000's logs
3613
- shell: true,
3614
- detached: true, // Run independently
3615
- cwd: cwd || process.cwd() // Use original working directory
3616
- })
3617
-
3618
- // Unref so this process doesn't keep MCP server alive
3619
- serverProcess.unref()
3620
-
3621
- logToDevFile(`Restart Dev Server: Spawned new server process with PID ${serverProcess.pid}`)
3622
-
3623
- // Wait a moment for server to start
3624
- await new Promise((resolve) => setTimeout(resolve, 1000))
3625
-
3626
- // Check if server is actually running on the port
3627
- try {
3628
- const { stdout: checkResult } = await execAsync(`lsof -ti :${appPort}`)
3629
- const isRunning = checkResult.trim() !== ""
3630
-
3631
- if (isRunning) {
3632
- logToDevFile("Restart Dev Server: Server successfully restarted and running on port")
3633
- return {
3634
- content: [
3635
- {
3636
- type: "text",
3637
- text: `✅ **DEV SERVER RESTARTED**\n\nSuccessfully restarted the development server on port ${appPort}.\n\n🎯 **STATUS:**\n• Old server process: Killed\n• New server process: Running (PID ${serverProcess.pid})\n• Port ${appPort}: Active\n• Browser monitoring: Unchanged\n• Logs: Still being captured\n\n💡 The server has been restarted while keeping dev3000's monitoring, screenshots, and logging intact.`
3638
- }
3639
- ]
3640
- }
3641
- }
3642
- logToDevFile("Restart Dev Server: Server process spawned but not yet listening on port (may still be starting)")
3643
- } catch {
3644
- logToDevFile("Restart Dev Server: Server process spawned but not yet listening on port (may still be starting)")
3645
- }
3646
-
3647
- return {
3648
- content: [
3649
- {
3650
- type: "text",
3651
- text: `🔄 **DEV SERVER RESTARTING**\n\nStarted a new server process (PID ${serverProcess.pid}).\n\n⏳ **STATUS:**\n• Old server: Killed\n• New server: Starting (may take a few moments)\n• Command: \`${serverCommand}\`\n• Port: ${appPort}\n\nThe server is restarting. Check the dev3000 logs to see when it's ready.`
3652
- }
3653
- ]
3654
- }
3655
- } catch (spawnError) {
3656
- logToDevFile(`Restart Dev Server: Failed to spawn new server process - ${spawnError}`)
3657
- return {
3658
- content: [
3659
- {
3660
- type: "text",
3661
- text: `❌ **RESTART FAILED**\n\nFailed to start new server process.\n\n**Error:** ${spawnError instanceof Error ? spawnError.message : String(spawnError)}\n\n**Command:** \`${serverCommand}\`\n\nThe old server was killed but the new one failed to start. You may need to manually restart dev3000.`
3662
- }
3663
- ]
3664
- }
3665
- }
3666
- } catch (error) {
3667
- logToDevFile(`Restart Dev Server: Error - ${error}`)
3668
- return {
3669
- content: [
3670
- {
3671
- type: "text",
3672
- text: `❌ **ERROR**\n\n${error instanceof Error ? error.message : String(error)}`
3673
- }
3674
- ]
3675
- }
3676
- }
3677
- }
3678
-
3679
- // Crawl app - discover all URLs
3680
- export interface CrawlAppParams {
3681
- depth?: number | "all"
3682
- limit?: number
3683
- projectName?: string
3684
- }
3685
-
3686
- export async function crawlApp(params: CrawlAppParams) {
3687
- const { depth = 1, limit = 3, projectName } = params
3688
-
3689
- try {
3690
- // Find active session
3691
- const sessions = findActiveSessions()
3692
- const session = projectName ? sessions.find((s) => s.projectName === projectName) : sessions[0]
3693
-
3694
- if (!session) {
3695
- return {
3696
- content: [
3697
- {
3698
- type: "text" as const,
3699
- text: projectName
3700
- ? `❌ No active session found for project "${projectName}". Available projects: ${sessions.map((s) => s.projectName).join(", ") || "none"}`
3701
- : "❌ No active dev3000 sessions found. Start dev3000 first with `d3k` in your project directory."
3702
- }
3703
- ]
3704
- }
3705
- }
3706
-
3707
- // Get CDP URL and app port from session
3708
- const sessionData = JSON.parse(readFileSync(session.sessionFile, "utf-8"))
3709
- const cdpUrl = sessionData.cdpUrl?.replace("http://", "ws://")
3710
- const appPort = sessionData.appPort || "3000"
3711
- const baseUrl = `http://localhost:${appPort}`
3712
-
3713
- if (!cdpUrl) {
3714
- return {
3715
- content: [
3716
- {
3717
- type: "text" as const,
3718
- text: "❌ No Chrome DevTools connection found. Browser monitoring must be active to crawl."
3719
- }
3720
- ]
3721
- }
3722
- }
3723
-
3724
- logToDevFile(`Crawl App: Starting crawl at depth ${depth} with limit ${limit} for ${baseUrl}`)
3725
-
3726
- // Connect to CDP
3727
- const ws = new WebSocket(cdpUrl)
3728
- await new Promise((resolve, reject) => {
3729
- ws.on("open", resolve)
3730
- ws.on("error", reject)
3731
- setTimeout(() => reject(new Error("CDP connection timeout")), 5000)
3732
- })
3733
-
3734
- let messageId = 2000
3735
- // biome-ignore lint/suspicious/noExplicitAny: CDP protocol responses are dynamic
3736
- const sendCommand = (method: string, params: Record<string, unknown> = {}): Promise<any> => {
3737
- return new Promise((resolve, reject) => {
3738
- const id = messageId++
3739
- const message = JSON.stringify({ id, method, params })
3740
-
3741
- const handler = (data: Buffer) => {
3742
- const response = JSON.parse(data.toString())
3743
- if (response.id === id) {
3744
- ws.off("message", handler)
3745
- if (response.error) {
3746
- reject(new Error(response.error.message))
3747
- } else {
3748
- resolve(response.result)
3749
- }
3750
- }
3751
- }
3752
-
3753
- ws.on("message", handler)
3754
- ws.send(message)
3755
-
3756
- setTimeout(() => {
3757
- ws.off("message", handler)
3758
- reject(new Error("Command timeout"))
3759
- }, 10000)
3760
- })
3761
- }
3762
-
3763
- // Enable necessary domains
3764
- await sendCommand("Runtime.enable")
3765
- await sendCommand("Page.enable")
3766
-
3767
- // Discovered URLs
3768
- const discovered = new Set<string>([baseUrl])
3769
- const visited = new Set<string>()
3770
- const toVisit: string[] = [baseUrl]
3771
-
3772
- let currentDepth = 0
3773
- const maxDepth = depth === "all" ? Number.POSITIVE_INFINITY : depth
3774
-
3775
- while (toVisit.length > 0 && currentDepth <= maxDepth) {
3776
- const currentLevelUrls = [...toVisit]
3777
- toVisit.length = 0
3778
-
3779
- logToDevFile(`Crawl App: Processing depth ${currentDepth} with ${currentLevelUrls.length} URLs`)
3780
-
3781
- for (const url of currentLevelUrls) {
3782
- if (visited.has(url)) continue
3783
- visited.add(url)
3784
-
3785
- try {
3786
- // Navigate to URL
3787
- logToDevFile(`Crawl App: Visiting ${url}`)
3788
- await sendCommand("Page.navigate", { url })
3789
-
3790
- // Wait for page load
3791
- await new Promise((resolve) => setTimeout(resolve, 2000))
3792
-
3793
- // Extract all links
3794
- const result = await sendCommand("Runtime.evaluate", {
3795
- expression: `
3796
- Array.from(document.querySelectorAll('a[href]')).map(a => {
3797
- try {
3798
- const url = new URL(a.href, window.location.href);
3799
- // Only return same-origin links
3800
- if (url.origin === window.location.origin) {
3801
- // Remove hash and query params for deduplication
3802
- return url.origin + url.pathname;
3803
- }
3804
- } catch {}
3805
- return null;
3806
- }).filter(Boolean)
3807
- `,
3808
- returnByValue: true
3809
- })
3810
-
3811
- const links = result.result?.value || []
3812
-
3813
- // Apply limit to prevent following too many links per page
3814
- let linksAdded = 0
3815
- for (const link of links) {
3816
- if (!discovered.has(link)) {
3817
- discovered.add(link)
3818
- if (currentDepth < maxDepth && linksAdded < limit) {
3819
- toVisit.push(link)
3820
- linksAdded++
3821
- }
3822
- }
3823
- }
3824
-
3825
- logToDevFile(
3826
- `Crawl App: Found ${links.length} links on ${url}, added ${linksAdded} to queue (limit: ${limit})`
3827
- )
3828
- } catch (error) {
3829
- logToDevFile(`Crawl App: Error visiting ${url} - ${error}`)
3830
- }
3831
- }
3832
-
3833
- currentDepth++
3834
-
3835
- // For "all" mode, stop when no new URLs are found
3836
- if (depth === "all" && toVisit.length === 0) {
3837
- break
3838
- }
3839
- }
3840
-
3841
- ws.close()
3842
-
3843
- const urls = Array.from(discovered).sort()
3844
- const depthReached = depth === "all" ? currentDepth - 1 : Math.min(currentDepth - 1, maxDepth)
3845
-
3846
- logToDevFile(`Crawl App: Complete - discovered ${urls.length} URLs at depth ${depthReached}`)
3847
-
3848
- return {
3849
- content: [
3850
- {
3851
- type: "text" as const,
3852
- text: `🕷️ **APP CRAWL COMPLETE**\n\n📊 **SUMMARY:**\n• Base URL: ${baseUrl}\n• Depth: ${depthReached}${depth === "all" ? " (exhaustive)" : ""}\n• Total URLs: ${urls.length}\n\n📍 **DISCOVERED URLs:**\n${urls.map((url) => `• ${url}`).join("\n")}\n\n💡 **NEXT STEPS:**\n• Use fix_my_app to check for errors across all pages\n• Use execute_browser_action to test specific pages\n• Verify all routes are working correctly`
3853
- }
3854
- ]
3855
- }
3856
- } catch (error) {
3857
- logToDevFile(`Crawl App: Error - ${error}`)
3858
- return {
3859
- content: [
3860
- {
3861
- type: "text" as const,
3862
- text: `❌ **CRAWL FAILED**\n\n${error instanceof Error ? error.message : String(error)}`
3863
- }
3864
- ]
3865
- }
3866
- }
3867
- }
3868
-
3869
- // ============================================================
3870
- // Get Skill
3871
- // ============================================================
3872
-
3873
- export interface GetSkillParams {
3874
- name: string
3875
- }
3876
-
3877
- /**
3878
- * Get the content of a d3k/Claude Code skill.
3879
- * Calls `d3k skill <name>` CLI to leverage the shared skill implementation.
3880
- *
3881
- * Skills are prompt templates stored in .claude/skills/{name}/SKILL.md
3882
- */
3883
- export async function getSkill(params: GetSkillParams) {
3884
- const { name } = params
3885
-
3886
- logToDevFile(`Get Skill: Fetching skill "${name}" via CLI`)
3887
-
3888
- try {
3889
- // Call d3k CLI to get skill content
3890
- // This leverages the shared skill implementation in src/skills/index.ts
3891
- const { stdout, stderr } = await execAsync(`d3k skill "${name}"`, {
3892
- timeout: 5000,
3893
- env: { ...process.env, NO_COLOR: "1" } // Disable chalk colors for clean output
3894
- })
3895
-
3896
- if (stderr && !stdout) {
3897
- // CLI returned error
3898
- logToDevFile(`Get Skill: CLI error for "${name}": ${stderr}`)
3899
- return {
3900
- content: [
3901
- {
3902
- type: "text" as const,
3903
- text: `❌ ${stderr.trim()}`
3904
- }
3905
- ]
3906
- }
3907
- }
3908
-
3909
- logToDevFile(`Get Skill: Found skill "${name}" (${stdout.length} chars)`)
3910
- return {
3911
- content: [
3912
- {
3913
- type: "text" as const,
3914
- text: stdout
3915
- }
3916
- ]
3917
- }
3918
- } catch (error) {
3919
- // CLI command failed (skill not found or d3k not available)
3920
- const errorMessage = error instanceof Error ? error.message : String(error)
3921
- logToDevFile(`Get Skill: Failed to get skill "${name}": ${errorMessage}`)
3922
-
3923
- // Try to get available skills for a helpful error message
3924
- try {
3925
- const { stdout: listOutput } = await execAsync("d3k skill --list", {
3926
- timeout: 5000,
3927
- env: { ...process.env, NO_COLOR: "1" }
3928
- })
3929
- return {
3930
- content: [
3931
- {
3932
- type: "text" as const,
3933
- text: `❌ Skill "${name}" not found.\n\n${listOutput}`
3934
- }
3935
- ]
3936
- }
3937
- } catch {
3938
- return {
3939
- content: [
3940
- {
3941
- type: "text" as const,
3942
- text: `❌ Skill "${name}" not found. Could not list available skills (d3k CLI may not be available).`
3943
- }
3944
- ]
3945
- }
3946
- }
3947
- }
3948
- }
3949
-
3950
- // ============================================================
3951
- // Agent Browser Integration (using vercel-labs/agent-browser)
3952
- // ============================================================
3953
-
3954
- export interface AgentBrowserActionParams {
3955
- action:
3956
- | "open"
3957
- | "click"
3958
- | "type"
3959
- | "fill"
3960
- | "scroll"
3961
- | "screenshot"
3962
- | "snapshot"
3963
- | "close"
3964
- | "reload"
3965
- | "back"
3966
- | "evaluate"
3967
- params?: Record<string, unknown>
3968
- session?: string // Session name for isolation
3969
- headed?: boolean // Run with visible browser window
3970
- profile?: string // Path to persistent browser profile directory (stores cookies, localStorage, etc.)
3971
- }
3972
-
3973
- /**
3974
- * Execute browser actions using agent-browser CLI
3975
- * This provides an alternative to raw CDP with better reliability in sandbox environments
3976
- */
3977
- export async function executeAgentBrowserAction({
3978
- action,
3979
- params = {},
3980
- session,
3981
- headed = false,
3982
- profile
3983
- }: AgentBrowserActionParams): Promise<{ content: Array<{ type: "text"; text: string }> }> {
3984
- try {
3985
- // Dynamic import of agent-browser wrapper
3986
- const agentBrowser = await import("../../../src/utils/agent-browser")
3987
-
3988
- // Check if agent-browser is available
3989
- if (!agentBrowser.isAgentBrowserAvailable()) {
3990
- return {
3991
- content: [
3992
- {
3993
- type: "text",
3994
- text: `❌ agent-browser is not available. Make sure it's installed with: bun add agent-browser && npx agent-browser install`
3995
- }
3996
- ]
3997
- }
3998
- }
3999
-
4000
- // Use project-specific profile path if not specified - this enables persistent sessions
4001
- // (cookies, localStorage, login state) across browser restarts, per-project
4002
- // Import getProjectDir to get the same profile path that d3k uses for Chrome
4003
- const { getProjectDir } = await import("../../../src/utils/project-name")
4004
- const defaultProfile = profile ?? join(getProjectDir(), "chrome-profile")
4005
-
4006
- // Extract CDP port to connect to existing browser that d3k launched
4007
- // CDP_URL format: ws://localhost:9222/devtools/browser/...
4008
- // Try env var first, then fall back to session file (MCP server starts before CDP is ready)
4009
- let cdpPort: number | undefined
4010
- let cdpUrl = process.env.CDP_URL
4011
-
4012
- // If env var is empty, read from session file
4013
- if (!cdpUrl) {
4014
- try {
4015
- const { existsSync, readFileSync } = await import("fs")
4016
- const sessionFile = join(getProjectDir(), "session.json")
4017
- if (existsSync(sessionFile)) {
4018
- const sessionInfo = JSON.parse(readFileSync(sessionFile, "utf8"))
4019
- cdpUrl = sessionInfo.cdpUrl
4020
- }
4021
- } catch {
4022
- // Ignore errors reading session file
4023
- }
4024
- }
4025
-
4026
- if (cdpUrl) {
4027
- const match = cdpUrl.match(/:(\d+)\//)
4028
- if (match) {
4029
- cdpPort = parseInt(match[1], 10)
4030
- }
4031
- }
4032
-
4033
- // Build common options - include cdpPort to connect to existing browser
4034
- const options = {
4035
- session,
4036
- headed,
4037
- profile: defaultProfile,
4038
- cdpPort
4039
- }
4040
-
4041
- let result: unknown
4042
-
4043
- switch (action) {
4044
- case "open": {
4045
- const url = typeof params.url === "string" ? params.url : "about:blank"
4046
- const openResult = await agentBrowser.openUrl(url, options)
4047
- result = openResult
4048
- break
4049
- }
4050
-
4051
- case "click": {
4052
- const target = typeof params.target === "string" ? params.target : "@e1"
4053
- const clickResult = await agentBrowser.click(target, options)
4054
- result = clickResult
4055
- break
4056
- }
4057
-
4058
- case "type": {
4059
- const text = typeof params.text === "string" ? params.text : ""
4060
- const typeResult = await agentBrowser.type(text, options)
4061
- result = typeResult
4062
- break
4063
- }
4064
-
4065
- case "fill": {
4066
- const fillTarget = typeof params.target === "string" ? params.target : ""
4067
- const fillValue = typeof params.value === "string" ? params.value : ""
4068
- const fillResult = await agentBrowser.fill(fillTarget, fillValue, options)
4069
- result = fillResult
4070
- break
4071
- }
4072
-
4073
- case "scroll": {
4074
- const direction = (params.direction as "up" | "down" | "left" | "right") || "down"
4075
- const amount = typeof params.amount === "number" ? params.amount : undefined
4076
- const scrollResult = await agentBrowser.scroll(direction, amount, options)
4077
- result = scrollResult
4078
- break
4079
- }
4080
-
4081
- case "screenshot": {
4082
- // Generate screenshot path
4083
- const sessionDir = join(homedir(), ".d3k", "screenshots")
4084
- const timestamp = new Date().toISOString().replace(/[:.]/g, "-")
4085
- const screenshotPath = join(sessionDir, `agent-browser-${timestamp}.png`)
4086
- const fullPage = params.fullPage === true
4087
- const screenshotResult = await agentBrowser.screenshot(screenshotPath, { ...options, fullPage })
4088
- result = screenshotResult
4089
- break
4090
- }
4091
-
4092
- case "snapshot": {
4093
- const interactive = params.interactive !== false
4094
- const compact = params.compact === true
4095
- const snapshotResult = await agentBrowser.snapshot({ ...options, interactive, compact })
4096
- result = {
4097
- elements: snapshotResult.elements,
4098
- elementCount: snapshotResult.elements.length,
4099
- raw: snapshotResult.raw.substring(0, 5000) // Truncate for context efficiency
4100
- }
4101
- break
4102
- }
4103
-
4104
- case "close": {
4105
- const closeResult = await agentBrowser.close(options)
4106
- result = closeResult
4107
- break
4108
- }
4109
-
4110
- case "reload": {
4111
- const reloadResult = await agentBrowser.reload(options)
4112
- result = reloadResult
4113
- break
4114
- }
4115
-
4116
- case "back": {
4117
- const backResult = await agentBrowser.back(options)
4118
- result = backResult
4119
- break
4120
- }
4121
-
4122
- case "evaluate": {
4123
- const expression = typeof params.expression === "string" ? params.expression : ""
4124
- const evalResult = await agentBrowser.evaluate(expression, options)
4125
- result = evalResult
4126
- break
4127
- }
4128
-
4129
- default:
4130
- return {
4131
- content: [
4132
- {
4133
- type: "text",
4134
- text: `❌ Unsupported agent-browser action: ${action}. Supported: open, click, type, fill, scroll, screenshot, snapshot, close, reload, back`
4135
- }
4136
- ]
4137
- }
4138
- }
4139
-
4140
- return {
4141
- content: [
4142
- {
4143
- type: "text",
4144
- text: `✅ agent-browser ${action} completed:\n${JSON.stringify(result, null, 2)}`
4145
- }
4146
- ]
4147
- }
4148
- } catch (error) {
4149
- return {
4150
- content: [
4151
- {
4152
- type: "text",
4153
- text: `❌ agent-browser ${action} failed: ${error instanceof Error ? error.message : String(error)}`
4154
- }
4155
- ]
4156
- }
4157
- }
4158
- }