@dunnewold-labs/mr-manager 0.4.0 → 0.4.2

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 +415 -522
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli/index.ts
4
- import { Command as Command28 } from "commander";
5
- import { existsSync as existsSync15 } from "fs";
6
- import { homedir as homedir4 } from "os";
4
+ import { Command as Command27 } from "commander";
5
+ import { existsSync as existsSync16 } from "fs";
6
+ import { homedir as homedir3 } from "os";
7
7
  import { join as join13 } from "path";
8
8
 
9
9
  // cli/commands/init.ts
@@ -177,19 +177,70 @@ var loginCommand = new Command3("login").description("Authenticate the CLI via b
177
177
  // cli/commands/projects.ts
178
178
  import { Command as Command4 } from "commander";
179
179
 
180
- // cli/api.ts
181
- import { readFileSync as readFileSync2 } from "fs";
180
+ // cli/package.ts
181
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
182
+ import { dirname, join as join2, resolve } from "path";
182
183
  import { fileURLToPath } from "url";
183
- import { dirname, join as join2 } from "path";
184
- var cliVersion = "unknown";
185
- try {
186
- const __filename = fileURLToPath(import.meta.url);
187
- const __dirname = dirname(__filename);
188
- const pkg = JSON.parse(readFileSync2(join2(__dirname, "..", "package.json"), "utf-8"));
189
- cliVersion = pkg.version;
190
- } catch {
191
- cliVersion = "0.2.0";
184
+
185
+ // cli/package.json
186
+ var package_default = {
187
+ name: "@dunnewold-labs/mr-manager",
188
+ version: "0.4.2",
189
+ description: "Mr. Manager - Task and project management CLI",
190
+ bin: {
191
+ mr: "./dist/index.mjs"
192
+ },
193
+ files: [
194
+ "dist"
195
+ ],
196
+ type: "module",
197
+ engines: {
198
+ node: ">=20"
199
+ },
200
+ publishConfig: {
201
+ access: "public"
202
+ },
203
+ scripts: {
204
+ postinstall: `echo '\\n Run "mr login" to authenticate with Mr. Manager.\\n'`
205
+ },
206
+ dependencies: {
207
+ commander: "^13.1.0"
208
+ }
209
+ };
210
+
211
+ // cli/package.ts
212
+ var FALLBACK_CLI_VERSION = "0.2.0";
213
+ var CLI_PACKAGE_NAME = "@dunnewold-labs/mr-manager";
214
+ var __filename = fileURLToPath(import.meta.url);
215
+ var __dirname = dirname(__filename);
216
+ function isCliPackageDir(dir) {
217
+ const pkgPath = join2(dir, "package.json");
218
+ if (!existsSync2(pkgPath)) {
219
+ return false;
220
+ }
221
+ try {
222
+ const pkg = JSON.parse(readFileSync2(pkgPath, "utf-8"));
223
+ return pkg.name === CLI_PACKAGE_NAME;
224
+ } catch {
225
+ return false;
226
+ }
227
+ }
228
+ function resolveCliDir() {
229
+ const candidates = [resolve(__dirname), resolve(__dirname, "..")];
230
+ for (const candidate of candidates) {
231
+ if (isCliPackageDir(candidate)) {
232
+ return candidate;
233
+ }
234
+ }
235
+ return resolve(__dirname, "..");
192
236
  }
237
+ var CLI_DIR = resolveCliDir();
238
+ var REPO_ROOT = resolve(CLI_DIR, "..");
239
+ var CLI_PACKAGE_PATH = join2(CLI_DIR, "package.json");
240
+ var CLI_VERSION = package_default.version ?? FALLBACK_CLI_VERSION;
241
+
242
+ // cli/api.ts
243
+ var cliVersion = CLI_VERSION;
193
244
  var ApiError = class extends Error {
194
245
  constructor(status, statusText, body) {
195
246
  super(`API error ${status} ${statusText}: ${body}`);
@@ -365,7 +416,7 @@ var unlinkCommand = new Command6("unlink").description("Remove current directory
365
416
 
366
417
  // cli/commands/context.ts
367
418
  import { Command as Command7 } from "commander";
368
- import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync2 } from "fs";
419
+ import { writeFileSync as writeFileSync2, readFileSync as readFileSync3, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
369
420
  import { join as join3, sep } from "path";
370
421
  var FEATURES_FILE = ".mr-features.md";
371
422
  function resolveProjectRoot() {
@@ -379,13 +430,13 @@ function resolveProjectRoot() {
379
430
  }
380
431
  function readFeatures() {
381
432
  const path = join3(resolveProjectRoot(), FEATURES_FILE);
382
- if (!existsSync2(path)) return null;
433
+ if (!existsSync3(path)) return null;
383
434
  return readFileSync3(path, "utf-8");
384
435
  }
385
436
  var contextCommand = new Command7("context").description("Output project context JSON for Claude Code").option("--install", "Install Claude Code command to .claude/commands/").action(async (opts) => {
386
437
  if (opts.install) {
387
438
  const dir = join3(process.cwd(), ".claude", "commands");
388
- if (!existsSync2(dir)) {
439
+ if (!existsSync3(dir)) {
389
440
  mkdirSync2(dir, { recursive: true });
390
441
  }
391
442
  const filePath = join3(dir, "mr-tasks.md");
@@ -408,11 +459,13 @@ var contextCommand = new Command7("context").description("Output project context
408
459
  `/api/projects/${projectId}/tasks`
409
460
  );
410
461
  const inProgress = allTasks.filter((t) => t.status === "in_progress" || t.status === "queued" || t.status === "delegated" || t.status === "review");
462
+ const needsAttention = allTasks.filter((t) => t.status === "error");
411
463
  const todo = allTasks.filter((t) => t.status === "todo");
412
464
  const recentlyCompleted = allTasks.filter((t) => t.status === "completed").slice(0, 10);
413
465
  const summaryLines = [
414
466
  `Project: ${project.name} (${project.status})`,
415
467
  ...inProgress.map((t) => `In Progress: ${t.title}`),
468
+ ...needsAttention.map((t) => `Needs Attention: ${t.title}`),
416
469
  `Todo: ${todo.length} tasks`,
417
470
  `Recently Completed: ${recentlyCompleted.length} tasks`
418
471
  ];
@@ -438,23 +491,22 @@ var contextCommand = new Command7("context").description("Output project context
438
491
  import { Command as Command8 } from "commander";
439
492
  import { spawn as spawn4, exec } from "child_process";
440
493
  import { randomUUID } from "crypto";
441
- import { resolve, join as join7 } from "path";
442
- import { readFileSync as readFileSync5, readdirSync, unlinkSync, existsSync as existsSync6, statSync } from "fs";
443
- import { homedir as homedir2 } from "os";
494
+ import { resolve as resolve2 } from "path";
495
+ import { readFileSync as readFileSync5, readdirSync, unlinkSync, existsSync as existsSync7, statSync } from "fs";
444
496
  import * as readline from "readline";
445
497
 
446
498
  // lib/test-runner.ts
447
499
  import { spawn as spawn2 } from "child_process";
448
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "fs";
500
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
449
501
  import { join as join5 } from "path";
450
502
 
451
503
  // lib/git-worktree.ts
452
504
  import { execSync as execSync2 } from "child_process";
453
- import { copyFileSync, existsSync as existsSync3 } from "fs";
505
+ import { copyFileSync, existsSync as existsSync4 } from "fs";
454
506
  import { join as join4 } from "path";
455
507
  function createWorktree(repoDir, branch, worktreeName) {
456
508
  const wtPath = join4(repoDir, ".mr-worktrees", worktreeName);
457
- if (existsSync3(wtPath)) {
509
+ if (existsSync4(wtPath)) {
458
510
  execSync2(`git checkout ${branch}`, { cwd: wtPath, stdio: "pipe" });
459
511
  return wtPath;
460
512
  }
@@ -482,7 +534,7 @@ function createWorktree(repoDir, branch, worktreeName) {
482
534
  }
483
535
  for (const envFile of [".env", ".env.local"]) {
484
536
  const src = join4(repoDir, envFile);
485
- if (existsSync3(src)) {
537
+ if (existsSync4(src)) {
486
538
  copyFileSync(src, join4(wtPath, envFile));
487
539
  }
488
540
  }
@@ -525,13 +577,13 @@ function extractBranchFromLink(link, cwd) {
525
577
  return null;
526
578
  }
527
579
  function installDependencies(wtPath) {
528
- if (existsSync3(join4(wtPath, "bun.lock")) || existsSync3(join4(wtPath, "bun.lockb"))) {
580
+ if (existsSync4(join4(wtPath, "bun.lock")) || existsSync4(join4(wtPath, "bun.lockb"))) {
529
581
  execSync2("bun install", { cwd: wtPath, stdio: "pipe" });
530
- } else if (existsSync3(join4(wtPath, "pnpm-lock.yaml"))) {
582
+ } else if (existsSync4(join4(wtPath, "pnpm-lock.yaml"))) {
531
583
  execSync2("pnpm install --frozen-lockfile", { cwd: wtPath, stdio: "pipe" });
532
- } else if (existsSync3(join4(wtPath, "package-lock.json"))) {
584
+ } else if (existsSync4(join4(wtPath, "package-lock.json"))) {
533
585
  execSync2("npm ci", { cwd: wtPath, stdio: "pipe" });
534
- } else if (existsSync3(join4(wtPath, "yarn.lock"))) {
586
+ } else if (existsSync4(join4(wtPath, "yarn.lock"))) {
535
587
  execSync2("yarn install --frozen-lockfile", { cwd: wtPath, stdio: "pipe" });
536
588
  }
537
589
  }
@@ -539,7 +591,7 @@ function installDependencies(wtPath) {
539
591
  // lib/test-runner.ts
540
592
  function detectDevCommand(wtPath) {
541
593
  const configPath2 = join5(wtPath, ".mr-test.json");
542
- if (existsSync4(configPath2)) {
594
+ if (existsSync5(configPath2)) {
543
595
  try {
544
596
  const config = JSON.parse(readFileSync4(configPath2, "utf-8"));
545
597
  if (config.devCommand) {
@@ -549,16 +601,16 @@ function detectDevCommand(wtPath) {
549
601
  } catch {
550
602
  }
551
603
  }
552
- if (existsSync4(join5(wtPath, "bun.lock")) || existsSync4(join5(wtPath, "bun.lockb"))) {
604
+ if (existsSync5(join5(wtPath, "bun.lock")) || existsSync5(join5(wtPath, "bun.lockb"))) {
553
605
  return { cmd: "bun", args: ["dev"] };
554
606
  }
555
- if (existsSync4(join5(wtPath, "pnpm-lock.yaml"))) {
607
+ if (existsSync5(join5(wtPath, "pnpm-lock.yaml"))) {
556
608
  return { cmd: "pnpm", args: ["dev"] };
557
609
  }
558
- if (existsSync4(join5(wtPath, "package-lock.json"))) {
610
+ if (existsSync5(join5(wtPath, "package-lock.json"))) {
559
611
  return { cmd: "npm", args: ["run", "dev"] };
560
612
  }
561
- if (existsSync4(join5(wtPath, "yarn.lock"))) {
613
+ if (existsSync5(join5(wtPath, "yarn.lock"))) {
562
614
  return { cmd: "yarn", args: ["dev"] };
563
615
  }
564
616
  return { cmd: "npm", args: ["run", "dev"] };
@@ -696,7 +748,7 @@ async function runTest(options) {
696
748
  postUpdate,
697
749
  onProgress
698
750
  } = options;
699
- const log3 = onProgress || (() => {
751
+ const log2 = onProgress || (() => {
700
752
  });
701
753
  const result = {
702
754
  status: "passed",
@@ -709,37 +761,37 @@ async function runTest(options) {
709
761
  let wtPath = null;
710
762
  const worktreeName = `mr-test-${taskId.slice(0, 8)}`;
711
763
  const timeoutHandle = setTimeout(() => {
712
- log3("Test timed out after 5 minutes");
764
+ log2("Test timed out after 5 minutes");
713
765
  if (devProc) devProc.kill("SIGTERM");
714
766
  }, 5 * 60 * 1e3);
715
767
  try {
716
- log3("Extracting branch from MR/PR link...");
768
+ log2("Extracting branch from MR/PR link...");
717
769
  const branch = extractBranchFromLink(taskLink, localPath);
718
770
  if (!branch) {
719
771
  throw new Error(`Could not extract branch from link: ${taskLink}`);
720
772
  }
721
- log3(`Branch: ${branch}`);
722
- log3("Creating git worktree...");
773
+ log2(`Branch: ${branch}`);
774
+ log2("Creating git worktree...");
723
775
  wtPath = createWorktree(localPath, branch, worktreeName);
724
- log3(`Worktree created at ${wtPath}`);
725
- log3("Installing dependencies...");
776
+ log2(`Worktree created at ${wtPath}`);
777
+ log2("Installing dependencies...");
726
778
  try {
727
779
  installDependencies(wtPath);
728
780
  } catch (err) {
729
- log3(`Warning: dependency install failed: ${err.message}`);
781
+ log2(`Warning: dependency install failed: ${err.message}`);
730
782
  }
731
- log3("Starting dev server...");
783
+ log2("Starting dev server...");
732
784
  const port = await findAvailablePort(4e3);
733
785
  devProc = await startDevServer(wtPath, port);
734
786
  const baseUrl = `http://127.0.0.1:${port}`;
735
- log3(`Dev server running on ${baseUrl}`);
787
+ log2(`Dev server running on ${baseUrl}`);
736
788
  await browseRunner(["goto", baseUrl]);
737
789
  const plan = customPlan || buildDefaultTestPlan(baseUrl);
738
- log3(`Executing ${plan.length}-step test plan...`);
790
+ log2(`Executing ${plan.length}-step test plan...`);
739
791
  for (let i = 0; i < plan.length; i++) {
740
792
  const step = plan[i];
741
793
  const stepDesc = step.description || `${step.command} ${(step.args || []).join(" ")}`;
742
- log3(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
794
+ log2(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
743
795
  try {
744
796
  if (step.command.startsWith("assert")) {
745
797
  const assertResult = await evaluateAssertion(step, i, browseRunner);
@@ -789,7 +841,7 @@ async function runTest(options) {
789
841
  }
790
842
  } catch (err) {
791
843
  result.errors.push(`Step ${i + 1} (${step.command}): ${err.message}`);
792
- log3(`Step ${i + 1} error: ${err.message}`);
844
+ log2(`Step ${i + 1} error: ${err.message}`);
793
845
  }
794
846
  }
795
847
  const totalAssertions = result.assertions.length;
@@ -835,18 +887,18 @@ async function runTest(options) {
835
887
 
836
888
  // cli/browse-runner.ts
837
889
  import { execSync as execSync4, spawn as spawn3 } from "child_process";
838
- import { existsSync as existsSync5 } from "fs";
890
+ import { existsSync as existsSync6 } from "fs";
839
891
  import { join as join6 } from "path";
840
892
  var BROWSE_DIR = join6(import.meta.dirname, "..", "..", "browse");
841
893
  var BROWSE_BINARY = join6(BROWSE_DIR, "dist", "browse");
842
894
  var BROWSE_DEV_CMD = join6(BROWSE_DIR, "src", "cli.ts");
843
895
  function getBrowseRunner() {
844
- if (existsSync5(BROWSE_BINARY)) {
896
+ if (existsSync6(BROWSE_BINARY)) {
845
897
  return { cmd: BROWSE_BINARY, args: [] };
846
898
  }
847
899
  try {
848
900
  execSync4("which bun", { stdio: "pipe" });
849
- if (existsSync5(BROWSE_DEV_CMD)) {
901
+ if (existsSync6(BROWSE_DEV_CMD)) {
850
902
  return { cmd: "bun", args: ["run", BROWSE_DEV_CMD] };
851
903
  }
852
904
  } catch {
@@ -886,95 +938,11 @@ async function runBrowseCommand2(browseArgs) {
886
938
 
887
939
  // cli/commands/watch.ts
888
940
  var FEATURES_FILE2 = ".mr-features.md";
889
- var CLAUDE_PROJECTS_DIR = join7(homedir2(), ".claude", "projects");
890
941
  function readFeaturesDoc(repoDir) {
891
- const path = resolve(repoDir, FEATURES_FILE2);
892
- if (!existsSync6(path)) return null;
942
+ const path = resolve2(repoDir, FEATURES_FILE2);
943
+ if (!existsSync7(path)) return null;
893
944
  return readFileSync5(path, "utf-8");
894
945
  }
895
- function toClaudePath(dir) {
896
- return dir.replace(/\//g, "-");
897
- }
898
- function findSessionFile(sessionId, repoDir) {
899
- const mainPath = join7(CLAUDE_PROJECTS_DIR, toClaudePath(repoDir));
900
- const mainFile = join7(mainPath, `${sessionId}.jsonl`);
901
- if (existsSync6(mainFile)) return mainFile;
902
- const worktreePrefix = toClaudePath(repoDir) + "-.mr-worktrees-";
903
- try {
904
- const entries = readdirSync(CLAUDE_PROJECTS_DIR);
905
- for (const entry of entries) {
906
- if (entry.startsWith(worktreePrefix)) {
907
- const worktreeFile = join7(CLAUDE_PROJECTS_DIR, entry, `${sessionId}.jsonl`);
908
- if (existsSync6(worktreeFile)) return worktreeFile;
909
- }
910
- }
911
- } catch {
912
- }
913
- return null;
914
- }
915
- function parseSessionJsonl(raw) {
916
- const lines = raw.split("\n").filter((l) => l.trim());
917
- const entries = [];
918
- for (const line of lines) {
919
- try {
920
- const parsed = JSON.parse(line);
921
- if (parsed.type !== "user" && parsed.type !== "assistant") continue;
922
- if (!parsed.message) continue;
923
- const content = [];
924
- const msgContent = parsed.message.content;
925
- if (typeof msgContent === "string") {
926
- content.push({ type: "text", text: msgContent });
927
- } else if (Array.isArray(msgContent)) {
928
- for (const block of msgContent) {
929
- if (block.type === "thinking" && block.thinking) {
930
- content.push({ type: "thinking", thinking: block.thinking });
931
- } else if (block.type === "text" && block.text) {
932
- content.push({ type: "text", text: block.text });
933
- } else if (block.type === "tool_use") {
934
- content.push({
935
- type: "tool_use",
936
- id: block.id,
937
- name: block.name,
938
- input: block.input ?? {}
939
- });
940
- } else if (block.type === "tool_result") {
941
- let resultContent = block.content;
942
- if (typeof resultContent === "string") {
943
- } else if (Array.isArray(resultContent)) {
944
- resultContent = resultContent.filter((r) => r.type === "text").map((r) => r.text).join("\n");
945
- } else {
946
- resultContent = "";
947
- }
948
- content.push({
949
- type: "tool_result",
950
- tool_use_id: block.tool_use_id,
951
- content: resultContent
952
- });
953
- }
954
- }
955
- }
956
- if (content.length === 0) continue;
957
- entries.push({
958
- type: parsed.type,
959
- timestamp: parsed.timestamp,
960
- uuid: parsed.uuid,
961
- content
962
- });
963
- } catch {
964
- }
965
- }
966
- return entries;
967
- }
968
- function readAndParseSessionLog(sessionId, repoDir) {
969
- const filePath = findSessionFile(sessionId, repoDir);
970
- if (!filePath) return null;
971
- try {
972
- const raw = readFileSync5(filePath, "utf-8");
973
- return parseSessionJsonl(raw);
974
- } catch {
975
- return null;
976
- }
977
- }
978
946
  var c = {
979
947
  reset: "\x1B[0m",
980
948
  bold: "\x1B[1m",
@@ -1028,9 +996,20 @@ async function postTaskUpdate(taskId, message, source = "system") {
1028
996
  logError(taskTag(shortId(taskId)), `Failed to post update: ${err.message}`);
1029
997
  }
1030
998
  }
999
+ var DEFAULT_TASK_STALL_TIMEOUT_MS = 45 * 60 * 1e3;
1000
+ function getTaskStallTimeoutMs() {
1001
+ const raw = process.env.MR_TASK_STALL_TIMEOUT_MS;
1002
+ if (!raw) return DEFAULT_TASK_STALL_TIMEOUT_MS;
1003
+ const parsed = Number(raw);
1004
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_TASK_STALL_TIMEOUT_MS;
1005
+ }
1006
+ function formatTimeoutMinutes(ms) {
1007
+ const minutes = Math.round(ms / 6e4);
1008
+ return `${minutes} minute${minutes === 1 ? "" : "s"}`;
1009
+ }
1031
1010
  function findDirectoryForProject(config, projectId, rootDir) {
1032
1011
  for (const [dir, pid] of Object.entries(config.directories)) {
1033
- if (pid === projectId && resolve(dir).startsWith(resolve(rootDir))) {
1012
+ if (pid === projectId && resolve2(dir).startsWith(resolve2(rootDir))) {
1034
1013
  return dir;
1035
1014
  }
1036
1015
  }
@@ -1102,14 +1081,14 @@ function findPrUrl(branchName, repoDir, vcs = "github") {
1102
1081
  }
1103
1082
  function isGitRepo(dir) {
1104
1083
  try {
1105
- return existsSync6(resolve(dir, ".git"));
1084
+ return existsSync7(resolve2(dir, ".git"));
1106
1085
  } catch {
1107
1086
  return false;
1108
1087
  }
1109
1088
  }
1110
1089
  function findChildGitRepos(parentDir) {
1111
1090
  try {
1112
- return readdirSync(parentDir).filter((name) => !name.startsWith(".")).map((name) => resolve(parentDir, name)).filter((path) => {
1091
+ return readdirSync(parentDir).filter((name) => !name.startsWith(".")).map((name) => resolve2(parentDir, name)).filter((path) => {
1113
1092
  try {
1114
1093
  return statSync(path).isDirectory() && isGitRepo(path);
1115
1094
  } catch {
@@ -1808,7 +1787,7 @@ function buildAgentArgs(agent, prompt2, mode, sessionId, name) {
1808
1787
  if (agent === "codex") {
1809
1788
  const args = ["exec"];
1810
1789
  if (mode === "execute") {
1811
- args.push("--full-auto");
1790
+ args.push("-a", "never", "-s", "danger-full-access");
1812
1791
  }
1813
1792
  args.push(prompt2);
1814
1793
  return { bin: "codex", args };
@@ -1847,7 +1826,7 @@ function runPlanningPhase(task, repoDir, agent) {
1847
1826
  ${output.trim()}`));
1848
1827
  return;
1849
1828
  }
1850
- const planPath = resolve(repoDir, "plan.md");
1829
+ const planPath = resolve2(repoDir, "plan.md");
1851
1830
  try {
1852
1831
  const planContent = readFileSync5(planPath, "utf-8");
1853
1832
  try {
@@ -1873,7 +1852,7 @@ function askYesNo(question) {
1873
1852
  });
1874
1853
  });
1875
1854
  }
1876
- function spawnAgent(agent, repoDir, prompt2, prefix, sessionId, name) {
1855
+ function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name) {
1877
1856
  const { bin, args } = buildAgentArgs(agent, prompt2, "execute", sessionId, name);
1878
1857
  const child = spawn4(bin, args, { cwd: repoDir, stdio: ["ignore", "pipe", "pipe"] });
1879
1858
  child.on("error", (err) => {
@@ -1882,12 +1861,19 @@ function spawnAgent(agent, repoDir, prompt2, prefix, sessionId, name) {
1882
1861
  logError(prefix, `Check that "${bin}" is on PATH and "${repoDir}" exists`);
1883
1862
  }
1884
1863
  });
1864
+ if (agent === "codex") {
1865
+ child.stdout?.on("data", () => onActivity?.());
1866
+ child.stderr?.on("data", () => onActivity?.());
1867
+ return child;
1868
+ }
1885
1869
  child.stdout?.on("data", (data) => {
1870
+ onActivity?.();
1886
1871
  for (const line of data.toString().split("\n")) {
1887
1872
  if (line) console.log(`${timestamp()} ${prefix} ${paint("dim", line)}`);
1888
1873
  }
1889
1874
  });
1890
1875
  child.stderr?.on("data", (data) => {
1876
+ onActivity?.();
1891
1877
  for (const line of data.toString().split("\n")) {
1892
1878
  if (line) logError(prefix, paint("dim", line));
1893
1879
  }
@@ -1900,9 +1886,10 @@ var watchCommand = new Command8("watch").description(
1900
1886
  const intervalMs = parseInt(opts.interval, 10) * 1e3;
1901
1887
  const dryRun = opts.dryRun;
1902
1888
  const planApproval = opts.planApproval;
1903
- const rootDir = opts.root ? resolve(opts.root) : process.cwd();
1889
+ const rootDir = opts.root ? resolve2(opts.root) : process.cwd();
1904
1890
  const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
1905
1891
  const scanAt = opts.scanAt;
1892
+ const taskStallTimeoutMs = getTaskStallTimeoutMs();
1906
1893
  const active = /* @__PURE__ */ new Map();
1907
1894
  const failed = /* @__PURE__ */ new Map();
1908
1895
  const queued = /* @__PURE__ */ new Set();
@@ -1914,6 +1901,7 @@ var watchCommand = new Command8("watch").description(
1914
1901
  `interval=${paint("cyan", opts.interval + "s")}`,
1915
1902
  `root=${paint("cyan", rootDir)}`,
1916
1903
  `agent=${paint("cyan", agent)}`,
1904
+ `stall-timeout=${paint("cyan", formatTimeoutMinutes(taskStallTimeoutMs))}`,
1917
1905
  ...planApproval ? [paint("yellow", "plan-approval")] : [],
1918
1906
  ...dryRun ? [paint("yellow", "dry-run")] : [],
1919
1907
  ...scanAt ? [`scan-at=${paint("cyan", scanAt)}`] : []
@@ -1983,21 +1971,32 @@ var watchCommand = new Command8("watch").description(
1983
1971
  }
1984
1972
  const prompt2 = buildExecutionPrompt(task, repoDir, subtasks, vcs, protoRefs, feedbackUpdates, existingResources, skillRefs);
1985
1973
  const sessionId = agent === "claude" ? randomUUID() : void 0;
1986
- const child = spawnAgent(agent, repoDir, prompt2, prefix, sessionId, task.title);
1974
+ const activeEntry = {
1975
+ process: void 0,
1976
+ title: task.title,
1977
+ repoDir,
1978
+ startedAt: Date.now(),
1979
+ lastActivityAt: Date.now()
1980
+ };
1981
+ const touchActivity = () => {
1982
+ activeEntry.lastActivityAt = Date.now();
1983
+ };
1984
+ const child = spawnAgent(agent, repoDir, prompt2, prefix, touchActivity, sessionId, task.title);
1985
+ activeEntry.process = child;
1987
1986
  if (sessionId) {
1988
1987
  api.patch(`/api/tasks/${task.id}`, { claudeSessionId: sessionId }).catch(() => {
1989
1988
  });
1990
1989
  logInfo(prefix, `Claude session: ${paint("dim", sessionId)}`);
1991
1990
  }
1992
- active.set(task.id, { process: child, title: task.title, repoDir });
1991
+ active.set(task.id, activeEntry);
1993
1992
  child.on("exit", async (code) => {
1994
1993
  active.delete(task.id);
1995
1994
  finishing.add(task.id);
1996
1995
  try {
1997
1996
  if (code === 0) {
1998
1997
  try {
1999
- const researchPath = resolve(repoDir, "research.md");
2000
- if (existsSync6(researchPath)) {
1998
+ const researchPath = resolve2(repoDir, "research.md");
1999
+ if (existsSync7(researchPath)) {
2001
2000
  try {
2002
2001
  const researchContent = readFileSync5(researchPath, "utf-8");
2003
2002
  const existingResearch = existingResources.find((r) => r.type === "research");
@@ -2019,32 +2018,8 @@ var watchCommand = new Command8("watch").description(
2019
2018
  logWarn(prefix, `Failed to upload research resource: ${err.message}`);
2020
2019
  }
2021
2020
  }
2022
- if (sessionId) {
2023
- try {
2024
- const sessionEntries = readAndParseSessionLog(sessionId, repoDir);
2025
- if (sessionEntries && sessionEntries.length > 0) {
2026
- const sessionContent = JSON.stringify(sessionEntries);
2027
- const existingSessionLog = existingResources.find((r) => r.type === "session-log");
2028
- if (existingSessionLog) {
2029
- await api.patch(`/api/tasks/${task.id}/resources/${existingSessionLog.id}`, {
2030
- content: sessionContent
2031
- });
2032
- logSuccess(prefix, `Updated session log resource (${sessionEntries.length} messages)`);
2033
- } else {
2034
- await api.post(`/api/tasks/${task.id}/resources`, {
2035
- type: "session-log",
2036
- title: `Session Log \u2014 ${task.title}`,
2037
- content: sessionContent
2038
- });
2039
- logSuccess(prefix, `Uploaded session log (${sessionEntries.length} messages)`);
2040
- }
2041
- }
2042
- } catch (err) {
2043
- logWarn(prefix, `Failed to upload session log: ${err.message}`);
2044
- }
2045
- }
2046
- const noMrPath = resolve(repoDir, ".mr-no-mr");
2047
- const noMrRequested = existsSync6(noMrPath);
2021
+ const noMrPath = resolve2(repoDir, ".mr-no-mr");
2022
+ const noMrRequested = existsSync7(noMrPath);
2048
2023
  let noMrDescription;
2049
2024
  if (noMrRequested) {
2050
2025
  noMrDescription = readFileSync5(noMrPath, "utf-8").trim();
@@ -2077,31 +2052,9 @@ var watchCommand = new Command8("watch").description(
2077
2052
  } catch (err) {
2078
2053
  logError(prefix, `Failed to update task: ${err.message}`);
2079
2054
  }
2080
- } else {
2055
+ } else if (!activeEntry.terminatedForError) {
2081
2056
  logError(prefix, `"${paint("bold", task.title)}" failed (exit ${code}), leaving status unchanged`);
2082
2057
  await postTaskUpdate(task.id, `Agent failed with exit code ${code}`, "system");
2083
- if (sessionId) {
2084
- try {
2085
- const sessionEntries = readAndParseSessionLog(sessionId, repoDir);
2086
- if (sessionEntries && sessionEntries.length > 0) {
2087
- const sessionContent = JSON.stringify(sessionEntries);
2088
- const existingSessionLog = existingResources.find((r) => r.type === "session-log");
2089
- if (existingSessionLog) {
2090
- await api.patch(`/api/tasks/${task.id}/resources/${existingSessionLog.id}`, {
2091
- content: sessionContent
2092
- });
2093
- } else {
2094
- await api.post(`/api/tasks/${task.id}/resources`, {
2095
- type: "session-log",
2096
- title: `Session Log \u2014 ${task.title}`,
2097
- content: sessionContent
2098
- });
2099
- }
2100
- logInfo(prefix, `Uploaded session log for failed task (${sessionEntries.length} messages)`);
2101
- }
2102
- } catch {
2103
- }
2104
- }
2105
2058
  }
2106
2059
  } finally {
2107
2060
  queued.delete(task.id);
@@ -2146,7 +2099,7 @@ var watchCommand = new Command8("watch").description(
2146
2099
  try {
2147
2100
  if (code === 0) {
2148
2101
  try {
2149
- const prdPath = resolve(repoDir, "prd.md");
2102
+ const prdPath = resolve2(repoDir, "prd.md");
2150
2103
  let prdContent;
2151
2104
  try {
2152
2105
  prdContent = readFileSync5(prdPath, "utf-8");
@@ -2200,7 +2153,7 @@ var watchCommand = new Command8("watch").description(
2200
2153
  const stalePattern = /^prototype-\d+\.html$/;
2201
2154
  for (const f of readdirSync(repoDir).filter((f2) => stalePattern.test(f2))) {
2202
2155
  try {
2203
- unlinkSync(resolve(repoDir, f));
2156
+ unlinkSync(resolve2(repoDir, f));
2204
2157
  } catch {
2205
2158
  }
2206
2159
  }
@@ -2230,7 +2183,7 @@ var watchCommand = new Command8("watch").description(
2230
2183
  const found = readdirSync(repoDir).filter((f) => protoPattern.test(f)).sort();
2231
2184
  const files = found.map((f) => ({
2232
2185
  name: f,
2233
- content: readFileSync5(resolve(repoDir, f), "utf-8")
2186
+ content: readFileSync5(resolve2(repoDir, f), "utf-8")
2234
2187
  }));
2235
2188
  if (files.length === 0) {
2236
2189
  logError(prefix, `No prototype HTML files found in ${repoDir}`);
@@ -2241,7 +2194,7 @@ var watchCommand = new Command8("watch").description(
2241
2194
  logSuccess(prefix, `"${paint("bold", proto.title)}" uploaded ${files.length} file(s)`);
2242
2195
  for (const file of files) {
2243
2196
  try {
2244
- unlinkSync(resolve(repoDir, file.name));
2197
+ unlinkSync(resolve2(repoDir, file.name));
2245
2198
  } catch {
2246
2199
  }
2247
2200
  }
@@ -2302,7 +2255,7 @@ var watchCommand = new Command8("watch").description(
2302
2255
  logDispatch(prefix, `"${paint("bold", idea.title)}"${idea.feedback ? paint("cyan", " (iteration)") : ""} ${paint("gray", repoDir)}`);
2303
2256
  for (const f of ["idea-plan.md", "idea-tasks.json", "idea-prototype.html"]) {
2304
2257
  try {
2305
- unlinkSync(resolve(repoDir, f));
2258
+ unlinkSync(resolve2(repoDir, f));
2306
2259
  } catch {
2307
2260
  }
2308
2261
  }
@@ -2319,9 +2272,9 @@ var watchCommand = new Command8("watch").description(
2319
2272
  let plan;
2320
2273
  let protoHtml;
2321
2274
  let followUpTasks;
2322
- const planPath = resolve(repoDir, "idea-plan.md");
2323
- const tasksPath = resolve(repoDir, "idea-tasks.json");
2324
- const protoPath = resolve(repoDir, "idea-prototype.html");
2275
+ const planPath = resolve2(repoDir, "idea-plan.md");
2276
+ const tasksPath = resolve2(repoDir, "idea-tasks.json");
2277
+ const protoPath = resolve2(repoDir, "idea-prototype.html");
2325
2278
  try {
2326
2279
  plan = readFileSync5(planPath, "utf-8");
2327
2280
  } catch {
@@ -2490,6 +2443,35 @@ ${divider}`);
2490
2443
  const tasks = [...nonTestQueued, ...nonTestDelegated];
2491
2444
  const config = loadConfig();
2492
2445
  const activeTaskIds = new Set(tasks.map((t) => t.id));
2446
+ for (const task of tasks) {
2447
+ if (task.status !== "delegated") continue;
2448
+ const sid = shortId(task.id);
2449
+ const prefix = taskTag(sid);
2450
+ const activeEntry = active.get(task.id);
2451
+ const idleMs = activeEntry ? Date.now() - activeEntry.lastActivityAt : null;
2452
+ const delegatedAtMs = task.inProgressSince ? Date.now() - new Date(task.inProgressSince).getTime() : null;
2453
+ const exceededIdleTimeout = idleMs !== null && idleMs >= taskStallTimeoutMs;
2454
+ const exceededOrphanTimeout = !activeEntry && delegatedAtMs !== null && delegatedAtMs >= taskStallTimeoutMs;
2455
+ if (!exceededIdleTimeout && !exceededOrphanTimeout) continue;
2456
+ const timeoutLabel = formatTimeoutMinutes(taskStallTimeoutMs);
2457
+ const reason = activeEntry ? `Agent process became idle for more than ${timeoutLabel}` : `Task remained delegated without an active watch process for more than ${timeoutLabel}`;
2458
+ logError(prefix, `"${task.title}" marked as error: ${reason}`);
2459
+ if (activeEntry) {
2460
+ activeEntry.terminatedForError = true;
2461
+ }
2462
+ try {
2463
+ await api.patch(`/api/tasks/${task.id}`, { status: "error" });
2464
+ await postTaskUpdate(task.id, `Task moved to error \u2014 ${reason}`, "system");
2465
+ } catch (err) {
2466
+ logError(prefix, `Failed to mark task as error: ${err.message}`);
2467
+ continue;
2468
+ }
2469
+ failed.set(task.id, reason);
2470
+ queued.delete(task.id);
2471
+ if (activeEntry) {
2472
+ activeEntry.process.kill("SIGTERM");
2473
+ }
2474
+ }
2493
2475
  for (const [taskId, entry] of active) {
2494
2476
  if (taskId.startsWith("proto-") || taskId.startsWith("repo-") || taskId.startsWith("scan-")) continue;
2495
2477
  if (!activeTaskIds.has(taskId)) {
@@ -2522,7 +2504,7 @@ ${divider}`);
2522
2504
  failed.set(task.id, reason);
2523
2505
  continue;
2524
2506
  }
2525
- if (!existsSync6(repoDir)) {
2507
+ if (!existsSync7(repoDir)) {
2526
2508
  const reason = `linked directory "${repoDir}" does not exist`;
2527
2509
  logError(prefix, `"${task.title}": ${reason} \u2014 will not retry`);
2528
2510
  failed.set(task.id, reason);
@@ -2593,7 +2575,7 @@ ${divider}`);
2593
2575
  logWarn(prefix, `"${proto.title}": no linked directory found \u2014 skipping`);
2594
2576
  continue;
2595
2577
  }
2596
- if (!existsSync6(repoDir)) {
2578
+ if (!existsSync7(repoDir)) {
2597
2579
  logError(prefix, `"${proto.title}": linked directory "${repoDir}" does not exist \u2014 skipping`);
2598
2580
  failed.set(key, `directory does not exist: ${repoDir}`);
2599
2581
  continue;
@@ -2987,11 +2969,8 @@ var subtaskCompleteCommand = new Command12("subtask-complete").description("Mark
2987
2969
  console.log(`\u2713 Subtask completed: ${subtask.title}`);
2988
2970
  });
2989
2971
 
2990
- // cli/commands/up.ts
2972
+ // cli/commands/prototype.ts
2991
2973
  import { Command as Command13 } from "commander";
2992
- import { spawn as spawn5 } from "child_process";
2993
- import { resolve as resolve2 } from "path";
2994
- import { fileURLToPath as fileURLToPath2 } from "url";
2995
2974
  var c4 = {
2996
2975
  reset: "\x1B[0m",
2997
2976
  bold: "\x1B[1m",
@@ -3000,113 +2979,28 @@ var c4 = {
3000
2979
  green: "\x1B[32m",
3001
2980
  yellow: "\x1B[33m",
3002
2981
  red: "\x1B[31m",
3003
- magenta: "\x1B[35m",
2982
+ blue: "\x1B[34m",
3004
2983
  gray: "\x1B[90m"
3005
2984
  };
3006
2985
  function paint4(color, text) {
3007
2986
  return `${c4[color]}${text}${c4.reset}`;
3008
2987
  }
3009
- function timestamp2() {
3010
- return paint4("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
3011
- }
3012
- function upTag() {
3013
- return paint4("cyan", "[up]");
3014
- }
3015
- function log(msg) {
3016
- console.log(`${timestamp2()} ${upTag()} ${msg}`);
3017
- }
3018
- function logErr(msg) {
3019
- console.error(`${timestamp2()} ${upTag()} ${paint4("red", "\u2717")} ${msg}`);
3020
- }
3021
- function spawnMr(args) {
3022
- const entry = process.argv[1];
3023
- const child = spawn5(process.execPath, [entry, ...args], {
3024
- stdio: ["ignore", "pipe", "pipe"]
3025
- });
3026
- return child;
3027
- }
3028
- var upCommand = new Command13("up").description("Run mr watch and mr digest together with a single command").option("--interval <seconds>", "Polling interval for both watch and digest", "15").option("--watch-interval <seconds>", "Override polling interval for mr watch").option("--digest-interval <seconds>", "Override polling interval for mr digest").option("--dry-run", "Pass --dry-run to mr watch", false).option("--plan-approval", "Pass --plan-approval to mr watch", false).option("--root <dir>", "Pass --root to mr watch (default: cwd)").option("--agent <agent>", "AI agent to use: claude or codex", "claude").action((opts) => {
3029
- const watchInterval = opts.watchInterval ?? opts.interval;
3030
- const digestInterval = opts.digestInterval ?? opts.interval;
3031
- const agent = opts.agent === "codex" ? "codex" : "claude";
3032
- const watchArgs = ["watch", "--interval", watchInterval, "--agent", agent];
3033
- if (opts.dryRun) watchArgs.push("--dry-run");
3034
- if (opts.planApproval) watchArgs.push("--plan-approval");
3035
- if (opts.root) watchArgs.push("--root", opts.root);
3036
- const digestArgs = ["digest", "--interval", digestInterval, "--agent", agent];
3037
- const banner = [
3038
- ``,
3039
- paint4("cyan", ` \u2554\u2566\u2557\u2566\u2550\u2557 \u2566 \u2566\u2554\u2550\u2557`),
3040
- paint4("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D \u2551 \u2551\u2560\u2550\u255D`),
3041
- paint4("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u2569 `),
3042
- paint4("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3043
- paint4("dim", ` mr watch + mr digest \xB7 powered by ${agent}`),
3044
- ``
3045
- ].join("\n");
3046
- console.log(banner);
3047
- const flags = [
3048
- `watch-interval=${paint4("cyan", watchInterval + "s")}`,
3049
- `digest-interval=${paint4("cyan", digestInterval + "s")}`,
3050
- `agent=${paint4("cyan", agent)}`,
3051
- ...opts.dryRun ? [paint4("yellow", "dry-run")] : [],
3052
- ...opts.planApproval ? [paint4("yellow", "plan-approval")] : [],
3053
- ...opts.root ? [`root=${paint4("cyan", resolve2(opts.root))}`] : []
3054
- ].join(" ");
3055
- console.log(` ${flags}
3056
- `);
3057
- const watchProc = spawnMr(watchArgs);
3058
- const digestProc = spawnMr(digestArgs);
3059
- for (const [proc, label] of [[watchProc, "watch"], [digestProc, "digest"]]) {
3060
- proc.stdout?.on("data", (d) => process.stdout.write(d));
3061
- proc.stderr?.on("data", (d) => process.stderr.write(d));
3062
- proc.on("exit", (code, signal) => {
3063
- if (signal !== "SIGTERM" && signal !== "SIGINT") {
3064
- logErr(`mr ${label} exited unexpectedly (code=${code ?? "?"}, signal=${signal ?? "none"})`);
3065
- }
3066
- });
3067
- }
3068
- function shutdown() {
3069
- log("Shutting down\u2026");
3070
- watchProc.kill("SIGTERM");
3071
- digestProc.kill("SIGTERM");
3072
- setTimeout(() => process.exit(0), 500);
3073
- }
3074
- process.on("SIGINT", shutdown);
3075
- process.on("SIGTERM", shutdown);
3076
- });
3077
-
3078
- // cli/commands/prototype.ts
3079
- import { Command as Command14 } from "commander";
3080
- var c5 = {
3081
- reset: "\x1B[0m",
3082
- bold: "\x1B[1m",
3083
- dim: "\x1B[2m",
3084
- cyan: "\x1B[36m",
3085
- green: "\x1B[32m",
3086
- yellow: "\x1B[33m",
3087
- red: "\x1B[31m",
3088
- blue: "\x1B[34m",
3089
- gray: "\x1B[90m"
3090
- };
3091
- function paint5(color, text) {
3092
- return `${c5[color]}${text}${c5.reset}`;
3093
- }
3094
2988
  function statusBadge(status) {
3095
2989
  switch (status) {
3096
2990
  case "pending":
3097
- return paint5("gray", "\u25CB pending");
2991
+ return paint4("gray", "\u25CB pending");
3098
2992
  case "in_progress":
3099
- return paint5("cyan", "\u27F3 in_progress");
2993
+ return paint4("cyan", "\u27F3 in_progress");
3100
2994
  case "completed":
3101
- return paint5("green", "\u2713 completed");
2995
+ return paint4("green", "\u2713 completed");
3102
2996
  case "failed":
3103
- return paint5("red", "\u2717 failed");
2997
+ return paint4("red", "\u2717 failed");
3104
2998
  default:
3105
- return paint5("gray", status);
2999
+ return paint4("gray", status);
3106
3000
  }
3107
3001
  }
3108
- var prototypeCommand = new Command14("prototype").description("Manage prototypes").addCommand(
3109
- new Command14("list").description("List prototypes for the linked project").option("--all", "Show prototypes for all projects").action(async (opts) => {
3002
+ var prototypeCommand = new Command13("prototype").description("Manage prototypes").addCommand(
3003
+ new Command13("list").description("List prototypes for the linked project").option("--all", "Show prototypes for all projects").action(async (opts) => {
3110
3004
  const params = new URLSearchParams();
3111
3005
  if (!opts.all) {
3112
3006
  const projectId = getLinkedProjectId();
@@ -3118,21 +3012,21 @@ var prototypeCommand = new Command14("prototype").description("Manage prototypes
3118
3012
  }
3119
3013
  const prototypes = await api.get(`/api/prototypes?${params.toString()}`);
3120
3014
  if (prototypes.length === 0) {
3121
- console.log(paint5("gray", "No prototypes found."));
3015
+ console.log(paint4("gray", "No prototypes found."));
3122
3016
  return;
3123
3017
  }
3124
3018
  console.log();
3125
3019
  for (const p of prototypes) {
3126
3020
  const date = new Date(p.createdAt).toLocaleDateString();
3127
3021
  console.log(
3128
- ` ${paint5("bold", p.title)} ${statusBadge(p.status)} ${paint5("gray", p.id.slice(0, 8))} ${paint5("dim", date)}`
3022
+ ` ${paint4("bold", p.title)} ${statusBadge(p.status)} ${paint4("gray", p.id.slice(0, 8))} ${paint4("dim", date)}`
3129
3023
  );
3130
- console.log(` ${paint5("dim", p.prompt.slice(0, 80) + (p.prompt.length > 80 ? "\u2026" : ""))}`);
3024
+ console.log(` ${paint4("dim", p.prompt.slice(0, 80) + (p.prompt.length > 80 ? "\u2026" : ""))}`);
3131
3025
  console.log();
3132
3026
  }
3133
3027
  })
3134
3028
  ).addCommand(
3135
- new Command14("create").description("Create a new prototype").argument("<title>", "Title of the prototype").requiredOption("--prompt <prompt>", "Design description / prompt").option("--project <projectId>", "Project ID (defaults to linked project)").option("--variants <count>", "Number of variants to generate (1-50)", "5").action(async (title, opts) => {
3029
+ new Command13("create").description("Create a new prototype").argument("<title>", "Title of the prototype").requiredOption("--prompt <prompt>", "Design description / prompt").option("--project <projectId>", "Project ID (defaults to linked project)").option("--variants <count>", "Number of variants to generate (1-50)", "5").action(async (title, opts) => {
3136
3030
  const projectId = opts.project ?? getLinkedProjectId();
3137
3031
  if (!projectId) {
3138
3032
  console.error('No project linked. Run "mr link <project-id>" or use --project.');
@@ -3146,37 +3040,37 @@ var prototypeCommand = new Command14("prototype").description("Manage prototypes
3146
3040
  projectId
3147
3041
  });
3148
3042
  console.log();
3149
- console.log(` ${paint5("green", "\u2713")} Created prototype: ${paint5("bold", prototype.title)}`);
3150
- console.log(` ${paint5("gray", "ID:")} ${prototype.id}`);
3151
- console.log(` ${paint5("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
3043
+ console.log(` ${paint4("green", "\u2713")} Created prototype: ${paint4("bold", prototype.title)}`);
3044
+ console.log(` ${paint4("gray", "ID:")} ${prototype.id}`);
3045
+ console.log(` ${paint4("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
3152
3046
  console.log();
3153
3047
  })
3154
3048
  ).addCommand(
3155
- new Command14("start").description("Start prototype generation (sets status to in_progress)").argument("<id>", "Prototype ID").action(async (id) => {
3049
+ new Command13("start").description("Start prototype generation (sets status to in_progress)").argument("<id>", "Prototype ID").action(async (id) => {
3156
3050
  const prototype = await api.patch(`/api/prototypes/${id}`, {
3157
3051
  status: "in_progress"
3158
3052
  });
3159
3053
  console.log();
3160
- console.log(` ${paint5("cyan", "\u27F3")} Started: ${paint5("bold", prototype.title)}`);
3161
- console.log(` ${paint5("gray", "The watch agent will pick this up shortly.")}`);
3054
+ console.log(` ${paint4("cyan", "\u27F3")} Started: ${paint4("bold", prototype.title)}`);
3055
+ console.log(` ${paint4("gray", "The watch agent will pick this up shortly.")}`);
3162
3056
  console.log();
3163
3057
  })
3164
3058
  ).addCommand(
3165
- new Command14("retry").description("Retry a failed prototype").argument("<id>", "Prototype ID").action(async (id) => {
3059
+ new Command13("retry").description("Retry a failed prototype").argument("<id>", "Prototype ID").action(async (id) => {
3166
3060
  const prototype = await api.patch(`/api/prototypes/${id}`, {
3167
3061
  status: "in_progress",
3168
3062
  files: null
3169
3063
  });
3170
3064
  console.log();
3171
- console.log(` ${paint5("cyan", "\u27F3")} Retrying: ${paint5("bold", prototype.title)}`);
3065
+ console.log(` ${paint4("cyan", "\u27F3")} Retrying: ${paint4("bold", prototype.title)}`);
3172
3066
  console.log();
3173
3067
  })
3174
3068
  );
3175
3069
 
3176
3070
  // cli/commands/setup.ts
3177
- import { Command as Command15 } from "commander";
3071
+ import { Command as Command14 } from "commander";
3178
3072
  import { exec as exec2 } from "child_process";
3179
- var c6 = {
3073
+ var c5 = {
3180
3074
  reset: "\x1B[0m",
3181
3075
  bold: "\x1B[1m",
3182
3076
  dim: "\x1B[2m",
@@ -3187,8 +3081,8 @@ var c6 = {
3187
3081
  magenta: "\x1B[35m",
3188
3082
  gray: "\x1B[90m"
3189
3083
  };
3190
- function paint6(color, text) {
3191
- return `${c6[color]}${text}${c6.reset}`;
3084
+ function paint5(color, text) {
3085
+ return `${c5[color]}${text}${c5.reset}`;
3192
3086
  }
3193
3087
  function commandExists(cmd) {
3194
3088
  return new Promise((resolve7) => {
@@ -3432,60 +3326,60 @@ async function checkApiConnectivity() {
3432
3326
  }
3433
3327
  }
3434
3328
  function printResults(checks) {
3435
- const maxNameLen = Math.max(...checks.map((c11) => c11.name.length));
3329
+ const maxNameLen = Math.max(...checks.map((c10) => c10.name.length));
3436
3330
  let allOk = true;
3437
3331
  for (const check of checks) {
3438
3332
  const isOptional = check.optional ?? false;
3439
- const icon = check.ok ? paint6("green", "\u2713") : isOptional ? paint6("yellow", "\u25CB") : paint6("red", "\u2717");
3333
+ const icon = check.ok ? paint5("green", "\u2713") : isOptional ? paint5("yellow", "\u25CB") : paint5("red", "\u2717");
3440
3334
  const name = check.name.padEnd(maxNameLen);
3441
- const msg = check.ok ? paint6("green", check.message) : isOptional ? paint6("yellow", check.message) : paint6("red", check.message);
3335
+ const msg = check.ok ? paint5("green", check.message) : isOptional ? paint5("yellow", check.message) : paint5("red", check.message);
3442
3336
  console.log(` ${icon} ${name} ${msg}`);
3443
3337
  if (!check.ok && !isOptional) allOk = false;
3444
3338
  }
3445
3339
  return allOk;
3446
3340
  }
3447
3341
  async function autoFix(checks, agent) {
3448
- const { spawn: spawn9 } = await import("child_process");
3449
- const ghInstalled = checks.find((c11) => c11.name === "GitHub CLI (gh)").ok;
3450
- const ghAuthed = checks.find((c11) => c11.name === "GitHub CLI auth").ok;
3451
- const mrAuthed = checks.find((c11) => c11.name === "Mr. Manager CLI auth").ok;
3452
- const claudeCheck = checks.find((c11) => c11.name === "Claude Code (claude)");
3342
+ const { spawn: spawn8 } = await import("child_process");
3343
+ const ghInstalled = checks.find((c10) => c10.name === "GitHub CLI (gh)").ok;
3344
+ const ghAuthed = checks.find((c10) => c10.name === "GitHub CLI auth").ok;
3345
+ const mrAuthed = checks.find((c10) => c10.name === "Mr. Manager CLI auth").ok;
3346
+ const claudeCheck = checks.find((c10) => c10.name === "Claude Code (claude)");
3453
3347
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
3454
- console.log(paint6("cyan", " Installing Claude Code..."));
3455
- console.log(paint6("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
3348
+ console.log(paint5("cyan", " Installing Claude Code..."));
3349
+ console.log(paint5("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
3456
3350
  await new Promise((resolve7) => {
3457
- const child = spawn9("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
3351
+ const child = spawn8("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
3458
3352
  child.on("exit", () => resolve7());
3459
3353
  });
3460
3354
  console.log("");
3461
3355
  }
3462
3356
  if (ghInstalled && !ghAuthed) {
3463
- console.log(paint6("cyan", " Running gh auth login..."));
3357
+ console.log(paint5("cyan", " Running gh auth login..."));
3464
3358
  await new Promise((resolve7) => {
3465
- const child = spawn9("gh", ["auth", "login"], { stdio: "inherit" });
3359
+ const child = spawn8("gh", ["auth", "login"], { stdio: "inherit" });
3466
3360
  child.on("exit", () => resolve7());
3467
3361
  });
3468
3362
  console.log("");
3469
3363
  }
3470
3364
  if (!mrAuthed) {
3471
- console.log(paint6("cyan", " Running mr login..."));
3365
+ console.log(paint5("cyan", " Running mr login..."));
3472
3366
  const entry = process.argv[1];
3473
3367
  await new Promise((resolve7) => {
3474
- const child = spawn9(process.execPath, [entry, "login"], { stdio: "inherit" });
3368
+ const child = spawn8(process.execPath, [entry, "login"], { stdio: "inherit" });
3475
3369
  child.on("exit", () => resolve7());
3476
3370
  });
3477
3371
  console.log("");
3478
3372
  }
3479
3373
  }
3480
- var setupCommand = new Command15("setup").description("Check that all dependencies for mr watch are installed and configured").option("--fix", "Attempt to auto-fix issues where possible", false).option("--agent <agent>", "AI agent to check: claude, codex, or gemini (default: claude)", "claude").action(async (opts) => {
3374
+ var setupCommand = new Command14("setup").description("Check that all dependencies for mr watch are installed and configured").option("--fix", "Attempt to auto-fix issues where possible", false).option("--agent <agent>", "AI agent to check: claude, codex, or gemini (default: claude)", "claude").action(async (opts) => {
3481
3375
  const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
3482
3376
  const banner = [
3483
3377
  ``,
3484
- paint6("cyan", ` \u2554\u2566\u2557\u2551\u2550\u2557 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2551 \u2551\u2554\u2550\u2557`),
3485
- paint6("magenta", ` \u2551\u2551\u2551\u2560\u2566\u2563 \u255A\u2550\u2557\u2551\u2563 \u2551 \u2551 \u2551\u2560\u2550\u2563`),
3486
- paint6("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u255A\u2550\u255D \u2569 \u255A\u2550\u255D\u2569 `),
3487
- paint6("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3488
- paint6("dim", ` environment check for mr watch (agent: ${agent})`),
3378
+ paint5("cyan", ` \u2554\u2566\u2557\u2551\u2550\u2557 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2551 \u2551\u2554\u2550\u2557`),
3379
+ paint5("magenta", ` \u2551\u2551\u2551\u2560\u2566\u2563 \u255A\u2550\u2557\u2551\u2563 \u2551 \u2551 \u2551\u2560\u2550\u2563`),
3380
+ paint5("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u255A\u2550\u255D \u2569 \u255A\u2550\u255D\u2569 `),
3381
+ paint5("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3382
+ paint5("dim", ` environment check for mr watch (agent: ${agent})`),
3489
3383
  ``
3490
3384
  ].join("\n");
3491
3385
  console.log(banner);
@@ -3505,18 +3399,18 @@ var setupCommand = new Command15("setup").description("Check that all dependenci
3505
3399
  const allOk = printResults(checks);
3506
3400
  console.log("");
3507
3401
  if (allOk) {
3508
- console.log(paint6("green", " All checks passed! You're ready to run mr watch."));
3402
+ console.log(paint5("green", " All checks passed! You're ready to run mr watch."));
3509
3403
  if (agent === "claude") {
3510
- console.log(paint6("dim", " Tip: check other agents with --agent codex or --agent gemini"));
3404
+ console.log(paint5("dim", " Tip: check other agents with --agent codex or --agent gemini"));
3511
3405
  }
3512
3406
  console.log("");
3513
3407
  return;
3514
3408
  }
3515
- const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
3409
+ const fixes = checks.filter((c10) => !c10.ok && c10.fix && !c10.optional);
3516
3410
  if (fixes.length > 0) {
3517
- console.log(paint6("yellow", " To fix:"));
3411
+ console.log(paint5("yellow", " To fix:"));
3518
3412
  for (const fix of fixes) {
3519
- console.log(` ${paint6("dim", "\u2192")} ${paint6("bold", fix.name)}: ${fix.fix}`);
3413
+ console.log(` ${paint5("dim", "\u2192")} ${paint5("bold", fix.name)}: ${fix.fix}`);
3520
3414
  }
3521
3415
  console.log("");
3522
3416
  }
@@ -3527,8 +3421,8 @@ var setupCommand = new Command15("setup").description("Check that all dependenci
3527
3421
  });
3528
3422
 
3529
3423
  // cli/commands/update.ts
3530
- import { Command as Command16 } from "commander";
3531
- var updateCommand = new Command16("update").description("Post a status update to a task (used by agents to report progress)").argument("<task-id>", "Task ID").argument("<message>", "Status update message").option("--source <source>", "Update source: agent, system, or user", "agent").action(async (taskId, message, opts) => {
3424
+ import { Command as Command15 } from "commander";
3425
+ var updateCommand = new Command15("update").description("Post a status update to a task (used by agents to report progress)").argument("<task-id>", "Task ID").argument("<message>", "Status update message").option("--source <source>", "Update source: agent, system, or user", "agent").action(async (taskId, message, opts) => {
3532
3426
  await api.post(`/api/tasks/${taskId}/updates`, {
3533
3427
  message,
3534
3428
  source: opts.source
@@ -3537,11 +3431,11 @@ var updateCommand = new Command16("update").description("Post a status update to
3537
3431
  });
3538
3432
 
3539
3433
  // cli/commands/screenshot.ts
3540
- import { Command as Command17 } from "commander";
3541
- import { readFileSync as readFileSync6, existsSync as existsSync7, unlinkSync as unlinkSync2 } from "fs";
3434
+ import { Command as Command16 } from "commander";
3435
+ import { readFileSync as readFileSync6, existsSync as existsSync8, unlinkSync as unlinkSync2 } from "fs";
3542
3436
  import { join as join8 } from "path";
3543
3437
  import { tmpdir } from "os";
3544
- var screenshotCommand = new Command17("screenshot").description(
3438
+ var screenshotCommand = new Command16("screenshot").description(
3545
3439
  "Take or attach a screenshot to a task update (agents use this to show their work)"
3546
3440
  ).argument("<task-id>", "Task ID").argument("[file]", "Path to an image file (if omitted, uses headless browser to screenshot the app)").option("-m, --message <message>", "Optional message to include with the screenshot").option("-u, --url <url>", "Custom URL to screenshot (defaults to the task's project page)").action(async (taskId, file, opts) => {
3547
3441
  let filePath = file;
@@ -3578,13 +3472,13 @@ var screenshotCommand = new Command17("screenshot").description(
3578
3472
  console.error("[screenshot] Make sure the browse daemon is set up: mr browse setup");
3579
3473
  process.exit(1);
3580
3474
  }
3581
- if (!existsSync7(tempFile)) {
3475
+ if (!existsSync8(tempFile)) {
3582
3476
  console.error("[screenshot] Screenshot file was not created");
3583
3477
  process.exit(1);
3584
3478
  }
3585
3479
  filePath = tempFile;
3586
3480
  }
3587
- if (!existsSync7(filePath)) {
3481
+ if (!existsSync8(filePath)) {
3588
3482
  console.error(`File not found: ${filePath}`);
3589
3483
  process.exit(1);
3590
3484
  }
@@ -3626,16 +3520,16 @@ var screenshotCommand = new Command17("screenshot").description(
3626
3520
  source: "agent"
3627
3521
  });
3628
3522
  console.log(`\u2713 Screenshot uploaded and attached to task`);
3629
- if (tempFile && existsSync7(tempFile)) {
3523
+ if (tempFile && existsSync8(tempFile)) {
3630
3524
  unlinkSync2(tempFile);
3631
3525
  }
3632
3526
  });
3633
3527
 
3634
3528
  // cli/commands/resume.ts
3635
- import { Command as Command18 } from "commander";
3636
- import { spawn as spawn6 } from "child_process";
3529
+ import { Command as Command17 } from "commander";
3530
+ import { spawn as spawn5 } from "child_process";
3637
3531
  import { resolve as resolve3 } from "path";
3638
- var c7 = {
3532
+ var c6 = {
3639
3533
  reset: "\x1B[0m",
3640
3534
  bold: "\x1B[1m",
3641
3535
  dim: "\x1B[2m",
@@ -3646,15 +3540,15 @@ var c7 = {
3646
3540
  magenta: "\x1B[35m",
3647
3541
  gray: "\x1B[90m"
3648
3542
  };
3649
- function paint7(color, text) {
3650
- return `${c7[color]}${text}${c7.reset}`;
3543
+ function paint6(color, text) {
3544
+ return `${c6[color]}${text}${c6.reset}`;
3651
3545
  }
3652
- var resumeCommand = new Command18("resume").description("Resume an interactive Claude session for a task (non-headless)").argument("<task-id>", "Task ID whose Claude session to resume").option("--dir <directory>", "Override the working directory for the session").action(async (taskId, opts) => {
3546
+ var resumeCommand = new Command17("resume").description("Resume an interactive Claude session for a task (non-headless)").argument("<task-id>", "Task ID whose Claude session to resume").option("--dir <directory>", "Override the working directory for the session").action(async (taskId, opts) => {
3653
3547
  const task = await api.get(`/api/tasks/${taskId}`);
3654
3548
  if (!task.claudeSessionId) {
3655
3549
  console.error(
3656
3550
  `
3657
- ${paint7("red", "\u2717")} No Claude session found for task ${paint7("dim", taskId.slice(0, 8))}`
3551
+ ${paint6("red", "\u2717")} No Claude session found for task ${paint6("dim", taskId.slice(0, 8))}`
3658
3552
  );
3659
3553
  console.error(
3660
3554
  ` The task may not have been dispatched yet.
@@ -3665,16 +3559,16 @@ var resumeCommand = new Command18("resume").description("Resume an interactive C
3665
3559
  if (task.status === "completed" || task.status === "todo") {
3666
3560
  console.error(
3667
3561
  `
3668
- ${paint7("yellow", "\u26A0")} Task ${paint7("dim", taskId.slice(0, 8))} has already completed.`
3562
+ ${paint6("yellow", "\u26A0")} Task ${paint6("dim", taskId.slice(0, 8))} has already completed.`
3669
3563
  );
3670
3564
  console.error(
3671
- ` Session ID: ${paint7("cyan", task.claudeSessionId)}`
3565
+ ` Session ID: ${paint6("cyan", task.claudeSessionId)}`
3672
3566
  );
3673
3567
  console.error(
3674
- ` View the session log in the Mr. Manager web UI, or run:`
3568
+ ` To resume the session, run:`
3675
3569
  );
3676
3570
  console.error(
3677
- ` ${paint7("dim", `claude --resume ${task.claudeSessionId}`)}
3571
+ ` ${paint6("dim", `claude --resume ${task.claudeSessionId}`)}
3678
3572
  `
3679
3573
  );
3680
3574
  process.exit(0);
@@ -3693,16 +3587,16 @@ var resumeCommand = new Command18("resume").description("Resume an interactive C
3693
3587
  const sid = taskId.slice(0, 8);
3694
3588
  console.log([
3695
3589
  ``,
3696
- paint7("magenta", ` \u2554\u2566\u2557\u2566\u2550\u2557 `) + paint7("bold", "resume"),
3697
- paint7("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D `) + paint7("dim", "\u2500".repeat(44)),
3698
- paint7("magenta", ` \u2569 \u2569\u2569\u255A\u2550 `) + task.title,
3699
- ` ${paint7("gray", sid)}`,
3590
+ paint6("magenta", ` \u2554\u2566\u2557\u2566\u2550\u2557 `) + paint6("bold", "resume"),
3591
+ paint6("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D `) + paint6("dim", "\u2500".repeat(44)),
3592
+ paint6("magenta", ` \u2569 \u2569\u2569\u255A\u2550 `) + task.title,
3593
+ ` ${paint6("gray", sid)}`,
3700
3594
  ``,
3701
- ` ${paint7("dim", "session")} ${paint7("cyan", task.claudeSessionId)}`,
3702
- ` ${paint7("dim", "cwd")} ${paint7("cyan", cwd)}`,
3595
+ ` ${paint6("dim", "session")} ${paint6("cyan", task.claudeSessionId)}`,
3596
+ ` ${paint6("dim", "cwd")} ${paint6("cyan", cwd)}`,
3703
3597
  ``
3704
3598
  ].join("\n"));
3705
- const child = spawn6("claude", ["--resume", task.claudeSessionId], {
3599
+ const child = spawn5("claude", ["--resume", task.claudeSessionId], {
3706
3600
  cwd,
3707
3601
  stdio: "inherit"
3708
3602
  });
@@ -3711,16 +3605,16 @@ var resumeCommand = new Command18("resume").description("Resume an interactive C
3711
3605
  });
3712
3606
  child.on("error", (err) => {
3713
3607
  console.error(`
3714
- ${paint7("red", "\u2717")} Failed to launch Claude: ${err.message}
3608
+ ${paint6("red", "\u2717")} Failed to launch Claude: ${err.message}
3715
3609
  `);
3716
3610
  process.exit(1);
3717
3611
  });
3718
3612
  });
3719
3613
 
3720
3614
  // cli/commands/browse.ts
3721
- import { Command as Command19 } from "commander";
3722
- import { execSync as execSync5, spawn as spawn7 } from "child_process";
3723
- import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
3615
+ import { Command as Command18 } from "commander";
3616
+ import { execSync as execSync5, spawn as spawn6 } from "child_process";
3617
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
3724
3618
  import { join as join9 } from "path";
3725
3619
  var BROWSE_DIR2 = join9(import.meta.dirname, "..", "..", "browse");
3726
3620
  function isProcessAlive(pid) {
@@ -3760,7 +3654,7 @@ async function ensureDevServer() {
3760
3654
  }
3761
3655
  const port = await findAvailablePort2(3e3);
3762
3656
  console.log(`[browse] Starting dev server on port ${port}...`);
3763
- const devProc = spawn7("npm", ["run", "dev", "--", "--port", String(port)], {
3657
+ const devProc = spawn6("npm", ["run", "dev", "--", "--port", String(port)], {
3764
3658
  stdio: ["ignore", "pipe", "pipe"],
3765
3659
  detached: true,
3766
3660
  cwd: join9(import.meta.dirname, "..", ".."),
@@ -3782,7 +3676,7 @@ async function ensureDevServer() {
3782
3676
  }
3783
3677
  throw new Error("Dev server failed to start within 30s");
3784
3678
  }
3785
- var browseCommand = new Command19("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
3679
+ 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(
3786
3680
  "--task-id <id>",
3787
3681
  "Attach screenshot to a task update (only for screenshot command)"
3788
3682
  ).option("--dev", "Auto-start local dev server before browsing").allowUnknownOption(true).action(
@@ -3825,7 +3719,7 @@ var browseCommand = new Command19("browse").description("Control a headless brow
3825
3719
  }
3826
3720
  if (command === "screenshot" && opts.taskId) {
3827
3721
  const screenshotPath = stdout.match(/Screenshot saved: (.+)/)?.[1];
3828
- if (!screenshotPath || !existsSync8(screenshotPath)) {
3722
+ if (!screenshotPath || !existsSync9(screenshotPath)) {
3829
3723
  console.error("[browse] Could not find screenshot file");
3830
3724
  process.exit(1);
3831
3725
  }
@@ -3865,12 +3759,12 @@ var browseCommand = new Command19("browse").description("Control a headless brow
3865
3759
  );
3866
3760
 
3867
3761
  // cli/commands/set-path.ts
3868
- import { Command as Command20 } from "commander";
3762
+ import { Command as Command19 } from "commander";
3869
3763
  import { resolve as resolve4 } from "path";
3870
- import { existsSync as existsSync9 } from "fs";
3871
- var setPathCommand = new Command20("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) => {
3764
+ import { existsSync as existsSync10 } from "fs";
3765
+ 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) => {
3872
3766
  const absolutePath = resolve4(pathArg);
3873
- if (!existsSync9(absolutePath)) {
3767
+ if (!existsSync10(absolutePath)) {
3874
3768
  console.error(`Error: Path does not exist: ${absolutePath}`);
3875
3769
  process.exit(1);
3876
3770
  }
@@ -3886,9 +3780,9 @@ var setPathCommand = new Command20("set-path").description("Set or update the lo
3886
3780
  });
3887
3781
 
3888
3782
  // cli/commands/test.ts
3889
- import { Command as Command21 } from "commander";
3890
- import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
3891
- var testCommand = new Command21("test").description("Run automated browser test for a task's MR/PR").argument("<task-id>", "Task ID to test").option("--plan <file>", "Path to a custom test plan JSON file").action(async (taskId, opts) => {
3783
+ import { Command as Command20 } from "commander";
3784
+ import { readFileSync as readFileSync8, existsSync as existsSync11 } from "fs";
3785
+ var testCommand = new Command20("test").description("Run automated browser test for a task's MR/PR").argument("<task-id>", "Task ID to test").option("--plan <file>", "Path to a custom test plan JSON file").action(async (taskId, opts) => {
3892
3786
  const config = loadConfig();
3893
3787
  console.log("[test] Fetching task...");
3894
3788
  let task;
@@ -3909,7 +3803,7 @@ var testCommand = new Command21("test").description("Run automated browser test
3909
3803
  );
3910
3804
  process.exit(1);
3911
3805
  }
3912
- if (!existsSync10(localPath)) {
3806
+ if (!existsSync11(localPath)) {
3913
3807
  console.error(
3914
3808
  `[test] Local repo path not found at ${localPath}. Run \`mr set-path\` to update.`
3915
3809
  );
@@ -4008,11 +3902,11 @@ var testCommand = new Command21("test").description("Run automated browser test
4008
3902
  });
4009
3903
 
4010
3904
  // cli/commands/features.ts
4011
- import { Command as Command22 } from "commander";
4012
- import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync11 } from "fs";
3905
+ import { Command as Command21 } from "commander";
3906
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync12 } from "fs";
4013
3907
  import { resolve as resolve5, sep as sep2 } from "path";
4014
3908
  var FEATURES_FILE3 = ".mr-features.md";
4015
- var c8 = {
3909
+ var c7 = {
4016
3910
  reset: "\x1B[0m",
4017
3911
  bold: "\x1B[1m",
4018
3912
  dim: "\x1B[2m",
@@ -4022,8 +3916,8 @@ var c8 = {
4022
3916
  magenta: "\x1B[35m",
4023
3917
  gray: "\x1B[90m"
4024
3918
  };
4025
- function paint8(color, text) {
4026
- return `${c8[color]}${text}${c8.reset}`;
3919
+ function paint7(color, text) {
3920
+ return `${c7[color]}${text}${c7.reset}`;
4027
3921
  }
4028
3922
  function resolveProjectRoot2() {
4029
3923
  const cwd = process.cwd();
@@ -4039,10 +3933,10 @@ function getFeaturesPath() {
4039
3933
  }
4040
3934
  function readFeatures2() {
4041
3935
  const path = getFeaturesPath();
4042
- if (!existsSync11(path)) return null;
3936
+ if (!existsSync12(path)) return null;
4043
3937
  return readFileSync9(path, "utf-8");
4044
3938
  }
4045
- var featuresCommand = new Command22("features").description("View or update the project features & goals document (.mr-features.md)").option("--update <content>", "Replace the features document with the given content").option("--file <path>", "Read content from a file and use it to update the features document").option("--path", "Print the path to the features file").action(async (opts) => {
3939
+ var featuresCommand = new Command21("features").description("View or update the project features & goals document (.mr-features.md)").option("--update <content>", "Replace the features document with the given content").option("--file <path>", "Read content from a file and use it to update the features document").option("--path", "Print the path to the features file").action(async (opts) => {
4046
3940
  if (opts.path) {
4047
3941
  console.log(getFeaturesPath());
4048
3942
  return;
@@ -4051,30 +3945,30 @@ var featuresCommand = new Command22("features").description("View or update the
4051
3945
  const content2 = readFileSync9(resolve5(opts.file), "utf-8");
4052
3946
  const featuresPath = getFeaturesPath();
4053
3947
  writeFileSync5(featuresPath, content2);
4054
- console.log(`${paint8("green", "\u2713")} Updated ${paint8("cyan", featuresPath)} from ${paint8("cyan", opts.file)}`);
3948
+ console.log(`${paint7("green", "\u2713")} Updated ${paint7("cyan", featuresPath)} from ${paint7("cyan", opts.file)}`);
4055
3949
  return;
4056
3950
  }
4057
3951
  if (opts.update) {
4058
3952
  const featuresPath = getFeaturesPath();
4059
3953
  writeFileSync5(featuresPath, opts.update);
4060
- console.log(`${paint8("green", "\u2713")} Updated ${paint8("cyan", featuresPath)}`);
3954
+ console.log(`${paint7("green", "\u2713")} Updated ${paint7("cyan", featuresPath)}`);
4061
3955
  return;
4062
3956
  }
4063
3957
  const content = readFeatures2();
4064
3958
  if (!content) {
4065
- console.log(paint8("dim", `No features document found. One will be created when an agent completes a task.`));
4066
- console.log(paint8("dim", `Path: ${getFeaturesPath()}`));
3959
+ console.log(paint7("dim", `No features document found. One will be created when an agent completes a task.`));
3960
+ console.log(paint7("dim", `Path: ${getFeaturesPath()}`));
4067
3961
  return;
4068
3962
  }
4069
3963
  console.log(content);
4070
3964
  });
4071
3965
 
4072
3966
  // cli/commands/no-mr.ts
4073
- import { Command as Command23 } from "commander";
3967
+ import { Command as Command22 } from "commander";
4074
3968
  import { writeFileSync as writeFileSync6 } from "fs";
4075
3969
  import { resolve as resolve6 } from "path";
4076
3970
  var NO_MR_FILE = ".mr-no-mr";
4077
- var noMrCommand = new Command23("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) => {
3971
+ 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) => {
4078
3972
  const filePath = resolve6(process.cwd(), NO_MR_FILE);
4079
3973
  writeFileSync6(filePath, description, "utf-8");
4080
3974
  await api.post(`/api/tasks/${taskId}/updates`, {
@@ -4086,8 +3980,8 @@ var noMrCommand = new Command23("no-mr").description("Signal that a task does no
4086
3980
  });
4087
3981
 
4088
3982
  // cli/commands/mobile.ts
4089
- import { Command as Command24 } from "commander";
4090
- function paint9(color, text) {
3983
+ import { Command as Command23 } from "commander";
3984
+ function paint8(color, text) {
4091
3985
  const colors = {
4092
3986
  cyan: "\x1B[36m",
4093
3987
  green: "\x1B[32m",
@@ -4098,7 +3992,7 @@ function paint9(color, text) {
4098
3992
  };
4099
3993
  return `${colors[color] ?? ""}${text}${colors.reset}`;
4100
3994
  }
4101
- var mobileCommand = new Command24("mobile").description(
3995
+ var mobileCommand = new Command23("mobile").description(
4102
3996
  "Start the Web-to-Mobile Conversion Wizard for the linked project"
4103
3997
  ).argument("[project-id]", "Project ID (defaults to linked project)").option("--framework <framework>", "Framework: react-native or native").option("--url <url>", "Web app URL for analysis").action(
4104
3998
  async (projectIdArg, opts) => {
@@ -4110,7 +4004,7 @@ var mobileCommand = new Command24("mobile").description(
4110
4004
  process.exit(1);
4111
4005
  }
4112
4006
  console.log(
4113
- paint9("cyan", "mobile") + paint9("dim", " \u2014 starting conversion wizard")
4007
+ paint8("cyan", "mobile") + paint8("dim", " \u2014 starting conversion wizard")
4114
4008
  );
4115
4009
  const task = await api.post("/api/tasks", {
4116
4010
  title: "Convert to Mobile App",
@@ -4141,13 +4035,13 @@ var mobileCommand = new Command24("mobile").description(
4141
4035
  }
4142
4036
  console.log(
4143
4037
  `
4144
- ${paint9("green", "\u2713")} Wizard initialized. Open the web UI to continue:
4038
+ ${paint8("green", "\u2713")} Wizard initialized. Open the web UI to continue:
4145
4039
  \u2192 Task ID: ${task.id}
4146
4040
  \u2192 Use the "Convert to Mobile" button on the project page`
4147
4041
  );
4148
4042
  }
4149
4043
  );
4150
- var statusSubcommand = new Command24("status").description("Show mobile conversion status for a task").argument("<task-id>", "Parent conversion task ID").action(async (taskId) => {
4044
+ var statusSubcommand = new Command23("status").description("Show mobile conversion status for a task").argument("<task-id>", "Parent conversion task ID").action(async (taskId) => {
4151
4045
  const resources = await api.get(
4152
4046
  `/api/tasks/${taskId}/resources`
4153
4047
  );
@@ -4160,7 +4054,7 @@ var statusSubcommand = new Command24("status").description("Show mobile conversi
4160
4054
  }
4161
4055
  try {
4162
4056
  const state = JSON.parse(wizardState.content);
4163
- console.log(paint9("cyan", "Mobile Conversion Status"));
4057
+ console.log(paint8("cyan", "Mobile Conversion Status"));
4164
4058
  console.log(` Phase: ${state.phase}`);
4165
4059
  if (state.framework) {
4166
4060
  console.log(` Framework: ${state.framework}`);
@@ -4183,13 +4077,13 @@ var statusSubcommand = new Command24("status").description("Show mobile conversi
4183
4077
  mobileCommand.addCommand(statusSubcommand);
4184
4078
 
4185
4079
  // cli/commands/scan.ts
4186
- import { Command as Command25 } from "commander";
4080
+ import { Command as Command24 } from "commander";
4187
4081
 
4188
4082
  // lib/scanner/index.ts
4189
- import { spawn as spawn8 } from "child_process";
4083
+ import { spawn as spawn7 } from "child_process";
4190
4084
 
4191
4085
  // lib/scanner/config.ts
4192
- import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
4086
+ import { readFileSync as readFileSync10, existsSync as existsSync13 } from "fs";
4193
4087
  import { join as join10 } from "path";
4194
4088
  var ALL_FINDING_TYPES = [
4195
4089
  "idea",
@@ -4207,7 +4101,7 @@ var DEFAULTS = {
4207
4101
  };
4208
4102
  function loadScanConfig(projectPath) {
4209
4103
  const configPath2 = join10(projectPath, ".mr-scan.json");
4210
- if (!existsSync12(configPath2)) {
4104
+ if (!existsSync13(configPath2)) {
4211
4105
  return { ...DEFAULTS };
4212
4106
  }
4213
4107
  try {
@@ -4253,13 +4147,13 @@ async function authenticateBrowseSession(magicUrl, runBrowse) {
4253
4147
  }
4254
4148
 
4255
4149
  // lib/scanner/codebase-analysis.ts
4256
- import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync13 } from "fs";
4150
+ import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync14 } from "fs";
4257
4151
  import { join as join11, relative } from "path";
4258
4152
  import { execSync as execSync6 } from "child_process";
4259
4153
  function resolveDir(projectPath, candidates) {
4260
4154
  for (const candidate of candidates) {
4261
4155
  const dir = join11(projectPath, candidate);
4262
- if (existsSync13(dir)) return dir;
4156
+ if (existsSync14(dir)) return dir;
4263
4157
  }
4264
4158
  return null;
4265
4159
  }
@@ -4293,7 +4187,7 @@ function discoverRoutes(projectPath) {
4293
4187
  }
4294
4188
  function extractModels(projectPath) {
4295
4189
  const schemaPath = join11(projectPath, "prisma", "schema.prisma");
4296
- if (existsSync13(schemaPath)) {
4190
+ if (existsSync14(schemaPath)) {
4297
4191
  const content = readFileSync11(schemaPath, "utf-8");
4298
4192
  const models2 = [];
4299
4193
  const modelRegex = /^model\s+(\w+)\s*\{/gm;
@@ -4307,7 +4201,7 @@ function extractModels(projectPath) {
4307
4201
  const drizzleDirs = ["src/db", "src/schema", "db", "drizzle"];
4308
4202
  for (const dir of drizzleDirs) {
4309
4203
  const fullDir = join11(projectPath, dir);
4310
- if (!existsSync13(fullDir)) continue;
4204
+ if (!existsSync14(fullDir)) continue;
4311
4205
  try {
4312
4206
  const entries = readdirSync2(fullDir, { withFileTypes: true });
4313
4207
  for (const entry of entries) {
@@ -4348,7 +4242,7 @@ function discoverComponents(projectPath) {
4348
4242
  function extractInternalLinks(projectPath) {
4349
4243
  const links = /* @__PURE__ */ new Set();
4350
4244
  function searchDir(dir) {
4351
- if (!existsSync13(dir)) return;
4245
+ if (!existsSync14(dir)) return;
4352
4246
  const entries = readdirSync2(dir, { withFileTypes: true });
4353
4247
  for (const entry of entries) {
4354
4248
  if (entry.isDirectory()) {
@@ -4652,10 +4546,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
4652
4546
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
4653
4547
 
4654
4548
  **Components:**
4655
- ${codebaseAnalysis.components.slice(0, 30).map((c11) => `- ${c11}`).join("\n")}
4549
+ ${codebaseAnalysis.components.slice(0, 30).map((c10) => `- ${c10}`).join("\n")}
4656
4550
 
4657
4551
  **Recent Git Commits:**
4658
- ${codebaseAnalysis.recentCommits.slice(0, 15).map((c11) => `- ${c11}`).join("\n")}
4552
+ ${codebaseAnalysis.recentCommits.slice(0, 15).map((c10) => `- ${c10}`).join("\n")}
4659
4553
 
4660
4554
  **Completed Tasks:**
4661
4555
  ${context.completedTasks.slice(0, 20).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -4865,7 +4759,7 @@ async function fetchScanContext(opts) {
4865
4759
  }
4866
4760
  function runClaude(prompt2) {
4867
4761
  return new Promise((resolve7, reject) => {
4868
- const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
4762
+ const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
4869
4763
  stdio: ["ignore", "pipe", "pipe"]
4870
4764
  });
4871
4765
  let output = "";
@@ -4921,7 +4815,7 @@ function parseSynthesisOutput(output) {
4921
4815
  }
4922
4816
 
4923
4817
  // cli/commands/scan.ts
4924
- var c9 = {
4818
+ var c8 = {
4925
4819
  reset: "\x1B[0m",
4926
4820
  bold: "\x1B[1m",
4927
4821
  dim: "\x1B[2m",
@@ -4932,53 +4826,53 @@ var c9 = {
4932
4826
  magenta: "\x1B[35m",
4933
4827
  gray: "\x1B[90m"
4934
4828
  };
4935
- function paint10(color, text) {
4936
- return `${c9[color]}${text}${c9.reset}`;
4829
+ function paint9(color, text) {
4830
+ return `${c8[color]}${text}${c8.reset}`;
4937
4831
  }
4938
- function timestamp3() {
4939
- return paint10("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
4832
+ function timestamp2() {
4833
+ return paint9("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
4940
4834
  }
4941
4835
  function scanTag() {
4942
- return paint10("magenta", "[scan]");
4836
+ return paint9("magenta", "[scan]");
4943
4837
  }
4944
- function log2(msg) {
4945
- console.log(`${timestamp3()} ${scanTag()} ${msg}`);
4838
+ function log(msg) {
4839
+ console.log(`${timestamp2()} ${scanTag()} ${msg}`);
4946
4840
  }
4947
4841
  function logOk(msg) {
4948
- console.log(`${timestamp3()} ${scanTag()} ${paint10("green", "\u2713")} ${msg}`);
4842
+ console.log(`${timestamp2()} ${scanTag()} ${paint9("green", "\u2713")} ${msg}`);
4949
4843
  }
4950
- function logErr2(msg) {
4951
- console.error(`${timestamp3()} ${scanTag()} ${paint10("red", "\u2717")} ${msg}`);
4844
+ function logErr(msg) {
4845
+ console.error(`${timestamp2()} ${scanTag()} ${paint9("red", "\u2717")} ${msg}`);
4952
4846
  }
4953
- var scanCommand = new Command25("scan").description("Run a product scan on the current project \u2014 analyzes codebase, crawls the app, and surfaces findings").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing scan report ID (created by UI trigger)").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
4847
+ var scanCommand = new Command24("scan").description("Run a product scan on the current project \u2014 analyzes codebase, crawls the app, and surfaces findings").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing scan report ID (created by UI trigger)").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
4954
4848
  const config = loadConfig();
4955
4849
  if (!config.apiKey) {
4956
- logErr2('Not authenticated. Run "mr login" first.');
4850
+ logErr('Not authenticated. Run "mr login" first.');
4957
4851
  process.exit(1);
4958
4852
  }
4959
4853
  const banner = [
4960
4854
  ``,
4961
- paint10("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
4962
- paint10("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
4963
- paint10("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
4964
- paint10("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
4965
- paint10("dim", ` autonomous product scanner`),
4855
+ paint9("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
4856
+ paint9("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
4857
+ paint9("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
4858
+ paint9("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
4859
+ paint9("dim", ` autonomous product scanner`),
4966
4860
  ``
4967
4861
  ].join("\n");
4968
4862
  console.log(banner);
4969
4863
  const projectId = opts.project || getLinkedProjectId();
4970
4864
  if (!projectId) {
4971
- logErr2('No project linked. Run "mr link" or pass --project <id>.');
4865
+ logErr('No project linked. Run "mr link" or pass --project <id>.');
4972
4866
  process.exit(1);
4973
4867
  }
4974
4868
  let project;
4975
4869
  try {
4976
4870
  project = await api.get(`/api/projects/${projectId}`);
4977
4871
  } catch {
4978
- logErr2(`Failed to fetch project ${projectId}`);
4872
+ logErr(`Failed to fetch project ${projectId}`);
4979
4873
  process.exit(1);
4980
4874
  }
4981
- log2(`Scanning project: ${paint10("cyan", project.name)}`);
4875
+ log(`Scanning project: ${paint9("cyan", project.name)}`);
4982
4876
  let projectPath = project.localPath;
4983
4877
  if (!projectPath) {
4984
4878
  for (const [dir, pid] of Object.entries(config.directories)) {
@@ -4994,12 +4888,12 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
4994
4888
  let reportId;
4995
4889
  if (opts.report) {
4996
4890
  reportId = opts.report;
4997
- log2(`Using existing scan report ${paint10("yellow", reportId.slice(0, 8))}`);
4891
+ log(`Using existing scan report ${paint9("yellow", reportId.slice(0, 8))}`);
4998
4892
  } else {
4999
4893
  try {
5000
4894
  const scans = await api.get(`/api/scans?projectId=${projectId}&status=processing`);
5001
4895
  if (scans.length > 0) {
5002
- logErr2("A scan is already in progress for this project. Wait for it to complete.");
4896
+ logErr("A scan is already in progress for this project. Wait for it to complete.");
5003
4897
  process.exit(1);
5004
4898
  }
5005
4899
  } catch {
@@ -5010,9 +4904,9 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5010
4904
  status: "pending"
5011
4905
  });
5012
4906
  reportId = report.id;
5013
- log2(`Created scan report ${paint10("yellow", reportId.slice(0, 8))}`);
4907
+ log(`Created scan report ${paint9("yellow", reportId.slice(0, 8))}`);
5014
4908
  } catch (err) {
5015
- logErr2(`Failed to create scan report: ${err.message}`);
4909
+ logErr(`Failed to create scan report: ${err.message}`);
5016
4910
  process.exit(1);
5017
4911
  }
5018
4912
  }
@@ -5023,7 +4917,7 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5023
4917
  try {
5024
4918
  const current = await api.get(`/api/scans/${reportId}`);
5025
4919
  if (current.status === "cancelled") {
5026
- log2(paint10("yellow", "Scan was cancelled \u2014 aborting."));
4920
+ log(paint9("yellow", "Scan was cancelled \u2014 aborting."));
5027
4921
  process.exit(0);
5028
4922
  }
5029
4923
  } catch {
@@ -5037,9 +4931,9 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5037
4931
  apiUrl: config.apiUrl,
5038
4932
  apiKey: config.apiKey,
5039
4933
  runBrowse: runBrowseCommand2,
5040
- onLog: log2,
4934
+ onLog: log,
5041
4935
  onProgress: (phase, detail) => {
5042
- log2(`${paint10("dim", `[${phase}]`)} ${detail}`);
4936
+ log(`${paint9("dim", `[${phase}]`)} ${detail}`);
5043
4937
  }
5044
4938
  });
5045
4939
  let wasCancelled = false;
@@ -5051,7 +4945,7 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5051
4945
  } catch {
5052
4946
  }
5053
4947
  if (wasCancelled) {
5054
- log2(paint10("yellow", "Scan was cancelled by user \u2014 discarding results."));
4948
+ log(paint9("yellow", "Scan was cancelled by user \u2014 discarding results."));
5055
4949
  process.exit(0);
5056
4950
  }
5057
4951
  await api.patch(`/api/scans/${reportId}`, {
@@ -5062,37 +4956,37 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5062
4956
  scanDurationMs: result.scanDurationMs,
5063
4957
  routesCrawled: result.routesCrawled
5064
4958
  });
5065
- logOk(`Scan complete \u2014 ${paint10("cyan", String(result.findings.length))} findings`);
4959
+ logOk(`Scan complete \u2014 ${paint9("cyan", String(result.findings.length))} findings`);
5066
4960
  console.log("");
5067
- console.log(` ${paint10("bold", "Summary:")} ${result.summary}`);
4961
+ console.log(` ${paint9("bold", "Summary:")} ${result.summary}`);
5068
4962
  console.log("");
5069
4963
  const high = result.findings.filter((f) => f.priority === "high");
5070
4964
  const medium = result.findings.filter((f) => f.priority === "medium");
5071
4965
  const low = result.findings.filter((f) => f.priority === "low");
5072
4966
  if (high.length > 0) {
5073
- console.log(` ${paint10("bold", paint10("red", `High Priority (${high.length})`))}`);
4967
+ console.log(` ${paint9("bold", paint9("red", `High Priority (${high.length})`))}`);
5074
4968
  for (const f of high) {
5075
- console.log(` ${paint10("red", "\u25CF")} [${f.type}] ${f.title}`);
5076
- console.log(` ${paint10("dim", f.description.slice(0, 120))}`);
4969
+ console.log(` ${paint9("red", "\u25CF")} [${f.type}] ${f.title}`);
4970
+ console.log(` ${paint9("dim", f.description.slice(0, 120))}`);
5077
4971
  }
5078
4972
  console.log("");
5079
4973
  }
5080
4974
  if (medium.length > 0) {
5081
- console.log(` ${paint10("bold", paint10("yellow", `Medium Priority (${medium.length})`))}`);
4975
+ console.log(` ${paint9("bold", paint9("yellow", `Medium Priority (${medium.length})`))}`);
5082
4976
  for (const f of medium) {
5083
- console.log(` ${paint10("yellow", "\u25CF")} [${f.type}] ${f.title}`);
4977
+ console.log(` ${paint9("yellow", "\u25CF")} [${f.type}] ${f.title}`);
5084
4978
  }
5085
4979
  console.log("");
5086
4980
  }
5087
4981
  if (low.length > 0) {
5088
- console.log(` ${paint10("dim", `Low Priority (${low.length})`)} `);
4982
+ console.log(` ${paint9("dim", `Low Priority (${low.length})`)} `);
5089
4983
  for (const f of low) {
5090
- console.log(` ${paint10("dim", `\u25CB [${f.type}] ${f.title}`)}`);
4984
+ console.log(` ${paint9("dim", `\u25CB [${f.type}] ${f.title}`)}`);
5091
4985
  }
5092
4986
  console.log("");
5093
4987
  }
5094
4988
  } catch (err) {
5095
- logErr2(`Scan failed: ${err.message}`);
4989
+ logErr(`Scan failed: ${err.message}`);
5096
4990
  try {
5097
4991
  await api.patch(`/api/scans/${reportId}`, {
5098
4992
  status: "failed",
@@ -5105,8 +4999,8 @@ var scanCommand = new Command25("scan").description("Run a product scan on the c
5105
4999
  });
5106
5000
 
5107
5001
  // cli/commands/idea.ts
5108
- import { Command as Command26 } from "commander";
5109
- var c10 = {
5002
+ import { Command as Command25 } from "commander";
5003
+ var c9 = {
5110
5004
  reset: "\x1B[0m",
5111
5005
  bold: "\x1B[1m",
5112
5006
  dim: "\x1B[2m",
@@ -5118,27 +5012,27 @@ var c10 = {
5118
5012
  gray: "\x1B[90m",
5119
5013
  magenta: "\x1B[35m"
5120
5014
  };
5121
- function paint11(color, text) {
5122
- return `${c10[color]}${text}${c10.reset}`;
5015
+ function paint10(color, text) {
5016
+ return `${c9[color]}${text}${c9.reset}`;
5123
5017
  }
5124
5018
  function statusBadge2(status) {
5125
5019
  switch (status) {
5126
5020
  case "draft":
5127
- return paint11("gray", "\u25CB draft");
5021
+ return paint10("gray", "\u25CB draft");
5128
5022
  case "generating":
5129
- return paint11("cyan", "\u27F3 generating");
5023
+ return paint10("cyan", "\u27F3 generating");
5130
5024
  case "generated":
5131
- return paint11("green", "\u2713 generated");
5025
+ return paint10("green", "\u2713 generated");
5132
5026
  case "promoted":
5133
- return paint11("magenta", "\u2191 promoted");
5027
+ return paint10("magenta", "\u2191 promoted");
5134
5028
  case "archived":
5135
- return paint11("dim", "\u2298 archived");
5029
+ return paint10("dim", "\u2298 archived");
5136
5030
  default:
5137
- return paint11("gray", status);
5031
+ return paint10("gray", status);
5138
5032
  }
5139
5033
  }
5140
- var ideaCommand = new Command26("idea").description("Manage ideas \u2014 brainstorm, generate prototypes & plans").addCommand(
5141
- new Command26("list").description("List ideas for the linked project").option("--all", "Show ideas for all projects").option("--status <status>", "Filter by status").action(async (opts) => {
5034
+ var ideaCommand = new Command25("idea").description("Manage ideas \u2014 brainstorm, generate prototypes & plans").addCommand(
5035
+ new Command25("list").description("List ideas for the linked project").option("--all", "Show ideas for all projects").option("--status <status>", "Filter by status").action(async (opts) => {
5142
5036
  const params = new URLSearchParams();
5143
5037
  if (!opts.all) {
5144
5038
  const projectId = getLinkedProjectId();
@@ -5149,23 +5043,23 @@ var ideaCommand = new Command26("idea").description("Manage ideas \u2014 brainst
5149
5043
  if (opts.status) params.set("status", opts.status);
5150
5044
  const ideas = await api.get(`/api/ideas?${params.toString()}`);
5151
5045
  if (ideas.length === 0) {
5152
- console.log(paint11("gray", "No ideas found."));
5046
+ console.log(paint10("gray", "No ideas found."));
5153
5047
  return;
5154
5048
  }
5155
5049
  console.log();
5156
5050
  for (const idea of ideas) {
5157
5051
  const date = new Date(idea.createdAt).toLocaleDateString();
5158
5052
  console.log(
5159
- ` ${paint11("bold", idea.title)} ${statusBadge2(idea.status)} ${paint11("gray", idea.id.slice(0, 8))} ${paint11("dim", date)}`
5053
+ ` ${paint10("bold", idea.title)} ${statusBadge2(idea.status)} ${paint10("gray", idea.id.slice(0, 8))} ${paint10("dim", date)}`
5160
5054
  );
5161
5055
  if (idea.description) {
5162
- console.log(` ${paint11("dim", idea.description.slice(0, 80) + (idea.description.length > 80 ? "\u2026" : ""))}`);
5056
+ console.log(` ${paint10("dim", idea.description.slice(0, 80) + (idea.description.length > 80 ? "\u2026" : ""))}`);
5163
5057
  }
5164
5058
  console.log();
5165
5059
  }
5166
5060
  })
5167
5061
  ).addCommand(
5168
- new Command26("create").description("Create a new idea").argument("<title>", "Title of the idea").option("--description <desc>", "Description of the idea").option("--project <projectId>", "Project ID (defaults to linked project)").option("--generate", "Immediately start generating plan & prototype").action(async (title, opts) => {
5062
+ new Command25("create").description("Create a new idea").argument("<title>", "Title of the idea").option("--description <desc>", "Description of the idea").option("--project <projectId>", "Project ID (defaults to linked project)").option("--generate", "Immediately start generating plan & prototype").action(async (title, opts) => {
5169
5063
  const projectId = opts.project ?? getLinkedProjectId() ?? null;
5170
5064
  const idea = await api.post("/api/ideas", {
5171
5065
  title,
@@ -5173,65 +5067,65 @@ var ideaCommand = new Command26("idea").description("Manage ideas \u2014 brainst
5173
5067
  projectId
5174
5068
  });
5175
5069
  console.log();
5176
- console.log(` ${paint11("green", "\u2713")} Created idea: ${paint11("bold", idea.title)}`);
5177
- console.log(` ${paint11("gray", "ID:")} ${idea.id}`);
5070
+ console.log(` ${paint10("green", "\u2713")} Created idea: ${paint10("bold", idea.title)}`);
5071
+ console.log(` ${paint10("gray", "ID:")} ${idea.id}`);
5178
5072
  if (opts.generate) {
5179
5073
  await api.post(`/api/ideas/${idea.id}/generate`);
5180
- console.log(` ${paint11("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
5074
+ console.log(` ${paint10("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
5181
5075
  }
5182
5076
  console.log();
5183
5077
  })
5184
5078
  ).addCommand(
5185
- new Command26("generate").description("Start generating plan & prototype for an idea").argument("<id>", "Idea ID").action(async (id) => {
5079
+ new Command25("generate").description("Start generating plan & prototype for an idea").argument("<id>", "Idea ID").action(async (id) => {
5186
5080
  const idea = await api.post(`/api/ideas/${id}/generate`);
5187
5081
  console.log();
5188
- console.log(` ${paint11("cyan", "\u27F3")} Generating: ${paint11("bold", idea.title)}`);
5189
- console.log(` ${paint11("gray", "The watch agent will pick this up shortly.")}`);
5082
+ console.log(` ${paint10("cyan", "\u27F3")} Generating: ${paint10("bold", idea.title)}`);
5083
+ console.log(` ${paint10("gray", "The watch agent will pick this up shortly.")}`);
5190
5084
  console.log();
5191
5085
  })
5192
5086
  ).addCommand(
5193
- new Command26("feedback").description("Send feedback to iterate on an idea's generated content").argument("<id>", "Idea ID").argument("<feedback>", "Feedback text").action(async (id, feedback) => {
5087
+ new Command25("feedback").description("Send feedback to iterate on an idea's generated content").argument("<id>", "Idea ID").argument("<feedback>", "Feedback text").action(async (id, feedback) => {
5194
5088
  const idea = await api.post(`/api/ideas/${id}/feedback`, { feedback });
5195
5089
  console.log();
5196
- console.log(` ${paint11("cyan", "\u27F3")} Feedback sent for: ${paint11("bold", idea.title)}`);
5197
- console.log(` ${paint11("gray", "The watch agent will re-generate with your feedback.")}`);
5090
+ console.log(` ${paint10("cyan", "\u27F3")} Feedback sent for: ${paint10("bold", idea.title)}`);
5091
+ console.log(` ${paint10("gray", "The watch agent will re-generate with your feedback.")}`);
5198
5092
  console.log();
5199
5093
  })
5200
5094
  ).addCommand(
5201
- new Command26("promote").description("Promote an idea to a task").argument("<id>", "Idea ID").action(async (id) => {
5095
+ new Command25("promote").description("Promote an idea to a task").argument("<id>", "Idea ID").action(async (id) => {
5202
5096
  const result = await api.post(`/api/ideas/${id}/promote`);
5203
5097
  console.log();
5204
- console.log(` ${paint11("green", "\u2713")} Promoted idea to task: ${paint11("bold", result.task.title)}`);
5205
- console.log(` ${paint11("gray", "Task ID:")} ${result.task.id}`);
5098
+ console.log(` ${paint10("green", "\u2713")} Promoted idea to task: ${paint10("bold", result.task.title)}`);
5099
+ console.log(` ${paint10("gray", "Task ID:")} ${result.task.id}`);
5206
5100
  console.log();
5207
5101
  })
5208
5102
  ).addCommand(
5209
- new Command26("spin-up").description("Spin up a new project with a GitHub repo from an idea").argument("<id>", "Idea ID").option("--name <name>", "Custom project name (defaults to idea title)").action(async (id, opts) => {
5103
+ new Command25("spin-up").description("Spin up a new project with a GitHub repo from an idea").argument("<id>", "Idea ID").option("--name <name>", "Custom project name (defaults to idea title)").action(async (id, opts) => {
5210
5104
  const body = {};
5211
5105
  if (opts.name) body.name = opts.name;
5212
5106
  const result = await api.post(`/api/ideas/${id}/spin-up`, body);
5213
5107
  console.log();
5214
- console.log(` ${paint11("green", "\u2713")} Spinning up project: ${paint11("bold", result.project.name)}`);
5215
- console.log(` ${paint11("gray", "Project ID:")} ${result.project.id}`);
5108
+ console.log(` ${paint10("green", "\u2713")} Spinning up project: ${paint10("bold", result.project.name)}`);
5109
+ console.log(` ${paint10("gray", "Project ID:")} ${result.project.id}`);
5216
5110
  if (result.tasks && result.tasks.length > 0) {
5217
- console.log(` ${paint11("green", "\u2713")} Created ${result.tasks.length} follow-up task(s):`);
5111
+ console.log(` ${paint10("green", "\u2713")} Created ${result.tasks.length} follow-up task(s):`);
5218
5112
  for (const task of result.tasks) {
5219
- console.log(` ${paint11("gray", "\u2022")} ${task.title}`);
5113
+ console.log(` ${paint10("gray", "\u2022")} ${task.title}`);
5220
5114
  }
5221
5115
  }
5222
- console.log(` ${paint11("cyan", "\u27F3")} Repo creation is queued \u2014 the watch daemon will pick it up.`);
5116
+ console.log(` ${paint10("cyan", "\u27F3")} Repo creation is queued \u2014 the watch daemon will pick it up.`);
5223
5117
  console.log();
5224
5118
  })
5225
5119
  );
5226
5120
 
5227
5121
  // cli/commands/doctor.ts
5228
- import { Command as Command27 } from "commander";
5229
- import { existsSync as existsSync14 } from "fs";
5230
- import { homedir as homedir3 } from "os";
5122
+ import { Command as Command26 } from "commander";
5123
+ import { existsSync as existsSync15 } from "fs";
5124
+ import { homedir as homedir2 } from "os";
5231
5125
  import { join as join12 } from "path";
5232
5126
  async function checkConfigExists() {
5233
- const configPath2 = join12(homedir3(), ".mr-manager", "config.json");
5234
- const exists = existsSync14(configPath2);
5127
+ const configPath2 = join12(homedir2(), ".mr-manager", "config.json");
5128
+ const exists = existsSync15(configPath2);
5235
5129
  if (!exists) {
5236
5130
  return {
5237
5131
  name: "Config file",
@@ -5273,12 +5167,12 @@ async function checkProjectLink() {
5273
5167
  optional: true
5274
5168
  };
5275
5169
  }
5276
- var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
5170
+ var doctorCommand = new Command26("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
5277
5171
  const banner = [
5278
5172
  ``,
5279
- paint6("cyan", ` MR DOCTOR`),
5280
- paint6("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
5281
- paint6("dim", ` diagnosing your mr environment`),
5173
+ paint5("cyan", ` MR DOCTOR`),
5174
+ paint5("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
5175
+ paint5("dim", ` diagnosing your mr environment`),
5282
5176
  ``
5283
5177
  ].join("\n");
5284
5178
  console.log(banner);
@@ -5300,15 +5194,15 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
5300
5194
  const allOk = printResults(checks);
5301
5195
  console.log("");
5302
5196
  if (allOk) {
5303
- console.log(paint6("green", " Everything looks good!"));
5197
+ console.log(paint5("green", " Everything looks good!"));
5304
5198
  console.log("");
5305
5199
  return;
5306
5200
  }
5307
- const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
5201
+ const fixes = checks.filter((c10) => !c10.ok && c10.fix && !c10.optional);
5308
5202
  if (fixes.length > 0) {
5309
- console.log(paint6("yellow", " To fix:"));
5203
+ console.log(paint5("yellow", " To fix:"));
5310
5204
  for (const fix of fixes) {
5311
- console.log(` ${paint6("dim", "\u2192")} ${paint6("bold", fix.name)}: ${fix.fix}`);
5205
+ console.log(` ${paint5("dim", "\u2192")} ${paint5("bold", fix.name)}: ${fix.fix}`);
5312
5206
  }
5313
5207
  console.log("");
5314
5208
  }
@@ -5316,13 +5210,13 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
5316
5210
  });
5317
5211
 
5318
5212
  // cli/index.ts
5319
- var configPath = join13(homedir4(), ".mr-manager", "config.json");
5320
- var isFirstRun = !existsSync15(configPath);
5213
+ var configPath = join13(homedir3(), ".mr-manager", "config.json");
5214
+ var isFirstRun = !existsSync16(configPath);
5321
5215
  var userArgs = process.argv.slice(2);
5322
5216
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
5323
5217
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
5324
5218
  if (isFirstRun && !shouldBypass) {
5325
- const c11 = {
5219
+ const c10 = {
5326
5220
  reset: "\x1B[0m",
5327
5221
  bold: "\x1B[1m",
5328
5222
  dim: "\x1B[2m",
@@ -5332,29 +5226,29 @@ if (isFirstRun && !shouldBypass) {
5332
5226
  magenta: "\x1B[35m"
5333
5227
  };
5334
5228
  console.log("");
5335
- console.log(`${c11.cyan} \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566\u2550\u2557${c11.reset}`);
5336
- console.log(`${c11.magenta} \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2560\u2550\u2563\u2551\u2551\u2551\u2560\u2550\u2563\u2551 \u2566\u2551\u2563 \u2560\u2566\u255D${c11.reset}`);
5337
- console.log(`${c11.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c11.reset}`);
5338
- console.log(`${c11.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c11.reset}`);
5229
+ console.log(`${c10.cyan} \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566\u2550\u2557${c10.reset}`);
5230
+ console.log(`${c10.magenta} \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2560\u2550\u2563\u2551\u2551\u2551\u2560\u2550\u2563\u2551 \u2566\u2551\u2563 \u2560\u2566\u255D${c10.reset}`);
5231
+ console.log(`${c10.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c10.reset}`);
5232
+ console.log(`${c10.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c10.reset}`);
5339
5233
  console.log("");
5340
- console.log(`${c11.bold} Welcome to Mr. Manager!${c11.reset}`);
5341
- console.log(`${c11.dim} Let's get you set up in a few quick steps.${c11.reset}`);
5234
+ console.log(`${c10.bold} Welcome to Mr. Manager!${c10.reset}`);
5235
+ console.log(`${c10.dim} Let's get you set up in a few quick steps.${c10.reset}`);
5342
5236
  console.log("");
5343
- console.log(` ${c11.yellow}Step 1:${c11.reset} Authenticate via Google OAuth`);
5344
- console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr login${c11.reset}`);
5237
+ console.log(` ${c10.yellow}Step 1:${c10.reset} Authenticate via Google OAuth`);
5238
+ console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr login${c10.reset}`);
5345
5239
  console.log("");
5346
- console.log(` ${c11.yellow}Step 2:${c11.reset} Verify your environment`);
5347
- console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr setup${c11.reset}`);
5240
+ console.log(` ${c10.yellow}Step 2:${c10.reset} Verify your environment`);
5241
+ console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr setup${c10.reset}`);
5348
5242
  console.log("");
5349
- console.log(` ${c11.yellow}Step 3:${c11.reset} Link a repo and start watching`);
5350
- console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr link${c11.reset} ${c11.dim}&&${c11.reset} ${c11.cyan}mr watch${c11.reset}`);
5243
+ console.log(` ${c10.yellow}Step 3:${c10.reset} Link a repo and start watching`);
5244
+ console.log(` ${c10.dim}Run:${c10.reset} ${c10.cyan}mr link${c10.reset} ${c10.dim}&&${c10.reset} ${c10.cyan}mr watch${c10.reset}`);
5351
5245
  console.log("");
5352
- console.log(`${c11.dim} Or run ${c11.reset}${c11.cyan}mr login${c11.reset}${c11.dim} to get started now.${c11.reset}`);
5246
+ console.log(`${c10.dim} Or run ${c10.reset}${c10.cyan}mr login${c10.reset}${c10.dim} to get started now.${c10.reset}`);
5353
5247
  console.log("");
5354
5248
  process.exit(0);
5355
5249
  }
5356
- var program = new Command28();
5357
- program.name("mr").description("Mr. Manager - Task and project management CLI").version("0.2.0");
5250
+ var program = new Command27();
5251
+ program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
5358
5252
  program.addCommand(initCommand);
5359
5253
  program.addCommand(authCommand);
5360
5254
  program.addCommand(loginCommand);
@@ -5370,7 +5264,6 @@ program.addCommand(undelegateCommand);
5370
5264
  program.addCommand(createCommand);
5371
5265
  program.addCommand(completeCommand);
5372
5266
  program.addCommand(subtaskCompleteCommand);
5373
- program.addCommand(upCommand);
5374
5267
  program.addCommand(prototypeCommand);
5375
5268
  program.addCommand(setupCommand);
5376
5269
  program.addCommand(updateCommand);