@dunnewold-labs/mr-manager 0.4.22 → 0.4.24

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 (2) hide show
  1. package/dist/index.mjs +149 -67
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -132,7 +132,7 @@ Remote login \u2014 visit this URL in any browser:
132
132
  }
133
133
  async function loginWithLocalServer(apiUrl) {
134
134
  const port = getRandomPort();
135
- return new Promise((resolve8, reject) => {
135
+ return new Promise((resolve9, reject) => {
136
136
  const server = createServer((req, res) => {
137
137
  const url = new URL(req.url ?? "/", `http://localhost:${port}`);
138
138
  const key = url.searchParams.get("key");
@@ -144,7 +144,7 @@ async function loginWithLocalServer(apiUrl) {
144
144
  </body></html>
145
145
  `);
146
146
  server.close();
147
- if (key) resolve8(key);
147
+ if (key) resolve9(key);
148
148
  else reject(new Error("No key received from server"));
149
149
  });
150
150
  server.listen(port, () => {
@@ -185,7 +185,7 @@ import { fileURLToPath } from "url";
185
185
  // cli/package.json
186
186
  var package_default = {
187
187
  name: "@dunnewold-labs/mr-manager",
188
- version: "0.4.22",
188
+ version: "0.4.24",
189
189
  description: "Mr. Manager - Task and project management CLI",
190
190
  bin: {
191
191
  mr: "./dist/index.mjs"
@@ -395,10 +395,10 @@ function detectVcs(cwd) {
395
395
  // cli/commands/link.ts
396
396
  function prompt(question) {
397
397
  const rl = createInterface({ input: process.stdin, output: process.stdout });
398
- return new Promise((resolve8) => {
398
+ return new Promise((resolve9) => {
399
399
  rl.question(question, (answer) => {
400
400
  rl.close();
401
- resolve8(answer.trim());
401
+ resolve9(answer.trim());
402
402
  });
403
403
  });
404
404
  }
@@ -1188,7 +1188,7 @@ Run the setup script: cd browse && ./setup`
1188
1188
  async function runBrowseCommand2(browseArgs) {
1189
1189
  const runner = getBrowseRunner();
1190
1190
  const fullArgs = [...runner.args, ...browseArgs];
1191
- return new Promise((resolve8) => {
1191
+ return new Promise((resolve9) => {
1192
1192
  const proc = spawn3(runner.cmd, fullArgs, {
1193
1193
  stdio: ["pipe", "pipe", "pipe"],
1194
1194
  env: { ...process.env }
@@ -1205,10 +1205,10 @@ async function runBrowseCommand2(browseArgs) {
1205
1205
  if (stderr && code !== 0) {
1206
1206
  process.stderr.write(stderr);
1207
1207
  }
1208
- resolve8({ stdout: stdout.trim(), exitCode: code || 0 });
1208
+ resolve9({ stdout: stdout.trim(), exitCode: code || 0 });
1209
1209
  });
1210
1210
  proc.on("error", () => {
1211
- resolve8({ stdout: "", exitCode: 1 });
1211
+ resolve9({ stdout: "", exitCode: 1 });
1212
1212
  });
1213
1213
  });
1214
1214
  }
@@ -1595,13 +1595,13 @@ ${task.notes}` : "";
1595
1595
  }
1596
1596
  function findPrUrl(branchName, repoDir, vcs = "github") {
1597
1597
  const cmd = vcs === "gitlab" ? `glab mr view "${branchName}" --output json 2>/dev/null | jq -r '.web_url // empty'` : `gh pr view "${branchName}" --json url -q .url`;
1598
- return new Promise((resolve8) => {
1598
+ return new Promise((resolve9) => {
1599
1599
  exec(
1600
1600
  cmd,
1601
1601
  { cwd: repoDir },
1602
1602
  (err, stdout) => {
1603
- if (err) resolve8(null);
1604
- else resolve8(stdout.trim() || null);
1603
+ if (err) resolve9(null);
1604
+ else resolve9(stdout.trim() || null);
1605
1605
  }
1606
1606
  );
1607
1607
  });
@@ -1640,8 +1640,8 @@ function extractPrUrlFromText(value) {
1640
1640
  return match ? match[0] : null;
1641
1641
  }
1642
1642
  function commandSucceeds(command, cwd) {
1643
- return new Promise((resolve8) => {
1644
- exec(command, { cwd }, (err) => resolve8(!err));
1643
+ return new Promise((resolve9) => {
1644
+ exec(command, { cwd }, (err) => resolve9(!err));
1645
1645
  });
1646
1646
  }
1647
1647
  async function createPrInRepo(task, branchName, repoDir, vcs, subtasks, protoRefs = [], feedbackUpdates = [], existingResources = [], skillRefs = []) {
@@ -1656,13 +1656,13 @@ async function createPrInRepo(task, branchName, repoDir, vcs, subtasks, protoRef
1656
1656
  `, "utf-8");
1657
1657
  const createCommand2 = vcs === "gitlab" ? `glab mr create --source-branch ${JSON.stringify(branchName)} --title ${JSON.stringify(task.title)} --description-file ${JSON.stringify(bodyPath)} --yes` : `gh pr create --head ${JSON.stringify(branchName)} --title ${JSON.stringify(task.title)} --body-file ${JSON.stringify(bodyPath)}`;
1658
1658
  try {
1659
- const output = await new Promise((resolve8, reject) => {
1659
+ const output = await new Promise((resolve9, reject) => {
1660
1660
  exec(createCommand2, { cwd: repoDir }, (err, stdout, stderr) => {
1661
1661
  if (err) {
1662
1662
  reject(new Error(stderr.trim() || stdout.trim() || err.message));
1663
1663
  return;
1664
1664
  }
1665
- resolve8(`${stdout}
1665
+ resolve9(`${stdout}
1666
1666
  ${stderr}`.trim());
1667
1667
  });
1668
1668
  });
@@ -1745,27 +1745,27 @@ async function extractPrUrlFromUpdates(taskId) {
1745
1745
  }
1746
1746
  function checkPrStatus(prUrl, repoDir, vcs = "github") {
1747
1747
  const cmd = vcs === "gitlab" ? `glab mr view "${prUrl}" --output json 2>/dev/null` : `gh pr view "${prUrl}" --json merged,mergeable 2>/dev/null`;
1748
- return new Promise((resolve8) => {
1748
+ return new Promise((resolve9) => {
1749
1749
  exec(cmd, { cwd: repoDir }, (err, stdout) => {
1750
1750
  if (err || !stdout.trim()) {
1751
- resolve8(null);
1751
+ resolve9(null);
1752
1752
  return;
1753
1753
  }
1754
1754
  try {
1755
1755
  const data = JSON.parse(stdout.trim());
1756
1756
  if (vcs === "gitlab") {
1757
- resolve8({
1757
+ resolve9({
1758
1758
  merged: data.state === "merged",
1759
1759
  hasConflicts: data.has_conflicts === true
1760
1760
  });
1761
1761
  } else {
1762
- resolve8({
1762
+ resolve9({
1763
1763
  merged: data.merged === true,
1764
1764
  hasConflicts: data.mergeable === "CONFLICTING"
1765
1765
  });
1766
1766
  }
1767
1767
  } catch {
1768
- resolve8(null);
1768
+ resolve9(null);
1769
1769
  }
1770
1770
  });
1771
1771
  });
@@ -2464,7 +2464,7 @@ function buildIdeaPrompt(idea, repoDir) {
2464
2464
  `- Do NOT exit until both files have been written and verified`
2465
2465
  ].join("\n");
2466
2466
  }
2467
- function buildAgentArgs(agent, prompt2, mode, sessionId, name, resumeSession = false, systemPrompt, maxTurns) {
2467
+ function buildAgentArgs(agent, prompt2, mode, sessionId, name, resumeSession = false, systemPrompt, maxTurns, claudeModel) {
2468
2468
  if (agent === "codex") {
2469
2469
  const args = [];
2470
2470
  if (mode === "execute") {
@@ -2494,19 +2494,37 @@ ${systemPrompt}` : prompt2;
2494
2494
  const nameArgs = name ? ["--name", name] : [];
2495
2495
  const systemArgs = systemPrompt ? ["--append-system-prompt", systemPrompt] : [];
2496
2496
  const turnsArgs = maxTurns ? ["--max-turns", String(maxTurns)] : [];
2497
+ const modelArgs = claudeModel ? ["--model", claudeModel] : [];
2497
2498
  if (mode === "plan") {
2498
- return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, "--permission-mode", "plan", "-p", prompt2] };
2499
+ return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, "--permission-mode", "plan", "-p", prompt2] };
2499
2500
  }
2500
2501
  const cfg = loadConfig();
2501
- const permissionMode = cfg.claudePermissionMode ?? "dangerously-skip-permissions";
2502
+ const permissionMode = cfg.claudePermissionMode ?? "auto";
2502
2503
  const permissionArgs = permissionMode === "dangerously-skip-permissions" ? ["--dangerously-skip-permissions"] : ["--permission-mode", "auto", "--enable-auto-mode"];
2503
- return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...permissionArgs, "-p", prompt2] };
2504
+ return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, ...permissionArgs, "-p", prompt2] };
2504
2505
  }
2505
2506
  function commandExists(cmd) {
2506
- return new Promise((resolve8) => {
2507
- exec(`command -v ${cmd}`, (err) => resolve8(!err));
2507
+ return new Promise((resolve9) => {
2508
+ exec(`command -v ${cmd}`, (err) => resolve9(!err));
2508
2509
  });
2509
2510
  }
2511
+ function resolveTaskAgentAndModel(delegatedModel, watchAgent) {
2512
+ if (!delegatedModel) return { agent: watchAgent };
2513
+ switch (delegatedModel) {
2514
+ case "claude-opus":
2515
+ return { agent: "claude", claudeModel: "claude-opus-4-6" };
2516
+ case "claude-sonnet":
2517
+ return { agent: "claude", claudeModel: "claude-sonnet-4-6" };
2518
+ case "claude-haiku":
2519
+ return { agent: "claude", claudeModel: "claude-haiku-4-5-20251001" };
2520
+ case "codex":
2521
+ return { agent: "codex" };
2522
+ case "gemini":
2523
+ return { agent: "gemini" };
2524
+ default:
2525
+ return { agent: watchAgent };
2526
+ }
2527
+ }
2510
2528
  function runPlanningPhase(task, repoDir, agent) {
2511
2529
  return new Promise((res, reject) => {
2512
2530
  const planPrompt = buildPlanningPrompt(task, repoDir);
@@ -2544,21 +2562,21 @@ ${output.trim()}`));
2544
2562
  });
2545
2563
  }
2546
2564
  function askYesNo(question) {
2547
- return new Promise((resolve8) => {
2565
+ return new Promise((resolve9) => {
2548
2566
  const rl = readline.createInterface({
2549
2567
  input: process.stdin,
2550
2568
  output: process.stdout
2551
2569
  });
2552
2570
  rl.question(question, (answer) => {
2553
2571
  rl.close();
2554
- resolve8(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
2572
+ resolve9(answer.trim().toLowerCase() === "y" || answer.trim().toLowerCase() === "yes");
2555
2573
  });
2556
2574
  });
2557
2575
  }
2558
- function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name, resumeSession = false, onSpawnError, systemPrompt, maxTurns) {
2576
+ function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name, resumeSession = false, onSpawnError, systemPrompt, maxTurns, claudeModel) {
2559
2577
  const jobLabel = name ?? "unknown";
2560
2578
  console.log(`${timestamp()} ${prefix} ${paint("dim", tokenLogLine("agent", jobLabel, prompt2, systemPrompt))}`);
2561
- const { bin, args } = buildAgentArgs(agent, prompt2, "execute", sessionId, name, resumeSession, systemPrompt, maxTurns);
2579
+ const { bin, args } = buildAgentArgs(agent, prompt2, "execute", sessionId, name, resumeSession, systemPrompt, maxTurns, claudeModel);
2562
2580
  const child = spawn4(bin, args, { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] });
2563
2581
  child.on("error", (err) => {
2564
2582
  logError(prefix, `Failed to spawn ${agent}: ${err.message}`);
@@ -2818,10 +2836,16 @@ var watchCommand = new Command8("watch").description(
2818
2836
  const touchActivity = () => {
2819
2837
  activeEntry.lastActivityAt = Date.now();
2820
2838
  };
2821
- const attemptOrder = await resolveAgentChain(agent);
2839
+ const { agent: taskAgent, claudeModel: taskClaudeModel } = resolveTaskAgentAndModel(task.delegatedModel, agent);
2840
+ if (taskClaudeModel) {
2841
+ logInfo(prefix, `Using model: ${paint("cyan", taskClaudeModel)}`);
2842
+ } else if (taskAgent !== agent) {
2843
+ logInfo(prefix, `Using agent override: ${paint("cyan", taskAgent)}`);
2844
+ }
2845
+ const attemptOrder = await resolveAgentChain(taskAgent);
2822
2846
  if (attemptOrder.length === 0) {
2823
- logError(prefix, `No available agents found for fallback chain starting at ${agent}`);
2824
- await moveTaskToError(task, prefix, `No available agent found for fallback chain starting at ${agent}`);
2847
+ logError(prefix, `No available agents found for fallback chain starting at ${taskAgent}`);
2848
+ await moveTaskToError(task, prefix, `No available agent found for fallback chain starting at ${taskAgent}`);
2825
2849
  if (activeEntry.cleanupRepoDir && activeEntry.cleanupWorktreePath) {
2826
2850
  removeWorktree(activeEntry.cleanupRepoDir, activeEntry.cleanupWorktreePath);
2827
2851
  }
@@ -2835,6 +2859,7 @@ var watchCommand = new Command8("watch").description(
2835
2859
  const pausedForNetwork = networkPaused.get(task.id);
2836
2860
  const shouldResumeClaudeSession = attemptAgent === "claude" && !!task.claudeSessionId && !resumeAlreadyRetried && (hasFeedback || pausedForNetwork?.resumeSession === true);
2837
2861
  const sessionId = attemptAgent === "claude" ? shouldResumeClaudeSession ? task.claudeSessionId : randomUUID() : void 0;
2862
+ const effectiveClaudeModel = attemptAgent === "claude" ? taskClaudeModel : void 0;
2838
2863
  const executionSystemPrompt = composeSystemPrompt(EXECUTION_SYSTEM_SECTIONS);
2839
2864
  const child = spawnAgent(
2840
2865
  attemptAgent,
@@ -2848,7 +2873,9 @@ var watchCommand = new Command8("watch").description(
2848
2873
  (err) => {
2849
2874
  spawnFailureReason = err.message;
2850
2875
  },
2851
- executionSystemPrompt
2876
+ executionSystemPrompt,
2877
+ void 0,
2878
+ effectiveClaudeModel
2852
2879
  );
2853
2880
  activeEntry.process = child;
2854
2881
  activeEntry.currentAgent = attemptAgent;
@@ -4445,14 +4472,14 @@ function paint5(color, text) {
4445
4472
  return `${c5[color]}${text}${c5.reset}`;
4446
4473
  }
4447
4474
  function commandExists2(cmd) {
4448
- return new Promise((resolve8) => {
4449
- exec2(`which ${cmd}`, (err) => resolve8(!err));
4475
+ return new Promise((resolve9) => {
4476
+ exec2(`which ${cmd}`, (err) => resolve9(!err));
4450
4477
  });
4451
4478
  }
4452
4479
  function execQuiet(cmd) {
4453
- return new Promise((resolve8) => {
4480
+ return new Promise((resolve9) => {
4454
4481
  exec2(cmd, (err, stdout, stderr) => {
4455
- resolve8({ ok: !err, stdout: stdout.trim(), stderr: stderr.trim() });
4482
+ resolve9({ ok: !err, stdout: stdout.trim(), stderr: stderr.trim() });
4456
4483
  });
4457
4484
  });
4458
4485
  }
@@ -4707,26 +4734,26 @@ async function autoFix(checks, agent) {
4707
4734
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
4708
4735
  console.log(paint5("cyan", " Installing Claude Code..."));
4709
4736
  console.log(paint5("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
4710
- await new Promise((resolve8) => {
4737
+ await new Promise((resolve9) => {
4711
4738
  const child = spawn8("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
4712
- child.on("exit", () => resolve8());
4739
+ child.on("exit", () => resolve9());
4713
4740
  });
4714
4741
  console.log("");
4715
4742
  }
4716
4743
  if (ghInstalled && !ghAuthed) {
4717
4744
  console.log(paint5("cyan", " Running gh auth login..."));
4718
- await new Promise((resolve8) => {
4745
+ await new Promise((resolve9) => {
4719
4746
  const child = spawn8("gh", ["auth", "login"], { stdio: "inherit" });
4720
- child.on("exit", () => resolve8());
4747
+ child.on("exit", () => resolve9());
4721
4748
  });
4722
4749
  console.log("");
4723
4750
  }
4724
4751
  if (!mrAuthed) {
4725
4752
  console.log(paint5("cyan", " Running mr login..."));
4726
4753
  const entry = process.argv[1];
4727
- await new Promise((resolve8) => {
4754
+ await new Promise((resolve9) => {
4728
4755
  const child = spawn8(process.execPath, [entry, "login"], { stdio: "inherit" });
4729
- child.on("exit", () => resolve8());
4756
+ child.on("exit", () => resolve9());
4730
4757
  });
4731
4758
  console.log("");
4732
4759
  }
@@ -4975,7 +5002,8 @@ var resumeCommand = new Command17("resume").description("Resume an interactive C
4975
5002
  import { Command as Command18 } from "commander";
4976
5003
  import { execSync as execSync4, spawn as spawn6 } from "child_process";
4977
5004
  import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
4978
- import { join as join8 } from "path";
5005
+ import { createHash } from "crypto";
5006
+ import { join as join8, resolve as resolve4 } from "path";
4979
5007
  var BROWSE_DIR2 = join8(import.meta.dirname, "..", "..", "browse");
4980
5008
  function isProcessAlive(pid) {
4981
5009
  try {
@@ -5003,43 +5031,93 @@ async function findAvailablePort2(startPort) {
5003
5031
  }
5004
5032
  throw new Error(`No available port found in range ${startPort}-${startPort + 99}`);
5005
5033
  }
5006
- async function ensureDevServer() {
5007
- const devStateFile = "/tmp/mr-dev-server.json";
5034
+ function detectDevCommand2(cwd) {
5035
+ try {
5036
+ const pkg = JSON.parse(readFileSync7(join8(cwd, "package.json"), "utf-8"));
5037
+ const scripts = pkg.scripts || {};
5038
+ if (scripts.dev) return "npm run dev";
5039
+ if (scripts.start) return "npm start";
5040
+ if (scripts.serve) return "npm run serve";
5041
+ } catch {
5042
+ }
5043
+ return "npm run dev";
5044
+ }
5045
+ function parseCommand(cmd) {
5046
+ const args = [];
5047
+ let current = "";
5048
+ let inSingle = false;
5049
+ let inDouble = false;
5050
+ for (let i = 0; i < cmd.length; i++) {
5051
+ const ch = cmd[i];
5052
+ if (ch === "'" && !inDouble) {
5053
+ inSingle = !inSingle;
5054
+ } else if (ch === '"' && !inSingle) {
5055
+ inDouble = !inDouble;
5056
+ } else if (ch === " " && !inSingle && !inDouble) {
5057
+ if (current) {
5058
+ args.push(current);
5059
+ current = "";
5060
+ }
5061
+ } else {
5062
+ current += ch;
5063
+ }
5064
+ }
5065
+ if (current) args.push(current);
5066
+ return args;
5067
+ }
5068
+ async function ensureDevServer(options = {}) {
5069
+ const projectCwd = options.cwd ? resolve4(options.cwd) : join8(import.meta.dirname, "..", "..");
5070
+ const devCmd = options.cmd || detectDevCommand2(projectCwd);
5071
+ const cwdHash = createHash("md5").update(projectCwd).digest("hex").slice(0, 8);
5072
+ const devStateFile = `/tmp/mr-dev-server-${cwdHash}.json`;
5008
5073
  try {
5009
5074
  const state = JSON.parse(readFileSync7(devStateFile, "utf-8"));
5010
5075
  if (isProcessAlive(state.pid) && await isPortResponding2(state.port)) {
5076
+ console.log(`[browse] Reusing dev server on port ${state.port} (${projectCwd})`);
5011
5077
  return state.port;
5012
5078
  }
5013
5079
  } catch {
5014
5080
  }
5015
5081
  const port = await findAvailablePort2(3e3);
5016
- console.log(`[browse] Starting dev server on port ${port}...`);
5017
- const devProc = spawn6("npm", ["run", "dev", "--", "--port", String(port)], {
5082
+ const argv = parseCommand(devCmd);
5083
+ const portFlag = options.portFlag;
5084
+ let spawnArgs;
5085
+ let spawnEnv = { ...process.env };
5086
+ if (portFlag) {
5087
+ spawnArgs = [...argv.slice(1), portFlag, String(port)];
5088
+ } else if (devCmd.includes("next") || devCmd.includes("vite") || devCmd.includes("webpack")) {
5089
+ spawnArgs = [...argv.slice(1), "--", "--port", String(port)];
5090
+ } else {
5091
+ spawnEnv.PORT = String(port);
5092
+ spawnArgs = argv.slice(1);
5093
+ }
5094
+ console.log(`[browse] Starting dev server: ${devCmd} (port ${port}) in ${projectCwd}`);
5095
+ const devProc = spawn6(argv[0], spawnArgs, {
5018
5096
  stdio: ["ignore", "pipe", "pipe"],
5019
5097
  detached: true,
5020
- cwd: join8(import.meta.dirname, "..", ".."),
5021
- env: { ...process.env }
5098
+ cwd: projectCwd,
5099
+ env: spawnEnv
5022
5100
  });
5023
5101
  devProc.unref();
5024
5102
  writeFileSync4(
5025
5103
  devStateFile,
5026
- JSON.stringify({ pid: devProc.pid, port, startedAt: (/* @__PURE__ */ new Date()).toISOString() }),
5104
+ JSON.stringify({ pid: devProc.pid, port, cwd: projectCwd, cmd: devCmd, startedAt: (/* @__PURE__ */ new Date()).toISOString() }),
5027
5105
  { mode: 384 }
5028
5106
  );
5029
5107
  const start = Date.now();
5030
- while (Date.now() - start < 3e4) {
5108
+ while (Date.now() - start < 6e4) {
5031
5109
  if (await isPortResponding2(port)) {
5032
5110
  console.log(`[browse] Dev server ready on port ${port}`);
5033
5111
  return port;
5034
5112
  }
5035
5113
  await new Promise((r) => setTimeout(r, 500));
5036
5114
  }
5037
- throw new Error("Dev server failed to start within 30s");
5115
+ throw new Error(`Dev server failed to start within 60s. Command: ${devCmd} in ${projectCwd}`);
5038
5116
  }
5039
5117
  var browseCommand = new Command18("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
5040
5118
  "--task-id <id>",
5041
5119
  "Attach output to a task update (for screenshot and recording-stop commands)"
5042
- ).option("--dev", "Auto-start local dev server before browsing").allowUnknownOption(true).action(
5120
+ ).option("--dev", "Auto-start local dev server before browsing").option("--dev-cwd <path>", "Working directory for the dev server (defaults to mr-manager root)").option("--dev-cmd <command>", "Dev server command to run (auto-detected from package.json if omitted)").option("--dev-port-flag <flag>", "CLI flag name used to set port (e.g. --port). Omit to use PORT env var.").allowUnknownOption(true).action(
5043
5121
  async (command, args, opts) => {
5044
5122
  if (!command) {
5045
5123
  const { stdout: stdout2 } = await runBrowseCommand2(["--help"]);
@@ -5058,7 +5136,11 @@ var browseCommand = new Command18("browse").description("Control a headless brow
5058
5136
  }
5059
5137
  if (opts.dev) {
5060
5138
  try {
5061
- const port = await ensureDevServer();
5139
+ const port = await ensureDevServer({
5140
+ cwd: opts.devCwd,
5141
+ cmd: opts.devCmd,
5142
+ portFlag: opts.devPortFlag
5143
+ });
5062
5144
  if (command === "goto" && args[0]) {
5063
5145
  const url = args[0];
5064
5146
  if (url.startsWith("/")) {
@@ -5176,10 +5258,10 @@ var browseCommand = new Command18("browse").description("Control a headless brow
5176
5258
 
5177
5259
  // cli/commands/set-path.ts
5178
5260
  import { Command as Command19 } from "commander";
5179
- import { resolve as resolve4 } from "path";
5261
+ import { resolve as resolve5 } from "path";
5180
5262
  import { existsSync as existsSync10 } from "fs";
5181
5263
  var setPathCommand = new Command19("set-path").description("Set or update the local repo path for a project").argument("<project-id>", "Project ID").argument("<path>", "Absolute or relative path to the local repo").action(async (projectId, pathArg) => {
5182
- const absolutePath = resolve4(pathArg);
5264
+ const absolutePath = resolve5(pathArg);
5183
5265
  if (!existsSync10(absolutePath)) {
5184
5266
  console.error(`Error: Path does not exist: ${absolutePath}`);
5185
5267
  process.exit(1);
@@ -5362,7 +5444,7 @@ var testCommand = new Command20("test").description("Run automated browser test
5362
5444
  // cli/commands/features.ts
5363
5445
  import { Command as Command21 } from "commander";
5364
5446
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync12 } from "fs";
5365
- import { resolve as resolve5, sep as sep2 } from "path";
5447
+ import { resolve as resolve6, sep as sep2 } from "path";
5366
5448
  var FEATURES_FILE3 = ".mr-features.md";
5367
5449
  var c7 = {
5368
5450
  reset: "\x1B[0m",
@@ -5387,7 +5469,7 @@ function resolveProjectRoot2() {
5387
5469
  return cwd;
5388
5470
  }
5389
5471
  function getFeaturesPath() {
5390
- return resolve5(resolveProjectRoot2(), FEATURES_FILE3);
5472
+ return resolve6(resolveProjectRoot2(), FEATURES_FILE3);
5391
5473
  }
5392
5474
  function readFeatures2() {
5393
5475
  const path = getFeaturesPath();
@@ -5400,7 +5482,7 @@ var featuresCommand = new Command21("features").description("View or update the
5400
5482
  return;
5401
5483
  }
5402
5484
  if (opts.file) {
5403
- const content2 = readFileSync9(resolve5(opts.file), "utf-8");
5485
+ const content2 = readFileSync9(resolve6(opts.file), "utf-8");
5404
5486
  const featuresPath = getFeaturesPath();
5405
5487
  writeFileSync5(featuresPath, content2);
5406
5488
  console.log(`${paint7("green", "\u2713")} Updated ${paint7("cyan", featuresPath)} from ${paint7("cyan", opts.file)}`);
@@ -5424,10 +5506,10 @@ var featuresCommand = new Command21("features").description("View or update the
5424
5506
  // cli/commands/no-mr.ts
5425
5507
  import { Command as Command22 } from "commander";
5426
5508
  import { writeFileSync as writeFileSync6 } from "fs";
5427
- import { resolve as resolve6 } from "path";
5509
+ import { resolve as resolve7 } from "path";
5428
5510
  var NO_MR_FILE = ".mr-no-mr";
5429
5511
  var noMrCommand = new Command22("no-mr").description("Signal that a task does not require a merge/pull request and describe what was done instead").argument("<task-id>", "Task ID").argument("<description>", "Description of what was done instead of creating an MR/PR").action(async (taskId, description) => {
5430
- const filePath = resolve6(process.cwd(), NO_MR_FILE);
5512
+ const filePath = resolve7(process.cwd(), NO_MR_FILE);
5431
5513
  writeFileSync6(filePath, description, "utf-8");
5432
5514
  await api.post(`/api/tasks/${taskId}/updates`, {
5433
5515
  message: `No MR/PR needed \u2014 ${description}`,
@@ -6114,7 +6196,7 @@ async function fetchScanContext(opts) {
6114
6196
  };
6115
6197
  }
6116
6198
  function runClaude(prompt2) {
6117
- return new Promise((resolve8, reject) => {
6199
+ return new Promise((resolve9, reject) => {
6118
6200
  const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6119
6201
  stdio: ["ignore", "pipe", "pipe"]
6120
6202
  });
@@ -6127,7 +6209,7 @@ function runClaude(prompt2) {
6127
6209
  errOutput += d.toString();
6128
6210
  });
6129
6211
  child.on("exit", (code) => {
6130
- if (code === 0) resolve8(output.trim());
6212
+ if (code === 0) resolve9(output.trim());
6131
6213
  else reject(new Error(`claude exited with code ${code}
6132
6214
  ${errOutput.trim()}`));
6133
6215
  });
@@ -6567,7 +6649,7 @@ var doctorCommand = new Command25("doctor").description("Diagnose Mr. Manager CL
6567
6649
 
6568
6650
  // cli/commands/prompt-audit.ts
6569
6651
  import { Command as Command26 } from "commander";
6570
- import { resolve as resolve7 } from "path";
6652
+ import { resolve as resolve8 } from "path";
6571
6653
  import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
6572
6654
  function auditLine(label, tokens) {
6573
6655
  const bar = "\u2588".repeat(Math.min(60, Math.round(tokens / 200)));
@@ -6622,7 +6704,7 @@ ${task.notes}` : "";
6622
6704
  const config = loadConfig();
6623
6705
  const repoDir = Object.entries(config.directories).find(([, pid]) => pid === task.projectId)?.[0];
6624
6706
  if (repoDir) {
6625
- const featuresPath = resolve7(repoDir, ".mr-features.md");
6707
+ const featuresPath = resolve8(repoDir, ".mr-features.md");
6626
6708
  if (existsSync16(featuresPath)) {
6627
6709
  const featuresContent = readFileSync12(featuresPath, "utf-8");
6628
6710
  sections.push({ name: "features-doc", tokens: estimateTokens(featuresContent) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.4.22",
3
+ "version": "0.4.24",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"