dev3000 0.0.101 → 0.0.103
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.
- package/README.md +2 -0
- package/dist/dev-environment.d.ts +1 -0
- package/dist/dev-environment.d.ts.map +1 -1
- package/dist/dev-environment.js +52 -8
- package/dist/dev-environment.js.map +1 -1
- package/dist/screencast-manager.d.ts.map +1 -1
- package/dist/screencast-manager.js +3 -4
- package/dist/screencast-manager.js.map +1 -1
- package/dist/src/tui-interface-impl.tsx +7 -1
- package/dist/tui-interface-impl.d.ts.map +1 -1
- package/dist/tui-interface-impl.js +4 -1
- package/dist/tui-interface-impl.js.map +1 -1
- package/mcp-server/.next/BUILD_ID +1 -1
- package/mcp-server/.next/build-manifest.json +2 -2
- package/mcp-server/.next/fallback-build-manifest.json +2 -2
- package/mcp-server/.next/prerender-manifest.json +3 -3
- package/mcp-server/.next/server/app/_global-error.html +2 -2
- package/mcp-server/.next/server/app/_global-error.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.html +1 -1
- package/mcp-server/.next/server/app/_not-found.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.html +1 -1
- package/mcp-server/.next/server/app/index.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__0691849a._.js +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__0691849a._.js.map +1 -1
- package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js +78 -15
- package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js.map +1 -1
- package/mcp-server/.next/server/server-reference-manifest.js +1 -1
- package/mcp-server/.next/server/server-reference-manifest.json +1 -1
- package/mcp-server/app/mcp/route.ts +28 -2
- package/mcp-server/app/mcp/tools.ts +486 -3
- package/package.json +2 -1
- package/src/tui-interface-impl.tsx +7 -1
- /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → I_0vbzGzd9Y9Tse-yfGKd}/_buildManifest.js +0 -0
- /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → I_0vbzGzd9Y9Tse-yfGKd}/_clientMiddlewareManifest.json +0 -0
- /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → I_0vbzGzd9Y9Tse-yfGKd}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"
|
|
1
|
+
self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"5LNEsGYjtwWobrqfCSeqJHwMBqVNoQL+Ca6PZ7kLWsc=\"\n}"
|
|
@@ -5,7 +5,14 @@ import type { Tool } from "@modelcontextprotocol/sdk/types.js"
|
|
|
5
5
|
import { createMcpHandler } from "mcp-handler"
|
|
6
6
|
import { z } from "zod"
|
|
7
7
|
import { getMCPClientManager } from "./client-manager"
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
crawlApp,
|
|
10
|
+
executeBrowserAction,
|
|
11
|
+
findComponentSource,
|
|
12
|
+
fixMyApp,
|
|
13
|
+
restartDevServer,
|
|
14
|
+
TOOL_DESCRIPTIONS
|
|
15
|
+
} from "./tools"
|
|
9
16
|
|
|
10
17
|
// Detect available package runner (bunx, npx, pnpm dlx, or fail)
|
|
11
18
|
const getPackageRunner = (): { command: string; args: string[] } | null => {
|
|
@@ -412,7 +419,8 @@ const handler = createMcpHandler(
|
|
|
412
419
|
returnRawData: z
|
|
413
420
|
.boolean()
|
|
414
421
|
.optional()
|
|
415
|
-
.describe("Return structured data for Claude orchestration instead of formatted text")
|
|
422
|
+
.describe("Return structured data for Claude orchestration instead of formatted text"),
|
|
423
|
+
createPR: z.boolean().optional().describe("Create a PR for the highest priority issue (default: false)")
|
|
416
424
|
},
|
|
417
425
|
async (params) => {
|
|
418
426
|
return fixMyApp(params)
|
|
@@ -502,6 +510,24 @@ const handler = createMcpHandler(
|
|
|
502
510
|
}
|
|
503
511
|
)
|
|
504
512
|
|
|
513
|
+
// App crawler tool
|
|
514
|
+
server.tool(
|
|
515
|
+
"crawl_app",
|
|
516
|
+
TOOL_DESCRIPTIONS.crawl_app,
|
|
517
|
+
{
|
|
518
|
+
depth: z
|
|
519
|
+
.union([z.number().int().min(1), z.literal("all")])
|
|
520
|
+
.optional()
|
|
521
|
+
.describe(
|
|
522
|
+
"Crawl depth: number (1=homepage only, 2=homepage+next level, etc.) or 'all' for exhaustive (default: 1)"
|
|
523
|
+
),
|
|
524
|
+
projectName: z.string().optional().describe("Project name (if multiple dev3000 instances are running)")
|
|
525
|
+
},
|
|
526
|
+
async (params) => {
|
|
527
|
+
return crawlApp(params)
|
|
528
|
+
}
|
|
529
|
+
)
|
|
530
|
+
|
|
505
531
|
// Tool that returns monitoring code for Claude to execute
|
|
506
532
|
// TODO: Commenting out for now - need to figure out the right approach for proactive monitoring
|
|
507
533
|
/*
|
|
@@ -12,7 +12,7 @@ const execAsync = promisify(exec)
|
|
|
12
12
|
// Tool descriptions
|
|
13
13
|
export const TOOL_DESCRIPTIONS = {
|
|
14
14
|
fix_my_app:
|
|
15
|
-
"🔧 **THE ULTIMATE FIND→FIX→VERIFY MACHINE!** This tool doesn't just find bugs - it FIXES them! Pure dev3000 magic that identifies issues,
|
|
15
|
+
"🔧 **THE ULTIMATE FIND→FIX→VERIFY MACHINE!** This tool doesn't just find bugs - it FIXES them! Pure dev3000 magic that identifies issues, prioritizes them, and creates focused PRs for the worst issue! 🪄\n\n🔥 **INSTANT FIXING SUPERPOWERS:**\n• Detects ALL error types: server crashes, browser errors, build failures, API issues, performance problems\n• **PRIORITIZES errors** using smart scoring (build > server > browser > network > warnings)\n• **Identifies the SINGLE WORST issue** that needs fixing right now\n• **Creates ONE focused PR** per run - no overwhelming multi-issue PRs!\n• Shows EXACT user interactions that triggered each error (clicks, navigation, etc.)\n• Provides EXACT fix code with file locations and line numbers\n• Verifies fixes by replaying the same interactions that caused the error!\n\n🎯 **SMART PRIORITIZATION:**\n• Build errors: 1000+ priority (blocks development)\n• Server errors: 500+ priority (affects functionality)\n• Browser errors: 300+ priority (user-facing issues)\n• Network errors: 200+ priority (intermittent issues)\n• Warnings: 100+ priority (nice to fix)\n• +Modifiers: Multiple occurrences, recency, reproducibility\n\n🚀 **ONE-PR-PER-RUN WORKFLOW:**\n1️⃣ I FIND all issues and their interactions\n2️⃣ I PRIORITIZE using smart scoring algorithm\n3️⃣ I IDENTIFY the single worst issue\n4️⃣ Set createPR=true to CREATE A FOCUSED PR for just that issue\n5️⃣ Fix that ONE issue, then run again for the next worst issue\n\n📍 **INTERACTION-BASED VERIFICATION:**\n• Every error includes the user interactions that led to it\n• Use execute_browser_action to replay these exact interactions\n• Verify your fix works by confirming the error doesn't reoccur\n• Example: Error shows '[INTERACTION] Click at (450,300)' → After fix, use execute_browser_action(action='click', params={x:450, y:300}) to verify\n\n⚡ **3 ACTION MODES:**\n• FIX NOW: 'What's broken RIGHT NOW?' → Find worst issue and optionally create PR\n• FIX REGRESSION: 'What broke during testing?' → Compare before/after and fix worst issue\n• FIX CONTINUOUSLY: 'Fix issues as they appear' → Monitor and fix proactively\n\n💡 **PERFECT FOR:** 'fix my app' or 'debug my app' or 'create pr for worst issue' requests. This tool identifies problems, ranks them by severity, and creates focused single-issue PRs - not giant multi-fix PRs!",
|
|
16
16
|
|
|
17
17
|
execute_browser_action:
|
|
18
18
|
"🌐 **INTELLIGENT BROWSER AUTOMATION** - Smart browser action routing that automatically delegates to chrome-devtools MCP when available for superior automation capabilities.\n\n🎯 **INTELLIGENT DELEGATION:**\n• Screenshots → chrome-devtools MCP (better quality, no conflicts)\n• Navigation → chrome-devtools MCP (more reliable page handling)\n• Clicks → chrome-devtools MCP (precise coordinate-based interaction)\n• JavaScript evaluation → chrome-devtools MCP (enhanced debugging)\n• Scrolling & typing → dev3000 fallback (specialized actions)\n\n⚡ **PROGRESSIVE ENHANCEMENT:**\n• Uses chrome-devtools MCP when available for best results\n• Falls back to dev3000's native implementation when chrome-devtools unavailable\n• Shares the same Chrome instance via CDP URL coordination\n• Eliminates browser conflicts between tools\n\n💡 **PERFECT FOR:** Browser automation that automatically chooses the best tool for each action, ensuring optimal results whether chrome-devtools MCP is available or not.",
|
|
@@ -24,7 +24,10 @@ export const TOOL_DESCRIPTIONS = {
|
|
|
24
24
|
"🔍 **COMPONENT SOURCE FINDER** - Maps DOM elements to their source code by extracting the React component function and finding unique patterns to search for.\n\n🎯 **HOW IT WORKS:**\n• Inspects the element via Chrome DevTools Protocol\n• Extracts the React component function source using .toString()\n• Identifies unique code patterns (specific JSX, classNames, imports)\n• Returns targeted grep patterns to find the exact source file\n\n💡 **PERFECT FOR:** Finding which file contains the code for a specific element, especially useful for CLS debugging when you need to fix layout shifts in specific components.",
|
|
25
25
|
|
|
26
26
|
restart_dev_server:
|
|
27
|
-
"🔄 **DEV SERVER RESTART** - Safely restarts the development server while preserving dev3000's monitoring, logs, and browser connection.\n\n🎯 **SMART RESTART LOGIC:**\n• First tries nextjs-dev MCP restart (if available and user has Next.js canary)\n• Falls back to dev3000's own restart mechanism:\n - Kills the old server process on the app port\n - Waits for clean shutdown\n - Spawns a new server with the same command that was originally used\n - Keeps dev3000's MCP server, browser monitoring, and screenshot capture running\n• All logging continues seamlessly - no data loss\n• Browser monitoring stays connected - no need to relaunch Chrome\n\n⚡ **WHEN TO USE:**\n• After modifying next.config.js, middleware, or environment variables\n• When you need a clean restart to clear server state\n• After significant code changes that Next.js HMR can't handle\n• When debugging persistent state or memory issues\n\n⚠️ **CRITICAL - DO NOT:**\n• ❌ NEVER manually run kill commands on the dev server like `pkill -f \"next dev\"` or `lsof -ti :3000 | xargs kill`\n• ❌ NEVER manually start the dev server with `npm run dev`, `pnpm dev`, `next dev`, etc.\n• ✅ ALWAYS use this tool for dev server restarts - it preserves all dev3000 infrastructure\n\n⚠️ **IMPORTANT:**\n• AVOID using this unnecessarily - Next.js HMR handles most changes automatically\n• Only restart when truly needed for config changes or state issues\n• The server will be offline for a few seconds during restart\n• Browser may show connection error briefly while server restarts\n\n💡 **PERFECT FOR:** 'restart the dev server', 'clean restart', 'reload the server' - but only when actually needed, not for regular code changes."
|
|
27
|
+
"🔄 **DEV SERVER RESTART** - Safely restarts the development server while preserving dev3000's monitoring, logs, and browser connection.\n\n🎯 **SMART RESTART LOGIC:**\n• First tries nextjs-dev MCP restart (if available and user has Next.js canary)\n• Falls back to dev3000's own restart mechanism:\n - Kills the old server process on the app port\n - Waits for clean shutdown\n - Spawns a new server with the same command that was originally used\n - Keeps dev3000's MCP server, browser monitoring, and screenshot capture running\n• All logging continues seamlessly - no data loss\n• Browser monitoring stays connected - no need to relaunch Chrome\n\n⚡ **WHEN TO USE:**\n• After modifying next.config.js, middleware, or environment variables\n• When you need a clean restart to clear server state\n• After significant code changes that Next.js HMR can't handle\n• When debugging persistent state or memory issues\n\n⚠️ **CRITICAL - DO NOT:**\n• ❌ NEVER manually run kill commands on the dev server like `pkill -f \"next dev\"` or `lsof -ti :3000 | xargs kill`\n• ❌ NEVER manually start the dev server with `npm run dev`, `pnpm dev`, `next dev`, etc.\n• ✅ ALWAYS use this tool for dev server restarts - it preserves all dev3000 infrastructure\n\n⚠️ **IMPORTANT:**\n• AVOID using this unnecessarily - Next.js HMR handles most changes automatically\n• Only restart when truly needed for config changes or state issues\n• The server will be offline for a few seconds during restart\n• Browser may show connection error briefly while server restarts\n\n💡 **PERFECT FOR:** 'restart the dev server', 'clean restart', 'reload the server' - but only when actually needed, not for regular code changes.",
|
|
28
|
+
|
|
29
|
+
crawl_app:
|
|
30
|
+
"🕷️ **APP CRAWLER** - Discovers all URLs in your app by crawling links starting from the homepage. Perfect for finding every page before running fixes or tests across your entire site.\n\n🎯 **SMART CRAWLING:**\n• Starts at your app's homepage (localhost)\n• Discovers all unique URLs at specified depth\n• Depth 1 = homepage links only\n• Depth 2 = homepage + links from those pages\n• Depth 'all' = exhaustive crawl until no new links found\n• Only follows same-origin links (stays within your app)\n• Deduplicates URLs automatically\n\n📊 **OUTPUT:**\n• List of all discovered URLs\n• Total count of unique pages\n• Depth reached\n• Ready to use with fix_my_app or other tools\n\n💡 **PERFECT FOR:**\n• 'crawl my app' or 'crawl my shit' - discover all pages\n• 'crawl my app and fix my shit' - find all pages then run fixes\n• Site-wide testing and debugging\n• Verifying all routes work before deployment\n\n⚡ **USAGE:**\n• Default: depth 1 (just homepage links)\n• Specify depth: 'crawl at depth 2' or depth=2\n• Full crawl: 'crawl all pages' or depth='all'"
|
|
28
31
|
}
|
|
29
32
|
|
|
30
33
|
// Types
|
|
@@ -46,6 +49,7 @@ export interface FixMyAppParams {
|
|
|
46
49
|
integrateNextjs?: boolean
|
|
47
50
|
integrateChromeDevtools?: boolean
|
|
48
51
|
returnRawData?: boolean
|
|
52
|
+
createPR?: boolean // Create a PR for the highest priority issue
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
export interface CreateIntegratedWorkflowParams {
|
|
@@ -119,6 +123,259 @@ export interface StructuredAnalysisResult {
|
|
|
119
123
|
}
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
export interface PrioritizedError {
|
|
127
|
+
error: string
|
|
128
|
+
category: "build" | "server" | "browser" | "network" | "warning"
|
|
129
|
+
severity: "critical" | "error" | "warning"
|
|
130
|
+
priorityScore: number
|
|
131
|
+
interactions: string[]
|
|
132
|
+
timestamp?: string
|
|
133
|
+
suggestedFix?: string
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Helper functions
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Calculate priority score for an error
|
|
140
|
+
* Higher score = higher priority to fix
|
|
141
|
+
*
|
|
142
|
+
* Scoring system:
|
|
143
|
+
* - Build errors: 1000+ (blocks development)
|
|
144
|
+
* - Server errors: 500+ (affects functionality)
|
|
145
|
+
* - Browser errors: 300+ (user-facing issues)
|
|
146
|
+
* - Network errors: 200+ (intermittent issues)
|
|
147
|
+
* - Warnings: 100+ (nice to fix)
|
|
148
|
+
*
|
|
149
|
+
* Additional modifiers:
|
|
150
|
+
* - Multiple occurrences: +50 per occurrence
|
|
151
|
+
* - Recent (last minute): +100
|
|
152
|
+
* - Has user interactions: +50 (reproducible)
|
|
153
|
+
*/
|
|
154
|
+
function calculateErrorPriority(
|
|
155
|
+
errorLine: string,
|
|
156
|
+
category: PrioritizedError["category"],
|
|
157
|
+
interactions: string[],
|
|
158
|
+
allErrors: string[]
|
|
159
|
+
): number {
|
|
160
|
+
let score = 0
|
|
161
|
+
|
|
162
|
+
// Base score by category
|
|
163
|
+
if (category === "build") {
|
|
164
|
+
score = 1000
|
|
165
|
+
} else if (category === "server") {
|
|
166
|
+
score = 500
|
|
167
|
+
} else if (category === "browser") {
|
|
168
|
+
score = 300
|
|
169
|
+
} else if (category === "network") {
|
|
170
|
+
score = 200
|
|
171
|
+
} else if (category === "warning") {
|
|
172
|
+
score = 100
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Severity multipliers
|
|
176
|
+
if (/CRITICAL|FATAL|crashed/i.test(errorLine)) {
|
|
177
|
+
score *= 2
|
|
178
|
+
} else if (/ERROR|Exception|FAIL/i.test(errorLine)) {
|
|
179
|
+
score *= 1.5
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Count occurrences of similar errors
|
|
183
|
+
const errorPattern = errorLine.replace(/\d+/g, "\\d+").substring(0, 100)
|
|
184
|
+
const occurrences = allErrors.filter((e) => new RegExp(errorPattern).test(e)).length
|
|
185
|
+
if (occurrences > 1) {
|
|
186
|
+
score += (occurrences - 1) * 50
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// Boost if has interactions (reproducible)
|
|
190
|
+
if (interactions.length > 0) {
|
|
191
|
+
score += 50
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Boost if recent (within last minute)
|
|
195
|
+
const timestampMatch = errorLine.match(/\[(\d{2}):(\d{2}):(\d{2})\.\d{3}\]/)
|
|
196
|
+
if (timestampMatch) {
|
|
197
|
+
const now = new Date()
|
|
198
|
+
const errorTime = new Date()
|
|
199
|
+
errorTime.setHours(parseInt(timestampMatch[1], 10))
|
|
200
|
+
errorTime.setMinutes(parseInt(timestampMatch[2], 10))
|
|
201
|
+
errorTime.setSeconds(parseInt(timestampMatch[3], 10))
|
|
202
|
+
|
|
203
|
+
const ageMinutes = (now.getTime() - errorTime.getTime()) / 1000 / 60
|
|
204
|
+
if (ageMinutes < 1) {
|
|
205
|
+
score += 100
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return score
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Find the single highest priority error from categorized errors
|
|
214
|
+
*/
|
|
215
|
+
function findHighestPriorityError(
|
|
216
|
+
categorizedErrors: {
|
|
217
|
+
serverErrors: string[]
|
|
218
|
+
browserErrors: string[]
|
|
219
|
+
buildErrors: string[]
|
|
220
|
+
networkErrors: string[]
|
|
221
|
+
warnings: string[]
|
|
222
|
+
},
|
|
223
|
+
allErrors: string[],
|
|
224
|
+
logLines: string[]
|
|
225
|
+
): PrioritizedError | null {
|
|
226
|
+
const prioritizedErrors: PrioritizedError[] = []
|
|
227
|
+
|
|
228
|
+
// Helper to find interactions before an error
|
|
229
|
+
const findInteractions = (errorLine: string): string[] => {
|
|
230
|
+
const errorIndex = logLines.indexOf(errorLine)
|
|
231
|
+
if (errorIndex === -1) return []
|
|
232
|
+
|
|
233
|
+
const interactions: string[] = []
|
|
234
|
+
for (let i = errorIndex - 1; i >= Math.max(0, errorIndex - 20) && interactions.length < 5; i--) {
|
|
235
|
+
if (
|
|
236
|
+
logLines[i].includes("[INTERACTION]") ||
|
|
237
|
+
logLines[i].includes("[NAVIGATION]") ||
|
|
238
|
+
logLines[i].includes("[PAGE]")
|
|
239
|
+
) {
|
|
240
|
+
interactions.unshift(logLines[i])
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return interactions
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Process build errors
|
|
247
|
+
for (const error of categorizedErrors.buildErrors) {
|
|
248
|
+
const interactions = findInteractions(error)
|
|
249
|
+
prioritizedErrors.push({
|
|
250
|
+
error,
|
|
251
|
+
category: "build",
|
|
252
|
+
severity: "critical",
|
|
253
|
+
priorityScore: calculateErrorPriority(error, "build", interactions, allErrors),
|
|
254
|
+
interactions
|
|
255
|
+
})
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Process server errors
|
|
259
|
+
for (const error of categorizedErrors.serverErrors) {
|
|
260
|
+
const interactions = findInteractions(error)
|
|
261
|
+
const severity: PrioritizedError["severity"] = /CRITICAL|FATAL/i.test(error) ? "critical" : "error"
|
|
262
|
+
prioritizedErrors.push({
|
|
263
|
+
error,
|
|
264
|
+
category: "server",
|
|
265
|
+
severity,
|
|
266
|
+
priorityScore: calculateErrorPriority(error, "server", interactions, allErrors),
|
|
267
|
+
interactions
|
|
268
|
+
})
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Process browser errors
|
|
272
|
+
for (const error of categorizedErrors.browserErrors) {
|
|
273
|
+
const interactions = findInteractions(error)
|
|
274
|
+
const severity: PrioritizedError["severity"] = /CRITICAL|FATAL/i.test(error) ? "critical" : "error"
|
|
275
|
+
prioritizedErrors.push({
|
|
276
|
+
error,
|
|
277
|
+
category: "browser",
|
|
278
|
+
severity,
|
|
279
|
+
priorityScore: calculateErrorPriority(error, "browser", interactions, allErrors),
|
|
280
|
+
interactions
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Process network errors
|
|
285
|
+
for (const error of categorizedErrors.networkErrors) {
|
|
286
|
+
const interactions = findInteractions(error)
|
|
287
|
+
prioritizedErrors.push({
|
|
288
|
+
error,
|
|
289
|
+
category: "network",
|
|
290
|
+
severity: "error",
|
|
291
|
+
priorityScore: calculateErrorPriority(error, "network", interactions, allErrors),
|
|
292
|
+
interactions
|
|
293
|
+
})
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Process warnings (only if no errors found)
|
|
297
|
+
if (prioritizedErrors.length === 0) {
|
|
298
|
+
for (const error of categorizedErrors.warnings) {
|
|
299
|
+
const interactions = findInteractions(error)
|
|
300
|
+
prioritizedErrors.push({
|
|
301
|
+
error,
|
|
302
|
+
category: "warning",
|
|
303
|
+
severity: "warning",
|
|
304
|
+
priorityScore: calculateErrorPriority(error, "warning", interactions, allErrors),
|
|
305
|
+
interactions
|
|
306
|
+
})
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Sort by priority score (highest first)
|
|
311
|
+
prioritizedErrors.sort((a, b) => b.priorityScore - a.priorityScore)
|
|
312
|
+
|
|
313
|
+
return prioritizedErrors[0] || null
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Create a PR for the highest priority issue
|
|
318
|
+
*/
|
|
319
|
+
async function createPRForIssue(prioritizedError: PrioritizedError, _projectName: string): Promise<string> {
|
|
320
|
+
try {
|
|
321
|
+
// Extract error details for PR title and body
|
|
322
|
+
const errorType = prioritizedError.category.toUpperCase()
|
|
323
|
+
const errorMessage = prioritizedError.error
|
|
324
|
+
.replace(/\[[^\]]+\]/g, "") // Remove timestamps and tags
|
|
325
|
+
.trim()
|
|
326
|
+
.substring(0, 100)
|
|
327
|
+
|
|
328
|
+
const prTitle = `Fix: ${errorType} - ${errorMessage}`
|
|
329
|
+
|
|
330
|
+
// Build PR body
|
|
331
|
+
const prBody = `## 🐛 Bug Fix - ${prioritizedError.category} Error
|
|
332
|
+
|
|
333
|
+
**Priority Score:** ${prioritizedError.priorityScore} (${prioritizedError.severity})
|
|
334
|
+
|
|
335
|
+
### Error Details
|
|
336
|
+
\`\`\`
|
|
337
|
+
${prioritizedError.error}
|
|
338
|
+
\`\`\`
|
|
339
|
+
|
|
340
|
+
${
|
|
341
|
+
prioritizedError.interactions.length > 0
|
|
342
|
+
? `### Reproduction Steps
|
|
343
|
+
The error occurred after these user interactions:
|
|
344
|
+
${prioritizedError.interactions.map((i, idx) => `${idx + 1}. ${i}`).join("\n")}
|
|
345
|
+
|
|
346
|
+
### Verification
|
|
347
|
+
After implementing the fix, verify by:
|
|
348
|
+
1. Replaying the same interactions using \`execute_browser_action\`
|
|
349
|
+
2. Confirming the error no longer appears in logs
|
|
350
|
+
3. Checking that functionality works as expected
|
|
351
|
+
`
|
|
352
|
+
: ""
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
### Suggested Fix
|
|
356
|
+
This PR addresses the ${prioritizedError.severity}-level ${prioritizedError.category} error detected by dev3000.
|
|
357
|
+
|
|
358
|
+
${prioritizedError.suggestedFix || "Please analyze the error and implement the appropriate fix."}
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
🤖 Generated with [dev3000](https://github.com/vercel-labs/dev3000) - AI-powered debugging
|
|
362
|
+
`
|
|
363
|
+
|
|
364
|
+
// Create a new branch
|
|
365
|
+
const branchName = `fix/${prioritizedError.category}-${Date.now()}`
|
|
366
|
+
|
|
367
|
+
// Use execAsync to run git and gh commands
|
|
368
|
+
await execAsync(`git checkout -b ${branchName}`)
|
|
369
|
+
|
|
370
|
+
// Create the PR using gh
|
|
371
|
+
await execAsync(`gh pr create --title "${prTitle}" --body "${prBody}" --head ${branchName}`)
|
|
372
|
+
|
|
373
|
+
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!`
|
|
374
|
+
} catch (error) {
|
|
375
|
+
return `❌ Failed to create PR: ${error instanceof Error ? error.message : String(error)}\n\nYou can manually create a PR with the error details above.`
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
122
379
|
// Helper functions
|
|
123
380
|
export function findActiveSessions(): Session[] {
|
|
124
381
|
const sessionDir = join(homedir(), ".d3k")
|
|
@@ -182,7 +439,8 @@ export async function fixMyApp({
|
|
|
182
439
|
includeTimestampInstructions = true,
|
|
183
440
|
integrateNextjs = false,
|
|
184
441
|
integrateChromeDevtools = false,
|
|
185
|
-
returnRawData = false
|
|
442
|
+
returnRawData = false,
|
|
443
|
+
createPR = false
|
|
186
444
|
}: FixMyAppParams): Promise<{ content: Array<{ type: "text"; text: string }> }> {
|
|
187
445
|
// 🎯 MCP ORCHESTRATION: Check which downstream MCPs are available
|
|
188
446
|
const { getMCPClientManager } = await import("./client-manager")
|
|
@@ -628,6 +886,47 @@ export async function fixMyApp({
|
|
|
628
886
|
results.push("• Combined = 90%+ issue resolution rate!")
|
|
629
887
|
}
|
|
630
888
|
}
|
|
889
|
+
|
|
890
|
+
// 🎯 PRIORITIZATION & PR CREATION
|
|
891
|
+
// Find the single highest priority error and optionally create a PR
|
|
892
|
+
const highestPriorityError = findHighestPriorityError(categorizedErrors, actionableErrors, logLines)
|
|
893
|
+
|
|
894
|
+
if (highestPriorityError) {
|
|
895
|
+
results.push("")
|
|
896
|
+
results.push("🎯 **HIGHEST PRIORITY ISSUE:**")
|
|
897
|
+
results.push(`📊 Priority Score: ${highestPriorityError.priorityScore}`)
|
|
898
|
+
results.push(`🏷️ Category: ${highestPriorityError.category.toUpperCase()}`)
|
|
899
|
+
results.push(`⚠️ Severity: ${highestPriorityError.severity.toUpperCase()}`)
|
|
900
|
+
results.push("")
|
|
901
|
+
results.push("❌ **Error:**")
|
|
902
|
+
results.push(` ${highestPriorityError.error}`)
|
|
903
|
+
|
|
904
|
+
if (highestPriorityError.interactions.length > 0) {
|
|
905
|
+
results.push("")
|
|
906
|
+
results.push("📍 **Reproduction Steps:**")
|
|
907
|
+
highestPriorityError.interactions.forEach((interaction, idx) => {
|
|
908
|
+
results.push(` ${idx + 1}. ${interaction}`)
|
|
909
|
+
})
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
// Create PR if requested
|
|
913
|
+
if (createPR) {
|
|
914
|
+
results.push("")
|
|
915
|
+
results.push("🚀 **CREATING PR FOR THIS ISSUE...**")
|
|
916
|
+
const prResult = await createPRForIssue(highestPriorityError, projectName || "")
|
|
917
|
+
results.push(prResult)
|
|
918
|
+
} else {
|
|
919
|
+
results.push("")
|
|
920
|
+
results.push("💡 **To create a PR for this issue:**")
|
|
921
|
+
results.push(" Run: fix_my_app(createPR=true)")
|
|
922
|
+
results.push("")
|
|
923
|
+
results.push(" This will:")
|
|
924
|
+
results.push(" • Create a new branch for the fix")
|
|
925
|
+
results.push(" • Generate a PR with full error context")
|
|
926
|
+
results.push(" • Include reproduction steps")
|
|
927
|
+
results.push(" • Focus on fixing just this ONE issue")
|
|
928
|
+
}
|
|
929
|
+
}
|
|
631
930
|
}
|
|
632
931
|
|
|
633
932
|
// Extract screenshot information (replaces get_recent_screenshots)
|
|
@@ -3606,3 +3905,187 @@ export async function restartDevServer(params: {
|
|
|
3606
3905
|
}
|
|
3607
3906
|
}
|
|
3608
3907
|
}
|
|
3908
|
+
|
|
3909
|
+
// Crawl app - discover all URLs
|
|
3910
|
+
export interface CrawlAppParams {
|
|
3911
|
+
depth?: number | "all"
|
|
3912
|
+
projectName?: string
|
|
3913
|
+
}
|
|
3914
|
+
|
|
3915
|
+
export async function crawlApp(params: CrawlAppParams) {
|
|
3916
|
+
const { depth = 1, projectName } = params
|
|
3917
|
+
|
|
3918
|
+
try {
|
|
3919
|
+
// Find active session
|
|
3920
|
+
const sessions = findActiveSessions()
|
|
3921
|
+
const session = projectName ? sessions.find((s) => s.projectName === projectName) : sessions[0]
|
|
3922
|
+
|
|
3923
|
+
if (!session) {
|
|
3924
|
+
return {
|
|
3925
|
+
content: [
|
|
3926
|
+
{
|
|
3927
|
+
type: "text" as const,
|
|
3928
|
+
text: projectName
|
|
3929
|
+
? `❌ No active session found for project "${projectName}". Available projects: ${sessions.map((s) => s.projectName).join(", ") || "none"}`
|
|
3930
|
+
: "❌ No active dev3000 sessions found. Start dev3000 first with `d3k` in your project directory."
|
|
3931
|
+
}
|
|
3932
|
+
]
|
|
3933
|
+
}
|
|
3934
|
+
}
|
|
3935
|
+
|
|
3936
|
+
// Get CDP URL and app port from session
|
|
3937
|
+
const sessionData = JSON.parse(readFileSync(session.sessionFile, "utf-8"))
|
|
3938
|
+
const cdpUrl = sessionData.cdpUrl?.replace("http://", "ws://")
|
|
3939
|
+
const appPort = sessionData.appPort || "3000"
|
|
3940
|
+
const baseUrl = `http://localhost:${appPort}`
|
|
3941
|
+
|
|
3942
|
+
if (!cdpUrl) {
|
|
3943
|
+
return {
|
|
3944
|
+
content: [
|
|
3945
|
+
{
|
|
3946
|
+
type: "text" as const,
|
|
3947
|
+
text: "❌ No Chrome DevTools connection found. Browser monitoring must be active to crawl."
|
|
3948
|
+
}
|
|
3949
|
+
]
|
|
3950
|
+
}
|
|
3951
|
+
}
|
|
3952
|
+
|
|
3953
|
+
logToDevFile(`Crawl App: Starting crawl at depth ${depth} for ${baseUrl}`)
|
|
3954
|
+
|
|
3955
|
+
// Connect to CDP
|
|
3956
|
+
const ws = new WebSocket(cdpUrl)
|
|
3957
|
+
await new Promise((resolve, reject) => {
|
|
3958
|
+
ws.on("open", resolve)
|
|
3959
|
+
ws.on("error", reject)
|
|
3960
|
+
setTimeout(() => reject(new Error("CDP connection timeout")), 5000)
|
|
3961
|
+
})
|
|
3962
|
+
|
|
3963
|
+
let messageId = 2000
|
|
3964
|
+
// biome-ignore lint/suspicious/noExplicitAny: CDP protocol responses are dynamic
|
|
3965
|
+
const sendCommand = (method: string, params: Record<string, unknown> = {}): Promise<any> => {
|
|
3966
|
+
return new Promise((resolve, reject) => {
|
|
3967
|
+
const id = messageId++
|
|
3968
|
+
const message = JSON.stringify({ id, method, params })
|
|
3969
|
+
|
|
3970
|
+
const handler = (data: Buffer) => {
|
|
3971
|
+
const response = JSON.parse(data.toString())
|
|
3972
|
+
if (response.id === id) {
|
|
3973
|
+
ws.off("message", handler)
|
|
3974
|
+
if (response.error) {
|
|
3975
|
+
reject(new Error(response.error.message))
|
|
3976
|
+
} else {
|
|
3977
|
+
resolve(response.result)
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
}
|
|
3981
|
+
|
|
3982
|
+
ws.on("message", handler)
|
|
3983
|
+
ws.send(message)
|
|
3984
|
+
|
|
3985
|
+
setTimeout(() => {
|
|
3986
|
+
ws.off("message", handler)
|
|
3987
|
+
reject(new Error("Command timeout"))
|
|
3988
|
+
}, 10000)
|
|
3989
|
+
})
|
|
3990
|
+
}
|
|
3991
|
+
|
|
3992
|
+
// Enable necessary domains
|
|
3993
|
+
await sendCommand("Runtime.enable")
|
|
3994
|
+
await sendCommand("Page.enable")
|
|
3995
|
+
|
|
3996
|
+
// Discovered URLs
|
|
3997
|
+
const discovered = new Set<string>([baseUrl])
|
|
3998
|
+
const visited = new Set<string>()
|
|
3999
|
+
const toVisit: string[] = [baseUrl]
|
|
4000
|
+
|
|
4001
|
+
let currentDepth = 0
|
|
4002
|
+
const maxDepth = depth === "all" ? Number.POSITIVE_INFINITY : depth
|
|
4003
|
+
|
|
4004
|
+
while (toVisit.length > 0 && currentDepth <= maxDepth) {
|
|
4005
|
+
const currentLevelUrls = [...toVisit]
|
|
4006
|
+
toVisit.length = 0
|
|
4007
|
+
|
|
4008
|
+
logToDevFile(`Crawl App: Processing depth ${currentDepth} with ${currentLevelUrls.length} URLs`)
|
|
4009
|
+
|
|
4010
|
+
for (const url of currentLevelUrls) {
|
|
4011
|
+
if (visited.has(url)) continue
|
|
4012
|
+
visited.add(url)
|
|
4013
|
+
|
|
4014
|
+
try {
|
|
4015
|
+
// Navigate to URL
|
|
4016
|
+
logToDevFile(`Crawl App: Visiting ${url}`)
|
|
4017
|
+
await sendCommand("Page.navigate", { url })
|
|
4018
|
+
|
|
4019
|
+
// Wait for page load
|
|
4020
|
+
await new Promise((resolve) => setTimeout(resolve, 2000))
|
|
4021
|
+
|
|
4022
|
+
// Extract all links
|
|
4023
|
+
const result = await sendCommand("Runtime.evaluate", {
|
|
4024
|
+
expression: `
|
|
4025
|
+
Array.from(document.querySelectorAll('a[href]')).map(a => {
|
|
4026
|
+
try {
|
|
4027
|
+
const url = new URL(a.href, window.location.href);
|
|
4028
|
+
// Only return same-origin links
|
|
4029
|
+
if (url.origin === window.location.origin) {
|
|
4030
|
+
// Remove hash and query params for deduplication
|
|
4031
|
+
return url.origin + url.pathname;
|
|
4032
|
+
}
|
|
4033
|
+
} catch {}
|
|
4034
|
+
return null;
|
|
4035
|
+
}).filter(Boolean)
|
|
4036
|
+
`,
|
|
4037
|
+
returnByValue: true
|
|
4038
|
+
})
|
|
4039
|
+
|
|
4040
|
+
const links = result.result?.value || []
|
|
4041
|
+
|
|
4042
|
+
for (const link of links) {
|
|
4043
|
+
if (!discovered.has(link)) {
|
|
4044
|
+
discovered.add(link)
|
|
4045
|
+
if (currentDepth < maxDepth) {
|
|
4046
|
+
toVisit.push(link)
|
|
4047
|
+
}
|
|
4048
|
+
}
|
|
4049
|
+
}
|
|
4050
|
+
|
|
4051
|
+
logToDevFile(`Crawl App: Found ${links.length} links on ${url}`)
|
|
4052
|
+
} catch (error) {
|
|
4053
|
+
logToDevFile(`Crawl App: Error visiting ${url} - ${error}`)
|
|
4054
|
+
}
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
currentDepth++
|
|
4058
|
+
|
|
4059
|
+
// For "all" mode, stop when no new URLs are found
|
|
4060
|
+
if (depth === "all" && toVisit.length === 0) {
|
|
4061
|
+
break
|
|
4062
|
+
}
|
|
4063
|
+
}
|
|
4064
|
+
|
|
4065
|
+
ws.close()
|
|
4066
|
+
|
|
4067
|
+
const urls = Array.from(discovered).sort()
|
|
4068
|
+
const depthReached = depth === "all" ? currentDepth - 1 : Math.min(currentDepth - 1, maxDepth)
|
|
4069
|
+
|
|
4070
|
+
logToDevFile(`Crawl App: Complete - discovered ${urls.length} URLs at depth ${depthReached}`)
|
|
4071
|
+
|
|
4072
|
+
return {
|
|
4073
|
+
content: [
|
|
4074
|
+
{
|
|
4075
|
+
type: "text" as const,
|
|
4076
|
+
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`
|
|
4077
|
+
}
|
|
4078
|
+
]
|
|
4079
|
+
}
|
|
4080
|
+
} catch (error) {
|
|
4081
|
+
logToDevFile(`Crawl App: Error - ${error}`)
|
|
4082
|
+
return {
|
|
4083
|
+
content: [
|
|
4084
|
+
{
|
|
4085
|
+
type: "text" as const,
|
|
4086
|
+
text: `❌ **CRAWL FAILED**\n\n${error instanceof Error ? error.message : String(error)}`
|
|
4087
|
+
}
|
|
4088
|
+
]
|
|
4089
|
+
}
|
|
4090
|
+
}
|
|
4091
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dev3000",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.103",
|
|
4
4
|
"description": "AI-powered development tools with browser monitoring and MCP server integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
"chalk": "^5.3.0",
|
|
64
64
|
"commander": "^14.0.1",
|
|
65
65
|
"ink": "^6.3.1",
|
|
66
|
+
"ink-spinner": "^5.0.0",
|
|
66
67
|
"next": "16.0.0-canary.18",
|
|
67
68
|
"ora": "^9.0.0",
|
|
68
69
|
"package-manager-detector": "^1.3.0",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from "chalk"
|
|
2
2
|
import { createReadStream, unwatchFile, watchFile } from "fs"
|
|
3
3
|
import { Box, render, Text, useInput, useStdout } from "ink"
|
|
4
|
+
import Spinner from "ink-spinner"
|
|
4
5
|
import { useEffect, useRef, useState } from "react"
|
|
5
6
|
import type { Readable } from "stream"
|
|
6
7
|
import { LOG_COLORS } from "./constants/log-colors.js"
|
|
@@ -46,6 +47,7 @@ const TUIApp = ({
|
|
|
46
47
|
const [scrollOffset, setScrollOffset] = useState(0)
|
|
47
48
|
const [initStatus, setInitStatus] = useState<string | null>("Initializing...")
|
|
48
49
|
const [appPort, setAppPort] = useState<string>(initialAppPort)
|
|
50
|
+
const [portConfirmed, setPortConfirmed] = useState<boolean>(false)
|
|
49
51
|
const logIdCounter = useRef(0)
|
|
50
52
|
const [clearFromLogId, setClearFromLogId] = useState<number>(0) // Track log ID to clear from
|
|
51
53
|
const { stdout } = useStdout()
|
|
@@ -112,6 +114,7 @@ const TUIApp = ({
|
|
|
112
114
|
useEffect(() => {
|
|
113
115
|
onAppPortUpdate((port: string) => {
|
|
114
116
|
setAppPort(port)
|
|
117
|
+
setPortConfirmed(true)
|
|
115
118
|
})
|
|
116
119
|
}, [onAppPortUpdate])
|
|
117
120
|
|
|
@@ -332,7 +335,10 @@ const TUIApp = ({
|
|
|
332
335
|
|
|
333
336
|
{/* Info on the right */}
|
|
334
337
|
<Box flexDirection="column" flexGrow={1}>
|
|
335
|
-
<
|
|
338
|
+
<Box>
|
|
339
|
+
<Text color="cyan">🌐 App: http://localhost:{appPort} </Text>
|
|
340
|
+
{!portConfirmed && <Spinner type="dots" />}
|
|
341
|
+
</Box>
|
|
336
342
|
<Text color="cyan">🤖 MCP: http://localhost:{mcpPort}</Text>
|
|
337
343
|
<Text color="cyan">
|
|
338
344
|
📸 Logs: http://localhost:{mcpPort}/logs
|
/package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → I_0vbzGzd9Y9Tse-yfGKd}/_buildManifest.js
RENAMED
|
File without changes
|
|
File without changes
|
/package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → I_0vbzGzd9Y9Tse-yfGKd}/_ssgManifest.js
RENAMED
|
File without changes
|