dev3000 0.0.101 → 0.0.102

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 (48) hide show
  1. package/README.md +2 -0
  2. package/dist/dev-environment.d.ts +1 -0
  3. package/dist/dev-environment.d.ts.map +1 -1
  4. package/dist/dev-environment.js +52 -8
  5. package/dist/dev-environment.js.map +1 -1
  6. package/dist/screencast-manager.d.ts.map +1 -1
  7. package/dist/screencast-manager.js +3 -4
  8. package/dist/screencast-manager.js.map +1 -1
  9. package/dist/src/tui-interface-impl.tsx +7 -1
  10. package/dist/tui-interface-impl.d.ts.map +1 -1
  11. package/dist/tui-interface-impl.js +4 -1
  12. package/dist/tui-interface-impl.js.map +1 -1
  13. package/mcp-server/.next/BUILD_ID +1 -1
  14. package/mcp-server/.next/build-manifest.json +2 -2
  15. package/mcp-server/.next/fallback-build-manifest.json +2 -2
  16. package/mcp-server/.next/prerender-manifest.json +3 -3
  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/_global-error.segments/__PAGE__.segment.rsc +1 -1
  20. package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  21. package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  22. package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  23. package/mcp-server/.next/server/app/_not-found.html +1 -1
  24. package/mcp-server/.next/server/app/_not-found.rsc +1 -1
  25. package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  26. package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  27. package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  28. package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  29. package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  30. package/mcp-server/.next/server/app/index.html +1 -1
  31. package/mcp-server/.next/server/app/index.rsc +1 -1
  32. package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  33. package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +1 -1
  34. package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +1 -1
  35. package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  36. package/mcp-server/.next/server/chunks/[root-of-the-server]__0691849a._.js +1 -1
  37. package/mcp-server/.next/server/chunks/[root-of-the-server]__0691849a._.js.map +1 -1
  38. package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js +35 -8
  39. package/mcp-server/.next/server/chunks/[root-of-the-server]__ae49815f._.js.map +1 -1
  40. package/mcp-server/.next/server/server-reference-manifest.js +1 -1
  41. package/mcp-server/.next/server/server-reference-manifest.json +1 -1
  42. package/mcp-server/app/mcp/route.ts +26 -1
  43. package/mcp-server/app/mcp/tools.ts +188 -1
  44. package/package.json +2 -1
  45. package/src/tui-interface-impl.tsx +7 -1
  46. /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → 4DzBVkDIn_yRVz53mYMf8}/_buildManifest.js +0 -0
  47. /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → 4DzBVkDIn_yRVz53mYMf8}/_clientMiddlewareManifest.json +0 -0
  48. /package/mcp-server/.next/static/{eVcHKbTsqk-_vUXyVOoaO → 4DzBVkDIn_yRVz53mYMf8}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"/PxvsAvzRaWzXUHwWLug3ZcveweVRuE36Tz0vOuDXP8=\"\n}"
1
+ self.__RSC_SERVER_MANIFEST="{\n \"node\": {},\n \"edge\": {},\n \"encryptionKey\": \"J2fGjU4v1SMY6g5pu3U2aY0GeU26Wipdet5Y3MnFxRU=\"\n}"
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "node": {},
3
3
  "edge": {},
4
- "encryptionKey": "/PxvsAvzRaWzXUHwWLug3ZcveweVRuE36Tz0vOuDXP8="
4
+ "encryptionKey": "J2fGjU4v1SMY6g5pu3U2aY0GeU26Wipdet5Y3MnFxRU="
5
5
  }
@@ -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 { executeBrowserAction, findComponentSource, fixMyApp, restartDevServer, TOOL_DESCRIPTIONS } from "./tools"
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 => {
@@ -502,6 +509,24 @@ const handler = createMcpHandler(
502
509
  }
503
510
  )
504
511
 
512
+ // App crawler tool
513
+ server.tool(
514
+ "crawl_app",
515
+ TOOL_DESCRIPTIONS.crawl_app,
516
+ {
517
+ depth: z
518
+ .union([z.number().int().min(1), z.literal("all")])
519
+ .optional()
520
+ .describe(
521
+ "Crawl depth: number (1=homepage only, 2=homepage+next level, etc.) or 'all' for exhaustive (default: 1)"
522
+ ),
523
+ projectName: z.string().optional().describe("Project name (if multiple dev3000 instances are running)")
524
+ },
525
+ async (params) => {
526
+ return crawlApp(params)
527
+ }
528
+ )
529
+
505
530
  // Tool that returns monitoring code for Claude to execute
506
531
  // TODO: Commenting out for now - need to figure out the right approach for proactive monitoring
507
532
  /*
@@ -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
@@ -3606,3 +3609,187 @@ export async function restartDevServer(params: {
3606
3609
  }
3607
3610
  }
3608
3611
  }
3612
+
3613
+ // Crawl app - discover all URLs
3614
+ export interface CrawlAppParams {
3615
+ depth?: number | "all"
3616
+ projectName?: string
3617
+ }
3618
+
3619
+ export async function crawlApp(params: CrawlAppParams) {
3620
+ const { depth = 1, projectName } = params
3621
+
3622
+ try {
3623
+ // Find active session
3624
+ const sessions = findActiveSessions()
3625
+ const session = projectName ? sessions.find((s) => s.projectName === projectName) : sessions[0]
3626
+
3627
+ if (!session) {
3628
+ return {
3629
+ content: [
3630
+ {
3631
+ type: "text" as const,
3632
+ text: projectName
3633
+ ? `❌ No active session found for project "${projectName}". Available projects: ${sessions.map((s) => s.projectName).join(", ") || "none"}`
3634
+ : "❌ No active dev3000 sessions found. Start dev3000 first with `d3k` in your project directory."
3635
+ }
3636
+ ]
3637
+ }
3638
+ }
3639
+
3640
+ // Get CDP URL and app port from session
3641
+ const sessionData = JSON.parse(readFileSync(session.sessionFile, "utf-8"))
3642
+ const cdpUrl = sessionData.cdpUrl?.replace("http://", "ws://")
3643
+ const appPort = sessionData.appPort || "3000"
3644
+ const baseUrl = `http://localhost:${appPort}`
3645
+
3646
+ if (!cdpUrl) {
3647
+ return {
3648
+ content: [
3649
+ {
3650
+ type: "text" as const,
3651
+ text: "❌ No Chrome DevTools connection found. Browser monitoring must be active to crawl."
3652
+ }
3653
+ ]
3654
+ }
3655
+ }
3656
+
3657
+ logToDevFile(`Crawl App: Starting crawl at depth ${depth} for ${baseUrl}`)
3658
+
3659
+ // Connect to CDP
3660
+ const ws = new WebSocket(cdpUrl)
3661
+ await new Promise((resolve, reject) => {
3662
+ ws.on("open", resolve)
3663
+ ws.on("error", reject)
3664
+ setTimeout(() => reject(new Error("CDP connection timeout")), 5000)
3665
+ })
3666
+
3667
+ let messageId = 2000
3668
+ // biome-ignore lint/suspicious/noExplicitAny: CDP protocol responses are dynamic
3669
+ const sendCommand = (method: string, params: Record<string, unknown> = {}): Promise<any> => {
3670
+ return new Promise((resolve, reject) => {
3671
+ const id = messageId++
3672
+ const message = JSON.stringify({ id, method, params })
3673
+
3674
+ const handler = (data: Buffer) => {
3675
+ const response = JSON.parse(data.toString())
3676
+ if (response.id === id) {
3677
+ ws.off("message", handler)
3678
+ if (response.error) {
3679
+ reject(new Error(response.error.message))
3680
+ } else {
3681
+ resolve(response.result)
3682
+ }
3683
+ }
3684
+ }
3685
+
3686
+ ws.on("message", handler)
3687
+ ws.send(message)
3688
+
3689
+ setTimeout(() => {
3690
+ ws.off("message", handler)
3691
+ reject(new Error("Command timeout"))
3692
+ }, 10000)
3693
+ })
3694
+ }
3695
+
3696
+ // Enable necessary domains
3697
+ await sendCommand("Runtime.enable")
3698
+ await sendCommand("Page.enable")
3699
+
3700
+ // Discovered URLs
3701
+ const discovered = new Set<string>([baseUrl])
3702
+ const visited = new Set<string>()
3703
+ const toVisit: string[] = [baseUrl]
3704
+
3705
+ let currentDepth = 0
3706
+ const maxDepth = depth === "all" ? Number.POSITIVE_INFINITY : depth
3707
+
3708
+ while (toVisit.length > 0 && currentDepth <= maxDepth) {
3709
+ const currentLevelUrls = [...toVisit]
3710
+ toVisit.length = 0
3711
+
3712
+ logToDevFile(`Crawl App: Processing depth ${currentDepth} with ${currentLevelUrls.length} URLs`)
3713
+
3714
+ for (const url of currentLevelUrls) {
3715
+ if (visited.has(url)) continue
3716
+ visited.add(url)
3717
+
3718
+ try {
3719
+ // Navigate to URL
3720
+ logToDevFile(`Crawl App: Visiting ${url}`)
3721
+ await sendCommand("Page.navigate", { url })
3722
+
3723
+ // Wait for page load
3724
+ await new Promise((resolve) => setTimeout(resolve, 2000))
3725
+
3726
+ // Extract all links
3727
+ const result = await sendCommand("Runtime.evaluate", {
3728
+ expression: `
3729
+ Array.from(document.querySelectorAll('a[href]')).map(a => {
3730
+ try {
3731
+ const url = new URL(a.href, window.location.href);
3732
+ // Only return same-origin links
3733
+ if (url.origin === window.location.origin) {
3734
+ // Remove hash and query params for deduplication
3735
+ return url.origin + url.pathname;
3736
+ }
3737
+ } catch {}
3738
+ return null;
3739
+ }).filter(Boolean)
3740
+ `,
3741
+ returnByValue: true
3742
+ })
3743
+
3744
+ const links = result.result?.value || []
3745
+
3746
+ for (const link of links) {
3747
+ if (!discovered.has(link)) {
3748
+ discovered.add(link)
3749
+ if (currentDepth < maxDepth) {
3750
+ toVisit.push(link)
3751
+ }
3752
+ }
3753
+ }
3754
+
3755
+ logToDevFile(`Crawl App: Found ${links.length} links on ${url}`)
3756
+ } catch (error) {
3757
+ logToDevFile(`Crawl App: Error visiting ${url} - ${error}`)
3758
+ }
3759
+ }
3760
+
3761
+ currentDepth++
3762
+
3763
+ // For "all" mode, stop when no new URLs are found
3764
+ if (depth === "all" && toVisit.length === 0) {
3765
+ break
3766
+ }
3767
+ }
3768
+
3769
+ ws.close()
3770
+
3771
+ const urls = Array.from(discovered).sort()
3772
+ const depthReached = depth === "all" ? currentDepth - 1 : Math.min(currentDepth - 1, maxDepth)
3773
+
3774
+ logToDevFile(`Crawl App: Complete - discovered ${urls.length} URLs at depth ${depthReached}`)
3775
+
3776
+ return {
3777
+ content: [
3778
+ {
3779
+ type: "text" as const,
3780
+ 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`
3781
+ }
3782
+ ]
3783
+ }
3784
+ } catch (error) {
3785
+ logToDevFile(`Crawl App: Error - ${error}`)
3786
+ return {
3787
+ content: [
3788
+ {
3789
+ type: "text" as const,
3790
+ text: `❌ **CRAWL FAILED**\n\n${error instanceof Error ? error.message : String(error)}`
3791
+ }
3792
+ ]
3793
+ }
3794
+ }
3795
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dev3000",
3
- "version": "0.0.101",
3
+ "version": "0.0.102",
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
- <Text color="cyan">🌐 App: http://localhost:{appPort}</Text>
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