dev3000 0.0.174 → 0.0.175

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 (71) hide show
  1. package/README.md +0 -4
  2. package/dist/cdp-monitor.d.ts +3 -0
  3. package/dist/cdp-monitor.d.ts.map +1 -1
  4. package/dist/cdp-monitor.js +225 -38
  5. package/dist/cdp-monitor.js.map +1 -1
  6. package/dist/cli.js +172 -217
  7. package/dist/cli.js.map +1 -1
  8. package/dist/commands/crawl.d.ts.map +1 -1
  9. package/dist/commands/crawl.js +4 -43
  10. package/dist/commands/crawl.js.map +1 -1
  11. package/dist/commands/errors.d.ts.map +1 -1
  12. package/dist/commands/errors.js +4 -53
  13. package/dist/commands/errors.js.map +1 -1
  14. package/dist/commands/fix.d.ts.map +1 -1
  15. package/dist/commands/fix.js +5 -74
  16. package/dist/commands/fix.js.map +1 -1
  17. package/dist/commands/logs.d.ts.map +1 -1
  18. package/dist/commands/logs.js +4 -53
  19. package/dist/commands/logs.js.map +1 -1
  20. package/dist/commands/skill-runner.d.ts +14 -0
  21. package/dist/commands/skill-runner.d.ts.map +1 -0
  22. package/dist/commands/skill-runner.js +494 -0
  23. package/dist/commands/skill-runner.js.map +1 -0
  24. package/dist/dev-environment.d.ts +4 -3
  25. package/dist/dev-environment.d.ts.map +1 -1
  26. package/dist/dev-environment.js +111 -95
  27. package/dist/dev-environment.js.map +1 -1
  28. package/dist/skills/d3k/internal-skill.md +145 -0
  29. package/dist/skills/index.test.ts +28 -1
  30. package/dist/skills/index.ts +58 -7
  31. package/dist/utils/agent-browser.d.ts.map +1 -1
  32. package/dist/utils/agent-browser.js +6 -3
  33. package/dist/utils/agent-browser.js.map +1 -1
  34. package/dist/utils/agent-detection.d.ts +1 -0
  35. package/dist/utils/agent-detection.d.ts.map +1 -1
  36. package/dist/utils/agent-detection.js +11 -0
  37. package/dist/utils/agent-detection.js.map +1 -1
  38. package/dist/utils/agent-selection.js +3 -3
  39. package/dist/utils/agent-selection.js.map +1 -1
  40. package/dist/utils/browser-command-argv.d.ts +1 -1
  41. package/dist/utils/browser-command-argv.d.ts.map +1 -1
  42. package/dist/utils/browser-command-argv.js +1 -1
  43. package/dist/utils/browser-command-argv.js.map +1 -1
  44. package/dist/utils/project-name.d.ts +2 -0
  45. package/dist/utils/project-name.d.ts.map +1 -1
  46. package/dist/utils/project-name.js +6 -0
  47. package/dist/utils/project-name.js.map +1 -1
  48. package/dist/utils/session.d.ts +14 -0
  49. package/dist/utils/session.d.ts.map +1 -0
  50. package/dist/utils/session.js +65 -0
  51. package/dist/utils/session.js.map +1 -0
  52. package/dist/utils/version-check.js +2 -2
  53. package/dist/utils/version-check.js.map +1 -1
  54. package/package.json +8 -18
  55. package/dist/commands/cloud-check-pr.d.ts +0 -9
  56. package/dist/commands/cloud-check-pr.d.ts.map +0 -1
  57. package/dist/commands/cloud-check-pr.js +0 -243
  58. package/dist/commands/cloud-check-pr.js.map +0 -1
  59. package/dist/commands/cloud-fix.d.ts +0 -13
  60. package/dist/commands/cloud-fix.d.ts.map +0 -1
  61. package/dist/commands/cloud-fix.js +0 -79
  62. package/dist/commands/cloud-fix.js.map +0 -1
  63. package/dist/commands/find-component.d.ts +0 -8
  64. package/dist/commands/find-component.d.ts.map +0 -1
  65. package/dist/commands/find-component.js +0 -182
  66. package/dist/commands/find-component.js.map +0 -1
  67. package/dist/skills/d3k/SKILL.md +0 -126
  68. package/dist/skills/index.d.ts +0 -46
  69. package/dist/skills/index.d.ts.map +0 -1
  70. package/dist/skills/index.js +0 -174
  71. package/dist/skills/index.js.map +0 -1
package/dist/cli.js CHANGED
@@ -63,108 +63,59 @@ function findAgentBrowser() {
63
63
  }
64
64
  return "agent-browser";
65
65
  }
66
- function findNextBrowserCli() {
67
- if (process.env.NEXT_BROWSER_PATH) {
68
- const runnablePath = getRunnablePath(process.env.NEXT_BROWSER_PATH);
69
- if (runnablePath) {
70
- return runnablePath;
71
- }
72
- }
73
- const cwd = process.cwd();
74
- const home = homedir();
75
- const searchPaths = [
76
- join(home, ".bun", "install", "global", "node_modules", "dev3000", "node_modules", "@vercel", "next-browser", "dist", "cli.js"),
77
- join(home, ".bun", "install", "global", "node_modules", "@vercel", "next-browser", "dist", "cli.js"),
78
- join(cwd, "node_modules", "@vercel", "next-browser", "dist", "cli.js"),
79
- join(cwd, "..", "node_modules", "@vercel", "next-browser", "dist", "cli.js")
80
- ];
81
- const globalNodeModules = [
82
- join("/usr", "local", "lib", "node_modules"),
83
- join("/opt", "homebrew", "lib", "node_modules")
84
- ];
85
- for (const root of globalNodeModules) {
86
- searchPaths.push(join(root, "dev3000", "node_modules", "@vercel", "next-browser", "dist", "cli.js"));
87
- searchPaths.push(join(root, "@vercel", "next-browser", "dist", "cli.js"));
88
- }
89
- for (const searchPath of searchPaths) {
90
- if (existsSync(searchPath)) {
91
- return searchPath;
92
- }
66
+ function getSessionCdpPort() {
67
+ const session = findCurrentSession();
68
+ const cdpUrl = session?.cdpUrl;
69
+ if (!cdpUrl) {
70
+ return null;
93
71
  }
94
- return null;
95
- }
96
- function getProjectBrowserToolPreference() {
97
72
  try {
98
- const sessionFile = join(getProjectDir(), "session.json");
99
- if (!existsSync(sessionFile)) {
100
- return "agent-browser";
73
+ const parsed = new URL(cdpUrl);
74
+ if (/^\d+$/.test(parsed.port)) {
75
+ return parsed.port;
101
76
  }
102
- const sessionInfo = JSON.parse(readFileSync(sessionFile, "utf8"));
103
- return sessionInfo.preferredBrowserTool === "next-browser" ? "next-browser" : "agent-browser";
104
77
  }
105
78
  catch {
106
- return "agent-browser";
79
+ const match = cdpUrl.match(/:(\d+)/);
80
+ if (match) {
81
+ return match[1];
82
+ }
107
83
  }
84
+ return null;
108
85
  }
109
- function runLocalBrowserTool(browserTool, args) {
86
+ function runLocalBrowserTool(args) {
110
87
  const env = { ...process.env };
111
88
  ensureCommandPath(env);
112
- if (browserTool === "agent-browser") {
113
- const subcommandIndex = args.findIndex((arg) => !arg.startsWith("-") && !arg.startsWith("@") && arg !== "9222");
114
- const subcommand = subcommandIndex >= 0 ? args[subcommandIndex] : null;
115
- if (subcommand === "errors") {
116
- console.log("\x1b[33m💡 Tip: Using `d3k errors` instead (shows browser + server errors)\x1b[0m\n");
117
- const d3kBin = process.argv[1];
118
- const result = spawnSync(d3kBin, ["errors"], { stdio: "inherit", shell: false });
119
- process.exit(result.status ?? 0);
120
- }
121
- if (subcommand === "console") {
122
- console.log("\x1b[33m💡 Tip: Using `d3k logs` instead (shows browser + server logs)\x1b[0m\n");
123
- const d3kBin = process.argv[1];
124
- const result = spawnSync(d3kBin, ["logs", "--type", "browser"], { stdio: "inherit", shell: false });
125
- process.exit(result.status ?? 0);
126
- }
127
- const binaryPath = findAgentBrowser();
128
- const result = spawnSync(binaryPath, args, {
129
- stdio: "pipe",
130
- shell: false,
131
- env
132
- });
133
- if (result.stdout?.length > 0) {
134
- process.stdout.write(result.stdout);
135
- }
136
- if (result.stderr?.length > 0) {
137
- process.stderr.write(result.stderr);
138
- }
139
- if (result.error) {
140
- console.error(`\nError spawning agent-browser: ${result.error.message}`);
141
- console.error(`Binary path: ${binaryPath}`);
142
- process.exit(1);
89
+ const subcommandIndex = args.findIndex((arg) => !arg.startsWith("-") && !arg.startsWith("@") && arg !== "9222");
90
+ const subcommand = subcommandIndex >= 0 ? args[subcommandIndex] : null;
91
+ if (subcommand === "errors") {
92
+ console.log("\x1b[33m💡 Tip: Using `d3k errors` instead (shows browser + server errors)\x1b[0m\n");
93
+ const d3kBin = process.argv[1];
94
+ const result = spawnSync(d3kBin, ["errors"], { stdio: "inherit", shell: false });
95
+ process.exit(result.status ?? 0);
96
+ }
97
+ if (subcommand === "console") {
98
+ console.log("\x1b[33m💡 Tip: Using `d3k logs` instead (shows browser + server logs)\x1b[0m\n");
99
+ const d3kBin = process.argv[1];
100
+ const result = spawnSync(d3kBin, ["logs", "--type", "browser"], { stdio: "inherit", shell: false });
101
+ process.exit(result.status ?? 0);
102
+ }
103
+ const binaryPath = findAgentBrowser();
104
+ if (subcommand !== "connect" &&
105
+ subcommand !== "close" &&
106
+ !args.includes("--cdp") &&
107
+ !args.includes("--profile") &&
108
+ !args.includes("--profile-dir")) {
109
+ const cdpPort = getSessionCdpPort();
110
+ if (cdpPort) {
111
+ spawnSync(binaryPath, ["connect", cdpPort], { stdio: "pipe", env, shell: false });
143
112
  }
144
- process.exit(result.status ?? 1);
145
113
  }
146
- const nodeVersionCheck = spawnSync("node", ["-e", "process.stdout.write(process.versions.node)"], {
147
- encoding: "utf-8",
114
+ const result = spawnSync(binaryPath, args, {
115
+ stdio: "pipe",
148
116
  env,
149
117
  shell: false
150
118
  });
151
- const nodeVersion = nodeVersionCheck.stdout?.trim();
152
- const nodeMajor = nodeVersion ? Number.parseInt(nodeVersion.split(".")[0] || "0", 10) : 0;
153
- if (nodeVersionCheck.status !== 0 || Number.isNaN(nodeMajor) || nodeMajor < 20) {
154
- console.error("\nnext-browser requires a system Node.js runtime >= 20.");
155
- console.error("Install Node 20+ or keep using agent-browser for local d3k sessions.");
156
- process.exit(1);
157
- }
158
- const cliPath = findNextBrowserCli();
159
- const nextBrowserHome = join(getProjectDir(), "next-browser-home");
160
- mkdirSync(nextBrowserHome, { recursive: true });
161
- env.HOME = nextBrowserHome;
162
- env.USERPROFILE = nextBrowserHome;
163
- const result = spawnSync(cliPath ? "node" : "next-browser", cliPath ? [cliPath, ...args] : args, {
164
- stdio: "pipe",
165
- shell: false,
166
- env
167
- });
168
119
  if (result.stdout?.length > 0) {
169
120
  process.stdout.write(result.stdout);
170
121
  }
@@ -172,10 +123,8 @@ function runLocalBrowserTool(browserTool, args) {
172
123
  process.stderr.write(result.stderr);
173
124
  }
174
125
  if (result.error) {
175
- console.error(`\nError spawning next-browser: ${result.error.message}`);
176
- if (cliPath) {
177
- console.error(`CLI path: ${cliPath}`);
178
- }
126
+ console.error(`\nError spawning agent-browser: ${result.error.message}`);
127
+ console.error(`Binary path: ${binaryPath}`);
179
128
  process.exit(1);
180
129
  }
181
130
  process.exit(result.status ?? 1);
@@ -183,13 +132,7 @@ function runLocalBrowserTool(browserTool, args) {
183
132
  import { getBrowserCommandInvocation } from "./utils/browser-command-argv.js";
184
133
  const browserCommandInvocation = getBrowserCommandInvocation(process.argv.slice(2));
185
134
  if (browserCommandInvocation && (process.argv[1]?.includes("d3k") || process.argv[1]?.includes("dev3000"))) {
186
- const { browserCommand, args } = browserCommandInvocation;
187
- const browserTool = browserCommand === "browser"
188
- ? getProjectBrowserToolPreference()
189
- : browserCommand === "next-browser"
190
- ? "next-browser"
191
- : "agent-browser";
192
- runLocalBrowserTool(browserTool, args);
135
+ runLocalBrowserTool(browserCommandInvocation.args);
193
136
  }
194
137
  import chalk from "chalk";
195
138
  import { execSync, spawnSync } from "child_process";
@@ -197,17 +140,17 @@ import { Command } from "commander";
197
140
  import { accessSync, appendFileSync, chmodSync, constants, copyFileSync, existsSync, mkdirSync, readFileSync } from "fs";
198
141
  import { homedir } from "os";
199
142
  import { detect } from "package-manager-detector";
200
- import { dirname, join } from "path";
143
+ import { dirname, join, resolve } from "path";
201
144
  import { fileURLToPath } from "url";
202
- import { cloudCheckPR } from "./commands/cloud-check-pr.js";
203
- import { cloudFix } from "./commands/cloud-fix.js";
145
+ import { runRemoteSkillCommand, shouldUseRemoteSkillRunner } from "./commands/skill-runner.js";
204
146
  import { createPersistentLogFile, findAvailablePort, startDevEnvironment } from "./dev-environment.js";
205
- import { getBundledSkillsPath, getSkill, getSkillsInfo, listAvailableSkills } from "./skills/index.js";
147
+ import { getBundledD3kSkillPath, getSkill, getSkillsInfo, listAvailableSkills } from "./skills/index.js";
206
148
  import { detectAIAgent } from "./utils/agent-detection.js";
207
149
  import { getAvailableAgents, getSkillsAgentId } from "./utils/agent-selection.js";
208
150
  import { ensureD3kHomeDir } from "./utils/d3k-dir.js";
209
151
  import { readProjectAgentName } from "./utils/project-metadata.js";
210
152
  import { getProjectDir, getProjectDisplayName } from "./utils/project-name.js";
153
+ import { findCurrentSession } from "./utils/session.js";
211
154
  import { checkForSkillUpdates, getApplicablePackages, getSkillsPathForLocation, installSkillPackage, isPackageInstalled, updateSkills } from "./utils/skill-installer.js";
212
155
  import { DEFAULT_TMUX_CONFIG, generateSessionName, generateTmuxCommands, getTmuxInstallInstructions, isTmuxInstalled } from "./utils/tmux-helpers.js";
213
156
  import { loadUserConfig, saveUserConfig } from "./utils/user-config.js";
@@ -269,49 +212,81 @@ process.on("unhandledRejection", (reason) => {
269
212
  process.exit(1);
270
213
  }
271
214
  });
215
+ function shellQuote(value) {
216
+ return `'${value.replace(/'/g, `'\\''`)}'`;
217
+ }
218
+ function validatePortOption(port) {
219
+ if (!/^\d+$/.test(port)) {
220
+ throw new Error("--port must be a numeric port number.");
221
+ }
222
+ const parsed = Number.parseInt(port, 10);
223
+ if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535) {
224
+ throw new Error("--port must be between 1 and 65535.");
225
+ }
226
+ return String(parsed);
227
+ }
228
+ function validatePositiveIntegerOption(name, value) {
229
+ if (!/^\d+$/.test(value)) {
230
+ throw new Error(`${name} must be a positive integer.`);
231
+ }
232
+ const parsed = Number.parseInt(value, 10);
233
+ if (!Number.isInteger(parsed) || parsed <= 0) {
234
+ throw new Error(`${name} must be a positive integer.`);
235
+ }
236
+ return String(parsed);
237
+ }
238
+ function validateScriptOption(script) {
239
+ if (!/^[A-Za-z0-9._:/-]+$/.test(script)) {
240
+ throw new Error("--script may only contain letters, numbers, dots, slashes, colons, underscores, and hyphens.");
241
+ }
242
+ return script;
243
+ }
244
+ function validateDateTimeOption(value) {
245
+ if (value !== "local" && value !== "utc") {
246
+ throw new Error("--date-time must be either 'local' or 'utc'.");
247
+ }
248
+ return value;
249
+ }
272
250
  /**
273
251
  * Build the d3k command string with forwarded options.
274
252
  */
275
253
  function buildD3kCommandWithOptions(options) {
276
254
  const d3kBase = process.argv[1].endsWith("d3k") ? "d3k" : "dev3000";
277
- const args = [d3kBase];
255
+ const args = [shellQuote(d3kBase)];
278
256
  // Forward options that were explicitly set
279
257
  if (options.port)
280
- args.push(`--port ${options.port}`);
258
+ args.push("--port", shellQuote(options.port));
281
259
  if (options.script)
282
- args.push(`--script ${options.script}`);
260
+ args.push("--script", shellQuote(options.script));
283
261
  if (options.command)
284
- args.push(`--command "${options.command.replace(/"/g, '\\"')}"`);
262
+ args.push("--command", shellQuote(options.command));
285
263
  if (options.startupTimeout)
286
- args.push(`--startup-timeout ${options.startupTimeout}`);
264
+ args.push("--startup-timeout", shellQuote(options.startupTimeout));
287
265
  if (options.browserNavigationTimeout) {
288
- args.push(`--browser-navigation-timeout ${options.browserNavigationTimeout}`);
266
+ args.push("--browser-navigation-timeout", shellQuote(options.browserNavigationTimeout));
289
267
  }
290
268
  if (options.profileDir)
291
- args.push(`--profile-dir "${options.profileDir}"`);
269
+ args.push("--profile-dir", shellQuote(options.profileDir));
292
270
  if (options.browserTool)
293
- args.push(`--browser-tool ${options.browserTool}`);
271
+ args.push("--browser-tool", shellQuote(options.browserTool));
294
272
  if (options.browser)
295
- args.push(`--browser "${options.browser}"`);
273
+ args.push("--browser", shellQuote(options.browser));
296
274
  if (options.serversOnly)
297
275
  args.push("--servers-only");
298
276
  if (options.headless)
299
277
  args.push("--headless");
300
278
  if (options.dateTime)
301
- args.push(`--date-time ${options.dateTime}`);
279
+ args.push("--date-time", shellQuote(options.dateTime));
302
280
  if (options.pluginReactScan)
303
281
  args.push("--plugin-react-scan");
304
282
  if (options.agentName)
305
- args.push(`--agent-name ${options.agentName}`);
283
+ args.push("--agent-name", shellQuote(options.agentName));
306
284
  return args.join(" ");
307
285
  }
308
286
  function ensureClaudeD3kSkill() {
309
287
  try {
310
- const bundledSkillsDir = getBundledSkillsPath();
311
- if (!bundledSkillsDir)
312
- return;
313
- const bundledSkillPath = join(bundledSkillsDir, "d3k", "SKILL.md");
314
- if (!existsSync(bundledSkillPath))
288
+ const bundledSkillPath = getBundledD3kSkillPath();
289
+ if (!bundledSkillPath)
315
290
  return;
316
291
  const skillsRoot = join(process.cwd(), ".claude", "skills");
317
292
  const skillDir = join(skillsRoot, "d3k");
@@ -778,7 +753,10 @@ program
778
753
  .option("--startup-timeout <seconds>", "Seconds to wait for your app server to become reachable", "30")
779
754
  .option("--browser-navigation-timeout <seconds>", "Seconds to wait for browser-initiated page navigation commands", "60")
780
755
  .option("--profile-dir <dir>", "Chrome profile directory")
781
- .option("--browser-tool <tool>", "Preferred local browser CLI: 'agent-browser' (default) or 'next-browser'", "agent-browser")
756
+ .option("--data-dir <dir>", "Override the per-project data directory (default: ~/.d3k/<project>/). Holds session.json, logs, screenshots, and the Chrome profile.")
757
+ .option("--log-file <path>", "Override the consolidated log file path (default: <data-dir>/logs/<timestamp>.log)")
758
+ .option("--screenshots-dir <dir>", "Override the screenshots directory (default: <data-dir>/screenshots)")
759
+ .option("--browser-tool <tool>", "Preferred local browser CLI: 'agent-browser'", "agent-browser")
782
760
  .option("--browser <path>", "Full path to browser executable (e.g. for Arc: '/Applications/Arc.app/Contents/MacOS/Arc')")
783
761
  .option("--servers-only", "Run servers only, skip browser launch")
784
762
  .option("--debug", "Enable debug logging to console (automatically disables TUI)")
@@ -794,10 +772,34 @@ program
794
772
  .option("--agent-name <name>", "Selected agent name (internal)")
795
773
  .option("--no-agent", "Skip agent selection prompt and run d3k standalone")
796
774
  .action(async (options) => {
775
+ if (options.dataDir) {
776
+ process.env.D3K_DATA_DIR = resolve(options.dataDir);
777
+ }
797
778
  const projectName = getProjectDisplayName();
798
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
+ }
799
800
  // Load user config early so it can be used for --with-agent and agent selection flows
800
801
  const userConfig = loadUserConfig();
802
+ const agentDetection = detectAIAgent();
801
803
  // Apply browser default from user config if not explicitly provided via CLI
802
804
  const browserOption = options.browser || userConfig.browser;
803
805
  // Handle --with-agent by spawning tmux with split panes
@@ -824,7 +826,23 @@ program
824
826
  const insideTmux = !!process.env.TMUX;
825
827
  let selectedAgent = null;
826
828
  let didPromptAgentSelection = false;
827
- let skillsAgentId = options.agentName ? getSkillsAgentId(options.agentName) : null;
829
+ let skillsAgentId = options.agentName
830
+ ? getSkillsAgentId(options.agentName)
831
+ : agentDetection.agentId || null;
832
+ if (agentDetection.isAgent) {
833
+ if (options.tui !== false) {
834
+ if (options.debug) {
835
+ console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-disabling TUI`);
836
+ }
837
+ options.tui = false;
838
+ }
839
+ if (agentDetection.agentId && options.skills !== false && !options.autoSkills) {
840
+ if (options.debug) {
841
+ console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-enabling project skill installs`);
842
+ }
843
+ options.autoSkills = true;
844
+ }
845
+ }
828
846
  if (process.stdin.isTTY && options.agent !== false && options.tui !== false && !options.debug && !insideTmux) {
829
847
  // Clear the terminal so d3k UI starts at the top of the screen
830
848
  process.stdout.write("\x1B[2J\x1B[0f");
@@ -968,6 +986,7 @@ program
968
986
  script: options.script,
969
987
  command: options.command,
970
988
  startupTimeout: options.startupTimeout,
989
+ browserNavigationTimeout: options.browserNavigationTimeout,
971
990
  profileDir: options.profileDir,
972
991
  browserTool: options.browserTool,
973
992
  browser: browserOption,
@@ -981,7 +1000,7 @@ program
981
1000
  }
982
1001
  }
983
1002
  if (options.autoSkills && options.skills !== false && !skillsAgentId) {
984
- skillsAgentId = "codex";
1003
+ skillsAgentId = agentDetection.agentId || "codex";
985
1004
  }
986
1005
  // Detect project type and configuration
987
1006
  const projectConfig = await detectProjectType(options.debug);
@@ -999,22 +1018,13 @@ program
999
1018
  console.error(chalk.yellow(` d3k --command "node server.js" -p 3000\n`));
1000
1019
  process.exit(1);
1001
1020
  }
1002
- // Detect if running under an AI agent and auto-disable TUI
1003
- const agentDetection = detectAIAgent();
1004
- if (agentDetection.isAgent && options.tui !== false) {
1005
- if (options.debug) {
1006
- console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-disabling TUI`);
1007
- }
1008
- // Override TUI setting to false when agent is detected
1009
- options.tui = false;
1010
- }
1011
1021
  // Use defaults from project detection if not explicitly provided
1012
1022
  const port = options.port || projectConfig.defaultPort;
1013
1023
  const script = options.script || projectConfig.defaultScript;
1014
1024
  const userSetPort = options.port !== undefined;
1015
1025
  const startupTimeoutSeconds = Number.parseInt(options.startupTimeout, 10);
1016
1026
  const browserNavigationTimeoutSeconds = Number.parseInt(options.browserNavigationTimeout, 10);
1017
- const browserTool = options.browserTool === "next-browser" ? "next-browser" : "agent-browser";
1027
+ const browserTool = "agent-browser";
1018
1028
  if (Number.isNaN(startupTimeoutSeconds) || startupTimeoutSeconds <= 0) {
1019
1029
  console.error(chalk.red("\n❌ --startup-timeout must be a positive integer (seconds).\n"));
1020
1030
  process.exit(1);
@@ -1094,7 +1104,7 @@ program
1094
1104
  const commandName = executablePath.endsWith("/d3k") || executablePath.includes("/d3k") ? "d3k" : "dev3000";
1095
1105
  try {
1096
1106
  // Create persistent log file
1097
- const logFile = createPersistentLogFile();
1107
+ const logFile = createPersistentLogFile(options.logFile);
1098
1108
  // Use a per-project Chrome profile by default so concurrent d3k sessions
1099
1109
  // don't fight over the same browser state. Honor explicit overrides.
1100
1110
  const profileDir = options.profileDir || join(getProjectDir(), "chrome-profile");
@@ -1126,7 +1136,8 @@ program
1126
1136
  agentName: options.agentName || undefined,
1127
1137
  skillsAgentId: skillsAgentId || undefined,
1128
1138
  autoSkills: options.skills !== false ? options.autoSkills || false : false,
1129
- installSkills: options.skills !== false
1139
+ installSkills: options.skills !== false,
1140
+ screenshotsDir: options.screenshotsDir
1130
1141
  });
1131
1142
  }
1132
1143
  catch (error) {
@@ -1134,41 +1145,6 @@ program
1134
1145
  process.exit(1);
1135
1146
  }
1136
1147
  });
1137
- // Cloud commands
1138
- const cloud = program.command("cloud").description("Cloud-based tools using Vercel Sandbox");
1139
- // Cloud fix command
1140
- cloud
1141
- .command("fix")
1142
- .description("Start a cloud fix workflow for the current project")
1143
- .option("--repo <url>", "Repository URL (e.g. https://github.com/user/repo)")
1144
- .option("--branch <name>", "Git branch to test")
1145
- .option("--project-dir <dir>", "Project directory within repo (e.g. 'www')")
1146
- .option("--debug", "Enable debug logging")
1147
- .action(async (options) => {
1148
- try {
1149
- await cloudFix(options);
1150
- }
1151
- catch (error) {
1152
- console.error(chalk.red("❌ Cloud fix failed:"), error);
1153
- process.exit(1);
1154
- }
1155
- });
1156
- // Cloud check-pr command
1157
- cloud
1158
- .command("check-pr [pr-number]")
1159
- .description("Verify a PR's changes work as expected using Vercel preview deployment")
1160
- .option("--repo <url>", "Repository URL (optional, auto-detected from git)")
1161
- .option("--url <preview-url>", "Preview deployment URL (optional, auto-detected from Vercel)")
1162
- .option("--debug", "Enable debug logging")
1163
- .action(async (prNumber, options) => {
1164
- try {
1165
- await cloudCheckPR({ ...options, prNumber });
1166
- }
1167
- catch (error) {
1168
- console.error(chalk.red("❌ Cloud check-pr failed:"), error);
1169
- process.exit(1);
1170
- }
1171
- });
1172
1148
  // Upgrade command
1173
1149
  program
1174
1150
  .command("upgrade")
@@ -1214,21 +1190,37 @@ program
1214
1190
  .command("agent-browser [args...]")
1215
1191
  .description("Run the bundled agent-browser CLI (e.g., d3k agent-browser screenshot /tmp/foo.png)")
1216
1192
  .allowUnknownOption(true);
1217
- program
1218
- .command("next-browser [args...]")
1219
- .description("Run the bundled next-browser CLI (e.g., d3k next-browser open http://localhost:3000)")
1220
- .allowUnknownOption(true);
1221
- program
1222
- .command("browser [args...]")
1223
- .description("Run the preferred local browser CLI for this session")
1224
- .allowUnknownOption(true);
1225
1193
  // Skill command - get skill content for use in prompts/workflows
1226
1194
  program
1227
1195
  .command("skill [name]")
1228
1196
  .description("Get skill content or list available skills")
1229
1197
  .option("-l, --list", "List all available skills")
1230
1198
  .option("-v, --verbose", "Show detailed skill information")
1231
- .action((name, options) => {
1199
+ .option("--team <team>", "Override the inferred Vercel team slug or ID for a hosted skill run")
1200
+ .option("--project <project>", "Override the inferred Vercel project name or ID for a hosted skill run")
1201
+ .option("--branch <branch>", "Git branch or ref to scan")
1202
+ .option("--project-dir <dir>", "Project root directory inside the repository")
1203
+ .option("--base-url <url>", "dev3000 base URL")
1204
+ .option("--wait", "Wait for the run to finish")
1205
+ .option("--json", "Print machine-readable JSON")
1206
+ .option("--no-install", "Do not install or repair the team skill runner project before starting")
1207
+ .action(async (name, options) => {
1208
+ if (shouldUseRemoteSkillRunner(name, options)) {
1209
+ try {
1210
+ await runRemoteSkillCommand(name, options);
1211
+ }
1212
+ catch (error) {
1213
+ const message = error instanceof Error ? error.message : String(error);
1214
+ if (options.json) {
1215
+ console.log(JSON.stringify({ success: false, error: message }, null, 2));
1216
+ }
1217
+ else {
1218
+ console.error(chalk.red(`Error: ${message}`));
1219
+ }
1220
+ process.exit(1);
1221
+ }
1222
+ return;
1223
+ }
1232
1224
  // List skills if --list flag or no name provided
1233
1225
  if (options.list || !name) {
1234
1226
  if (options.verbose) {
@@ -1321,14 +1313,6 @@ program
1321
1313
  const { crawlApp } = await import("./commands/crawl.js");
1322
1314
  await crawlApp(options);
1323
1315
  });
1324
- // Find-component command - map DOM to React source
1325
- program
1326
- .command("find-component <selector>")
1327
- .description("Find React component source for a DOM selector")
1328
- .action(async (selector) => {
1329
- const { findComponent } = await import("./commands/find-component.js");
1330
- await findComponent(selector);
1331
- });
1332
1316
  program
1333
1317
  .command("resume")
1334
1318
  .description("Resume the last supported AI agent session for this project")
@@ -1340,37 +1324,8 @@ program
1340
1324
  program
1341
1325
  .command("cdp-port")
1342
1326
  .description("Output the CDP port for the current d3k session (for use in scripts)")
1343
- .action(async () => {
1344
- const sessionDir = join(homedir(), ".d3k");
1345
- const projectDir = getProjectDir();
1346
- const projectName = projectDir.split("/").pop() || "unknown";
1347
- // Try to find session.json in ~/.d3k/{project-name}/
1348
- const entries = existsSync(sessionDir)
1349
- ? await import("fs").then((fs) => fs.readdirSync(sessionDir, { withFileTypes: true }))
1350
- : [];
1351
- for (const entry of entries) {
1352
- if (entry.isDirectory() && entry.name.startsWith(projectName.substring(0, 20))) {
1353
- const sessionFile = join(sessionDir, entry.name, "session.json");
1354
- if (existsSync(sessionFile)) {
1355
- try {
1356
- const content = JSON.parse(readFileSync(sessionFile, "utf-8"));
1357
- if (content.cdpUrl) {
1358
- // Extract port from URL like "ws://localhost:9223/devtools/browser/..."
1359
- const match = content.cdpUrl.match(/:(\d+)/);
1360
- if (match) {
1361
- console.log(match[1]);
1362
- process.exit(0);
1363
- }
1364
- }
1365
- }
1366
- catch {
1367
- // Continue searching
1368
- }
1369
- }
1370
- }
1371
- }
1372
- // Default to 9222 if no session found
1373
- console.log("9222");
1327
+ .action(() => {
1328
+ console.log(getSessionCdpPort() || "9222");
1374
1329
  });
1375
1330
  program.parse();
1376
1331
  //# sourceMappingURL=cli.js.map