dev3000 0.0.151 → 0.0.154

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 (169) hide show
  1. package/README.md +121 -103
  2. package/bin/dev3000 +10 -5
  3. package/dist/cdp-monitor.d.ts.map +1 -1
  4. package/dist/cdp-monitor.js +2 -3
  5. package/dist/cdp-monitor.js.map +1 -1
  6. package/dist/cli.js +301 -75
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/cloud-check-pr.d.ts +1 -0
  9. package/dist/commands/cloud-check-pr.d.ts.map +1 -1
  10. package/dist/commands/cloud-check-pr.js +18 -6
  11. package/dist/commands/cloud-check-pr.js.map +1 -1
  12. package/dist/commands/crawl.d.ts +12 -0
  13. package/dist/commands/crawl.d.ts.map +1 -0
  14. package/dist/commands/crawl.js +140 -0
  15. package/dist/commands/crawl.js.map +1 -0
  16. package/dist/commands/errors.d.ts +14 -0
  17. package/dist/commands/errors.d.ts.map +1 -0
  18. package/dist/commands/errors.js +295 -0
  19. package/dist/commands/errors.js.map +1 -0
  20. package/dist/commands/find-component.d.ts +8 -0
  21. package/dist/commands/find-component.d.ts.map +1 -0
  22. package/dist/commands/find-component.js +182 -0
  23. package/dist/commands/find-component.js.map +1 -0
  24. package/dist/commands/fix.d.ts +13 -0
  25. package/dist/commands/fix.d.ts.map +1 -0
  26. package/dist/commands/fix.js +288 -0
  27. package/dist/commands/fix.js.map +1 -0
  28. package/dist/commands/logs.d.ts +13 -0
  29. package/dist/commands/logs.d.ts.map +1 -0
  30. package/dist/commands/logs.js +195 -0
  31. package/dist/commands/logs.js.map +1 -0
  32. package/dist/commands/restart.d.ts +8 -0
  33. package/dist/commands/restart.d.ts.map +1 -0
  34. package/dist/commands/restart.js +92 -0
  35. package/dist/commands/restart.js.map +1 -0
  36. package/dist/components/PackageSelector.d.ts +12 -0
  37. package/dist/components/PackageSelector.d.ts.map +1 -0
  38. package/dist/components/PackageSelector.js +74 -0
  39. package/dist/components/PackageSelector.js.map +1 -0
  40. package/dist/dev-environment.d.ts +0 -7
  41. package/dist/dev-environment.d.ts.map +1 -1
  42. package/dist/dev-environment.js +108 -674
  43. package/dist/dev-environment.js.map +1 -1
  44. package/dist/screencast-manager.d.ts.map +1 -1
  45. package/dist/screencast-manager.js +7 -8
  46. package/dist/screencast-manager.js.map +1 -1
  47. package/dist/services/parsers/output-processor.js +2 -2
  48. package/dist/services/parsers/output-processor.js.map +1 -1
  49. package/dist/skills/d3k/SKILL.md +38 -27
  50. package/dist/skills/index.d.ts +10 -14
  51. package/dist/skills/index.d.ts.map +1 -1
  52. package/dist/skills/index.js +47 -77
  53. package/dist/skills/index.js.map +1 -1
  54. package/dist/skills/index.test.ts +13 -55
  55. package/dist/skills/index.ts +48 -81
  56. package/dist/src/tui-interface-impl.tsx +6 -15
  57. package/dist/tui-interface-impl.d.ts.map +1 -1
  58. package/dist/tui-interface-impl.js +4 -4
  59. package/dist/tui-interface-impl.js.map +1 -1
  60. package/dist/tui-interface-opentui.d.ts.map +1 -1
  61. package/dist/tui-interface-opentui.js +227 -100
  62. package/dist/tui-interface-opentui.js.map +1 -1
  63. package/dist/utils/agent-browser.d.ts +2 -0
  64. package/dist/utils/agent-browser.d.ts.map +1 -1
  65. package/dist/utils/agent-browser.js +46 -8
  66. package/dist/utils/agent-browser.js.map +1 -1
  67. package/dist/utils/agent-selection.d.ts.map +1 -1
  68. package/dist/utils/agent-selection.js +9 -2
  69. package/dist/utils/agent-selection.js.map +1 -1
  70. package/dist/utils/skill-installer.d.ts +55 -29
  71. package/dist/utils/skill-installer.d.ts.map +1 -1
  72. package/dist/utils/skill-installer.js +118 -229
  73. package/dist/utils/skill-installer.js.map +1 -1
  74. package/dist/utils/tmux-helpers.d.ts +1 -2
  75. package/dist/utils/tmux-helpers.d.ts.map +1 -1
  76. package/dist/utils/tmux-helpers.js +17 -18
  77. package/dist/utils/tmux-helpers.js.map +1 -1
  78. package/dist/utils/version-check.d.ts +2 -1
  79. package/dist/utils/version-check.d.ts.map +1 -1
  80. package/dist/utils/version-check.js +9 -0
  81. package/dist/utils/version-check.js.map +1 -1
  82. package/mcp-server/.next/BUILD_ID +1 -1
  83. package/mcp-server/.next/build-manifest.json +2 -2
  84. package/mcp-server/.next/fallback-build-manifest.json +2 -2
  85. package/mcp-server/.next/prerender-manifest.json +3 -3
  86. package/mcp-server/.next/server/app/_global-error/page.js +1 -1
  87. package/mcp-server/.next/server/app/_global-error/page.js.nft.json +1 -1
  88. package/mcp-server/.next/server/app/_global-error.html +2 -2
  89. package/mcp-server/.next/server/app/_global-error.rsc +1 -1
  90. package/mcp-server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  91. package/mcp-server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  92. package/mcp-server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  93. package/mcp-server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  94. package/mcp-server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  95. package/mcp-server/.next/server/app/_not-found/page.js +1 -1
  96. package/mcp-server/.next/server/app/_not-found/page.js.nft.json +1 -1
  97. package/mcp-server/.next/server/app/_not-found.html +1 -1
  98. package/mcp-server/.next/server/app/_not-found.rsc +1 -1
  99. package/mcp-server/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  100. package/mcp-server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  101. package/mcp-server/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  102. package/mcp-server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  103. package/mcp-server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  104. package/mcp-server/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  105. package/mcp-server/.next/server/app/api/screenshots/capture/route.js +2 -2
  106. package/mcp-server/.next/server/app/api/screenshots/capture/route.js.nft.json +1 -1
  107. package/mcp-server/.next/server/app/index.html +1 -1
  108. package/mcp-server/.next/server/app/index.rsc +2 -2
  109. package/mcp-server/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
  110. package/mcp-server/.next/server/app/index.segments/_full.segment.rsc +2 -2
  111. package/mcp-server/.next/server/app/index.segments/_head.segment.rsc +1 -1
  112. package/mcp-server/.next/server/app/index.segments/_index.segment.rsc +1 -1
  113. package/mcp-server/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  114. package/mcp-server/.next/server/app/logs/page.js +1 -1
  115. package/mcp-server/.next/server/app/logs/page.js.nft.json +1 -1
  116. package/mcp-server/.next/server/app/mcp/route.js +4 -4
  117. package/mcp-server/.next/server/app/mcp/route.js.nft.json +1 -1
  118. package/mcp-server/.next/server/app/page.js +1 -1
  119. package/mcp-server/.next/server/app/page.js.nft.json +1 -1
  120. package/mcp-server/.next/server/app/page_client-reference-manifest.js +1 -1
  121. package/mcp-server/.next/server/app/video/[session]/page.js +1 -1
  122. package/mcp-server/.next/server/app/video/[session]/page.js.nft.json +1 -1
  123. package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js +3 -3
  124. package/mcp-server/.next/server/chunks/[root-of-the-server]__2f95edf0._.js.map +1 -1
  125. package/mcp-server/.next/server/chunks/[root-of-the-server]__69e6dfb7._.js +3 -0
  126. package/mcp-server/.next/server/chunks/{[root-of-the-server]__444592aa._.js.map → [root-of-the-server]__69e6dfb7._.js.map} +1 -1
  127. package/mcp-server/.next/server/chunks/[root-of-the-server]__6baff21e._.js +4 -0
  128. package/mcp-server/.next/server/chunks/{[root-of-the-server]__130a5f58._.js.map → [root-of-the-server]__6baff21e._.js.map} +1 -1
  129. package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js +1 -1
  130. package/mcp-server/.next/server/chunks/[root-of-the-server]__6f790e1f._.js.map +1 -1
  131. package/mcp-server/.next/server/chunks/[root-of-the-server]__c8cf5b23._.js +3 -0
  132. package/mcp-server/.next/server/chunks/[root-of-the-server]__e6a83e60._.js +4 -0
  133. package/mcp-server/.next/server/chunks/{[root-of-the-server]__b71c83ed._.js.map → [root-of-the-server]__e6a83e60._.js.map} +1 -1
  134. package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js +32 -66
  135. package/mcp-server/.next/server/chunks/mcp-server_app_mcp_tools_ts_faf6d7df._.js.map +1 -1
  136. package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js +1 -1
  137. package/mcp-server/.next/server/chunks/src_utils_agent-browser_ts_cc00e0d8._.js.map +1 -1
  138. package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js +3 -0
  139. package/mcp-server/.next/server/chunks/src_utils_project-name_ts_1fab1dd5._.js.map +1 -0
  140. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__f66148e5._.js → [root-of-the-server]__b17d4048._.js} +2 -2
  141. package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__50eb2eba._.js → [root-of-the-server]__dcf84f77._.js} +2 -2
  142. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js +1 -1
  143. package/mcp-server/.next/server/chunks/ssr/mcp-server_app_page_tsx_9fc46577._.js.map +1 -1
  144. package/mcp-server/.next/server/pages/404.html +1 -1
  145. package/mcp-server/.next/server/pages/500.html +2 -2
  146. package/mcp-server/.next/server/server-reference-manifest.js +1 -1
  147. package/mcp-server/.next/server/server-reference-manifest.json +1 -1
  148. package/mcp-server/.next/static/chunks/{2422ea9ed874427b.js → 3f3f8e7d16ba3bf4.js} +1 -1
  149. package/mcp-server/app/api/tools/route.ts +5 -4
  150. package/mcp-server/app/mcp/route.ts +8 -63
  151. package/mcp-server/app/mcp/tools.ts +71 -445
  152. package/mcp-server/app/page.tsx +1 -1
  153. package/mcp-server/package.json +2 -0
  154. package/package.json +6 -5
  155. package/src/tui-interface-impl.tsx +6 -15
  156. package/dist/components/SkillSelector.d.ts +0 -10
  157. package/dist/components/SkillSelector.d.ts.map +0 -1
  158. package/dist/components/SkillSelector.js +0 -87
  159. package/dist/components/SkillSelector.js.map +0 -1
  160. package/mcp-server/.next/server/chunks/[root-of-the-server]__130a5f58._.js +0 -4
  161. package/mcp-server/.next/server/chunks/[root-of-the-server]__444592aa._.js +0 -3
  162. package/mcp-server/.next/server/chunks/[root-of-the-server]__8f84b4cc._.js +0 -3
  163. package/mcp-server/.next/server/chunks/[root-of-the-server]__b71c83ed._.js +0 -4
  164. /package/mcp-server/.next/server/chunks/{[root-of-the-server]__8f84b4cc._.js.map → [root-of-the-server]__c8cf5b23._.js.map} +0 -0
  165. /package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__f66148e5._.js.map → [root-of-the-server]__b17d4048._.js.map} +0 -0
  166. /package/mcp-server/.next/server/chunks/ssr/{[root-of-the-server]__50eb2eba._.js.map → [root-of-the-server]__dcf84f77._.js.map} +0 -0
  167. /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_buildManifest.js +0 -0
  168. /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_clientMiddlewareManifest.json +0 -0
  169. /package/mcp-server/.next/static/{HVxg1xsmhVm6VO2KBlH_- → MfA6U1EFS31sVuHA4xDF_}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  import chalk from "chalk";
2
2
  import { spawn } from "child_process";
3
- import { appendFileSync, copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readdirSync, readFileSync, realpathSync, statSync, unlinkSync, writeFileSync } from "fs";
3
+ import { appendFileSync, copyFileSync, existsSync, mkdirSync, readdirSync, readFileSync, statSync, unlinkSync, writeFileSync } from "fs";
4
4
  import https from "https";
5
5
  import ora from "ora";
6
6
  import { homedir, tmpdir } from "os";
@@ -9,6 +9,7 @@ import { fileURLToPath } from "url";
9
9
  import { CDPMonitor } from "./cdp-monitor.js";
10
10
  import { ScreencastManager } from "./screencast-manager.js";
11
11
  import { NextJsErrorDetector, OutputProcessor, StandardLogParser } from "./services/parsers/index.js";
12
+ import { getBundledSkillsPath } from "./skills/index.js";
12
13
  import { DevTUI } from "./tui-interface.js";
13
14
  import { formatMcpConfigTargets, MCP_CONFIG_TARGETS } from "./utils/mcp-configs.js";
14
15
  import { getProjectDir, getProjectDisplayName, getProjectName } from "./utils/project-name.js";
@@ -21,8 +22,9 @@ const MCP_NAMES = {
21
22
  NEXTJS_DEV: "dev3000-nextjs-dev",
22
23
  VERCEL: "vercel"
23
24
  };
24
- // Vercel MCP URL (public OAuth-based MCP)
25
- const VERCEL_MCP_URL = "https://mcp.vercel.com";
25
+ // Vercel MCP URL (public OAuth-based MCP) - kept for potential future use
26
+ // @ts-expect-error Unused but kept for reference
27
+ const _VERCEL_MCP_URL = "https://mcp.vercel.com";
26
28
  /**
27
29
  * Patterns for identifying orphaned MCP-related processes to clean up on startup.
28
30
  *
@@ -38,8 +40,10 @@ export const ORPHANED_PROCESS_CLEANUP_PATTERNS = [
38
40
  ];
39
41
  /**
40
42
  * Check if the current project has a .vercel directory (indicating a Vercel project)
43
+ * Kept for potential future use
41
44
  */
42
- function hasVercelProject() {
45
+ // @ts-expect-error Unused but kept for potential future use
46
+ function _hasVercelProject() {
43
47
  return existsSync(join(process.cwd(), ".vercel"));
44
48
  }
45
49
  /**
@@ -128,17 +132,6 @@ class Logger {
128
132
  }
129
133
  }
130
134
  }
131
- function detectPackageManagerForRun() {
132
- if (existsSync("bun.lockb"))
133
- return "bun";
134
- if (existsSync("pnpm-lock.yaml"))
135
- return "pnpm";
136
- if (existsSync("yarn.lock"))
137
- return "yarn";
138
- if (existsSync("package-lock.json"))
139
- return "npm";
140
- return "npm"; // fallback
141
- }
142
135
  /**
143
136
  * Detect if we're in a sandbox environment (Vercel Sandbox, Docker, etc.)
144
137
  * where lsof and other system utilities may not be available.
@@ -171,7 +164,7 @@ export function countActiveD3kInstances(excludeCurrentPid = false) {
171
164
  const pidPath = join(tmpDir, pidFile);
172
165
  const pidStr = readFileSync(pidPath, "utf-8").trim();
173
166
  const pid = parseInt(pidStr, 10);
174
- if (isNaN(pid))
167
+ if (Number.isNaN(pid))
175
168
  continue;
176
169
  // Skip current process if requested
177
170
  if (excludeCurrentPid && pid === process.pid)
@@ -465,47 +458,28 @@ async function isChromeDevtoolsMcpSupported() {
465
458
  }
466
459
  }
467
460
  /**
468
- * Ensure MCP server configurations are added to project's .mcp.json (Claude Code)
461
+ * Clean up old dev3000 MCP entries from project's .mcp.json (Claude Code)
462
+ * MCP server has been removed - we now use CLI commands instead
469
463
  */
470
- async function ensureMcpServers(mcpPort, _appPort, _enableChromeDevtools) {
464
+ async function ensureMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
471
465
  try {
472
466
  const settingsPath = join(process.cwd(), ".mcp.json");
473
- // Read or create settings
474
- let settings;
475
- if (existsSync(settingsPath)) {
476
- const settingsContent = readFileSync(settingsPath, "utf-8");
477
- settings = JSON.parse(settingsContent);
478
- }
479
- else {
480
- settings = {};
467
+ if (!existsSync(settingsPath)) {
468
+ return; // Nothing to clean up
481
469
  }
482
- // Ensure mcpServers structure exists
470
+ const settingsContent = readFileSync(settingsPath, "utf-8");
471
+ const settings = JSON.parse(settingsContent);
483
472
  if (!settings.mcpServers) {
484
- settings.mcpServers = {};
485
- }
486
- let added = false;
487
- // Add dev3000 MCP server (HTTP type)
488
- // NOTE: dev3000 now acts as an MCP orchestrator/gateway that internally
489
- // spawns and connects to chrome-devtools-mcp and next-devtools-mcp as stdio processes,
490
- // so users only need to configure dev3000 once!
491
- if (!settings.mcpServers[MCP_NAMES.DEV3000]) {
492
- settings.mcpServers[MCP_NAMES.DEV3000] = {
493
- type: "http",
494
- url: `http://localhost:${mcpPort}/mcp`
495
- };
496
- added = true;
497
- }
498
- // Add Vercel MCP if this is a Vercel project (.vercel directory exists)
499
- // Vercel MCP uses OAuth authentication handled by the client (Claude Code)
500
- if (hasVercelProject() && !settings.mcpServers[MCP_NAMES.VERCEL]) {
501
- settings.mcpServers[MCP_NAMES.VERCEL] = {
502
- type: "http",
503
- url: VERCEL_MCP_URL
504
- };
505
- added = true;
506
- }
507
- // Write if we added anything
508
- if (added) {
473
+ return; // Nothing to clean up
474
+ }
475
+ let removed = false;
476
+ // Remove dev3000 MCP entry if it exists (MCP server removed)
477
+ if (settings.mcpServers[MCP_NAMES.DEV3000]) {
478
+ delete settings.mcpServers[MCP_NAMES.DEV3000];
479
+ removed = true;
480
+ }
481
+ // Write if we removed anything
482
+ if (removed) {
509
483
  writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
510
484
  }
511
485
  }
@@ -514,51 +488,29 @@ async function ensureMcpServers(mcpPort, _appPort, _enableChromeDevtools) {
514
488
  }
515
489
  }
516
490
  /**
517
- * Ensure MCP server configurations are added to project's .cursor/mcp.json
491
+ * Clean up old dev3000 MCP entries from project's .cursor/mcp.json
492
+ * MCP server has been removed - we now use CLI commands instead
518
493
  */
519
- async function ensureCursorMcpServers(mcpPort, _appPort, _enableChromeDevtools) {
494
+ async function ensureCursorMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
520
495
  try {
521
496
  const cursorDir = join(process.cwd(), ".cursor");
522
497
  const settingsPath = join(cursorDir, "mcp.json");
523
- // Ensure .cursor directory exists
524
- if (!existsSync(cursorDir)) {
525
- mkdirSync(cursorDir, { recursive: true });
498
+ if (!existsSync(settingsPath)) {
499
+ return; // Nothing to clean up
526
500
  }
527
- // Read or create settings
528
- let settings;
529
- if (existsSync(settingsPath)) {
530
- const settingsContent = readFileSync(settingsPath, "utf-8");
531
- settings = JSON.parse(settingsContent);
501
+ const settingsContent = readFileSync(settingsPath, "utf-8");
502
+ const settings = JSON.parse(settingsContent);
503
+ if (!settings.mcpServers) {
504
+ return; // Nothing to clean up
532
505
  }
533
- else {
534
- settings = {};
506
+ let removed = false;
507
+ // Remove dev3000 MCP entry if it exists (MCP server removed)
508
+ if (settings.mcpServers[MCP_NAMES.DEV3000]) {
509
+ delete settings.mcpServers[MCP_NAMES.DEV3000];
510
+ removed = true;
535
511
  }
536
- // Ensure mcpServers structure exists
537
- if (!settings.mcpServers) {
538
- settings.mcpServers = {};
539
- }
540
- let added = false;
541
- // Add dev3000 MCP server
542
- // NOTE: dev3000 now acts as an MCP orchestrator/gateway that internally
543
- // spawns and connects to chrome-devtools-mcp and next-devtools-mcp as stdio processes
544
- if (!settings.mcpServers[MCP_NAMES.DEV3000]) {
545
- settings.mcpServers[MCP_NAMES.DEV3000] = {
546
- type: "http",
547
- url: `http://localhost:${mcpPort}/mcp`
548
- };
549
- added = true;
550
- }
551
- // Add Vercel MCP if this is a Vercel project (.vercel directory exists)
552
- // Vercel MCP uses OAuth authentication handled by the client (Cursor)
553
- if (hasVercelProject() && !settings.mcpServers[MCP_NAMES.VERCEL]) {
554
- settings.mcpServers[MCP_NAMES.VERCEL] = {
555
- type: "http",
556
- url: VERCEL_MCP_URL
557
- };
558
- added = true;
559
- }
560
- // Write if we added anything
561
- if (added) {
512
+ // Write if we removed anything
513
+ if (removed) {
562
514
  writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
563
515
  }
564
516
  }
@@ -567,66 +519,29 @@ async function ensureCursorMcpServers(mcpPort, _appPort, _enableChromeDevtools)
567
519
  }
568
520
  }
569
521
  /**
570
- * Ensure MCP server configurations are added to project's opencode.json
571
- * OpenCode uses a different structure: "mcp" instead of "mcpServers"
572
- *
573
- * IMPORTANT: OpenCode has issues with "type": "remote" for HTTP MCP servers.
574
- * The workaround is to use "type": "local" with mcp-remote package to proxy requests.
575
- * See: https://github.com/sst/opencode/issues/1595
522
+ * Clean up old dev3000 MCP entries from project's opencode.json
523
+ * OpenCode uses "mcp" instead of "mcpServers"
524
+ * MCP server has been removed - we now use CLI commands instead
576
525
  */
577
- async function ensureOpenCodeMcpServers(mcpPort, _appPort, _enableChromeDevtools) {
526
+ async function ensureOpenCodeMcpServers(_mcpPort, _appPort, _enableChromeDevtools) {
578
527
  try {
579
528
  const settingsPath = join(process.cwd(), "opencode.json");
580
- // Read or create settings - OpenCode uses "mcp" not "mcpServers"
581
- let settings;
582
- if (existsSync(settingsPath)) {
583
- const settingsContent = readFileSync(settingsPath, "utf-8");
584
- settings = JSON.parse(settingsContent);
529
+ if (!existsSync(settingsPath)) {
530
+ return; // Nothing to clean up
585
531
  }
586
- else {
587
- settings = {};
588
- }
589
- // Ensure mcp structure exists
532
+ const settingsContent = readFileSync(settingsPath, "utf-8");
533
+ const settings = JSON.parse(settingsContent);
590
534
  if (!settings.mcp) {
591
- settings.mcp = {};
592
- }
593
- let changed = false;
594
- // Always update dev3000 MCP server config to ensure correct format
595
- // Try simple remote type first - no OAuth needed for local dev3000
596
- const expectedDev3000Config = {
597
- type: "remote",
598
- url: `http://localhost:${mcpPort}/mcp`,
599
- enabled: true
600
- };
601
- const currentDev3000 = settings.mcp[MCP_NAMES.DEV3000];
602
- if (!currentDev3000 ||
603
- currentDev3000.type !== expectedDev3000Config.type ||
604
- currentDev3000.url !== expectedDev3000Config.url) {
605
- settings.mcp[MCP_NAMES.DEV3000] = expectedDev3000Config;
606
- changed = true;
607
- }
608
- // Always update Vercel MCP if this is a Vercel project (.vercel directory exists)
609
- // Vercel MCP requires OAuth, so use OpenCode's native remote type with oauth: {}
610
- // This triggers OpenCode's built-in OAuth flow instead of mcp-remote
611
- // See: https://github.com/sst/opencode/issues/5444
612
- if (hasVercelProject()) {
613
- const expectedVercelConfig = {
614
- type: "remote",
615
- url: VERCEL_MCP_URL,
616
- oauth: {},
617
- enabled: true
618
- };
619
- const currentVercel = settings.mcp[MCP_NAMES.VERCEL];
620
- if (!currentVercel ||
621
- currentVercel.type !== expectedVercelConfig.type ||
622
- currentVercel.url !== expectedVercelConfig.url ||
623
- !currentVercel.oauth) {
624
- settings.mcp[MCP_NAMES.VERCEL] = expectedVercelConfig;
625
- changed = true;
626
- }
627
- }
628
- // Write if we changed anything
629
- if (changed) {
535
+ return; // Nothing to clean up
536
+ }
537
+ let removed = false;
538
+ // Remove dev3000 MCP entry if it exists (MCP server removed)
539
+ if (settings.mcp[MCP_NAMES.DEV3000]) {
540
+ delete settings.mcp[MCP_NAMES.DEV3000];
541
+ removed = true;
542
+ }
543
+ // Write if we removed anything
544
+ if (removed) {
630
545
  writeFileSync(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, "utf-8");
631
546
  }
632
547
  }
@@ -636,29 +551,31 @@ async function ensureOpenCodeMcpServers(mcpPort, _appPort, _enableChromeDevtools
636
551
  }
637
552
  /**
638
553
  * Ensure d3k skill is installed in project's .claude/skills/d3k/
639
- * This provides Claude with context about how to use d3k's MCP tools
554
+ * Claude Code reads from .claude/skills/ (must be real files, not symlinks)
640
555
  */
641
556
  async function ensureD3kSkill() {
642
557
  try {
558
+ const bundledSkillsDir = getBundledSkillsPath();
559
+ if (!bundledSkillsDir)
560
+ return;
561
+ const bundledSkillPath = join(bundledSkillsDir, "d3k", "SKILL.md");
562
+ if (!existsSync(bundledSkillPath))
563
+ return;
564
+ // Install directly to .claude/skills/d3k/ (where Claude Code looks)
643
565
  const skillDir = join(process.cwd(), ".claude", "skills", "d3k");
644
566
  const skillPath = join(skillDir, "SKILL.md");
645
- // Skip if skill already exists
567
+ // Check if already up to date
568
+ const bundledContent = readFileSync(bundledSkillPath, "utf-8");
646
569
  if (existsSync(skillPath)) {
647
- return;
648
- }
649
- // Find the bundled skill file
650
- const __filename = fileURLToPath(import.meta.url);
651
- const __dirname = dirname(__filename);
652
- const bundledSkillPath = join(__dirname, "skills", "d3k", "SKILL.md");
653
- // Check if bundled skill exists
654
- if (!existsSync(bundledSkillPath)) {
655
- return; // Skill not bundled, skip silently
570
+ const existingContent = readFileSync(skillPath, "utf-8");
571
+ if (existingContent === bundledContent) {
572
+ return; // Already up to date
573
+ }
656
574
  }
657
- // Create skill directory
575
+ // Copy skill to .claude/skills/d3k/
658
576
  if (!existsSync(skillDir)) {
659
577
  mkdirSync(skillDir, { recursive: true });
660
578
  }
661
- // Copy skill file to project
662
579
  copyFileSync(bundledSkillPath, skillPath);
663
580
  }
664
581
  catch (_error) {
@@ -819,14 +736,12 @@ function pruneOldLogs(baseDir) {
819
736
  }
820
737
  export class DevEnvironment {
821
738
  serverProcess = null;
822
- mcpServerProcess = null;
823
739
  cdpMonitor = null;
824
740
  screencastManager = null;
825
741
  logger;
826
742
  outputProcessor;
827
743
  options;
828
744
  screenshotDir;
829
- mcpPublicDir;
830
745
  pidFile;
831
746
  lockFile;
832
747
  spinner;
@@ -868,14 +783,12 @@ export class DevEnvironment {
868
783
  const currentFile = fileURLToPath(import.meta.url);
869
784
  packageRoot = dirname(dirname(currentFile));
870
785
  }
871
- // Always use MCP server's public directory for screenshots to ensure they're web-accessible
872
- // and avoid permission issues with /var/log paths
873
- this.screenshotDir = join(packageRoot, "mcp-server", "public", "screenshots");
874
- // Use project-specific PID and lock files to allow multiple projects to run simultaneously
786
+ // Store screenshots in project-specific directory for local access
875
787
  const projectName = getProjectName();
788
+ this.screenshotDir = join(getProjectDir(), "screenshots");
789
+ // Use project-specific PID and lock files to allow multiple projects to run simultaneously
876
790
  this.pidFile = join(tmpdir(), `dev3000-${projectName}.pid`);
877
791
  this.lockFile = join(tmpdir(), `dev3000-${projectName}.lock`);
878
- this.mcpPublicDir = join(packageRoot, "mcp-server", "public", "screenshots");
879
792
  // Read version - for compiled binaries, use injected version; otherwise read from package.json
880
793
  this.version = "0.0.0";
881
794
  // Check for compile-time injected version first
@@ -912,12 +825,18 @@ export class DevEnvironment {
912
825
  spinner: "dots",
913
826
  isEnabled: !options.tui // Disable spinner in TUI mode
914
827
  });
915
- // Ensure directories exist
916
- if (!existsSync(this.screenshotDir)) {
917
- mkdirSync(this.screenshotDir, { recursive: true });
828
+ // Ensure screenshot directory exists
829
+ try {
830
+ if (!existsSync(this.screenshotDir)) {
831
+ mkdirSync(this.screenshotDir, { recursive: true });
832
+ }
918
833
  }
919
- if (!existsSync(this.mcpPublicDir)) {
920
- mkdirSync(this.mcpPublicDir, { recursive: true });
834
+ catch {
835
+ // Fall back to temp directory if project dir isn't writable
836
+ this.screenshotDir = join(tmpdir(), "d3k-screenshots");
837
+ if (!existsSync(this.screenshotDir)) {
838
+ mkdirSync(this.screenshotDir, { recursive: true });
839
+ }
921
840
  }
922
841
  // Initialize project-specific D3K log file (clear for new session)
923
842
  this.initializeD3KLog();
@@ -926,12 +845,11 @@ export class DevEnvironment {
926
845
  // Clean up orphaned Playwright/Chrome processes from previous crashed sessions
927
846
  // This prevents "kill EPERM" errors when MCP tries to spawn new browsers
928
847
  await cleanupOrphanedPlaywrightProcesses((msg) => this.debugLog(msg));
929
- // Always kill any existing MCP server to ensure clean state
930
- // We ALWAYS try to kill, even if port appears free - there can be race conditions
931
- if (this.options.mcpPort) {
932
- this.debugLog(`Ensuring port ${this.options.mcpPort} is free (always kill)`);
933
- await this.killMcpServer();
934
- }
848
+ // MCP server removed - no longer need to kill
849
+ // if (this.options.mcpPort) {
850
+ // this.debugLog(`Ensuring port ${this.options.mcpPort} is free (always kill)`)
851
+ // await this.killMcpServer()
852
+ // }
935
853
  // Check if user explicitly set ports via CLI flags
936
854
  const userSetAppPort = this.options.userSetPort || false;
937
855
  // If user didn't set ports, find available ones first (before checking)
@@ -963,79 +881,7 @@ export class DevEnvironment {
963
881
  throw new Error(`Port ${this.options.port} is already in use. Please free the port and try again.`);
964
882
  }
965
883
  }
966
- // Now check MCP port availability (it should be free after killing)
967
- if (this.options.mcpPort) {
968
- const available = await isPortAvailable(this.options.mcpPort);
969
- if (!available) {
970
- if (this.spinner?.isSpinning) {
971
- this.spinner.fail(`Port ${this.options.mcpPort} is still in use after cleanup`);
972
- }
973
- if (!silent) {
974
- console.log(chalk.yellow(`💡 To force kill port ${this.options.mcpPort}, run: lsof -ti:${this.options.mcpPort} | xargs kill -9`));
975
- }
976
- if (this.tui) {
977
- await this.tui.shutdown();
978
- }
979
- throw new Error(`Port ${this.options.mcpPort} is still in use. Please free the port and try again.`);
980
- }
981
- }
982
- }
983
- async killMcpServer() {
984
- // In sandbox environments, skip lsof-based process cleanup
985
- if (isInSandbox()) {
986
- this.debugLog("killMcpServer skipped: Running in sandbox environment");
987
- return;
988
- }
989
- // Retry loop to ensure port is fully released
990
- const maxRetries = 5;
991
- for (let attempt = 1; attempt <= maxRetries; attempt++) {
992
- try {
993
- // First, get the PIDs of processes LISTENING on the port
994
- // Use -sTCP:LISTEN to only find servers, not clients connecting to the port
995
- // This prevents killing curl when it's polling for MCP server readiness
996
- const getPidsProcess = spawn("lsof", ["-ti", `:${this.options.mcpPort}`, "-sTCP:LISTEN"], {
997
- stdio: "pipe"
998
- });
999
- const pids = await new Promise((resolve, reject) => {
1000
- let output = "";
1001
- getPidsProcess.stdout?.on("data", (data) => {
1002
- output += data.toString();
1003
- });
1004
- getPidsProcess.on("error", (err) => reject(err));
1005
- getPidsProcess.on("exit", () => resolve(output.trim()));
1006
- });
1007
- if (!pids) {
1008
- this.debugLog(`Port ${this.options.mcpPort} is free (attempt ${attempt})`);
1009
- return; // Port is already free
1010
- }
1011
- this.debugLog(`Found MCP server processes (attempt ${attempt}): ${pids}`);
1012
- // Kill each PID individually with kill -9
1013
- const pidList = pids.split("\n").filter(Boolean);
1014
- for (const pid of pidList) {
1015
- await new Promise((resolve) => {
1016
- const killCmd = spawn("kill", ["-9", pid.trim()], { stdio: "ignore" });
1017
- killCmd.on("exit", (code) => {
1018
- this.debugLog(`Kill command for PID ${pid} exited with code ${code}`);
1019
- resolve();
1020
- });
1021
- });
1022
- }
1023
- // Give it time to fully release the port (longer waits, macOS can be slow)
1024
- const waitTime = 1000 * attempt;
1025
- this.debugLog(`Waiting ${waitTime}ms for port ${this.options.mcpPort} to be released...`);
1026
- await new Promise((resolve) => setTimeout(resolve, waitTime));
1027
- // Check if port is now free
1028
- const available = await isPortAvailable(this.options.mcpPort?.toString() ?? "");
1029
- if (available) {
1030
- this.debugLog(`Port ${this.options.mcpPort} released successfully`);
1031
- return;
1032
- }
1033
- }
1034
- catch (error) {
1035
- this.debugLog(`Error killing MCP server (attempt ${attempt}): ${error}`);
1036
- }
1037
- }
1038
- this.debugLog(`Warning: Port ${this.options.mcpPort} may still be in use after ${maxRetries} attempts`);
884
+ // MCP server removed - no longer need to check mcpPort availability
1039
885
  }
1040
886
  async checkProcessHealth() {
1041
887
  if (this.isShuttingDown)
@@ -1047,7 +893,8 @@ export class DevEnvironment {
1047
893
  return true;
1048
894
  }
1049
895
  try {
1050
- const ports = [this.options.port, this.options.mcpPort];
896
+ // Only check app port - MCP server has been removed
897
+ const ports = [this.options.port];
1051
898
  for (const port of ports) {
1052
899
  const result = await new Promise((resolve, reject) => {
1053
900
  const proc = spawn("lsof", ["-ti", `:${port}`], { stdio: "pipe" });
@@ -1226,9 +1073,8 @@ export class DevEnvironment {
1226
1073
  // Start user's dev server
1227
1074
  await this.tui.updateStatus("Starting your dev server...");
1228
1075
  await this.startServer();
1229
- // Start MCP server
1230
- await this.tui.updateStatus(`Starting ${this.options.commandName} MCP server...`);
1231
- await this.startMcpServer();
1076
+ // MCP server removed - using CLI commands instead
1077
+ // await this.startMcpServer()
1232
1078
  // Wait for servers to be ready
1233
1079
  await this.tui.updateStatus("Waiting for app...");
1234
1080
  const serverStarted = await this.waitForServer();
@@ -1241,8 +1087,8 @@ export class DevEnvironment {
1241
1087
  }
1242
1088
  // Update TUI with confirmed port (may have changed during server startup)
1243
1089
  this.tui.updateAppPort(this.options.port);
1244
- await this.tui.updateStatus("Waiting for MCP...");
1245
- await this.waitForMcpServer();
1090
+ // MCP server removed - using CLI commands instead
1091
+ // await this.waitForMcpServer()
1246
1092
  // Configure AI CLI integrations (both dev3000 and chrome-devtools MCPs)
1247
1093
  if (!this.options.serversOnly) {
1248
1094
  await this.tui.updateStatus("Configuring AI CLI integrations...");
@@ -1289,9 +1135,8 @@ export class DevEnvironment {
1289
1135
  // Start user's dev server
1290
1136
  this.spinner.text = "Starting your dev server...";
1291
1137
  await this.startServer();
1292
- // Start MCP server
1293
- this.spinner.text = `Starting ${this.options.commandName} MCP server...`;
1294
- await this.startMcpServer();
1138
+ // MCP server removed - using CLI commands instead
1139
+ // await this.startMcpServer()
1295
1140
  // Wait for servers to be ready
1296
1141
  this.spinner.text = "Waiting for app...";
1297
1142
  const serverStarted = await this.waitForServer();
@@ -1302,8 +1147,8 @@ export class DevEnvironment {
1302
1147
  console.error(chalk.yellow("Exiting without launching browser."));
1303
1148
  process.exit(1);
1304
1149
  }
1305
- this.spinner.text = "Waiting for MCP...";
1306
- await this.waitForMcpServer();
1150
+ // MCP server removed - using CLI commands instead
1151
+ // await this.waitForMcpServer()
1307
1152
  // Configure AI CLI integrations (both dev3000 and chrome-devtools MCPs)
1308
1153
  if (!this.options.serversOnly) {
1309
1154
  this.spinner.text = "Configuring AI CLI integrations...";
@@ -1340,8 +1185,7 @@ export class DevEnvironment {
1340
1185
  console.log(chalk.cyan(`Logs: ${this.options.logFile}`));
1341
1186
  console.log(chalk.cyan("☝️ Give this to an AI to auto debug and fix your app\n"));
1342
1187
  console.log(chalk.cyan(`🌐 Your App: ${this.serverProtocol}://localhost:${this.options.port}`));
1343
- console.log(chalk.cyan(`🤖 MCP Server: http://localhost:${this.options.mcpPort}`));
1344
- console.log(chalk.cyan(`📸 Visual Timeline: http://localhost:${this.options.mcpPort}/logs?project=${encodeURIComponent(projectName)}`));
1188
+ console.log(chalk.cyan(`🔧 CLI Tools: d3k fix, d3k crawl, d3k find-component`));
1345
1189
  if (this.options.serversOnly) {
1346
1190
  console.log(chalk.cyan("🖥️ Servers-only mode - use Chrome extension for browser monitoring"));
1347
1191
  }
@@ -1649,250 +1493,6 @@ export class DevEnvironment {
1649
1493
  // Ignore errors reading log file
1650
1494
  }
1651
1495
  }
1652
- async startMcpServer() {
1653
- this.debugLog("Starting MCP server setup");
1654
- // Note: MCP server cleanup now happens earlier in checkPortsAvailable()
1655
- // to ensure the port is free before we check availability
1656
- // Get the path to our bundled MCP server
1657
- // Handle both normal npm install and compiled binary cases
1658
- let mcpServerPath;
1659
- // Check if we're running from a compiled binary
1660
- // Compiled binaries have process.execPath pointing to the binary itself
1661
- const execPath = process.execPath;
1662
- const isCompiledBinary = execPath.includes("@d3k/darwin-") || execPath.includes("d3k-darwin-") || execPath.endsWith("/dev3000");
1663
- if (isCompiledBinary) {
1664
- // For compiled binaries, mcp-server is a sibling to the bin directory
1665
- // Structure: packages/d3k-darwin-arm64/bin/dev3000 -> packages/d3k-darwin-arm64/mcp-server
1666
- const binDir = dirname(execPath);
1667
- const packageDir = dirname(binDir);
1668
- mcpServerPath = join(packageDir, "mcp-server");
1669
- this.debugLog(`Compiled binary detected, MCP server path: ${mcpServerPath}`);
1670
- }
1671
- else {
1672
- // Normal npm install - mcp-server is in the package root
1673
- const currentFile = fileURLToPath(import.meta.url);
1674
- const packageRoot = dirname(dirname(currentFile)); // Go up from dist/ to package root
1675
- mcpServerPath = join(packageRoot, "mcp-server");
1676
- this.debugLog(`Standard install detected, MCP server path: ${mcpServerPath}`);
1677
- }
1678
- this.debugLog(`Initial MCP server path: ${mcpServerPath}`);
1679
- // For pnpm global installs, resolve symlinks to get the real path
1680
- if (existsSync(mcpServerPath)) {
1681
- try {
1682
- const realPath = realpathSync(mcpServerPath);
1683
- if (realPath !== mcpServerPath) {
1684
- this.debugLog(`MCP server path resolved from symlink: ${mcpServerPath} -> ${realPath}`);
1685
- mcpServerPath = realPath;
1686
- }
1687
- }
1688
- catch (e) {
1689
- // Error resolving path, continue with original
1690
- this.debugLog(`Error resolving real path: ${e}`);
1691
- }
1692
- }
1693
- this.debugLog(`Final MCP server path: ${mcpServerPath}`);
1694
- if (!existsSync(mcpServerPath)) {
1695
- throw new Error(`MCP server directory not found at ${mcpServerPath}`);
1696
- }
1697
- this.debugLog("MCP server directory found");
1698
- // Check if MCP server dependencies are installed, install if missing
1699
- // Detect global install by checking if the mcp-server path is outside the current working directory
1700
- // This handles both pnpm (.pnpm) and npm (/lib/node_modules/) global installs
1701
- const isGlobalInstall = mcpServerPath.includes(".pnpm") ||
1702
- mcpServerPath.includes("/lib/node_modules/") ||
1703
- !mcpServerPath.startsWith(process.cwd());
1704
- this.debugLog(`Is global install: ${isGlobalInstall}`);
1705
- let nodeModulesPath = join(mcpServerPath, "node_modules");
1706
- let actualWorkingDir = mcpServerPath;
1707
- this.debugLog(`Node modules path: ${nodeModulesPath}`);
1708
- if (isGlobalInstall) {
1709
- const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
1710
- nodeModulesPath = join(tmpDirPath, "node_modules");
1711
- actualWorkingDir = tmpDirPath;
1712
- // Update screenshot and MCP public directory to use the temp directory for global installs
1713
- this.screenshotDir = join(actualWorkingDir, "public", "screenshots");
1714
- this.mcpPublicDir = join(actualWorkingDir, "public", "screenshots");
1715
- if (!existsSync(this.mcpPublicDir)) {
1716
- mkdirSync(this.mcpPublicDir, { recursive: true });
1717
- }
1718
- }
1719
- // Check if .next build directory exists - if so, skip dependency installation
1720
- const nextBuildPath = join(mcpServerPath, ".next");
1721
- this.debugLog(`Checking for pre-built MCP server at: ${nextBuildPath}`);
1722
- let isPreBuilt = false;
1723
- if (existsSync(nextBuildPath)) {
1724
- this.debugLog("MCP server is pre-built (.next directory exists), skipping dependency installation");
1725
- isPreBuilt = true;
1726
- // For global installs with pre-built servers, we'll run from the original location
1727
- // No need to copy anything to temp directory
1728
- if (isGlobalInstall) {
1729
- this.debugLog("Global install with pre-built server - will run from original location");
1730
- actualWorkingDir = mcpServerPath;
1731
- // Still need to set up screenshot directory in temp
1732
- const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
1733
- this.screenshotDir = join(tmpDirPath, "public", "screenshots");
1734
- this.mcpPublicDir = join(tmpDirPath, "public", "screenshots");
1735
- if (!existsSync(this.mcpPublicDir)) {
1736
- mkdirSync(this.mcpPublicDir, { recursive: true });
1737
- }
1738
- }
1739
- }
1740
- else {
1741
- this.debugLog("No .next directory found, installing/updating MCP server dependencies");
1742
- this.debugLog(`WARNING: MCP server appears to not be pre-built. This is unexpected for a published package.`);
1743
- await this.installMcpServerDeps(mcpServerPath);
1744
- }
1745
- // Use version already read in constructor
1746
- // For global installs, only copy files if NOT pre-built
1747
- // Pre-built servers run from their original location
1748
- if (isGlobalInstall && actualWorkingDir !== mcpServerPath && !isPreBuilt) {
1749
- const requiredFiles = ["app", "public", "next.config.ts", "next-env.d.ts", "tsconfig.json", ".next"];
1750
- for (const file of requiredFiles) {
1751
- const srcPath = join(mcpServerPath, file);
1752
- const destPath = join(actualWorkingDir, file);
1753
- // Check if we need to copy (source exists and destination doesn't exist or source is newer)
1754
- if (existsSync(srcPath)) {
1755
- let shouldCopy = !existsSync(destPath);
1756
- // If destination exists, check if source is newer
1757
- if (!shouldCopy && existsSync(destPath)) {
1758
- const srcStat = lstatSync(srcPath);
1759
- const destStat = lstatSync(destPath);
1760
- shouldCopy = srcStat.mtime > destStat.mtime;
1761
- }
1762
- if (shouldCopy) {
1763
- // Remove existing destination if it exists
1764
- if (existsSync(destPath)) {
1765
- if (lstatSync(destPath).isDirectory()) {
1766
- cpSync(destPath, `${destPath}.bak`, { recursive: true });
1767
- cpSync(srcPath, destPath, { recursive: true, force: true });
1768
- }
1769
- else {
1770
- unlinkSync(destPath);
1771
- copyFileSync(srcPath, destPath);
1772
- }
1773
- }
1774
- else {
1775
- if (lstatSync(srcPath).isDirectory()) {
1776
- cpSync(srcPath, destPath, { recursive: true });
1777
- }
1778
- else {
1779
- copyFileSync(srcPath, destPath);
1780
- }
1781
- }
1782
- }
1783
- }
1784
- }
1785
- }
1786
- // Start the MCP server
1787
- this.debugLog(`MCP server working directory: ${actualWorkingDir}`);
1788
- this.debugLog(`MCP server port: ${this.options.mcpPort}`);
1789
- this.debugLog(`Screenshot directory: ${this.screenshotDir}`);
1790
- this.debugLog(`Is pre-built: ${isPreBuilt}`);
1791
- this.debugLog(`Is global install: ${isGlobalInstall}`);
1792
- let mcpCommand;
1793
- let mcpCwd = actualWorkingDir;
1794
- if (isGlobalInstall && isPreBuilt) {
1795
- // For global installs with pre-built servers, use the standalone server directly
1796
- // This avoids the turbopack runtime issues with npx
1797
- const serverJsPath = join(mcpServerPath, ".next", "standalone", "mcp-server", "server.js");
1798
- if (existsSync(serverJsPath)) {
1799
- // Use the standalone server directly
1800
- this.debugLog(`Global install with standalone server at ${serverJsPath}`);
1801
- mcpCommand = ["node", serverJsPath];
1802
- mcpCwd = dirname(serverJsPath);
1803
- }
1804
- else {
1805
- // Check for start-production.mjs script
1806
- const startProdScript = join(mcpServerPath, "start-production.mjs");
1807
- if (existsSync(startProdScript)) {
1808
- // Use the production script
1809
- this.debugLog(`Global install with start-production.mjs script`);
1810
- mcpCommand = ["node", startProdScript];
1811
- mcpCwd = mcpServerPath;
1812
- }
1813
- else {
1814
- // Fallback to finding Next.js binary
1815
- const dev3000NodeModules = join(mcpServerPath, "..", "..", "node_modules");
1816
- const nextBinPath = join(dev3000NodeModules, ".bin", "next");
1817
- this.debugLog(`Looking for Next.js at: ${nextBinPath}`);
1818
- if (existsSync(nextBinPath)) {
1819
- // Found Next.js in the dev3000 package
1820
- this.debugLog(`Global install with Next.js found at ${nextBinPath}`);
1821
- mcpCommand = [nextBinPath, "start"];
1822
- mcpCwd = mcpServerPath;
1823
- }
1824
- else {
1825
- // Fallback to npx with the exact version we built with
1826
- this.debugLog(`Global install with pre-built server - using npx next start`);
1827
- mcpCommand = ["npx", "--yes", "next@15.5.1-canary.30", "start"];
1828
- mcpCwd = mcpServerPath;
1829
- }
1830
- }
1831
- }
1832
- }
1833
- else {
1834
- // Non-global or non-pre-built: use package manager
1835
- const packageManagerForRun = detectPackageManagerForRun();
1836
- this.debugLog(`Using package manager: ${packageManagerForRun}`);
1837
- mcpCommand = [packageManagerForRun, "run", "start"];
1838
- mcpCwd = actualWorkingDir;
1839
- }
1840
- this.debugLog(`MCP server command: ${mcpCommand.join(" ")}`);
1841
- this.debugLog(`MCP server cwd: ${mcpCwd}`);
1842
- // Get CDP URL for MCP orchestration
1843
- const cdpUrl = this.cdpMonitor?.getCdpUrl() || null;
1844
- // Start MCP server as a true background singleton process
1845
- this.mcpServerProcess = spawn(mcpCommand[0], mcpCommand.slice(1), {
1846
- stdio: ["ignore", "pipe", "pipe"],
1847
- detached: true, // Run independently of parent process
1848
- cwd: mcpCwd,
1849
- env: {
1850
- ...process.env,
1851
- PORT: this.options.mcpPort,
1852
- LOG_FILE_PATH: this.options.logFile, // Pass log file path to MCP server
1853
- DEV3000_VERSION: this.version, // Pass version to MCP server
1854
- SCREENSHOT_DIR: this.screenshotDir, // Pass screenshot directory for global installs
1855
- CDP_URL: cdpUrl || "" // Pass CDP URL for chrome-devtools MCP orchestration
1856
- }
1857
- });
1858
- // Unref the process so it continues running after parent exits
1859
- this.mcpServerProcess.unref();
1860
- this.debugLog("MCP server process spawned as singleton background service");
1861
- // Log MCP server output to separate file for debugging
1862
- const mcpLogFile = join(dirname(this.options.logFile), "mcp.log");
1863
- writeFileSync(mcpLogFile, ""); // Clear the file
1864
- // In debug mode, output the MCP log file path
1865
- if (this.options.debug) {
1866
- console.log(chalk.gray(`[DEBUG] MCP server logs: ${mcpLogFile}`));
1867
- }
1868
- this.mcpServerProcess.stdout?.on("data", (data) => {
1869
- const message = data.toString().trim();
1870
- if (message) {
1871
- const timestamp = new Date().toISOString();
1872
- appendFileSync(mcpLogFile, `[${timestamp}] [MCP-STDOUT] ${message}\n`);
1873
- }
1874
- });
1875
- this.mcpServerProcess.stderr?.on("data", (data) => {
1876
- const message = data.toString().trim();
1877
- if (message) {
1878
- const timestamp = new Date().toISOString();
1879
- appendFileSync(mcpLogFile, `[${timestamp}] [MCP-STDERR] ${message}\n`);
1880
- // Only show critical errors in stdout for debugging
1881
- // Exclude MCP Orchestrator connection errors (they're expected and non-critical)
1882
- if ((message.includes("FATAL") || message.includes("Error:")) && !message.includes("[MCP Orchestrator]")) {
1883
- console.error(chalk.red("[ERROR]"), message);
1884
- }
1885
- }
1886
- });
1887
- this.mcpServerProcess.on("exit", (code) => {
1888
- this.debugLog(`MCP server process exited with code ${code}`);
1889
- // Only show exit messages for unexpected failures, not restarts
1890
- if (code !== 0 && code !== null) {
1891
- this.logger.log("server", `MCP server process exited with code ${code}`);
1892
- }
1893
- });
1894
- this.debugLog("MCP server event handlers setup complete");
1895
- }
1896
1496
  async waitForServer() {
1897
1497
  const maxAttempts = 30;
1898
1498
  let attempts = 0;
@@ -1943,172 +1543,6 @@ export class DevEnvironment {
1943
1543
  this.debugLog(`Server readiness check timed out after ${totalTime}ms (${maxAttempts} attempts)`);
1944
1544
  return false;
1945
1545
  }
1946
- detectPackageManagerInDir(dir) {
1947
- if (existsSync(join(dir, "bun.lockb")))
1948
- return "bun";
1949
- if (existsSync(join(dir, "pnpm-lock.yaml")))
1950
- return "pnpm";
1951
- if (existsSync(join(dir, "yarn.lock")))
1952
- return "yarn";
1953
- if (existsSync(join(dir, "package-lock.json")))
1954
- return "npm";
1955
- return "npm"; // fallback
1956
- }
1957
- async installMcpServerDeps(mcpServerPath) {
1958
- return new Promise((resolve, reject) => {
1959
- // For global installs, we need to install to a writable location
1960
- // Detect global install by checking if the path is outside the current working directory
1961
- const isGlobalInstall = mcpServerPath.includes(".pnpm") ||
1962
- mcpServerPath.includes("/lib/node_modules/") ||
1963
- !mcpServerPath.startsWith(process.cwd());
1964
- let workingDir = mcpServerPath;
1965
- if (isGlobalInstall) {
1966
- // Create a writable copy in temp directory for global installs
1967
- const tmpDirPath = join(tmpdir(), "dev3000-mcp-deps");
1968
- // Ensure tmp directory exists
1969
- if (!existsSync(tmpDirPath)) {
1970
- mkdirSync(tmpDirPath, { recursive: true });
1971
- }
1972
- // Always copy package.json to temp directory to ensure it's up to date
1973
- const tmpPackageJson = join(tmpDirPath, "package.json");
1974
- const sourcePackageJson = join(mcpServerPath, "package.json");
1975
- // Debug: Check if source package.json exists
1976
- if (!existsSync(sourcePackageJson)) {
1977
- const errorDetails = [
1978
- `ERROR: package.json not found at ${sourcePackageJson}`,
1979
- `MCP server path: ${mcpServerPath}`,
1980
- `Contents of MCP server directory:`
1981
- ];
1982
- try {
1983
- const files = readdirSync(mcpServerPath);
1984
- files.forEach((file) => {
1985
- errorDetails.push(` - ${file}`);
1986
- });
1987
- }
1988
- catch (e) {
1989
- errorDetails.push(` Error listing directory: ${e}`);
1990
- }
1991
- // Additional debug: Check parent directories
1992
- errorDetails.push(`Parent directory: ${dirname(mcpServerPath)}`);
1993
- try {
1994
- const parentFiles = readdirSync(dirname(mcpServerPath));
1995
- parentFiles.forEach((file) => {
1996
- errorDetails.push(` Parent dir file: ${file}`);
1997
- });
1998
- }
1999
- catch (e) {
2000
- errorDetails.push(` Error listing parent directory: ${e}`);
2001
- }
2002
- // Log all error details
2003
- errorDetails.forEach((detail) => {
2004
- this.debugLog(detail);
2005
- });
2006
- reject(new Error(`MCP server package.json not found at ${sourcePackageJson}`));
2007
- return;
2008
- }
2009
- copyFileSync(sourcePackageJson, tmpPackageJson);
2010
- workingDir = tmpDirPath;
2011
- }
2012
- // Detect package manager from MCP server directory, not current directory
2013
- const packageManager = this.detectPackageManagerInDir(mcpServerPath);
2014
- // Package manager specific install args to include devDependencies
2015
- const installArgs = packageManager === "pnpm"
2016
- ? ["install", "--prod=false"] // Install both prod and dev dependencies
2017
- : packageManager === "bun"
2018
- ? ["install", "--dev"] // bun syntax
2019
- : ["install", "--include=dev"]; // npm/yarn syntax
2020
- const fullCommand = `${packageManager} ${installArgs.join(" ")}`;
2021
- if (this.options.debug) {
2022
- console.log(`[DEBUG] Installing MCP server dependencies...`);
2023
- console.log(`[DEBUG] Working directory: ${workingDir}`);
2024
- console.log(`[DEBUG] Package manager detected: ${packageManager}`);
2025
- console.log(`[DEBUG] Command: ${fullCommand}`);
2026
- console.log(`[DEBUG] Is global install: ${isGlobalInstall}`);
2027
- }
2028
- const installStartTime = Date.now();
2029
- const installProcess = spawn(packageManager, installArgs, {
2030
- stdio: ["ignore", "pipe", "pipe"],
2031
- cwd: workingDir
2032
- });
2033
- // Add timeout (3 minutes)
2034
- const timeout = setTimeout(() => {
2035
- if (this.options.debug) {
2036
- console.log(`[DEBUG] Installation timed out after 3 minutes`);
2037
- }
2038
- installProcess.kill("SIGKILL");
2039
- reject(new Error("MCP server dependency installation timed out after 3 minutes"));
2040
- }, 3 * 60 * 1000);
2041
- // Capture output for debugging, but suppress for normal operation
2042
- let debugOutput = "";
2043
- let debugErrors = "";
2044
- installProcess.stdout?.on("data", (data) => {
2045
- const text = data.toString();
2046
- if (this.options.debug) {
2047
- debugOutput += text;
2048
- }
2049
- });
2050
- installProcess.stderr?.on("data", (data) => {
2051
- const text = data.toString();
2052
- if (this.options.debug) {
2053
- debugErrors += text;
2054
- }
2055
- });
2056
- installProcess.on("exit", (code) => {
2057
- clearTimeout(timeout);
2058
- const installTime = Date.now() - installStartTime;
2059
- if (this.options.debug) {
2060
- console.log(`[DEBUG] Installation completed in ${installTime}ms with exit code: ${code}`);
2061
- if (debugOutput) {
2062
- console.log(`[DEBUG] stdout:`, debugOutput.trim());
2063
- }
2064
- if (debugErrors) {
2065
- console.log(`[DEBUG] stderr:`, debugErrors.trim());
2066
- }
2067
- }
2068
- if (code === 0) {
2069
- resolve();
2070
- }
2071
- else {
2072
- const errorMsg = `MCP server dependency installation failed with exit code ${code}`;
2073
- const fullError = this.options.debug && debugErrors ? `${errorMsg}\nstderr: ${debugErrors.trim()}` : errorMsg;
2074
- reject(new Error(fullError));
2075
- }
2076
- });
2077
- installProcess.on("error", (error) => {
2078
- clearTimeout(timeout);
2079
- reject(new Error(`Failed to start MCP server dependency installation: ${error.message}`));
2080
- });
2081
- });
2082
- }
2083
- async waitForMcpServer() {
2084
- const maxAttempts = 30;
2085
- let attempts = 0;
2086
- while (attempts < maxAttempts) {
2087
- try {
2088
- // Test the actual MCP endpoint
2089
- const response = await fetch(`http://localhost:${this.options.mcpPort}`, {
2090
- method: "HEAD",
2091
- signal: AbortSignal.timeout(2000)
2092
- });
2093
- this.debugLog(`MCP server health check: ${response.status}`);
2094
- if (response.status === 500) {
2095
- const errorText = await response.text();
2096
- this.debugLog(`MCP server 500 error: ${errorText}`);
2097
- }
2098
- if (response.ok || response.status === 404) {
2099
- // 404 is OK - means server is responding
2100
- return;
2101
- }
2102
- }
2103
- catch (error) {
2104
- this.debugLog(`MCP server not ready (attempt ${attempts}): ${error}`);
2105
- }
2106
- attempts++;
2107
- await new Promise((resolve) => setTimeout(resolve, 1000));
2108
- }
2109
- this.debugLog("MCP server health check failed, terminating");
2110
- throw new Error(`MCP server failed to start after ${maxAttempts} seconds. Check the logs for errors.`);
2111
- }
2112
1546
  initializeD3KLog() {
2113
1547
  try {
2114
1548
  const projectDir = getProjectDir();
@@ -2165,8 +1599,8 @@ export class DevEnvironment {
2165
1599
  if (!existsSync(this.options.profileDir)) {
2166
1600
  mkdirSync(this.options.profileDir, { recursive: true });
2167
1601
  }
2168
- // Initialize CDP monitor with enhanced logging - use MCP public directory for screenshots
2169
- this.cdpMonitor = new CDPMonitor(this.options.profileDir, this.mcpPublicDir, (_source, message) => {
1602
+ // Initialize CDP monitor with enhanced logging
1603
+ this.cdpMonitor = new CDPMonitor(this.options.profileDir, this.screenshotDir, (_source, message) => {
2170
1604
  this.logger.log("browser", message);
2171
1605
  }, this.options.debug, this.options.browser, this.options.pluginReactScan, this.options.port, // App server port to monitor
2172
1606
  this.options.mcpPort, // MCP server port to ignore