dev3000 0.0.84 → 0.0.85

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 (156) hide show
  1. package/dist/dev-environment.d.ts.map +1 -1
  2. package/dist/dev-environment.js +31 -186
  3. package/dist/dev-environment.js.map +1 -1
  4. package/dist/src/tui-interface-impl.tsx +1 -1
  5. package/dist/tui-interface-impl.js +1 -1
  6. package/dist/tui-interface-impl.js.map +1 -1
  7. package/mcp-server/.next/BUILD_ID +1 -1
  8. package/mcp-server/.next/app-path-routes-manifest.json +1 -0
  9. package/mcp-server/.next/build-manifest.json +5 -6
  10. package/mcp-server/.next/fallback-build-manifest.json +2 -2
  11. package/mcp-server/.next/next-server.js.nft.json +1 -1
  12. package/mcp-server/.next/prerender-manifest.json +3 -3
  13. package/mcp-server/.next/server/app/_global-error/page/build-manifest.json +3 -4
  14. package/mcp-server/.next/server/app/_global-error/page.js +1 -1
  15. package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
  16. package/mcp-server/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  17. package/mcp-server/.next/server/app/_global-error.html +2 -2
  18. package/mcp-server/.next/server/app/_global-error.rsc +1 -1
  19. package/mcp-server/.next/server/app/_not-found/page/build-manifest.json +3 -4
  20. package/mcp-server/.next/server/app/_not-found/page.js +1 -1
  21. package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
  22. package/mcp-server/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  23. package/mcp-server/.next/server/app/_not-found.html +1 -1
  24. package/mcp-server/.next/server/app/_not-found.rsc +2 -2
  25. package/mcp-server/.next/server/app/api/jank/[session]/route.js +1 -1
  26. package/mcp-server/.next/server/app/api/jank/[session]/route.js.nft.json +1 -1
  27. package/mcp-server/.next/server/app/api/logs/head/route.js.nft.json +1 -1
  28. package/mcp-server/.next/server/app/api/logs/list/route.js.nft.json +1 -1
  29. package/mcp-server/.next/server/app/api/logs/rotate/route.js +1 -1
  30. package/mcp-server/.next/server/app/api/logs/rotate/route.js.nft.json +1 -1
  31. package/mcp-server/.next/server/app/api/logs/stream/route.js.nft.json +1 -1
  32. package/mcp-server/.next/server/app/api/logs/tail/route.js.nft.json +1 -1
  33. package/mcp-server/.next/server/app/api/orchestrator/route/app-paths-manifest.json +3 -0
  34. package/mcp-server/.next/server/app/api/orchestrator/route/build-manifest.json +11 -0
  35. package/mcp-server/.next/server/app/api/orchestrator/route/server-reference-manifest.json +4 -0
  36. package/mcp-server/.next/server/app/api/orchestrator/route.js +8 -0
  37. package/mcp-server/.next/server/app/api/orchestrator/route.js.map +5 -0
  38. package/mcp-server/.next/server/app/api/orchestrator/route.js.nft.json +1 -0
  39. package/mcp-server/.next/server/app/api/orchestrator/route_client-reference-manifest.js +2 -0
  40. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js +1 -1
  41. package/mcp-server/.next/server/app/api/screenshots/[filename]/route.js.nft.json +1 -1
  42. package/mcp-server/.next/server/app/api/screenshots/list/route.js +1 -1
  43. package/mcp-server/.next/server/app/api/screenshots/list/route.js.nft.json +1 -1
  44. package/mcp-server/.next/server/app/index.html +1 -1
  45. package/mcp-server/.next/server/app/index.rsc +3 -3
  46. package/mcp-server/.next/server/app/logs/page/build-manifest.json +3 -4
  47. package/mcp-server/.next/server/app/logs/page.js +1 -1
  48. package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
  49. package/mcp-server/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  50. package/mcp-server/.next/server/app/mcp/route.js +4 -3
  51. package/mcp-server/.next/server/app/mcp/route.js.nft.json +1 -1
  52. package/mcp-server/.next/server/app/page/build-manifest.json +3 -4
  53. package/mcp-server/.next/server/app/page.js +1 -1
  54. package/mcp-server/.next/server/app/page.js.nft.json +1 -1
  55. package/mcp-server/.next/server/app/page_client-reference-manifest.js +1 -1
  56. package/mcp-server/.next/server/app/video/[session]/page/build-manifest.json +3 -4
  57. package/mcp-server/.next/server/app/video/[session]/page.js +1 -1
  58. package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
  59. package/mcp-server/.next/server/app/video/[session]/page_client-reference-manifest.js +1 -1
  60. package/mcp-server/.next/server/app-paths-manifest.json +1 -0
  61. package/mcp-server/.next/server/chunks/[externals]_node:crypto_c20cce38._.js +3 -0
  62. package/mcp-server/.next/server/chunks/[root-of-the-server]__05e38acd._.js +3 -0
  63. package/mcp-server/.next/server/chunks/[root-of-the-server]__05e38acd._.js.map +1 -0
  64. package/mcp-server/.next/server/chunks/{[root-of-the-server]__6ee9a99f._.js → [root-of-the-server]__3130ceec._.js} +2 -2
  65. package/mcp-server/.next/server/chunks/[root-of-the-server]__4637d3dd._.js +3 -0
  66. package/mcp-server/.next/server/chunks/{[root-of-the-server]__bc773251._.js → [root-of-the-server]__5114419d._.js} +2 -2
  67. package/mcp-server/.next/server/chunks/[root-of-the-server]__5248c9ff._.js +3 -0
  68. package/mcp-server/.next/server/chunks/[root-of-the-server]__5248c9ff._.js.map +1 -0
  69. package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js +7 -0
  70. package/mcp-server/.next/server/chunks/[root-of-the-server]__94037b23._.js.map +1 -0
  71. package/mcp-server/.next/server/chunks/{[root-of-the-server]__55c04517._.js → [root-of-the-server]__a004a307._.js} +2 -2
  72. package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js +91 -0
  73. package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js.map +1 -0
  74. package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js +7 -0
  75. package/mcp-server/.next/server/chunks/[root-of-the-server]__cc74dbef._.js.map +1 -0
  76. package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js +17 -0
  77. package/mcp-server/.next/server/chunks/[root-of-the-server]__dc0b0022._.js.map +1 -0
  78. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_orchestrator_route_actions_c6fba9ec.js +3 -0
  79. package/mcp-server/.next/server/chunks/mcp-server__next-internal_server_app_api_orchestrator_route_actions_c6fba9ec.js.map +1 -0
  80. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__61c905ce._.js +3 -0
  81. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__bf771f7e._.js.map → [root-of-the-server]__61c905ce._.js.map} +1 -1
  82. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__8863b0f6._.js +3 -0
  83. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__8863b0f6._.js.map +1 -0
  84. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__9bc632fa._.js +3 -0
  85. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__c4e78a20._.js.map → [root-of-the-server]__9bc632fa._.js.map} +1 -1
  86. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__a4bb31a6._.js +3 -0
  87. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__a4bb31a6._.js.map +1 -0
  88. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__cf17e26a._.js → [root-of-the-server]__aed64cd3._.js} +2 -2
  89. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__aed64cd3._.js.map +1 -0
  90. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__cf030fda._.js +3 -0
  91. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__cf030fda._.js.map +1 -0
  92. package/mcp-server/.next/server/chunks/ssr/{_96ba1d8e._.js → _74bf5366._.js} +2 -2
  93. package/mcp-server/.next/server/chunks/ssr/_74bf5366._.js.map +1 -0
  94. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js +3 -0
  95. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js.map +1 -0
  96. package/mcp-server/.next/server/chunks/ssr/mcp-server_components_dark-mode-toggle_tsx_f31dd15d._.js +3 -0
  97. package/mcp-server/.next/server/chunks/ssr/mcp-server_components_dark-mode-toggle_tsx_f31dd15d._.js.map +1 -0
  98. package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_3d10aa72._.js +3 -0
  99. package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_3d10aa72._.js.map +1 -0
  100. package/mcp-server/.next/server/middleware-build-manifest.js +3 -4
  101. package/mcp-server/.next/server/server-reference-manifest.js +1 -1
  102. package/mcp-server/.next/server/server-reference-manifest.json +1 -1
  103. package/mcp-server/.next/static/chunks/{55cedb1a993e951c.js → 1d2676338b51bbcf.js} +1 -1
  104. package/mcp-server/.next/static/chunks/312a4c1c7f6436ef.js +1 -0
  105. package/mcp-server/.next/static/chunks/8364b81221aabb64.js +1 -0
  106. package/mcp-server/.next/static/chunks/a42830291751fe48.js +1 -0
  107. package/mcp-server/.next/static/chunks/bb2e6ac36794beea.css +1 -0
  108. package/mcp-server/.next/static/chunks/c578923146856601.js +1 -0
  109. package/mcp-server/.next/static/chunks/turbopack-22a9e2c19fd12b04.js +3 -0
  110. package/mcp-server/app/api/orchestrator/route.ts +73 -0
  111. package/mcp-server/app/globals.css +0 -1
  112. package/mcp-server/app/logs/LogsClient.infinite-loop.test.tsx +1 -1
  113. package/mcp-server/app/mcp/client-manager.ts +281 -0
  114. package/mcp-server/app/mcp/route.ts +166 -0
  115. package/mcp-server/app/mcp/tools.ts +71 -18
  116. package/mcp-server/app/page.tsx +79 -65
  117. package/mcp-server/package.json +7 -9
  118. package/package.json +3 -8
  119. package/src/tui-interface-impl.tsx +1 -1
  120. package/mcp-server/.next/server/chunks/[root-of-the-server]__00592d3f._.js +0 -91
  121. package/mcp-server/.next/server/chunks/[root-of-the-server]__00592d3f._.js.map +0 -1
  122. package/mcp-server/.next/server/chunks/[root-of-the-server]__177c72c6._.js +0 -17
  123. package/mcp-server/.next/server/chunks/[root-of-the-server]__177c72c6._.js.map +0 -1
  124. package/mcp-server/.next/server/chunks/[root-of-the-server]__2942c072._.js +0 -3
  125. package/mcp-server/.next/server/chunks/[root-of-the-server]__e6dcd8bf._.js +0 -3
  126. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__22d96eb1._.js +0 -3
  127. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__22d96eb1._.js.map +0 -1
  128. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__63dd19ce._.js +0 -3
  129. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__63dd19ce._.js.map +0 -1
  130. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__94d8e211._.js +0 -3
  131. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__94d8e211._.js.map +0 -1
  132. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__bf771f7e._.js +0 -3
  133. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__c4e78a20._.js +0 -3
  134. package/mcp-server/.next/server/chunks/ssr/[root-of-the-server]__cf17e26a._.js.map +0 -1
  135. package/mcp-server/.next/server/chunks/ssr/_28084f73._.js +0 -3
  136. package/mcp-server/.next/server/chunks/ssr/_28084f73._.js.map +0 -1
  137. package/mcp-server/.next/server/chunks/ssr/_96ba1d8e._.js.map +0 -1
  138. package/mcp-server/.next/server/chunks/ssr/_aa32a659._.js +0 -3
  139. package/mcp-server/.next/server/chunks/ssr/_aa32a659._.js.map +0 -1
  140. package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_b2368b47._.js +0 -3
  141. package/mcp-server/.next/server/chunks/ssr/node_modules__pnpm_b2368b47._.js.map +0 -1
  142. package/mcp-server/.next/static/chunks/0cf268b98f36b0b5.css +0 -1
  143. package/mcp-server/.next/static/chunks/571fbcba7786c174.js +0 -1
  144. package/mcp-server/.next/static/chunks/5fdb882fe407a798.js +0 -1
  145. package/mcp-server/.next/static/chunks/98e04adc8a00688a.js +0 -1
  146. package/mcp-server/.next/static/chunks/db42008ac1e97815.js +0 -1
  147. package/mcp-server/.next/static/chunks/dbbdc51e76f4cea4.js +0 -1
  148. package/mcp-server/.next/static/chunks/turbopack-7112e6b6aee1d84f.js +0 -3
  149. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__2942c072._.js.map → [externals]_node:crypto_c20cce38._.js.map} +0 -0
  150. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__6ee9a99f._.js.map → [root-of-the-server]__3130ceec._.js.map} +0 -0
  151. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__e6dcd8bf._.js.map → [root-of-the-server]__4637d3dd._.js.map} +0 -0
  152. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__bc773251._.js.map → [root-of-the-server]__5114419d._.js.map} +0 -0
  153. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__55c04517._.js.map → [root-of-the-server]__a004a307._.js.map} +0 -0
  154. /package/mcp-server/.next/static/{xBA_-Bho-qV9WoLREZ2m_ → 5ZNPgBUtuG6HhHaBXqChL}/_buildManifest.js +0 -0
  155. /package/mcp-server/.next/static/{xBA_-Bho-qV9WoLREZ2m_ → 5ZNPgBUtuG6HhHaBXqChL}/_clientMiddlewareManifest.json +0 -0
  156. /package/mcp-server/.next/static/{xBA_-Bho-qV9WoLREZ2m_ → 5ZNPgBUtuG6HhHaBXqChL}/_ssgManifest.js +0 -0
@@ -1,9 +1,175 @@
1
+ import { readFileSync } from "node:fs"
2
+ import { homedir } from "node:os"
3
+ import { join } from "node:path"
1
4
  import { createMcpHandler } from "mcp-handler"
2
5
  import { z } from "zod"
6
+ import { getMCPClientManager } from "./client-manager"
3
7
  import { executeBrowserAction, findComponentSource, fixMyApp, TOOL_DESCRIPTIONS } from "./tools"
4
8
 
9
+ // Detect available package runner (npx, pnpm dlx, or fail)
10
+ const getPackageRunner = (): { command: string; args: string[] } | null => {
11
+ try {
12
+ const { execSync } = require("node:child_process")
13
+
14
+ // Try npx first (most common)
15
+ try {
16
+ execSync("npx --version", { stdio: "ignore" })
17
+ return { command: "npx", args: ["-y"] }
18
+ } catch {
19
+ // npx not available or is aliased to pnpm dlx
20
+ }
21
+
22
+ // Try pnpm dlx as fallback
23
+ try {
24
+ execSync("pnpm --version", { stdio: "ignore" })
25
+ return { command: "pnpm", args: ["dlx"] }
26
+ } catch {
27
+ // pnpm not available
28
+ }
29
+
30
+ console.error("[MCP Orchestrator] Neither npx nor pnpm found - cannot spawn chrome-devtools MCP")
31
+ return null
32
+ } catch (error) {
33
+ console.error("[MCP Orchestrator] Failed to detect package runner:", error)
34
+ return null
35
+ }
36
+ }
37
+
38
+ // Initialize MCP client manager for orchestration
39
+ // This will connect to chrome-devtools and nextjs-dev MCPs when available
40
+ const initializeOrchestration = async () => {
41
+ const clientManager = getMCPClientManager()
42
+
43
+ // Helper to get config from session files in ~/.d3k/
44
+ const getConfigFromSessions = () => {
45
+ const config: Parameters<typeof clientManager.initialize>[0] = {}
46
+ const sessionDir = join(homedir(), ".d3k")
47
+
48
+ try {
49
+ // Read all *.json session files
50
+ const { readdirSync, existsSync } = require("node:fs")
51
+ if (!existsSync(sessionDir)) return config
52
+
53
+ const sessionFiles = readdirSync(sessionDir).filter((f: string) => f.endsWith(".json"))
54
+
55
+ // Use the most recent session with CDP URL
56
+ for (const file of sessionFiles) {
57
+ try {
58
+ const sessionPath = join(sessionDir, file)
59
+ const sessionData = JSON.parse(readFileSync(sessionPath, "utf-8"))
60
+
61
+ // Configure chrome-devtools MCP if CDP URL is available
62
+ if (sessionData.cdpUrl && !config.chromeDevtools) {
63
+ const cdpUrl = sessionData.cdpUrl.replace("ws://", "http://") // Convert ws:// to http:// for browser URL
64
+ const runner = getPackageRunner()
65
+
66
+ if (runner) {
67
+ config.chromeDevtools = {
68
+ command: runner.command,
69
+ args: [...runner.args, "chrome-devtools-mcp@latest", "--browserUrl", cdpUrl],
70
+ enabled: true
71
+ }
72
+ } else {
73
+ console.warn("[MCP Orchestrator] Cannot configure chrome-devtools MCP: no package runner available")
74
+ }
75
+ }
76
+
77
+ // Configure nextjs-dev MCP if app port is available
78
+ if (sessionData.appPort && !config.nextjsDev) {
79
+ config.nextjsDev = {
80
+ url: `http://localhost:${sessionData.appPort}/_next/mcp`,
81
+ enabled: true
82
+ }
83
+ }
84
+
85
+ // Break if we have both MCPs
86
+ if (config.chromeDevtools && config.nextjsDev) break
87
+ } catch {
88
+ // Skip invalid session files
89
+ }
90
+ }
91
+ } catch (error) {
92
+ console.warn("[MCP Orchestrator] Failed to read session files:", error)
93
+ }
94
+
95
+ return config
96
+ }
97
+
98
+ try {
99
+ // Initial attempt to connect
100
+ const config = getConfigFromSessions()
101
+ if (Object.keys(config).length > 0) {
102
+ await clientManager.initialize(config)
103
+ console.log(`[MCP Orchestrator] Initialized with ${Object.keys(config).join(", ")}`)
104
+ } else {
105
+ console.log("[MCP Orchestrator] No downstream MCPs found yet (will retry)")
106
+ }
107
+
108
+ // Since MCP server starts before Chrome, periodically retry connection
109
+ // This allows late-binding to chrome-devtools MCP after Chrome launches
110
+ let retryCount = 0
111
+ const maxRetries = 10
112
+ const retryInterval = setInterval(async () => {
113
+ retryCount++
114
+
115
+ const newConfig = getConfigFromSessions()
116
+ const hasChromeDevtools = !!newConfig.chromeDevtools
117
+ const hasNextjs = !!newConfig.nextjsDev
118
+ const alreadyConnectedChrome = clientManager.isConnected("chrome-devtools")
119
+ const alreadyConnectedNextjs = clientManager.isConnected("nextjs-dev")
120
+
121
+ // Check if we have new MCPs to connect to
122
+ const needsChrome = hasChromeDevtools && !alreadyConnectedChrome
123
+ const needsNextjs = hasNextjs && !alreadyConnectedNextjs
124
+
125
+ if (needsChrome || needsNextjs) {
126
+ const toConnect = [needsChrome && "chrome-devtools", needsNextjs && "nextjs-dev"].filter(Boolean)
127
+ console.log(`[MCP Orchestrator] Retry ${retryCount}: Attempting to connect to ${toConnect.join(", ")}`)
128
+ try {
129
+ await clientManager.initialize(newConfig)
130
+ console.log("[MCP Orchestrator] Successfully connected to downstream MCPs")
131
+ } catch (error) {
132
+ console.warn(`[MCP Orchestrator] Retry ${retryCount} failed:`, error)
133
+ }
134
+ }
135
+
136
+ // Stop retrying after max attempts or when both are connected
137
+ if (retryCount >= maxRetries || (alreadyConnectedChrome && alreadyConnectedNextjs)) {
138
+ clearInterval(retryInterval)
139
+ const connected = clientManager.getConnectedMCPs()
140
+ console.log(`[MCP Orchestrator] Stopped retry loop (connected: ${connected.join(", ") || "none"})`)
141
+ }
142
+ }, 2000) // Retry every 2 seconds
143
+ } catch (error) {
144
+ console.warn("[MCP Orchestrator] Failed to initialize downstream MCPs:", error)
145
+ }
146
+ }
147
+
148
+ // Initialize on module load
149
+ initializeOrchestration().catch(console.error)
150
+
5
151
  const handler = createMcpHandler(
6
152
  (server) => {
153
+ // Dynamically register proxied tools from downstream MCPs
154
+ const clientManager = getMCPClientManager()
155
+ const downstreamTools = clientManager.getAllTools()
156
+
157
+ for (const { mcpName, tool } of downstreamTools) {
158
+ // Add prefix to avoid conflicts with dev3000's own tools
159
+ const proxiedToolName = `${mcpName}_${tool.name}`
160
+
161
+ server.tool(
162
+ proxiedToolName,
163
+ `[${mcpName}] ${tool.description || ""}`,
164
+ tool.inputSchema as any,
165
+ async (params: Record<string, unknown>) => {
166
+ // Proxy the call to the downstream MCP
167
+ return clientManager.callTool(mcpName, tool.name, params)
168
+ }
169
+ )
170
+ }
171
+
172
+ // Dev3000's own tools below:
7
173
  // Enhanced fix_my_app - the ultimate error fixing tool
8
174
  server.tool(
9
175
  "fix_my_app",
@@ -181,7 +181,27 @@ export async function fixMyApp({
181
181
  integrateChromeDevtools = false,
182
182
  returnRawData = false
183
183
  }: FixMyAppParams): Promise<{ content: Array<{ type: "text"; text: string }> }> {
184
- // 🎯 INTELLIGENT DELEGATION: Check if nextjs-dev MCP is available for Next.js-specific analysis
184
+ // 🎯 MCP ORCHESTRATION: Check which downstream MCPs are available
185
+ const { getMCPClientManager } = await import("./client-manager")
186
+ const clientManager = getMCPClientManager()
187
+ const connectedMCPs = clientManager.getConnectedMCPs()
188
+
189
+ const hasNextjsDev = connectedMCPs.includes("nextjs-dev")
190
+ const hasChromeDevtools = connectedMCPs.includes("chrome-devtools")
191
+
192
+ if (connectedMCPs.length > 0) {
193
+ logToDevFile(`Fix My App: Connected to downstream MCPs: ${connectedMCPs.join(", ")}`)
194
+ }
195
+
196
+ // Auto-detect integration flags based on connected MCPs
197
+ if (hasNextjsDev && integrateNextjs === false) {
198
+ integrateNextjs = true
199
+ }
200
+ if (hasChromeDevtools && integrateChromeDevtools === false) {
201
+ integrateChromeDevtools = true
202
+ }
203
+
204
+ // Legacy delegation check (keeping for backwards compatibility)
185
205
  const canDelegateNextjs = await canDelegateToNextjs()
186
206
  if (canDelegateNextjs) {
187
207
  logToDevFile(`Fix My App: Recommending dev3000-nextjs-dev MCP for Next.js-specific analysis`)
@@ -619,6 +639,9 @@ export async function fixMyApp({
619
639
  results.push(`• ${match[1]}`)
620
640
  }
621
641
  })
642
+ results.push("")
643
+ results.push("💡 **TIP**: Use analyze_visual_diff tool to compare screenshots and identify changes")
644
+ results.push(" (Advanced: screenshots are also accessible via curl if needed)")
622
645
  }
623
646
 
624
647
  // Jank/Layout Shift Detection (from ScreencastManager passive captures)
@@ -711,6 +734,11 @@ export async function fixMyApp({
711
734
  results.push("• Check for web fonts causing text reflow (font-display: swap)")
712
735
  results.push(`• Raw screenshots: ${jankResult.screenshotDir}`)
713
736
  results.push("")
737
+ results.push("📸 **ANALYZING SCREENSHOTS:**")
738
+ results.push("• RECOMMENDED: Use analyze_visual_diff tool with before/after URLs (shown above)")
739
+ results.push("• The tool provides structured instructions for comparing frames")
740
+ results.push("• Advanced: Screenshots are also accessible via curl if needed")
741
+ results.push("")
714
742
  results.push(`🎬 **IMPORTANT**: Share this frame sequence link with the user: ${videoUrl}`)
715
743
  }
716
744
  }
@@ -2148,7 +2176,8 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
2148
2176
  if (existsSync(metadataPath)) {
2149
2177
  try {
2150
2178
  const metadata = JSON.parse(readFileSync(metadataPath, "utf-8"))
2151
- if (metadata.layoutShifts && metadata.layoutShifts.length > 0) {
2179
+ // Set realCLSData even if there are zero shifts - this tells us Chrome ran and found nothing
2180
+ if (metadata.layoutShifts !== undefined) {
2152
2181
  realCLSData = {
2153
2182
  score: metadata.totalCLS || 0,
2154
2183
  grade: metadata.clsGrade || "unknown",
@@ -2182,22 +2211,33 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
2182
2211
 
2183
2212
  // Look for CLS entries with Before/After URLs
2184
2213
  // Format: [BROWSER] [CDP] CLS #N (score: X, time: Yms):
2185
- // [BROWSER] [CDP] - <ELEMENT> shifted...
2214
+ // [BROWSER] [CDP] - <ELEMENT> shifted... (variable number of these)
2186
2215
  // [BROWSER] [CDP] Before: http://...
2187
2216
  // [BROWSER] [CDP] After: http://...
2188
2217
  for (let i = 0; i < lines.length; i++) {
2189
2218
  const clsMatch = lines[i].match(/\[CDP\] CLS #\d+ \(score: [\d.]+, time: (\d+)ms\):/)
2190
2219
  if (clsMatch) {
2191
2220
  const timestamp = parseInt(clsMatch[1], 10)
2192
- // Look ahead for Before and After URLs (skip the shift description line)
2193
- if (i + 3 < lines.length) {
2194
- const beforeMatch = lines[i + 2].match(/Before:\s+(http:\/\/\S+)/)
2195
- const afterMatch = lines[i + 3].match(/After:\s+(http:\/\/\S+)/)
2196
- if (beforeMatch && afterMatch) {
2221
+ // Look ahead for Before and After URLs (scan next 10 lines for them)
2222
+ let beforeUrl: string | null = null
2223
+ let afterUrl: string | null = null
2224
+
2225
+ for (let j = i + 1; j < Math.min(i + 10, lines.length); j++) {
2226
+ if (!beforeUrl) {
2227
+ const beforeMatch = lines[j].match(/Before:\s+(http:\/\/\S+)/)
2228
+ if (beforeMatch) beforeUrl = beforeMatch[1]
2229
+ }
2230
+ if (!afterUrl) {
2231
+ const afterMatch = lines[j].match(/After:\s+(http:\/\/\S+)/)
2232
+ if (afterMatch) afterUrl = afterMatch[1]
2233
+ }
2234
+ // Stop if we found both
2235
+ if (beforeUrl && afterUrl) {
2197
2236
  frameUrlMap.set(timestamp, {
2198
- before: beforeMatch[1],
2199
- after: afterMatch[1]
2237
+ before: beforeUrl,
2238
+ after: afterUrl
2200
2239
  })
2240
+ break
2201
2241
  }
2202
2242
  }
2203
2243
  }
@@ -2207,8 +2247,21 @@ async function detectJankFromScreenshots(_projectName?: string): Promise<{
2207
2247
  // Ignore log parsing errors
2208
2248
  }
2209
2249
 
2210
- // If we have real CLS data, use it to flag visual severity
2211
- if (realCLSData && realCLSData.shifts.length > 0) {
2250
+ // If we have real CLS data from Chrome's PerformanceObserver, trust it completely
2251
+ if (realCLSData) {
2252
+ // If Chrome says there are no shifts, return immediately - don't fall back to pixel diff
2253
+ if (realCLSData.shifts.length === 0) {
2254
+ return {
2255
+ detections: [],
2256
+ sessionId: latestSessionId,
2257
+ totalFrames: sessionFiles.length,
2258
+ screenshotDir,
2259
+ realCLS: { score: 0, grade: realCLSData.grade }
2260
+ }
2261
+ }
2262
+
2263
+ // Process actual layout shifts detected by Chrome
2264
+ // Trust Chrome's Layout Instability API completely - if Chrome reports it, it's real
2212
2265
  realCLSData.shifts.forEach((shift) => {
2213
2266
  const element = shift.sources?.[0]?.node || "unknown"
2214
2267
  const isCriticalElement = ["NAV", "HEADER", "BUTTON", "A"].includes(element.toUpperCase())
@@ -2742,11 +2795,13 @@ export async function analyzeVisualDiff(params: {
2742
2795
  results.push("")
2743
2796
  results.push("To analyze the visual differences between these two screenshots:")
2744
2797
  results.push("")
2745
- results.push("**Step 1: Load the BEFORE image**")
2746
- results.push(`Use the Read tool to load: \`${beforeImageUrl}\``)
2798
+ results.push("**Step 1: Fetch and analyze the BEFORE image**")
2799
+ results.push(`Use WebFetch with URL: \`${beforeImageUrl}\``)
2800
+ results.push(`Prompt: "Describe this screenshot in detail, focusing on layout and visible elements"`)
2747
2801
  results.push("")
2748
- results.push("**Step 2: Load the AFTER image**")
2749
- results.push(`Use the Read tool to load: \`${afterImageUrl}\``)
2802
+ results.push("**Step 2: Fetch and analyze the AFTER image**")
2803
+ results.push(`Use WebFetch with URL: \`${afterImageUrl}\``)
2804
+ results.push(`Prompt: "Describe this screenshot in detail, focusing on layout and visible elements"`)
2750
2805
  results.push("")
2751
2806
  results.push("**Step 3: Compare and describe the differences**")
2752
2807
 
@@ -2767,8 +2822,6 @@ export async function analyzeVisualDiff(params: {
2767
2822
  results.push("• Which element(s) changed")
2768
2823
  results.push("• What appeared/moved/resized")
2769
2824
  results.push("• Why this caused other elements to shift")
2770
- results.push("")
2771
- results.push("💡 **TIP:** Load both images first, then describe the differences in detail.")
2772
2825
 
2773
2826
  return {
2774
2827
  content: [{ type: "text", text: results.join("\n") }]
@@ -3,6 +3,7 @@
3
3
  import Link from "next/link"
4
4
  import { useEffect, useState } from "react"
5
5
  import { DarkModeToggle } from "@/components/dark-mode-toggle"
6
+ import { ChromeIcon, NextJsIcon } from "@/components/mcp-icons"
6
7
  import { useDarkMode } from "@/hooks/use-dark-mode"
7
8
 
8
9
  interface MCPTool {
@@ -24,6 +25,22 @@ interface ToolsResponse {
24
25
  categories: string[]
25
26
  }
26
27
 
28
+ interface OrchestratorResponse {
29
+ orchestratorEnabled: boolean
30
+ connectedMCPs: string[]
31
+ totalConnections: number
32
+ mcpDetails: Array<{
33
+ name: string
34
+ connected: boolean
35
+ toolCount: number
36
+ tools: string[]
37
+ projects: string[]
38
+ }>
39
+ totalProjects: number
40
+ projects: string[]
41
+ message: string
42
+ }
43
+
27
44
  // Format tool descriptions by parsing markdown-style sections
28
45
  function formatToolDescription(description: string) {
29
46
  const slugify = (value: string) =>
@@ -46,18 +63,19 @@ function formatToolDescription(description: string) {
46
63
  const normalizedKey = getUniqueKey(slugify(rawSection) || "section")
47
64
  let section = rawSection
48
65
 
66
+ // Remove markdown formatting
67
+ section = section.replace(/\*\*/g, "").trim()
68
+
49
69
  // Remove excessive emojis
50
- // Simple emoji reduction - just remove most emojis except the first in sequences
51
70
  section = section.replace(/(\u{1F300}-\u{1F9FF}|\u{2600}-\u{26FF}|\u{1F900}-\u{1F9FF})+/gu, (match) => {
52
- // Keep only the first emoji in a sequence
53
71
  return match.charAt(0)
54
72
  })
55
73
 
56
74
  // Handle different section types
57
- if (section.includes("**") && section.includes(":")) {
75
+ if (section.includes(":")) {
58
76
  // This is a header with content
59
77
  const [header, ...contentParts] = section.split(":")
60
- const cleanHeader = header.replace(/\*\*/g, "").trim()
78
+ const cleanHeader = header.trim()
61
79
  const content = contentParts.join(":").trim()
62
80
 
63
81
  // Check if content has bullet points
@@ -96,7 +114,6 @@ function formatToolDescription(description: string) {
96
114
  return (
97
115
  <ol className="space-y-1 ml-4" key={`${normalizedKey}-steps`}>
98
116
  {items.map((item, itemIdx) => {
99
- // Clean up the item text
100
117
  const cleanItem = item
101
118
  .replace(/^\d+[\ud83c-\ud83e][\udc00-\udfff]\s*/, "")
102
119
  .replace(/^\d+\.\s*/, "")
@@ -125,14 +142,17 @@ function formatToolDescription(description: string) {
125
142
 
126
143
  export default function HomePage() {
127
144
  const [tools, setTools] = useState<ToolsResponse | null>(null)
145
+ const [orchestrator, setOrchestrator] = useState<OrchestratorResponse | null>(null)
128
146
  const [loading, setLoading] = useState(true)
129
147
  const [darkMode, setDarkMode] = useDarkMode()
130
148
 
131
149
  useEffect(() => {
132
- fetch("/api/tools")
133
- .then((res) => res.json())
134
- .then((data) => {
135
- setTools(data)
150
+ // Fetch both tools and orchestrator status in parallel
151
+ Promise.all([fetch("/api/tools"), fetch("/api/orchestrator")])
152
+ .then(([toolsRes, orchRes]) => Promise.all([toolsRes.json(), orchRes.json()]))
153
+ .then(([toolsData, orchData]) => {
154
+ setTools(toolsData)
155
+ setOrchestrator(orchData)
136
156
  setLoading(false)
137
157
  })
138
158
  .catch(() => {
@@ -165,12 +185,6 @@ export default function HomePage() {
165
185
  </div>
166
186
  </div>
167
187
  <div className="flex items-center gap-4">
168
- <Link
169
- href="/logs"
170
- className="inline-flex items-center gap-2 px-5 py-3 bg-primary text-primary-foreground text-sm font-medium rounded hover:bg-primary/90 transition-colors"
171
- >
172
- 📊 View Logs
173
- </Link>
174
188
  <a
175
189
  href="https://github.com/vercel-labs/dev3000#setup"
176
190
  target="_blank"
@@ -187,56 +201,59 @@ export default function HomePage() {
187
201
 
188
202
  {/* Main Content - no sidebar needed with only 2 tools */}
189
203
  <main className="max-w-7xl mx-auto px-6 py-8">
190
- {/* Quick Start */}
191
- <section className="mb-16">
192
- <div className="bg-primary/10 border border-primary/20 rounded p-8">
193
- <h2 className="text-xl font-semibold mb-4">🚀 Quick Start</h2>
194
- <div className="space-y-4">
195
- <div>
196
- <span className="text-sm font-medium">MCP Endpoint:</span>
197
- <code className="ml-3 px-4 py-2 bg-primary/20 text-foreground text-sm font-mono rounded">
198
- {tools?.endpoint || "http://localhost:3684/mcp"}
199
- </code>
200
- </div>
201
- <div className="text-sm">
202
- <p className="mb-3">Connect your AI tools to this MCP server for real-time development debugging:</p>
203
- <div className="flex gap-6">
204
- <a
205
- href="https://github.com/vercel-labs/dev3000#claude-desktop"
206
- target="_blank"
207
- rel="noopener noreferrer"
208
- className="text-primary hover:text-primary/80 font-medium"
209
- >
210
- Claude Desktop Setup
211
- </a>
212
- <a
213
- href="https://github.com/vercel-labs/dev3000#cursor"
214
- target="_blank"
215
- rel="noopener noreferrer"
216
- className="text-primary hover:text-primary/80 font-medium"
217
- >
218
- Cursor Setup
219
- </a>
204
+ {/* MCP Connections Status */}
205
+ {!loading && orchestrator && (
206
+ <section className="mb-16">
207
+ <div className="bg-accent/10 border border-accent/20 rounded p-8">
208
+ <h2 className="text-xl font-semibold mb-4">🔌 MCP Connections</h2>
209
+ <p className="text-sm text-muted-foreground mb-4">{orchestrator.message}</p>
210
+
211
+ {orchestrator.totalConnections > 0 ? (
212
+ <>
213
+ <div className="flex flex-wrap gap-3 mb-4">
214
+ {orchestrator.mcpDetails.map((mcp) => {
215
+ const Icon =
216
+ mcp.name === "chrome-devtools" ? ChromeIcon : mcp.name === "nextjs-dev" ? NextJsIcon : null
217
+ return (
218
+ <div
219
+ key={mcp.name}
220
+ className="inline-flex items-center gap-2 bg-background/50 border border-border rounded px-3 py-2"
221
+ >
222
+ {Icon && <Icon className="shrink-0" />}
223
+ <span className="font-semibold font-mono text-sm">{mcp.name}</span>
224
+ <span className="w-2 h-2 bg-green-500 rounded-full"></span>
225
+ </div>
226
+ )
227
+ })}
228
+ </div>
229
+ {orchestrator.totalProjects > 0 && (
230
+ <div className="text-sm text-muted-foreground">
231
+ <span className="font-medium">Active projects:</span>{" "}
232
+ {orchestrator.projects.map((project, idx) => (
233
+ <span key={project}>
234
+ <code className="text-foreground bg-background/50 px-1.5 py-0.5 rounded">{project}</code>
235
+ {idx < orchestrator.projects.length - 1 && ", "}
236
+ </span>
237
+ ))}
238
+ </div>
239
+ )}
240
+ </>
241
+ ) : (
242
+ <div className="text-sm text-muted-foreground bg-background/50 border border-border rounded p-4">
243
+ <p className="mb-2">⏳ Waiting for downstream MCPs to become available...</p>
244
+ <p className="text-xs">
245
+ dev3000 will automatically connect to <code className="text-foreground">chrome-devtools</code> and{" "}
246
+ <code className="text-foreground">nextjs-dev</code> MCPs when Chrome and your dev server start.
247
+ </p>
220
248
  </div>
221
- </div>
249
+ )}
222
250
  </div>
223
- </div>
224
- </section>
251
+ </section>
252
+ )}
225
253
 
226
254
  {/* Tools Documentation */}
227
- <section>
228
- <div className="mb-12">
229
- <div>
230
- <h2 className="text-3xl font-bold mb-3">Available Tools</h2>
231
- <p className="text-muted-foreground text-lg">
232
- {loading
233
- ? "Loading MCP tools..."
234
- : `${tools?.totalTools || 0} tools across ${
235
- tools?.categories.length || 0
236
- } categories for AI-powered development debugging`}
237
- </p>
238
- </div>
239
- </div>
255
+ <section className="bg-accent/10 border border-accent/20 rounded p-8">
256
+ <h2 className="text-xl font-semibold mb-4">🛠️ dev3000 Tools</h2>
240
257
 
241
258
  {loading ? (
242
259
  <div className="text-center py-16">
@@ -249,7 +266,7 @@ export default function HomePage() {
249
266
  <div
250
267
  key={tool.name}
251
268
  id={tool.name}
252
- className="border border-border rounded-lg p-6 hover:border-muted-foreground/50 transition-colors"
269
+ className="bg-background/50 border border-border rounded-lg p-6 hover:border-muted-foreground/50 transition-colors"
253
270
  >
254
271
  <div className="mb-4">
255
272
  <h4 className="text-xl font-semibold font-mono mb-3">{tool.name}</h4>
@@ -352,9 +369,6 @@ export default function HomePage() {
352
369
  >
353
370
  Homepage
354
371
  </a>
355
- <Link href="/logs" className="hover:text-gray-900 transition-colors">
356
- Logs
357
- </Link>
358
372
  </div>
359
373
  </div>
360
374
  </div>
@@ -11,11 +11,10 @@
11
11
  "tools": "tsx tools-cli.ts"
12
12
  },
13
13
  "dependencies": {
14
- "@modelcontextprotocol/sdk": "^1.11.0",
14
+ "@modelcontextprotocol/sdk": "catalog:",
15
15
  "@radix-ui/react-slot": "^1.1.1",
16
16
  "@types/pixelmatch": "^5.2.6",
17
17
  "@types/pngjs": "^6.0.5",
18
- "autoprefixer": "^10.4.20",
19
18
  "class-variance-authority": "^0.7.1",
20
19
  "clsx": "^2.1.1",
21
20
  "commander": "^14.0.1",
@@ -31,15 +30,14 @@
31
30
  "zod": "^3.22.4"
32
31
  },
33
32
  "devDependencies": {
34
- "@tailwindcss/postcss": "^4.1.9",
33
+ "@tailwindcss/postcss": "catalog:",
35
34
  "@types/node": "^24.5.2",
36
35
  "@types/react": "19.1.13",
37
- "@types/react-dom": "19.1.9",
38
- "@types/ws": "^8.5.12",
36
+ "autoprefixer": "catalog:",
39
37
  "babel-plugin-react-compiler": "^19.1.0-rc.3",
40
- "postcss": "^8.5",
41
- "tailwindcss": "^4.1.9",
42
- "tw-animate-css": "^1.3.8",
43
- "typescript": "^5.0.0"
38
+ "postcss": "catalog:",
39
+ "tailwindcss": "catalog:",
40
+ "typescript": "^5.0.0",
41
+ "vitest": "^3.2.4"
44
42
  }
45
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev3000",
3
- "version": "0.0.84",
3
+ "version": "0.0.85",
4
4
  "description": "AI-powered development tools with browser monitoring and MCP server integration",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -59,29 +59,23 @@
59
59
  "claude"
60
60
  ],
61
61
  "dependencies": {
62
- "@antfu/ni": "^26.0.1",
63
62
  "@biomejs/biome": "^2.2.4",
64
63
  "chalk": "^5.3.0",
65
- "cli-progress": "^3.12.0",
66
64
  "commander": "^14.0.1",
67
65
  "ink": "^6.3.1",
68
- "mcp-handler": "^1.0.2",
69
66
  "next": "15.5.1-canary.30",
70
67
  "ora": "^9.0.0",
71
68
  "package-manager-detector": "^1.3.0",
72
69
  "react": "^19.1.1",
73
70
  "react-dom": "^19.1.1",
74
71
  "ws": "^8.18.3",
75
- "zod": "^3.22.4",
76
72
  "typescript": "^5.0.0"
77
73
  },
78
74
  "devDependencies": {
79
- "@types/cli-progress": "^3.11.6",
80
75
  "@types/node": "^24.5.2",
81
76
  "@types/react": "^19.0.0",
82
77
  "@types/ws": "^8.5.12",
83
78
  "husky": "^9.0.0",
84
- "js-yaml": "^4.1.0",
85
79
  "tsx": "^4.20.3",
86
80
  "vitest": "^3.2.4"
87
81
  },
@@ -99,5 +93,6 @@
99
93
  "url": "git+https://github.com/vercel-labs/dev3000.git"
100
94
  },
101
95
  "author": "Lindsey Simon <lindsey@vercel.com>",
102
- "license": "MIT"
96
+ "license": "MIT",
97
+ "packageManager": "pnpm@10.15.0+sha512.486ebc259d3e999a4e8691ce03b5cac4a71cbeca39372a9b762cb500cfdf0873e2cb16abe3d951b1ee2cf012503f027b98b6584e4df22524e0c7450d9ec7aa7b"
103
98
  }
@@ -307,7 +307,7 @@ const TUIApp = ({
307
307
  {/* Info on the right */}
308
308
  <Box flexDirection="column" flexGrow={1}>
309
309
  <Text color="cyan">🌐 App: http://localhost:{appPort}</Text>
310
- <Text color="cyan">🤖 MCP: http://localhost:{mcpPort}/mcp</Text>
310
+ <Text color="cyan">🤖 MCP: http://localhost:{mcpPort}</Text>
311
311
  <Text color="cyan">
312
312
  📸 Logs: http://localhost:{mcpPort}/logs
313
313
  {projectName ? `?project=${encodeURIComponent(projectName)}` : ""}