@dunnewold-labs/mr-manager 0.4.39 → 0.4.42

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 +493 -229
  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.39",
188
+ version: "0.4.42",
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;
@@ -3121,12 +3121,19 @@ var watchCommand = new Command9("watch").description(
3121
3121
  if (code === 0) {
3122
3122
  try {
3123
3123
  const noMrPath = resolve2(executionDir, ".mr-no-mr");
3124
- const noMrRequested = existsSync7(noMrPath);
3124
+ let noMrRequested = false;
3125
3125
  let noMrDescription;
3126
- if (noMrRequested) {
3127
- noMrDescription = readFileSync5(noMrPath, "utf-8").trim();
3128
- unlinkSync(noMrPath);
3129
- logSuccess(prefix, `No ${vcs === "gitlab" ? "MR" : "PR"} needed \u2014 ${noMrDescription}`);
3126
+ if (existsSync7(noMrPath)) {
3127
+ const noMrMtime = statSync(noMrPath).mtimeMs;
3128
+ if (noMrMtime >= activeEntry.startedAt) {
3129
+ noMrRequested = true;
3130
+ noMrDescription = readFileSync5(noMrPath, "utf-8").trim();
3131
+ unlinkSync(noMrPath);
3132
+ logSuccess(prefix, `No ${vcs === "gitlab" ? "MR" : "PR"} needed \u2014 ${noMrDescription}`);
3133
+ } else {
3134
+ unlinkSync(noMrPath);
3135
+ logWarn(prefix, `Ignoring stale .mr-no-mr left in ${executionDir} (written before this run)`);
3136
+ }
3130
3137
  }
3131
3138
  const prLabel = vcs === "gitlab" ? "MR" : "PR";
3132
3139
  let prUrl = null;
@@ -4939,7 +4946,7 @@ async function checkApiConnectivity() {
4939
4946
  }
4940
4947
  }
4941
4948
  function printResults(checks) {
4942
- const maxNameLen = Math.max(...checks.map((c13) => c13.name.length));
4949
+ const maxNameLen = Math.max(...checks.map((c14) => c14.name.length));
4943
4950
  let allOk = true;
4944
4951
  for (const check of checks) {
4945
4952
  const isOptional = check.optional ?? false;
@@ -4952,16 +4959,16 @@ function printResults(checks) {
4952
4959
  return allOk;
4953
4960
  }
4954
4961
  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)");
4962
+ const { spawn: spawn10 } = await import("child_process");
4963
+ const ghInstalled = checks.find((c14) => c14.name === "GitHub CLI (gh)").ok;
4964
+ const ghAuthed = checks.find((c14) => c14.name === "GitHub CLI auth").ok;
4965
+ const mrAuthed = checks.find((c14) => c14.name === "Mr. Manager CLI auth").ok;
4966
+ const claudeCheck = checks.find((c14) => c14.name === "Claude Code (claude)");
4960
4967
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
4961
4968
  console.log(paint5("cyan", " Installing Claude Code..."));
4962
4969
  console.log(paint5("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
4963
4970
  await new Promise((resolve9) => {
4964
- const child = spawn9("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
4971
+ const child = spawn10("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
4965
4972
  child.on("exit", () => resolve9());
4966
4973
  });
4967
4974
  console.log("");
@@ -4969,7 +4976,7 @@ async function autoFix(checks, agent) {
4969
4976
  if (ghInstalled && !ghAuthed) {
4970
4977
  console.log(paint5("cyan", " Running gh auth login..."));
4971
4978
  await new Promise((resolve9) => {
4972
- const child = spawn9("gh", ["auth", "login"], { stdio: "inherit" });
4979
+ const child = spawn10("gh", ["auth", "login"], { stdio: "inherit" });
4973
4980
  child.on("exit", () => resolve9());
4974
4981
  });
4975
4982
  console.log("");
@@ -4978,7 +4985,7 @@ async function autoFix(checks, agent) {
4978
4985
  console.log(paint5("cyan", " Running mr login..."));
4979
4986
  const entry = process.argv[1];
4980
4987
  await new Promise((resolve9) => {
4981
- const child = spawn9(process.execPath, [entry, "login"], { stdio: "inherit" });
4988
+ const child = spawn10(process.execPath, [entry, "login"], { stdio: "inherit" });
4982
4989
  child.on("exit", () => resolve9());
4983
4990
  });
4984
4991
  console.log("");
@@ -5019,7 +5026,7 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
5019
5026
  console.log("");
5020
5027
  return;
5021
5028
  }
5022
- const fixes = checks.filter((c13) => !c13.ok && c13.fix && !c13.optional);
5029
+ const fixes = checks.filter((c14) => !c14.ok && c14.fix && !c14.optional);
5023
5030
  if (fixes.length > 0) {
5024
5031
  console.log(paint5("yellow", " To fix:"));
5025
5032
  for (const fix of fixes) {
@@ -5763,20 +5770,24 @@ var noMrCommand = new Command24("no-mr").description("Signal that a task does no
5763
5770
  });
5764
5771
 
5765
5772
  // cli/commands/review.ts
5773
+ import { Command as Command26 } from "commander";
5774
+ import { spawn as spawn8, execSync as execSync6 } from "child_process";
5775
+ import { existsSync as existsSync14, statSync as statSync2 } from "fs";
5776
+
5777
+ // cli/commands/review-apply.ts
5766
5778
  import { Command as Command25 } from "commander";
5767
5779
  import { spawn as spawn7, execSync as execSync5 } from "child_process";
5768
- import { existsSync as existsSync13, statSync as statSync2 } from "fs";
5780
+ import { existsSync as existsSync13 } from "fs";
5769
5781
  var c8 = {
5770
5782
  reset: "\x1B[0m",
5771
- bold: "\x1B[1m",
5772
5783
  dim: "\x1B[2m",
5773
5784
  cyan: "\x1B[36m",
5774
5785
  green: "\x1B[32m",
5775
5786
  yellow: "\x1B[33m",
5776
5787
  red: "\x1B[31m",
5777
- magenta: "\x1B[35m",
5788
+ blue: "\x1B[34m",
5778
5789
  gray: "\x1B[90m",
5779
- blue: "\x1B[34m"
5790
+ magenta: "\x1B[35m"
5780
5791
  };
5781
5792
  function paint8(color, text) {
5782
5793
  return `${c8[color]}${text}${c8.reset}`;
@@ -5785,7 +5796,7 @@ function timestamp2() {
5785
5796
  return paint8("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
5786
5797
  }
5787
5798
  function tag() {
5788
- return paint8("blue", "[review]");
5799
+ return paint8("blue", "[review apply]");
5789
5800
  }
5790
5801
  function log(msg) {
5791
5802
  console.log(`${timestamp2()} ${tag()} ${msg}`);
@@ -5796,32 +5807,270 @@ function logOk(msg) {
5796
5807
  function logErr(msg) {
5797
5808
  console.error(`${timestamp2()} ${tag()} ${paint8("red", "\u2717")} ${msg}`);
5798
5809
  }
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) => {
5810
+ 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
5811
  const config = loadConfig();
5801
5812
  if (!config.apiKey) {
5802
5813
  logErr('Not authenticated. Run "mr login" first.');
5803
5814
  process.exit(1);
5804
5815
  }
5816
+ let review;
5817
+ try {
5818
+ review = await api.get(`/api/reviews/${id}`);
5819
+ } catch (err) {
5820
+ logErr(`Could not load review ${id}: ${err.message}`);
5821
+ process.exit(1);
5822
+ }
5823
+ const comments = review.comments ?? [];
5824
+ const findings = review.findings ?? [];
5825
+ const excluded = new Set(review.excludedFiles ?? []);
5826
+ if (comments.length === 0 && findings.length === 0) {
5827
+ logErr("Review has no comments or findings to act on.");
5828
+ process.exit(1);
5829
+ }
5830
+ const actionableComments = comments.filter((c14) => !excluded.has(c14.file));
5831
+ const actionableFindings = findings.filter((f) => !excluded.has(f.file));
5832
+ if (actionableComments.length === 0 && actionableFindings.length === 0) {
5833
+ logErr("All files with comments/findings are excluded \u2014 nothing to do.");
5834
+ process.exit(1);
5835
+ }
5836
+ let project;
5837
+ try {
5838
+ project = await api.get(`/api/projects/${review.projectId}`);
5839
+ } catch {
5840
+ logErr(`Could not load project ${review.projectId}`);
5841
+ process.exit(1);
5842
+ }
5843
+ let projectPath = project.localPath;
5844
+ if (!projectPath) {
5845
+ for (const [dir, pid] of Object.entries(config.directories)) {
5846
+ if (pid === review.projectId) {
5847
+ projectPath = dir;
5848
+ break;
5849
+ }
5850
+ }
5851
+ }
5852
+ if (!projectPath) projectPath = process.cwd();
5853
+ if (!existsSync13(projectPath) || !existsSync13(`${projectPath}/.git`)) {
5854
+ logErr(`Project path not a git checkout: ${projectPath}`);
5855
+ logErr(`Set the project's localPath or run from inside the repo.`);
5856
+ process.exit(1);
5857
+ }
5858
+ try {
5859
+ execSync5(`git fetch origin ${review.branch}`, { cwd: projectPath, stdio: "ignore" });
5860
+ } catch {
5861
+ }
5862
+ try {
5863
+ execSync5(`git checkout ${review.branch}`, { cwd: projectPath, stdio: "pipe" });
5864
+ } catch (err) {
5865
+ logErr(`Could not checkout branch ${review.branch}: ${err.message}`);
5866
+ process.exit(1);
5867
+ }
5868
+ log(`Project: ${paint8("cyan", project.name)}`);
5869
+ log(`Branch: ${paint8("cyan", review.branch)}`);
5870
+ log(`Comments: ${paint8("yellow", String(actionableComments.length))}, findings: ${paint8("yellow", String(actionableFindings.length))}`);
5871
+ if (excluded.size > 0) {
5872
+ log(`Excluded files: ${paint8("dim", [...excluded].join(", "))}`);
5873
+ }
5874
+ try {
5875
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "processing", fixErrorMessage: null });
5876
+ } catch {
5877
+ }
5878
+ const prompt2 = buildApplyPrompt({
5879
+ branch: review.branch,
5880
+ baseBranch: review.baseBranch,
5881
+ comments: actionableComments,
5882
+ findings: actionableFindings,
5883
+ excluded: [...excluded]
5884
+ });
5885
+ log("Asking Claude to apply the changes\u2026");
5886
+ try {
5887
+ await runClaudeInteractive(prompt2, projectPath);
5888
+ } catch (err) {
5889
+ const message = err.message || "Unknown error";
5890
+ logErr(`Agent fix failed: ${message}`);
5891
+ try {
5892
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "failed", fixErrorMessage: message });
5893
+ } catch {
5894
+ }
5895
+ process.exit(1);
5896
+ }
5897
+ const dirty = execSync5("git status --porcelain", { cwd: projectPath, encoding: "utf-8" }).trim();
5898
+ if (!dirty) {
5899
+ log(paint8("yellow", "Agent didn't change any files."));
5900
+ try {
5901
+ await api.patch(`/api/reviews/${id}`, {
5902
+ fixStatus: "completed",
5903
+ fixErrorMessage: "No file changes were made by the agent."
5904
+ });
5905
+ } catch {
5906
+ }
5907
+ return;
5908
+ }
5909
+ if (opts.commit === false) {
5910
+ logOk("Changes left unstaged for review (--no-commit).");
5911
+ try {
5912
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "completed" });
5913
+ } catch {
5914
+ }
5915
+ return;
5916
+ }
5917
+ const commitMessage = buildCommitMessage({
5918
+ commentsCount: actionableComments.length,
5919
+ findingsCount: actionableFindings.length
5920
+ });
5921
+ try {
5922
+ execSync5("git add -A", { cwd: projectPath });
5923
+ execSync5(`git commit -m ${JSON.stringify(commitMessage)}`, { cwd: projectPath, stdio: "pipe" });
5924
+ } catch (err) {
5925
+ const message = err.message || "Unknown error";
5926
+ logErr(`Commit failed: ${message}`);
5927
+ try {
5928
+ await api.patch(`/api/reviews/${id}`, { fixStatus: "failed", fixErrorMessage: message });
5929
+ } catch {
5930
+ }
5931
+ process.exit(1);
5932
+ }
5933
+ const sha = execSync5("git rev-parse HEAD", { cwd: projectPath, encoding: "utf-8" }).trim();
5934
+ logOk(`Committed ${paint8("yellow", sha.slice(0, 10))}`);
5935
+ if (opts.push !== false) {
5936
+ try {
5937
+ execSync5(`git push origin ${review.branch}`, { cwd: projectPath, stdio: "pipe" });
5938
+ logOk(`Pushed to origin/${review.branch}`);
5939
+ } catch (err) {
5940
+ const message = err.message || "Unknown error";
5941
+ logErr(`Push failed: ${message}`);
5942
+ try {
5943
+ await api.patch(`/api/reviews/${id}`, {
5944
+ fixStatus: "failed",
5945
+ fixErrorMessage: `Committed ${sha.slice(0, 10)} but push failed: ${message}`,
5946
+ fixCommitSha: sha
5947
+ });
5948
+ } catch {
5949
+ }
5950
+ process.exit(1);
5951
+ }
5952
+ }
5953
+ try {
5954
+ await api.patch(`/api/reviews/${id}`, {
5955
+ fixStatus: "completed",
5956
+ fixCommitSha: sha,
5957
+ fixErrorMessage: null
5958
+ });
5959
+ } catch {
5960
+ }
5961
+ logOk("Done.");
5962
+ });
5963
+ function buildApplyPrompt(args) {
5964
+ const lines = [];
5965
+ 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.`);
5966
+ lines.push("");
5967
+ 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.");
5968
+ lines.push("");
5969
+ if (args.excluded.length > 0) {
5970
+ lines.push("Do NOT modify these files (they were excluded by the reviewer):");
5971
+ for (const path of args.excluded) lines.push(` - ${path}`);
5972
+ lines.push("");
5973
+ }
5974
+ if (args.comments.length > 0) {
5975
+ lines.push("USER COMMENTS:");
5976
+ for (const c14 of args.comments) {
5977
+ const where = c14.line ? `${c14.file}:${c14.line}` : c14.file;
5978
+ lines.push(`- [${where}] ${c14.body}`);
5979
+ }
5980
+ lines.push("");
5981
+ }
5982
+ if (args.findings.length > 0) {
5983
+ lines.push("AUTOMATED FINDINGS TO ADDRESS:");
5984
+ for (const f of args.findings) {
5985
+ const where = f.line ? `${f.file}:${f.line}` : f.file;
5986
+ lines.push(`- [${f.severity}/${f.type}] [${where}] ${f.title}`);
5987
+ if (f.description) lines.push(` ${f.description}`);
5988
+ if (f.suggestion) lines.push(` Suggested: ${f.suggestion}`);
5989
+ }
5990
+ lines.push("");
5991
+ }
5992
+ 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.");
5993
+ return lines.join("\n");
5994
+ }
5995
+ function buildCommitMessage(args) {
5996
+ const parts = [];
5997
+ if (args.commentsCount > 0) parts.push(`${args.commentsCount} review comment${args.commentsCount === 1 ? "" : "s"}`);
5998
+ if (args.findingsCount > 0) parts.push(`${args.findingsCount} finding${args.findingsCount === 1 ? "" : "s"}`);
5999
+ const summary = parts.length > 0 ? parts.join(" + ") : "review feedback";
6000
+ return `chore: apply ${summary}
6001
+
6002
+ Generated by mr review apply.`;
6003
+ }
6004
+ function runClaudeInteractive(prompt2, cwd) {
6005
+ return new Promise((resolve9, reject) => {
6006
+ const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6007
+ cwd,
6008
+ stdio: ["ignore", "inherit", "inherit"]
6009
+ });
6010
+ child.on("exit", (code) => {
6011
+ if (code === 0) resolve9();
6012
+ else reject(new Error(`claude exited with code ${code}`));
6013
+ });
6014
+ });
6015
+ }
6016
+
6017
+ // cli/commands/review.ts
6018
+ var c9 = {
6019
+ reset: "\x1B[0m",
6020
+ bold: "\x1B[1m",
6021
+ dim: "\x1B[2m",
6022
+ cyan: "\x1B[36m",
6023
+ green: "\x1B[32m",
6024
+ yellow: "\x1B[33m",
6025
+ red: "\x1B[31m",
6026
+ magenta: "\x1B[35m",
6027
+ gray: "\x1B[90m",
6028
+ blue: "\x1B[34m"
6029
+ };
6030
+ function paint9(color, text) {
6031
+ return `${c9[color]}${text}${c9.reset}`;
6032
+ }
6033
+ function timestamp3() {
6034
+ return paint9("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
6035
+ }
6036
+ function tag2() {
6037
+ return paint9("blue", "[review]");
6038
+ }
6039
+ function log2(msg) {
6040
+ console.log(`${timestamp3()} ${tag2()} ${msg}`);
6041
+ }
6042
+ function logOk2(msg) {
6043
+ console.log(`${timestamp3()} ${tag2()} ${paint9("green", "\u2713")} ${msg}`);
6044
+ }
6045
+ function logErr2(msg) {
6046
+ console.error(`${timestamp3()} ${tag2()} ${paint9("red", "\u2717")} ${msg}`);
6047
+ }
6048
+ 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) => {
6049
+ const config = loadConfig();
6050
+ if (!config.apiKey) {
6051
+ logErr2('Not authenticated. Run "mr login" first.');
6052
+ process.exit(1);
6053
+ }
5805
6054
  const banner = [
5806
6055
  ``,
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`),
6056
+ paint9("blue", ` \u2566\u2550\u2557\u2554\u2550\u2557\u2566 \u2566\u2566\u2554\u2550\u2557\u2566 \u2566`),
6057
+ paint9("blue", ` \u2560\u2566\u255D\u2551\u2563 \u255A\u2557\u2554\u255D\u2551\u2551\u2563 \u2551\u2551\u2551`),
6058
+ paint9("blue", ` \u2569\u255A\u2550\u255A\u2550\u255D \u255A\u255D \u2569\u255A\u2550\u255D\u255A\u2569\u255D`),
6059
+ paint9("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
6060
+ paint9("dim", ` automated code review`),
5812
6061
  ``
5813
6062
  ].join("\n");
5814
6063
  console.log(banner);
5815
6064
  const projectId = opts.project || getLinkedProjectId();
5816
6065
  if (!projectId) {
5817
- logErr('No project linked. Run "mr link" or pass --project <id>.');
6066
+ logErr2('No project linked. Run "mr link" or pass --project <id>.');
5818
6067
  process.exit(1);
5819
6068
  }
5820
6069
  let project;
5821
6070
  try {
5822
6071
  project = await api.get(`/api/projects/${projectId}`);
5823
6072
  } catch {
5824
- logErr(`Failed to fetch project ${projectId}`);
6073
+ logErr2(`Failed to fetch project ${projectId}`);
5825
6074
  process.exit(1);
5826
6075
  }
5827
6076
  const baseBranch = opts.base || "main";
@@ -5835,19 +6084,19 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5835
6084
  if (canUseRemote && remote) {
5836
6085
  const num = prNumber ?? remote.number;
5837
6086
  if (!num) {
5838
- logErr(`Could not determine PR number from URL: ${prUrl}`);
6087
+ logErr2(`Could not determine PR number from URL: ${prUrl}`);
5839
6088
  process.exit(1);
5840
6089
  }
5841
- log(`Fetching diff from ${paint8("cyan", remote.host)} for ${paint8("yellow", `#${num}`)} in ${paint8("dim", `${remote.owner}/${remote.repo}`)}`);
6090
+ log2(`Fetching diff from ${paint9("cyan", remote.host)} for ${paint9("yellow", `#${num}`)} in ${paint9("dim", `${remote.owner}/${remote.repo}`)}`);
5842
6091
  try {
5843
6092
  diff = fetchRemoteDiff(remote, num);
5844
6093
  } 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.`);
6094
+ logErr2(`Failed to fetch diff from ${remote.host}: ${err.message}`);
6095
+ logErr2(`Ensure ${remote.host === "github" ? "`gh`" : "`glab`"} is installed and authenticated.`);
5847
6096
  process.exit(1);
5848
6097
  }
5849
6098
  if (!branch) branch = `pr-${num}`;
5850
- log(`Reviewing PR ${paint8("cyan", `#${num}`)} against ${paint8("dim", baseBranch)}`);
6099
+ log2(`Reviewing PR ${paint9("cyan", `#${num}`)} against ${paint9("dim", baseBranch)}`);
5851
6100
  } else {
5852
6101
  let projectPath = project.localPath;
5853
6102
  if (!projectPath) {
@@ -5861,70 +6110,82 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5861
6110
  if (!projectPath) {
5862
6111
  projectPath = process.cwd();
5863
6112
  }
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.`);
6113
+ if (!existsSync14(projectPath)) {
6114
+ logErr2(`Project path does not exist: ${projectPath}`);
6115
+ logErr2(`Update the project's localPath, attach a PR URL to the review, or run "mr link" from the correct directory.`);
5867
6116
  process.exit(1);
5868
6117
  }
5869
6118
  try {
5870
6119
  if (!statSync2(projectPath).isDirectory()) {
5871
- logErr(`Project path is not a directory: ${projectPath}`);
6120
+ logErr2(`Project path is not a directory: ${projectPath}`);
5872
6121
  process.exit(1);
5873
6122
  }
5874
6123
  } catch (err) {
5875
- logErr(`Cannot stat project path ${projectPath}: ${err.message}`);
6124
+ logErr2(`Cannot stat project path ${projectPath}: ${err.message}`);
5876
6125
  process.exit(1);
5877
6126
  }
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.`);
6127
+ if (!existsSync14(`${projectPath}/.git`)) {
6128
+ logErr2(`Project path is not a git repository: ${projectPath}`);
6129
+ logErr2(`Update the project's localPath to point to the local checkout, or attach a PR URL to the review.`);
5881
6130
  process.exit(1);
5882
6131
  }
5883
- log(`Using project path: ${paint8("dim", projectPath)}`);
6132
+ log2(`Using project path: ${paint9("dim", projectPath)}`);
5884
6133
  if (!branch) {
5885
6134
  try {
5886
- branch = execSync5("git rev-parse --abbrev-ref HEAD", {
6135
+ branch = execSync6("git rev-parse --abbrev-ref HEAD", {
5887
6136
  cwd: projectPath,
5888
6137
  encoding: "utf-8"
5889
6138
  }).trim();
5890
6139
  } catch {
5891
- logErr("Could not determine current branch. Pass --branch <name>.");
6140
+ logErr2("Could not determine current branch. Pass --branch <name>.");
5892
6141
  process.exit(1);
5893
6142
  }
5894
6143
  }
5895
- log(`Reviewing branch: ${paint8("cyan", branch)} against ${paint8("dim", baseBranch)}`);
5896
- try {
5897
- diff = execSync5(`git diff ${baseBranch}...${branch} -- . ':!*.lock' ':!package-lock.json' ':!pnpm-lock.yaml'`, {
5898
- cwd: projectPath,
5899
- encoding: "utf-8",
5900
- maxBuffer: 10 * 1024 * 1024
5901
- }).trim();
5902
- } catch (err) {
6144
+ log2(`Reviewing branch: ${paint9("cyan", branch)} against ${paint9("dim", baseBranch)}`);
6145
+ const pathspec = `-- . ':!*.lock' ':!package-lock.json' ':!pnpm-lock.yaml'`;
6146
+ const tryDiff = (range) => {
5903
6147
  try {
5904
- execSync5(`git fetch origin ${baseBranch}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
5905
- diff = execSync5(`git diff origin/${baseBranch}...${branch} -- . ':!*.lock' ':!package-lock.json' ':!pnpm-lock.yaml'`, {
6148
+ return execSync6(`git diff ${range} ${pathspec}`, {
5906
6149
  cwd: projectPath,
5907
6150
  encoding: "utf-8",
5908
- maxBuffer: 10 * 1024 * 1024
6151
+ maxBuffer: 10 * 1024 * 1024,
6152
+ stdio: ["ignore", "pipe", "pipe"]
5909
6153
  }).trim();
5910
6154
  } catch {
5911
- logErr(`Failed to get diff between ${baseBranch} and ${branch}: ${err.message}`);
5912
- process.exit(1);
6155
+ return null;
6156
+ }
6157
+ };
6158
+ const tryFetch = (ref) => {
6159
+ try {
6160
+ execSync6(`git fetch origin ${ref}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
6161
+ } catch {
5913
6162
  }
6163
+ };
6164
+ let result = tryDiff(`${baseBranch}...${branch}`);
6165
+ if (result === null) {
6166
+ tryFetch(baseBranch);
6167
+ tryFetch(branch);
6168
+ result = tryDiff(`origin/${baseBranch}...${branch}`) ?? tryDiff(`origin/${baseBranch}...origin/${branch}`) ?? tryDiff(`${baseBranch}...origin/${branch}`);
6169
+ }
6170
+ if (result === null) {
6171
+ logErr2(`Failed to get diff between ${baseBranch} and ${branch} in ${projectPath}.`);
6172
+ logErr2(`Branch may not exist locally or on origin. Try fetching it manually, or attach a PR URL to the review.`);
6173
+ process.exit(1);
5914
6174
  }
6175
+ diff = result;
5915
6176
  }
5916
- log(`Project: ${paint8("cyan", project.name)}`);
6177
+ log2(`Project: ${paint9("cyan", project.name)}`);
5917
6178
  if (!diff) {
5918
- logOk("No changes found between branches. Nothing to review.");
6179
+ logOk2("No changes found between branches. Nothing to review.");
5919
6180
  process.exit(0);
5920
6181
  }
5921
6182
  diff = stripLockFileDiffs(diff);
5922
6183
  const filesChanged = (diff.match(/^diff --git/gm) ?? []).length;
5923
- log(`Diff size: ${paint8("yellow", `${diff.length.toLocaleString()} chars`)}, ${paint8("yellow", `${filesChanged} files`)}`);
6184
+ log2(`Diff size: ${paint9("yellow", `${diff.length.toLocaleString()} chars`)}, ${paint9("yellow", `${filesChanged} files`)}`);
5924
6185
  let reportId;
5925
6186
  if (opts.report) {
5926
6187
  reportId = opts.report;
5927
- log(`Using existing review report ${paint8("yellow", reportId.slice(0, 8))}`);
6188
+ log2(`Using existing review report ${paint9("yellow", reportId.slice(0, 8))}`);
5928
6189
  } else {
5929
6190
  try {
5930
6191
  const report = await api.post("/api/reviews", {
@@ -5933,14 +6194,17 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5933
6194
  baseBranch
5934
6195
  });
5935
6196
  reportId = report.id;
5936
- log(`Created review report ${paint8("yellow", reportId.slice(0, 8))}`);
6197
+ log2(`Created review report ${paint9("yellow", reportId.slice(0, 8))}`);
5937
6198
  } catch (err) {
5938
- logErr(`Failed to create review report: ${err.message}`);
6199
+ logErr2(`Failed to create review report: ${err.message}`);
5939
6200
  process.exit(1);
5940
6201
  }
5941
6202
  }
5942
6203
  try {
5943
- await api.patch(`/api/reviews/${reportId}`, { status: "processing" });
6204
+ await api.patch(`/api/reviews/${reportId}`, {
6205
+ status: "processing",
6206
+ diff
6207
+ });
5944
6208
  } catch {
5945
6209
  }
5946
6210
  const startTime = Date.now();
@@ -5948,10 +6212,10 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5948
6212
  let truncatedDiff = diff;
5949
6213
  if (diff.length > MAX_DIFF_CHARS) {
5950
6214
  truncatedDiff = diff.slice(0, MAX_DIFF_CHARS) + "\n\n... (diff truncated, review covers first " + MAX_DIFF_CHARS.toLocaleString() + " characters)";
5951
- log(paint8("yellow", `Diff truncated to ${MAX_DIFF_CHARS.toLocaleString()} chars for review`));
6215
+ log2(paint9("yellow", `Diff truncated to ${MAX_DIFF_CHARS.toLocaleString()} chars for review`));
5952
6216
  }
5953
6217
  try {
5954
- log("Running code review with Claude...");
6218
+ log2("Running code review with Claude...");
5955
6219
  const prompt2 = buildReviewPrompt(branch, baseBranch, truncatedDiff);
5956
6220
  const output = await runClaude(prompt2);
5957
6221
  const result = parseReviewOutput(output);
@@ -5963,7 +6227,7 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5963
6227
  } catch {
5964
6228
  }
5965
6229
  if (wasCancelled) {
5966
- log(paint8("yellow", "Review was cancelled \u2014 discarding results."));
6230
+ log2(paint9("yellow", "Review was cancelled \u2014 discarding results."));
5967
6231
  process.exit(0);
5968
6232
  }
5969
6233
  await api.patch(`/api/reviews/${reportId}`, {
@@ -5973,28 +6237,28 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5973
6237
  filesReviewed: filesChanged,
5974
6238
  reviewDurationMs: duration
5975
6239
  });
5976
- logOk(`Review completed in ${paint8("cyan", formatDuration(duration))}`);
5977
- logOk(`Found ${paint8("yellow", String(result.findings.length))} findings`);
6240
+ logOk2(`Review completed in ${paint9("cyan", formatDuration(duration))}`);
6241
+ logOk2(`Found ${paint9("yellow", String(result.findings.length))} findings`);
5978
6242
  if (result.findings.length > 0) {
5979
6243
  console.log("");
5980
6244
  const critical = result.findings.filter((f) => f.severity === "critical").length;
5981
6245
  const high = result.findings.filter((f) => f.severity === "high").length;
5982
6246
  const medium = result.findings.filter((f) => f.severity === "medium").length;
5983
6247
  const low = result.findings.filter((f) => f.severity === "low").length;
5984
- if (critical > 0) console.log(` ${paint8("red", "\u25CF")} ${critical} critical`);
5985
- if (high > 0) console.log(` ${paint8("red", "\u25CF")} ${high} high`);
5986
- if (medium > 0) console.log(` ${paint8("yellow", "\u25CF")} ${medium} medium`);
5987
- if (low > 0) console.log(` ${paint8("dim", "\u25CF")} ${low} low`);
6248
+ if (critical > 0) console.log(` ${paint9("red", "\u25CF")} ${critical} critical`);
6249
+ if (high > 0) console.log(` ${paint9("red", "\u25CF")} ${high} high`);
6250
+ if (medium > 0) console.log(` ${paint9("yellow", "\u25CF")} ${medium} medium`);
6251
+ if (low > 0) console.log(` ${paint9("dim", "\u25CF")} ${low} low`);
5988
6252
  console.log("");
5989
6253
  }
5990
6254
  if (result.summary) {
5991
- console.log(paint8("dim", " " + result.summary));
6255
+ console.log(paint9("dim", " " + result.summary));
5992
6256
  console.log("");
5993
6257
  }
5994
6258
  } catch (err) {
5995
6259
  const duration = Date.now() - startTime;
5996
6260
  const errorMessage = err.message || "Unknown error";
5997
- logErr(`Review failed: ${errorMessage}`);
6261
+ logErr2(`Review failed: ${errorMessage}`);
5998
6262
  try {
5999
6263
  await api.patch(`/api/reviews/${reportId}`, {
6000
6264
  status: "failed",
@@ -6033,13 +6297,13 @@ function parsePrUrl(url) {
6033
6297
  }
6034
6298
  function fetchRemoteDiff(remote, number) {
6035
6299
  if (remote.host === "github") {
6036
- return execSync5(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6300
+ return execSync6(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6037
6301
  encoding: "utf-8",
6038
6302
  maxBuffer: 20 * 1024 * 1024
6039
6303
  }).trim();
6040
6304
  }
6041
6305
  const project = `${remote.owner}/${remote.repo}`;
6042
- return execSync5(`glab mr diff ${number} --repo ${project} --raw`, {
6306
+ return execSync6(`glab mr diff ${number} --repo ${project} --raw`, {
6043
6307
  encoding: "utf-8",
6044
6308
  maxBuffer: 20 * 1024 * 1024
6045
6309
  }).trim();
@@ -6110,7 +6374,7 @@ ${diff}
6110
6374
  }
6111
6375
  function runClaude(prompt2) {
6112
6376
  return new Promise((resolve9, reject) => {
6113
- const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6377
+ const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6114
6378
  stdio: ["ignore", "pipe", "pipe"]
6115
6379
  });
6116
6380
  let output = "";
@@ -6162,13 +6426,13 @@ function parseReviewOutput(output) {
6162
6426
  }
6163
6427
 
6164
6428
  // cli/commands/scan.ts
6165
- import { Command as Command26 } from "commander";
6429
+ import { Command as Command27 } from "commander";
6166
6430
 
6167
6431
  // lib/scanner/index.ts
6168
- import { spawn as spawn8 } from "child_process";
6432
+ import { spawn as spawn9 } from "child_process";
6169
6433
 
6170
6434
  // lib/scanner/config.ts
6171
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
6435
+ import { readFileSync as readFileSync10, existsSync as existsSync15 } from "fs";
6172
6436
  import { join as join9 } from "path";
6173
6437
  var ALL_FINDING_TYPES = [
6174
6438
  "idea",
@@ -6186,7 +6450,7 @@ var DEFAULTS = {
6186
6450
  };
6187
6451
  function loadScanConfig(projectPath) {
6188
6452
  const configPath2 = join9(projectPath, ".mr-scan.json");
6189
- if (!existsSync14(configPath2)) {
6453
+ if (!existsSync15(configPath2)) {
6190
6454
  return { ...DEFAULTS };
6191
6455
  }
6192
6456
  try {
@@ -6232,13 +6496,13 @@ async function authenticateBrowseSession(magicUrl, runBrowse) {
6232
6496
  }
6233
6497
 
6234
6498
  // lib/scanner/codebase-analysis.ts
6235
- import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync15 } from "fs";
6499
+ import { readdirSync as readdirSync2, readFileSync as readFileSync11, existsSync as existsSync16 } from "fs";
6236
6500
  import { join as join10, relative } from "path";
6237
- import { execSync as execSync6 } from "child_process";
6501
+ import { execSync as execSync7 } from "child_process";
6238
6502
  function resolveDir(projectPath, candidates) {
6239
6503
  for (const candidate of candidates) {
6240
6504
  const dir = join10(projectPath, candidate);
6241
- if (existsSync15(dir)) return dir;
6505
+ if (existsSync16(dir)) return dir;
6242
6506
  }
6243
6507
  return null;
6244
6508
  }
@@ -6272,7 +6536,7 @@ function discoverRoutes(projectPath) {
6272
6536
  }
6273
6537
  function extractModels(projectPath) {
6274
6538
  const schemaPath = join10(projectPath, "prisma", "schema.prisma");
6275
- if (existsSync15(schemaPath)) {
6539
+ if (existsSync16(schemaPath)) {
6276
6540
  const content = readFileSync11(schemaPath, "utf-8");
6277
6541
  const models2 = [];
6278
6542
  const modelRegex = /^model\s+(\w+)\s*\{/gm;
@@ -6286,7 +6550,7 @@ function extractModels(projectPath) {
6286
6550
  const drizzleDirs = ["src/db", "src/schema", "db", "drizzle"];
6287
6551
  for (const dir of drizzleDirs) {
6288
6552
  const fullDir = join10(projectPath, dir);
6289
- if (!existsSync15(fullDir)) continue;
6553
+ if (!existsSync16(fullDir)) continue;
6290
6554
  try {
6291
6555
  const entries = readdirSync2(fullDir, { withFileTypes: true });
6292
6556
  for (const entry of entries) {
@@ -6327,7 +6591,7 @@ function discoverComponents(projectPath) {
6327
6591
  function extractInternalLinks(projectPath) {
6328
6592
  const links = /* @__PURE__ */ new Set();
6329
6593
  function searchDir(dir) {
6330
- if (!existsSync15(dir)) return;
6594
+ if (!existsSync16(dir)) return;
6331
6595
  const entries = readdirSync2(dir, { withFileTypes: true });
6332
6596
  for (const entry of entries) {
6333
6597
  if (entry.isDirectory()) {
@@ -6360,7 +6624,7 @@ function extractInternalLinks(projectPath) {
6360
6624
  }
6361
6625
  function getRecentCommits(projectPath, count = 20) {
6362
6626
  try {
6363
- const output = execSync6(
6627
+ const output = execSync7(
6364
6628
  `git log --oneline -${count} --no-decorate`,
6365
6629
  { cwd: projectPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6366
6630
  );
@@ -6628,10 +6892,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
6628
6892
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
6629
6893
 
6630
6894
  **Components:**
6631
- ${codebaseAnalysis.components.slice(0, 15).map((c13) => `- ${c13}`).join("\n")}
6895
+ ${codebaseAnalysis.components.slice(0, 15).map((c14) => `- ${c14}`).join("\n")}
6632
6896
 
6633
6897
  **Recent Git Commits:**
6634
- ${codebaseAnalysis.recentCommits.slice(0, 8).map((c13) => `- ${c13}`).join("\n")}
6898
+ ${codebaseAnalysis.recentCommits.slice(0, 8).map((c14) => `- ${c14}`).join("\n")}
6635
6899
 
6636
6900
  **Completed Tasks:**
6637
6901
  ${context.completedTasks.slice(0, 10).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -6846,7 +7110,7 @@ async function fetchScanContext(opts) {
6846
7110
  }
6847
7111
  function runClaude2(prompt2) {
6848
7112
  return new Promise((resolve9, reject) => {
6849
- const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
7113
+ const child = spawn9("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6850
7114
  stdio: ["ignore", "pipe", "pipe"]
6851
7115
  });
6852
7116
  let output = "";
@@ -6902,7 +7166,7 @@ function parseSynthesisOutput(output) {
6902
7166
  }
6903
7167
 
6904
7168
  // cli/commands/scan.ts
6905
- var c9 = {
7169
+ var c10 = {
6906
7170
  reset: "\x1B[0m",
6907
7171
  bold: "\x1B[1m",
6908
7172
  dim: "\x1B[2m",
@@ -6913,53 +7177,53 @@ var c9 = {
6913
7177
  magenta: "\x1B[35m",
6914
7178
  gray: "\x1B[90m"
6915
7179
  };
6916
- function paint9(color, text) {
6917
- return `${c9[color]}${text}${c9.reset}`;
7180
+ function paint10(color, text) {
7181
+ return `${c10[color]}${text}${c10.reset}`;
6918
7182
  }
6919
- function timestamp3() {
6920
- return paint9("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
7183
+ function timestamp4() {
7184
+ return paint10("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
6921
7185
  }
6922
7186
  function scanTag() {
6923
- return paint9("magenta", "[scan]");
7187
+ return paint10("magenta", "[scan]");
6924
7188
  }
6925
- function log2(msg) {
6926
- console.log(`${timestamp3()} ${scanTag()} ${msg}`);
7189
+ function log3(msg) {
7190
+ console.log(`${timestamp4()} ${scanTag()} ${msg}`);
6927
7191
  }
6928
- function logOk2(msg) {
6929
- console.log(`${timestamp3()} ${scanTag()} ${paint9("green", "\u2713")} ${msg}`);
7192
+ function logOk3(msg) {
7193
+ console.log(`${timestamp4()} ${scanTag()} ${paint10("green", "\u2713")} ${msg}`);
6930
7194
  }
6931
- function logErr2(msg) {
6932
- console.error(`${timestamp3()} ${scanTag()} ${paint9("red", "\u2717")} ${msg}`);
7195
+ function logErr3(msg) {
7196
+ console.error(`${timestamp4()} ${scanTag()} ${paint10("red", "\u2717")} ${msg}`);
6933
7197
  }
6934
- 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) => {
7198
+ 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) => {
6935
7199
  const config = loadConfig();
6936
7200
  if (!config.apiKey) {
6937
- logErr2('Not authenticated. Run "mr login" first.');
7201
+ logErr3('Not authenticated. Run "mr login" first.');
6938
7202
  process.exit(1);
6939
7203
  }
6940
7204
  const banner = [
6941
7205
  ``,
6942
- paint9("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
6943
- paint9("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
6944
- paint9("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
6945
- paint9("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
6946
- paint9("dim", ` autonomous product scanner`),
7206
+ paint10("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
7207
+ paint10("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
7208
+ paint10("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
7209
+ paint10("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
7210
+ paint10("dim", ` autonomous product scanner`),
6947
7211
  ``
6948
7212
  ].join("\n");
6949
7213
  console.log(banner);
6950
7214
  const projectId = opts.project || getLinkedProjectId();
6951
7215
  if (!projectId) {
6952
- logErr2('No project linked. Run "mr link" or pass --project <id>.');
7216
+ logErr3('No project linked. Run "mr link" or pass --project <id>.');
6953
7217
  process.exit(1);
6954
7218
  }
6955
7219
  let project;
6956
7220
  try {
6957
7221
  project = await api.get(`/api/projects/${projectId}`);
6958
7222
  } catch {
6959
- logErr2(`Failed to fetch project ${projectId}`);
7223
+ logErr3(`Failed to fetch project ${projectId}`);
6960
7224
  process.exit(1);
6961
7225
  }
6962
- log2(`Scanning project: ${paint9("cyan", project.name)}`);
7226
+ log3(`Scanning project: ${paint10("cyan", project.name)}`);
6963
7227
  let projectPath = project.localPath;
6964
7228
  if (!projectPath) {
6965
7229
  for (const [dir, pid] of Object.entries(config.directories)) {
@@ -6976,7 +7240,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
6976
7240
  let customPrompt = typeof opts.prompt === "string" && opts.prompt.trim().length > 0 ? opts.prompt.trim() : null;
6977
7241
  if (opts.report) {
6978
7242
  reportId = opts.report;
6979
- log2(`Using existing scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7243
+ log3(`Using existing scan report ${paint10("yellow", reportId.slice(0, 8))}`);
6980
7244
  try {
6981
7245
  const existing = await api.get(`/api/scans/${reportId}`);
6982
7246
  if (!customPrompt && existing.customPrompt) {
@@ -6988,7 +7252,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
6988
7252
  try {
6989
7253
  const scans = await api.get(`/api/scans?projectId=${projectId}&status=processing`);
6990
7254
  if (scans.length > 0) {
6991
- logErr2("A scan is already in progress for this project. Wait for it to complete.");
7255
+ logErr3("A scan is already in progress for this project. Wait for it to complete.");
6992
7256
  process.exit(1);
6993
7257
  }
6994
7258
  } catch {
@@ -7000,9 +7264,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7000
7264
  customPrompt
7001
7265
  });
7002
7266
  reportId = report.id;
7003
- log2(`Created scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7267
+ log3(`Created scan report ${paint10("yellow", reportId.slice(0, 8))}`);
7004
7268
  } catch (err) {
7005
- logErr2(`Failed to create scan report: ${err.message}`);
7269
+ logErr3(`Failed to create scan report: ${err.message}`);
7006
7270
  process.exit(1);
7007
7271
  }
7008
7272
  }
@@ -7013,7 +7277,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7013
7277
  try {
7014
7278
  const current = await api.get(`/api/scans/${reportId}`);
7015
7279
  if (current.status === "cancelled") {
7016
- log2(paint9("yellow", "Scan was cancelled \u2014 aborting."));
7280
+ log3(paint10("yellow", "Scan was cancelled \u2014 aborting."));
7017
7281
  process.exit(0);
7018
7282
  }
7019
7283
  } catch {
@@ -7027,9 +7291,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7027
7291
  apiUrl: config.apiUrl,
7028
7292
  apiKey: config.apiKey,
7029
7293
  runBrowse: runBrowseCommand2,
7030
- onLog: log2,
7294
+ onLog: log3,
7031
7295
  onProgress: (phase, detail) => {
7032
- log2(`${paint9("dim", `[${phase}]`)} ${detail}`);
7296
+ log3(`${paint10("dim", `[${phase}]`)} ${detail}`);
7033
7297
  },
7034
7298
  customPrompt
7035
7299
  });
@@ -7042,7 +7306,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7042
7306
  } catch {
7043
7307
  }
7044
7308
  if (wasCancelled) {
7045
- log2(paint9("yellow", "Scan was cancelled by user \u2014 discarding results."));
7309
+ log3(paint10("yellow", "Scan was cancelled by user \u2014 discarding results."));
7046
7310
  process.exit(0);
7047
7311
  }
7048
7312
  await api.patch(`/api/scans/${reportId}`, {
@@ -7053,37 +7317,37 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7053
7317
  scanDurationMs: result.scanDurationMs,
7054
7318
  routesCrawled: result.routesCrawled
7055
7319
  });
7056
- logOk2(`Scan complete \u2014 ${paint9("cyan", String(result.findings.length))} findings`);
7320
+ logOk3(`Scan complete \u2014 ${paint10("cyan", String(result.findings.length))} findings`);
7057
7321
  console.log("");
7058
- console.log(` ${paint9("bold", "Summary:")} ${result.summary}`);
7322
+ console.log(` ${paint10("bold", "Summary:")} ${result.summary}`);
7059
7323
  console.log("");
7060
7324
  const high = result.findings.filter((f) => f.priority === "high");
7061
7325
  const medium = result.findings.filter((f) => f.priority === "medium");
7062
7326
  const low = result.findings.filter((f) => f.priority === "low");
7063
7327
  if (high.length > 0) {
7064
- console.log(` ${paint9("bold", paint9("red", `High Priority (${high.length})`))}`);
7328
+ console.log(` ${paint10("bold", paint10("red", `High Priority (${high.length})`))}`);
7065
7329
  for (const f of high) {
7066
- console.log(` ${paint9("red", "\u25CF")} [${f.type}] ${f.title}`);
7067
- console.log(` ${paint9("dim", f.description.slice(0, 120))}`);
7330
+ console.log(` ${paint10("red", "\u25CF")} [${f.type}] ${f.title}`);
7331
+ console.log(` ${paint10("dim", f.description.slice(0, 120))}`);
7068
7332
  }
7069
7333
  console.log("");
7070
7334
  }
7071
7335
  if (medium.length > 0) {
7072
- console.log(` ${paint9("bold", paint9("yellow", `Medium Priority (${medium.length})`))}`);
7336
+ console.log(` ${paint10("bold", paint10("yellow", `Medium Priority (${medium.length})`))}`);
7073
7337
  for (const f of medium) {
7074
- console.log(` ${paint9("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7338
+ console.log(` ${paint10("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7075
7339
  }
7076
7340
  console.log("");
7077
7341
  }
7078
7342
  if (low.length > 0) {
7079
- console.log(` ${paint9("dim", `Low Priority (${low.length})`)} `);
7343
+ console.log(` ${paint10("dim", `Low Priority (${low.length})`)} `);
7080
7344
  for (const f of low) {
7081
- console.log(` ${paint9("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7345
+ console.log(` ${paint10("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7082
7346
  }
7083
7347
  console.log("");
7084
7348
  }
7085
7349
  } catch (err) {
7086
- logErr2(`Scan failed: ${err.message}`);
7350
+ logErr3(`Scan failed: ${err.message}`);
7087
7351
  try {
7088
7352
  await api.patch(`/api/scans/${reportId}`, {
7089
7353
  status: "failed",
@@ -7096,13 +7360,13 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7096
7360
  });
7097
7361
 
7098
7362
  // cli/commands/doctor.ts
7099
- import { Command as Command27 } from "commander";
7100
- import { existsSync as existsSync16 } from "fs";
7363
+ import { Command as Command28 } from "commander";
7364
+ import { existsSync as existsSync17 } from "fs";
7101
7365
  import { homedir as homedir2 } from "os";
7102
7366
  import { join as join11 } from "path";
7103
7367
  async function checkConfigExists() {
7104
7368
  const configPath2 = join11(homedir2(), ".mr-manager", "config.json");
7105
- const exists = existsSync16(configPath2);
7369
+ const exists = existsSync17(configPath2);
7106
7370
  if (!exists) {
7107
7371
  return {
7108
7372
  name: "Config file",
@@ -7144,7 +7408,7 @@ async function checkProjectLink() {
7144
7408
  optional: true
7145
7409
  };
7146
7410
  }
7147
- var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
7411
+ var doctorCommand = new Command28("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
7148
7412
  const banner = [
7149
7413
  ``,
7150
7414
  paint5("cyan", ` MR DOCTOR`),
@@ -7175,7 +7439,7 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7175
7439
  console.log("");
7176
7440
  return;
7177
7441
  }
7178
- const fixes = checks.filter((c13) => !c13.ok && c13.fix && !c13.optional);
7442
+ const fixes = checks.filter((c14) => !c14.ok && c14.fix && !c14.optional);
7179
7443
  if (fixes.length > 0) {
7180
7444
  console.log(paint5("yellow", " To fix:"));
7181
7445
  for (const fix of fixes) {
@@ -7187,14 +7451,14 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7187
7451
  });
7188
7452
 
7189
7453
  // cli/commands/prompt-audit.ts
7190
- import { Command as Command28 } from "commander";
7454
+ import { Command as Command29 } from "commander";
7191
7455
  import { resolve as resolve8 } from "path";
7192
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
7456
+ import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
7193
7457
  function auditLine(label, tokens) {
7194
7458
  const bar = "\u2588".repeat(Math.min(60, Math.round(tokens / 200)));
7195
7459
  return ` ${label.padEnd(30)} ${formatTokenCount(tokens).padStart(8)} ${bar}`;
7196
7460
  }
7197
- 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) => {
7461
+ 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) => {
7198
7462
  const results = [];
7199
7463
  if (opts.task) {
7200
7464
  try {
@@ -7248,7 +7512,7 @@ ${task.notes}` : "";
7248
7512
  const repoDir = Object.entries(config.directories).find(([, pid]) => pid === task.projectId)?.[0];
7249
7513
  if (repoDir) {
7250
7514
  const featuresPath = resolve8(repoDir, ".mr-features.md");
7251
- if (existsSync17(featuresPath)) {
7515
+ if (existsSync18(featuresPath)) {
7252
7516
  const featuresContent = readFileSync12(featuresPath, "utf-8");
7253
7517
  sections.push({ name: "features-doc", tokens: estimateTokens(featuresContent) });
7254
7518
  }
@@ -7403,9 +7667,9 @@ ${r.jobType} [${r.identifier}]`);
7403
7667
  });
7404
7668
 
7405
7669
  // cli/commands/skill.ts
7406
- import { Command as Command29 } from "commander";
7670
+ import { Command as Command30 } from "commander";
7407
7671
  import { createInterface as createInterface3 } from "readline/promises";
7408
- var c10 = {
7672
+ var c11 = {
7409
7673
  reset: "\x1B[0m",
7410
7674
  bold: "\x1B[1m",
7411
7675
  dim: "\x1B[2m",
@@ -7438,7 +7702,7 @@ function formatSize(bytes) {
7438
7702
  if (bytes < 1024) return `${bytes}b`;
7439
7703
  return `${(bytes / 1024).toFixed(1)}kb`;
7440
7704
  }
7441
- var skillCommand = new Command29("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
7705
+ var skillCommand = new Command30("skill").description("Manage skills \u2014 reusable playbooks for AI agents");
7442
7706
  skillCommand.command("list").alias("ls").description("List all skills").option("--category <category>", "Filter by category").action(async (opts) => {
7443
7707
  const params = new URLSearchParams();
7444
7708
  if (opts.category) params.set("category", opts.category);
@@ -7446,17 +7710,17 @@ skillCommand.command("list").alias("ls").description("List all skills").option("
7446
7710
  `/api/skills${params.toString() ? `?${params}` : ""}`
7447
7711
  );
7448
7712
  if (skills.length === 0) {
7449
- console.log(`${c10.dim}No skills found.${c10.reset}`);
7713
+ console.log(`${c11.dim}No skills found.${c11.reset}`);
7450
7714
  return;
7451
7715
  }
7452
7716
  for (const skill of skills) {
7453
- const cat = skill.category ? ` ${c10.dim}[${skill.category}]${c10.reset}` : "";
7454
- const scope = skill.projectId ? ` ${c10.dim}(project)${c10.reset}` : ` ${c10.dim}(global)${c10.reset}`;
7455
- console.log(` ${c10.cyan}${skill.name}${c10.reset}${cat}${scope}`);
7717
+ const cat = skill.category ? ` ${c11.dim}[${skill.category}]${c11.reset}` : "";
7718
+ const scope = skill.projectId ? ` ${c11.dim}(project)${c11.reset}` : ` ${c11.dim}(global)${c11.reset}`;
7719
+ console.log(` ${c11.cyan}${skill.name}${c11.reset}${cat}${scope}`);
7456
7720
  if (skill.description) {
7457
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7721
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7458
7722
  }
7459
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7723
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7460
7724
  }
7461
7725
  });
7462
7726
  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) => {
@@ -7492,7 +7756,7 @@ skillCommand.command("create").description("Create a new skill from a markdown f
7492
7756
  projectId
7493
7757
  });
7494
7758
  console.log(
7495
- `${c10.green}Created skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}`
7759
+ `${c11.green}Created skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}`
7496
7760
  );
7497
7761
  });
7498
7762
  skillCommand.command("update").description(
@@ -7553,14 +7817,14 @@ skillCommand.command("update").description(
7553
7817
  const beforeContent = existing.content ?? "";
7554
7818
  const afterContent = typeof patch.content === "string" ? patch.content : beforeContent;
7555
7819
  const contentChanged = typeof patch.content === "string" && patch.content !== beforeContent;
7556
- const sizeDiff = typeof patch.content === "string" ? ` ${c10.dim}(${formatSize(Buffer.byteLength(beforeContent, "utf-8"))} -> ${formatSize(Buffer.byteLength(afterContent, "utf-8"))})${c10.reset}` : "";
7820
+ const sizeDiff = typeof patch.content === "string" ? ` ${c11.dim}(${formatSize(Buffer.byteLength(beforeContent, "utf-8"))} -> ${formatSize(Buffer.byteLength(afterContent, "utf-8"))})${c11.reset}` : "";
7557
7821
  const priorRevisions = existing.revisions?.length ?? 0;
7558
7822
  const revisionsAfter = priorRevisions + (contentChanged ? 1 : 0);
7559
- const revisionNote = contentChanged ? ` ${c10.dim}kept ${revisionsAfter} prior revision${revisionsAfter === 1 ? "" : "s"}${c10.reset}` : "";
7823
+ const revisionNote = contentChanged ? ` ${c11.dim}kept ${revisionsAfter} prior revision${revisionsAfter === 1 ? "" : "s"}${c11.reset}` : "";
7560
7824
  console.log(
7561
- `${c10.green}Updated skill:${c10.reset} ${c10.bold}${updated.name}${c10.reset}${sizeDiff}${revisionNote}`
7825
+ `${c11.green}Updated skill:${c11.reset} ${c11.bold}${updated.name}${c11.reset}${sizeDiff}${revisionNote}`
7562
7826
  );
7563
- console.log(` ${c10.dim}id: ${updated.id}${c10.reset}`);
7827
+ console.log(` ${c11.dim}id: ${updated.id}${c11.reset}`);
7564
7828
  });
7565
7829
  skillCommand.command("delete").alias("rm").description("Delete a skill").argument("<idOrName>", "Skill id or name").option("--yes", "Skip confirmation prompt").action(async (idOrName, opts) => {
7566
7830
  let skill;
@@ -7573,7 +7837,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7573
7837
  if (!opts.yes) {
7574
7838
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7575
7839
  const answer = (await rl.question(
7576
- `Delete skill ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}? [y/N] `
7840
+ `Delete skill ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}? [y/N] `
7577
7841
  )).trim().toLowerCase();
7578
7842
  rl.close();
7579
7843
  if (answer !== "y" && answer !== "yes") {
@@ -7583,7 +7847,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7583
7847
  }
7584
7848
  await api.del(`/api/skills/${skill.id}`);
7585
7849
  console.log(
7586
- `${c10.yellow}Deleted skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset} ${c10.dim}(${skill.id})${c10.reset}`
7850
+ `${c11.yellow}Deleted skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset} ${c11.dim}(${skill.id})${c11.reset}`
7587
7851
  );
7588
7852
  });
7589
7853
  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) => {
@@ -7597,22 +7861,22 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7597
7861
  process.exit(1);
7598
7862
  }
7599
7863
  }
7600
- console.log(`${c10.dim}Generating skill...${c10.reset}`);
7864
+ console.log(`${c11.dim}Generating skill...${c11.reset}`);
7601
7865
  try {
7602
7866
  const skill = await api.post("/api/skills/generate", {
7603
7867
  prompt: prompt2,
7604
7868
  projectId
7605
7869
  });
7606
7870
  console.log(
7607
- `${c10.green}Generated skill:${c10.reset} ${c10.bold}${skill.name}${c10.reset}`
7871
+ `${c11.green}Generated skill:${c11.reset} ${c11.bold}${skill.name}${c11.reset}`
7608
7872
  );
7609
7873
  if (skill.description) {
7610
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7874
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7611
7875
  }
7612
7876
  if (skill.category) {
7613
- console.log(` ${c10.dim}Category: ${skill.category}${c10.reset}`);
7877
+ console.log(` ${c11.dim}Category: ${skill.category}${c11.reset}`);
7614
7878
  }
7615
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7879
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7616
7880
  } catch (err) {
7617
7881
  console.error(`Failed to generate skill: ${err.message}`);
7618
7882
  process.exit(1);
@@ -7620,8 +7884,8 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7620
7884
  });
7621
7885
 
7622
7886
  // cli/commands/resource.ts
7623
- import { Command as Command30 } from "commander";
7624
- var c11 = {
7887
+ import { Command as Command31 } from "commander";
7888
+ var c12 = {
7625
7889
  reset: "\x1B[0m",
7626
7890
  bold: "\x1B[1m",
7627
7891
  dim: "\x1B[2m",
@@ -7631,16 +7895,16 @@ var c11 = {
7631
7895
  magenta: "\x1B[35m"
7632
7896
  };
7633
7897
  var TYPE_COLORS = {
7634
- plan: c11.cyan,
7635
- research: c11.magenta,
7636
- "test-plan": c11.yellow,
7637
- note: c11.green
7898
+ plan: c12.cyan,
7899
+ research: c12.magenta,
7900
+ "test-plan": c12.yellow,
7901
+ note: c12.green
7638
7902
  };
7639
7903
  function typeLabel(type) {
7640
- const color = TYPE_COLORS[type] ?? c11.dim;
7641
- return `${color}${type}${c11.reset}`;
7904
+ const color = TYPE_COLORS[type] ?? c12.dim;
7905
+ return `${color}${type}${c12.reset}`;
7642
7906
  }
7643
- var resourceCommand = new Command30("resource").description("Manage resources \u2014 documents, plans, research, and notes");
7907
+ var resourceCommand = new Command31("resource").description("Manage resources \u2014 documents, plans, research, and notes");
7644
7908
  resourceCommand.command("list").alias("ls").description("List resources for the linked project (or all)").option("--all", "List all resources across projects").action(async (opts) => {
7645
7909
  const params = new URLSearchParams();
7646
7910
  if (opts.all) {
@@ -7653,13 +7917,13 @@ resourceCommand.command("list").alias("ls").description("List resources for the
7653
7917
  `/api/resources${params.toString() ? `?${params}` : ""}`
7654
7918
  );
7655
7919
  if (resources.length === 0) {
7656
- console.log(`${c11.dim}No resources found.${c11.reset}`);
7920
+ console.log(`${c12.dim}No resources found.${c12.reset}`);
7657
7921
  return;
7658
7922
  }
7659
7923
  for (const r of resources) {
7660
- const project = r.projectName ? ` ${c11.dim}[${r.projectName}]${c11.reset}` : "";
7661
- console.log(` ${typeLabel(r.type)} ${c11.bold}${r.title}${c11.reset}${project}`);
7662
- console.log(` ${c11.dim}id: ${r.id}${c11.reset}`);
7924
+ const project = r.projectName ? ` ${c12.dim}[${r.projectName}]${c12.reset}` : "";
7925
+ console.log(` ${typeLabel(r.type)} ${c12.bold}${r.title}${c12.reset}${project}`);
7926
+ console.log(` ${c12.dim}id: ${r.id}${c12.reset}`);
7663
7927
  }
7664
7928
  });
7665
7929
  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) => {
@@ -7684,9 +7948,9 @@ resourceCommand.command("create").description("Create a new resource from a file
7684
7948
  content: content.trim()
7685
7949
  });
7686
7950
  console.log(
7687
- `${c11.green}Created resource:${c11.reset} ${c11.bold}${title}${c11.reset} ${c11.dim}(${resource.id})${c11.reset}`
7951
+ `${c12.green}Created resource:${c12.reset} ${c12.bold}${title}${c12.reset} ${c12.dim}(${resource.id})${c12.reset}`
7688
7952
  );
7689
- console.log(` ${c11.dim}Attached to task ${opts.task}${c11.reset}`);
7953
+ console.log(` ${c12.dim}Attached to task ${opts.task}${c12.reset}`);
7690
7954
  return;
7691
7955
  }
7692
7956
  let projectId;
@@ -7706,7 +7970,7 @@ resourceCommand.command("create").description("Create a new resource from a file
7706
7970
  projectId
7707
7971
  });
7708
7972
  console.log(
7709
- `${c11.green}Created resource:${c11.reset} ${c11.bold}${title}${c11.reset} ${c11.dim}(${result.resource.id})${c11.reset}`
7973
+ `${c12.green}Created resource:${c12.reset} ${c12.bold}${title}${c12.reset} ${c12.dim}(${result.resource.id})${c12.reset}`
7710
7974
  );
7711
7975
  });
7712
7976
  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) => {
@@ -7725,30 +7989,30 @@ resourceCommand.command("generate").alias("gen").description("Generate a resourc
7725
7989
  process.exit(1);
7726
7990
  }
7727
7991
  }
7728
- console.log(`${c11.dim}Generating ${type}...${c11.reset}`);
7992
+ console.log(`${c12.dim}Generating ${type}...${c12.reset}`);
7729
7993
  const result = await api.post("/api/resources", {
7730
7994
  type,
7731
7995
  prompt: prompt2,
7732
7996
  projectId
7733
7997
  });
7734
7998
  console.log(
7735
- `${c11.green}Queued:${c11.reset} ${c11.bold}${result.task.title}${c11.reset}`
7999
+ `${c12.green}Queued:${c12.reset} ${c12.bold}${result.task.title}${c12.reset}`
7736
8000
  );
7737
- console.log(` ${c11.dim}task: ${result.task.id}${c11.reset}`);
8001
+ console.log(` ${c12.dim}task: ${result.task.id}${c12.reset}`);
7738
8002
  });
7739
8003
 
7740
8004
  // cli/commands/tests.ts
7741
- import { Command as Command31 } from "commander";
7742
- var c12 = {
8005
+ import { Command as Command32 } from "commander";
8006
+ var c13 = {
7743
8007
  reset: "\x1B[0m",
7744
8008
  dim: "\x1B[2m",
7745
8009
  yellow: "\x1B[33m"
7746
8010
  };
7747
- var testsCommand = new Command31("tests").description("List MR Test scenarios for the linked project").action(async () => {
8011
+ var testsCommand = new Command32("tests").description("List MR Test scenarios for the linked project").action(async () => {
7748
8012
  const projectId = getLinkedProjectId();
7749
8013
  if (!projectId) {
7750
8014
  console.error(
7751
- `${c12.yellow}No project linked to this directory.${c12.reset} Run "mr link <project-id>" first.`
8015
+ `${c13.yellow}No project linked to this directory.${c13.reset} Run "mr link <project-id>" first.`
7752
8016
  );
7753
8017
  process.exit(1);
7754
8018
  }
@@ -7761,13 +8025,13 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7761
8025
  process.exit(1);
7762
8026
  }
7763
8027
  if (scenarios.length === 0) {
7764
- console.log(`${c12.dim}No test scenarios found for this project.${c12.reset}`);
8028
+ console.log(`${c13.dim}No test scenarios found for this project.${c13.reset}`);
7765
8029
  return;
7766
8030
  }
7767
8031
  for (const scenario of scenarios) {
7768
8032
  console.log(`### ${scenario.name}`);
7769
8033
  if (scenario.description) {
7770
- console.log(`${c12.dim}${scenario.description}${c12.reset}`);
8034
+ console.log(`${c13.dim}${scenario.description}${c13.reset}`);
7771
8035
  console.log();
7772
8036
  }
7773
8037
  console.log(scenario.content);
@@ -7776,9 +8040,9 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7776
8040
  });
7777
8041
 
7778
8042
  // cli/commands/walkthrough.ts
7779
- import { Command as Command32 } from "commander";
8043
+ import { Command as Command33 } from "commander";
7780
8044
  var SKILL_NAME = "Generate Walkthrough Video";
7781
- 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) => {
8045
+ 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) => {
7782
8046
  const skills = await api.get("/api/skills");
7783
8047
  const skill = skills.find((s) => s.name === SKILL_NAME);
7784
8048
  if (!skill) {
@@ -7806,12 +8070,12 @@ var walkthroughCommand = new Command32("walkthrough").description("Attach the Ge
7806
8070
 
7807
8071
  // cli/index.ts
7808
8072
  var configPath = join12(homedir3(), ".mr-manager", "config.json");
7809
- var isFirstRun = !existsSync18(configPath);
8073
+ var isFirstRun = !existsSync19(configPath);
7810
8074
  var userArgs = process.argv.slice(2);
7811
8075
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
7812
8076
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
7813
8077
  if (isFirstRun && !shouldBypass) {
7814
- const c13 = {
8078
+ const c14 = {
7815
8079
  reset: "\x1B[0m",
7816
8080
  bold: "\x1B[1m",
7817
8081
  dim: "\x1B[2m",
@@ -7821,28 +8085,28 @@ if (isFirstRun && !shouldBypass) {
7821
8085
  magenta: "\x1B[35m"
7822
8086
  };
7823
8087
  console.log("");
7824
- 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}`);
7825
- 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}`);
7826
- 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}`);
7827
- 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}`);
8088
+ 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}`);
8089
+ 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}`);
8090
+ 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}`);
8091
+ 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}`);
7828
8092
  console.log("");
7829
- console.log(`${c13.bold} Welcome to Mr. Manager!${c13.reset}`);
7830
- console.log(`${c13.dim} Let's get you set up in a few quick steps.${c13.reset}`);
8093
+ console.log(`${c14.bold} Welcome to Mr. Manager!${c14.reset}`);
8094
+ console.log(`${c14.dim} Let's get you set up in a few quick steps.${c14.reset}`);
7831
8095
  console.log("");
7832
- console.log(` ${c13.yellow}Step 1:${c13.reset} Authenticate via Google OAuth`);
7833
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr login${c13.reset}`);
8096
+ console.log(` ${c14.yellow}Step 1:${c14.reset} Authenticate via Google OAuth`);
8097
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr login${c14.reset}`);
7834
8098
  console.log("");
7835
- console.log(` ${c13.yellow}Step 2:${c13.reset} Verify your environment`);
7836
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr setup${c13.reset}`);
8099
+ console.log(` ${c14.yellow}Step 2:${c14.reset} Verify your environment`);
8100
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr setup${c14.reset}`);
7837
8101
  console.log("");
7838
- console.log(` ${c13.yellow}Step 3:${c13.reset} Link a repo and start watching`);
7839
- console.log(` ${c13.dim}Run:${c13.reset} ${c13.cyan}mr link${c13.reset} ${c13.dim}&&${c13.reset} ${c13.cyan}mr watch${c13.reset}`);
8102
+ console.log(` ${c14.yellow}Step 3:${c14.reset} Link a repo and start watching`);
8103
+ console.log(` ${c14.dim}Run:${c14.reset} ${c14.cyan}mr link${c14.reset} ${c14.dim}&&${c14.reset} ${c14.cyan}mr watch${c14.reset}`);
7840
8104
  console.log("");
7841
- console.log(`${c13.dim} Or run ${c13.reset}${c13.cyan}mr login${c13.reset}${c13.dim} to get started now.${c13.reset}`);
8105
+ console.log(`${c14.dim} Or run ${c14.reset}${c14.cyan}mr login${c14.reset}${c14.dim} to get started now.${c14.reset}`);
7842
8106
  console.log("");
7843
8107
  process.exit(0);
7844
8108
  }
7845
- var program = new Command33();
8109
+ var program = new Command34();
7846
8110
  program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
7847
8111
  program.addCommand(initCommand);
7848
8112
  program.addCommand(authCommand);