dev3000 0.0.186 → 0.0.187

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 (235) hide show
  1. package/README.md +102 -13
  2. package/bin/dev3000 +1 -2
  3. package/dist/cdp-monitor.d.ts +7 -12
  4. package/dist/cdp-monitor.d.ts.map +1 -1
  5. package/dist/cdp-monitor.js +86 -170
  6. package/dist/cdp-monitor.js.map +1 -1
  7. package/dist/cli.d.ts +1 -2
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +1 -1369
  10. package/dist/cli.js.map +1 -1
  11. package/dist/index.d.ts +1 -2
  12. package/dist/index.d.ts.map +1 -1
  13. package/dist/index.js +0 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/portless.d.ts +3 -0
  16. package/dist/portless.d.ts.map +1 -1
  17. package/dist/portless.js +10 -0
  18. package/dist/portless.js.map +1 -1
  19. package/dist/runtime/bridge.d.ts +26 -0
  20. package/dist/runtime/bridge.d.ts.map +1 -0
  21. package/dist/runtime/bridge.js +340 -0
  22. package/dist/runtime/bridge.js.map +1 -0
  23. package/dist/runtime/chrome-isolation.d.ts +16 -0
  24. package/dist/runtime/chrome-isolation.d.ts.map +1 -0
  25. package/dist/runtime/chrome-isolation.js +97 -0
  26. package/dist/runtime/chrome-isolation.js.map +1 -0
  27. package/dist/runtime/cli.js +116 -26
  28. package/dist/runtime/cli.js.map +1 -1
  29. package/dist/runtime/event-views.d.ts +21 -0
  30. package/dist/runtime/event-views.d.ts.map +1 -0
  31. package/dist/runtime/event-views.js +53 -0
  32. package/dist/runtime/event-views.js.map +1 -0
  33. package/dist/runtime/harness.d.ts +16 -0
  34. package/dist/runtime/harness.d.ts.map +1 -0
  35. package/dist/runtime/harness.js +68 -0
  36. package/dist/runtime/harness.js.map +1 -0
  37. package/dist/runtime/local-proxy.d.ts +12 -0
  38. package/dist/runtime/local-proxy.d.ts.map +1 -0
  39. package/dist/runtime/local-proxy.js +114 -0
  40. package/dist/runtime/local-proxy.js.map +1 -0
  41. package/dist/runtime/runtime.d.ts +14 -0
  42. package/dist/runtime/runtime.d.ts.map +1 -1
  43. package/dist/runtime/runtime.js +80 -19
  44. package/dist/runtime/runtime.js.map +1 -1
  45. package/dist/runtime/sandbox-agent.d.ts +6 -0
  46. package/dist/runtime/sandbox-agent.d.ts.map +1 -0
  47. package/dist/runtime/sandbox-agent.js +35 -0
  48. package/dist/runtime/sandbox-agent.js.map +1 -0
  49. package/dist/runtime/sandbox-command.d.ts +9 -0
  50. package/dist/runtime/sandbox-command.d.ts.map +1 -0
  51. package/dist/runtime/sandbox-command.js +101 -0
  52. package/dist/runtime/sandbox-command.js.map +1 -0
  53. package/dist/runtime/sandbox-proxy.d.ts +11 -0
  54. package/dist/runtime/sandbox-proxy.d.ts.map +1 -0
  55. package/dist/runtime/sandbox-proxy.js +123 -0
  56. package/dist/runtime/sandbox-proxy.js.map +1 -0
  57. package/dist/runtime/sandbox-sessions.d.ts +31 -0
  58. package/dist/runtime/sandbox-sessions.d.ts.map +1 -0
  59. package/dist/runtime/sandbox-sessions.js +102 -0
  60. package/dist/runtime/sandbox-sessions.js.map +1 -0
  61. package/dist/runtime/sandbox.d.ts +21 -0
  62. package/dist/runtime/sandbox.d.ts.map +1 -0
  63. package/dist/runtime/sandbox.js +481 -0
  64. package/dist/runtime/sandbox.js.map +1 -0
  65. package/dist/utils/agent-browser.d.ts +2 -120
  66. package/dist/utils/agent-browser.d.ts.map +1 -1
  67. package/dist/utils/agent-browser.js +13 -376
  68. package/dist/utils/agent-browser.js.map +1 -1
  69. package/dist/utils/project-name.d.ts +2 -2
  70. package/dist/utils/project-name.js +2 -2
  71. package/package.json +13 -22
  72. package/skills/d3k/SKILL.md +23 -1
  73. package/dist/commands/crawl.d.ts +0 -12
  74. package/dist/commands/crawl.d.ts.map +0 -1
  75. package/dist/commands/crawl.js +0 -101
  76. package/dist/commands/crawl.js.map +0 -1
  77. package/dist/commands/errors.d.ts +0 -14
  78. package/dist/commands/errors.d.ts.map +0 -1
  79. package/dist/commands/errors.js +0 -246
  80. package/dist/commands/errors.js.map +0 -1
  81. package/dist/commands/fix.d.ts +0 -13
  82. package/dist/commands/fix.d.ts.map +0 -1
  83. package/dist/commands/fix.js +0 -219
  84. package/dist/commands/fix.js.map +0 -1
  85. package/dist/commands/logs.d.ts +0 -13
  86. package/dist/commands/logs.d.ts.map +0 -1
  87. package/dist/commands/logs.js +0 -146
  88. package/dist/commands/logs.js.map +0 -1
  89. package/dist/commands/resume.d.ts +0 -11
  90. package/dist/commands/resume.d.ts.map +0 -1
  91. package/dist/commands/resume.js +0 -75
  92. package/dist/commands/resume.js.map +0 -1
  93. package/dist/commands/status.d.ts +0 -21
  94. package/dist/commands/status.d.ts.map +0 -1
  95. package/dist/commands/status.js +0 -44
  96. package/dist/commands/status.js.map +0 -1
  97. package/dist/components/AgentSelector.d.ts +0 -12
  98. package/dist/components/AgentSelector.d.ts.map +0 -1
  99. package/dist/components/AgentSelector.js +0 -99
  100. package/dist/components/AgentSelector.js.map +0 -1
  101. package/dist/components/PackageSelector.d.ts +0 -13
  102. package/dist/components/PackageSelector.d.ts.map +0 -1
  103. package/dist/components/PackageSelector.js +0 -79
  104. package/dist/components/PackageSelector.js.map +0 -1
  105. package/dist/constants/log-colors.d.ts +0 -43
  106. package/dist/constants/log-colors.d.ts.map +0 -1
  107. package/dist/constants/log-colors.js +0 -59
  108. package/dist/constants/log-colors.js.map +0 -1
  109. package/dist/dev-environment.d.ts +0 -187
  110. package/dist/dev-environment.d.ts.map +0 -1
  111. package/dist/dev-environment.js +0 -2248
  112. package/dist/dev-environment.js.map +0 -1
  113. package/dist/screencast-manager.d.ts +0 -78
  114. package/dist/screencast-manager.d.ts.map +0 -1
  115. package/dist/screencast-manager.js +0 -529
  116. package/dist/screencast-manager.js.map +0 -1
  117. package/dist/services/parsers/error-detectors/base.d.ts +0 -24
  118. package/dist/services/parsers/error-detectors/base.d.ts.map +0 -1
  119. package/dist/services/parsers/error-detectors/base.js +0 -44
  120. package/dist/services/parsers/error-detectors/base.js.map +0 -1
  121. package/dist/services/parsers/error-detectors/index.d.ts +0 -6
  122. package/dist/services/parsers/error-detectors/index.d.ts.map +0 -1
  123. package/dist/services/parsers/error-detectors/index.js +0 -6
  124. package/dist/services/parsers/error-detectors/index.js.map +0 -1
  125. package/dist/services/parsers/error-detectors/nextjs.d.ts +0 -9
  126. package/dist/services/parsers/error-detectors/nextjs.d.ts.map +0 -1
  127. package/dist/services/parsers/error-detectors/nextjs.js +0 -52
  128. package/dist/services/parsers/error-detectors/nextjs.js.map +0 -1
  129. package/dist/services/parsers/index.d.ts +0 -9
  130. package/dist/services/parsers/index.d.ts.map +0 -1
  131. package/dist/services/parsers/index.js +0 -10
  132. package/dist/services/parsers/index.js.map +0 -1
  133. package/dist/services/parsers/log-parsers/base.d.ts +0 -26
  134. package/dist/services/parsers/log-parsers/base.d.ts.map +0 -1
  135. package/dist/services/parsers/log-parsers/base.js +0 -6
  136. package/dist/services/parsers/log-parsers/base.js.map +0 -1
  137. package/dist/services/parsers/log-parsers/index.d.ts +0 -6
  138. package/dist/services/parsers/log-parsers/index.d.ts.map +0 -1
  139. package/dist/services/parsers/log-parsers/index.js +0 -5
  140. package/dist/services/parsers/log-parsers/index.js.map +0 -1
  141. package/dist/services/parsers/log-parsers/standard.d.ts +0 -9
  142. package/dist/services/parsers/log-parsers/standard.d.ts.map +0 -1
  143. package/dist/services/parsers/log-parsers/standard.js +0 -17
  144. package/dist/services/parsers/log-parsers/standard.js.map +0 -1
  145. package/dist/services/parsers/output-processor.d.ts +0 -39
  146. package/dist/services/parsers/output-processor.d.ts.map +0 -1
  147. package/dist/services/parsers/output-processor.js +0 -54
  148. package/dist/services/parsers/output-processor.js.map +0 -1
  149. package/dist/skills/d3k/internal-skill.md +0 -32
  150. package/dist/skills/index.d.ts +0 -54
  151. package/dist/skills/index.d.ts.map +0 -1
  152. package/dist/skills/index.js +0 -214
  153. package/dist/skills/index.js.map +0 -1
  154. package/dist/src/tui-interface-impl.tsx +0 -644
  155. package/dist/tui-interface-impl.d.ts +0 -30
  156. package/dist/tui-interface-impl.d.ts.map +0 -1
  157. package/dist/tui-interface-impl.js +0 -403
  158. package/dist/tui-interface-impl.js.map +0 -1
  159. package/dist/tui-interface-opentui.d.ts +0 -33
  160. package/dist/tui-interface-opentui.d.ts.map +0 -1
  161. package/dist/tui-interface-opentui.js +0 -1128
  162. package/dist/tui-interface-opentui.js.map +0 -1
  163. package/dist/tui-interface.d.ts +0 -38
  164. package/dist/tui-interface.d.ts.map +0 -1
  165. package/dist/tui-interface.js +0 -84
  166. package/dist/tui-interface.js.map +0 -1
  167. package/dist/utils/agent-detection.d.ts +0 -17
  168. package/dist/utils/agent-detection.d.ts.map +0 -1
  169. package/dist/utils/agent-detection.js +0 -81
  170. package/dist/utils/agent-detection.js.map +0 -1
  171. package/dist/utils/agent-selection.d.ts +0 -25
  172. package/dist/utils/agent-selection.d.ts.map +0 -1
  173. package/dist/utils/agent-selection.js +0 -73
  174. package/dist/utils/agent-selection.js.map +0 -1
  175. package/dist/utils/browser-command-argv.d.ts +0 -19
  176. package/dist/utils/browser-command-argv.d.ts.map +0 -1
  177. package/dist/utils/browser-command-argv.js +0 -149
  178. package/dist/utils/browser-command-argv.js.map +0 -1
  179. package/dist/utils/build-version.d.ts +0 -2
  180. package/dist/utils/build-version.d.ts.map +0 -1
  181. package/dist/utils/build-version.js +0 -8
  182. package/dist/utils/build-version.js.map +0 -1
  183. package/dist/utils/cli-options.d.ts +0 -5
  184. package/dist/utils/cli-options.d.ts.map +0 -1
  185. package/dist/utils/cli-options.js +0 -36
  186. package/dist/utils/cli-options.js.map +0 -1
  187. package/dist/utils/d3k-dir.d.ts +0 -3
  188. package/dist/utils/d3k-dir.d.ts.map +0 -1
  189. package/dist/utils/d3k-dir.js +0 -12
  190. package/dist/utils/d3k-dir.js.map +0 -1
  191. package/dist/utils/d3k-skill-installer.d.ts +0 -4
  192. package/dist/utils/d3k-skill-installer.d.ts.map +0 -1
  193. package/dist/utils/d3k-skill-installer.js +0 -30
  194. package/dist/utils/d3k-skill-installer.js.map +0 -1
  195. package/dist/utils/log-filename.d.ts +0 -30
  196. package/dist/utils/log-filename.d.ts.map +0 -1
  197. package/dist/utils/log-filename.js +0 -60
  198. package/dist/utils/log-filename.js.map +0 -1
  199. package/dist/utils/project-detector.d.ts +0 -25
  200. package/dist/utils/project-detector.d.ts.map +0 -1
  201. package/dist/utils/project-detector.js +0 -153
  202. package/dist/utils/project-detector.js.map +0 -1
  203. package/dist/utils/project-metadata.d.ts +0 -4
  204. package/dist/utils/project-metadata.d.ts.map +0 -1
  205. package/dist/utils/project-metadata.js +0 -48
  206. package/dist/utils/project-metadata.js.map +0 -1
  207. package/dist/utils/repo-validate.d.ts +0 -2
  208. package/dist/utils/repo-validate.d.ts.map +0 -1
  209. package/dist/utils/repo-validate.js +0 -10
  210. package/dist/utils/repo-validate.js.map +0 -1
  211. package/dist/utils/session.d.ts +0 -19
  212. package/dist/utils/session.d.ts.map +0 -1
  213. package/dist/utils/session.js +0 -65
  214. package/dist/utils/session.js.map +0 -1
  215. package/dist/utils/skill-installer.d.ts +0 -65
  216. package/dist/utils/skill-installer.d.ts.map +0 -1
  217. package/dist/utils/skill-installer.js +0 -265
  218. package/dist/utils/skill-installer.js.map +0 -1
  219. package/dist/utils/timestamp.d.ts +0 -20
  220. package/dist/utils/timestamp.d.ts.map +0 -1
  221. package/dist/utils/timestamp.js +0 -57
  222. package/dist/utils/timestamp.js.map +0 -1
  223. package/dist/utils/tmux-helpers.d.ts +0 -36
  224. package/dist/utils/tmux-helpers.d.ts.map +0 -1
  225. package/dist/utils/tmux-helpers.js +0 -118
  226. package/dist/utils/tmux-helpers.js.map +0 -1
  227. package/dist/utils/user-config.d.ts +0 -12
  228. package/dist/utils/user-config.d.ts.map +0 -1
  229. package/dist/utils/user-config.js +0 -46
  230. package/dist/utils/user-config.js.map +0 -1
  231. package/dist/utils/version-check.d.ts +0 -52
  232. package/dist/utils/version-check.d.ts.map +0 -1
  233. package/dist/utils/version-check.js +0 -304
  234. package/dist/utils/version-check.js.map +0 -1
  235. package/src/tui-interface-impl.tsx +0 -644
package/dist/cli.js CHANGED
@@ -1,1370 +1,2 @@
1
- #!/usr/bin/env bun
2
- function getRunnablePath(searchPath) {
3
- if (!existsSync(searchPath)) {
4
- return null;
5
- }
6
- if (process.platform === "win32") {
7
- return searchPath;
8
- }
9
- try {
10
- accessSync(searchPath, constants.X_OK);
11
- return searchPath;
12
- }
13
- catch {
14
- try {
15
- chmodSync(searchPath, 0o755);
16
- accessSync(searchPath, constants.X_OK);
17
- return searchPath;
18
- }
19
- catch {
20
- return null;
21
- }
22
- }
23
- }
24
- function ensureCommandPath(env) {
25
- if (!env.PATH || env.PATH === "") {
26
- env.PATH = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin";
27
- }
28
- }
29
- function findAgentBrowser() {
30
- const os = process.platform === "darwin" ? "darwin" : process.platform === "win32" ? "win32" : "linux";
31
- const arch = process.arch === "arm64" ? "arm64" : "x64";
32
- const nativeName = `agent-browser-${os}-${arch}`;
33
- const platformPkg = `${os}-${arch}`;
34
- const cwd = process.cwd();
35
- const home = homedir();
36
- const searchPaths = [
37
- join(home, ".bun", "install", "global", "node_modules", "@d3k", platformPkg, "node_modules", ".bin", nativeName),
38
- join(home, ".bun", "install", "global", "node_modules", "agent-browser", "bin", nativeName),
39
- join(home, ".bun", "install", "global", "node_modules", "dev3000", "node_modules", ".bin", nativeName),
40
- join(cwd, "node_modules", ".bin", nativeName),
41
- join(cwd, "node_modules", "agent-browser", "bin", nativeName),
42
- join(home, ".bun", "install", "global", "node_modules", "@d3k", platformPkg, "node_modules", ".bin", "agent-browser"),
43
- join(home, ".bun", "install", "global", "node_modules", "dev3000", "node_modules", ".bin", "agent-browser"),
44
- join(home, ".bun", "install", "global", "node_modules", ".bin", "agent-browser"),
45
- join(home, ".bun", "install", "global", "node_modules", "agent-browser", "bin", "agent-browser.js"),
46
- join(cwd, "node_modules", ".bin", "agent-browser"),
47
- join(cwd, "node_modules", "agent-browser", "bin", "agent-browser.js")
48
- ];
49
- const globalNodeModules = [
50
- join("/usr", "local", "lib", "node_modules"),
51
- join("/opt", "homebrew", "lib", "node_modules")
52
- ];
53
- for (const root of globalNodeModules) {
54
- searchPaths.push(join(root, "dev3000", "node_modules", ".bin", nativeName));
55
- searchPaths.push(join(root, "dev3000", "node_modules", ".bin", "agent-browser"));
56
- searchPaths.push(join(root, "agent-browser", "bin", nativeName));
57
- searchPaths.push(join(root, "agent-browser", "bin", "agent-browser.js"));
58
- }
59
- for (const p of searchPaths) {
60
- const runnablePath = getRunnablePath(p);
61
- if (runnablePath)
62
- return runnablePath;
63
- }
64
- return "agent-browser";
65
- }
66
- function getSessionCdpPort() {
67
- const session = findCurrentSession();
68
- return getSessionCdpPortForSession(session);
69
- }
70
- function getSessionCdpPortForSession(session) {
71
- const cdpUrl = session?.cdpUrl;
72
- if (!cdpUrl) {
73
- return null;
74
- }
75
- try {
76
- const parsed = new URL(cdpUrl);
77
- if (/^\d+$/.test(parsed.port)) {
78
- return parsed.port;
79
- }
80
- }
81
- catch {
82
- const match = cdpUrl.match(/:(\d+)/);
83
- if (match) {
84
- return match[1];
85
- }
86
- }
87
- return null;
88
- }
89
- function printUnsafeAgentBrowserOpenError(reason, hasActiveSession) {
90
- const sessionHint = hasActiveSession
91
- ? "The active d3k session has no usable d3k-managed browser. This usually means d3k was started with --servers-only, browser startup failed, or the session is stale."
92
- : "No active d3k session with a managed browser was found.";
93
- console.error([
94
- "Refusing to run `d3k agent-browser open` because it would create or use an agent-browser-managed browser instead of d3k's active browser.",
95
- "",
96
- reason,
97
- sessionHint,
98
- "",
99
- "For OAuth/auth debugging, start d3k normally so it launches the app and browser together:",
100
- ' d3k --command "<dev command>" --port <port> --startup-timeout <seconds> --app-url "<url>"',
101
- "",
102
- "Then navigate the d3k-managed browser with:",
103
- ' d3k agent-browser --require-d3k-browser open "<url>"',
104
- "",
105
- "For an explicit non-auth standalone/fresh-profile browser, pass:",
106
- ' d3k agent-browser --allow-new-browser open "<url>"'
107
- ].join("\n"));
108
- }
109
- function runLocalBrowserTool(args) {
110
- const env = { ...process.env };
111
- ensureCommandPath(env);
112
- const invocation = parseAgentBrowserInvocationArgs(args);
113
- const toolArgs = invocation.args;
114
- const subcommand = invocation.subcommand;
115
- const hasExplicitCdp = hasAgentBrowserOption(toolArgs, "--cdp");
116
- const hasProfile = hasAgentBrowserOption(toolArgs, "--profile") || hasAgentBrowserOption(toolArgs, "--profile-dir");
117
- if (subcommand === "errors") {
118
- console.log("\x1b[33m💡 Tip: Using `d3k errors` instead (shows browser + server errors)\x1b[0m\n");
119
- const d3kBin = process.argv[1];
120
- const result = spawnSync(d3kBin, ["errors"], { stdio: "inherit", shell: false });
121
- process.exit(result.status ?? 0);
122
- }
123
- if (subcommand === "console") {
124
- console.log("\x1b[33m💡 Tip: Using `d3k logs` instead (shows browser + server logs)\x1b[0m\n");
125
- const d3kBin = process.argv[1];
126
- const result = spawnSync(d3kBin, ["logs", "--type", "browser"], { stdio: "inherit", shell: false });
127
- process.exit(result.status ?? 0);
128
- }
129
- const binaryPath = findAgentBrowser();
130
- const openNeedsD3kBrowser = isAgentBrowserOpenCommand(subcommand) && !invocation.allowNewBrowser && !hasExplicitCdp;
131
- if (invocation.allowNewBrowser && invocation.requireD3kBrowser) {
132
- console.error("`--allow-new-browser` and `--require-d3k-browser` cannot be used together.");
133
- process.exit(1);
134
- }
135
- if (invocation.requireD3kBrowser && hasExplicitCdp) {
136
- console.error("`--require-d3k-browser` uses the active d3k session. Remove `--cdp` or use `--allow-new-browser`.");
137
- process.exit(1);
138
- }
139
- const activeSession = findCurrentSession();
140
- const sessionCdpPort = getSessionCdpPortForSession(activeSession);
141
- const needsD3kBrowser = invocation.requireD3kBrowser || openNeedsD3kBrowser;
142
- if (needsD3kBrowser && hasProfile) {
143
- printUnsafeAgentBrowserOpenError("`--profile` and `--profile-dir` make agent-browser use its own browser profile instead of d3k's active browser.", activeSession !== null);
144
- process.exit(1);
145
- }
146
- if (needsD3kBrowser && !sessionCdpPort) {
147
- printUnsafeAgentBrowserOpenError("Opening a URL without a d3k-managed browser would launch a new automation browser, which can break Google OAuth/Supabase auth with `This browser or app may not be secure`.", activeSession !== null);
148
- process.exit(1);
149
- }
150
- if (subcommand !== "connect" && subcommand !== "close" && !hasExplicitCdp && !hasProfile) {
151
- if (sessionCdpPort) {
152
- const connectResult = spawnSync(binaryPath, ["connect", sessionCdpPort], { stdio: "pipe", env, shell: false });
153
- if (needsD3kBrowser && connectResult.status !== 0) {
154
- printUnsafeAgentBrowserOpenError("d3k found a browser session, but agent-browser could not connect to it.", activeSession !== null);
155
- process.exit(connectResult.status ?? 1);
156
- }
157
- }
158
- }
159
- const result = spawnSync(binaryPath, toolArgs, {
160
- stdio: "pipe",
161
- env,
162
- shell: false
163
- });
164
- if (result.stdout?.length > 0) {
165
- process.stdout.write(result.stdout);
166
- }
167
- if (result.stderr?.length > 0) {
168
- process.stderr.write(result.stderr);
169
- }
170
- if (result.error) {
171
- console.error(`\nError spawning agent-browser: ${result.error.message}`);
172
- console.error(`Binary path: ${binaryPath}`);
173
- process.exit(1);
174
- }
175
- process.exit(result.status ?? 1);
176
- }
177
- import { getBrowserCommandInvocation, hasAgentBrowserOption, isAgentBrowserOpenCommand, parseAgentBrowserInvocationArgs } from "./utils/browser-command-argv.js";
178
- const browserCommandInvocation = getBrowserCommandInvocation(process.argv.slice(2));
179
- if (browserCommandInvocation && (process.argv[1]?.includes("d3k") || process.argv[1]?.includes("dev3000"))) {
180
- runLocalBrowserTool(browserCommandInvocation.args);
181
- }
182
- import chalk from "chalk";
183
- import { execSync, spawnSync } from "child_process";
184
- import { Command } from "commander";
185
- import { accessSync, appendFileSync, chmodSync, constants, existsSync, readFileSync } from "fs";
186
- import { homedir } from "os";
187
- import { detect } from "package-manager-detector";
188
- import { dirname, join, resolve } from "path";
189
- import { fileURLToPath } from "url";
190
- import { createPersistentLogFile, findAvailablePort, startDevEnvironment } from "./dev-environment.js";
191
- import { getPortlessProxyStatus, installPortlessService } from "./portless.js";
192
- import { getSkill, getSkillsInfo, listAvailableSkills } from "./skills/index.js";
193
- import { detectAIAgent } from "./utils/agent-detection.js";
194
- import { getAvailableAgents, getSkillsAgentId } from "./utils/agent-selection.js";
195
- import { validateDateTimeOption, validatePortOption, validatePositiveIntegerOption, validateScriptOption } from "./utils/cli-options.js";
196
- import { ensureD3kHomeDir } from "./utils/d3k-dir.js";
197
- import { ensureD3kSkill } from "./utils/d3k-skill-installer.js";
198
- import { readProjectAgentName } from "./utils/project-metadata.js";
199
- import { getProjectDir, getProjectDisplayName } from "./utils/project-name.js";
200
- import { findCurrentSession } from "./utils/session.js";
201
- import { checkForSkillUpdates, getApplicablePackages, getSkillsPathForLocation, installSkillPackage, isPackageInstalled, updateSkills } from "./utils/skill-installer.js";
202
- import { DEFAULT_TMUX_CONFIG, generateSessionName, generateTmuxCommands, getTmuxInstallInstructions, isTmuxInstalled } from "./utils/tmux-helpers.js";
203
- import { loadUserConfig, saveUserConfig } from "./utils/user-config.js";
204
- import { checkForUpdates, getUpgradeCommand, performUpgrade } from "./utils/version-check.js";
205
- // Global error handlers to log crashes
206
- const crashLogPath = join(ensureD3kHomeDir(), "crash.log");
207
- function logCrash(type, error) {
208
- try {
209
- ensureD3kHomeDir();
210
- const timestamp = new Date().toISOString();
211
- const errorStr = error instanceof Error ? `${error.message}\n${error.stack}` : String(error);
212
- const logEntry = `[${timestamp}] ${type}: ${errorStr}\n\n`;
213
- appendFileSync(crashLogPath, logEntry);
214
- console.error(chalk.red(`\n💥 d3k crashed: ${error instanceof Error ? error.message : error}`));
215
- console.error(chalk.gray(` Details logged to: ${crashLogPath}`));
216
- }
217
- catch {
218
- // If we can't log, at least print to stderr
219
- console.error(`d3k crashed: ${error}`);
220
- }
221
- }
222
- function triggerEmergencyShutdown(reason, error) {
223
- const handler = globalThis.__d3kEmergencyShutdown;
224
- if (typeof handler === "function") {
225
- try {
226
- handler(reason, error);
227
- return true;
228
- }
229
- catch {
230
- return false;
231
- }
232
- }
233
- return false;
234
- }
235
- function setTerminalTitle(rootDir) {
236
- const safeRootDir = rootDir
237
- .split("\u001b")
238
- .join("")
239
- .split("\u0007")
240
- .join("")
241
- .replace(/[\r\n]+/g, " ")
242
- .trim();
243
- const title = `d3k ${safeRootDir}`;
244
- // Some terminals prefer process.title over OSC escape sequences.
245
- process.title = title;
246
- if (!process.stdout.isTTY)
247
- return;
248
- process.stdout.write(`\u001b]0;${title}\u0007\u001b]2;${title}\u0007`);
249
- }
250
- process.on("uncaughtException", (error) => {
251
- logCrash("Uncaught Exception", error);
252
- if (!triggerEmergencyShutdown("uncaughtException", error)) {
253
- process.exit(1);
254
- }
255
- });
256
- process.on("unhandledRejection", (reason) => {
257
- logCrash("Unhandled Promise Rejection", reason);
258
- if (!triggerEmergencyShutdown("unhandledRejection", reason)) {
259
- process.exit(1);
260
- }
261
- });
262
- function shellQuote(value) {
263
- return `'${value.replace(/'/g, `'\\''`)}'`;
264
- }
265
- /**
266
- * Build the d3k command string with forwarded options.
267
- */
268
- function buildD3kCommandWithOptions(options) {
269
- const d3kBase = process.argv[1].endsWith("d3k") ? "d3k" : "dev3000";
270
- const args = [shellQuote(d3kBase)];
271
- // Forward options that were explicitly set
272
- if (options.port)
273
- args.push("--port", shellQuote(options.port));
274
- if (options.script)
275
- args.push("--script", shellQuote(options.script));
276
- if (options.command)
277
- args.push("--command", shellQuote(options.command));
278
- if (options.startupTimeout)
279
- args.push("--startup-timeout", shellQuote(options.startupTimeout));
280
- if (options.browserNavigationTimeout) {
281
- args.push("--browser-navigation-timeout", shellQuote(options.browserNavigationTimeout));
282
- }
283
- if (options.profileDir)
284
- args.push("--profile-dir", shellQuote(options.profileDir));
285
- if (options.browserTool)
286
- args.push("--browser-tool", shellQuote(options.browserTool));
287
- if (options.browser)
288
- args.push("--browser", shellQuote(options.browser));
289
- if (options.appUrl)
290
- args.push("--app-url", shellQuote(options.appUrl));
291
- if (options.externalCdpBase)
292
- args.push("--external-cdp-base", shellQuote(options.externalCdpBase));
293
- if (options.serversOnly)
294
- args.push("--servers-only");
295
- if (options.headless)
296
- args.push("--headless");
297
- if (options.dateTime)
298
- args.push("--date-time", shellQuote(options.dateTime));
299
- if (options.pluginReactScan)
300
- args.push("--plugin-react-scan");
301
- if (options.agentName)
302
- args.push("--agent-name", shellQuote(options.agentName));
303
- if (options.portless === false)
304
- args.push("--no-portless");
305
- return args.join(" ");
306
- }
307
- /**
308
- * Launch d3k with an agent using tmux for proper terminal multiplexing.
309
- * This creates a split-screen with the agent on the left and d3k logs on the right.
310
- */
311
- async function launchWithTmux(agentCommand, forwardedOptions = {}) {
312
- const { execSync } = await import("child_process");
313
- const { appendFileSync, writeFileSync } = await import("fs");
314
- // Log file for debugging crashes
315
- const d3kDir = ensureD3kHomeDir();
316
- const crashLogPath = join(d3kDir, "crash.log");
317
- const logCrash = (message, error) => {
318
- const timestamp = new Date().toISOString();
319
- const errorStr = error instanceof Error ? `${error.message}\n${error.stack}` : String(error || "");
320
- const logEntry = `[${timestamp}] ${message}${errorStr ? `\n${errorStr}` : ""}\n`;
321
- try {
322
- appendFileSync(crashLogPath, logEntry);
323
- }
324
- catch {
325
- // Ignore write errors
326
- }
327
- console.error(chalk.red(message));
328
- if (error)
329
- console.error(error);
330
- };
331
- // Check if tmux is installed
332
- if (!(await isTmuxInstalled())) {
333
- console.error(chalk.red("\n❌ tmux is not installed."));
334
- console.error(chalk.yellow("\nThe --with-agent flag requires tmux for split-screen mode."));
335
- console.error(chalk.cyan("\nTo install tmux:"));
336
- for (const instruction of getTmuxInstallInstructions()) {
337
- console.error(chalk.gray(` ${instruction}`));
338
- }
339
- console.error(chalk.yellow("\nAlternatively, run d3k and your agent in separate terminal tabs.\n"));
340
- process.exit(1);
341
- }
342
- // Generate a unique session name
343
- const sessionName = generateSessionName();
344
- const agentPaneLogPath = join(d3kDir, "tmux-last-agent.log");
345
- const d3kPaneLogPath = join(d3kDir, "tmux-last-d3k.log");
346
- // Build d3k command with forwarded options
347
- const d3kCommand = buildD3kCommandWithOptions(forwardedOptions);
348
- // Generate tmux commands using the helper
349
- const commands = generateTmuxCommands({
350
- sessionName,
351
- d3kCommand,
352
- agentCommand,
353
- paneWidthPercent: DEFAULT_TMUX_CONFIG.paneWidthPercent,
354
- d3kLogPath: d3kPaneLogPath,
355
- agentLogPath: agentPaneLogPath
356
- });
357
- // Create a shell script that sets up tmux and attaches
358
- // This ensures clean terminal state by exec'ing into tmux
359
- const scriptPath = join(d3kDir, "launch-tmux.sh");
360
- // Get the first command (new-session) and remaining commands
361
- const [newSessionCmd, ...remainingCommands] = commands;
362
- // Modify new-session to include terminal dimensions (detected at runtime)
363
- // This ensures the 80/20 split ratio is maintained when the window is resized on attach
364
- const newSessionWithSize = newSessionCmd.replace("tmux new-session -d", "tmux new-session -d -x $COLS -y $LINES");
365
- const scriptContent = `#!/bin/bash
366
- set -euo pipefail
367
-
368
- # Reset terminal state
369
- stty sane 2>/dev/null || true
370
- reset 2>/dev/null || true
371
-
372
- # Get terminal dimensions for proper pane sizing
373
- COLS=$(tput cols)
374
- LINES=$(tput lines)
375
-
376
- # Setup tmux session with actual terminal size
377
- ${newSessionWithSize} && \\
378
- ${remainingCommands.join(" && \\\n")}
379
-
380
- # Verify split setup succeeded before attaching.
381
- PANE_COUNT=$(tmux list-panes -t "${sessionName}" 2>/dev/null | wc -l | tr -d ' ')
382
- if [ "\${PANE_COUNT}" -lt 2 ]; then
383
- echo "❌ Failed to initialize split panes (expected 2, got \${PANE_COUNT})."
384
- tmux kill-session -t "${sessionName}" 2>/dev/null || true
385
- exit 1
386
- fi
387
-
388
- # Replace this process with tmux attach
389
- exec tmux attach-session -t "${sessionName}"
390
- `;
391
- try {
392
- const tmuxStartTime = Date.now();
393
- // Write the launch script
394
- writeFileSync(scriptPath, scriptContent, { mode: 0o755 });
395
- // Spawn bash with the script, inheriting stdio
396
- // The script will exec into tmux, replacing bash
397
- const { spawnSync } = await import("child_process");
398
- const result = spawnSync("bash", [scriptPath], {
399
- stdio: "inherit",
400
- shell: false
401
- });
402
- const tmuxDurationMs = Date.now() - tmuxStartTime;
403
- const exitCode = result.status ?? (result.error ? 1 : 0);
404
- // Clean up session on exit
405
- try {
406
- execSync(`tmux kill-session -t "${sessionName}" 2>/dev/null`, { stdio: "ignore" });
407
- }
408
- catch {
409
- // Session might already be killed
410
- }
411
- // If tmux exits quickly, surface diagnostics to avoid a "silent crash" experience.
412
- if (exitCode !== 0 || tmuxDurationMs < 15_000) {
413
- console.error("");
414
- if (exitCode !== 0) {
415
- console.error(chalk.red(`❌ tmux session exited with code ${exitCode}`));
416
- }
417
- else {
418
- console.error(chalk.yellow("⚠️ tmux session exited quickly"));
419
- }
420
- console.error(chalk.gray(`Agent pane log: ${agentPaneLogPath}`));
421
- console.error(chalk.gray(`d3k pane log: ${d3kPaneLogPath}`));
422
- console.error(chalk.gray(`Crash log: ${crashLogPath}`));
423
- console.error("");
424
- }
425
- // Clear screen only on clean, long-running sessions.
426
- if (exitCode === 0 && tmuxDurationMs >= 15_000) {
427
- process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
428
- console.log(chalk.gray("Thanks for using d3k!"));
429
- }
430
- process.exit(exitCode);
431
- }
432
- catch (error) {
433
- logCrash("Failed to start tmux session", error);
434
- process.exit(1);
435
- }
436
- }
437
- /**
438
- * Show interactive agent selection prompt using Ink.
439
- * Returns the selected agent config, or null if user chose "No agent".
440
- */
441
- async function promptAgentSelection(defaultAgentName) {
442
- const { render } = await import("ink");
443
- const React = await import("react");
444
- const { AgentSelector } = await import("./components/AgentSelector.js");
445
- const agents = getAvailableAgents();
446
- // Store the result to return after Ink exits
447
- let selectedResult = null;
448
- try {
449
- const { unmount, waitUntilExit, clear } = render(React.createElement(AgentSelector, {
450
- agents,
451
- defaultAgentName,
452
- onComplete: (result) => {
453
- selectedResult = result.agent;
454
- // Always save the selection for next time
455
- try {
456
- if (result.agent) {
457
- saveUserConfig({ defaultAgent: result.agent });
458
- }
459
- else {
460
- // User chose "No agent" - clear the saved default
461
- saveUserConfig({ defaultAgent: undefined });
462
- }
463
- }
464
- catch (_error) {
465
- console.warn(chalk.yellow("Warning: Could not save agent preference"));
466
- }
467
- // Clear the Ink output and unmount
468
- clear();
469
- unmount();
470
- }
471
- }));
472
- // Wait for Ink to fully exit
473
- await waitUntilExit();
474
- // Clear terminal and scrollback to remove any Ink artifacts
475
- process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
476
- }
477
- catch (error) {
478
- console.error(chalk.red("Error in agent selection:"), error);
479
- return null;
480
- }
481
- return selectedResult;
482
- }
483
- /**
484
- * Show interactive skill selection prompt using Ink.
485
- * Returns the selected skills and install location, or empty array if user skipped.
486
- */
487
- async function promptPackageSelection(packages, agentId) {
488
- const { render } = await import("ink");
489
- const React = await import("react");
490
- const { PackageSelector } = await import("./components/PackageSelector.js");
491
- let selectedPackages = [];
492
- let installLocation = "project";
493
- try {
494
- const { unmount, waitUntilExit, clear } = render(React.createElement(PackageSelector, {
495
- packages,
496
- agentId,
497
- onComplete: (selected, location) => {
498
- selectedPackages = selected;
499
- installLocation = location;
500
- clear();
501
- unmount();
502
- },
503
- onSkip: () => {
504
- clear();
505
- unmount();
506
- }
507
- }));
508
- await waitUntilExit();
509
- // Clear terminal and scrollback to remove any Ink artifacts
510
- process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
511
- }
512
- catch (error) {
513
- console.error(chalk.red("Error in package selection:"), error);
514
- return { packages: [], location: "project" };
515
- }
516
- return { packages: selectedPackages, location: installLocation };
517
- }
518
- function detectPythonCommand(debug = false) {
519
- // Check if we're in a virtual environment
520
- if (process.env.VIRTUAL_ENV) {
521
- if (debug) {
522
- console.log(`[DEBUG] Virtual environment detected: ${process.env.VIRTUAL_ENV}`);
523
- console.log(`[DEBUG] Using activated python command`);
524
- }
525
- return "python";
526
- }
527
- // Check if python3 is available and prefer it
528
- try {
529
- execSync("python3 --version", { stdio: "ignore" });
530
- if (debug) {
531
- console.log(`[DEBUG] python3 is available, using python3`);
532
- }
533
- return "python3";
534
- }
535
- catch {
536
- if (debug) {
537
- console.log(`[DEBUG] python3 not available, falling back to python`);
538
- }
539
- return "python";
540
- }
541
- }
542
- async function detectProjectType(debug = false) {
543
- // Helper to check if package.json has a dev script (indicates Node.js project)
544
- const hasNodeDevScript = () => {
545
- try {
546
- if (existsSync("package.json")) {
547
- const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
548
- return !!packageJson.scripts?.dev;
549
- }
550
- }
551
- catch {
552
- // Ignore parse errors
553
- }
554
- return false;
555
- };
556
- // Check for Node.js project FIRST if package.json has a dev script
557
- // This takes priority over Python/Rails detection for hybrid projects
558
- const detected = await detect();
559
- if (detected && hasNodeDevScript()) {
560
- if (debug) {
561
- console.log(`[DEBUG] Node.js project detected (package.json with dev script takes priority)`);
562
- }
563
- // Continue to Node.js detection below
564
- }
565
- else {
566
- // Check for Python project (only if no Node.js dev script)
567
- if (existsSync("requirements.txt") || existsSync("pyproject.toml")) {
568
- if (debug) {
569
- console.log(`[DEBUG] Python project detected (found requirements.txt or pyproject.toml)`);
570
- }
571
- return {
572
- type: "python",
573
- defaultScript: "main.py",
574
- defaultPort: "8000", // Common Python web server port
575
- pythonCommand: detectPythonCommand(debug)
576
- };
577
- }
578
- // Check for Rails project
579
- if (existsSync("Gemfile") && existsSync("config/application.rb")) {
580
- if (debug) {
581
- console.log(`[DEBUG] Rails project detected (found Gemfile and config/application.rb)`);
582
- }
583
- return {
584
- type: "rails",
585
- defaultScript: "server",
586
- defaultPort: "3000" // Rails default port
587
- };
588
- }
589
- }
590
- // Helper to detect framework for Node.js projects
591
- const detectFramework = () => {
592
- // Check for Next.js
593
- const nextConfigFiles = ["next.config.js", "next.config.ts", "next.config.mjs", "next.config.cjs"];
594
- if (nextConfigFiles.some((file) => existsSync(file))) {
595
- if (debug) {
596
- console.log(`[DEBUG] Next.js framework detected`);
597
- }
598
- return "nextjs";
599
- }
600
- // Check for Svelte - look for svelte.config.js or svelte dependency
601
- if (existsSync("svelte.config.js")) {
602
- if (debug) {
603
- console.log(`[DEBUG] Svelte framework detected (svelte.config.js)`);
604
- }
605
- return "svelte";
606
- }
607
- // Check package.json for svelte dependency
608
- try {
609
- if (existsSync("package.json")) {
610
- const packageJson = JSON.parse(readFileSync("package.json", "utf-8"));
611
- const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
612
- if (deps.svelte || deps["@sveltejs/kit"]) {
613
- if (debug) {
614
- console.log(`[DEBUG] Svelte framework detected (package.json dependency)`);
615
- }
616
- return "svelte";
617
- }
618
- }
619
- }
620
- catch {
621
- // Ignore parse errors
622
- }
623
- return "other";
624
- };
625
- if (detected) {
626
- const framework = detectFramework();
627
- if (debug) {
628
- console.log(`[DEBUG] Node.js project detected with ${detected.agent} package manager and ${framework} framework`);
629
- }
630
- return {
631
- type: "node",
632
- framework,
633
- packageManager: detected.agent,
634
- defaultScript: "dev",
635
- defaultPort: "3000"
636
- };
637
- }
638
- // Check if this is a valid project directory
639
- // If we get here, no lock files or project markers were found
640
- // Check if package.json exists - if not, this isn't a valid project directory
641
- if (!existsSync("package.json")) {
642
- if (debug) {
643
- console.log(`[DEBUG] No project files detected - not a valid project directory`);
644
- }
645
- return {
646
- type: "node",
647
- framework: "other",
648
- packageManager: "npm",
649
- defaultScript: "dev",
650
- defaultPort: "3000",
651
- noProjectDetected: true // Flag to indicate no project was found
652
- };
653
- }
654
- // Fallback to npm for Node.js (package.json exists but no lock file)
655
- const framework = detectFramework();
656
- if (debug) {
657
- console.log(`[DEBUG] Node.js project detected (package.json exists, no lock file), defaulting to npm and ${framework} framework`);
658
- }
659
- return {
660
- type: "node",
661
- framework,
662
- packageManager: "npm",
663
- defaultScript: "dev",
664
- defaultPort: "3000"
665
- };
666
- }
667
- // Read version from package.json or use compile-time injected version
668
- function getVersion() {
669
- // Check for compile-time injected version first (for standalone binaries)
670
- if (typeof __D3K_VERSION__ !== "undefined") {
671
- return __D3K_VERSION__;
672
- }
673
- try {
674
- const currentFile = fileURLToPath(import.meta.url);
675
- const packageRoot = dirname(dirname(currentFile)); // Go up from dist/ to package root
676
- const packageJsonPath = join(packageRoot, "package.json");
677
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
678
- let version = packageJson.version;
679
- // Use git to detect if we're in the dev3000 source repository
680
- try {
681
- const gitRemote = execSync("git remote get-url origin 2>/dev/null", {
682
- cwd: packageRoot,
683
- encoding: "utf8"
684
- }).trim();
685
- if (gitRemote.includes("vercel-labs/dev3000") && !version.includes("canary")) {
686
- version += "-local";
687
- }
688
- }
689
- catch {
690
- // Not in git repo or no git - use version as-is
691
- }
692
- return version;
693
- }
694
- catch (_error) {
695
- return "0.0.0"; // fallback
696
- }
697
- }
698
- // Check if installed globally before proceeding
699
- function checkGlobalInstall() {
700
- const currentFile = fileURLToPath(import.meta.url);
701
- const packageRoot = dirname(dirname(currentFile));
702
- // Check common global install paths
703
- const globalPaths = [
704
- "/usr/local/lib/node_modules",
705
- "/usr/lib/node_modules",
706
- process.env.NPM_CONFIG_PREFIX && join(process.env.NPM_CONFIG_PREFIX, "lib/node_modules"),
707
- process.env.PNPM_HOME,
708
- process.platform === "win32" && process.env.APPDATA && join(process.env.APPDATA, "npm/node_modules"),
709
- process.env.HOME && join(process.env.HOME, ".npm-global/lib/node_modules"),
710
- process.env.HOME && join(process.env.HOME, ".pnpm"),
711
- process.env.HOME && join(process.env.HOME, ".yarn/global/node_modules")
712
- ].filter(Boolean);
713
- // Check if our package path contains any of these global paths
714
- for (const globalPath of globalPaths) {
715
- if (packageRoot.includes(globalPath)) {
716
- return true;
717
- }
718
- }
719
- // Additional check: if we're in node_modules but not in a project's node_modules
720
- if (packageRoot.includes("node_modules") && !existsSync(join(packageRoot, "..", "..", "..", "package.json"))) {
721
- return true;
722
- }
723
- // If we're in dev (running from source), that's fine
724
- if (!packageRoot.includes("node_modules")) {
725
- return true;
726
- }
727
- return false;
728
- }
729
- // Perform the check
730
- if (!checkGlobalInstall()) {
731
- console.error(chalk.red("\n❌ Error: dev3000 must be installed globally.\n"));
732
- console.error(chalk.white("This package won't work correctly as a local dependency.\n"));
733
- console.error(chalk.cyan("To install globally, use one of these commands:"));
734
- console.error(chalk.gray(" pnpm install -g dev3000"));
735
- console.error(chalk.gray(" npm install -g dev3000"));
736
- console.error(chalk.gray(" yarn global add dev3000\n"));
737
- console.error(chalk.white("Then run 'd3k' or 'dev3000' from any project directory.\n"));
738
- process.exit(1);
739
- }
740
- const program = new Command();
741
- program
742
- .name("dev3000")
743
- .description("Agent-first local web debugging runtime with a managed browser")
744
- .version(getVersion());
745
- program
746
- .description("Agent-first local web debugging runtime with a managed browser")
747
- .option("-p, --port <port>", "Development server port (auto-detected by project type)")
748
- .option("-s, --script <script>", "Script to run (e.g. dev, main.py) - auto-detected by project type")
749
- .option("-c, --command <command>", "Custom command to run (overrides auto-detection and --script)")
750
- .option("--startup-timeout <seconds>", "Seconds to wait for your app server to become reachable", "30")
751
- .option("--browser-navigation-timeout <seconds>", "Seconds to wait for browser-initiated page navigation commands", "60")
752
- .option("--profile-dir <dir>", "Chrome profile directory")
753
- .option("--data-dir <dir>", "Override the per-project data directory (default: ~/.d3k/<project>/). Holds session.json, logs, screenshots, and the Chrome profile.")
754
- .option("--log-file <path>", "Override the consolidated log file path (default: <data-dir>/logs/<timestamp>.log)")
755
- .option("--screenshots-dir <dir>", "Override the screenshots directory (default: <data-dir>/screenshots)")
756
- .option("--browser-tool <tool>", "Preferred local browser CLI: 'agent-browser'", "agent-browser")
757
- .option("--browser <path>", "Full path to browser executable (e.g. for Arc: '/Applications/Arc.app/Contents/MacOS/Arc')")
758
- .option("--app-url <url>", "URL to open in the monitored browser instead of the local app URL")
759
- .option("--external-cdp-base <url>", "Existing CDP HTTP endpoint to connect to instead of launching Chrome")
760
- .option("--servers-only", "Run servers only, skip browser launch")
761
- .option("--debug", "Enable debug logging to console (automatically disables TUI)")
762
- .option("-t, --tail", "Output consolidated logfile to terminal (like tail -f)")
763
- .option("--tui", "Enable the interactive TUI dashboard (off by default)", false)
764
- .option("--portless", "Use stable Portless URLs (default)", true)
765
- .option("--no-portless", "Use a direct localhost URL instead of Portless")
766
- .option("--date-time <format>", "Timestamp format: 'local' (default, e.g. 12:54:03 PM) or 'utc' (ISO string)", "local")
767
- .option("--plugin-react-scan", "Enable react-scan performance monitoring for React applications")
768
- .option("--headless", "Run Chrome in headless mode (for serverless/CI environments)")
769
- .option("--auto-skills", "Automatically install recommended skills without prompts (headless-safe, installs to project skills dir)")
770
- .option("--no-skills", "Disable skill installation and update checks")
771
- .option("--with-agent <command>", 'Run an agent (e.g. claude) in split-screen mode using tmux. Example: --with-agent "claude"')
772
- .option("--agent-name <name>", "Selected agent name (internal)")
773
- .option("--no-agent", "Skip agent selection prompt and run d3k standalone")
774
- .action(async (options) => {
775
- if (options.dataDir) {
776
- process.env.D3K_DATA_DIR = resolve(options.dataDir);
777
- }
778
- const projectName = getProjectDisplayName();
779
- setTerminalTitle(projectName);
780
- try {
781
- if (options.port) {
782
- options.port = validatePortOption(options.port);
783
- }
784
- if (options.script) {
785
- options.script = validateScriptOption(options.script);
786
- }
787
- options.startupTimeout = validatePositiveIntegerOption("--startup-timeout", options.startupTimeout);
788
- options.browserNavigationTimeout = validatePositiveIntegerOption("--browser-navigation-timeout", options.browserNavigationTimeout);
789
- if (options.browserTool && options.browserTool !== "agent-browser") {
790
- throw new Error("--browser-tool must be 'agent-browser'.");
791
- }
792
- options.browserTool = "agent-browser";
793
- options.dateTime = validateDateTimeOption(options.dateTime || "local");
794
- }
795
- catch (error) {
796
- const message = error instanceof Error ? error.message : String(error);
797
- console.error(chalk.red(`\n❌ ${message}\n`));
798
- process.exit(1);
799
- }
800
- // Load user config early so it can be used for --with-agent and agent selection flows
801
- const userConfig = loadUserConfig();
802
- const agentDetection = detectAIAgent();
803
- // Apply browser default from user config if not explicitly provided via CLI
804
- const browserOption = options.browser || userConfig.browser;
805
- // Handle --with-agent by spawning tmux with split panes
806
- if (options.withAgent) {
807
- await launchWithTmux(options.withAgent, {
808
- port: options.port,
809
- script: options.script,
810
- command: options.command,
811
- startupTimeout: options.startupTimeout,
812
- browserNavigationTimeout: options.browserNavigationTimeout,
813
- profileDir: options.profileDir,
814
- browserTool: options.browserTool,
815
- browser: browserOption,
816
- appUrl: options.appUrl,
817
- externalCdpBase: options.externalCdpBase,
818
- serversOnly: options.serversOnly,
819
- headless: options.headless,
820
- dateTime: options.dateTime,
821
- pluginReactScan: options.pluginReactScan,
822
- agentName: options.agentName,
823
- portless: options.portless
824
- });
825
- return;
826
- }
827
- // Handle agent selection for split-screen mode (explicit with --tui)
828
- // Skip unless --tui is provided, or if already inside tmux (to avoid nested prompts)
829
- const insideTmux = !!process.env.TMUX;
830
- let selectedAgent = null;
831
- let didPromptAgentSelection = false;
832
- let skillsAgentId = options.agentName
833
- ? getSkillsAgentId(options.agentName)
834
- : agentDetection.agentId || null;
835
- if (agentDetection.isAgent) {
836
- if (options.tui) {
837
- if (options.debug) {
838
- console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-disabling TUI`);
839
- }
840
- options.tui = false;
841
- }
842
- if (agentDetection.agentId && options.skills !== false && !options.autoSkills) {
843
- if (options.debug) {
844
- console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-enabling project skill installs`);
845
- }
846
- options.autoSkills = true;
847
- }
848
- }
849
- if (process.stdin.isTTY && options.agent !== false && options.tui === true && !options.debug && !insideTmux) {
850
- // Clear the terminal so d3k UI starts at the top of the screen
851
- process.stdout.write("\x1B[2J\x1B[0f");
852
- // Check if tmux is available before showing prompt
853
- const tmuxAvailable = await isTmuxInstalled();
854
- if (!tmuxAvailable) {
855
- console.warn(chalk.yellow("⚠️ tmux not installed - agent split-screen mode unavailable"));
856
- console.warn(chalk.gray(" Install tmux to enable: brew install tmux (macOS)"));
857
- // Continue with normal startup
858
- }
859
- else {
860
- // Always show prompt, pre-selecting the last-used option
861
- const preferredAgentName = readProjectAgentName() || userConfig.defaultAgent?.name;
862
- selectedAgent = await promptAgentSelection(preferredAgentName);
863
- didPromptAgentSelection = true;
864
- if (selectedAgent) {
865
- if (selectedAgent.name === "debug") {
866
- // User chose debug mode - enable debug and continue with normal startup
867
- options.debug = true;
868
- }
869
- }
870
- else if (options.debug) {
871
- console.log("[DEBUG] No agent selected, continuing with normal startup");
872
- }
873
- // User chose "No agent" or "debug" - continue with normal startup
874
- }
875
- const skillsAgentName = selectedAgent?.name && selectedAgent.name !== "debug"
876
- ? selectedAgent.name
877
- : !didPromptAgentSelection
878
- ? userConfig.defaultAgent?.name
879
- : undefined;
880
- skillsAgentId = getSkillsAgentId(skillsAgentName);
881
- if (skillsAgentId && options.skills !== false) {
882
- const resolvedSkillsAgentId = skillsAgentId;
883
- // Check for skill updates and offer new packages
884
- try {
885
- // Show loading message
886
- process.stdout.write(chalk.gray(" Checking for skills...\r"));
887
- // 1. Check for updates to existing skills
888
- const { hasUpdates } = await checkForSkillUpdates();
889
- // Clear the loading message line
890
- process.stdout.write("\x1B[2K\r");
891
- if (hasUpdates) {
892
- // Show which packages have updates (use applicable packages since lock file may not exist)
893
- const applicablePackages = getApplicablePackages();
894
- console.log(chalk.cyan("📦 Skill updates available"));
895
- for (const pkg of applicablePackages) {
896
- console.log(chalk.gray(` • ${pkg.repo}`));
897
- }
898
- const { default: readline } = await import("readline");
899
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
900
- const answer = await new Promise((resolve) => {
901
- rl.question(chalk.white(" Update now? (Y/n) "), resolve);
902
- });
903
- rl.close();
904
- if (answer.toLowerCase() !== "n") {
905
- console.log(chalk.gray(" Updating skills..."));
906
- const updateResult = await updateSkills();
907
- if (updateResult.success) {
908
- console.log(chalk.green(" ✓ Skills updated"));
909
- }
910
- else {
911
- console.log(chalk.yellow(" ⚠ Some skills failed to update"));
912
- }
913
- }
914
- console.log("");
915
- }
916
- // 2. Show all applicable packages with install status
917
- // Skip if we just handled updates
918
- if (!hasUpdates) {
919
- const applicablePackages = getApplicablePackages();
920
- const packagesWithStatus = applicablePackages.map((pkg) => ({
921
- ...pkg,
922
- installed: isPackageInstalled(pkg, resolvedSkillsAgentId)
923
- }));
924
- const hasUninstalled = packagesWithStatus.some((p) => !p.installed);
925
- // Only show package selector if there are packages to install
926
- if (packagesWithStatus.length > 0 && hasUninstalled) {
927
- const { packages: selectedPackages, location } = await promptPackageSelection(packagesWithStatus, resolvedSkillsAgentId);
928
- if (selectedPackages.length > 0) {
929
- const locationLabel = location === "global" ? "globally" : "to project";
930
- const targetPath = getSkillsPathForLocation(resolvedSkillsAgentId, location);
931
- if (targetPath) {
932
- const displayPath = targetPath.path.replace(process.env.HOME || "", "~");
933
- console.log(chalk.cyan(`Installing ${selectedPackages.length} skill package(s) ${locationLabel} → ${displayPath}...`));
934
- }
935
- else {
936
- console.log(chalk.cyan(`Installing ${selectedPackages.length} skill package(s) ${locationLabel}...`));
937
- }
938
- const results = { success: [], failed: [] };
939
- for (let i = 0; i < selectedPackages.length; i++) {
940
- const pkg = selectedPackages[i];
941
- console.log(chalk.gray(` [${i + 1}/${selectedPackages.length}] ${pkg.displayName}...`));
942
- const result = await installSkillPackage(pkg, location, resolvedSkillsAgentId);
943
- if (result.success) {
944
- results.success.push(pkg.displayName);
945
- }
946
- else {
947
- results.failed.push(pkg.displayName);
948
- }
949
- }
950
- if (results.success.length > 0) {
951
- console.log(chalk.green(`✓ Installed: ${results.success.join(", ")}`));
952
- }
953
- if (results.failed.length > 0) {
954
- console.log(chalk.yellow(`⚠ Failed: ${results.failed.join(", ")}`));
955
- }
956
- console.log("");
957
- }
958
- else {
959
- // User skipped package installation, show skills are up to date
960
- console.log(chalk.green("✓ Skills up to date"));
961
- console.log("");
962
- }
963
- }
964
- else {
965
- // No updates and no new packages - show success
966
- console.log(chalk.green("✓ Skills up to date"));
967
- console.log("");
968
- }
969
- }
970
- }
971
- catch {
972
- // Show error briefly, then continue
973
- console.log(chalk.yellow("⚠ Could not check for skill updates"));
974
- console.log("");
975
- }
976
- }
977
- if (tmuxAvailable && selectedAgent && selectedAgent.name !== "debug") {
978
- // User selected an agent - launch with tmux after skills install
979
- if (options.debug) {
980
- console.log(`[DEBUG] Launching tmux with agent command: ${selectedAgent.command}`);
981
- }
982
- if (skillsAgentId === "claude-code" && options.skills !== false) {
983
- ensureD3kSkill("claude-code");
984
- }
985
- // Clear screen and scrollback before launching tmux so when tmux exits, terminal is clean
986
- process.stdout.write("\x1b[2J\x1b[H\x1b[3J");
987
- await launchWithTmux(selectedAgent.command, {
988
- port: options.port,
989
- script: options.script,
990
- command: options.command,
991
- startupTimeout: options.startupTimeout,
992
- browserNavigationTimeout: options.browserNavigationTimeout,
993
- profileDir: options.profileDir,
994
- browserTool: options.browserTool,
995
- browser: browserOption,
996
- appUrl: options.appUrl,
997
- externalCdpBase: options.externalCdpBase,
998
- serversOnly: options.serversOnly,
999
- headless: options.headless,
1000
- dateTime: options.dateTime,
1001
- pluginReactScan: options.pluginReactScan,
1002
- agentName: selectedAgent.name,
1003
- portless: options.portless
1004
- });
1005
- return;
1006
- }
1007
- }
1008
- if (options.autoSkills && options.skills !== false && !skillsAgentId) {
1009
- skillsAgentId = agentDetection.agentId || "codex";
1010
- }
1011
- // Detect project type and configuration
1012
- const projectConfig = await detectProjectType(options.debug);
1013
- // Check if we're in a valid project directory
1014
- if (projectConfig.noProjectDetected) {
1015
- console.error(chalk.red("\n❌ No project detected in current directory.\n"));
1016
- console.error(chalk.white("dev3000 requires a project with one of these files:"));
1017
- console.error(chalk.gray(" • package.json (Node.js/JavaScript)"));
1018
- console.error(chalk.gray(" • requirements.txt or pyproject.toml (Python)"));
1019
- console.error(chalk.gray(" • Gemfile + config/application.rb (Rails)\n"));
1020
- console.error(chalk.cyan("💡 To get started:"));
1021
- console.error(chalk.gray(" • Navigate to an existing project directory, or"));
1022
- console.error(chalk.gray(" • Create a new project (e.g., 'npx create-next-app@latest'), or"));
1023
- console.error(chalk.gray(" • Use --command to run a custom command:\n"));
1024
- console.error(chalk.yellow(` d3k --command "node server.js" -p 3000\n`));
1025
- process.exit(1);
1026
- }
1027
- // Use defaults from project detection if not explicitly provided
1028
- const port = options.port || projectConfig.defaultPort;
1029
- const script = options.script || projectConfig.defaultScript;
1030
- const userSetPort = options.port !== undefined;
1031
- const startupTimeoutSeconds = Number.parseInt(options.startupTimeout, 10);
1032
- const browserNavigationTimeoutSeconds = Number.parseInt(options.browserNavigationTimeout, 10);
1033
- const browserTool = "agent-browser";
1034
- if (Number.isNaN(startupTimeoutSeconds) || startupTimeoutSeconds <= 0) {
1035
- console.error(chalk.red("\n❌ --startup-timeout must be a positive integer (seconds).\n"));
1036
- process.exit(1);
1037
- }
1038
- if (Number.isNaN(browserNavigationTimeoutSeconds) || browserNavigationTimeoutSeconds <= 0) {
1039
- console.error(chalk.red("\n❌ --browser-navigation-timeout must be a positive integer (seconds).\n"));
1040
- process.exit(1);
1041
- }
1042
- // Generate server command based on custom command or project type
1043
- let serverCommand;
1044
- if (options.command) {
1045
- // Use custom command if provided - this overrides everything
1046
- serverCommand = options.command;
1047
- if (options.debug) {
1048
- console.log(`[DEBUG] Using custom command: ${serverCommand}`);
1049
- }
1050
- }
1051
- else if (projectConfig.type === "python") {
1052
- serverCommand = `${projectConfig.pythonCommand} ${script}`;
1053
- // Python frameworks typically use --port or -p, but it varies by framework
1054
- // For now, we'll let users handle Python port config manually
1055
- }
1056
- else if (projectConfig.type === "rails") {
1057
- serverCommand = `bundle exec rails ${script}`;
1058
- // Append port for Rails when user explicitly sets it
1059
- if (userSetPort) {
1060
- serverCommand += ` -p ${port}`;
1061
- }
1062
- }
1063
- else {
1064
- // Node.js project
1065
- serverCommand = `${projectConfig.packageManager} run ${script}`;
1066
- // Append port for Node.js projects when user explicitly sets it
1067
- if (userSetPort) {
1068
- // Different frameworks need different port argument syntax
1069
- // Note: We don't use -- separator as it conflicts with scripts that already have args
1070
- if (projectConfig.framework === "nextjs") {
1071
- // Next.js uses -p or --port flag
1072
- serverCommand += ` -p ${port}`;
1073
- }
1074
- else {
1075
- // Other frameworks (Vite, etc.) typically use --port
1076
- serverCommand += ` --port ${port}`;
1077
- }
1078
- }
1079
- }
1080
- // Check for circular dependency - detect if the script would invoke dev3000 itself
1081
- // Skip this check if using a custom command
1082
- if (projectConfig.type === "node" && !options.command) {
1083
- try {
1084
- const packageJson = JSON.parse(readFileSync(join(process.cwd(), "package.json"), "utf-8"));
1085
- const scriptContent = packageJson.scripts?.[script];
1086
- // Check if the script invokes dev3000 or d3k
1087
- if (scriptContent && (scriptContent.includes("dev3000") || /\bd3k\b/.test(scriptContent))) {
1088
- console.error(chalk.red(`\n❌ Circular dependency detected!`));
1089
- console.error(chalk.yellow(` The "${script}" script in package.json calls dev3000, which would create an infinite loop.`));
1090
- console.error(chalk.yellow(` Current script content: "${scriptContent}"`));
1091
- console.error(chalk.yellow(`\n💡 Fix this by either:`));
1092
- console.error(chalk.yellow(` 1. Change the "${script}" script to call your actual dev server`));
1093
- console.error(chalk.yellow(` 2. Use dev3000 globally (run 'd3k' directly) instead of via package.json scripts`));
1094
- console.error(chalk.yellow(` 3. Use a different script name that doesn't invoke dev3000 (e.g., '${script === "dev" ? "dev:next" : "dev:server"}')`));
1095
- process.exit(1);
1096
- }
1097
- }
1098
- catch (_error) {
1099
- // Ignore errors reading package.json
1100
- }
1101
- }
1102
- if (options.debug) {
1103
- console.log(`[DEBUG] Project type: ${projectConfig.type}`);
1104
- console.log(`[DEBUG] Port: ${port} (${options.port ? "explicit" : "auto-detected"})`);
1105
- console.log(`[DEBUG] Script: ${script} (${options.script ? "explicit" : "auto-detected"})`);
1106
- console.log(`[DEBUG] Server command: ${serverCommand}`);
1107
- }
1108
- // Detect which command name was used (dev3000 or d3k)
1109
- const executablePath = process.argv[1];
1110
- const commandName = executablePath.endsWith("/d3k") || executablePath.includes("/d3k") ? "d3k" : "dev3000";
1111
- try {
1112
- const portlessEnabled = options.portless !== false;
1113
- if (portlessEnabled) {
1114
- const portlessStatus = getPortlessProxyStatus();
1115
- if (portlessStatus.setupRequired) {
1116
- console.log(chalk.cyan("Canonical Portless HTTPS routing is not ready; setting it up now..."));
1117
- const setupResult = await installPortlessService();
1118
- if (!setupResult.success) {
1119
- throw new Error(setupResult.error || "Portless setup failed");
1120
- }
1121
- }
1122
- }
1123
- // Create persistent log file
1124
- const logFile = createPersistentLogFile(options.logFile);
1125
- // Use a per-project Chrome profile by default so concurrent d3k sessions
1126
- // don't fight over the same browser state. Honor explicit overrides.
1127
- const profileDir = options.profileDir || join(getProjectDir(), "chrome-profile");
1128
- // Find available Chrome debug port (starting from 9222)
1129
- // Each d3k instance needs its own debug port to avoid conflicts
1130
- const debugPort = await findAvailablePort(9222);
1131
- await startDevEnvironment({
1132
- ...options,
1133
- browser: browserOption,
1134
- browserTool,
1135
- appUrl: options.appUrl,
1136
- externalCdpBase: options.externalCdpBase,
1137
- port,
1138
- debugPort: Number.parseInt(debugPort, 10),
1139
- defaultPort: projectConfig.defaultPort,
1140
- framework: projectConfig.framework,
1141
- userSetPort,
1142
- logFile,
1143
- profileDir,
1144
- serverCommand,
1145
- debug: options.debug,
1146
- serversOnly: options.serversOnly,
1147
- commandName,
1148
- startupTimeoutSeconds,
1149
- browserNavigationTimeoutSeconds,
1150
- tail: options.tail,
1151
- // TUI is opt-in now; only enable when explicitly requested and not in debug
1152
- tui: options.tui === true && !options.debug,
1153
- portless: portlessEnabled,
1154
- customServerCommand: Boolean(options.command),
1155
- dateTimeFormat: options.dateTime || "local",
1156
- pluginReactScan: options.pluginReactScan || false,
1157
- agentName: options.agentName || undefined,
1158
- skillsAgentId: skillsAgentId || undefined,
1159
- autoSkills: options.skills !== false ? options.autoSkills || false : false,
1160
- installSkills: options.skills !== false,
1161
- screenshotsDir: options.screenshotsDir
1162
- });
1163
- }
1164
- catch (error) {
1165
- const message = error instanceof Error ? error.message : String(error);
1166
- console.error(chalk.red(`❌ Failed to start development environment: ${message}`));
1167
- process.exit(1);
1168
- }
1169
- });
1170
- const portlessCommand = program.command("portless").description("Set up the canonical port-free Portless proxy");
1171
- portlessCommand
1172
- .command("status")
1173
- .description("Check whether canonical Portless HTTPS routing is ready")
1174
- .option("--json", "Output machine-readable Portless status")
1175
- .action((options) => {
1176
- const status = getPortlessProxyStatus();
1177
- if (options.json) {
1178
- console.log(JSON.stringify(status, null, 2));
1179
- return;
1180
- }
1181
- console.log(status.canonical
1182
- ? chalk.green("Canonical Portless HTTPS routing is ready.")
1183
- : chalk.yellow("Portless setup is required."));
1184
- if (status.target)
1185
- console.log(chalk.gray(`Proxy target: ${status.target}`));
1186
- console.log(chalk.gray(`Startup service: ${status.serviceInstalled ? "installed" : "not installed"}`));
1187
- if (status.setupRequired)
1188
- console.log(chalk.cyan("Run `d3k portless setup` once before starting d3k."));
1189
- process.exitCode = status.setupRequired ? 1 : 0;
1190
- });
1191
- portlessCommand
1192
- .command("setup")
1193
- .description("Install the HTTPS Portless proxy as an OS startup service on port 443")
1194
- .action(async () => {
1195
- console.log(chalk.cyan("Installing the Portless HTTPS service on port 443..."));
1196
- const result = await installPortlessService();
1197
- if (!result.success) {
1198
- console.error(chalk.red(`Portless service setup failed: ${result.error || "unknown error"}`));
1199
- process.exit(1);
1200
- }
1201
- console.log(chalk.green("Portless is ready. d3k will now use URLs such as https://my-app.localhost."));
1202
- });
1203
- // Upgrade command
1204
- program
1205
- .command("upgrade")
1206
- .description("Upgrade dev3000 to the latest version")
1207
- .option("--check", "Only check for updates without upgrading")
1208
- .action(async (options) => {
1209
- console.log(chalk.cyan("Checking for updates...\n"));
1210
- const versionInfo = await checkForUpdates();
1211
- console.log(chalk.white(`Current version: ${chalk.yellow(versionInfo.currentVersion)}`));
1212
- if (versionInfo.latestVersion) {
1213
- console.log(chalk.white(`Latest version: ${chalk.green(versionInfo.latestVersion)}`));
1214
- }
1215
- else {
1216
- console.log(chalk.gray("Could not fetch latest version from npm registry"));
1217
- }
1218
- if (!versionInfo.updateAvailable) {
1219
- console.log(chalk.green("\n✓ You're already on the latest version!"));
1220
- process.exit(0);
1221
- }
1222
- console.log(chalk.yellow(`\n↑ Update available: ${versionInfo.currentVersion} → ${versionInfo.latestVersion}`));
1223
- if (options.check) {
1224
- const upgradeCmd = getUpgradeCommand(versionInfo.packageManager);
1225
- console.log(chalk.cyan(`\nTo upgrade, run: ${chalk.white(upgradeCmd)}`));
1226
- console.log(chalk.gray(`Or run: ${chalk.white("d3k upgrade")}`));
1227
- process.exit(0);
1228
- }
1229
- console.log("");
1230
- const result = performUpgrade();
1231
- if (result.success) {
1232
- console.log(chalk.green("\n✓ Upgrade completed successfully!"));
1233
- console.log(chalk.gray("Run 'd3k --version' to verify the new version."));
1234
- }
1235
- else {
1236
- console.error(chalk.red(`\n✗ Upgrade failed: ${result.error}`));
1237
- const upgradeCmd = getUpgradeCommand(versionInfo.packageManager);
1238
- console.log(chalk.yellow(`\nTry running manually: ${chalk.white(upgradeCmd)}`));
1239
- process.exit(1);
1240
- }
1241
- });
1242
- // Agent-browser command - registered for --help display only
1243
- // Actual handling happens at the top of the file before Commander runs
1244
- program
1245
- .command("agent-browser [args...]")
1246
- .description("Run the bundled agent-browser CLI. `open` requires d3k's managed browser unless --allow-new-browser is passed.")
1247
- .allowUnknownOption(true);
1248
- // Skill command - get skill content for use in prompts/workflows
1249
- program
1250
- .command("skill [name]")
1251
- .description("Get skill content or list available skills")
1252
- .option("-l, --list", "List all available skills")
1253
- .option("-v, --verbose", "Show detailed skill information")
1254
- .action(async (name, options) => {
1255
- // List skills if --list flag or no name provided
1256
- if (options.list || !name) {
1257
- if (options.verbose) {
1258
- const skills = getSkillsInfo();
1259
- if (skills.length === 0) {
1260
- console.log(chalk.yellow("No skills found."));
1261
- console.log(chalk.gray("\nSkills are loaded from:"));
1262
- console.log(chalk.gray(" • .agents/skills/ (project-local)"));
1263
- console.log(chalk.gray(" • d3k built-in skills"));
1264
- process.exit(0);
1265
- }
1266
- console.log(chalk.cyan("Available skills:\n"));
1267
- for (const skill of skills) {
1268
- console.log(chalk.white(` ${chalk.bold(skill.name)}`));
1269
- console.log(chalk.gray(` ${skill.description}`));
1270
- console.log(chalk.gray(` Path: ${skill.path}\n`));
1271
- }
1272
- }
1273
- else {
1274
- const skills = listAvailableSkills();
1275
- if (skills.length === 0) {
1276
- console.log(chalk.yellow("No skills found."));
1277
- process.exit(0);
1278
- }
1279
- console.log(chalk.cyan("Available skills:"));
1280
- for (const skill of skills) {
1281
- console.log(chalk.white(` • ${skill}`));
1282
- }
1283
- console.log(chalk.gray("\nUse 'd3k skill <name>' to get skill content"));
1284
- console.log(chalk.gray("Use 'd3k skill --list --verbose' for details"));
1285
- }
1286
- process.exit(0);
1287
- }
1288
- // Get specific skill content
1289
- const result = getSkill(name);
1290
- if (!result.found) {
1291
- console.error(chalk.red(`Error: ${result.error}`));
1292
- if (result.availableSkills && result.availableSkills.length > 0) {
1293
- console.log(chalk.yellow("\nAvailable skills:"));
1294
- for (const skill of result.availableSkills) {
1295
- console.log(chalk.gray(` • ${skill}`));
1296
- }
1297
- }
1298
- process.exit(1);
1299
- }
1300
- // Output skill content to stdout (no formatting, for piping/parsing)
1301
- console.log(result.content);
1302
- });
1303
- // Errors command - quick view of recent errors
1304
- program
1305
- .command("errors")
1306
- .description("Show recent errors from d3k logs (browser + server)")
1307
- .option("-n, --count <count>", "Number of errors to show", "10")
1308
- .option("-a, --all", "Show all errors, not just recent")
1309
- .option("-c, --context", "Show interactions before each error (for replay)")
1310
- .option("--json", "Output as JSON for parsing")
1311
- .action(async (options) => {
1312
- const { showErrors } = await import("./commands/errors.js");
1313
- await showErrors(options);
1314
- });
1315
- // Logs command - view recent logs
1316
- program
1317
- .command("logs")
1318
- .description("Show recent logs from d3k (browser + server)")
1319
- .option("-n, --count <count>", "Number of lines to show", "50")
1320
- .option("-t, --type <type>", "Filter by type: browser, server, network, all", "all")
1321
- .option("--json", "Output as JSON for parsing")
1322
- .action(async (options) => {
1323
- const { showLogs } = await import("./commands/logs.js");
1324
- await showLogs(options);
1325
- });
1326
- // Fix command - diagnose application errors from logs
1327
- program
1328
- .command("fix")
1329
- .description("Diagnose application errors from d3k logs")
1330
- .option("-f, --focus <area>", "Focus area: build, runtime, network, ui, all", "all")
1331
- .option("-t, --time <minutes>", "Minutes to analyze back", "10")
1332
- .option("--json", "Output as JSON for parsing")
1333
- .action(async (options) => {
1334
- const { fixMyApp } = await import("./commands/fix.js");
1335
- await fixMyApp(options);
1336
- });
1337
- // Crawl command - discover app URLs
1338
- program
1339
- .command("crawl")
1340
- .description("Discover URLs by crawling the app")
1341
- .option("-d, --depth <depth>", "Crawl depth (1, 2, 3, or 'all')", "1")
1342
- .option("-l, --limit <limit>", "Max links per page to follow", "3")
1343
- .action(async (options) => {
1344
- const { crawlApp } = await import("./commands/crawl.js");
1345
- await crawlApp(options);
1346
- });
1347
- program
1348
- .command("resume")
1349
- .description("Resume the last supported AI agent session for this project")
1350
- .action(async () => {
1351
- const { resumeLastAgent } = await import("./commands/resume.js");
1352
- await resumeLastAgent();
1353
- });
1354
- program
1355
- .command("status")
1356
- .description("Show whether this project's d3k runtime is ready")
1357
- .option("--json", "Output machine-readable runtime details")
1358
- .action(async (options) => {
1359
- const { printD3kStatus } = await import("./commands/status.js");
1360
- process.exitCode = printD3kStatus(options);
1361
- });
1362
- // CDP port command - get the CDP port from the session file
1363
- program
1364
- .command("cdp-port")
1365
- .description("Output the CDP port for the current d3k session (for use in scripts)")
1366
- .action(() => {
1367
- console.log(getSessionCdpPort() || "9222");
1368
- });
1369
- program.parse();
1
+ import "./runtime/cli.js";
1370
2
  //# sourceMappingURL=cli.js.map