dev3000 0.0.173 → 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 (83) hide show
  1. package/README.md +0 -4
  2. package/dist/cdp-monitor.d.ts +21 -2
  3. package/dist/cdp-monitor.d.ts.map +1 -1
  4. package/dist/cdp-monitor.js +449 -107
  5. package/dist/cdp-monitor.js.map +1 -1
  6. package/dist/cli.js +193 -216
  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/resume.d.ts +11 -0
  21. package/dist/commands/resume.d.ts.map +1 -0
  22. package/dist/commands/resume.js +75 -0
  23. package/dist/commands/resume.js.map +1 -0
  24. package/dist/commands/skill-runner.d.ts +14 -0
  25. package/dist/commands/skill-runner.d.ts.map +1 -0
  26. package/dist/commands/skill-runner.js +494 -0
  27. package/dist/commands/skill-runner.js.map +1 -0
  28. package/dist/dev-environment.d.ts +26 -3
  29. package/dist/dev-environment.d.ts.map +1 -1
  30. package/dist/dev-environment.js +285 -118
  31. package/dist/dev-environment.js.map +1 -1
  32. package/dist/skills/d3k/internal-skill.md +145 -0
  33. package/dist/skills/index.test.ts +28 -1
  34. package/dist/skills/index.ts +58 -7
  35. package/dist/tui-interface-opentui.d.ts +2 -0
  36. package/dist/tui-interface-opentui.d.ts.map +1 -1
  37. package/dist/tui-interface-opentui.js +17 -3
  38. package/dist/tui-interface-opentui.js.map +1 -1
  39. package/dist/utils/agent-browser.d.ts.map +1 -1
  40. package/dist/utils/agent-browser.js +6 -3
  41. package/dist/utils/agent-browser.js.map +1 -1
  42. package/dist/utils/agent-detection.d.ts +1 -0
  43. package/dist/utils/agent-detection.d.ts.map +1 -1
  44. package/dist/utils/agent-detection.js +11 -0
  45. package/dist/utils/agent-detection.js.map +1 -1
  46. package/dist/utils/agent-selection.js +4 -4
  47. package/dist/utils/agent-selection.js.map +1 -1
  48. package/dist/utils/browser-command-argv.d.ts +1 -1
  49. package/dist/utils/browser-command-argv.d.ts.map +1 -1
  50. package/dist/utils/browser-command-argv.js +1 -1
  51. package/dist/utils/browser-command-argv.js.map +1 -1
  52. package/dist/utils/project-metadata.d.ts +4 -0
  53. package/dist/utils/project-metadata.d.ts.map +1 -0
  54. package/dist/utils/project-metadata.js +48 -0
  55. package/dist/utils/project-metadata.js.map +1 -0
  56. package/dist/utils/project-name.d.ts +2 -0
  57. package/dist/utils/project-name.d.ts.map +1 -1
  58. package/dist/utils/project-name.js +6 -0
  59. package/dist/utils/project-name.js.map +1 -1
  60. package/dist/utils/session.d.ts +14 -0
  61. package/dist/utils/session.d.ts.map +1 -0
  62. package/dist/utils/session.js +65 -0
  63. package/dist/utils/session.js.map +1 -0
  64. package/dist/utils/version-check.js +2 -2
  65. package/dist/utils/version-check.js.map +1 -1
  66. package/package.json +9 -21
  67. package/dist/commands/cloud-check-pr.d.ts +0 -9
  68. package/dist/commands/cloud-check-pr.d.ts.map +0 -1
  69. package/dist/commands/cloud-check-pr.js +0 -243
  70. package/dist/commands/cloud-check-pr.js.map +0 -1
  71. package/dist/commands/cloud-fix.d.ts +0 -13
  72. package/dist/commands/cloud-fix.d.ts.map +0 -1
  73. package/dist/commands/cloud-fix.js +0 -79
  74. package/dist/commands/cloud-fix.js.map +0 -1
  75. package/dist/commands/find-component.d.ts +0 -8
  76. package/dist/commands/find-component.d.ts.map +0 -1
  77. package/dist/commands/find-component.js +0 -182
  78. package/dist/commands/find-component.js.map +0 -1
  79. package/dist/skills/d3k/SKILL.md +0 -126
  80. package/dist/skills/index.d.ts +0 -46
  81. package/dist/skills/index.d.ts.map +0 -1
  82. package/dist/skills/index.js +0 -174
  83. 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);
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 });
126
112
  }
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);
143
- }
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,16 +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";
151
+ import { readProjectAgentName } from "./utils/project-metadata.js";
209
152
  import { getProjectDir, getProjectDisplayName } from "./utils/project-name.js";
153
+ import { findCurrentSession } from "./utils/session.js";
210
154
  import { checkForSkillUpdates, getApplicablePackages, getSkillsPathForLocation, installSkillPackage, isPackageInstalled, updateSkills } from "./utils/skill-installer.js";
211
155
  import { DEFAULT_TMUX_CONFIG, generateSessionName, generateTmuxCommands, getTmuxInstallInstructions, isTmuxInstalled } from "./utils/tmux-helpers.js";
212
156
  import { loadUserConfig, saveUserConfig } from "./utils/user-config.js";
@@ -268,46 +212,81 @@ process.on("unhandledRejection", (reason) => {
268
212
  process.exit(1);
269
213
  }
270
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
+ }
271
250
  /**
272
251
  * Build the d3k command string with forwarded options.
273
252
  */
274
253
  function buildD3kCommandWithOptions(options) {
275
254
  const d3kBase = process.argv[1].endsWith("d3k") ? "d3k" : "dev3000";
276
- const args = [d3kBase];
255
+ const args = [shellQuote(d3kBase)];
277
256
  // Forward options that were explicitly set
278
257
  if (options.port)
279
- args.push(`--port ${options.port}`);
258
+ args.push("--port", shellQuote(options.port));
280
259
  if (options.script)
281
- args.push(`--script ${options.script}`);
260
+ args.push("--script", shellQuote(options.script));
282
261
  if (options.command)
283
- args.push(`--command "${options.command.replace(/"/g, '\\"')}"`);
262
+ args.push("--command", shellQuote(options.command));
284
263
  if (options.startupTimeout)
285
- args.push(`--startup-timeout ${options.startupTimeout}`);
264
+ args.push("--startup-timeout", shellQuote(options.startupTimeout));
265
+ if (options.browserNavigationTimeout) {
266
+ args.push("--browser-navigation-timeout", shellQuote(options.browserNavigationTimeout));
267
+ }
286
268
  if (options.profileDir)
287
- args.push(`--profile-dir "${options.profileDir}"`);
269
+ args.push("--profile-dir", shellQuote(options.profileDir));
288
270
  if (options.browserTool)
289
- args.push(`--browser-tool ${options.browserTool}`);
271
+ args.push("--browser-tool", shellQuote(options.browserTool));
290
272
  if (options.browser)
291
- args.push(`--browser "${options.browser}"`);
273
+ args.push("--browser", shellQuote(options.browser));
292
274
  if (options.serversOnly)
293
275
  args.push("--servers-only");
294
276
  if (options.headless)
295
277
  args.push("--headless");
296
278
  if (options.dateTime)
297
- args.push(`--date-time ${options.dateTime}`);
279
+ args.push("--date-time", shellQuote(options.dateTime));
298
280
  if (options.pluginReactScan)
299
281
  args.push("--plugin-react-scan");
300
282
  if (options.agentName)
301
- args.push(`--agent-name ${options.agentName}`);
283
+ args.push("--agent-name", shellQuote(options.agentName));
302
284
  return args.join(" ");
303
285
  }
304
286
  function ensureClaudeD3kSkill() {
305
287
  try {
306
- const bundledSkillsDir = getBundledSkillsPath();
307
- if (!bundledSkillsDir)
308
- return;
309
- const bundledSkillPath = join(bundledSkillsDir, "d3k", "SKILL.md");
310
- if (!existsSync(bundledSkillPath))
288
+ const bundledSkillPath = getBundledD3kSkillPath();
289
+ if (!bundledSkillPath)
311
290
  return;
312
291
  const skillsRoot = join(process.cwd(), ".claude", "skills");
313
292
  const skillDir = join(skillsRoot, "d3k");
@@ -772,8 +751,12 @@ program
772
751
  .option("-s, --script <script>", "Script to run (e.g. dev, main.py) - auto-detected by project type")
773
752
  .option("-c, --command <command>", "Custom command to run (overrides auto-detection and --script)")
774
753
  .option("--startup-timeout <seconds>", "Seconds to wait for your app server to become reachable", "30")
754
+ .option("--browser-navigation-timeout <seconds>", "Seconds to wait for browser-initiated page navigation commands", "60")
775
755
  .option("--profile-dir <dir>", "Chrome profile directory")
776
- .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")
777
760
  .option("--browser <path>", "Full path to browser executable (e.g. for Arc: '/Applications/Arc.app/Contents/MacOS/Arc')")
778
761
  .option("--servers-only", "Run servers only, skip browser launch")
779
762
  .option("--debug", "Enable debug logging to console (automatically disables TUI)")
@@ -789,10 +772,34 @@ program
789
772
  .option("--agent-name <name>", "Selected agent name (internal)")
790
773
  .option("--no-agent", "Skip agent selection prompt and run d3k standalone")
791
774
  .action(async (options) => {
775
+ if (options.dataDir) {
776
+ process.env.D3K_DATA_DIR = resolve(options.dataDir);
777
+ }
792
778
  const projectName = getProjectDisplayName();
793
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
+ }
794
800
  // Load user config early so it can be used for --with-agent and agent selection flows
795
801
  const userConfig = loadUserConfig();
802
+ const agentDetection = detectAIAgent();
796
803
  // Apply browser default from user config if not explicitly provided via CLI
797
804
  const browserOption = options.browser || userConfig.browser;
798
805
  // Handle --with-agent by spawning tmux with split panes
@@ -802,6 +809,7 @@ program
802
809
  script: options.script,
803
810
  command: options.command,
804
811
  startupTimeout: options.startupTimeout,
812
+ browserNavigationTimeout: options.browserNavigationTimeout,
805
813
  profileDir: options.profileDir,
806
814
  browserTool: options.browserTool,
807
815
  browser: browserOption,
@@ -818,7 +826,23 @@ program
818
826
  const insideTmux = !!process.env.TMUX;
819
827
  let selectedAgent = null;
820
828
  let didPromptAgentSelection = false;
821
- 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
+ }
822
846
  if (process.stdin.isTTY && options.agent !== false && options.tui !== false && !options.debug && !insideTmux) {
823
847
  // Clear the terminal so d3k UI starts at the top of the screen
824
848
  process.stdout.write("\x1B[2J\x1B[0f");
@@ -831,7 +855,8 @@ program
831
855
  }
832
856
  else {
833
857
  // Always show prompt, pre-selecting the last-used option
834
- selectedAgent = await promptAgentSelection(userConfig.defaultAgent?.name);
858
+ const preferredAgentName = readProjectAgentName() || userConfig.defaultAgent?.name;
859
+ selectedAgent = await promptAgentSelection(preferredAgentName);
835
860
  didPromptAgentSelection = true;
836
861
  if (selectedAgent) {
837
862
  if (selectedAgent.name === "debug") {
@@ -961,19 +986,21 @@ program
961
986
  script: options.script,
962
987
  command: options.command,
963
988
  startupTimeout: options.startupTimeout,
989
+ browserNavigationTimeout: options.browserNavigationTimeout,
964
990
  profileDir: options.profileDir,
965
991
  browserTool: options.browserTool,
966
992
  browser: browserOption,
967
993
  serversOnly: options.serversOnly,
968
994
  headless: options.headless,
969
995
  dateTime: options.dateTime,
970
- pluginReactScan: options.pluginReactScan
996
+ pluginReactScan: options.pluginReactScan,
997
+ agentName: selectedAgent.name
971
998
  });
972
999
  return;
973
1000
  }
974
1001
  }
975
1002
  if (options.autoSkills && options.skills !== false && !skillsAgentId) {
976
- skillsAgentId = "codex";
1003
+ skillsAgentId = agentDetection.agentId || "codex";
977
1004
  }
978
1005
  // Detect project type and configuration
979
1006
  const projectConfig = await detectProjectType(options.debug);
@@ -991,25 +1018,21 @@ program
991
1018
  console.error(chalk.yellow(` d3k --command "node server.js" -p 3000\n`));
992
1019
  process.exit(1);
993
1020
  }
994
- // Detect if running under an AI agent and auto-disable TUI
995
- const agentDetection = detectAIAgent();
996
- if (agentDetection.isAgent && options.tui !== false) {
997
- if (options.debug) {
998
- console.log(`[DEBUG] AI agent detected: ${agentDetection.agentName} (${agentDetection.reason}), auto-disabling TUI`);
999
- }
1000
- // Override TUI setting to false when agent is detected
1001
- options.tui = false;
1002
- }
1003
1021
  // Use defaults from project detection if not explicitly provided
1004
1022
  const port = options.port || projectConfig.defaultPort;
1005
1023
  const script = options.script || projectConfig.defaultScript;
1006
1024
  const userSetPort = options.port !== undefined;
1007
1025
  const startupTimeoutSeconds = Number.parseInt(options.startupTimeout, 10);
1008
- const browserTool = options.browserTool === "next-browser" ? "next-browser" : "agent-browser";
1026
+ const browserNavigationTimeoutSeconds = Number.parseInt(options.browserNavigationTimeout, 10);
1027
+ const browserTool = "agent-browser";
1009
1028
  if (Number.isNaN(startupTimeoutSeconds) || startupTimeoutSeconds <= 0) {
1010
1029
  console.error(chalk.red("\n❌ --startup-timeout must be a positive integer (seconds).\n"));
1011
1030
  process.exit(1);
1012
1031
  }
1032
+ if (Number.isNaN(browserNavigationTimeoutSeconds) || browserNavigationTimeoutSeconds <= 0) {
1033
+ console.error(chalk.red("\n❌ --browser-navigation-timeout must be a positive integer (seconds).\n"));
1034
+ process.exit(1);
1035
+ }
1013
1036
  // Generate server command based on custom command or project type
1014
1037
  let serverCommand;
1015
1038
  if (options.command) {
@@ -1081,7 +1104,7 @@ program
1081
1104
  const commandName = executablePath.endsWith("/d3k") || executablePath.includes("/d3k") ? "d3k" : "dev3000";
1082
1105
  try {
1083
1106
  // Create persistent log file
1084
- const logFile = createPersistentLogFile();
1107
+ const logFile = createPersistentLogFile(options.logFile);
1085
1108
  // Use a per-project Chrome profile by default so concurrent d3k sessions
1086
1109
  // don't fight over the same browser state. Honor explicit overrides.
1087
1110
  const profileDir = options.profileDir || join(getProjectDir(), "chrome-profile");
@@ -1104,14 +1127,17 @@ program
1104
1127
  serversOnly: options.serversOnly,
1105
1128
  commandName,
1106
1129
  startupTimeoutSeconds,
1130
+ browserNavigationTimeoutSeconds,
1107
1131
  tail: options.tail,
1108
1132
  tui: options.tui && !options.debug, // TUI is default unless --no-tui or --debug is specified
1109
1133
  portless: options.portless === true,
1110
1134
  dateTimeFormat: options.dateTime || "local",
1111
1135
  pluginReactScan: options.pluginReactScan || false,
1136
+ agentName: options.agentName || undefined,
1112
1137
  skillsAgentId: skillsAgentId || undefined,
1113
1138
  autoSkills: options.skills !== false ? options.autoSkills || false : false,
1114
- installSkills: options.skills !== false
1139
+ installSkills: options.skills !== false,
1140
+ screenshotsDir: options.screenshotsDir
1115
1141
  });
1116
1142
  }
1117
1143
  catch (error) {
@@ -1119,41 +1145,6 @@ program
1119
1145
  process.exit(1);
1120
1146
  }
1121
1147
  });
1122
- // Cloud commands
1123
- const cloud = program.command("cloud").description("Cloud-based tools using Vercel Sandbox");
1124
- // Cloud fix command
1125
- cloud
1126
- .command("fix")
1127
- .description("Start a cloud fix workflow for the current project")
1128
- .option("--repo <url>", "Repository URL (e.g. https://github.com/user/repo)")
1129
- .option("--branch <name>", "Git branch to test")
1130
- .option("--project-dir <dir>", "Project directory within repo (e.g. 'www')")
1131
- .option("--debug", "Enable debug logging")
1132
- .action(async (options) => {
1133
- try {
1134
- await cloudFix(options);
1135
- }
1136
- catch (error) {
1137
- console.error(chalk.red("❌ Cloud fix failed:"), error);
1138
- process.exit(1);
1139
- }
1140
- });
1141
- // Cloud check-pr command
1142
- cloud
1143
- .command("check-pr [pr-number]")
1144
- .description("Verify a PR's changes work as expected using Vercel preview deployment")
1145
- .option("--repo <url>", "Repository URL (optional, auto-detected from git)")
1146
- .option("--url <preview-url>", "Preview deployment URL (optional, auto-detected from Vercel)")
1147
- .option("--debug", "Enable debug logging")
1148
- .action(async (prNumber, options) => {
1149
- try {
1150
- await cloudCheckPR({ ...options, prNumber });
1151
- }
1152
- catch (error) {
1153
- console.error(chalk.red("❌ Cloud check-pr failed:"), error);
1154
- process.exit(1);
1155
- }
1156
- });
1157
1148
  // Upgrade command
1158
1149
  program
1159
1150
  .command("upgrade")
@@ -1199,21 +1190,37 @@ program
1199
1190
  .command("agent-browser [args...]")
1200
1191
  .description("Run the bundled agent-browser CLI (e.g., d3k agent-browser screenshot /tmp/foo.png)")
1201
1192
  .allowUnknownOption(true);
1202
- program
1203
- .command("next-browser [args...]")
1204
- .description("Run the bundled next-browser CLI (e.g., d3k next-browser open http://localhost:3000)")
1205
- .allowUnknownOption(true);
1206
- program
1207
- .command("browser [args...]")
1208
- .description("Run the preferred local browser CLI for this session")
1209
- .allowUnknownOption(true);
1210
1193
  // Skill command - get skill content for use in prompts/workflows
1211
1194
  program
1212
1195
  .command("skill [name]")
1213
1196
  .description("Get skill content or list available skills")
1214
1197
  .option("-l, --list", "List all available skills")
1215
1198
  .option("-v, --verbose", "Show detailed skill information")
1216
- .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
+ }
1217
1224
  // List skills if --list flag or no name provided
1218
1225
  if (options.list || !name) {
1219
1226
  if (options.verbose) {
@@ -1306,49 +1313,19 @@ program
1306
1313
  const { crawlApp } = await import("./commands/crawl.js");
1307
1314
  await crawlApp(options);
1308
1315
  });
1309
- // Find-component command - map DOM to React source
1310
1316
  program
1311
- .command("find-component <selector>")
1312
- .description("Find React component source for a DOM selector")
1313
- .action(async (selector) => {
1314
- const { findComponent } = await import("./commands/find-component.js");
1315
- await findComponent(selector);
1317
+ .command("resume")
1318
+ .description("Resume the last supported AI agent session for this project")
1319
+ .action(async () => {
1320
+ const { resumeLastAgent } = await import("./commands/resume.js");
1321
+ await resumeLastAgent();
1316
1322
  });
1317
1323
  // CDP port command - get the CDP port from the session file
1318
1324
  program
1319
1325
  .command("cdp-port")
1320
1326
  .description("Output the CDP port for the current d3k session (for use in scripts)")
1321
- .action(async () => {
1322
- const sessionDir = join(homedir(), ".d3k");
1323
- const projectDir = getProjectDir();
1324
- const projectName = projectDir.split("/").pop() || "unknown";
1325
- // Try to find session.json in ~/.d3k/{project-name}/
1326
- const entries = existsSync(sessionDir)
1327
- ? await import("fs").then((fs) => fs.readdirSync(sessionDir, { withFileTypes: true }))
1328
- : [];
1329
- for (const entry of entries) {
1330
- if (entry.isDirectory() && entry.name.startsWith(projectName.substring(0, 20))) {
1331
- const sessionFile = join(sessionDir, entry.name, "session.json");
1332
- if (existsSync(sessionFile)) {
1333
- try {
1334
- const content = JSON.parse(readFileSync(sessionFile, "utf-8"));
1335
- if (content.cdpUrl) {
1336
- // Extract port from URL like "ws://localhost:9223/devtools/browser/..."
1337
- const match = content.cdpUrl.match(/:(\d+)/);
1338
- if (match) {
1339
- console.log(match[1]);
1340
- process.exit(0);
1341
- }
1342
- }
1343
- }
1344
- catch {
1345
- // Continue searching
1346
- }
1347
- }
1348
- }
1349
- }
1350
- // Default to 9222 if no session found
1351
- console.log("9222");
1327
+ .action(() => {
1328
+ console.log(getSessionCdpPort() || "9222");
1352
1329
  });
1353
1330
  program.parse();
1354
1331
  //# sourceMappingURL=cli.js.map