@dunnewold-labs/mr-manager 0.4.40 → 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 +473 -221
  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.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,42 +6110,42 @@ 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)}`);
6144
+ log2(`Reviewing branch: ${paint9("cyan", branch)} against ${paint9("dim", baseBranch)}`);
5896
6145
  const pathspec = `-- . ':!*.lock' ':!package-lock.json' ':!pnpm-lock.yaml'`;
5897
6146
  const tryDiff = (range) => {
5898
6147
  try {
5899
- return execSync5(`git diff ${range} ${pathspec}`, {
6148
+ return execSync6(`git diff ${range} ${pathspec}`, {
5900
6149
  cwd: projectPath,
5901
6150
  encoding: "utf-8",
5902
6151
  maxBuffer: 10 * 1024 * 1024,
@@ -5908,7 +6157,7 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5908
6157
  };
5909
6158
  const tryFetch = (ref) => {
5910
6159
  try {
5911
- execSync5(`git fetch origin ${ref}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
6160
+ execSync6(`git fetch origin ${ref}`, { cwd: projectPath, encoding: "utf-8", stdio: "pipe" });
5912
6161
  } catch {
5913
6162
  }
5914
6163
  };
@@ -5919,24 +6168,24 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5919
6168
  result = tryDiff(`origin/${baseBranch}...${branch}`) ?? tryDiff(`origin/${baseBranch}...origin/${branch}`) ?? tryDiff(`${baseBranch}...origin/${branch}`);
5920
6169
  }
5921
6170
  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.`);
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.`);
5924
6173
  process.exit(1);
5925
6174
  }
5926
6175
  diff = result;
5927
6176
  }
5928
- log(`Project: ${paint8("cyan", project.name)}`);
6177
+ log2(`Project: ${paint9("cyan", project.name)}`);
5929
6178
  if (!diff) {
5930
- logOk("No changes found between branches. Nothing to review.");
6179
+ logOk2("No changes found between branches. Nothing to review.");
5931
6180
  process.exit(0);
5932
6181
  }
5933
6182
  diff = stripLockFileDiffs(diff);
5934
6183
  const filesChanged = (diff.match(/^diff --git/gm) ?? []).length;
5935
- 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`)}`);
5936
6185
  let reportId;
5937
6186
  if (opts.report) {
5938
6187
  reportId = opts.report;
5939
- log(`Using existing review report ${paint8("yellow", reportId.slice(0, 8))}`);
6188
+ log2(`Using existing review report ${paint9("yellow", reportId.slice(0, 8))}`);
5940
6189
  } else {
5941
6190
  try {
5942
6191
  const report = await api.post("/api/reviews", {
@@ -5945,14 +6194,17 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5945
6194
  baseBranch
5946
6195
  });
5947
6196
  reportId = report.id;
5948
- log(`Created review report ${paint8("yellow", reportId.slice(0, 8))}`);
6197
+ log2(`Created review report ${paint9("yellow", reportId.slice(0, 8))}`);
5949
6198
  } catch (err) {
5950
- logErr(`Failed to create review report: ${err.message}`);
6199
+ logErr2(`Failed to create review report: ${err.message}`);
5951
6200
  process.exit(1);
5952
6201
  }
5953
6202
  }
5954
6203
  try {
5955
- await api.patch(`/api/reviews/${reportId}`, { status: "processing" });
6204
+ await api.patch(`/api/reviews/${reportId}`, {
6205
+ status: "processing",
6206
+ diff
6207
+ });
5956
6208
  } catch {
5957
6209
  }
5958
6210
  const startTime = Date.now();
@@ -5960,10 +6212,10 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5960
6212
  let truncatedDiff = diff;
5961
6213
  if (diff.length > MAX_DIFF_CHARS) {
5962
6214
  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`));
6215
+ log2(paint9("yellow", `Diff truncated to ${MAX_DIFF_CHARS.toLocaleString()} chars for review`));
5964
6216
  }
5965
6217
  try {
5966
- log("Running code review with Claude...");
6218
+ log2("Running code review with Claude...");
5967
6219
  const prompt2 = buildReviewPrompt(branch, baseBranch, truncatedDiff);
5968
6220
  const output = await runClaude(prompt2);
5969
6221
  const result = parseReviewOutput(output);
@@ -5975,7 +6227,7 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5975
6227
  } catch {
5976
6228
  }
5977
6229
  if (wasCancelled) {
5978
- log(paint8("yellow", "Review was cancelled \u2014 discarding results."));
6230
+ log2(paint9("yellow", "Review was cancelled \u2014 discarding results."));
5979
6231
  process.exit(0);
5980
6232
  }
5981
6233
  await api.patch(`/api/reviews/${reportId}`, {
@@ -5985,28 +6237,28 @@ var reviewCommand = new Command25("review").description("Run an automated code r
5985
6237
  filesReviewed: filesChanged,
5986
6238
  reviewDurationMs: duration
5987
6239
  });
5988
- logOk(`Review completed in ${paint8("cyan", formatDuration(duration))}`);
5989
- 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`);
5990
6242
  if (result.findings.length > 0) {
5991
6243
  console.log("");
5992
6244
  const critical = result.findings.filter((f) => f.severity === "critical").length;
5993
6245
  const high = result.findings.filter((f) => f.severity === "high").length;
5994
6246
  const medium = result.findings.filter((f) => f.severity === "medium").length;
5995
6247
  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`);
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`);
6000
6252
  console.log("");
6001
6253
  }
6002
6254
  if (result.summary) {
6003
- console.log(paint8("dim", " " + result.summary));
6255
+ console.log(paint9("dim", " " + result.summary));
6004
6256
  console.log("");
6005
6257
  }
6006
6258
  } catch (err) {
6007
6259
  const duration = Date.now() - startTime;
6008
6260
  const errorMessage = err.message || "Unknown error";
6009
- logErr(`Review failed: ${errorMessage}`);
6261
+ logErr2(`Review failed: ${errorMessage}`);
6010
6262
  try {
6011
6263
  await api.patch(`/api/reviews/${reportId}`, {
6012
6264
  status: "failed",
@@ -6045,13 +6297,13 @@ function parsePrUrl(url) {
6045
6297
  }
6046
6298
  function fetchRemoteDiff(remote, number) {
6047
6299
  if (remote.host === "github") {
6048
- return execSync5(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6300
+ return execSync6(`gh pr diff ${number} --repo ${remote.owner}/${remote.repo}`, {
6049
6301
  encoding: "utf-8",
6050
6302
  maxBuffer: 20 * 1024 * 1024
6051
6303
  }).trim();
6052
6304
  }
6053
6305
  const project = `${remote.owner}/${remote.repo}`;
6054
- return execSync5(`glab mr diff ${number} --repo ${project} --raw`, {
6306
+ return execSync6(`glab mr diff ${number} --repo ${project} --raw`, {
6055
6307
  encoding: "utf-8",
6056
6308
  maxBuffer: 20 * 1024 * 1024
6057
6309
  }).trim();
@@ -6122,7 +6374,7 @@ ${diff}
6122
6374
  }
6123
6375
  function runClaude(prompt2) {
6124
6376
  return new Promise((resolve9, reject) => {
6125
- const child = spawn7("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6377
+ const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6126
6378
  stdio: ["ignore", "pipe", "pipe"]
6127
6379
  });
6128
6380
  let output = "";
@@ -6174,13 +6426,13 @@ function parseReviewOutput(output) {
6174
6426
  }
6175
6427
 
6176
6428
  // cli/commands/scan.ts
6177
- import { Command as Command26 } from "commander";
6429
+ import { Command as Command27 } from "commander";
6178
6430
 
6179
6431
  // lib/scanner/index.ts
6180
- import { spawn as spawn8 } from "child_process";
6432
+ import { spawn as spawn9 } from "child_process";
6181
6433
 
6182
6434
  // lib/scanner/config.ts
6183
- import { readFileSync as readFileSync10, existsSync as existsSync14 } from "fs";
6435
+ import { readFileSync as readFileSync10, existsSync as existsSync15 } from "fs";
6184
6436
  import { join as join9 } from "path";
6185
6437
  var ALL_FINDING_TYPES = [
6186
6438
  "idea",
@@ -6198,7 +6450,7 @@ var DEFAULTS = {
6198
6450
  };
6199
6451
  function loadScanConfig(projectPath) {
6200
6452
  const configPath2 = join9(projectPath, ".mr-scan.json");
6201
- if (!existsSync14(configPath2)) {
6453
+ if (!existsSync15(configPath2)) {
6202
6454
  return { ...DEFAULTS };
6203
6455
  }
6204
6456
  try {
@@ -6244,13 +6496,13 @@ async function authenticateBrowseSession(magicUrl, runBrowse) {
6244
6496
  }
6245
6497
 
6246
6498
  // lib/scanner/codebase-analysis.ts
6247
- 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";
6248
6500
  import { join as join10, relative } from "path";
6249
- import { execSync as execSync6 } from "child_process";
6501
+ import { execSync as execSync7 } from "child_process";
6250
6502
  function resolveDir(projectPath, candidates) {
6251
6503
  for (const candidate of candidates) {
6252
6504
  const dir = join10(projectPath, candidate);
6253
- if (existsSync15(dir)) return dir;
6505
+ if (existsSync16(dir)) return dir;
6254
6506
  }
6255
6507
  return null;
6256
6508
  }
@@ -6284,7 +6536,7 @@ function discoverRoutes(projectPath) {
6284
6536
  }
6285
6537
  function extractModels(projectPath) {
6286
6538
  const schemaPath = join10(projectPath, "prisma", "schema.prisma");
6287
- if (existsSync15(schemaPath)) {
6539
+ if (existsSync16(schemaPath)) {
6288
6540
  const content = readFileSync11(schemaPath, "utf-8");
6289
6541
  const models2 = [];
6290
6542
  const modelRegex = /^model\s+(\w+)\s*\{/gm;
@@ -6298,7 +6550,7 @@ function extractModels(projectPath) {
6298
6550
  const drizzleDirs = ["src/db", "src/schema", "db", "drizzle"];
6299
6551
  for (const dir of drizzleDirs) {
6300
6552
  const fullDir = join10(projectPath, dir);
6301
- if (!existsSync15(fullDir)) continue;
6553
+ if (!existsSync16(fullDir)) continue;
6302
6554
  try {
6303
6555
  const entries = readdirSync2(fullDir, { withFileTypes: true });
6304
6556
  for (const entry of entries) {
@@ -6339,7 +6591,7 @@ function discoverComponents(projectPath) {
6339
6591
  function extractInternalLinks(projectPath) {
6340
6592
  const links = /* @__PURE__ */ new Set();
6341
6593
  function searchDir(dir) {
6342
- if (!existsSync15(dir)) return;
6594
+ if (!existsSync16(dir)) return;
6343
6595
  const entries = readdirSync2(dir, { withFileTypes: true });
6344
6596
  for (const entry of entries) {
6345
6597
  if (entry.isDirectory()) {
@@ -6372,7 +6624,7 @@ function extractInternalLinks(projectPath) {
6372
6624
  }
6373
6625
  function getRecentCommits(projectPath, count = 20) {
6374
6626
  try {
6375
- const output = execSync6(
6627
+ const output = execSync7(
6376
6628
  `git log --oneline -${count} --no-decorate`,
6377
6629
  { cwd: projectPath, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
6378
6630
  );
@@ -6640,10 +6892,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
6640
6892
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
6641
6893
 
6642
6894
  **Components:**
6643
- ${codebaseAnalysis.components.slice(0, 15).map((c13) => `- ${c13}`).join("\n")}
6895
+ ${codebaseAnalysis.components.slice(0, 15).map((c14) => `- ${c14}`).join("\n")}
6644
6896
 
6645
6897
  **Recent Git Commits:**
6646
- ${codebaseAnalysis.recentCommits.slice(0, 8).map((c13) => `- ${c13}`).join("\n")}
6898
+ ${codebaseAnalysis.recentCommits.slice(0, 8).map((c14) => `- ${c14}`).join("\n")}
6647
6899
 
6648
6900
  **Completed Tasks:**
6649
6901
  ${context.completedTasks.slice(0, 10).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -6858,7 +7110,7 @@ async function fetchScanContext(opts) {
6858
7110
  }
6859
7111
  function runClaude2(prompt2) {
6860
7112
  return new Promise((resolve9, reject) => {
6861
- const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
7113
+ const child = spawn9("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
6862
7114
  stdio: ["ignore", "pipe", "pipe"]
6863
7115
  });
6864
7116
  let output = "";
@@ -6914,7 +7166,7 @@ function parseSynthesisOutput(output) {
6914
7166
  }
6915
7167
 
6916
7168
  // cli/commands/scan.ts
6917
- var c9 = {
7169
+ var c10 = {
6918
7170
  reset: "\x1B[0m",
6919
7171
  bold: "\x1B[1m",
6920
7172
  dim: "\x1B[2m",
@@ -6925,53 +7177,53 @@ var c9 = {
6925
7177
  magenta: "\x1B[35m",
6926
7178
  gray: "\x1B[90m"
6927
7179
  };
6928
- function paint9(color, text) {
6929
- return `${c9[color]}${text}${c9.reset}`;
7180
+ function paint10(color, text) {
7181
+ return `${c10[color]}${text}${c10.reset}`;
6930
7182
  }
6931
- function timestamp3() {
6932
- 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 }));
6933
7185
  }
6934
7186
  function scanTag() {
6935
- return paint9("magenta", "[scan]");
7187
+ return paint10("magenta", "[scan]");
6936
7188
  }
6937
- function log2(msg) {
6938
- console.log(`${timestamp3()} ${scanTag()} ${msg}`);
7189
+ function log3(msg) {
7190
+ console.log(`${timestamp4()} ${scanTag()} ${msg}`);
6939
7191
  }
6940
- function logOk2(msg) {
6941
- console.log(`${timestamp3()} ${scanTag()} ${paint9("green", "\u2713")} ${msg}`);
7192
+ function logOk3(msg) {
7193
+ console.log(`${timestamp4()} ${scanTag()} ${paint10("green", "\u2713")} ${msg}`);
6942
7194
  }
6943
- function logErr2(msg) {
6944
- console.error(`${timestamp3()} ${scanTag()} ${paint9("red", "\u2717")} ${msg}`);
7195
+ function logErr3(msg) {
7196
+ console.error(`${timestamp4()} ${scanTag()} ${paint10("red", "\u2717")} ${msg}`);
6945
7197
  }
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) => {
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) => {
6947
7199
  const config = loadConfig();
6948
7200
  if (!config.apiKey) {
6949
- logErr2('Not authenticated. Run "mr login" first.');
7201
+ logErr3('Not authenticated. Run "mr login" first.');
6950
7202
  process.exit(1);
6951
7203
  }
6952
7204
  const banner = [
6953
7205
  ``,
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`),
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`),
6959
7211
  ``
6960
7212
  ].join("\n");
6961
7213
  console.log(banner);
6962
7214
  const projectId = opts.project || getLinkedProjectId();
6963
7215
  if (!projectId) {
6964
- logErr2('No project linked. Run "mr link" or pass --project <id>.');
7216
+ logErr3('No project linked. Run "mr link" or pass --project <id>.');
6965
7217
  process.exit(1);
6966
7218
  }
6967
7219
  let project;
6968
7220
  try {
6969
7221
  project = await api.get(`/api/projects/${projectId}`);
6970
7222
  } catch {
6971
- logErr2(`Failed to fetch project ${projectId}`);
7223
+ logErr3(`Failed to fetch project ${projectId}`);
6972
7224
  process.exit(1);
6973
7225
  }
6974
- log2(`Scanning project: ${paint9("cyan", project.name)}`);
7226
+ log3(`Scanning project: ${paint10("cyan", project.name)}`);
6975
7227
  let projectPath = project.localPath;
6976
7228
  if (!projectPath) {
6977
7229
  for (const [dir, pid] of Object.entries(config.directories)) {
@@ -6988,7 +7240,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
6988
7240
  let customPrompt = typeof opts.prompt === "string" && opts.prompt.trim().length > 0 ? opts.prompt.trim() : null;
6989
7241
  if (opts.report) {
6990
7242
  reportId = opts.report;
6991
- log2(`Using existing scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7243
+ log3(`Using existing scan report ${paint10("yellow", reportId.slice(0, 8))}`);
6992
7244
  try {
6993
7245
  const existing = await api.get(`/api/scans/${reportId}`);
6994
7246
  if (!customPrompt && existing.customPrompt) {
@@ -7000,7 +7252,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7000
7252
  try {
7001
7253
  const scans = await api.get(`/api/scans?projectId=${projectId}&status=processing`);
7002
7254
  if (scans.length > 0) {
7003
- 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.");
7004
7256
  process.exit(1);
7005
7257
  }
7006
7258
  } catch {
@@ -7012,9 +7264,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7012
7264
  customPrompt
7013
7265
  });
7014
7266
  reportId = report.id;
7015
- log2(`Created scan report ${paint9("yellow", reportId.slice(0, 8))}`);
7267
+ log3(`Created scan report ${paint10("yellow", reportId.slice(0, 8))}`);
7016
7268
  } catch (err) {
7017
- logErr2(`Failed to create scan report: ${err.message}`);
7269
+ logErr3(`Failed to create scan report: ${err.message}`);
7018
7270
  process.exit(1);
7019
7271
  }
7020
7272
  }
@@ -7025,7 +7277,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7025
7277
  try {
7026
7278
  const current = await api.get(`/api/scans/${reportId}`);
7027
7279
  if (current.status === "cancelled") {
7028
- log2(paint9("yellow", "Scan was cancelled \u2014 aborting."));
7280
+ log3(paint10("yellow", "Scan was cancelled \u2014 aborting."));
7029
7281
  process.exit(0);
7030
7282
  }
7031
7283
  } catch {
@@ -7039,9 +7291,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7039
7291
  apiUrl: config.apiUrl,
7040
7292
  apiKey: config.apiKey,
7041
7293
  runBrowse: runBrowseCommand2,
7042
- onLog: log2,
7294
+ onLog: log3,
7043
7295
  onProgress: (phase, detail) => {
7044
- log2(`${paint9("dim", `[${phase}]`)} ${detail}`);
7296
+ log3(`${paint10("dim", `[${phase}]`)} ${detail}`);
7045
7297
  },
7046
7298
  customPrompt
7047
7299
  });
@@ -7054,7 +7306,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7054
7306
  } catch {
7055
7307
  }
7056
7308
  if (wasCancelled) {
7057
- log2(paint9("yellow", "Scan was cancelled by user \u2014 discarding results."));
7309
+ log3(paint10("yellow", "Scan was cancelled by user \u2014 discarding results."));
7058
7310
  process.exit(0);
7059
7311
  }
7060
7312
  await api.patch(`/api/scans/${reportId}`, {
@@ -7065,37 +7317,37 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7065
7317
  scanDurationMs: result.scanDurationMs,
7066
7318
  routesCrawled: result.routesCrawled
7067
7319
  });
7068
- logOk2(`Scan complete \u2014 ${paint9("cyan", String(result.findings.length))} findings`);
7320
+ logOk3(`Scan complete \u2014 ${paint10("cyan", String(result.findings.length))} findings`);
7069
7321
  console.log("");
7070
- console.log(` ${paint9("bold", "Summary:")} ${result.summary}`);
7322
+ console.log(` ${paint10("bold", "Summary:")} ${result.summary}`);
7071
7323
  console.log("");
7072
7324
  const high = result.findings.filter((f) => f.priority === "high");
7073
7325
  const medium = result.findings.filter((f) => f.priority === "medium");
7074
7326
  const low = result.findings.filter((f) => f.priority === "low");
7075
7327
  if (high.length > 0) {
7076
- console.log(` ${paint9("bold", paint9("red", `High Priority (${high.length})`))}`);
7328
+ console.log(` ${paint10("bold", paint10("red", `High Priority (${high.length})`))}`);
7077
7329
  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))}`);
7330
+ console.log(` ${paint10("red", "\u25CF")} [${f.type}] ${f.title}`);
7331
+ console.log(` ${paint10("dim", f.description.slice(0, 120))}`);
7080
7332
  }
7081
7333
  console.log("");
7082
7334
  }
7083
7335
  if (medium.length > 0) {
7084
- console.log(` ${paint9("bold", paint9("yellow", `Medium Priority (${medium.length})`))}`);
7336
+ console.log(` ${paint10("bold", paint10("yellow", `Medium Priority (${medium.length})`))}`);
7085
7337
  for (const f of medium) {
7086
- console.log(` ${paint9("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7338
+ console.log(` ${paint10("yellow", "\u25CF")} [${f.type}] ${f.title}`);
7087
7339
  }
7088
7340
  console.log("");
7089
7341
  }
7090
7342
  if (low.length > 0) {
7091
- console.log(` ${paint9("dim", `Low Priority (${low.length})`)} `);
7343
+ console.log(` ${paint10("dim", `Low Priority (${low.length})`)} `);
7092
7344
  for (const f of low) {
7093
- console.log(` ${paint9("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7345
+ console.log(` ${paint10("dim", `\u25CB [${f.type}] ${f.title}`)}`);
7094
7346
  }
7095
7347
  console.log("");
7096
7348
  }
7097
7349
  } catch (err) {
7098
- logErr2(`Scan failed: ${err.message}`);
7350
+ logErr3(`Scan failed: ${err.message}`);
7099
7351
  try {
7100
7352
  await api.patch(`/api/scans/${reportId}`, {
7101
7353
  status: "failed",
@@ -7108,13 +7360,13 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
7108
7360
  });
7109
7361
 
7110
7362
  // cli/commands/doctor.ts
7111
- import { Command as Command27 } from "commander";
7112
- import { existsSync as existsSync16 } from "fs";
7363
+ import { Command as Command28 } from "commander";
7364
+ import { existsSync as existsSync17 } from "fs";
7113
7365
  import { homedir as homedir2 } from "os";
7114
7366
  import { join as join11 } from "path";
7115
7367
  async function checkConfigExists() {
7116
7368
  const configPath2 = join11(homedir2(), ".mr-manager", "config.json");
7117
- const exists = existsSync16(configPath2);
7369
+ const exists = existsSync17(configPath2);
7118
7370
  if (!exists) {
7119
7371
  return {
7120
7372
  name: "Config file",
@@ -7156,7 +7408,7 @@ async function checkProjectLink() {
7156
7408
  optional: true
7157
7409
  };
7158
7410
  }
7159
- 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 () => {
7160
7412
  const banner = [
7161
7413
  ``,
7162
7414
  paint5("cyan", ` MR DOCTOR`),
@@ -7187,7 +7439,7 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7187
7439
  console.log("");
7188
7440
  return;
7189
7441
  }
7190
- const fixes = checks.filter((c13) => !c13.ok && c13.fix && !c13.optional);
7442
+ const fixes = checks.filter((c14) => !c14.ok && c14.fix && !c14.optional);
7191
7443
  if (fixes.length > 0) {
7192
7444
  console.log(paint5("yellow", " To fix:"));
7193
7445
  for (const fix of fixes) {
@@ -7199,14 +7451,14 @@ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CL
7199
7451
  });
7200
7452
 
7201
7453
  // cli/commands/prompt-audit.ts
7202
- import { Command as Command28 } from "commander";
7454
+ import { Command as Command29 } from "commander";
7203
7455
  import { resolve as resolve8 } from "path";
7204
- import { existsSync as existsSync17, readFileSync as readFileSync12 } from "fs";
7456
+ import { existsSync as existsSync18, readFileSync as readFileSync12 } from "fs";
7205
7457
  function auditLine(label, tokens) {
7206
7458
  const bar = "\u2588".repeat(Math.min(60, Math.round(tokens / 200)));
7207
7459
  return ` ${label.padEnd(30)} ${formatTokenCount(tokens).padStart(8)} ${bar}`;
7208
7460
  }
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) => {
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) => {
7210
7462
  const results = [];
7211
7463
  if (opts.task) {
7212
7464
  try {
@@ -7260,7 +7512,7 @@ ${task.notes}` : "";
7260
7512
  const repoDir = Object.entries(config.directories).find(([, pid]) => pid === task.projectId)?.[0];
7261
7513
  if (repoDir) {
7262
7514
  const featuresPath = resolve8(repoDir, ".mr-features.md");
7263
- if (existsSync17(featuresPath)) {
7515
+ if (existsSync18(featuresPath)) {
7264
7516
  const featuresContent = readFileSync12(featuresPath, "utf-8");
7265
7517
  sections.push({ name: "features-doc", tokens: estimateTokens(featuresContent) });
7266
7518
  }
@@ -7415,9 +7667,9 @@ ${r.jobType} [${r.identifier}]`);
7415
7667
  });
7416
7668
 
7417
7669
  // cli/commands/skill.ts
7418
- import { Command as Command29 } from "commander";
7670
+ import { Command as Command30 } from "commander";
7419
7671
  import { createInterface as createInterface3 } from "readline/promises";
7420
- var c10 = {
7672
+ var c11 = {
7421
7673
  reset: "\x1B[0m",
7422
7674
  bold: "\x1B[1m",
7423
7675
  dim: "\x1B[2m",
@@ -7450,7 +7702,7 @@ function formatSize(bytes) {
7450
7702
  if (bytes < 1024) return `${bytes}b`;
7451
7703
  return `${(bytes / 1024).toFixed(1)}kb`;
7452
7704
  }
7453
- 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");
7454
7706
  skillCommand.command("list").alias("ls").description("List all skills").option("--category <category>", "Filter by category").action(async (opts) => {
7455
7707
  const params = new URLSearchParams();
7456
7708
  if (opts.category) params.set("category", opts.category);
@@ -7458,17 +7710,17 @@ skillCommand.command("list").alias("ls").description("List all skills").option("
7458
7710
  `/api/skills${params.toString() ? `?${params}` : ""}`
7459
7711
  );
7460
7712
  if (skills.length === 0) {
7461
- console.log(`${c10.dim}No skills found.${c10.reset}`);
7713
+ console.log(`${c11.dim}No skills found.${c11.reset}`);
7462
7714
  return;
7463
7715
  }
7464
7716
  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}`);
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}`);
7468
7720
  if (skill.description) {
7469
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7721
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7470
7722
  }
7471
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7723
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7472
7724
  }
7473
7725
  });
7474
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) => {
@@ -7504,7 +7756,7 @@ skillCommand.command("create").description("Create a new skill from a markdown f
7504
7756
  projectId
7505
7757
  });
7506
7758
  console.log(
7507
- `${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}`
7508
7760
  );
7509
7761
  });
7510
7762
  skillCommand.command("update").description(
@@ -7565,14 +7817,14 @@ skillCommand.command("update").description(
7565
7817
  const beforeContent = existing.content ?? "";
7566
7818
  const afterContent = typeof patch.content === "string" ? patch.content : beforeContent;
7567
7819
  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}` : "";
7820
+ const sizeDiff = typeof patch.content === "string" ? ` ${c11.dim}(${formatSize(Buffer.byteLength(beforeContent, "utf-8"))} -> ${formatSize(Buffer.byteLength(afterContent, "utf-8"))})${c11.reset}` : "";
7569
7821
  const priorRevisions = existing.revisions?.length ?? 0;
7570
7822
  const revisionsAfter = priorRevisions + (contentChanged ? 1 : 0);
7571
- 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}` : "";
7572
7824
  console.log(
7573
- `${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}`
7574
7826
  );
7575
- console.log(` ${c10.dim}id: ${updated.id}${c10.reset}`);
7827
+ console.log(` ${c11.dim}id: ${updated.id}${c11.reset}`);
7576
7828
  });
7577
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) => {
7578
7830
  let skill;
@@ -7585,7 +7837,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7585
7837
  if (!opts.yes) {
7586
7838
  const rl = createInterface3({ input: process.stdin, output: process.stdout });
7587
7839
  const answer = (await rl.question(
7588
- `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] `
7589
7841
  )).trim().toLowerCase();
7590
7842
  rl.close();
7591
7843
  if (answer !== "y" && answer !== "yes") {
@@ -7595,7 +7847,7 @@ skillCommand.command("delete").alias("rm").description("Delete a skill").argumen
7595
7847
  }
7596
7848
  await api.del(`/api/skills/${skill.id}`);
7597
7849
  console.log(
7598
- `${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}`
7599
7851
  );
7600
7852
  });
7601
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) => {
@@ -7609,22 +7861,22 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7609
7861
  process.exit(1);
7610
7862
  }
7611
7863
  }
7612
- console.log(`${c10.dim}Generating skill...${c10.reset}`);
7864
+ console.log(`${c11.dim}Generating skill...${c11.reset}`);
7613
7865
  try {
7614
7866
  const skill = await api.post("/api/skills/generate", {
7615
7867
  prompt: prompt2,
7616
7868
  projectId
7617
7869
  });
7618
7870
  console.log(
7619
- `${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}`
7620
7872
  );
7621
7873
  if (skill.description) {
7622
- console.log(` ${c10.dim}${skill.description}${c10.reset}`);
7874
+ console.log(` ${c11.dim}${skill.description}${c11.reset}`);
7623
7875
  }
7624
7876
  if (skill.category) {
7625
- console.log(` ${c10.dim}Category: ${skill.category}${c10.reset}`);
7877
+ console.log(` ${c11.dim}Category: ${skill.category}${c11.reset}`);
7626
7878
  }
7627
- console.log(` ${c10.dim}id: ${skill.id}${c10.reset}`);
7879
+ console.log(` ${c11.dim}id: ${skill.id}${c11.reset}`);
7628
7880
  } catch (err) {
7629
7881
  console.error(`Failed to generate skill: ${err.message}`);
7630
7882
  process.exit(1);
@@ -7632,8 +7884,8 @@ skillCommand.command("generate").alias("gen").description("Generate a new skill
7632
7884
  });
7633
7885
 
7634
7886
  // cli/commands/resource.ts
7635
- import { Command as Command30 } from "commander";
7636
- var c11 = {
7887
+ import { Command as Command31 } from "commander";
7888
+ var c12 = {
7637
7889
  reset: "\x1B[0m",
7638
7890
  bold: "\x1B[1m",
7639
7891
  dim: "\x1B[2m",
@@ -7643,16 +7895,16 @@ var c11 = {
7643
7895
  magenta: "\x1B[35m"
7644
7896
  };
7645
7897
  var TYPE_COLORS = {
7646
- plan: c11.cyan,
7647
- research: c11.magenta,
7648
- "test-plan": c11.yellow,
7649
- note: c11.green
7898
+ plan: c12.cyan,
7899
+ research: c12.magenta,
7900
+ "test-plan": c12.yellow,
7901
+ note: c12.green
7650
7902
  };
7651
7903
  function typeLabel(type) {
7652
- const color = TYPE_COLORS[type] ?? c11.dim;
7653
- return `${color}${type}${c11.reset}`;
7904
+ const color = TYPE_COLORS[type] ?? c12.dim;
7905
+ return `${color}${type}${c12.reset}`;
7654
7906
  }
7655
- 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");
7656
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) => {
7657
7909
  const params = new URLSearchParams();
7658
7910
  if (opts.all) {
@@ -7665,13 +7917,13 @@ resourceCommand.command("list").alias("ls").description("List resources for the
7665
7917
  `/api/resources${params.toString() ? `?${params}` : ""}`
7666
7918
  );
7667
7919
  if (resources.length === 0) {
7668
- console.log(`${c11.dim}No resources found.${c11.reset}`);
7920
+ console.log(`${c12.dim}No resources found.${c12.reset}`);
7669
7921
  return;
7670
7922
  }
7671
7923
  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}`);
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}`);
7675
7927
  }
7676
7928
  });
7677
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) => {
@@ -7696,9 +7948,9 @@ resourceCommand.command("create").description("Create a new resource from a file
7696
7948
  content: content.trim()
7697
7949
  });
7698
7950
  console.log(
7699
- `${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}`
7700
7952
  );
7701
- console.log(` ${c11.dim}Attached to task ${opts.task}${c11.reset}`);
7953
+ console.log(` ${c12.dim}Attached to task ${opts.task}${c12.reset}`);
7702
7954
  return;
7703
7955
  }
7704
7956
  let projectId;
@@ -7718,7 +7970,7 @@ resourceCommand.command("create").description("Create a new resource from a file
7718
7970
  projectId
7719
7971
  });
7720
7972
  console.log(
7721
- `${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}`
7722
7974
  );
7723
7975
  });
7724
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) => {
@@ -7737,30 +7989,30 @@ resourceCommand.command("generate").alias("gen").description("Generate a resourc
7737
7989
  process.exit(1);
7738
7990
  }
7739
7991
  }
7740
- console.log(`${c11.dim}Generating ${type}...${c11.reset}`);
7992
+ console.log(`${c12.dim}Generating ${type}...${c12.reset}`);
7741
7993
  const result = await api.post("/api/resources", {
7742
7994
  type,
7743
7995
  prompt: prompt2,
7744
7996
  projectId
7745
7997
  });
7746
7998
  console.log(
7747
- `${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}`
7748
8000
  );
7749
- console.log(` ${c11.dim}task: ${result.task.id}${c11.reset}`);
8001
+ console.log(` ${c12.dim}task: ${result.task.id}${c12.reset}`);
7750
8002
  });
7751
8003
 
7752
8004
  // cli/commands/tests.ts
7753
- import { Command as Command31 } from "commander";
7754
- var c12 = {
8005
+ import { Command as Command32 } from "commander";
8006
+ var c13 = {
7755
8007
  reset: "\x1B[0m",
7756
8008
  dim: "\x1B[2m",
7757
8009
  yellow: "\x1B[33m"
7758
8010
  };
7759
- 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 () => {
7760
8012
  const projectId = getLinkedProjectId();
7761
8013
  if (!projectId) {
7762
8014
  console.error(
7763
- `${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.`
7764
8016
  );
7765
8017
  process.exit(1);
7766
8018
  }
@@ -7773,13 +8025,13 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7773
8025
  process.exit(1);
7774
8026
  }
7775
8027
  if (scenarios.length === 0) {
7776
- 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}`);
7777
8029
  return;
7778
8030
  }
7779
8031
  for (const scenario of scenarios) {
7780
8032
  console.log(`### ${scenario.name}`);
7781
8033
  if (scenario.description) {
7782
- console.log(`${c12.dim}${scenario.description}${c12.reset}`);
8034
+ console.log(`${c13.dim}${scenario.description}${c13.reset}`);
7783
8035
  console.log();
7784
8036
  }
7785
8037
  console.log(scenario.content);
@@ -7788,9 +8040,9 @@ var testsCommand = new Command31("tests").description("List MR Test scenarios fo
7788
8040
  });
7789
8041
 
7790
8042
  // cli/commands/walkthrough.ts
7791
- import { Command as Command32 } from "commander";
8043
+ import { Command as Command33 } from "commander";
7792
8044
  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) => {
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) => {
7794
8046
  const skills = await api.get("/api/skills");
7795
8047
  const skill = skills.find((s) => s.name === SKILL_NAME);
7796
8048
  if (!skill) {
@@ -7818,12 +8070,12 @@ var walkthroughCommand = new Command32("walkthrough").description("Attach the Ge
7818
8070
 
7819
8071
  // cli/index.ts
7820
8072
  var configPath = join12(homedir3(), ".mr-manager", "config.json");
7821
- var isFirstRun = !existsSync18(configPath);
8073
+ var isFirstRun = !existsSync19(configPath);
7822
8074
  var userArgs = process.argv.slice(2);
7823
8075
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
7824
8076
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
7825
8077
  if (isFirstRun && !shouldBypass) {
7826
- const c13 = {
8078
+ const c14 = {
7827
8079
  reset: "\x1B[0m",
7828
8080
  bold: "\x1B[1m",
7829
8081
  dim: "\x1B[2m",
@@ -7833,28 +8085,28 @@ if (isFirstRun && !shouldBypass) {
7833
8085
  magenta: "\x1B[35m"
7834
8086
  };
7835
8087
  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}`);
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}`);
7840
8092
  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}`);
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}`);
7843
8095
  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}`);
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}`);
7846
8098
  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}`);
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}`);
7849
8101
  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}`);
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}`);
7852
8104
  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}`);
8105
+ console.log(`${c14.dim} Or run ${c14.reset}${c14.cyan}mr login${c14.reset}${c14.dim} to get started now.${c14.reset}`);
7854
8106
  console.log("");
7855
8107
  process.exit(0);
7856
8108
  }
7857
- var program = new Command33();
8109
+ var program = new Command34();
7858
8110
  program.name("mr").description("Mr. Manager - Task and project management CLI").version(CLI_VERSION);
7859
8111
  program.addCommand(initCommand);
7860
8112
  program.addCommand(authCommand);