@dunnewold-labs/mr-manager 0.4.40 → 0.4.43

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 +461 -216
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli/index.ts
4
- import { Command as Command33 } from "commander";
5
- import { existsSync as existsSync18 } from "fs";
4
+ import { Command as Command34 } from "commander";
5
+ import { existsSync as existsSync19 } from "fs";
6
6
  import { homedir as homedir3 } from "os";
7
7
  import { join as join12 } from "path";
8
8
 
@@ -185,7 +185,7 @@ import { fileURLToPath } from "url";
185
185
  // cli/package.json
186
186
  var package_default = {
187
187
  name: "@dunnewold-labs/mr-manager",
188
- version: "0.4.40",
188
+ version: "0.4.43",
189
189
  description: "Mr. Manager - Task and project management CLI",
190
190
  bin: {
191
191
  mr: "./dist/index.mjs"
@@ -900,7 +900,7 @@ async function runTest(options) {
900
900
  recordingEnabled = true,
901
901
  recordingContext = "test-run"
902
902
  } = options;
903
- const log3 = onProgress || (() => {
903
+ const log4 = onProgress || (() => {
904
904
  });
905
905
  const result = {
906
906
  status: "passed",
@@ -917,30 +917,30 @@ async function runTest(options) {
917
917
  let wtPath = null;
918
918
  const worktreeName = `mr-test-${taskId.slice(0, 8)}`;
919
919
  const timeoutHandle = setTimeout(() => {
920
- log3("Test timed out after 5 minutes");
920
+ log4("Test timed out after 5 minutes");
921
921
  if (devProc) devProc.kill("SIGTERM");
922
922
  }, 5 * 60 * 1e3);
923
923
  try {
924
- log3("Extracting branch from MR/PR link...");
924
+ log4("Extracting branch from MR/PR link...");
925
925
  const branch = extractBranchFromLink(taskLink, localPath);
926
926
  if (!branch) {
927
927
  throw new Error(`Could not extract branch from link: ${taskLink}`);
928
928
  }
929
- log3(`Branch: ${branch}`);
930
- log3("Creating git worktree...");
929
+ log4(`Branch: ${branch}`);
930
+ log4("Creating git worktree...");
931
931
  wtPath = createWorktree(localPath, branch, worktreeName).path;
932
- log3(`Worktree created at ${wtPath}`);
933
- log3("Installing dependencies...");
932
+ log4(`Worktree created at ${wtPath}`);
933
+ log4("Installing dependencies...");
934
934
  try {
935
935
  installDependencies(wtPath);
936
936
  } catch (err) {
937
- log3(`Warning: dependency install failed: ${err.message}`);
937
+ log4(`Warning: dependency install failed: ${err.message}`);
938
938
  }
939
- log3("Starting dev server...");
939
+ log4("Starting dev server...");
940
940
  const port = await findAvailablePort(4e3);
941
941
  devProc = await startDevServer(wtPath, port);
942
942
  const baseUrl = `http://127.0.0.1:${port}`;
943
- log3(`Dev server running on ${baseUrl}`);
943
+ log4(`Dev server running on ${baseUrl}`);
944
944
  let recordingStarted = false;
945
945
  if (recordingEnabled) {
946
946
  try {
@@ -949,28 +949,28 @@ async function runTest(options) {
949
949
  throw new Error(recordingStart.stdout || "recording-start failed");
950
950
  }
951
951
  recordingStarted = true;
952
- log3("Proof recording started");
952
+ log4("Proof recording started");
953
953
  } catch (err) {
954
954
  result.proof = {
955
955
  state: "proof_missing_capture_failed",
956
956
  details: `Proof recording could not start: ${err.message}`
957
957
  };
958
- log3(result.proof.details);
958
+ log4(result.proof.details);
959
959
  }
960
960
  } else {
961
961
  result.proof = {
962
962
  state: "proof_missing_disabled",
963
963
  details: "Proof recording disabled for this run."
964
964
  };
965
- log3(result.proof.details);
965
+ log4(result.proof.details);
966
966
  }
967
967
  await browseRunner(["goto", baseUrl]);
968
968
  const plan = customPlan || buildDefaultTestPlan(baseUrl);
969
- log3(`Executing ${plan.length}-step test plan...`);
969
+ log4(`Executing ${plan.length}-step test plan...`);
970
970
  for (let i = 0; i < plan.length; i++) {
971
971
  const step = plan[i];
972
972
  const stepDesc = step.description || `${step.command} ${(step.args || []).join(" ")}`;
973
- log3(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
973
+ log4(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
974
974
  try {
975
975
  if (step.command.startsWith("assert")) {
976
976
  const assertResult = await evaluateAssertion(step, i, browseRunner);
@@ -1020,7 +1020,7 @@ async function runTest(options) {
1020
1020
  }
1021
1021
  } catch (err) {
1022
1022
  result.errors.push(`Step ${i + 1} (${step.command}): ${err.message}`);
1023
- log3(`Step ${i + 1} error: ${err.message}`);
1023
+ log4(`Step ${i + 1} error: ${err.message}`);
1024
1024
  }
1025
1025
  }
1026
1026
  if (recordingStarted) {
@@ -1032,7 +1032,7 @@ async function runTest(options) {
1032
1032
  throw new Error(recordingStop.stdout || "recording-stop did not return a file path");
1033
1033
  }
1034
1034
  result.proof.localPath = savedPath;
1035
- log3(`Proof recording finalized at ${savedPath}`);
1035
+ log4(`Proof recording finalized at ${savedPath}`);
1036
1036
  if (uploadVideo) {
1037
1037
  const videoUrl = await uploadVideo(savedPath);
1038
1038
  if (videoUrl) {
@@ -1054,14 +1054,14 @@ async function runTest(options) {
1054
1054
  }
1055
1055
  ]
1056
1056
  };
1057
- log3(`Proof recording uploaded to ${videoUrl}`);
1057
+ log4(`Proof recording uploaded to ${videoUrl}`);
1058
1058
  } else {
1059
1059
  result.proof = {
1060
1060
  state: "proof_missing_upload_failed",
1061
1061
  details: "Proof recording captured, but upload failed.",
1062
1062
  localPath: savedPath
1063
1063
  };
1064
- log3(result.proof.details);
1064
+ log4(result.proof.details);
1065
1065
  }
1066
1066
  } else {
1067
1067
  result.proof = {
@@ -1069,14 +1069,14 @@ async function runTest(options) {
1069
1069
  details: "Proof recording captured, but no upload handler was configured.",
1070
1070
  localPath: savedPath
1071
1071
  };
1072
- log3(result.proof.details);
1072
+ log4(result.proof.details);
1073
1073
  }
1074
1074
  } catch (err) {
1075
1075
  result.proof = {
1076
1076
  state: "proof_missing_capture_failed",
1077
1077
  details: `Proof recording could not be finalized: ${err.message}`
1078
1078
  };
1079
- log3(result.proof.details);
1079
+ log4(result.proof.details);
1080
1080
  }
1081
1081
  }
1082
1082
  const totalAssertions = result.assertions.length;
@@ -4939,7 +4939,7 @@ async function checkApiConnectivity() {
4939
4939
  }
4940
4940
  }
4941
4941
  function printResults(checks) {
4942
- const maxNameLen = Math.max(...checks.map((c13) => c13.name.length));
4942
+ const maxNameLen = Math.max(...checks.map((c14) => c14.name.length));
4943
4943
  let allOk = true;
4944
4944
  for (const check of checks) {
4945
4945
  const isOptional = check.optional ?? false;
@@ -4952,16 +4952,16 @@ function printResults(checks) {
4952
4952
  return allOk;
4953
4953
  }
4954
4954
  async function autoFix(checks, agent) {
4955
- const { spawn: spawn9 } = await import("child_process");
4956
- const ghInstalled = checks.find((c13) => c13.name === "GitHub CLI (gh)").ok;
4957
- const ghAuthed = checks.find((c13) => c13.name === "GitHub CLI auth").ok;
4958
- const mrAuthed = checks.find((c13) => c13.name === "Mr. Manager CLI auth").ok;
4959
- const claudeCheck = checks.find((c13) => c13.name === "Claude Code (claude)");
4955
+ const { spawn: spawn10 } = await import("child_process");
4956
+ const ghInstalled = checks.find((c14) => c14.name === "GitHub CLI (gh)").ok;
4957
+ const ghAuthed = checks.find((c14) => c14.name === "GitHub CLI auth").ok;
4958
+ const mrAuthed = checks.find((c14) => c14.name === "Mr. Manager CLI auth").ok;
4959
+ const claudeCheck = checks.find((c14) => c14.name === "Claude Code (claude)");
4960
4960
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
4961
4961
  console.log(paint5("cyan", " Installing Claude Code..."));
4962
4962
  console.log(paint5("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
4963
4963
  await new Promise((resolve9) => {
4964
- const child = spawn9("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
4964
+ const child = spawn10("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
4965
4965
  child.on("exit", () => resolve9());
4966
4966
  });
4967
4967
  console.log("");
@@ -4969,7 +4969,7 @@ async function autoFix(checks, agent) {
4969
4969
  if (ghInstalled && !ghAuthed) {
4970
4970
  console.log(paint5("cyan", " Running gh auth login..."));
4971
4971
  await new Promise((resolve9) => {
4972
- const child = spawn9("gh", ["auth", "login"], { stdio: "inherit" });
4972
+ const child = spawn10("gh", ["auth", "login"], { stdio: "inherit" });
4973
4973
  child.on("exit", () => resolve9());
4974
4974
  });
4975
4975
  console.log("");
@@ -4978,7 +4978,7 @@ async function autoFix(checks, agent) {
4978
4978
  console.log(paint5("cyan", " Running mr login..."));
4979
4979
  const entry = process.argv[1];
4980
4980
  await new Promise((resolve9) => {
4981
- const child = spawn9(process.execPath, [entry, "login"], { stdio: "inherit" });
4981
+ const child = spawn10(process.execPath, [entry, "login"], { stdio: "inherit" });
4982
4982
  child.on("exit", () => resolve9());
4983
4983
  });
4984
4984
  console.log("");
@@ -5019,7 +5019,7 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
5019
5019
  console.log("");
5020
5020
  return;
5021
5021
  }
5022
- const fixes = checks.filter((c13) => !c13.ok && c13.fix && !c13.optional);
5022
+ const fixes = checks.filter((c14) => !c14.ok && c14.fix && !c14.optional);
5023
5023
  if (fixes.length > 0) {
5024
5024
  console.log(paint5("yellow", " To fix:"));
5025
5025
  for (const fix of fixes) {
@@ -5763,20 +5763,24 @@ var noMrCommand = new Command24("no-mr").description("Signal that a task does no
5763
5763
  });
5764
5764
 
5765
5765
  // cli/commands/review.ts
5766
+ import { Command as Command26 } from "commander";
5767
+ import { spawn as spawn8, execSync as execSync6 } from "child_process";
5768
+ import { existsSync as existsSync14, statSync as statSync2 } from "fs";
5769
+
5770
+ // cli/commands/review-apply.ts
5766
5771
  import { Command as Command25 } from "commander";
5767
5772
  import { spawn as spawn7, execSync as execSync5 } from "child_process";
5768
- import { existsSync as existsSync13, statSync as statSync2 } from "fs";
5773
+ import { existsSync as existsSync13 } from "fs";
5769
5774
  var c8 = {
5770
5775
  reset: "\x1B[0m",
5771
- bold: "\x1B[1m",
5772
5776
  dim: "\x1B[2m",
5773
5777
  cyan: "\x1B[36m",
5774
5778
  green: "\x1B[32m",
5775
5779
  yellow: "\x1B[33m",
5776
5780
  red: "\x1B[31m",
5777
- magenta: "\x1B[35m",
5781
+ blue: "\x1B[34m",
5778
5782
  gray: "\x1B[90m",
5779
- blue: "\x1B[34m"
5783
+ magenta: "\x1B[35m"
5780
5784
  };
5781
5785
  function paint8(color, text) {
5782
5786
  return `${c8[color]}${text}${c8.reset}`;
@@ -5785,7 +5789,7 @@ function timestamp2() {
5785
5789
  return paint8("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
5786
5790
  }
5787
5791
  function tag() {
5788
- return paint8("blue", "[review]");
5792
+ return paint8("blue", "[review apply]");
5789
5793
  }
5790
5794
  function log(msg) {
5791
5795
  console.log(`${timestamp2()} ${tag()} ${msg}`);
@@ -5796,32 +5800,270 @@ function logOk(msg) {
5796
5800
  function logErr(msg) {
5797
5801
  console.error(`${timestamp2()} ${tag()} ${paint8("red", "\u2717")} ${msg}`);
5798
5802
  }
5799
- var reviewCommand = new Command25("review").description("Run an automated code review on a branch").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing review report ID (created by UI trigger)").option("--branch <name>", "Branch to review (defaults to current branch)").option("--base <name>", "Base branch to diff against (defaults to main)").option("--pr-url <url>", "Pull/merge request URL; when set, diff is fetched via gh/glab and no local checkout is needed").option("--pr-number <n>", "Pull/merge request number (used with --pr-url)").action(async (opts) => {
5803
+ var reviewApplyCommand = new Command25("apply").description("Apply review comments and findings using the Claude agent").argument("<id>", "Code review ID (the one shown in the Reviews UI)").option("--no-push", "Skip pushing the fix commit to the remote").option("--no-commit", "Apply changes but don't commit (for dry-run review)").action(async (id, opts) => {
5800
5804
  const config = loadConfig();
5801
5805
  if (!config.apiKey) {
5802
5806
  logErr('Not authenticated. Run "mr login" first.');
5803
5807
  process.exit(1);
5804
5808
  }
5809
+ let review;
5810
+ try {
5811
+ review = await api.get(`/api/reviews/${id}`);
5812
+ } catch (err) {
5813
+ logErr(`Could not load review ${id}: ${err.message}`);
5814
+ process.exit(1);
5815
+ }
5816
+ const comments = review.comments ?? [];
5817
+ const findings = review.findings ?? [];
5818
+ const excluded = new Set(review.excludedFiles ?? []);
5819
+ if (comments.length === 0 && findings.length === 0) {
5820
+ logErr("Review has no comments or findings to act on.");
5821
+ process.exit(1);
5822
+ }
5823
+ const actionableComments = comments.filter((c14) => !excluded.has(c14.file));
5824
+ const actionableFindings = findings.filter((f) => !excluded.has(f.file));
5825
+ if (actionableComments.length === 0 && actionableFindings.length === 0) {
5826
+ logErr("All files with comments/findings are excluded \u2014 nothing to do.");
5827
+ process.exit(1);
5828
+ }
5829
+ let project;
5830
+ try {
5831
+ project = await api.get(`/api/projects/${review.projectId}`);
5832
+ } catch {
5833
+ logErr(`Could not load project ${review.projectId}`);
5834
+ process.exit(1);
5835
+ }
5836
+ let projectPath = project.localPath;
5837
+ if (!projectPath) {
5838
+ for (const [dir, pid] of Object.entries(config.directories)) {
5839
+ if (pid === review.projectId) {
5840
+ projectPath = dir;
5841
+ break;
5842
+ }
5843
+ }
5844
+ }
5845
+ if (!projectPath) projectPath = process.cwd();
5846
+ if (!existsSync13(projectPath) || !existsSync13(`${projectPath}/.git`)) {
5847
+ logErr(`Project path not a git checkout: ${projectPath}`);
5848
+ logErr(`Set the project's localPath or run from inside the repo.`);
5849
+ process.exit(1);
5850
+ }
5851
+ try {
5852
+ execSync5(`git fetch origin ${review.branch}`, { cwd: projectPath, stdio: "ignore" });
5853
+ } catch {
5854
+ }
5855
+ try {
5856
+ execSync5(`git checkout ${review.branch}`, { cwd: projectPath, stdio: "pipe" });
5857
+ } catch (err) {
5858
+ logErr(`Could not checkout branch ${review.branch}: ${err.message}`);
5859
+ process.exit(1);
5860
+ }
5861
+ log(`Project: ${paint8("cyan", project.name)}`);
5862
+ log(`Branch: ${paint8("cyan", review.branch)}`);
5863
+ log(`Comments: ${paint8("yellow", String(actionableComments.length))}, findings: ${paint8("yellow", String(actionableFindings.length))}`);
5864
+ if (excluded.size > 0) {
5865
+ log(`Excluded files: ${paint8("dim", [...excluded].join(", "))}`);
5866
+ }
5867
+ try {
5868
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "processing", fixErrorMessage: null });
5869
+ } catch {
5870
+ }
5871
+ const prompt2 = buildApplyPrompt({
5872
+ branch: review.branch,
5873
+ baseBranch: review.baseBranch,
5874
+ comments: actionableComments,
5875
+ findings: actionableFindings,
5876
+ excluded: [...excluded]
5877
+ });
5878
+ log("Asking Claude to apply the changes\u2026");
5879
+ try {
5880
+ await runClaudeInteractive(prompt2, projectPath);
5881
+ } catch (err) {
5882
+ const message = err.message || "Unknown error";
5883
+ logErr(`Agent fix failed: ${message}`);
5884
+ try {
5885
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "failed", fixErrorMessage: message });
5886
+ } catch {
5887
+ }
5888
+ process.exit(1);
5889
+ }
5890
+ const dirty = execSync5("git status --porcelain", { cwd: projectPath, encoding: "utf-8" }).trim();
5891
+ if (!dirty) {
5892
+ log(paint8("yellow", "Agent didn't change any files."));
5893
+ try {
5894
+ await api.patch(`/api/reviews/${id}`, {
5895
+ fixStatus: "completed",
5896
+ fixErrorMessage: "No file changes were made by the agent."
5897
+ });
5898
+ } catch {
5899
+ }
5900
+ return;
5901
+ }
5902
+ if (opts.commit === false) {
5903
+ logOk("Changes left unstaged for review (--no-commit).");
5904
+ try {
5905
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "completed" });
5906
+ } catch {
5907
+ }
5908
+ return;
5909
+ }
5910
+ const commitMessage = buildCommitMessage({
5911
+ commentsCount: actionableComments.length,
5912
+ findingsCount: actionableFindings.length
5913
+ });
5914
+ try {
5915
+ execSync5("git add -A", { cwd: projectPath });
5916
+ execSync5(`git commit -m ${JSON.stringify(commitMessage)}`, { cwd: projectPath, stdio: "pipe" });
5917
+ } catch (err) {
5918
+ const message = err.message || "Unknown error";
5919
+ logErr(`Commit failed: ${message}`);
5920
+ try {
5921
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "failed", fixErrorMessage: message });
5922
+ } catch {
5923
+ }
5924
+ process.exit(1);
5925
+ }
5926
+ const sha = execSync5("git rev-parse HEAD", { cwd: projectPath, encoding: "utf-8" }).trim();
5927
+ logOk(`Committed ${paint8("yellow", sha.slice(0, 10))}`);
5928
+ if (opts.push !== false) {
5929
+ try {
5930
+ execSync5(`git push origin ${review.branch}`, { cwd: projectPath, stdio: "pipe" });
5931
+ logOk(`Pushed to origin/${review.branch}`);
5932
+ } catch (err) {
5933
+ const message = err.message || "Unknown error";
5934
+ logErr(`Push failed: ${message}`);
5935
+ try {
5936
+ await api.patch(`/api/reviews/${id}`, {
5937
+ fixStatus: "failed",
5938
+ fixErrorMessage: `Committed ${sha.slice(0, 10)} but push failed: ${message}`,
5939
+ fixCommitSha: sha
5940
+ });
5941
+ } catch {
5942
+ }
5943
+ process.exit(1);
5944
+ }
5945
+ }
5946
+ try {
5947
+ await api.patch(`/api/reviews/${id}`, {
5948
+ fixStatus: "completed",
5949
+ fixCommitSha: sha,
5950
+ fixErrorMessage: null
5951
+ });
5952
+ } catch {
5953
+ }
5954
+ logOk("Done.");
5955
+ });
5956
+ function buildApplyPrompt(args) {
5957
+ const lines = [];
5958
+ lines.push(`You are a code review agent. The user has reviewed the diff on branch "${args.branch}" (against "${args.baseBranch}") and left the feedback below.`);
5959
+ lines.push("");
5960
+ lines.push("Make the requested changes by editing files in the current working directory. Be conservative \u2014 only change what the comments and findings call out.");
5961
+ lines.push("");
5962
+ if (args.excluded.length > 0) {
5963
+ lines.push("Do NOT modify these files (they were excluded by the reviewer):");
5964
+ for (const path of args.excluded) lines.push(` - ${path}`);
5965
+ lines.push("");
5966
+ }
5967
+ if (args.comments.length > 0) {
5968
+ lines.push("USER COMMENTS:");
5969
+ for (const c14 of args.comments) {
5970
+ const where = c14.line ? `${c14.file}:${c14.line}` : c14.file;
5971
+ lines.push(`- [${where}] ${c14.body}`);
5972
+ }
5973
+ lines.push("");
5974
+ }
5975
+ if (args.findings.length > 0) {
5976
+ lines.push("AUTOMATED FINDINGS TO ADDRESS:");
5977
+ for (const f of args.findings) {
5978
+ const where = f.line ? `${f.file}:${f.line}` : f.file;
5979
+ lines.push(`- [${f.severity}/${f.type}] [${where}] ${f.title}`);
5980
+ if (f.description) lines.push(` ${f.description}`);
5981
+ if (f.suggestion) lines.push(` Suggested: ${f.suggestion}`);
5982
+ }
5983
+ lines.push("");
5984
+ }
5985
+ lines.push("Make the edits now. Do not run the test suite. Stage no commits \u2014 just edit files. If a request can't be safely actioned, skip it and continue.");
5986
+ return lines.join("\n");
5987
+ }
5988
+ function buildCommitMessage(args) {
5989
+ const parts = [];
5990
+ if (args.commentsCount > 0) parts.push(`${args.commentsCount} review comment${args.commentsCount === 1 ? "" : "s"}`);
5991
+ if (args.findingsCount > 0) parts.push(`${args.findingsCount} finding${args.findingsCount === 1 ? "" : "s"}`);
5992
+ const summary = parts.length > 0 ? parts.join(" + ") : "review feedback";
5993
+ return `chore: apply ${summary}
5994
+
5995
+ Generated by mr review apply.`;
5996
+ }
5997
+ function runClaudeInteractive(prompt2, cwd) {
5998
+ return new Promise((resolve9, reject) => {
5999
+ const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6000
+ cwd,
6001
+ stdio: ["ignore", "inherit", "inherit"]
6002
+ });
6003
+ child.on("exit", (code) => {
6004
+ if (code === 0) resolve9();
6005
+ else reject(new Error(`claude exited with code ${code}`));
6006
+ });
6007
+ });
6008
+ }
6009
+
6010
+ // cli/commands/review.ts
6011
+ var c9 = {
6012
+ reset: "\x1B[0m",
6013
+ bold: "\x1B[1m",
6014
+ dim: "\x1B[2m",
6015
+ cyan: "\x1B[36m",
6016
+ green: "\x1B[32m",
6017
+ yellow: "\x1B[33m",
6018
+ red: "\x1B[31m",
6019
+ magenta: "\x1B[35m",
6020
+ gray: "\x1B[90m",
6021
+ blue: "\x1B[34m"
6022
+ };
6023
+ function paint9(color, text) {
6024
+ return `${c9[color]}${text}${c9.reset}`;
6025
+ }
6026
+ function timestamp3() {
6027
+ return paint9("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
6028
+ }
6029
+ function tag2() {
6030
+ return paint9("blue", "[review]");
6031
+ }
6032
+ function log2(msg) {
6033
+ console.log(`${timestamp3()} ${tag2()} ${msg}`);
6034
+ }
6035
+ function logOk2(msg) {
6036
+ console.log(`${timestamp3()} ${tag2()} ${paint9("green", "\u2713")} ${msg}`);
6037
+ }
6038
+ function logErr2(msg) {
6039
+ console.error(`${timestamp3()} ${tag2()} ${paint9("red", "\u2717")} ${msg}`);
6040
+ }
6041
+ var reviewCommand = new Command26("review").description("Run an automated code review on a branch").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing review report ID (created by UI trigger)").option("--branch <name>", "Branch to review (defaults to current branch)").option("--base <name>", "Base branch to diff against (defaults to main)").option("--pr-url <url>", "Pull/merge request URL; when set, diff is fetched via gh/glab and no local checkout is needed").option("--pr-number <n>", "Pull/merge request number (used with --pr-url)").addCommand(reviewApplyCommand).action(async (opts) => {
6042
+ const config = loadConfig();
6043
+ if (!config.apiKey) {
6044
+ logErr2('Not authenticated. Run "mr login" first.');
6045
+ process.exit(1);
6046
+ }
5805
6047
  const banner = [
5806
6048
  ``,
5807
- paint8("blue", ` \u2566\u2550\u2557\u2554\u2550\u2557\u2566 \u2566\u2566\u2554\u2550\u2557\u2566 \u2566`),
5808
- paint8("blue", ` \u2560\u2566\u255D\u2551\u2563 \u255A\u2557\u2554\u255D\u2551\u2551\u2563 \u2551\u2551\u2551`),
5809
- paint8("blue", ` \u2569\u255A\u2550\u255A\u2550\u255D \u255A\u255D \u2569\u255A\u2550\u255D\u255A\u2569\u255D`),
5810
- paint8("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
5811
- paint8("dim", ` automated code review`),
6049
+ paint9("blue", ` \u2566\u2550\u2557\u2554\u2550\u2557\u2566 \u2566\u2566\u2554\u2550\u2557\u2566 \u2566`),
6050
+ paint9("blue", ` \u2560\u2566\u255D\u2551\u2563 \u255A\u2557\u2554\u255D\u2551\u2551\u2563 \u2551\u2551\u2551`),
6051
+ paint9("blue", ` \u2569\u255A\u2550\u255A\u2550\u255D \u255A\u255D \u2569\u255A\u2550\u255D\u255A\u2569\u255D`),
6052
+ paint9("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
6053
+ paint9("dim", ` automated code review`),
5812
6054
  ``
5813
6055
  ].join("\n");
5814
6056
  console.log(banner);
5815
6057
  const projectId = opts.project || getLinkedProjectId();
5816
6058
  if (!projectId) {
5817
- logErr('No project linked. Run "mr link" or pass --project <id>.');
6059
+ logErr2('No project linked. Run "mr link" or pass --project <id>.');
5818
6060
  process.exit(1);
5819
6061
  }
5820
6062
  let project;
5821
6063
  try {
5822
6064
  project = await api.get(`/api/projects/${projectId}`);
5823
6065
  } catch {
5824
- logErr(`Failed to fetch project ${projectId}`);
6066
+ logErr2(`Failed to fetch project ${projectId}`);
5825
6067
  process.exit(1);
5826
6068
  }
5827
6069
  const baseBranch = opts.base || "main";
@@ -5835,19 +6077,19 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5835
6077
  if (canUseRemote && remote) {
5836
6078
  const num = prNumber ?? remote.number;
5837
6079
  if (!num) {
5838
- logErr(`Could not determine PR number from URL: ${prUrl}`);
6080
+ logErr2(`Could not determine PR number from URL: ${prUrl}`);
5839
6081
  process.exit(1);
5840
6082
  }
5841
- log(`Fetching diff from ${paint8("cyan", remote.host)} for ${paint8("yellow", `#${num}`)} in ${paint8("dim", `${remote.owner}/${remote.repo}`)}`);
6083
+ log2(`Fetching diff from ${paint9("cyan", remote.host)} for ${paint9("yellow", `#${num}`)} in ${paint9("dim", `${remote.owner}/${remote.repo}`)}`);
5842
6084
  try {
5843
6085
  diff = fetchRemoteDiff(remote, num);
5844
6086
  } catch (err) {
5845
- logErr(`Failed to fetch diff from ${remote.host}: ${err.message}`);
5846
- logErr(`Ensure ${remote.host === "github" ? "`gh`" : "`glab`"} is installed and authenticated.`);
6087
+ logErr2(`Failed to fetch diff from ${remote.host}: ${err.message}`);
6088
+ logErr2(`Ensure ${remote.host === "github" ? "`gh`" : "`glab`"} is installed and authenticated.`);
5847
6089
  process.exit(1);
5848
6090
  }
5849
6091
  if (!branch) branch = `pr-${num}`;
5850
- log(`Reviewing PR ${paint8("cyan", `#${num}`)} against ${paint8("dim", baseBranch)}`);
6092
+ log2(`Reviewing PR ${paint9("cyan", `#${num}`)} against ${paint9("dim", baseBranch)}`);
5851
6093
  } else {
5852
6094
  let projectPath = project.localPath;
5853
6095
  if (!projectPath) {
@@ -5861,42 +6103,42 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5861
6103
  if (!projectPath) {
5862
6104
  projectPath = process.cwd();
5863
6105
  }
5864
- if (!existsSync13(projectPath)) {
5865
- logErr(`Project path does not exist: ${projectPath}`);
5866
- logErr(`Update the project's localPath, attach a PR URL to the review, or run "mr link" from the correct directory.`);
6106
+ if (!existsSync14(projectPath)) {
6107
+ logErr2(`Project path does not exist: ${projectPath}`);
6108
+ logErr2(`Update the project's localPath, attach a PR URL to the review, or run "mr link" from the correct directory.`);
5867
6109
  process.exit(1);
5868
6110
  }
5869
6111
  try {
5870
6112
  if (!statSync2(projectPath).isDirectory()) {
5871
- logErr(`Project path is not a directory: ${projectPath}`);
6113
+ logErr2(`Project path is not a directory: ${projectPath}`);
5872
6114
  process.exit(1);
5873
6115
  }
5874
6116
  } catch (err) {
5875
- logErr(`Cannot stat project path ${projectPath}: ${err.message}`);
6117
+ logErr2(`Cannot stat project path ${projectPath}: ${err.message}`);
5876
6118
  process.exit(1);
5877
6119
  }
5878
- if (!existsSync13(`${projectPath}/.git`)) {
5879
- logErr(`Project path is not a git repository: ${projectPath}`);
5880
- logErr(`Update the project's localPath to point to the local checkout, or attach a PR URL to the review.`);
6120
+ if (!existsSync14(`${projectPath}/.git`)) {
6121
+ logErr2(`Project path is not a git repository: ${projectPath}`);
6122
+ logErr2(`Update the project's localPath to point to the local checkout, or attach a PR URL to the review.`);
5881
6123
  process.exit(1);
5882
6124
  }
5883
- log(`Using project path: ${paint8("dim", projectPath)}`);
6125
+ log2(`Using project path: ${paint9("dim", projectPath)}`);
5884
6126
  if (!branch) {
5885
6127
  try {
5886
- branch = execSync5("git rev-parse --abbrev-ref HEAD", {
6128
+ branch = execSync6("git rev-parse --abbrev-ref HEAD", {
5887
6129
  cwd: projectPath,
5888
6130
  encoding: "utf-8"
5889
6131
  }).trim();
5890
6132
  } catch {
5891
- logErr("Could not determine current branch. Pass --branch <name>.");
6133
+ logErr2("Could not determine current branch. Pass --branch <name>.");
5892
6134
  process.exit(1);
5893
6135
  }
5894
6136
  }
5895
- log(`Reviewing branch: ${paint8("cyan", branch)} against ${paint8("dim", baseBranch)}`);
6137
+ log2(`Reviewing branch: ${paint9("cyan", branch)} against ${paint9("dim", baseBranch)}`);
5896
6138
  const pathspec = `-- . ':!*.lock' ':!package-lock.json' ':!pnpm-lock.yaml'`;
5897
6139
  const tryDiff = (range) => {
5898
6140
  try {
5899
- return execSync5(`git diff ${range} ${pathspec}`, {
6141
+ return execSync6(`git diff ${range} ${pathspec}`, {
5900
6142
  cwd: projectPath,
5901
6143
  encoding: "utf-8",
5902
6144
  maxBuffer: 10 * 1024 * 1024,
@@ -5908,7 +6150,7 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5908
6150
  };
5909
6151
  const tryFetch = (ref) => {
5910
6152
  try {
5911
- execSync5(`git fetch origin ${ref}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
6153
+ execSync6(`git fetch origin ${ref}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
5912
6154
  } catch {
5913
6155
  }
5914
6156
  };
@@ -5919,24 +6161,24 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5919
6161
  result = tryDiff(`origin/${baseBranch}...${branch}`) ?? tryDiff(`origin/${baseBranch}...origin/${branch}`) ?? tryDiff(`${baseBranch}...origin/${branch}`);
5920
6162
  }
5921
6163
  if (result === null) {
5922
- logErr(`Failed to get diff between ${baseBranch} and ${branch} in ${projectPath}.`);
5923
- logErr(`Branch may not exist locally or on origin. Try fetching it manually, or attach a PR URL to the review.`);
6164
+ logErr2(`Failed to get diff between ${baseBranch} and ${branch} in ${projectPath}.`);
6165
+ logErr2(`Branch may not exist locally or on origin. Try fetching it manually, or attach a PR URL to the review.`);
5924
6166
  process.exit(1);
5925
6167
  }
5926
6168
  diff = result;
5927
6169
  }
5928
- log(`Project: ${paint8("cyan", project.name)}`);
6170
+ log2(`Project: ${paint9("cyan", project.name)}`);
5929
6171
  if (!diff) {
5930
- logOk("No changes found between branches. Nothing to review.");
6172
+ logOk2("No changes found between branches. Nothing to review.");
5931
6173
  process.exit(0);
5932
6174
  }
5933
6175
  diff = stripLockFileDiffs(diff);
5934
6176
  const filesChanged = (diff.match(/^diff --git/gm) ?? []).length;
5935
- log(`Diff size: ${paint8("yellow", `${diff.length.toLocaleString()} chars`)}, ${paint8("yellow", `${filesChanged} files`)}`);
6177
+ log2(`Diff size: ${paint9("yellow", `${diff.length.toLocaleString()} chars`)}, ${paint9("yellow", `${filesChanged} files`)}`);
5936
6178
  let reportId;
5937
6179
  if (opts.report) {
5938
6180
  reportId = opts.report;
5939
- log(`Using existing review report ${paint8("yellow", reportId.slice(0, 8))}`);
6181
+ log2(`Using existing review report ${paint9("yellow", reportId.slice(0, 8))}`);
5940
6182
  } else {
5941
6183
  try {
5942
6184
  const report = await api.post("/api/reviews", {
@@ -5945,14 +6187,17 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5945
6187
  baseBranch
5946
6188
  });
5947
6189
  reportId = report.id;
5948
- log(`Created review report ${paint8("yellow", reportId.slice(0, 8))}`);
6190
+ log2(`Created review report ${paint9("yellow", reportId.slice(0, 8))}`);
5949
6191
  } catch (err) {
5950
- logErr(`Failed to create review report: ${err.message}`);
6192
+ logErr2(`Failed to create review report: ${err.message}`);
5951
6193
  process.exit(1);
5952
6194
  }
5953
6195
  }
5954
6196
  try {
5955
- await api.patch(`/api/reviews/${reportId}`, { status: "processing" });
6197
+ await api.patch(`/api/reviews/${reportId}`, {
6198
+ status: "processing",
6199
+ diff
6200
+ });
5956
6201
  } catch {
5957
6202
  }
5958
6203
  const startTime = Date.now();
@@ -5960,10 +6205,10 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5960
6205
  let truncatedDiff = diff;
5961
6206
  if (diff.length > MAX_DIFF_CHARS) {
5962
6207
  truncatedDiff = diff.slice(0, MAX_DIFF_CHARS) + "\n\n... (diff truncated, review covers first " + MAX_DIFF_CHARS.toLocaleString() + " characters)";
5963
- log(paint8("yellow", `Diff truncated to ${MAX_DIFF_CHARS.toLocaleString()} chars for review`));
6208
+ log2(paint9("yellow", `Diff truncated to ${MAX_DIFF_CHARS.toLocaleString()} chars for review`));
5964
6209
  }
5965
6210
  try {
5966
- log("Running code review with Claude...");
6211
+ log2("Running code review with Claude...");
5967
6212
  const prompt2 = buildReviewPrompt(branch, baseBranch, truncatedDiff);
5968
6213
  const output = await runClaude(prompt2);
5969
6214
  const result = parseReviewOutput(output);
@@ -5975,7 +6220,7 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5975
6220
  } catch {
5976
6221
  }
5977
6222
  if (wasCancelled) {
5978
- log(paint8("yellow", "Review was cancelled \u2014 discarding results."));
6223
+ log2(paint9("yellow", "Review was cancelled \u2014 discarding results."));
5979
6224
  process.exit(0);
5980
6225
  }
5981
6226
  await api.patch(`/api/reviews/${reportId}`, {
@@ -5985,28 +6230,28 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5985
6230
  filesReviewed: filesChanged,
5986
6231
  reviewDurationMs: duration
5987
6232
  });
5988
- logOk(`Review completed in ${paint8("cyan", formatDuration(duration))}`);
5989
- logOk(`Found ${paint8("yellow", String(result.findings.length))} findings`);
6233
+ logOk2(`Review completed in ${paint9("cyan", formatDuration(duration))}`);
6234
+ logOk2(`Found ${paint9("yellow", String(result.findings.length))} findings`);
5990
6235
  if (result.findings.length > 0) {
5991
6236
  console.log("");
5992
6237
  const critical = result.findings.filter((f) => f.severity === "critical").length;
5993
6238
  const high = result.findings.filter((f) => f.severity === "high").length;
5994
6239
  const medium = result.findings.filter((f) => f.severity === "medium").length;
5995
6240
  const low = result.findings.filter((f) => f.severity === "low").length;
5996
- if (critical > 0) console.log(` ${paint8("red", "\u25CF")} ${critical} critical`);
5997
- if (high > 0) console.log(` ${paint8("red", "\u25CF")} ${high} high`);
5998
- if (medium > 0) console.log(` ${paint8("yellow", "\u25CF")} ${medium} medium`);
5999
- if (low > 0) console.log(` ${paint8("dim", "\u25CF")} ${low} low`);
6241
+ if (critical > 0) console.log(` ${paint9("red", "\u25CF")} ${critical} critical`);
6242
+ if (high > 0) console.log(` ${paint9("red", "\u25CF")} ${high} high`);
6243
+ if (medium > 0) console.log(` ${paint9("yellow", "\u25CF")} ${medium} medium`);
6244
+ if (low > 0) console.log(` ${paint9("dim", "\u25CF")} ${low} low`);
6000
6245
  console.log("");
6001
6246
  }
6002
6247
  if (result.summary) {
6003
- console.log(paint8("dim", " " + result.summary));
6248
+ console.log(paint9("dim", " " + result.summary));
6004
6249
  console.log("");
6005
6250
  }
6006
6251
  } catch (err) {
6007
6252
  const duration = Date.now() - startTime;
6008
6253
  const errorMessage = err.message || "Unknown error";
6009
- logErr(`Review failed: ${errorMessage}`);
6254
+ logErr2(`Review failed: ${errorMessage}`);
6010
6255
  try {
6011
6256
  await api.patch(`/api/reviews/${reportId}`, {
6012
6257
  status: "failed",
@@ -6045,13 +6290,13 @@ function parsePrUrl(url) {
6045
6290
  }
6046
6291
  function fetchRemoteDiff(remote, number) {
6047
6292
  if (remote.host === "github") {
6048
- return execSync5(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6293
+ return execSync6(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6049
6294
  encoding: "utf-8",
6050
6295
  maxBuffer: 20 * 1024 * 1024
6051
6296
  }).trim();
6052
6297
  }
6053
6298
  const project = `${remote.owner}/${remote.repo}`;
6054
- return execSync5(`glab mr diff ${number} --repo ${project} --raw`, {
6299
+ return execSync6(`glab mr diff ${number} --repo ${project} --raw`, {
6055
6300
  encoding: "utf-8",
6056
6301
  maxBuffer: 20 * 1024 * 1024
6057
6302
  }).trim();
@@ -6122,7 +6367,7 @@ ${diff}
6122
6367
  }
6123
6368
  function runClaude(prompt2) {
6124
6369
  return new Promise((resolve9, reject) => {
6125
- const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6370
+ const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6126
6371
  stdio: ["ignore", "pipe", "pipe"]
6127
6372
  });
6128
6373
  let output = "";
@@ -6174,13 +6419,13 @@ function parseReviewOutput(output) {
6174
6419
  }
6175
6420
 
6176
6421
  // cli/commands/scan.ts
6177
- import { Command as Command26 } from "commander";
6422
+ import { Command as Command27 } from "commander";
6178
6423
 
6179
6424
  // lib/scanner/index.ts
6180
- import { spawn as spawn8 } from "child_process";
6425
+ import { spawn as spawn9 } from "child_process";
6181
6426
 
6182
6427
  // lib/scanner/config.ts
6183
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
6428
+ import { readFileSync as readFileSync10, existsSync as existsSync15 } from "fs";
6184
6429
  import { join as join9 } from "path";
6185
6430
  var ALL_FINDING_TYPES = [
6186
6431
  "idea",
@@ -6198,7 +6443,7 @@ var DEFAULTS = {
6198
6443
  };
6199
6444
  function loadScanConfig(projectPath) {
6200
6445
  const configPath2 = join9(projectPath, ".mr-scan.json");
6201
- if (!existsSync14(configPath2)) {
6446
+ if (!existsSync15(configPath2)) {
6202
6447
  return { ...DEFAULTS };
6203
6448
  }
6204
6449
  try {
@@ -6244,13 +6489,13 @@ async function authenticateBrowseSession(magicUrl, runBrowse) {
6244
6489
  }
6245
6490
 
6246
6491
  // lib/scanner/codebase-analysis.ts
6247
- import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync15 } from "fs";
6492
+ import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync16 } from "fs";
6248
6493
  import { join as join10, relative } from "path";
6249
- import { execSync as execSync6 } from "child_process";
6494
+ import { execSync as execSync7 } from "child_process";
6250
6495
  function resolveDir(projectPath, candidates) {
6251
6496
  for (const candidate of candidates) {
6252
6497
  const dir = join10(projectPath, candidate);
6253
- if (existsSync15(dir)) return dir;
6498
+ if (existsSync16(dir)) return dir;
6254
6499
  }
6255
6500
  return null;
6256
6501
  }
@@ -6284,7 +6529,7 @@ function discoverRoutes(projectPath) {
6284
6529
  }
6285
6530
  function extractModels(projectPath) {
6286
6531
  const schemaPath = join10(projectPath, "prisma", "schema.prisma");
6287
- if (existsSync15(schemaPath)) {
6532
+ if (existsSync16(schemaPath)) {
6288
6533
  const content = readFileSync11(schemaPath, "utf-8");
6289
6534
  const models2 = [];
6290
6535
  const modelRegex = /^model\s+(\w+)\s*\{/gm;
@@ -6298,7 +6543,7 @@ function extractModels(projectPath) {
6298
6543
  const drizzleDirs = ["src/db", "src/schema", "db", "drizzle"];
6299
6544
  for (const dir of drizzleDirs) {
6300
6545
  const fullDir = join10(projectPath, dir);
6301
- if (!existsSync15(fullDir)) continue;
6546
+ if (!existsSync16(fullDir)) continue;
6302
6547
  try {
6303
6548
  const entries = readdirSync2(fullDir, { withFileTypes: true });
6304
6549
  for (const entry of entries) {
@@ -6339,7 +6584,7 @@ function discoverComponents(projectPath) {
6339
6584
  function extractInternalLinks(projectPath) {
6340
6585
  const links = /* @__PURE__ */ new Set();
6341
6586
  function searchDir(dir) {
6342
- if (!existsSync15(dir)) return;
6587
+ if (!existsSync16(dir)) return;
6343
6588
  const entries = readdirSync2(dir, { withFileTypes: true });
6344
6589
  for (const entry of entries) {
6345
6590
  if (entry.isDirectory()) {
@@ -6372,7 +6617,7 @@ function extractInternalLinks(projectPath) {
6372
6617
  }
6373
6618
  function getRecentCommits(projectPath, count = 20) {
6374
6619
  try {
6375
- const output = execSync6(
6620
+ const output = execSync7(
6376
6621
  `git log --oneline -${count} --no-decorate`,
6377
6622
  { cwd: projectPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6378
6623
  );
@@ -6640,10 +6885,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
6640
6885
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
6641
6886
 
6642
6887
  **Components:**
6643
- ${codebaseAnalysis.components.slice(0, 15).map((c13) => `- ${c13}`).join("\n")}
6888
+ ${codebaseAnalysis.components.slice(0, 15).map((c14) => `- ${c14}`).join("\n")}
6644
6889
 
6645
6890
  **Recent Git Commits:**
6646
- ${codebaseAnalysis.recentCommits.slice(0, 8).map((c13) => `- ${c13}`).join("\n")}
6891
+ ${codebaseAnalysis.recentCommits.slice(0, 8).map((c14) => `- ${c14}`).join("\n")}
6647
6892
 
6648
6893
  **Completed Tasks:**
6649
6894
  ${context.completedTasks.slice(0, 10).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -6858,7 +7103,7 @@ async function fetchScanContext(opts) {
6858
7103
  }
6859
7104
  function runClaude2(prompt2) {
6860
7105
  return new Promise((resolve9, reject) => {
6861
- const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
7106
+ const child = spawn9("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6862
7107
  stdio: ["ignore", "pipe", "pipe"]
6863
7108
  });
6864
7109
  let output = "";
@@ -6914,7 +7159,7 @@ function parseSynthesisOutput(output) {
6914
7159
  }
6915
7160
 
6916
7161
  // cli/commands/scan.ts
6917
- var c9 = {
7162
+ var c10 = {
6918
7163
  reset: "\x1B[0m",
6919
7164
  bold: "\x1B[1m",
6920
7165
  dim: "\x1B[2m",
@@ -6925,53 +7170,53 @@ var c9 = {
6925
7170
  magenta: "\x1B[35m",
6926
7171
  gray: "\x1B[90m"
6927
7172
  };
6928
- function paint9(color, text) {
6929
- return `${c9[color]}${text}${c9.reset}`;
7173
+ function paint10(color, text) {
7174
+ return `${c10[color]}${text}${c10.reset}`;
6930
7175
  }
6931
- function timestamp3() {
6932
- return paint9("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
7176
+ function timestamp4() {
7177
+ return paint10("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
6933
7178
  }
6934
7179
  function scanTag() {
6935
- return paint9("magenta", "[scan]");
7180
+ return paint10("magenta", "[scan]");
6936
7181
  }
6937
- function log2(msg) {
6938
- console.log(`${timestamp3()} ${scanTag()} ${msg}`);
7182
+ function log3(msg) {
7183
+ console.log(`${timestamp4()} ${scanTag()} ${msg}`);
6939
7184
  }
6940
- function logOk2(msg) {
6941
- console.log(`${timestamp3()} ${scanTag()} ${paint9("green", "\u2713")} ${msg}`);
7185
+ function logOk3(msg) {
7186
+ console.log(`${timestamp4()} ${scanTag()} ${paint10("green", "\u2713")} ${msg}`);
6942
7187
  }
6943
- function logErr2(msg) {
6944
- console.error(`${timestamp3()} ${scanTag()} ${paint9("red", "\u2717")} ${msg}`);
7188
+ function logErr3(msg) {
7189
+ console.error(`${timestamp4()} ${scanTag()} ${paint10("red", "\u2717")} ${msg}`);
6945
7190
  }
6946
- var scanCommand = new Command26("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("--prompt <prompt>", "Custom scan direction/prompt to focus the scan on").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
7191
+ var scanCommand = new Command27("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("--prompt <prompt>", "Custom scan direction/prompt to focus the scan on").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
6947
7192
  const config = loadConfig();
6948
7193
  if (!config.apiKey) {
6949
- logErr2('Not authenticated. Run "mr login" first.');
7194
+ logErr3('Not authenticated. Run "mr login" first.');
6950
7195
  process.exit(1);
6951
7196
  }
6952
7197
  const banner = [
6953
7198
  ``,
6954
- paint9("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
6955
- paint9("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
6956
- paint9("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
6957
- paint9("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
6958
- paint9("dim", ` autonomous product scanner`),
7199
+ paint10("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
7200
+ paint10("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
7201
+ paint10("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
7202
+ paint10("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
7203
+ paint10("dim", ` autonomous product scanner`),
6959
7204
  ``
6960
7205
  ].join("\n");
6961
7206
  console.log(banner);
6962
7207
  const projectId = opts.project || getLinkedProjectId();
6963
7208
  if (!projectId) {
6964
- logErr2('No project linked. Run "mr link" or pass --project <id>.');
7209
+ logErr3('No project linked. Run "mr link" or pass --project <id>.');
6965
7210
  process.exit(1);
6966
7211
  }
6967
7212
  let project;
6968
7213
  try {
6969
7214
  project = await api.get(`/api/projects/${projectId}`);
6970
7215
  } catch {
6971
- logErr2(`Failed to fetch project ${projectId}`);
7216
+ logErr3(`Failed to fetch project ${projectId}`);
6972
7217
  process.exit(1);
6973
7218
  }
6974
- log2(`Scanning project: ${paint9("cyan", project.name)}`);
7219
+ log3(`Scanning project: ${paint10("cyan", project.name)}`);
6975
7220
  let projectPath = project.localPath;
6976
7221
  if (!projectPath) {
6977
7222
  for (const [dir, pid] of Object.entries(config.directories)) {
@@ -6988,7 +7233,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
6988
7233
  let customPrompt = typeof opts.prompt === "string" && opts.prompt.trim().length > 0 ? opts.prompt.trim() : null;
6989
7234
  if (opts.report) {
6990
7235
  reportId = opts.report;
6991
- log2(`Using existing scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7236
+ log3(`Using existing scan report ${paint10("yellow", reportId.slice(0, 8))}`);
6992
7237
  try {
6993
7238
  const existing = await api.get(`/api/scans/${reportId}`);
6994
7239
  if (!customPrompt && existing.customPrompt) {
@@ -7000,7 +7245,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7000
7245
  try {
7001
7246
  const scans = await api.get(`/api/scans?projectId=${projectId}&status=processing`);
7002
7247
  if (scans.length > 0) {
7003
- logErr2("A scan is already in progress for this project. Wait for it to complete.");
7248
+ logErr3("A scan is already in progress for this project. Wait for it to complete.");
7004
7249
  process.exit(1);
7005
7250
  }
7006
7251
  } catch {
@@ -7012,9 +7257,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7012
7257
  customPrompt
7013
7258
  });
7014
7259
  reportId = report.id;
7015
- log2(`Created scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7260
+ log3(`Created scan report ${paint10("yellow", reportId.slice(0, 8))}`);
7016
7261
  } catch (err) {
7017
- logErr2(`Failed to create scan report: ${err.message}`);
7262
+ logErr3(`Failed to create scan report: ${err.message}`);
7018
7263
  process.exit(1);
7019
7264
  }
7020
7265
  }
@@ -7025,7 +7270,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7025
7270
  try {
7026
7271
  const current = await api.get(`/api/scans/${reportId}`);
7027
7272
  if (current.status === "cancelled") {
7028
- log2(paint9("yellow", "Scan was cancelled \u2014 aborting."));
7273
+ log3(paint10("yellow", "Scan was cancelled \u2014 aborting."));
7029
7274
  process.exit(0);
7030
7275
  }
7031
7276
  } catch {
@@ -7039,9 +7284,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7039
7284
  apiUrl: config.apiUrl,
7040
7285
  apiKey: config.apiKey,
7041
7286
  runBrowse: runBrowseCommand2,
7042
- onLog: log2,
7287
+ onLog: log3,
7043
7288
  onProgress: (phase, detail) => {
7044
- log2(`${paint9("dim", `[${phase}]`)} ${detail}`);
7289
+ log3(`${paint10("dim", `[${phase}]`)} ${detail}`);
7045
7290
  },
7046
7291
  customPrompt
7047
7292
  });
@@ -7054,7 +7299,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7054
7299
  } catch {
7055
7300
  }
7056
7301
  if (wasCancelled) {
7057
- log2(paint9("yellow", "Scan was cancelled by user \u2014 discarding results."));
7302
+ log3(paint10("yellow", "Scan was cancelled by user \u2014 discarding results."));
7058
7303
  process.exit(0);
7059
7304
  }
7060
7305
  await api.patch(`/api/scans/${reportId}`, {
@@ -7065,37 +7310,37 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7065
7310
  scanDurationMs: result.scanDurationMs,
7066
7311
  routesCrawled: result.routesCrawled
7067
7312
  });
7068
- logOk2(`Scan complete \u2014 ${paint9("cyan", String(result.findings.length))} findings`);
7313
+ logOk3(`Scan complete \u2014 ${paint10("cyan", String(result.findings.length))} findings`);
7069
7314
  console.log("");
7070
- console.log(` ${paint9("bold", "Summary:")} ${result.summary}`);
7315
+ console.log(` ${paint10("bold", "Summary:")} ${result.summary}`);
7071
7316
  console.log("");
7072
7317
  const high = result.findings.filter((f) => f.priority === "high");
7073
7318
  const medium = result.findings.filter((f) => f.priority === "medium");
7074
7319
  const low = result.findings.filter((f) => f.priority === "low");
7075
7320
  if (high.length > 0) {
7076
- console.log(` ${paint9("bold", paint9("red", `High Priority (${high.length})`))}`);
7321
+ console.log(` ${paint10("bold", paint10("red", `High Priority (${high.length})`))}`);
7077
7322
  for (const f of high) {
7078
- console.log(` ${paint9("red", "\u25CF")} [${f.type}] ${f.title}`);
7079
- console.log(` ${paint9("dim", f.description.slice(0, 120))}`);
7323
+ console.log(` ${paint10("red", "\u25CF")} [${f.type}] ${f.title}`);
7324
+ console.log(` ${paint10("dim", f.description.slice(0, 120))}`);
7080
7325
  }
7081
7326
  console.log("");
7082
7327
  }
7083
7328
  if (medium.length > 0) {
7084
- console.log(` ${paint9("bold", paint9("yellow", `Medium Priority (${medium.length})`))}`);
7329
+ console.log(` ${paint10("bold", paint10("yellow", `Medium Priority (${medium.length})`))}`);
7085
7330
  for (const f of medium) {
7086
- console.log(` ${paint9("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7331
+ console.log(` ${paint10("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7087
7332
  }
7088
7333
  console.log("");
7089
7334
  }
7090
7335
  if (low.length > 0) {
7091
- console.log(` ${paint9("dim", `Low Priority (${low.length})`)} `);
7336
+ console.log(` ${paint10("dim", `Low Priority (${low.length})`)} `);
7092
7337
  for (const f of low) {
7093
- console.log(` ${paint9("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7338
+ console.log(` ${paint10("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7094
7339
  }
7095
7340
  console.log("");
7096
7341
  }
7097
7342
  } catch (err) {
7098
- logErr2(`Scan failed: ${err.message}`);
7343
+ logErr3(`Scan failed: ${err.message}`);
7099
7344
  try {
7100
7345
  await api.patch(`/api/scans/${reportId}`, {
7101
7346
  status: "failed",
@@ -7108,13 +7353,13 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7108
7353
  });
7109
7354
 
7110
7355
  // cli/commands/doctor.ts
7111
- import { Command as Command27 } from "commander";
7112
- import { existsSync as existsSync16 } from "fs";
7356
+ import { Command as Command28 } from "commander";
7357
+ import { existsSync as existsSync17 } from "fs";
7113
7358
  import { homedir as homedir2 } from "os";
7114
7359
  import { join as join11 } from "path";
7115
7360
  async function checkConfigExists() {
7116
7361
  const configPath2 = join11(homedir2(), ".mr-manager", "config.json");
7117
- const exists = existsSync16(configPath2);
7362
+ const exists = existsSync17(configPath2);
7118
7363
  if (!exists) {
7119
7364
  return {
7120
7365
  name: "Config file",
@@ -7156,7 +7401,7 @@ async function checkProjectLink() {
7156
7401
  optional: true
7157
7402
  };
7158
7403
  }
7159
- var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
7404
+ var doctorCommand = new Command28("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
7160
7405
  const banner = [
7161
7406
  ``,
7162
7407
  paint5("cyan", ` MR DOCTOR`),
@@ -7187,7 +7432,7 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7187
7432
  console.log("");
7188
7433
  return;
7189
7434
  }
7190
- const fixes = checks.filter((c13) => !c13.ok && c13.fix && !c13.optional);
7435
+ const fixes = checks.filter((c14) => !c14.ok && c14.fix && !c14.optional);
7191
7436
  if (fixes.length > 0) {
7192
7437
  console.log(paint5("yellow", " To fix:"));
7193
7438
  for (const fix of fixes) {
@@ -7199,14 +7444,14 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7199
7444
  });
7200
7445
 
7201
7446
  // cli/commands/prompt-audit.ts
7202
- import { Command as Command28 } from "commander";
7447
+ import { Command as Command29 } from "commander";
7203
7448
  import { resolve as resolve8 } from "path";
7204
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
7449
+ import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
7205
7450
  function auditLine(label, tokens) {
7206
7451
  const bar = "\u2588".repeat(Math.min(60, Math.round(tokens / 200)));
7207
7452
  return ` ${label.padEnd(30)} ${formatTokenCount(tokens).padStart(8)} ${bar}`;
7208
7453
  }
7209
- var promptAuditCommand = new Command28("prompt-audit").description("Dry-run prompt construction and report estimated token counts by job type").option("--task <id>", "Audit prompts for a specific task ID").option("--all", "Audit all supported job types with representative data", false).option("--json", "Output as JSON instead of plain text", false).action(async (opts) => {
7454
+ var promptAuditCommand = new Command29("prompt-audit").description("Dry-run prompt construction and report estimated token counts by job type").option("--task <id>", "Audit prompts for a specific task ID").option("--all", "Audit all supported job types with representative data", false).option("--json", "Output as JSON instead of plain text", false).action(async (opts) => {
7210
7455
  const results = [];
7211
7456
  if (opts.task) {
7212
7457
  try {
@@ -7260,7 +7505,7 @@ ${task.notes}` : "";
7260
7505
  const repoDir = Object.entries(config.directories).find(([, pid]) => pid === task.projectId)?.[0];
7261
7506
  if (repoDir) {
7262
7507
  const featuresPath = resolve8(repoDir, ".mr-features.md");
7263
- if (existsSync17(featuresPath)) {
7508
+ if (existsSync18(featuresPath)) {
7264
7509
  const featuresContent = readFileSync12(featuresPath, "utf-8");
7265
7510
  sections.push({ name: "features-doc", tokens: estimateTokens(featuresContent) });
7266
7511
  }
@@ -7415,9 +7660,9 @@ ${r.jobType} [${r.identifier}]`);
7415
7660
  });
7416
7661
 
7417
7662
  // cli/commands/skill.ts
7418
- import { Command as Command29 } from "commander";
7663
+ import { Command as Command30 } from "commander";
7419
7664
  import { createInterface as createInterface3 } from "readline/promises";
7420
- var c10 = {
7665
+ var c11 = {
7421
7666
  reset: "\x1B[0m",
7422
7667
  bold: "\x1B[1m",
7423
7668
  dim: "\x1B[2m",
@@ -7450,7 +7695,7 @@ function formatSize(bytes) {
7450
7695
  if (bytes < 1024) return `${bytes}b`;
7451
7696
  return `${(bytes / 1024).toFixed(1)}kb`;
7452
7697
  }
7453
- var skillCommand = new Command29("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
7698
+ var skillCommand = new Command30("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
7454
7699
  skillCommand.command("list").alias("ls").description("List all skills").option("--category <category>", "Filter by category").action(async (opts) => {
7455
7700
  const params = new URLSearchParams();
7456
7701
  if (opts.category) params.set("category", opts.category);
@@ -7458,17 +7703,17 @@ skillCommand.command("list").alias("ls").description("List all skills").option("
7458
7703
  `/api/skills${params.toString() ? `?${params}` : ""}`
7459
7704
  );
7460
7705
  if (skills.length === 0) {
7461
- console.log(`${c10.dim}No skills found.${c10.reset}`);
7706
+ console.log(`${c11.dim}No skills found.${c11.reset}`);
7462
7707
  return;
7463
7708
  }
7464
7709
  for (const skill of skills) {
7465
- const cat = skill.category ? ` ${c10.dim}[${skill.category}]${c10.reset}` : "";
7466
- const scope = skill.projectId ? ` ${c10.dim}(project)${c10.reset}` : ` ${c10.dim}(global)${c10.reset}`;
7467
- console.log(` ${c10.cyan}${skill.name}${c10.reset}${cat}${scope}`);
7710
+ const cat = skill.category ? ` ${c11.dim}[${skill.category}]${c11.reset}` : "";
7711
+ const scope = skill.projectId ? ` ${c11.dim}(project)${c11.reset}` : ` ${c11.dim}(global)${c11.reset}`;
7712
+ console.log(` ${c11.cyan}${skill.name}${c11.reset}${cat}${scope}`);
7468
7713
  if (skill.description) {
7469
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7714
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7470
7715
  }
7471
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7716
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7472
7717
  }
7473
7718
  });
7474
7719
  skillCommand.command("create").description("Create a new skill from a markdown file or inline content").argument("<name>", "Skill name").option("-d, --description <desc>", "Short description").option("-c, --category <cat>", "Category (e.g. Deployment, Testing)").option("-f, --file <path>", "Read content from a markdown file").option("--content <text>", "Inline markdown content").option("-p, --project", "Scope to the linked project").action(async (name, opts) => {
@@ -7504,7 +7749,7 @@ skillCommand.command("create").description("Create a new skill from a markdown f
7504
7749
  projectId
7505
7750
  });
7506
7751
  console.log(
7507
- `${c10.green}Created skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}`
7752
+ `${c11.green}Created skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}`
7508
7753
  );
7509
7754
  });
7510
7755
  skillCommand.command("update").description(
@@ -7565,14 +7810,14 @@ skillCommand.command("update").description(
7565
7810
  const beforeContent = existing.content ?? "";
7566
7811
  const afterContent = typeof patch.content === "string" ? patch.content : beforeContent;
7567
7812
  const contentChanged = typeof patch.content === "string" && patch.content !== beforeContent;
7568
- const sizeDiff = typeof patch.content === "string" ? ` ${c10.dim}(${formatSize(Buffer.byteLength(beforeContent, "utf-8"))} -> ${formatSize(Buffer.byteLength(afterContent, "utf-8"))})${c10.reset}` : "";
7813
+ const sizeDiff = typeof patch.content === "string" ? ` ${c11.dim}(${formatSize(Buffer.byteLength(beforeContent, "utf-8"))} -> ${formatSize(Buffer.byteLength(afterContent, "utf-8"))})${c11.reset}` : "";
7569
7814
  const priorRevisions = existing.revisions?.length ?? 0;
7570
7815
  const revisionsAfter = priorRevisions + (contentChanged ? 1 : 0);
7571
- const revisionNote = contentChanged ? ` ${c10.dim}kept ${revisionsAfter} prior revision${revisionsAfter === 1 ? "" : "s"}${c10.reset}` : "";
7816
+ const revisionNote = contentChanged ? ` ${c11.dim}kept ${revisionsAfter} prior revision${revisionsAfter === 1 ? "" : "s"}${c11.reset}` : "";
7572
7817
  console.log(
7573
- `${c10.green}Updated skill:${c10.reset} ${c10.bold}${updated.name}${c10.reset}${sizeDiff}${revisionNote}`
7818
+ `${c11.green}Updated skill:${c11.reset} ${c11.bold}${updated.name}${c11.reset}${sizeDiff}${revisionNote}`
7574
7819
  );
7575
- console.log(` ${c10.dim}id: ${updated.id}${c10.reset}`);
7820
+ console.log(` ${c11.dim}id: ${updated.id}${c11.reset}`);
7576
7821
  });
7577
7822
  skillCommand.command("delete").alias("rm").description("Delete a skill").argument("<idOrName>", "Skill id or name").option("--yes", "Skip confirmation prompt").action(async (idOrName, opts) => {
7578
7823
  let skill;
@@ -7585,7 +7830,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7585
7830
  if (!opts.yes) {
7586
7831
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7587
7832
  const answer = (await rl.question(
7588
- `Delete skill ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}? [y/N] `
7833
+ `Delete skill ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}? [y/N] `
7589
7834
  )).trim().toLowerCase();
7590
7835
  rl.close();
7591
7836
  if (answer !== "y" && answer !== "yes") {
@@ -7595,7 +7840,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7595
7840
  }
7596
7841
  await api.del(`/api/skills/${skill.id}`);
7597
7842
  console.log(
7598
- `${c10.yellow}Deleted skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}`
7843
+ `${c11.yellow}Deleted skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}`
7599
7844
  );
7600
7845
  });
7601
7846
  skillCommand.command("generate").alias("gen").description("Generate a new skill using AI from a text prompt").argument("<prompt>", "Describe the skill to generate").option("-p, --project", "Scope to the linked project").action(async (prompt2, opts) => {
@@ -7609,22 +7854,22 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7609
7854
  process.exit(1);
7610
7855
  }
7611
7856
  }
7612
- console.log(`${c10.dim}Generating skill...${c10.reset}`);
7857
+ console.log(`${c11.dim}Generating skill...${c11.reset}`);
7613
7858
  try {
7614
7859
  const skill = await api.post("/api/skills/generate", {
7615
7860
  prompt: prompt2,
7616
7861
  projectId
7617
7862
  });
7618
7863
  console.log(
7619
- `${c10.green}Generated skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset}`
7864
+ `${c11.green}Generated skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset}`
7620
7865
  );
7621
7866
  if (skill.description) {
7622
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7867
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7623
7868
  }
7624
7869
  if (skill.category) {
7625
- console.log(` ${c10.dim}Category: ${skill.category}${c10.reset}`);
7870
+ console.log(` ${c11.dim}Category: ${skill.category}${c11.reset}`);
7626
7871
  }
7627
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7872
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7628
7873
  } catch (err) {
7629
7874
  console.error(`Failed to generate skill: ${err.message}`);
7630
7875
  process.exit(1);
@@ -7632,8 +7877,8 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7632
7877
  });
7633
7878
 
7634
7879
  // cli/commands/resource.ts
7635
- import { Command as Command30 } from "commander";
7636
- var c11 = {
7880
+ import { Command as Command31 } from "commander";
7881
+ var c12 = {
7637
7882
  reset: "\x1B[0m",
7638
7883
  bold: "\x1B[1m",
7639
7884
  dim: "\x1B[2m",
@@ -7643,16 +7888,16 @@ var c11 = {
7643
7888
  magenta: "\x1B[35m"
7644
7889
  };
7645
7890
  var TYPE_COLORS = {
7646
- plan: c11.cyan,
7647
- research: c11.magenta,
7648
- "test-plan": c11.yellow,
7649
- note: c11.green
7891
+ plan: c12.cyan,
7892
+ research: c12.magenta,
7893
+ "test-plan": c12.yellow,
7894
+ note: c12.green
7650
7895
  };
7651
7896
  function typeLabel(type) {
7652
- const color = TYPE_COLORS[type] ?? c11.dim;
7653
- return `${color}${type}${c11.reset}`;
7897
+ const color = TYPE_COLORS[type] ?? c12.dim;
7898
+ return `${color}${type}${c12.reset}`;
7654
7899
  }
7655
- var resourceCommand = new Command30("resource").description("Manage resources \u2014 documents, plans, research, and notes");
7900
+ var resourceCommand = new Command31("resource").description("Manage resources \u2014 documents, plans, research, and notes");
7656
7901
  resourceCommand.command("list").alias("ls").description("List resources for the linked project (or all)").option("--all", "List all resources across projects").action(async (opts) => {
7657
7902
  const params = new URLSearchParams();
7658
7903
  if (opts.all) {
@@ -7665,13 +7910,13 @@ resourceCommand.command("list").alias("ls").description("List resources for the
7665
7910
  `/api/resources${params.toString() ? `?${params}` : ""}`
7666
7911
  );
7667
7912
  if (resources.length === 0) {
7668
- console.log(`${c11.dim}No resources found.${c11.reset}`);
7913
+ console.log(`${c12.dim}No resources found.${c12.reset}`);
7669
7914
  return;
7670
7915
  }
7671
7916
  for (const r of resources) {
7672
- const project = r.projectName ? ` ${c11.dim}[${r.projectName}]${c11.reset}` : "";
7673
- console.log(` ${typeLabel(r.type)} ${c11.bold}${r.title}${c11.reset}${project}`);
7674
- console.log(` ${c11.dim}id: ${r.id}${c11.reset}`);
7917
+ const project = r.projectName ? ` ${c12.dim}[${r.projectName}]${c12.reset}` : "";
7918
+ console.log(` ${typeLabel(r.type)} ${c12.bold}${r.title}${c12.reset}${project}`);
7919
+ console.log(` ${c12.dim}id: ${r.id}${c12.reset}`);
7675
7920
  }
7676
7921
  });
7677
7922
  resourceCommand.command("create").description("Create a new resource from a file or inline content").argument("<title>", "Resource title").option("-t, --type <type>", "Resource type (note, plan, research, test-plan)", "note").option("-f, --file <path>", "Read content from a file").option("--content <text>", "Inline content").option("--task <taskId>", "Attach to an existing task instead of creating a standalone resource").option("-p, --project", "Scope to the linked project").action(async (title, opts) => {
@@ -7696,9 +7941,9 @@ resourceCommand.command("create").description("Create a new resource from a file
7696
7941
  content: content.trim()
7697
7942
  });
7698
7943
  console.log(
7699
- `${c11.green}Created resource:${c11.reset} ${c11.bold}${title}${c11.reset} ${c11.dim}(${resource.id})${c11.reset}`
7944
+ `${c12.green}Created resource:${c12.reset} ${c12.bold}${title}${c12.reset} ${c12.dim}(${resource.id})${c12.reset}`
7700
7945
  );
7701
- console.log(` ${c11.dim}Attached to task ${opts.task}${c11.reset}`);
7946
+ console.log(` ${c12.dim}Attached to task ${opts.task}${c12.reset}`);
7702
7947
  return;
7703
7948
  }
7704
7949
  let projectId;
@@ -7718,7 +7963,7 @@ resourceCommand.command("create").description("Create a new resource from a file
7718
7963
  projectId
7719
7964
  });
7720
7965
  console.log(
7721
- `${c11.green}Created resource:${c11.reset} ${c11.bold}${title}${c11.reset} ${c11.dim}(${result.resource.id})${c11.reset}`
7966
+ `${c12.green}Created resource:${c12.reset} ${c12.bold}${title}${c12.reset} ${c12.dim}(${result.resource.id})${c12.reset}`
7722
7967
  );
7723
7968
  });
7724
7969
  resourceCommand.command("generate").alias("gen").description("Generate a resource using AI from a text prompt").argument("<prompt>", "Describe the resource to generate").option("-t, --type <type>", "Resource type (plan or research)", "research").option("-p, --project", "Scope to the linked project").action(async (prompt2, opts) => {
@@ -7737,30 +7982,30 @@ resourceCommand.command("generate").alias("gen").description("Generate a resourc
7737
7982
  process.exit(1);
7738
7983
  }
7739
7984
  }
7740
- console.log(`${c11.dim}Generating ${type}...${c11.reset}`);
7985
+ console.log(`${c12.dim}Generating ${type}...${c12.reset}`);
7741
7986
  const result = await api.post("/api/resources", {
7742
7987
  type,
7743
7988
  prompt: prompt2,
7744
7989
  projectId
7745
7990
  });
7746
7991
  console.log(
7747
- `${c11.green}Queued:${c11.reset} ${c11.bold}${result.task.title}${c11.reset}`
7992
+ `${c12.green}Queued:${c12.reset} ${c12.bold}${result.task.title}${c12.reset}`
7748
7993
  );
7749
- console.log(` ${c11.dim}task: ${result.task.id}${c11.reset}`);
7994
+ console.log(` ${c12.dim}task: ${result.task.id}${c12.reset}`);
7750
7995
  });
7751
7996
 
7752
7997
  // cli/commands/tests.ts
7753
- import { Command as Command31 } from "commander";
7754
- var c12 = {
7998
+ import { Command as Command32 } from "commander";
7999
+ var c13 = {
7755
8000
  reset: "\x1B[0m",
7756
8001
  dim: "\x1B[2m",
7757
8002
  yellow: "\x1B[33m"
7758
8003
  };
7759
- var testsCommand = new Command31("tests").description("List MR Test scenarios for the linked project").action(async () => {
8004
+ var testsCommand = new Command32("tests").description("List MR Test scenarios for the linked project").action(async () => {
7760
8005
  const projectId = getLinkedProjectId();
7761
8006
  if (!projectId) {
7762
8007
  console.error(
7763
- `${c12.yellow}No project linked to this directory.${c12.reset} Run "mr link <project-id>" first.`
8008
+ `${c13.yellow}No project linked to this directory.${c13.reset} Run "mr link <project-id>" first.`
7764
8009
  );
7765
8010
  process.exit(1);
7766
8011
  }
@@ -7773,13 +8018,13 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7773
8018
  process.exit(1);
7774
8019
  }
7775
8020
  if (scenarios.length === 0) {
7776
- console.log(`${c12.dim}No test scenarios found for this project.${c12.reset}`);
8021
+ console.log(`${c13.dim}No test scenarios found for this project.${c13.reset}`);
7777
8022
  return;
7778
8023
  }
7779
8024
  for (const scenario of scenarios) {
7780
8025
  console.log(`### ${scenario.name}`);
7781
8026
  if (scenario.description) {
7782
- console.log(`${c12.dim}${scenario.description}${c12.reset}`);
8027
+ console.log(`${c13.dim}${scenario.description}${c13.reset}`);
7783
8028
  console.log();
7784
8029
  }
7785
8030
  console.log(scenario.content);
@@ -7788,9 +8033,9 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7788
8033
  });
7789
8034
 
7790
8035
  // cli/commands/walkthrough.ts
7791
- import { Command as Command32 } from "commander";
8036
+ import { Command as Command33 } from "commander";
7792
8037
  var SKILL_NAME = "Generate Walkthrough Video";
7793
- var walkthroughCommand = new Command32("walkthrough").description("Attach the Generate Walkthrough Video skill to a task and queue it").argument("<task-id>", "Task ID to generate a walkthrough for").option("--prod", "Allow recording against production (requires explicit confirmation)").action(async (taskId, opts) => {
8038
+ var walkthroughCommand = new Command33("walkthrough").description("Attach the Generate Walkthrough Video skill to a task and queue it").argument("<task-id>", "Task ID to generate a walkthrough for").option("--prod", "Allow recording against production (requires explicit confirmation)").action(async (taskId, opts) => {
7794
8039
  const skills = await api.get("/api/skills");
7795
8040
  const skill = skills.find((s) => s.name === SKILL_NAME);
7796
8041
  if (!skill) {
@@ -7818,12 +8063,12 @@ var walkthroughCommand = new Command32("walkthrough").description("Attach the Ge
7818
8063
 
7819
8064
  // cli/index.ts
7820
8065
  var configPath = join12(homedir3(), ".mr-manager", "config.json");
7821
- var isFirstRun = !existsSync18(configPath);
8066
+ var isFirstRun = !existsSync19(configPath);
7822
8067
  var userArgs = process.argv.slice(2);
7823
8068
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
7824
8069
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
7825
8070
  if (isFirstRun && !shouldBypass) {
7826
- const c13 = {
8071
+ const c14 = {
7827
8072
  reset: "\x1B[0m",
7828
8073
  bold: "\x1B[1m",
7829
8074
  dim: "\x1B[2m",
@@ -7833,28 +8078,28 @@ if (isFirstRun && !shouldBypass) {
7833
8078
  magenta: "\x1B[35m"
7834
8079
  };
7835
8080
  console.log("");
7836
- console.log(`${c13.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${c13.reset}`);
7837
- console.log(`${c13.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${c13.reset}`);
7838
- console.log(`${c13.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c13.reset}`);
7839
- console.log(`${c13.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${c13.reset}`);
8081
+ console.log(`${c14.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${c14.reset}`);
8082
+ console.log(`${c14.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${c14.reset}`);
8083
+ console.log(`${c14.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c14.reset}`);
8084
+ console.log(`${c14.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${c14.reset}`);
7840
8085
  console.log("");
7841
- console.log(`${c13.bold} Welcome to Mr. Manager!${c13.reset}`);
7842
- console.log(`${c13.dim} Let's get you set up in a few quick steps.${c13.reset}`);
8086
+ console.log(`${c14.bold} Welcome to Mr. Manager!${c14.reset}`);
8087
+ console.log(`${c14.dim} Let's get you set up in a few quick steps.${c14.reset}`);
7843
8088
  console.log("");
7844
- console.log(` ${c13.yellow}Step 1:${c13.reset} Authenticate via Google OAuth`);
7845
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr login${c13.reset}`);
8089
+ console.log(` ${c14.yellow}Step 1:${c14.reset} Authenticate via Google OAuth`);
8090
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr login${c14.reset}`);
7846
8091
  console.log("");
7847
- console.log(` ${c13.yellow}Step 2:${c13.reset} Verify your environment`);
7848
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr setup${c13.reset}`);
8092
+ console.log(` ${c14.yellow}Step 2:${c14.reset} Verify your environment`);
8093
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr setup${c14.reset}`);
7849
8094
  console.log("");
7850
- console.log(` ${c13.yellow}Step 3:${c13.reset} Link a repo and start watching`);
7851
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr link${c13.reset} ${c13.dim}&&${c13.reset} ${c13.cyan}mr watch${c13.reset}`);
8095
+ console.log(` ${c14.yellow}Step 3:${c14.reset} Link a repo and start watching`);
8096
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr link${c14.reset} ${c14.dim}&&${c14.reset} ${c14.cyan}mr watch${c14.reset}`);
7852
8097
  console.log("");
7853
- console.log(`${c13.dim} Or run ${c13.reset}${c13.cyan}mr login${c13.reset}${c13.dim} to get started now.${c13.reset}`);
8098
+ console.log(`${c14.dim} Or run ${c14.reset}${c14.cyan}mr login${c14.reset}${c14.dim} to get started now.${c14.reset}`);
7854
8099
  console.log("");
7855
8100
  process.exit(0);
7856
8101
  }
7857
- var program = new Command33();
8102
+ var program = new Command34();
7858
8103
  program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
7859
8104
  program.addCommand(initCommand);
7860
8105
  program.addCommand(authCommand);