@dunnewold-labs/mr-manager 0.2.0 → 0.4.0

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 +823 -1025
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // cli/index.ts
4
- import { Command as Command29 } from "commander";
4
+ import { Command as Command28 } from "commander";
5
5
  import { existsSync as existsSync15 } from "fs";
6
6
  import { homedir as homedir4 } from "os";
7
7
  import { join as join13 } from "path";
@@ -255,11 +255,9 @@ var tasksCommand = new Command5("tasks").description("List tasks for the linked
255
255
  const params = new URLSearchParams();
256
256
  params.set("projectId", projectId);
257
257
  if (opts.inProgress) {
258
- params.set("inProgress", "true");
258
+ params.set("status", "in_progress");
259
259
  } else if (opts.completed) {
260
- params.set("completed", "true");
261
- } else if (!opts.all) {
262
- params.set("completed", "false");
260
+ params.set("status", "completed");
263
261
  }
264
262
  path += `?${params.toString()}`;
265
263
  const tasks = await api.get(path);
@@ -409,9 +407,9 @@ var contextCommand = new Command7("context").description("Output project context
409
407
  const allTasks = await api.get(
410
408
  `/api/projects/${projectId}/tasks`
411
409
  );
412
- const inProgress = allTasks.filter((t) => t.inProgress && !t.completed);
413
- const todo = allTasks.filter((t) => !t.completed && !t.inProgress);
414
- const recentlyCompleted = allTasks.filter((t) => t.completed).slice(0, 10);
410
+ const inProgress = allTasks.filter((t) => t.status === "in_progress" || t.status === "queued" || t.status === "delegated" || t.status === "review");
411
+ const todo = allTasks.filter((t) => t.status === "todo");
412
+ const recentlyCompleted = allTasks.filter((t) => t.status === "completed").slice(0, 10);
415
413
  const summaryLines = [
416
414
  `Project: ${project.name} (${project.status})`,
417
415
  ...inProgress.map((t) => `In Progress: ${t.title}`),
@@ -698,7 +696,7 @@ async function runTest(options) {
698
696
  postUpdate,
699
697
  onProgress
700
698
  } = options;
701
- const log4 = onProgress || (() => {
699
+ const log3 = onProgress || (() => {
702
700
  });
703
701
  const result = {
704
702
  status: "passed",
@@ -711,37 +709,37 @@ async function runTest(options) {
711
709
  let wtPath = null;
712
710
  const worktreeName = `mr-test-${taskId.slice(0, 8)}`;
713
711
  const timeoutHandle = setTimeout(() => {
714
- log4("Test timed out after 5 minutes");
712
+ log3("Test timed out after 5 minutes");
715
713
  if (devProc) devProc.kill("SIGTERM");
716
714
  }, 5 * 60 * 1e3);
717
715
  try {
718
- log4("Extracting branch from MR/PR link...");
716
+ log3("Extracting branch from MR/PR link...");
719
717
  const branch = extractBranchFromLink(taskLink, localPath);
720
718
  if (!branch) {
721
719
  throw new Error(`Could not extract branch from link: ${taskLink}`);
722
720
  }
723
- log4(`Branch: ${branch}`);
724
- log4("Creating git worktree...");
721
+ log3(`Branch: ${branch}`);
722
+ log3("Creating git worktree...");
725
723
  wtPath = createWorktree(localPath, branch, worktreeName);
726
- log4(`Worktree created at ${wtPath}`);
727
- log4("Installing dependencies...");
724
+ log3(`Worktree created at ${wtPath}`);
725
+ log3("Installing dependencies...");
728
726
  try {
729
727
  installDependencies(wtPath);
730
728
  } catch (err) {
731
- log4(`Warning: dependency install failed: ${err.message}`);
729
+ log3(`Warning: dependency install failed: ${err.message}`);
732
730
  }
733
- log4("Starting dev server...");
731
+ log3("Starting dev server...");
734
732
  const port = await findAvailablePort(4e3);
735
733
  devProc = await startDevServer(wtPath, port);
736
734
  const baseUrl = `http://127.0.0.1:${port}`;
737
- log4(`Dev server running on ${baseUrl}`);
735
+ log3(`Dev server running on ${baseUrl}`);
738
736
  await browseRunner(["goto", baseUrl]);
739
737
  const plan = customPlan || buildDefaultTestPlan(baseUrl);
740
- log4(`Executing ${plan.length}-step test plan...`);
738
+ log3(`Executing ${plan.length}-step test plan...`);
741
739
  for (let i = 0; i < plan.length; i++) {
742
740
  const step = plan[i];
743
741
  const stepDesc = step.description || `${step.command} ${(step.args || []).join(" ")}`;
744
- log4(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
742
+ log3(`Step ${i + 1}/${plan.length}: ${stepDesc}`);
745
743
  try {
746
744
  if (step.command.startsWith("assert")) {
747
745
  const assertResult = await evaluateAssertion(step, i, browseRunner);
@@ -791,7 +789,7 @@ async function runTest(options) {
791
789
  }
792
790
  } catch (err) {
793
791
  result.errors.push(`Step ${i + 1} (${step.command}): ${err.message}`);
794
- log4(`Step ${i + 1} error: ${err.message}`);
792
+ log3(`Step ${i + 1} error: ${err.message}`);
795
793
  }
796
794
  }
797
795
  const totalAssertions = result.assertions.length;
@@ -1908,6 +1906,8 @@ var watchCommand = new Command8("watch").description(
1908
1906
  const active = /* @__PURE__ */ new Map();
1909
1907
  const failed = /* @__PURE__ */ new Map();
1910
1908
  const queued = /* @__PURE__ */ new Set();
1909
+ const finishing = /* @__PURE__ */ new Set();
1910
+ let pollRunning = false;
1911
1911
  const approvalQueue = [];
1912
1912
  let approvalRunning = false;
1913
1913
  const flags = [
@@ -1992,32 +1992,94 @@ var watchCommand = new Command8("watch").description(
1992
1992
  active.set(task.id, { process: child, title: task.title, repoDir });
1993
1993
  child.on("exit", async (code) => {
1994
1994
  active.delete(task.id);
1995
- queued.delete(task.id);
1996
- if (code === 0) {
1997
- try {
1998
- const researchPath = resolve(repoDir, "research.md");
1999
- if (existsSync6(researchPath)) {
2000
- try {
2001
- const researchContent = readFileSync5(researchPath, "utf-8");
2002
- const existingResearch = existingResources.find((r) => r.type === "research");
2003
- if (existingResearch) {
2004
- await api.patch(`/api/tasks/${task.id}/resources/${existingResearch.id}`, {
2005
- content: researchContent
2006
- });
2007
- logSuccess(prefix, `Updated existing research resource`);
1995
+ finishing.add(task.id);
1996
+ try {
1997
+ if (code === 0) {
1998
+ try {
1999
+ const researchPath = resolve(repoDir, "research.md");
2000
+ if (existsSync6(researchPath)) {
2001
+ try {
2002
+ const researchContent = readFileSync5(researchPath, "utf-8");
2003
+ const existingResearch = existingResources.find((r) => r.type === "research");
2004
+ if (existingResearch) {
2005
+ await api.patch(`/api/tasks/${task.id}/resources/${existingResearch.id}`, {
2006
+ content: researchContent
2007
+ });
2008
+ logSuccess(prefix, `Updated existing research resource`);
2009
+ } else {
2010
+ await api.post(`/api/tasks/${task.id}/resources`, {
2011
+ type: "research",
2012
+ title: `Research \u2014 ${task.title}`,
2013
+ content: researchContent
2014
+ });
2015
+ logSuccess(prefix, `Uploaded research.md as task resource`);
2016
+ }
2017
+ unlinkSync(researchPath);
2018
+ } catch (err) {
2019
+ logWarn(prefix, `Failed to upload research resource: ${err.message}`);
2020
+ }
2021
+ }
2022
+ if (sessionId) {
2023
+ try {
2024
+ const sessionEntries = readAndParseSessionLog(sessionId, repoDir);
2025
+ if (sessionEntries && sessionEntries.length > 0) {
2026
+ const sessionContent = JSON.stringify(sessionEntries);
2027
+ const existingSessionLog = existingResources.find((r) => r.type === "session-log");
2028
+ if (existingSessionLog) {
2029
+ await api.patch(`/api/tasks/${task.id}/resources/${existingSessionLog.id}`, {
2030
+ content: sessionContent
2031
+ });
2032
+ logSuccess(prefix, `Updated session log resource (${sessionEntries.length} messages)`);
2033
+ } else {
2034
+ await api.post(`/api/tasks/${task.id}/resources`, {
2035
+ type: "session-log",
2036
+ title: `Session Log \u2014 ${task.title}`,
2037
+ content: sessionContent
2038
+ });
2039
+ logSuccess(prefix, `Uploaded session log (${sessionEntries.length} messages)`);
2040
+ }
2041
+ }
2042
+ } catch (err) {
2043
+ logWarn(prefix, `Failed to upload session log: ${err.message}`);
2044
+ }
2045
+ }
2046
+ const noMrPath = resolve(repoDir, ".mr-no-mr");
2047
+ const noMrRequested = existsSync6(noMrPath);
2048
+ let noMrDescription;
2049
+ if (noMrRequested) {
2050
+ noMrDescription = readFileSync5(noMrPath, "utf-8").trim();
2051
+ unlinkSync(noMrPath);
2052
+ logSuccess(prefix, `No ${vcs === "gitlab" ? "MR" : "PR"} needed \u2014 ${noMrDescription}`);
2053
+ }
2054
+ const prLabel = vcs === "gitlab" ? "MR" : "PR";
2055
+ let prUrl = null;
2056
+ if (!noMrRequested) {
2057
+ prUrl = await findPrUrlAcrossRepos(branchName, repoDir, vcs);
2058
+ if (!prUrl) {
2059
+ prUrl = await extractPrUrlFromUpdates(task.id);
2060
+ if (prUrl) {
2061
+ logInfo(prefix, `Found ${prLabel} URL from agent updates: ${paint("cyan", prUrl)}`);
2062
+ }
2063
+ }
2064
+ if (prUrl) {
2065
+ logSuccess(prefix, `${prLabel} ready: ${paint("cyan", prUrl)}`);
2008
2066
  } else {
2009
- await api.post(`/api/tasks/${task.id}/resources`, {
2010
- type: "research",
2011
- title: `Research \u2014 ${task.title}`,
2012
- content: researchContent
2013
- });
2014
- logSuccess(prefix, `Uploaded research.md as task resource`);
2067
+ logWarn(prefix, `No ${prLabel} found for branch ${paint("cyan", branchName)}`);
2068
+ await postTaskUpdate(task.id, `Agent finished \u2014 no ${prLabel} found for branch ${branchName}`, "system");
2015
2069
  }
2016
- unlinkSync(researchPath);
2017
- } catch (err) {
2018
- logWarn(prefix, `Failed to upload research resource: ${err.message}`);
2019
2070
  }
2071
+ await api.patch(`/api/tasks/${task.id}`, {
2072
+ status: "review",
2073
+ ...prUrl ? { link: prUrl } : {}
2074
+ });
2075
+ logSuccess(prefix, `"${paint("bold", task.title)}" marked ready for review`);
2076
+ await postTaskUpdate(task.id, noMrRequested ? `Task marked ready for review \u2014 no ${prLabel} needed: ${noMrDescription}` : "Task marked ready for review", "system");
2077
+ } catch (err) {
2078
+ logError(prefix, `Failed to update task: ${err.message}`);
2020
2079
  }
2080
+ } else {
2081
+ logError(prefix, `"${paint("bold", task.title)}" failed (exit ${code}), leaving status unchanged`);
2082
+ await postTaskUpdate(task.id, `Agent failed with exit code ${code}`, "system");
2021
2083
  if (sessionId) {
2022
2084
  try {
2023
2085
  const sessionEntries = readAndParseSessionLog(sessionId, repoDir);
@@ -2028,80 +2090,22 @@ var watchCommand = new Command8("watch").description(
2028
2090
  await api.patch(`/api/tasks/${task.id}/resources/${existingSessionLog.id}`, {
2029
2091
  content: sessionContent
2030
2092
  });
2031
- logSuccess(prefix, `Updated session log resource (${sessionEntries.length} messages)`);
2032
2093
  } else {
2033
2094
  await api.post(`/api/tasks/${task.id}/resources`, {
2034
2095
  type: "session-log",
2035
2096
  title: `Session Log \u2014 ${task.title}`,
2036
2097
  content: sessionContent
2037
2098
  });
2038
- logSuccess(prefix, `Uploaded session log (${sessionEntries.length} messages)`);
2039
2099
  }
2100
+ logInfo(prefix, `Uploaded session log for failed task (${sessionEntries.length} messages)`);
2040
2101
  }
2041
- } catch (err) {
2042
- logWarn(prefix, `Failed to upload session log: ${err.message}`);
2043
- }
2044
- }
2045
- const noMrPath = resolve(repoDir, ".mr-no-mr");
2046
- const noMrRequested = existsSync6(noMrPath);
2047
- let noMrDescription;
2048
- if (noMrRequested) {
2049
- noMrDescription = readFileSync5(noMrPath, "utf-8").trim();
2050
- unlinkSync(noMrPath);
2051
- logSuccess(prefix, `No ${vcs === "gitlab" ? "MR" : "PR"} needed \u2014 ${noMrDescription}`);
2052
- }
2053
- const prLabel = vcs === "gitlab" ? "MR" : "PR";
2054
- let prUrl = null;
2055
- if (!noMrRequested) {
2056
- prUrl = await findPrUrlAcrossRepos(branchName, repoDir, vcs);
2057
- if (!prUrl) {
2058
- prUrl = await extractPrUrlFromUpdates(task.id);
2059
- if (prUrl) {
2060
- logInfo(prefix, `Found ${prLabel} URL from agent updates: ${paint("cyan", prUrl)}`);
2061
- }
2062
- }
2063
- if (prUrl) {
2064
- logSuccess(prefix, `${prLabel} ready: ${paint("cyan", prUrl)}`);
2065
- } else {
2066
- logWarn(prefix, `No ${prLabel} found for branch ${paint("cyan", branchName)}`);
2067
- await postTaskUpdate(task.id, `Agent finished \u2014 no ${prLabel} found for branch ${branchName}`, "system");
2102
+ } catch {
2068
2103
  }
2069
2104
  }
2070
- await api.patch(`/api/tasks/${task.id}`, {
2071
- inProgress: false,
2072
- readyForReview: true,
2073
- ...prUrl ? { link: prUrl } : {}
2074
- });
2075
- logSuccess(prefix, `"${paint("bold", task.title)}" marked ready for review`);
2076
- await postTaskUpdate(task.id, noMrRequested ? `Task marked ready for review \u2014 no ${prLabel} needed: ${noMrDescription}` : "Task marked ready for review", "system");
2077
- } catch (err) {
2078
- logError(prefix, `Failed to update task: ${err.message}`);
2079
- }
2080
- } else {
2081
- logError(prefix, `"${paint("bold", task.title)}" failed (exit ${code}), leaving status unchanged`);
2082
- await postTaskUpdate(task.id, `Agent failed with exit code ${code}`, "system");
2083
- if (sessionId) {
2084
- try {
2085
- const sessionEntries = readAndParseSessionLog(sessionId, repoDir);
2086
- if (sessionEntries && sessionEntries.length > 0) {
2087
- const sessionContent = JSON.stringify(sessionEntries);
2088
- const existingSessionLog = existingResources.find((r) => r.type === "session-log");
2089
- if (existingSessionLog) {
2090
- await api.patch(`/api/tasks/${task.id}/resources/${existingSessionLog.id}`, {
2091
- content: sessionContent
2092
- });
2093
- } else {
2094
- await api.post(`/api/tasks/${task.id}/resources`, {
2095
- type: "session-log",
2096
- title: `Session Log \u2014 ${task.title}`,
2097
- content: sessionContent
2098
- });
2099
- }
2100
- logInfo(prefix, `Uploaded session log for failed task (${sessionEntries.length} messages)`);
2101
- }
2102
- } catch {
2103
- }
2104
2105
  }
2106
+ } finally {
2107
+ queued.delete(task.id);
2108
+ finishing.delete(task.id);
2105
2109
  }
2106
2110
  });
2107
2111
  }
@@ -2138,50 +2142,54 @@ var watchCommand = new Command8("watch").description(
2138
2142
  active.set(task.id, { process: child, title: task.title, repoDir });
2139
2143
  child.on("exit", async (code) => {
2140
2144
  active.delete(task.id);
2141
- queued.delete(task.id);
2142
- if (code === 0) {
2143
- try {
2144
- const prdPath = resolve(repoDir, "prd.md");
2145
- let prdContent;
2145
+ finishing.add(task.id);
2146
+ try {
2147
+ if (code === 0) {
2146
2148
  try {
2147
- prdContent = readFileSync5(prdPath, "utf-8");
2148
- logInfo(prefix, `Read PRD from ${paint("cyan", "prd.md")} (${prdContent.length} chars)`);
2149
- unlinkSync(prdPath);
2150
- } catch {
2151
- logWarn(prefix, `No prd.md file found in ${repoDir} \u2014 PRD may have been posted inline`);
2152
- }
2153
- if (prdContent) {
2149
+ const prdPath = resolve(repoDir, "prd.md");
2150
+ let prdContent;
2154
2151
  try {
2155
- if (existingPlanResource) {
2156
- await api.patch(`/api/tasks/${task.id}/resources/${existingPlanResource.id}`, {
2157
- content: prdContent
2158
- });
2159
- logSuccess(prefix, `Updated existing PRD resource`);
2160
- } else {
2161
- await api.post(`/api/tasks/${task.id}/resources`, {
2162
- type: "plan",
2163
- title: `PRD \u2014 ${task.title}`,
2164
- content: prdContent
2165
- });
2166
- logSuccess(prefix, `Uploaded PRD as task resource`);
2152
+ prdContent = readFileSync5(prdPath, "utf-8");
2153
+ logInfo(prefix, `Read PRD from ${paint("cyan", "prd.md")} (${prdContent.length} chars)`);
2154
+ unlinkSync(prdPath);
2155
+ } catch {
2156
+ logWarn(prefix, `No prd.md file found in ${repoDir} \u2014 PRD may have been posted inline`);
2157
+ }
2158
+ if (prdContent) {
2159
+ try {
2160
+ if (existingPlanResource) {
2161
+ await api.patch(`/api/tasks/${task.id}/resources/${existingPlanResource.id}`, {
2162
+ content: prdContent
2163
+ });
2164
+ logSuccess(prefix, `Updated existing PRD resource`);
2165
+ } else {
2166
+ await api.post(`/api/tasks/${task.id}/resources`, {
2167
+ type: "plan",
2168
+ title: `PRD \u2014 ${task.title}`,
2169
+ content: prdContent
2170
+ });
2171
+ logSuccess(prefix, `Uploaded PRD as task resource`);
2172
+ }
2173
+ } catch (err) {
2174
+ logWarn(prefix, `Failed to upload PRD resource: ${err.message}`);
2167
2175
  }
2168
- } catch (err) {
2169
- logWarn(prefix, `Failed to upload PRD resource: ${err.message}`);
2170
2176
  }
2177
+ await api.patch(`/api/tasks/${task.id}`, {
2178
+ status: "review",
2179
+ ...prdContent ? { prdContent } : {}
2180
+ });
2181
+ logSuccess(prefix, `"${paint("bold", task.title)}" PRD generated and marked ready for review`);
2182
+ await postTaskUpdate(task.id, "PRD generated \u2014 ready for review", "system");
2183
+ } catch (err) {
2184
+ logError(prefix, `Failed to update task: ${err.message}`);
2171
2185
  }
2172
- await api.patch(`/api/tasks/${task.id}`, {
2173
- inProgress: false,
2174
- readyForReview: true,
2175
- ...prdContent ? { prdContent } : {}
2176
- });
2177
- logSuccess(prefix, `"${paint("bold", task.title)}" PRD generated and marked ready for review`);
2178
- await postTaskUpdate(task.id, "PRD generated \u2014 ready for review", "system");
2179
- } catch (err) {
2180
- logError(prefix, `Failed to update task: ${err.message}`);
2186
+ } else {
2187
+ logError(prefix, `"${paint("bold", task.title)}" PRD generation failed (exit ${code}), leaving status unchanged`);
2188
+ await postTaskUpdate(task.id, `PRD generation failed with exit code ${code}`, "system");
2181
2189
  }
2182
- } else {
2183
- logError(prefix, `"${paint("bold", task.title)}" PRD generation failed (exit ${code}), leaving status unchanged`);
2184
- await postTaskUpdate(task.id, `PRD generation failed with exit code ${code}`, "system");
2190
+ } finally {
2191
+ queued.delete(task.id);
2192
+ finishing.delete(task.id);
2185
2193
  }
2186
2194
  });
2187
2195
  }
@@ -2212,42 +2220,48 @@ var watchCommand = new Command8("watch").description(
2212
2220
  const child = spawnAgent(agent, repoDir, prompt2, prefix, void 0, proto.title);
2213
2221
  active.set(`proto-${proto.id}`, { process: child, title: proto.title, repoDir });
2214
2222
  child.on("exit", async (code) => {
2215
- active.delete(`proto-${proto.id}`);
2216
- queued.delete(`proto-${proto.id}`);
2217
- if (code === 0) {
2218
- try {
2219
- const protoPattern = /^prototype-\d+\.html$/;
2220
- const found = readdirSync(repoDir).filter((f) => protoPattern.test(f)).sort();
2221
- const files = found.map((f) => ({
2222
- name: f,
2223
- content: readFileSync5(resolve(repoDir, f), "utf-8")
2224
- }));
2225
- if (files.length === 0) {
2226
- logError(prefix, `No prototype HTML files found in ${repoDir}`);
2227
- await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
2228
- return;
2229
- }
2230
- await api.patch(`/api/prototypes/${proto.id}`, { status: "completed", files });
2231
- logSuccess(prefix, `"${paint("bold", proto.title)}" uploaded ${files.length} file(s)`);
2232
- for (const file of files) {
2223
+ const key = `proto-${proto.id}`;
2224
+ active.delete(key);
2225
+ finishing.add(key);
2226
+ try {
2227
+ if (code === 0) {
2228
+ try {
2229
+ const protoPattern = /^prototype-\d+\.html$/;
2230
+ const found = readdirSync(repoDir).filter((f) => protoPattern.test(f)).sort();
2231
+ const files = found.map((f) => ({
2232
+ name: f,
2233
+ content: readFileSync5(resolve(repoDir, f), "utf-8")
2234
+ }));
2235
+ if (files.length === 0) {
2236
+ logError(prefix, `No prototype HTML files found in ${repoDir}`);
2237
+ await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
2238
+ return;
2239
+ }
2240
+ await api.patch(`/api/prototypes/${proto.id}`, { status: "completed", files });
2241
+ logSuccess(prefix, `"${paint("bold", proto.title)}" uploaded ${files.length} file(s)`);
2242
+ for (const file of files) {
2243
+ try {
2244
+ unlinkSync(resolve(repoDir, file.name));
2245
+ } catch {
2246
+ }
2247
+ }
2248
+ } catch (err) {
2249
+ logError(prefix, `Failed to upload prototype: ${err.message}`);
2233
2250
  try {
2234
- unlinkSync(resolve(repoDir, file.name));
2251
+ await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
2235
2252
  } catch {
2236
2253
  }
2237
2254
  }
2238
- } catch (err) {
2239
- logError(prefix, `Failed to upload prototype: ${err.message}`);
2255
+ } else {
2256
+ logError(prefix, `"${paint("bold", proto.title)}" prototype failed (exit ${code})`);
2240
2257
  try {
2241
2258
  await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
2242
2259
  } catch {
2243
2260
  }
2244
2261
  }
2245
- } else {
2246
- logError(prefix, `"${paint("bold", proto.title)}" prototype failed (exit ${code})`);
2247
- try {
2248
- await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
2249
- } catch {
2250
- }
2262
+ } finally {
2263
+ queued.delete(key);
2264
+ finishing.delete(key);
2251
2265
  }
2252
2266
  });
2253
2267
  }
@@ -2263,16 +2277,22 @@ var watchCommand = new Command8("watch").description(
2263
2277
  const child = spawnAgent(agent, workDir, prompt2, prefix, void 0, project.name);
2264
2278
  active.set(`repo-${project.id}`, { process: child, title: project.name, repoDir: workDir });
2265
2279
  child.on("exit", async (code) => {
2266
- active.delete(`repo-${project.id}`);
2267
- queued.delete(`repo-${project.id}`);
2268
- if (code === 0) {
2269
- logSuccess(prefix, `"${paint("bold", project.name)}" repo creation complete`);
2270
- } else {
2271
- logError(prefix, `"${paint("bold", project.name)}" repo creation failed (exit ${code})`);
2272
- try {
2273
- await api.patch(`/api/projects/${project.id}`, { repoCreationStatus: "failed" });
2274
- } catch {
2280
+ const key = `repo-${project.id}`;
2281
+ active.delete(key);
2282
+ finishing.add(key);
2283
+ try {
2284
+ if (code === 0) {
2285
+ logSuccess(prefix, `"${paint("bold", project.name)}" repo creation complete`);
2286
+ } else {
2287
+ logError(prefix, `"${paint("bold", project.name)}" repo creation failed (exit ${code})`);
2288
+ try {
2289
+ await api.patch(`/api/projects/${project.id}`, { repoCreationStatus: "failed" });
2290
+ } catch {
2291
+ }
2275
2292
  }
2293
+ } finally {
2294
+ queued.delete(key);
2295
+ finishing.delete(key);
2276
2296
  }
2277
2297
  });
2278
2298
  }
@@ -2290,85 +2310,91 @@ var watchCommand = new Command8("watch").description(
2290
2310
  const child = spawnAgent(agent, repoDir, prompt2, prefix, void 0, idea.title);
2291
2311
  active.set(`idea-${idea.id}`, { process: child, title: idea.title, repoDir });
2292
2312
  child.on("exit", async (code) => {
2293
- active.delete(`idea-${idea.id}`);
2294
- queued.delete(`idea-${idea.id}`);
2295
- if (code === 0) {
2296
- try {
2297
- let plan;
2298
- let protoHtml;
2299
- let followUpTasks;
2300
- const planPath = resolve(repoDir, "idea-plan.md");
2301
- const tasksPath = resolve(repoDir, "idea-tasks.json");
2302
- const protoPath = resolve(repoDir, "idea-prototype.html");
2303
- try {
2304
- plan = readFileSync5(planPath, "utf-8");
2305
- } catch {
2306
- }
2307
- try {
2308
- protoHtml = readFileSync5(protoPath, "utf-8");
2309
- } catch {
2310
- }
2313
+ const key = `idea-${idea.id}`;
2314
+ active.delete(key);
2315
+ finishing.add(key);
2316
+ try {
2317
+ if (code === 0) {
2311
2318
  try {
2312
- const tasksRaw = readFileSync5(tasksPath, "utf-8");
2313
- const parsed = JSON.parse(tasksRaw);
2314
- if (Array.isArray(parsed)) {
2315
- followUpTasks = parsed.filter((t) => t && typeof t === "object" && "title" in t);
2319
+ let plan;
2320
+ let protoHtml;
2321
+ let followUpTasks;
2322
+ const planPath = resolve(repoDir, "idea-plan.md");
2323
+ const tasksPath = resolve(repoDir, "idea-tasks.json");
2324
+ const protoPath = resolve(repoDir, "idea-prototype.html");
2325
+ try {
2326
+ plan = readFileSync5(planPath, "utf-8");
2327
+ } catch {
2316
2328
  }
2317
- } catch {
2318
- }
2319
- if (!plan && !protoHtml) {
2320
- logError(prefix, `No output files found in ${repoDir}`);
2321
- await api.patch(`/api/ideas/${idea.id}`, { status: "draft" });
2322
- return;
2323
- }
2324
- const updateData = { status: "generated" };
2325
- if (plan) updateData.plan = plan;
2326
- if (followUpTasks) updateData.followUpTasks = followUpTasks;
2327
- if (protoHtml) {
2328
2329
  try {
2329
- const proto = await api.post("/api/prototypes", {
2330
- title: `${idea.title} \u2014 Idea Prototype`,
2331
- prompt: idea.description || idea.title,
2332
- variantCount: 1,
2333
- projectId: idea.projectId ?? null
2334
- });
2335
- await api.patch(`/api/prototypes/${proto.id}`, {
2336
- status: "completed",
2337
- files: [{ name: "idea-prototype.html", content: protoHtml }]
2338
- });
2339
- updateData.generatedPrototypeId = proto.id;
2340
- logSuccess(prefix, `Prototype created: ${paint("gray", proto.id.slice(0, 8))}`);
2341
- } catch (err) {
2342
- logError(prefix, `Failed to create prototype: ${err.message}`);
2330
+ protoHtml = readFileSync5(protoPath, "utf-8");
2331
+ } catch {
2332
+ }
2333
+ try {
2334
+ const tasksRaw = readFileSync5(tasksPath, "utf-8");
2335
+ const parsed = JSON.parse(tasksRaw);
2336
+ if (Array.isArray(parsed)) {
2337
+ followUpTasks = parsed.filter((t) => t && typeof t === "object" && "title" in t);
2338
+ }
2339
+ } catch {
2340
+ }
2341
+ if (!plan && !protoHtml) {
2342
+ logError(prefix, `No output files found in ${repoDir}`);
2343
+ await api.patch(`/api/ideas/${idea.id}`, { status: "draft" });
2344
+ return;
2345
+ }
2346
+ const updateData = { status: "generated" };
2347
+ if (plan) updateData.plan = plan;
2348
+ if (followUpTasks) updateData.followUpTasks = followUpTasks;
2349
+ if (protoHtml) {
2350
+ try {
2351
+ const proto = await api.post("/api/prototypes", {
2352
+ title: `${idea.title} \u2014 Idea Prototype`,
2353
+ prompt: idea.description || idea.title,
2354
+ variantCount: 1,
2355
+ projectId: idea.projectId ?? null
2356
+ });
2357
+ await api.patch(`/api/prototypes/${proto.id}`, {
2358
+ status: "completed",
2359
+ files: [{ name: "idea-prototype.html", content: protoHtml }]
2360
+ });
2361
+ updateData.generatedPrototypeId = proto.id;
2362
+ logSuccess(prefix, `Prototype created: ${paint("gray", proto.id.slice(0, 8))}`);
2363
+ } catch (err) {
2364
+ logError(prefix, `Failed to create prototype: ${err.message}`);
2365
+ }
2366
+ }
2367
+ await api.patch(`/api/ideas/${idea.id}`, updateData);
2368
+ logSuccess(prefix, `"${paint("bold", idea.title)}" generation complete`);
2369
+ try {
2370
+ unlinkSync(planPath);
2371
+ } catch {
2372
+ }
2373
+ try {
2374
+ unlinkSync(tasksPath);
2375
+ } catch {
2376
+ }
2377
+ try {
2378
+ unlinkSync(protoPath);
2379
+ } catch {
2380
+ }
2381
+ } catch (err) {
2382
+ logError(prefix, `Failed to upload idea output: ${err.message}`);
2383
+ try {
2384
+ await api.patch(`/api/ideas/${idea.id}`, { status: "draft" });
2385
+ } catch {
2343
2386
  }
2344
2387
  }
2345
- await api.patch(`/api/ideas/${idea.id}`, updateData);
2346
- logSuccess(prefix, `"${paint("bold", idea.title)}" generation complete`);
2347
- try {
2348
- unlinkSync(planPath);
2349
- } catch {
2350
- }
2351
- try {
2352
- unlinkSync(tasksPath);
2353
- } catch {
2354
- }
2355
- try {
2356
- unlinkSync(protoPath);
2357
- } catch {
2358
- }
2359
- } catch (err) {
2360
- logError(prefix, `Failed to upload idea output: ${err.message}`);
2388
+ } else {
2389
+ logError(prefix, `"${paint("bold", idea.title)}" generation failed (exit ${code})`);
2361
2390
  try {
2362
2391
  await api.patch(`/api/ideas/${idea.id}`, { status: "draft" });
2363
2392
  } catch {
2364
2393
  }
2365
2394
  }
2366
- } else {
2367
- logError(prefix, `"${paint("bold", idea.title)}" generation failed (exit ${code})`);
2368
- try {
2369
- await api.patch(`/api/ideas/${idea.id}`, { status: "draft" });
2370
- } catch {
2371
- }
2395
+ } finally {
2396
+ queued.delete(key);
2397
+ finishing.delete(key);
2372
2398
  }
2373
2399
  });
2374
2400
  }
@@ -2447,322 +2473,342 @@ ${divider}`);
2447
2473
  }
2448
2474
  }
2449
2475
  async function poll() {
2450
- let tasks;
2476
+ if (pollRunning) return;
2477
+ pollRunning = true;
2451
2478
  try {
2452
- const allInProgress = await api.get("/api/tasks?inProgress=true");
2453
- tasks = allInProgress.filter((t) => t.delegated || t.planMode);
2454
- } catch (err) {
2455
- logError(watchTag(), `Failed to fetch tasks: ${err.message}`);
2456
- return;
2457
- }
2458
- const config = loadConfig();
2459
- const inProgressIds = new Set(tasks.map((t) => t.id));
2460
- for (const [taskId, entry] of active) {
2461
- if (taskId.startsWith("proto-") || taskId.startsWith("repo-") || taskId.startsWith("scan-")) continue;
2462
- if (!inProgressIds.has(taskId)) {
2463
- logWarn(watchTag(), `Task ${paint("yellow", taskId.slice(0, 8))} no longer in-progress, terminating\u2026`);
2464
- entry.process.kill("SIGTERM");
2465
- active.delete(taskId);
2466
- queued.delete(taskId);
2467
- }
2468
- }
2469
- const nonTaskPrefixes = ["proto-", "repo-", "scan-", "idea-", "test-"];
2470
- for (const taskId of failed.keys()) {
2471
- if (nonTaskPrefixes.some((p) => taskId.startsWith(p))) continue;
2472
- if (!inProgressIds.has(taskId)) failed.delete(taskId);
2473
- }
2474
- for (const taskId of queued) {
2475
- if (nonTaskPrefixes.some((p) => taskId.startsWith(p))) continue;
2476
- if (!inProgressIds.has(taskId)) queued.delete(taskId);
2477
- }
2478
- for (const task of tasks) {
2479
- if (queued.has(task.id)) continue;
2480
- if (failed.has(task.id)) continue;
2481
- const sid = shortId(task.id);
2482
- const prefix = taskTag(sid);
2483
- const repoDir = findDirectoryForProject(config, task.projectId, rootDir);
2484
- if (!repoDir) {
2485
- const reason = `no linked directory found under ${rootDir}`;
2486
- logError(prefix, `"${task.title}": ${reason} \u2014 will not retry`);
2487
- failed.set(task.id, reason);
2488
- continue;
2479
+ let queuedTasks;
2480
+ let delegatedTasks;
2481
+ try {
2482
+ queuedTasks = await api.get("/api/tasks?status=queued");
2483
+ delegatedTasks = await api.get("/api/tasks?status=delegated");
2484
+ } catch (err) {
2485
+ logError(watchTag(), `Failed to fetch tasks: ${err.message}`);
2486
+ return;
2489
2487
  }
2490
- if (!existsSync6(repoDir)) {
2491
- const reason = `linked directory "${repoDir}" does not exist`;
2492
- logError(prefix, `"${task.title}": ${reason} \u2014 will not retry`);
2493
- failed.set(task.id, reason);
2494
- continue;
2488
+ const nonTestQueued = queuedTasks.filter((t) => t.mode !== "testing");
2489
+ const nonTestDelegated = delegatedTasks.filter((t) => t.mode !== "testing");
2490
+ const tasks = [...nonTestQueued, ...nonTestDelegated];
2491
+ const config = loadConfig();
2492
+ const activeTaskIds = new Set(tasks.map((t) => t.id));
2493
+ for (const [taskId, entry] of active) {
2494
+ if (taskId.startsWith("proto-") || taskId.startsWith("repo-") || taskId.startsWith("scan-")) continue;
2495
+ if (!activeTaskIds.has(taskId)) {
2496
+ logWarn(watchTag(), `Task ${paint("yellow", taskId.slice(0, 8))} no longer active, terminating\u2026`);
2497
+ entry.process.kill("SIGTERM");
2498
+ active.delete(taskId);
2499
+ queued.delete(taskId);
2500
+ }
2495
2501
  }
2496
- queued.add(task.id);
2497
- if (dryRun) {
2498
- logInfo(
2499
- watchTag(),
2500
- `${paint("yellow", "[dry-run]")} would dispatch "${paint("bold", task.title)}" (${paint("gray", task.id)}) in ${paint("cyan", repoDir)}`
2501
- );
2502
- continue;
2502
+ const nonTaskPrefixes = ["proto-", "repo-", "scan-", "idea-", "test-"];
2503
+ for (const taskId of failed.keys()) {
2504
+ if (nonTaskPrefixes.some((p) => taskId.startsWith(p))) continue;
2505
+ if (!activeTaskIds.has(taskId)) failed.delete(taskId);
2503
2506
  }
2504
- if (task.planMode) {
2505
- dispatchPlanModeTask(task, repoDir);
2506
- } else if (planApproval) {
2507
- approvalQueue.push({ task, repoDir });
2508
- processApprovalQueue();
2509
- } else {
2510
- dispatchTask(task, repoDir);
2507
+ for (const taskId of queued) {
2508
+ if (nonTaskPrefixes.some((p) => taskId.startsWith(p))) continue;
2509
+ if (finishing.has(taskId)) continue;
2510
+ if (!activeTaskIds.has(taskId)) queued.delete(taskId);
2511
2511
  }
2512
- }
2513
- let prototypes = [];
2514
- try {
2515
- prototypes = await api.get("/api/prototypes?status=in_progress");
2516
- } catch (err) {
2517
- logError(watchTag(), `Failed to fetch prototypes: ${err.message}`);
2518
- }
2519
- const inProgressProtoKeys = new Set(prototypes.map((p) => `proto-${p.id}`));
2520
- for (const [key, entry] of active) {
2521
- if (key.startsWith("proto-") && !inProgressProtoKeys.has(key)) {
2522
- logWarn(watchTag(), `Prototype ${paint("yellow", key)} no longer in_progress, terminating\u2026`);
2523
- entry.process.kill("SIGTERM");
2524
- active.delete(key);
2525
- queued.delete(key);
2526
- }
2527
- }
2528
- for (const key of failed.keys()) {
2529
- if (key.startsWith("proto-") && !inProgressProtoKeys.has(key)) failed.delete(key);
2530
- }
2531
- for (const key of queued) {
2532
- if (key.startsWith("proto-") && !inProgressProtoKeys.has(key)) queued.delete(key);
2533
- }
2534
- const activeProtoCount = [...active.keys()].filter((k) => k.startsWith("proto-")).length;
2535
- let protoSlots = Math.max(0, 2 - activeProtoCount);
2536
- for (const proto of prototypes) {
2537
- if (protoSlots <= 0) break;
2538
- const key = `proto-${proto.id}`;
2539
- if (queued.has(key)) continue;
2540
- if (failed.has(key)) continue;
2541
- const sid = shortId(proto.id);
2542
- const prefix = protoTag(sid);
2543
- if (!proto.projectId) {
2544
- logWarn(prefix, `"${proto.title}": no projectId \u2014 skipping`);
2545
- continue;
2512
+ for (const task of tasks) {
2513
+ if (queued.has(task.id)) continue;
2514
+ if (finishing.has(task.id)) continue;
2515
+ if (failed.has(task.id)) continue;
2516
+ const sid = shortId(task.id);
2517
+ const prefix = taskTag(sid);
2518
+ const repoDir = findDirectoryForProject(config, task.projectId, rootDir);
2519
+ if (!repoDir) {
2520
+ const reason = `no linked directory found under ${rootDir}`;
2521
+ logError(prefix, `"${task.title}": ${reason} \u2014 will not retry`);
2522
+ failed.set(task.id, reason);
2523
+ continue;
2524
+ }
2525
+ if (!existsSync6(repoDir)) {
2526
+ const reason = `linked directory "${repoDir}" does not exist`;
2527
+ logError(prefix, `"${task.title}": ${reason} \u2014 will not retry`);
2528
+ failed.set(task.id, reason);
2529
+ continue;
2530
+ }
2531
+ if (task.status === "queued") {
2532
+ try {
2533
+ await api.patch(`/api/tasks/${task.id}`, { status: "delegated" });
2534
+ } catch (err) {
2535
+ logError(prefix, `Failed to mark task as delegated: ${err.message}`);
2536
+ continue;
2537
+ }
2538
+ }
2539
+ queued.add(task.id);
2540
+ if (dryRun) {
2541
+ logInfo(
2542
+ watchTag(),
2543
+ `${paint("yellow", "[dry-run]")} would dispatch "${paint("bold", task.title)}" (${paint("gray", task.id)}) in ${paint("cyan", repoDir)}`
2544
+ );
2545
+ continue;
2546
+ }
2547
+ if (task.mode === "planning") {
2548
+ dispatchPlanModeTask(task, repoDir);
2549
+ } else if (planApproval) {
2550
+ approvalQueue.push({ task, repoDir });
2551
+ processApprovalQueue();
2552
+ } else {
2553
+ dispatchTask(task, repoDir);
2554
+ }
2546
2555
  }
2547
- const repoDir = findDirectoryForProject(config, proto.projectId, rootDir);
2548
- if (!repoDir) {
2549
- logWarn(prefix, `"${proto.title}": no linked directory found \u2014 skipping`);
2550
- continue;
2556
+ let prototypes = [];
2557
+ try {
2558
+ prototypes = await api.get("/api/prototypes?status=in_progress");
2559
+ } catch (err) {
2560
+ logError(watchTag(), `Failed to fetch prototypes: ${err.message}`);
2551
2561
  }
2552
- if (!existsSync6(repoDir)) {
2553
- logError(prefix, `"${proto.title}": linked directory "${repoDir}" does not exist \u2014 skipping`);
2554
- failed.set(key, `directory does not exist: ${repoDir}`);
2555
- continue;
2562
+ const inProgressProtoKeys = new Set(prototypes.map((p) => `proto-${p.id}`));
2563
+ for (const [key, entry] of active) {
2564
+ if (key.startsWith("proto-") && !inProgressProtoKeys.has(key)) {
2565
+ logWarn(watchTag(), `Prototype ${paint("yellow", key)} no longer in_progress, terminating\u2026`);
2566
+ entry.process.kill("SIGTERM");
2567
+ active.delete(key);
2568
+ queued.delete(key);
2569
+ }
2556
2570
  }
2557
- queued.add(key);
2558
- protoSlots--;
2559
- if (dryRun) {
2560
- logInfo(
2561
- watchTag(),
2562
- `${paint("yellow", "[dry-run]")} would dispatch prototype "${paint("bold", proto.title)}" in ${paint("cyan", repoDir)}`
2563
- );
2564
- continue;
2571
+ for (const key of failed.keys()) {
2572
+ if (key.startsWith("proto-") && !inProgressProtoKeys.has(key)) failed.delete(key);
2565
2573
  }
2566
- dispatchPrototypeJob(proto, repoDir);
2567
- }
2568
- let testTasks = [];
2569
- try {
2570
- testTasks = await api.get("/api/tasks?testStatus=pending");
2571
- } catch (err) {
2572
- logError(watchTag(), `Failed to fetch test tasks: ${err.message}`);
2573
- }
2574
- for (const task of testTasks) {
2575
- const key = `test-${task.id}`;
2576
- if (queued.has(key)) continue;
2577
- if (failed.has(key)) continue;
2578
- const sid = shortId(task.id);
2579
- const prefix = testTag(sid);
2580
- if (!task.link) {
2581
- logWarn(prefix, `"${task.title}": no MR/PR link \u2014 skipping`);
2582
- continue;
2574
+ for (const key of queued) {
2575
+ if (key.startsWith("proto-") && !inProgressProtoKeys.has(key) && !finishing.has(key)) queued.delete(key);
2583
2576
  }
2584
- queued.add(key);
2585
- if (dryRun) {
2586
- logInfo(
2587
- watchTag(),
2588
- `${paint("yellow", "[dry-run]")} would run test for "${paint("bold", task.title)}"`
2589
- );
2590
- continue;
2577
+ const activeProtoCount = [...active.keys()].filter((k) => k.startsWith("proto-")).length;
2578
+ let protoSlots = Math.max(0, 2 - activeProtoCount);
2579
+ for (const proto of prototypes) {
2580
+ if (protoSlots <= 0) break;
2581
+ const key = `proto-${proto.id}`;
2582
+ if (queued.has(key)) continue;
2583
+ if (finishing.has(key)) continue;
2584
+ if (failed.has(key)) continue;
2585
+ const sid = shortId(proto.id);
2586
+ const prefix = protoTag(sid);
2587
+ if (!proto.projectId) {
2588
+ logWarn(prefix, `"${proto.title}": no projectId \u2014 skipping`);
2589
+ continue;
2590
+ }
2591
+ const repoDir = findDirectoryForProject(config, proto.projectId, rootDir);
2592
+ if (!repoDir) {
2593
+ logWarn(prefix, `"${proto.title}": no linked directory found \u2014 skipping`);
2594
+ continue;
2595
+ }
2596
+ if (!existsSync6(repoDir)) {
2597
+ logError(prefix, `"${proto.title}": linked directory "${repoDir}" does not exist \u2014 skipping`);
2598
+ failed.set(key, `directory does not exist: ${repoDir}`);
2599
+ continue;
2600
+ }
2601
+ queued.add(key);
2602
+ protoSlots--;
2603
+ if (dryRun) {
2604
+ logInfo(
2605
+ watchTag(),
2606
+ `${paint("yellow", "[dry-run]")} would dispatch prototype "${paint("bold", proto.title)}" in ${paint("cyan", repoDir)}`
2607
+ );
2608
+ continue;
2609
+ }
2610
+ dispatchPrototypeJob(proto, repoDir);
2591
2611
  }
2592
- (async () => {
2593
- logDispatch(prefix, `Running test for "${paint("bold", task.title)}"\u2026`);
2594
- try {
2595
- await api.patch(`/api/tasks/${task.id}`, { testStatus: "running" });
2596
- await postTaskUpdate(task.id, "Test started \u2014 setting up environment\u2026");
2597
- let localPath;
2598
- try {
2599
- const project = await api.get(`/api/projects/${task.projectId}`);
2600
- localPath = project.localPath ?? void 0;
2601
- } catch {
2602
- }
2603
- if (!localPath) {
2604
- const cfg = loadConfig();
2605
- localPath = findDirectoryForProject(cfg, task.projectId, rootDir) ?? void 0;
2606
- }
2607
- if (!localPath) {
2608
- throw new Error("Local repo path not configured. Run `mr link` in the project directory.");
2609
- }
2610
- let testPlan;
2612
+ const testTasks = queuedTasks.filter((t) => t.mode === "testing");
2613
+ for (const task of testTasks) {
2614
+ const key = `test-${task.id}`;
2615
+ if (queued.has(key)) continue;
2616
+ if (finishing.has(key)) continue;
2617
+ if (failed.has(key)) continue;
2618
+ const sid = shortId(task.id);
2619
+ const prefix = testTag(sid);
2620
+ if (!task.link) {
2621
+ logWarn(prefix, `"${task.title}": no MR/PR link \u2014 skipping`);
2622
+ continue;
2623
+ }
2624
+ queued.add(key);
2625
+ if (dryRun) {
2626
+ logInfo(
2627
+ watchTag(),
2628
+ `${paint("yellow", "[dry-run]")} would run test for "${paint("bold", task.title)}"`
2629
+ );
2630
+ continue;
2631
+ }
2632
+ (async () => {
2633
+ logDispatch(prefix, `Running test for "${paint("bold", task.title)}"\u2026`);
2611
2634
  try {
2612
- const resources = await api.get(`/api/tasks/${task.id}/resources`);
2613
- const planResource = resources.find((r) => r.type === "test-plan");
2614
- if (planResource) {
2615
- testPlan = JSON.parse(planResource.content);
2616
- logInfo(prefix, `Using agent-authored test plan (${testPlan.length} steps)`);
2635
+ await api.patch(`/api/tasks/${task.id}`, { status: "delegated" });
2636
+ await postTaskUpdate(task.id, "Test started \u2014 setting up environment\u2026");
2637
+ let localPath;
2638
+ try {
2639
+ const project = await api.get(`/api/projects/${task.projectId}`);
2640
+ localPath = project.localPath ?? void 0;
2641
+ } catch {
2617
2642
  }
2618
- } catch {
2619
- }
2620
- const result = await runTest({
2621
- taskId: task.id,
2622
- taskLink: task.link,
2623
- localPath,
2624
- testPlan,
2625
- browseRunner: runBrowseCommand2,
2626
- uploadScreenshot: async (screenshotPath, message) => {
2627
- try {
2628
- const { readFileSync: readFileSync12 } = await import("fs");
2629
- const cfg = loadConfig();
2630
- const imageBuffer = readFileSync12(screenshotPath);
2631
- const fileName = screenshotPath.split("/").pop() || "test-screenshot.png";
2632
- const formData = new FormData();
2633
- const blob = new Blob([imageBuffer], { type: "image/png" });
2634
- formData.append("file", blob, fileName);
2635
- formData.append("prefix", "test-screenshots");
2636
- const uploadRes = await fetch(`${cfg.apiUrl}/api/upload`, {
2637
- method: "POST",
2638
- headers: { Authorization: `Bearer ${cfg.apiKey}` },
2639
- body: formData
2640
- });
2641
- if (!uploadRes.ok) return null;
2642
- const uploadData = await uploadRes.json();
2643
- await api.post(`/api/tasks/${task.id}/updates`, {
2644
- message,
2645
- imageUrl: uploadData.url,
2646
- source: "system"
2647
- });
2648
- return uploadData.url;
2649
- } catch {
2650
- return null;
2643
+ if (!localPath) {
2644
+ const cfg = loadConfig();
2645
+ localPath = findDirectoryForProject(cfg, task.projectId, rootDir) ?? void 0;
2646
+ }
2647
+ if (!localPath) {
2648
+ throw new Error("Local repo path not configured. Run `mr link` in the project directory.");
2649
+ }
2650
+ let testPlan;
2651
+ try {
2652
+ const resources = await api.get(`/api/tasks/${task.id}/resources`);
2653
+ const planResource = resources.find((r) => r.type === "test-plan");
2654
+ if (planResource) {
2655
+ testPlan = JSON.parse(planResource.content);
2656
+ logInfo(prefix, `Using agent-authored test plan (${testPlan.length} steps)`);
2651
2657
  }
2652
- },
2653
- postUpdate: async (message, imageUrl) => {
2654
- await postTaskUpdate(task.id, message);
2655
- },
2656
- onProgress: (msg) => logInfo(prefix, msg)
2657
- });
2658
- await api.patch(`/api/tasks/${task.id}`, { testStatus: result.status });
2659
- logSuccess(prefix, result.summary);
2660
- } catch (err) {
2661
- logError(prefix, `Test failed: ${err.message}`);
2662
- try {
2663
- await api.patch(`/api/tasks/${task.id}`, { testStatus: "failed" });
2664
- await postTaskUpdate(task.id, `Test failed: ${err.message}`);
2665
- } catch {
2658
+ } catch {
2659
+ }
2660
+ const result = await runTest({
2661
+ taskId: task.id,
2662
+ taskLink: task.link,
2663
+ localPath,
2664
+ testPlan,
2665
+ browseRunner: runBrowseCommand2,
2666
+ uploadScreenshot: async (screenshotPath, message) => {
2667
+ try {
2668
+ const { readFileSync: readFileSync12 } = await import("fs");
2669
+ const cfg = loadConfig();
2670
+ const imageBuffer = readFileSync12(screenshotPath);
2671
+ const fileName = screenshotPath.split("/").pop() || "test-screenshot.png";
2672
+ const formData = new FormData();
2673
+ const blob = new Blob([imageBuffer], { type: "image/png" });
2674
+ formData.append("file", blob, fileName);
2675
+ formData.append("prefix", "test-screenshots");
2676
+ const uploadRes = await fetch(`${cfg.apiUrl}/api/upload`, {
2677
+ method: "POST",
2678
+ headers: { Authorization: `Bearer ${cfg.apiKey}` },
2679
+ body: formData
2680
+ });
2681
+ if (!uploadRes.ok) return null;
2682
+ const uploadData = await uploadRes.json();
2683
+ await api.post(`/api/tasks/${task.id}/updates`, {
2684
+ message,
2685
+ imageUrl: uploadData.url,
2686
+ source: "system"
2687
+ });
2688
+ return uploadData.url;
2689
+ } catch {
2690
+ return null;
2691
+ }
2692
+ },
2693
+ postUpdate: async (message, imageUrl) => {
2694
+ await postTaskUpdate(task.id, message);
2695
+ },
2696
+ onProgress: (msg) => logInfo(prefix, msg)
2697
+ });
2698
+ await api.patch(`/api/tasks/${task.id}`, { status: "completed", testResult: result.status });
2699
+ logSuccess(prefix, result.summary);
2700
+ } catch (err) {
2701
+ logError(prefix, `Test failed: ${err.message}`);
2702
+ try {
2703
+ await api.patch(`/api/tasks/${task.id}`, { status: "completed", testResult: "failed" });
2704
+ await postTaskUpdate(task.id, `Test failed: ${err.message}`);
2705
+ } catch {
2706
+ }
2707
+ failed.set(key, err.message);
2708
+ } finally {
2709
+ queued.delete(key);
2666
2710
  }
2667
- failed.set(key, err.message);
2668
- } finally {
2711
+ })();
2712
+ }
2713
+ let pendingProjects = [];
2714
+ try {
2715
+ pendingProjects = await api.get("/api/projects?repoCreationStatus=pending");
2716
+ } catch (err) {
2717
+ logError(watchTag(), `Failed to fetch pending projects: ${err.message}`);
2718
+ }
2719
+ for (const project of pendingProjects) {
2720
+ const key = `repo-${project.id}`;
2721
+ if (queued.has(key)) continue;
2722
+ if (finishing.has(key)) continue;
2723
+ if (failed.has(key)) continue;
2724
+ queued.add(key);
2725
+ if (dryRun) {
2726
+ logInfo(
2727
+ watchTag(),
2728
+ `${paint("yellow", "[dry-run]")} would create repo for "${paint("bold", project.name)}" in ${paint("cyan", rootDir)}`
2729
+ );
2730
+ continue;
2731
+ }
2732
+ dispatchRepoCreation(project, rootDir);
2733
+ }
2734
+ let generatingIdeas = [];
2735
+ try {
2736
+ generatingIdeas = await api.get("/api/ideas?status=generating");
2737
+ } catch (err) {
2738
+ logError(watchTag(), `Failed to fetch generating ideas: ${err.message}`);
2739
+ }
2740
+ const inProgressIdeaKeys = new Set(generatingIdeas.map((i) => `idea-${i.id}`));
2741
+ for (const [key, entry] of active) {
2742
+ if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key)) {
2743
+ logWarn(watchTag(), `Idea ${paint("yellow", key)} no longer generating, terminating\u2026`);
2744
+ entry.process.kill("SIGTERM");
2745
+ active.delete(key);
2669
2746
  queued.delete(key);
2670
2747
  }
2671
- })();
2672
- }
2673
- let pendingProjects = [];
2674
- try {
2675
- pendingProjects = await api.get("/api/projects?repoCreationStatus=pending");
2676
- } catch (err) {
2677
- logError(watchTag(), `Failed to fetch pending projects: ${err.message}`);
2678
- }
2679
- for (const project of pendingProjects) {
2680
- const key = `repo-${project.id}`;
2681
- if (queued.has(key)) continue;
2682
- if (failed.has(key)) continue;
2683
- queued.add(key);
2684
- if (dryRun) {
2685
- logInfo(
2686
- watchTag(),
2687
- `${paint("yellow", "[dry-run]")} would create repo for "${paint("bold", project.name)}" in ${paint("cyan", rootDir)}`
2688
- );
2689
- continue;
2690
2748
  }
2691
- dispatchRepoCreation(project, rootDir);
2692
- }
2693
- let generatingIdeas = [];
2694
- try {
2695
- generatingIdeas = await api.get("/api/ideas?status=generating");
2696
- } catch (err) {
2697
- logError(watchTag(), `Failed to fetch generating ideas: ${err.message}`);
2698
- }
2699
- const inProgressIdeaKeys = new Set(generatingIdeas.map((i) => `idea-${i.id}`));
2700
- for (const [key, entry] of active) {
2701
- if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key)) {
2702
- logWarn(watchTag(), `Idea ${paint("yellow", key)} no longer generating, terminating\u2026`);
2703
- entry.process.kill("SIGTERM");
2704
- active.delete(key);
2705
- queued.delete(key);
2749
+ for (const key of failed.keys()) {
2750
+ if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key)) failed.delete(key);
2706
2751
  }
2707
- }
2708
- for (const key of failed.keys()) {
2709
- if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key)) failed.delete(key);
2710
- }
2711
- for (const key of queued) {
2712
- if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key)) queued.delete(key);
2713
- }
2714
- const activeIdeaCount = [...active.keys()].filter((k) => k.startsWith("idea-")).length;
2715
- let ideaSlots = Math.max(0, 1 - activeIdeaCount);
2716
- for (const idea of generatingIdeas) {
2717
- if (ideaSlots <= 0) break;
2718
- const key = `idea-${idea.id}`;
2719
- if (queued.has(key)) continue;
2720
- if (failed.has(key)) continue;
2721
- const sid = shortId(idea.id);
2722
- const prefix = ideaTag(sid);
2723
- let repoDir;
2724
- if (idea.projectId) {
2725
- const dir = findDirectoryForProject(config, idea.projectId, rootDir);
2726
- if (!dir) {
2727
- logWarn(prefix, `"${idea.title}": no linked directory found \u2014 skipping`);
2752
+ for (const key of queued) {
2753
+ if (key.startsWith("idea-") && !inProgressIdeaKeys.has(key) && !finishing.has(key)) queued.delete(key);
2754
+ }
2755
+ const activeIdeaCount = [...active.keys()].filter((k) => k.startsWith("idea-")).length;
2756
+ let ideaSlots = Math.max(0, 1 - activeIdeaCount);
2757
+ for (const idea of generatingIdeas) {
2758
+ if (ideaSlots <= 0) break;
2759
+ const key = `idea-${idea.id}`;
2760
+ if (queued.has(key)) continue;
2761
+ if (finishing.has(key)) continue;
2762
+ if (failed.has(key)) continue;
2763
+ const sid = shortId(idea.id);
2764
+ const prefix = ideaTag(sid);
2765
+ let repoDir;
2766
+ if (idea.projectId) {
2767
+ const dir = findDirectoryForProject(config, idea.projectId, rootDir);
2768
+ if (!dir) {
2769
+ logWarn(prefix, `"${idea.title}": no linked directory found \u2014 skipping`);
2770
+ continue;
2771
+ }
2772
+ repoDir = dir;
2773
+ } else {
2774
+ repoDir = rootDir;
2775
+ }
2776
+ queued.add(key);
2777
+ ideaSlots--;
2778
+ if (dryRun) {
2779
+ logInfo(
2780
+ watchTag(),
2781
+ `${paint("yellow", "[dry-run]")} would dispatch idea "${paint("bold", idea.title)}" in ${paint("cyan", repoDir)}`
2782
+ );
2728
2783
  continue;
2729
2784
  }
2730
- repoDir = dir;
2731
- } else {
2732
- repoDir = rootDir;
2785
+ dispatchIdeaJob(idea, repoDir);
2733
2786
  }
2734
- queued.add(key);
2735
- ideaSlots--;
2736
- if (dryRun) {
2737
- logInfo(
2738
- watchTag(),
2739
- `${paint("yellow", "[dry-run]")} would dispatch idea "${paint("bold", idea.title)}" in ${paint("cyan", repoDir)}`
2740
- );
2741
- continue;
2787
+ let pendingScans = [];
2788
+ try {
2789
+ pendingScans = await api.get("/api/scans?status=pending&limit=5");
2790
+ } catch (err) {
2791
+ logError(watchTag(), `Failed to fetch pending scans: ${err.message}`);
2742
2792
  }
2743
- dispatchIdeaJob(idea, repoDir);
2744
- }
2745
- let pendingScans = [];
2746
- try {
2747
- pendingScans = await api.get("/api/scans?status=pending&limit=5");
2748
- } catch (err) {
2749
- logError(watchTag(), `Failed to fetch pending scans: ${err.message}`);
2750
- }
2751
- for (const scan of pendingScans) {
2752
- const key = `scan-${scan.id}`;
2753
- if (queued.has(key)) continue;
2754
- if (failed.has(key)) continue;
2755
- const sid = shortId(scan.id);
2756
- const prefix = `${paint("magenta", `[scan:${sid}]`)}`;
2757
- queued.add(key);
2758
- if (dryRun) {
2759
- logInfo(
2760
- watchTag(),
2761
- `${paint("yellow", "[dry-run]")} would run scan ${paint("yellow", sid)} for project ${paint("cyan", scan.projectId)}`
2762
- );
2763
- continue;
2793
+ for (const scan of pendingScans) {
2794
+ const key = `scan-${scan.id}`;
2795
+ if (queued.has(key)) continue;
2796
+ if (finishing.has(key)) continue;
2797
+ if (failed.has(key)) continue;
2798
+ const sid = shortId(scan.id);
2799
+ const prefix = `${paint("magenta", `[scan:${sid}]`)}`;
2800
+ queued.add(key);
2801
+ if (dryRun) {
2802
+ logInfo(
2803
+ watchTag(),
2804
+ `${paint("yellow", "[dry-run]")} would run scan ${paint("yellow", sid)} for project ${paint("cyan", scan.projectId)}`
2805
+ );
2806
+ continue;
2807
+ }
2808
+ dispatchScan(scan, prefix, key);
2764
2809
  }
2765
- dispatchScan(scan, prefix, key);
2810
+ } finally {
2811
+ pollRunning = false;
2766
2812
  }
2767
2813
  }
2768
2814
  async function shutdown() {
@@ -2859,21 +2905,20 @@ function printTaskBanner(action, title, id) {
2859
2905
  }
2860
2906
  var startCommand = new Command9("start").description("Mark a task as in-progress (you are working on it)").argument("<task-id>", "Task ID to start").action(async (taskId) => {
2861
2907
  const task = await api.patch(`/api/tasks/${taskId}`, {
2862
- inProgress: true,
2863
- delegated: false
2908
+ status: "in_progress"
2864
2909
  });
2865
2910
  printTaskBanner(paint2("green", "start"), task.title, task.id);
2866
2911
  });
2867
- var delegateCommand = new Command9("delegate").description("Mark a task as in-progress and delegate it to the watch agent").argument("<task-id>", "Task ID to delegate").action(async (taskId) => {
2912
+ var delegateCommand = new Command9("delegate").description("Queue a task for the watch agent to pick up").argument("<task-id>", "Task ID to delegate").action(async (taskId) => {
2868
2913
  const task = await api.patch(`/api/tasks/${taskId}`, {
2869
- inProgress: true,
2870
- delegated: true
2914
+ status: "queued"
2871
2915
  });
2872
2916
  printTaskBanner(paint2("cyan", "delegate"), task.title, task.id);
2873
2917
  });
2874
- var undelegateCommand = new Command9("undelegate").description("Remove delegation from a task (keep it in-progress)").argument("<task-id>", "Task ID to undelegate").action(async (taskId) => {
2918
+ var undelegateCommand = new Command9("undelegate").description("Remove delegation from a task and return to todo").argument("<task-id>", "Task ID to undelegate").action(async (taskId) => {
2875
2919
  const task = await api.patch(`/api/tasks/${taskId}`, {
2876
- delegated: false
2920
+ status: "todo",
2921
+ mode: "development"
2877
2922
  });
2878
2923
  printTaskBanner(paint2("yellow", "undelegate"), task.title, task.id);
2879
2924
  });
@@ -2892,7 +2937,7 @@ var createCommand = new Command10("create").description("Create a new task in th
2892
2937
  title,
2893
2938
  projectId,
2894
2939
  notes: opts.notes,
2895
- inProgress: opts.inProgress ?? false
2940
+ status: opts.inProgress ? "in_progress" : "todo"
2896
2941
  });
2897
2942
  const t = task;
2898
2943
  console.log(`Created: ${t.title} (${t.id})`);
@@ -2927,8 +2972,7 @@ function printTaskBanner2(action, title, id) {
2927
2972
  }
2928
2973
  var completeCommand = new Command11("complete").description("Mark a task as completed").argument("<task-id>", "Task ID to complete").action(async (taskId) => {
2929
2974
  const task = await api.patch(`/api/tasks/${taskId}`, {
2930
- completed: true,
2931
- inProgress: false
2975
+ status: "completed"
2932
2976
  });
2933
2977
  printTaskBanner2(paint3("green", "complete \u2713"), task.title, task.id);
2934
2978
  });
@@ -2943,9 +2987,11 @@ var subtaskCompleteCommand = new Command12("subtask-complete").description("Mark
2943
2987
  console.log(`\u2713 Subtask completed: ${subtask.title}`);
2944
2988
  });
2945
2989
 
2946
- // cli/commands/digest.ts
2990
+ // cli/commands/up.ts
2947
2991
  import { Command as Command13 } from "commander";
2948
2992
  import { spawn as spawn5 } from "child_process";
2993
+ import { resolve as resolve2 } from "path";
2994
+ import { fileURLToPath as fileURLToPath2 } from "url";
2949
2995
  var c4 = {
2950
2996
  reset: "\x1B[0m",
2951
2997
  bold: "\x1B[1m",
@@ -2963,270 +3009,23 @@ function paint4(color, text) {
2963
3009
  function timestamp2() {
2964
3010
  return paint4("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
2965
3011
  }
2966
- function digestTag() {
2967
- return paint4("magenta", "[digest]");
2968
- }
2969
- function shortId2(id) {
2970
- return id.slice(0, 8);
3012
+ function upTag() {
3013
+ return paint4("cyan", "[up]");
2971
3014
  }
2972
3015
  function log(msg) {
2973
- console.log(`${timestamp2()} ${digestTag()} ${msg}`);
2974
- }
2975
- function logOk(msg) {
2976
- console.log(`${timestamp2()} ${digestTag()} ${paint4("green", "\u2713")} ${msg}`);
3016
+ console.log(`${timestamp2()} ${upTag()} ${msg}`);
2977
3017
  }
2978
3018
  function logErr(msg) {
2979
- console.error(`${timestamp2()} ${digestTag()} ${paint4("red", "\u2717")} ${msg}`);
2980
- }
2981
- function buildPrompt(messages) {
2982
- const emailsText = messages.map(
2983
- (msg, i) => `Email ${i + 1}:
2984
- ID: ${msg.id}
2985
- Subject: ${msg.subject}
2986
- From: ${msg.sender} <${msg.senderEmail}>
2987
- Date: ${msg.date}
2988
- Snippet: ${msg.snippet}
2989
- Body preview: ${msg.body.slice(0, 500)}`
2990
- ).join("\n\n---\n\n");
2991
- return `You are an email triage assistant. Analyze the following unread emails and produce a structured digest.
2992
-
2993
- For each email, determine:
2994
- 1. Is it relevant/actionable (requires attention, is personal, work-related, or important)?
2995
- 2. Or is it not relevant (newsletters, marketing, automated notifications, promotions)?
2996
- 3. Assign a category that groups it with similar emails (e.g. "Finance & Trading", "Real Estate", "Work", "Personal", "Promotions & Newsletters", "Travel", "Health", etc.).
2997
- 4. Write a 2-3 sentence summary of the email's content and what action (if any) is needed.
2998
-
2999
- Also write a 2-3 sentence executive summary of the overall inbox state.
3000
-
3001
- Here are the unread emails:
3002
-
3003
- ${emailsText}
3004
-
3005
- Respond with a JSON object in this exact format:
3006
- {
3007
- "summary": "2-3 sentence overview of the inbox",
3008
- "items": [
3009
- {
3010
- "emailId": "the email ID",
3011
- "summary": "2-3 sentence summary of this email's content and any action needed",
3012
- "isRelevant": true or false,
3013
- "relevanceReason": "brief explanation of why relevant or not",
3014
- "category": "short category label (2-4 words)"
3015
- }
3016
- ]
3017
- }
3018
-
3019
- Be conservative with "isRelevant: false" \u2014 only mark as not relevant if clearly promotional, newsletter, or automated. Personal emails, work emails, and anything requiring a response should be relevant. Group similar emails under the same category label.`;
3020
- }
3021
- function runAgent(prompt2, agent) {
3022
- return new Promise((resolve7, reject) => {
3023
- let bin;
3024
- let args;
3025
- if (agent === "codex") {
3026
- bin = "codex";
3027
- args = ["exec", "--full-auto", prompt2];
3028
- } else {
3029
- bin = "claude";
3030
- args = ["-p", "--dangerously-skip-permissions", prompt2];
3031
- }
3032
- const child = spawn5(bin, args, {
3033
- stdio: ["ignore", "pipe", "pipe"]
3034
- });
3035
- let output = "";
3036
- let errOutput = "";
3037
- child.stdout?.on("data", (d) => {
3038
- output += d.toString();
3039
- });
3040
- child.stderr?.on("data", (d) => {
3041
- errOutput += d.toString();
3042
- });
3043
- child.on("exit", (code) => {
3044
- if (code === 0) resolve7(output.trim());
3045
- else reject(new Error(`${bin} exited with code ${code}
3046
- ${errOutput.trim()}`));
3047
- });
3048
- });
3049
- }
3050
- async function processDigest(digest, agent) {
3051
- const sid = shortId2(digest.id);
3052
- if (digest.rawMessages.length === 0) {
3053
- await api.post(`/api/email-digest/${digest.id}/complete`, {
3054
- summary: "No unread emails found in your inbox.",
3055
- items: []
3056
- });
3057
- logOk(`${paint4("yellow", sid)} completed (no emails)`);
3058
- return;
3059
- }
3060
- log(`${paint4("yellow", sid)} analyzing ${paint4("cyan", String(digest.rawMessages.length))} emails\u2026`);
3061
- const prompt2 = buildPrompt(digest.rawMessages);
3062
- const output = await runAgent(prompt2, agent);
3063
- const jsonMatch = output.match(/\{[\s\S]*\}/);
3064
- if (!jsonMatch) {
3065
- throw new Error("Could not extract JSON from Claude output");
3066
- }
3067
- const parsed = JSON.parse(jsonMatch[0]);
3068
- const msgMap = new Map(digest.rawMessages.map((m) => [m.id, m]));
3069
- const items = parsed.items.map((item) => {
3070
- const msg = msgMap.get(item.emailId);
3071
- if (!msg) return null;
3072
- return {
3073
- emailId: msg.id,
3074
- threadId: msg.threadId,
3075
- subject: msg.subject,
3076
- sender: msg.sender,
3077
- senderEmail: msg.senderEmail,
3078
- date: msg.date,
3079
- snippet: msg.snippet,
3080
- summary: item.summary || msg.snippet,
3081
- isRelevant: item.isRelevant,
3082
- relevanceReason: item.relevanceReason,
3083
- category: item.category || (item.isRelevant ? "General" : "Promotions & Newsletters"),
3084
- archived: false
3085
- };
3086
- }).filter((item) => item !== null);
3087
- await api.post(`/api/email-digest/${digest.id}/complete`, {
3088
- summary: parsed.summary,
3089
- items
3090
- });
3091
- const relevantItems = items.filter((i) => i.isRelevant);
3092
- const noiseItems = items.filter((i) => !i.isRelevant);
3093
- logOk(
3094
- `${paint4("yellow", sid)} done \u2014 ${paint4("green", String(relevantItems.length))} relevant, ${paint4("gray", String(noiseItems.length))} not relevant`
3095
- );
3096
- console.log("");
3097
- console.log(` ${paint4("bold", "Summary:")} ${parsed.summary}`);
3098
- console.log("");
3099
- if (relevantItems.length > 0) {
3100
- console.log(` ${paint4("bold", paint4("green", "Important"))}`);
3101
- for (const item of relevantItems) {
3102
- console.log(` ${paint4("cyan", "\u2022")} ${item.subject} ${paint4("dim", `\u2014 ${item.sender}`)}`);
3103
- }
3104
- console.log("");
3105
- }
3106
- if (noiseItems.length > 0) {
3107
- console.log(` ${paint4("dim", "Not important")}`);
3108
- for (const item of noiseItems) {
3109
- console.log(` ${paint4("dim", `\u2022 ${item.subject} \u2014 ${item.sender}`)}`);
3110
- }
3111
- console.log("");
3112
- }
3113
- }
3114
- async function generateDigest() {
3115
- let result;
3116
- try {
3117
- result = await api.post("/api/email-digest/generate");
3118
- } catch (err) {
3119
- logErr(`Auto-generate failed: ${err.message}`);
3120
- return;
3121
- }
3122
- if (result.needsReauth) {
3123
- logErr("Auto-generate skipped: Gmail re-authentication required");
3124
- return;
3125
- }
3126
- if (result.status === "completed") {
3127
- log(`Auto-generated digest ${paint4("yellow", shortId2(result.id))} \u2014 no new emails`);
3128
- } else {
3129
- log(`Auto-generated digest ${paint4("yellow", shortId2(result.id))} \u2014 queued for processing`);
3130
- }
3131
- }
3132
- var digestCommand = new Command13("digest").description("Process pending email digests using an AI coding agent (run alongside mr watch)").option("--interval <seconds>", "Polling interval in seconds", "15").option("--generate-interval <seconds>", "How often to auto-generate new digests", "3600").option("--once", "Process pending digests once and exit", false).option("--agent <agent>", "AI agent to use: claude or codex", "claude").action(async (opts) => {
3133
- const intervalMs = parseInt(opts.interval, 10) * 1e3;
3134
- const generateIntervalMs = parseInt(opts.generateInterval, 10) * 1e3;
3135
- const once = opts.once;
3136
- const agent = opts.agent === "codex" ? "codex" : "claude";
3137
- const processing = /* @__PURE__ */ new Set();
3138
- const banner = [
3139
- ``,
3140
- paint4("magenta", ` \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2566\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557`),
3141
- paint4("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2551 \u2566\u2551\u2563 \u255A\u2550\u2557 \u2551`),
3142
- paint4("magenta", ` \u2569 \u2569\u2569\u255A\u2550 \u2550\u2569\u255D\u2569\u255A\u2550\u255D\u255A\u2550\u255D\u255A\u2550\u255D \u2569`),
3143
- paint4("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3144
- paint4("dim", ` email digest processor \xB7 powered by ${agent}`),
3145
- ``
3146
- ].join("\n");
3147
- console.log(banner);
3148
- console.log(
3149
- ` interval=${paint4("cyan", opts.interval + "s")} generate-interval=${paint4("cyan", opts.generateInterval + "s")}${once ? ` ${paint4("yellow", "once")}` : ""}
3150
- `
3151
- );
3152
- async function poll() {
3153
- let pending;
3154
- try {
3155
- pending = await api.get("/api/email-digest/pending");
3156
- } catch (err) {
3157
- logErr(`Failed to fetch pending digests: ${err.message}`);
3158
- return;
3159
- }
3160
- if (pending.length === 0) return;
3161
- const digest = pending[pending.length - 1];
3162
- if (processing.has(digest.id)) return;
3163
- log(`Processing digest ${paint4("yellow", shortId2(digest.id))} (${paint4("cyan", String(digest.rawMessages.length))} emails)`);
3164
- processing.add(digest.id);
3165
- processDigest(digest, agent).catch((err) => logErr(`${shortId2(digest.id)} failed: ${err.message}`)).finally(() => processing.delete(digest.id));
3166
- }
3167
- process.on("SIGINT", () => {
3168
- console.log(`
3169
- ${timestamp2()} ${digestTag()} Shutting down\u2026`);
3170
- process.exit(0);
3171
- });
3172
- await generateDigest();
3173
- await poll();
3174
- if (once) {
3175
- const wait = () => new Promise((resolve7) => {
3176
- if (processing.size === 0) return resolve7();
3177
- const check = setInterval(() => {
3178
- if (processing.size === 0) {
3179
- clearInterval(check);
3180
- resolve7();
3181
- }
3182
- }, 500);
3183
- });
3184
- await wait();
3185
- } else {
3186
- setInterval(poll, intervalMs);
3187
- setInterval(generateDigest, generateIntervalMs);
3188
- }
3189
- });
3190
-
3191
- // cli/commands/up.ts
3192
- import { Command as Command14 } from "commander";
3193
- import { spawn as spawn6 } from "child_process";
3194
- import { resolve as resolve2 } from "path";
3195
- import { fileURLToPath as fileURLToPath2 } from "url";
3196
- var c5 = {
3197
- reset: "\x1B[0m",
3198
- bold: "\x1B[1m",
3199
- dim: "\x1B[2m",
3200
- cyan: "\x1B[36m",
3201
- green: "\x1B[32m",
3202
- yellow: "\x1B[33m",
3203
- red: "\x1B[31m",
3204
- magenta: "\x1B[35m",
3205
- gray: "\x1B[90m"
3206
- };
3207
- function paint5(color, text) {
3208
- return `${c5[color]}${text}${c5.reset}`;
3209
- }
3210
- function timestamp3() {
3211
- return paint5("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
3212
- }
3213
- function upTag() {
3214
- return paint5("cyan", "[up]");
3215
- }
3216
- function log2(msg) {
3217
- console.log(`${timestamp3()} ${upTag()} ${msg}`);
3218
- }
3219
- function logErr2(msg) {
3220
- console.error(`${timestamp3()} ${upTag()} ${paint5("red", "\u2717")} ${msg}`);
3019
+ console.error(`${timestamp2()} ${upTag()} ${paint4("red", "\u2717")} ${msg}`);
3221
3020
  }
3222
3021
  function spawnMr(args) {
3223
3022
  const entry = process.argv[1];
3224
- const child = spawn6(process.execPath, [entry, ...args], {
3023
+ const child = spawn5(process.execPath, [entry, ...args], {
3225
3024
  stdio: ["ignore", "pipe", "pipe"]
3226
3025
  });
3227
3026
  return child;
3228
3027
  }
3229
- var upCommand = new Command14("up").description("Run mr watch and mr digest together with a single command").option("--interval <seconds>", "Polling interval for both watch and digest", "15").option("--watch-interval <seconds>", "Override polling interval for mr watch").option("--digest-interval <seconds>", "Override polling interval for mr digest").option("--dry-run", "Pass --dry-run to mr watch", false).option("--plan-approval", "Pass --plan-approval to mr watch", false).option("--root <dir>", "Pass --root to mr watch (default: cwd)").option("--agent <agent>", "AI agent to use: claude or codex", "claude").action((opts) => {
3028
+ var upCommand = new Command13("up").description("Run mr watch and mr digest together with a single command").option("--interval <seconds>", "Polling interval for both watch and digest", "15").option("--watch-interval <seconds>", "Override polling interval for mr watch").option("--digest-interval <seconds>", "Override polling interval for mr digest").option("--dry-run", "Pass --dry-run to mr watch", false).option("--plan-approval", "Pass --plan-approval to mr watch", false).option("--root <dir>", "Pass --root to mr watch (default: cwd)").option("--agent <agent>", "AI agent to use: claude or codex", "claude").action((opts) => {
3230
3029
  const watchInterval = opts.watchInterval ?? opts.interval;
3231
3030
  const digestInterval = opts.digestInterval ?? opts.interval;
3232
3031
  const agent = opts.agent === "codex" ? "codex" : "claude";
@@ -3237,21 +3036,21 @@ var upCommand = new Command14("up").description("Run mr watch and mr digest toge
3237
3036
  const digestArgs = ["digest", "--interval", digestInterval, "--agent", agent];
3238
3037
  const banner = [
3239
3038
  ``,
3240
- paint5("cyan", ` \u2554\u2566\u2557\u2566\u2550\u2557 \u2566 \u2566\u2554\u2550\u2557`),
3241
- paint5("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D \u2551 \u2551\u2560\u2550\u255D`),
3242
- paint5("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u2569 `),
3243
- paint5("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3244
- paint5("dim", ` mr watch + mr digest \xB7 powered by ${agent}`),
3039
+ paint4("cyan", ` \u2554\u2566\u2557\u2566\u2550\u2557 \u2566 \u2566\u2554\u2550\u2557`),
3040
+ paint4("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D \u2551 \u2551\u2560\u2550\u255D`),
3041
+ paint4("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u2569 `),
3042
+ paint4("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3043
+ paint4("dim", ` mr watch + mr digest \xB7 powered by ${agent}`),
3245
3044
  ``
3246
3045
  ].join("\n");
3247
3046
  console.log(banner);
3248
3047
  const flags = [
3249
- `watch-interval=${paint5("cyan", watchInterval + "s")}`,
3250
- `digest-interval=${paint5("cyan", digestInterval + "s")}`,
3251
- `agent=${paint5("cyan", agent)}`,
3252
- ...opts.dryRun ? [paint5("yellow", "dry-run")] : [],
3253
- ...opts.planApproval ? [paint5("yellow", "plan-approval")] : [],
3254
- ...opts.root ? [`root=${paint5("cyan", resolve2(opts.root))}`] : []
3048
+ `watch-interval=${paint4("cyan", watchInterval + "s")}`,
3049
+ `digest-interval=${paint4("cyan", digestInterval + "s")}`,
3050
+ `agent=${paint4("cyan", agent)}`,
3051
+ ...opts.dryRun ? [paint4("yellow", "dry-run")] : [],
3052
+ ...opts.planApproval ? [paint4("yellow", "plan-approval")] : [],
3053
+ ...opts.root ? [`root=${paint4("cyan", resolve2(opts.root))}`] : []
3255
3054
  ].join(" ");
3256
3055
  console.log(` ${flags}
3257
3056
  `);
@@ -3262,12 +3061,12 @@ var upCommand = new Command14("up").description("Run mr watch and mr digest toge
3262
3061
  proc.stderr?.on("data", (d) => process.stderr.write(d));
3263
3062
  proc.on("exit", (code, signal) => {
3264
3063
  if (signal !== "SIGTERM" && signal !== "SIGINT") {
3265
- logErr2(`mr ${label} exited unexpectedly (code=${code ?? "?"}, signal=${signal ?? "none"})`);
3064
+ logErr(`mr ${label} exited unexpectedly (code=${code ?? "?"}, signal=${signal ?? "none"})`);
3266
3065
  }
3267
3066
  });
3268
3067
  }
3269
3068
  function shutdown() {
3270
- log2("Shutting down\u2026");
3069
+ log("Shutting down\u2026");
3271
3070
  watchProc.kill("SIGTERM");
3272
3071
  digestProc.kill("SIGTERM");
3273
3072
  setTimeout(() => process.exit(0), 500);
@@ -3277,8 +3076,8 @@ var upCommand = new Command14("up").description("Run mr watch and mr digest toge
3277
3076
  });
3278
3077
 
3279
3078
  // cli/commands/prototype.ts
3280
- import { Command as Command15 } from "commander";
3281
- var c6 = {
3079
+ import { Command as Command14 } from "commander";
3080
+ var c5 = {
3282
3081
  reset: "\x1B[0m",
3283
3082
  bold: "\x1B[1m",
3284
3083
  dim: "\x1B[2m",
@@ -3289,25 +3088,25 @@ var c6 = {
3289
3088
  blue: "\x1B[34m",
3290
3089
  gray: "\x1B[90m"
3291
3090
  };
3292
- function paint6(color, text) {
3293
- return `${c6[color]}${text}${c6.reset}`;
3091
+ function paint5(color, text) {
3092
+ return `${c5[color]}${text}${c5.reset}`;
3294
3093
  }
3295
3094
  function statusBadge(status) {
3296
3095
  switch (status) {
3297
3096
  case "pending":
3298
- return paint6("gray", "\u25CB pending");
3097
+ return paint5("gray", "\u25CB pending");
3299
3098
  case "in_progress":
3300
- return paint6("cyan", "\u27F3 in_progress");
3099
+ return paint5("cyan", "\u27F3 in_progress");
3301
3100
  case "completed":
3302
- return paint6("green", "\u2713 completed");
3101
+ return paint5("green", "\u2713 completed");
3303
3102
  case "failed":
3304
- return paint6("red", "\u2717 failed");
3103
+ return paint5("red", "\u2717 failed");
3305
3104
  default:
3306
- return paint6("gray", status);
3105
+ return paint5("gray", status);
3307
3106
  }
3308
3107
  }
3309
- var prototypeCommand = new Command15("prototype").description("Manage prototypes").addCommand(
3310
- new Command15("list").description("List prototypes for the linked project").option("--all", "Show prototypes for all projects").action(async (opts) => {
3108
+ var prototypeCommand = new Command14("prototype").description("Manage prototypes").addCommand(
3109
+ new Command14("list").description("List prototypes for the linked project").option("--all", "Show prototypes for all projects").action(async (opts) => {
3311
3110
  const params = new URLSearchParams();
3312
3111
  if (!opts.all) {
3313
3112
  const projectId = getLinkedProjectId();
@@ -3319,21 +3118,21 @@ var prototypeCommand = new Command15("prototype").description("Manage prototypes
3319
3118
  }
3320
3119
  const prototypes = await api.get(`/api/prototypes?${params.toString()}`);
3321
3120
  if (prototypes.length === 0) {
3322
- console.log(paint6("gray", "No prototypes found."));
3121
+ console.log(paint5("gray", "No prototypes found."));
3323
3122
  return;
3324
3123
  }
3325
3124
  console.log();
3326
3125
  for (const p of prototypes) {
3327
3126
  const date = new Date(p.createdAt).toLocaleDateString();
3328
3127
  console.log(
3329
- ` ${paint6("bold", p.title)} ${statusBadge(p.status)} ${paint6("gray", p.id.slice(0, 8))} ${paint6("dim", date)}`
3128
+ ` ${paint5("bold", p.title)} ${statusBadge(p.status)} ${paint5("gray", p.id.slice(0, 8))} ${paint5("dim", date)}`
3330
3129
  );
3331
- console.log(` ${paint6("dim", p.prompt.slice(0, 80) + (p.prompt.length > 80 ? "\u2026" : ""))}`);
3130
+ console.log(` ${paint5("dim", p.prompt.slice(0, 80) + (p.prompt.length > 80 ? "\u2026" : ""))}`);
3332
3131
  console.log();
3333
3132
  }
3334
3133
  })
3335
3134
  ).addCommand(
3336
- new Command15("create").description("Create a new prototype").argument("<title>", "Title of the prototype").requiredOption("--prompt <prompt>", "Design description / prompt").option("--project <projectId>", "Project ID (defaults to linked project)").option("--variants <count>", "Number of variants to generate (1-50)", "5").action(async (title, opts) => {
3135
+ new Command14("create").description("Create a new prototype").argument("<title>", "Title of the prototype").requiredOption("--prompt <prompt>", "Design description / prompt").option("--project <projectId>", "Project ID (defaults to linked project)").option("--variants <count>", "Number of variants to generate (1-50)", "5").action(async (title, opts) => {
3337
3136
  const projectId = opts.project ?? getLinkedProjectId();
3338
3137
  if (!projectId) {
3339
3138
  console.error('No project linked. Run "mr link <project-id>" or use --project.');
@@ -3347,37 +3146,37 @@ var prototypeCommand = new Command15("prototype").description("Manage prototypes
3347
3146
  projectId
3348
3147
  });
3349
3148
  console.log();
3350
- console.log(` ${paint6("green", "\u2713")} Created prototype: ${paint6("bold", prototype.title)}`);
3351
- console.log(` ${paint6("gray", "ID:")} ${prototype.id}`);
3352
- console.log(` ${paint6("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
3149
+ console.log(` ${paint5("green", "\u2713")} Created prototype: ${paint5("bold", prototype.title)}`);
3150
+ console.log(` ${paint5("gray", "ID:")} ${prototype.id}`);
3151
+ console.log(` ${paint5("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
3353
3152
  console.log();
3354
3153
  })
3355
3154
  ).addCommand(
3356
- new Command15("start").description("Start prototype generation (sets status to in_progress)").argument("<id>", "Prototype ID").action(async (id) => {
3155
+ new Command14("start").description("Start prototype generation (sets status to in_progress)").argument("<id>", "Prototype ID").action(async (id) => {
3357
3156
  const prototype = await api.patch(`/api/prototypes/${id}`, {
3358
3157
  status: "in_progress"
3359
3158
  });
3360
3159
  console.log();
3361
- console.log(` ${paint6("cyan", "\u27F3")} Started: ${paint6("bold", prototype.title)}`);
3362
- console.log(` ${paint6("gray", "The watch agent will pick this up shortly.")}`);
3160
+ console.log(` ${paint5("cyan", "\u27F3")} Started: ${paint5("bold", prototype.title)}`);
3161
+ console.log(` ${paint5("gray", "The watch agent will pick this up shortly.")}`);
3363
3162
  console.log();
3364
3163
  })
3365
3164
  ).addCommand(
3366
- new Command15("retry").description("Retry a failed prototype").argument("<id>", "Prototype ID").action(async (id) => {
3165
+ new Command14("retry").description("Retry a failed prototype").argument("<id>", "Prototype ID").action(async (id) => {
3367
3166
  const prototype = await api.patch(`/api/prototypes/${id}`, {
3368
3167
  status: "in_progress",
3369
3168
  files: null
3370
3169
  });
3371
3170
  console.log();
3372
- console.log(` ${paint6("cyan", "\u27F3")} Retrying: ${paint6("bold", prototype.title)}`);
3171
+ console.log(` ${paint5("cyan", "\u27F3")} Retrying: ${paint5("bold", prototype.title)}`);
3373
3172
  console.log();
3374
3173
  })
3375
3174
  );
3376
3175
 
3377
3176
  // cli/commands/setup.ts
3378
- import { Command as Command16 } from "commander";
3177
+ import { Command as Command15 } from "commander";
3379
3178
  import { exec as exec2 } from "child_process";
3380
- var c7 = {
3179
+ var c6 = {
3381
3180
  reset: "\x1B[0m",
3382
3181
  bold: "\x1B[1m",
3383
3182
  dim: "\x1B[2m",
@@ -3388,8 +3187,8 @@ var c7 = {
3388
3187
  magenta: "\x1B[35m",
3389
3188
  gray: "\x1B[90m"
3390
3189
  };
3391
- function paint7(color, text) {
3392
- return `${c7[color]}${text}${c7.reset}`;
3190
+ function paint6(color, text) {
3191
+ return `${c6[color]}${text}${c6.reset}`;
3393
3192
  }
3394
3193
  function commandExists(cmd) {
3395
3194
  return new Promise((resolve7) => {
@@ -3633,60 +3432,60 @@ async function checkApiConnectivity() {
3633
3432
  }
3634
3433
  }
3635
3434
  function printResults(checks) {
3636
- const maxNameLen = Math.max(...checks.map((c12) => c12.name.length));
3435
+ const maxNameLen = Math.max(...checks.map((c11) => c11.name.length));
3637
3436
  let allOk = true;
3638
3437
  for (const check of checks) {
3639
3438
  const isOptional = check.optional ?? false;
3640
- const icon = check.ok ? paint7("green", "\u2713") : isOptional ? paint7("yellow", "\u25CB") : paint7("red", "\u2717");
3439
+ const icon = check.ok ? paint6("green", "\u2713") : isOptional ? paint6("yellow", "\u25CB") : paint6("red", "\u2717");
3641
3440
  const name = check.name.padEnd(maxNameLen);
3642
- const msg = check.ok ? paint7("green", check.message) : isOptional ? paint7("yellow", check.message) : paint7("red", check.message);
3441
+ const msg = check.ok ? paint6("green", check.message) : isOptional ? paint6("yellow", check.message) : paint6("red", check.message);
3643
3442
  console.log(` ${icon} ${name} ${msg}`);
3644
3443
  if (!check.ok && !isOptional) allOk = false;
3645
3444
  }
3646
3445
  return allOk;
3647
3446
  }
3648
3447
  async function autoFix(checks, agent) {
3649
- const { spawn: spawn10 } = await import("child_process");
3650
- const ghInstalled = checks.find((c12) => c12.name === "GitHub CLI (gh)").ok;
3651
- const ghAuthed = checks.find((c12) => c12.name === "GitHub CLI auth").ok;
3652
- const mrAuthed = checks.find((c12) => c12.name === "Mr. Manager CLI auth").ok;
3653
- const claudeCheck = checks.find((c12) => c12.name === "Claude Code (claude)");
3448
+ const { spawn: spawn9 } = await import("child_process");
3449
+ const ghInstalled = checks.find((c11) => c11.name === "GitHub CLI (gh)").ok;
3450
+ const ghAuthed = checks.find((c11) => c11.name === "GitHub CLI auth").ok;
3451
+ const mrAuthed = checks.find((c11) => c11.name === "Mr. Manager CLI auth").ok;
3452
+ const claudeCheck = checks.find((c11) => c11.name === "Claude Code (claude)");
3654
3453
  if (claudeCheck && !claudeCheck.ok && agent === "claude") {
3655
- console.log(paint7("cyan", " Installing Claude Code..."));
3656
- console.log(paint7("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
3454
+ console.log(paint6("cyan", " Installing Claude Code..."));
3455
+ console.log(paint6("dim", " Running: curl -fsSL https://claude.ai/install.sh | bash"));
3657
3456
  await new Promise((resolve7) => {
3658
- const child = spawn10("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
3457
+ const child = spawn9("bash", ["-c", "curl -fsSL https://claude.ai/install.sh | bash"], { stdio: "inherit" });
3659
3458
  child.on("exit", () => resolve7());
3660
3459
  });
3661
3460
  console.log("");
3662
3461
  }
3663
3462
  if (ghInstalled && !ghAuthed) {
3664
- console.log(paint7("cyan", " Running gh auth login..."));
3463
+ console.log(paint6("cyan", " Running gh auth login..."));
3665
3464
  await new Promise((resolve7) => {
3666
- const child = spawn10("gh", ["auth", "login"], { stdio: "inherit" });
3465
+ const child = spawn9("gh", ["auth", "login"], { stdio: "inherit" });
3667
3466
  child.on("exit", () => resolve7());
3668
3467
  });
3669
3468
  console.log("");
3670
3469
  }
3671
3470
  if (!mrAuthed) {
3672
- console.log(paint7("cyan", " Running mr login..."));
3471
+ console.log(paint6("cyan", " Running mr login..."));
3673
3472
  const entry = process.argv[1];
3674
3473
  await new Promise((resolve7) => {
3675
- const child = spawn10(process.execPath, [entry, "login"], { stdio: "inherit" });
3474
+ const child = spawn9(process.execPath, [entry, "login"], { stdio: "inherit" });
3676
3475
  child.on("exit", () => resolve7());
3677
3476
  });
3678
3477
  console.log("");
3679
3478
  }
3680
3479
  }
3681
- var setupCommand = new Command16("setup").description("Check that all dependencies for mr watch are installed and configured").option("--fix", "Attempt to auto-fix issues where possible", false).option("--agent <agent>", "AI agent to check: claude, codex, or gemini (default: claude)", "claude").action(async (opts) => {
3480
+ var setupCommand = new Command15("setup").description("Check that all dependencies for mr watch are installed and configured").option("--fix", "Attempt to auto-fix issues where possible", false).option("--agent <agent>", "AI agent to check: claude, codex, or gemini (default: claude)", "claude").action(async (opts) => {
3682
3481
  const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
3683
3482
  const banner = [
3684
3483
  ``,
3685
- paint7("cyan", ` \u2554\u2566\u2557\u2551\u2550\u2557 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2551 \u2551\u2554\u2550\u2557`),
3686
- paint7("magenta", ` \u2551\u2551\u2551\u2560\u2566\u2563 \u255A\u2550\u2557\u2551\u2563 \u2551 \u2551 \u2551\u2560\u2550\u2563`),
3687
- paint7("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u255A\u2550\u255D \u2569 \u255A\u2550\u255D\u2569 `),
3688
- paint7("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`),
3689
- paint7("dim", ` environment check for mr watch (agent: ${agent})`),
3484
+ paint6("cyan", ` \u2554\u2566\u2557\u2551\u2550\u2557 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2551 \u2551\u2554\u2550\u2557`),
3485
+ paint6("magenta", ` \u2551\u2551\u2551\u2560\u2566\u2563 \u255A\u2550\u2557\u2551\u2563 \u2551 \u2551 \u2551\u2560\u2550\u2563`),
3486
+ paint6("cyan", ` \u2569 \u2569\u2569\u255A\u2550 \u255A\u2550\u255D\u255A\u2550\u255D \u2569 \u255A\u2550\u255D\u2569 `),
3487
+ paint6("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
3488
+ paint6("dim", ` environment check for mr watch (agent: ${agent})`),
3690
3489
  ``
3691
3490
  ].join("\n");
3692
3491
  console.log(banner);
@@ -3706,18 +3505,18 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
3706
3505
  const allOk = printResults(checks);
3707
3506
  console.log("");
3708
3507
  if (allOk) {
3709
- console.log(paint7("green", " All checks passed! You're ready to run mr watch."));
3508
+ console.log(paint6("green", " All checks passed! You're ready to run mr watch."));
3710
3509
  if (agent === "claude") {
3711
- console.log(paint7("dim", " Tip: check other agents with --agent codex or --agent gemini"));
3510
+ console.log(paint6("dim", " Tip: check other agents with --agent codex or --agent gemini"));
3712
3511
  }
3713
3512
  console.log("");
3714
3513
  return;
3715
3514
  }
3716
- const fixes = checks.filter((c12) => !c12.ok && c12.fix && !c12.optional);
3515
+ const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
3717
3516
  if (fixes.length > 0) {
3718
- console.log(paint7("yellow", " To fix:"));
3517
+ console.log(paint6("yellow", " To fix:"));
3719
3518
  for (const fix of fixes) {
3720
- console.log(` ${paint7("dim", "\u2192")} ${paint7("bold", fix.name)}: ${fix.fix}`);
3519
+ console.log(` ${paint6("dim", "\u2192")} ${paint6("bold", fix.name)}: ${fix.fix}`);
3721
3520
  }
3722
3521
  console.log("");
3723
3522
  }
@@ -3728,8 +3527,8 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
3728
3527
  });
3729
3528
 
3730
3529
  // cli/commands/update.ts
3731
- import { Command as Command17 } from "commander";
3732
- var updateCommand = new Command17("update").description("Post a status update to a task (used by agents to report progress)").argument("<task-id>", "Task ID").argument("<message>", "Status update message").option("--source <source>", "Update source: agent, system, or user", "agent").action(async (taskId, message, opts) => {
3530
+ import { Command as Command16 } from "commander";
3531
+ var updateCommand = new Command16("update").description("Post a status update to a task (used by agents to report progress)").argument("<task-id>", "Task ID").argument("<message>", "Status update message").option("--source <source>", "Update source: agent, system, or user", "agent").action(async (taskId, message, opts) => {
3733
3532
  await api.post(`/api/tasks/${taskId}/updates`, {
3734
3533
  message,
3735
3534
  source: opts.source
@@ -3738,11 +3537,11 @@ var updateCommand = new Command17("update").description("Post a status update to
3738
3537
  });
3739
3538
 
3740
3539
  // cli/commands/screenshot.ts
3741
- import { Command as Command18 } from "commander";
3540
+ import { Command as Command17 } from "commander";
3742
3541
  import { readFileSync as readFileSync6, existsSync as existsSync7, unlinkSync as unlinkSync2 } from "fs";
3743
3542
  import { join as join8 } from "path";
3744
3543
  import { tmpdir } from "os";
3745
- var screenshotCommand = new Command18("screenshot").description(
3544
+ var screenshotCommand = new Command17("screenshot").description(
3746
3545
  "Take or attach a screenshot to a task update (agents use this to show their work)"
3747
3546
  ).argument("<task-id>", "Task ID").argument("[file]", "Path to an image file (if omitted, uses headless browser to screenshot the app)").option("-m, --message <message>", "Optional message to include with the screenshot").option("-u, --url <url>", "Custom URL to screenshot (defaults to the task's project page)").action(async (taskId, file, opts) => {
3748
3547
  let filePath = file;
@@ -3833,10 +3632,10 @@ var screenshotCommand = new Command18("screenshot").description(
3833
3632
  });
3834
3633
 
3835
3634
  // cli/commands/resume.ts
3836
- import { Command as Command19 } from "commander";
3837
- import { spawn as spawn7 } from "child_process";
3635
+ import { Command as Command18 } from "commander";
3636
+ import { spawn as spawn6 } from "child_process";
3838
3637
  import { resolve as resolve3 } from "path";
3839
- var c8 = {
3638
+ var c7 = {
3840
3639
  reset: "\x1B[0m",
3841
3640
  bold: "\x1B[1m",
3842
3641
  dim: "\x1B[2m",
@@ -3847,15 +3646,15 @@ var c8 = {
3847
3646
  magenta: "\x1B[35m",
3848
3647
  gray: "\x1B[90m"
3849
3648
  };
3850
- function paint8(color, text) {
3851
- return `${c8[color]}${text}${c8.reset}`;
3649
+ function paint7(color, text) {
3650
+ return `${c7[color]}${text}${c7.reset}`;
3852
3651
  }
3853
- var resumeCommand = new Command19("resume").description("Resume an interactive Claude session for a task (non-headless)").argument("<task-id>", "Task ID whose Claude session to resume").option("--dir <directory>", "Override the working directory for the session").action(async (taskId, opts) => {
3652
+ var resumeCommand = new Command18("resume").description("Resume an interactive Claude session for a task (non-headless)").argument("<task-id>", "Task ID whose Claude session to resume").option("--dir <directory>", "Override the working directory for the session").action(async (taskId, opts) => {
3854
3653
  const task = await api.get(`/api/tasks/${taskId}`);
3855
3654
  if (!task.claudeSessionId) {
3856
3655
  console.error(
3857
3656
  `
3858
- ${paint8("red", "\u2717")} No Claude session found for task ${paint8("dim", taskId.slice(0, 8))}`
3657
+ ${paint7("red", "\u2717")} No Claude session found for task ${paint7("dim", taskId.slice(0, 8))}`
3859
3658
  );
3860
3659
  console.error(
3861
3660
  ` The task may not have been dispatched yet.
@@ -3863,19 +3662,19 @@ var resumeCommand = new Command19("resume").description("Resume an interactive C
3863
3662
  );
3864
3663
  process.exit(1);
3865
3664
  }
3866
- if (task.completed || !task.inProgress && !task.delegated) {
3665
+ if (task.status === "completed" || task.status === "todo") {
3867
3666
  console.error(
3868
3667
  `
3869
- ${paint8("yellow", "\u26A0")} Task ${paint8("dim", taskId.slice(0, 8))} has already completed.`
3668
+ ${paint7("yellow", "\u26A0")} Task ${paint7("dim", taskId.slice(0, 8))} has already completed.`
3870
3669
  );
3871
3670
  console.error(
3872
- ` Session ID: ${paint8("cyan", task.claudeSessionId)}`
3671
+ ` Session ID: ${paint7("cyan", task.claudeSessionId)}`
3873
3672
  );
3874
3673
  console.error(
3875
3674
  ` View the session log in the Mr. Manager web UI, or run:`
3876
3675
  );
3877
3676
  console.error(
3878
- ` ${paint8("dim", `claude --resume ${task.claudeSessionId}`)}
3677
+ ` ${paint7("dim", `claude --resume ${task.claudeSessionId}`)}
3879
3678
  `
3880
3679
  );
3881
3680
  process.exit(0);
@@ -3894,16 +3693,16 @@ var resumeCommand = new Command19("resume").description("Resume an interactive C
3894
3693
  const sid = taskId.slice(0, 8);
3895
3694
  console.log([
3896
3695
  ``,
3897
- paint8("magenta", ` \u2554\u2566\u2557\u2566\u2550\u2557 `) + paint8("bold", "resume"),
3898
- paint8("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D `) + paint8("dim", "\u2500".repeat(44)),
3899
- paint8("magenta", ` \u2569 \u2569\u2569\u255A\u2550 `) + task.title,
3900
- ` ${paint8("gray", sid)}`,
3696
+ paint7("magenta", ` \u2554\u2566\u2557\u2566\u2550\u2557 `) + paint7("bold", "resume"),
3697
+ paint7("magenta", ` \u2551\u2551\u2551\u2560\u2566\u255D `) + paint7("dim", "\u2500".repeat(44)),
3698
+ paint7("magenta", ` \u2569 \u2569\u2569\u255A\u2550 `) + task.title,
3699
+ ` ${paint7("gray", sid)}`,
3901
3700
  ``,
3902
- ` ${paint8("dim", "session")} ${paint8("cyan", task.claudeSessionId)}`,
3903
- ` ${paint8("dim", "cwd")} ${paint8("cyan", cwd)}`,
3701
+ ` ${paint7("dim", "session")} ${paint7("cyan", task.claudeSessionId)}`,
3702
+ ` ${paint7("dim", "cwd")} ${paint7("cyan", cwd)}`,
3904
3703
  ``
3905
3704
  ].join("\n"));
3906
- const child = spawn7("claude", ["--resume", task.claudeSessionId], {
3705
+ const child = spawn6("claude", ["--resume", task.claudeSessionId], {
3907
3706
  cwd,
3908
3707
  stdio: "inherit"
3909
3708
  });
@@ -3912,15 +3711,15 @@ var resumeCommand = new Command19("resume").description("Resume an interactive C
3912
3711
  });
3913
3712
  child.on("error", (err) => {
3914
3713
  console.error(`
3915
- ${paint8("red", "\u2717")} Failed to launch Claude: ${err.message}
3714
+ ${paint7("red", "\u2717")} Failed to launch Claude: ${err.message}
3916
3715
  `);
3917
3716
  process.exit(1);
3918
3717
  });
3919
3718
  });
3920
3719
 
3921
3720
  // cli/commands/browse.ts
3922
- import { Command as Command20 } from "commander";
3923
- import { execSync as execSync5, spawn as spawn8 } from "child_process";
3721
+ import { Command as Command19 } from "commander";
3722
+ import { execSync as execSync5, spawn as spawn7 } from "child_process";
3924
3723
  import { existsSync as existsSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "fs";
3925
3724
  import { join as join9 } from "path";
3926
3725
  var BROWSE_DIR2 = join9(import.meta.dirname, "..", "..", "browse");
@@ -3961,7 +3760,7 @@ async function ensureDevServer() {
3961
3760
  }
3962
3761
  const port = await findAvailablePort2(3e3);
3963
3762
  console.log(`[browse] Starting dev server on port ${port}...`);
3964
- const devProc = spawn8("npm", ["run", "dev", "--", "--port", String(port)], {
3763
+ const devProc = spawn7("npm", ["run", "dev", "--", "--port", String(port)], {
3965
3764
  stdio: ["ignore", "pipe", "pipe"],
3966
3765
  detached: true,
3967
3766
  cwd: join9(import.meta.dirname, "..", ".."),
@@ -3983,7 +3782,7 @@ async function ensureDevServer() {
3983
3782
  }
3984
3783
  throw new Error("Dev server failed to start within 30s");
3985
3784
  }
3986
- var browseCommand = new Command20("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
3785
+ var browseCommand = new Command19("browse").description("Control a headless browser for QA and testing").argument("[command]", "Browse command (goto, click, fill, screenshot, etc.)").argument("[args...]", "Command arguments").option(
3987
3786
  "--task-id <id>",
3988
3787
  "Attach screenshot to a task update (only for screenshot command)"
3989
3788
  ).option("--dev", "Auto-start local dev server before browsing").allowUnknownOption(true).action(
@@ -4066,10 +3865,10 @@ var browseCommand = new Command20("browse").description("Control a headless brow
4066
3865
  );
4067
3866
 
4068
3867
  // cli/commands/set-path.ts
4069
- import { Command as Command21 } from "commander";
3868
+ import { Command as Command20 } from "commander";
4070
3869
  import { resolve as resolve4 } from "path";
4071
3870
  import { existsSync as existsSync9 } from "fs";
4072
- var setPathCommand = new Command21("set-path").description("Set or update the local repo path for a project").argument("<project-id>", "Project ID").argument("<path>", "Absolute or relative path to the local repo").action(async (projectId, pathArg) => {
3871
+ var setPathCommand = new Command20("set-path").description("Set or update the local repo path for a project").argument("<project-id>", "Project ID").argument("<path>", "Absolute or relative path to the local repo").action(async (projectId, pathArg) => {
4073
3872
  const absolutePath = resolve4(pathArg);
4074
3873
  if (!existsSync9(absolutePath)) {
4075
3874
  console.error(`Error: Path does not exist: ${absolutePath}`);
@@ -4087,9 +3886,9 @@ var setPathCommand = new Command21("set-path").description("Set or update the lo
4087
3886
  });
4088
3887
 
4089
3888
  // cli/commands/test.ts
4090
- import { Command as Command22 } from "commander";
3889
+ import { Command as Command21 } from "commander";
4091
3890
  import { readFileSync as readFileSync8, existsSync as existsSync10 } from "fs";
4092
- var testCommand = new Command22("test").description("Run automated browser test for a task's MR/PR").argument("<task-id>", "Task ID to test").option("--plan <file>", "Path to a custom test plan JSON file").action(async (taskId, opts) => {
3891
+ var testCommand = new Command21("test").description("Run automated browser test for a task's MR/PR").argument("<task-id>", "Task ID to test").option("--plan <file>", "Path to a custom test plan JSON file").action(async (taskId, opts) => {
4093
3892
  const config = loadConfig();
4094
3893
  console.log("[test] Fetching task...");
4095
3894
  let task;
@@ -4209,11 +4008,11 @@ var testCommand = new Command22("test").description("Run automated browser test
4209
4008
  });
4210
4009
 
4211
4010
  // cli/commands/features.ts
4212
- import { Command as Command23 } from "commander";
4011
+ import { Command as Command22 } from "commander";
4213
4012
  import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync11 } from "fs";
4214
4013
  import { resolve as resolve5, sep as sep2 } from "path";
4215
4014
  var FEATURES_FILE3 = ".mr-features.md";
4216
- var c9 = {
4015
+ var c8 = {
4217
4016
  reset: "\x1B[0m",
4218
4017
  bold: "\x1B[1m",
4219
4018
  dim: "\x1B[2m",
@@ -4223,8 +4022,8 @@ var c9 = {
4223
4022
  magenta: "\x1B[35m",
4224
4023
  gray: "\x1B[90m"
4225
4024
  };
4226
- function paint9(color, text) {
4227
- return `${c9[color]}${text}${c9.reset}`;
4025
+ function paint8(color, text) {
4026
+ return `${c8[color]}${text}${c8.reset}`;
4228
4027
  }
4229
4028
  function resolveProjectRoot2() {
4230
4029
  const cwd = process.cwd();
@@ -4243,7 +4042,7 @@ function readFeatures2() {
4243
4042
  if (!existsSync11(path)) return null;
4244
4043
  return readFileSync9(path, "utf-8");
4245
4044
  }
4246
- var featuresCommand = new Command23("features").description("View or update the project features & goals document (.mr-features.md)").option("--update <content>", "Replace the features document with the given content").option("--file <path>", "Read content from a file and use it to update the features document").option("--path", "Print the path to the features file").action(async (opts) => {
4045
+ var featuresCommand = new Command22("features").description("View or update the project features & goals document (.mr-features.md)").option("--update <content>", "Replace the features document with the given content").option("--file <path>", "Read content from a file and use it to update the features document").option("--path", "Print the path to the features file").action(async (opts) => {
4247
4046
  if (opts.path) {
4248
4047
  console.log(getFeaturesPath());
4249
4048
  return;
@@ -4252,30 +4051,30 @@ var featuresCommand = new Command23("features").description("View or update the
4252
4051
  const content2 = readFileSync9(resolve5(opts.file), "utf-8");
4253
4052
  const featuresPath = getFeaturesPath();
4254
4053
  writeFileSync5(featuresPath, content2);
4255
- console.log(`${paint9("green", "\u2713")} Updated ${paint9("cyan", featuresPath)} from ${paint9("cyan", opts.file)}`);
4054
+ console.log(`${paint8("green", "\u2713")} Updated ${paint8("cyan", featuresPath)} from ${paint8("cyan", opts.file)}`);
4256
4055
  return;
4257
4056
  }
4258
4057
  if (opts.update) {
4259
4058
  const featuresPath = getFeaturesPath();
4260
4059
  writeFileSync5(featuresPath, opts.update);
4261
- console.log(`${paint9("green", "\u2713")} Updated ${paint9("cyan", featuresPath)}`);
4060
+ console.log(`${paint8("green", "\u2713")} Updated ${paint8("cyan", featuresPath)}`);
4262
4061
  return;
4263
4062
  }
4264
4063
  const content = readFeatures2();
4265
4064
  if (!content) {
4266
- console.log(paint9("dim", `No features document found. One will be created when an agent completes a task.`));
4267
- console.log(paint9("dim", `Path: ${getFeaturesPath()}`));
4065
+ console.log(paint8("dim", `No features document found. One will be created when an agent completes a task.`));
4066
+ console.log(paint8("dim", `Path: ${getFeaturesPath()}`));
4268
4067
  return;
4269
4068
  }
4270
4069
  console.log(content);
4271
4070
  });
4272
4071
 
4273
4072
  // cli/commands/no-mr.ts
4274
- import { Command as Command24 } from "commander";
4073
+ import { Command as Command23 } from "commander";
4275
4074
  import { writeFileSync as writeFileSync6 } from "fs";
4276
4075
  import { resolve as resolve6 } from "path";
4277
4076
  var NO_MR_FILE = ".mr-no-mr";
4278
- var noMrCommand = new Command24("no-mr").description("Signal that a task does not require a merge/pull request and describe what was done instead").argument("<task-id>", "Task ID").argument("<description>", "Description of what was done instead of creating an MR/PR").action(async (taskId, description) => {
4077
+ var noMrCommand = new Command23("no-mr").description("Signal that a task does not require a merge/pull request and describe what was done instead").argument("<task-id>", "Task ID").argument("<description>", "Description of what was done instead of creating an MR/PR").action(async (taskId, description) => {
4279
4078
  const filePath = resolve6(process.cwd(), NO_MR_FILE);
4280
4079
  writeFileSync6(filePath, description, "utf-8");
4281
4080
  await api.post(`/api/tasks/${taskId}/updates`, {
@@ -4287,8 +4086,8 @@ var noMrCommand = new Command24("no-mr").description("Signal that a task does no
4287
4086
  });
4288
4087
 
4289
4088
  // cli/commands/mobile.ts
4290
- import { Command as Command25 } from "commander";
4291
- function paint10(color, text) {
4089
+ import { Command as Command24 } from "commander";
4090
+ function paint9(color, text) {
4292
4091
  const colors = {
4293
4092
  cyan: "\x1B[36m",
4294
4093
  green: "\x1B[32m",
@@ -4299,7 +4098,7 @@ function paint10(color, text) {
4299
4098
  };
4300
4099
  return `${colors[color] ?? ""}${text}${colors.reset}`;
4301
4100
  }
4302
- var mobileCommand = new Command25("mobile").description(
4101
+ var mobileCommand = new Command24("mobile").description(
4303
4102
  "Start the Web-to-Mobile Conversion Wizard for the linked project"
4304
4103
  ).argument("[project-id]", "Project ID (defaults to linked project)").option("--framework <framework>", "Framework: react-native or native").option("--url <url>", "Web app URL for analysis").action(
4305
4104
  async (projectIdArg, opts) => {
@@ -4311,12 +4110,12 @@ var mobileCommand = new Command25("mobile").description(
4311
4110
  process.exit(1);
4312
4111
  }
4313
4112
  console.log(
4314
- paint10("cyan", "mobile") + paint10("dim", " \u2014 starting conversion wizard")
4113
+ paint9("cyan", "mobile") + paint9("dim", " \u2014 starting conversion wizard")
4315
4114
  );
4316
4115
  const task = await api.post("/api/tasks", {
4317
4116
  title: "Convert to Mobile App",
4318
4117
  projectId,
4319
- inProgress: true
4118
+ status: "in_progress"
4320
4119
  });
4321
4120
  console.log(` Created task: ${task.title} (${task.id})`);
4322
4121
  console.log(" Analyzing web app...");
@@ -4342,13 +4141,13 @@ var mobileCommand = new Command25("mobile").description(
4342
4141
  }
4343
4142
  console.log(
4344
4143
  `
4345
- ${paint10("green", "\u2713")} Wizard initialized. Open the web UI to continue:
4144
+ ${paint9("green", "\u2713")} Wizard initialized. Open the web UI to continue:
4346
4145
  \u2192 Task ID: ${task.id}
4347
4146
  \u2192 Use the "Convert to Mobile" button on the project page`
4348
4147
  );
4349
4148
  }
4350
4149
  );
4351
- var statusSubcommand = new Command25("status").description("Show mobile conversion status for a task").argument("<task-id>", "Parent conversion task ID").action(async (taskId) => {
4150
+ var statusSubcommand = new Command24("status").description("Show mobile conversion status for a task").argument("<task-id>", "Parent conversion task ID").action(async (taskId) => {
4352
4151
  const resources = await api.get(
4353
4152
  `/api/tasks/${taskId}/resources`
4354
4153
  );
@@ -4361,7 +4160,7 @@ var statusSubcommand = new Command25("status").description("Show mobile conversi
4361
4160
  }
4362
4161
  try {
4363
4162
  const state = JSON.parse(wizardState.content);
4364
- console.log(paint10("cyan", "Mobile Conversion Status"));
4163
+ console.log(paint9("cyan", "Mobile Conversion Status"));
4365
4164
  console.log(` Phase: ${state.phase}`);
4366
4165
  if (state.framework) {
4367
4166
  console.log(` Framework: ${state.framework}`);
@@ -4384,10 +4183,10 @@ var statusSubcommand = new Command25("status").description("Show mobile conversi
4384
4183
  mobileCommand.addCommand(statusSubcommand);
4385
4184
 
4386
4185
  // cli/commands/scan.ts
4387
- import { Command as Command26 } from "commander";
4186
+ import { Command as Command25 } from "commander";
4388
4187
 
4389
4188
  // lib/scanner/index.ts
4390
- import { spawn as spawn9 } from "child_process";
4189
+ import { spawn as spawn8 } from "child_process";
4391
4190
 
4392
4191
  // lib/scanner/config.ts
4393
4192
  import { readFileSync as readFileSync10, existsSync as existsSync12 } from "fs";
@@ -4853,10 +4652,10 @@ ${codebaseAnalysis.routes.map((r) => `- ${r}`).join("\n")}
4853
4652
  ${codebaseAnalysis.prismaModels.map((m) => `- ${m}`).join("\n")}
4854
4653
 
4855
4654
  **Components:**
4856
- ${codebaseAnalysis.components.slice(0, 30).map((c12) => `- ${c12}`).join("\n")}
4655
+ ${codebaseAnalysis.components.slice(0, 30).map((c11) => `- ${c11}`).join("\n")}
4857
4656
 
4858
4657
  **Recent Git Commits:**
4859
- ${codebaseAnalysis.recentCommits.slice(0, 15).map((c12) => `- ${c12}`).join("\n")}
4658
+ ${codebaseAnalysis.recentCommits.slice(0, 15).map((c11) => `- ${c11}`).join("\n")}
4860
4659
 
4861
4660
  **Completed Tasks:**
4862
4661
  ${context.completedTasks.slice(0, 20).map((t) => `- ${t.title}`).join("\n") || "None"}
@@ -5066,7 +4865,7 @@ async function fetchScanContext(opts) {
5066
4865
  }
5067
4866
  function runClaude(prompt2) {
5068
4867
  return new Promise((resolve7, reject) => {
5069
- const child = spawn9("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
4868
+ const child = spawn8("claude", ["-p", "--dangerously-skip-permissions", prompt2], {
5070
4869
  stdio: ["ignore", "pipe", "pipe"]
5071
4870
  });
5072
4871
  let output = "";
@@ -5122,7 +4921,7 @@ function parseSynthesisOutput(output) {
5122
4921
  }
5123
4922
 
5124
4923
  // cli/commands/scan.ts
5125
- var c10 = {
4924
+ var c9 = {
5126
4925
  reset: "\x1B[0m",
5127
4926
  bold: "\x1B[1m",
5128
4927
  dim: "\x1B[2m",
@@ -5133,53 +4932,53 @@ var c10 = {
5133
4932
  magenta: "\x1B[35m",
5134
4933
  gray: "\x1B[90m"
5135
4934
  };
5136
- function paint11(color, text) {
5137
- return `${c10[color]}${text}${c10.reset}`;
4935
+ function paint10(color, text) {
4936
+ return `${c9[color]}${text}${c9.reset}`;
5138
4937
  }
5139
- function timestamp4() {
5140
- return paint11("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
4938
+ function timestamp3() {
4939
+ return paint10("gray", (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false }));
5141
4940
  }
5142
4941
  function scanTag() {
5143
- return paint11("magenta", "[scan]");
4942
+ return paint10("magenta", "[scan]");
5144
4943
  }
5145
- function log3(msg) {
5146
- console.log(`${timestamp4()} ${scanTag()} ${msg}`);
4944
+ function log2(msg) {
4945
+ console.log(`${timestamp3()} ${scanTag()} ${msg}`);
5147
4946
  }
5148
- function logOk2(msg) {
5149
- console.log(`${timestamp4()} ${scanTag()} ${paint11("green", "\u2713")} ${msg}`);
4947
+ function logOk(msg) {
4948
+ console.log(`${timestamp3()} ${scanTag()} ${paint10("green", "\u2713")} ${msg}`);
5150
4949
  }
5151
- function logErr3(msg) {
5152
- console.error(`${timestamp4()} ${scanTag()} ${paint11("red", "\u2717")} ${msg}`);
4950
+ function logErr2(msg) {
4951
+ console.error(`${timestamp3()} ${scanTag()} ${paint10("red", "\u2717")} ${msg}`);
5153
4952
  }
5154
- 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("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
4953
+ var scanCommand = new Command25("scan").description("Run a product scan on the current project \u2014 analyzes codebase, crawls the app, and surfaces findings").option("--project <id>", "Project ID (defaults to linked project)").option("--report <id>", "Use an existing scan report ID (created by UI trigger)").option("--no-crawl", "Skip live crawl (codebase analysis only)").action(async (opts) => {
5155
4954
  const config = loadConfig();
5156
4955
  if (!config.apiKey) {
5157
- logErr3('Not authenticated. Run "mr login" first.');
4956
+ logErr2('Not authenticated. Run "mr login" first.');
5158
4957
  process.exit(1);
5159
4958
  }
5160
4959
  const banner = [
5161
4960
  ``,
5162
- paint11("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
5163
- paint11("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
5164
- paint11("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
5165
- paint11("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
5166
- paint11("dim", ` autonomous product scanner`),
4961
+ paint10("magenta", ` \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2557\u2554`),
4962
+ paint10("magenta", ` \u255A\u2550\u2557\u2551 \u2560\u2550\u2563\u2551\u2551\u2551`),
4963
+ paint10("magenta", ` \u255A\u2550\u255D\u255A\u2550\u255D\u2569 \u2569\u255D\u255A\u255D`),
4964
+ paint10("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
4965
+ paint10("dim", ` autonomous product scanner`),
5167
4966
  ``
5168
4967
  ].join("\n");
5169
4968
  console.log(banner);
5170
4969
  const projectId = opts.project || getLinkedProjectId();
5171
4970
  if (!projectId) {
5172
- logErr3('No project linked. Run "mr link" or pass --project <id>.');
4971
+ logErr2('No project linked. Run "mr link" or pass --project <id>.');
5173
4972
  process.exit(1);
5174
4973
  }
5175
4974
  let project;
5176
4975
  try {
5177
4976
  project = await api.get(`/api/projects/${projectId}`);
5178
4977
  } catch {
5179
- logErr3(`Failed to fetch project ${projectId}`);
4978
+ logErr2(`Failed to fetch project ${projectId}`);
5180
4979
  process.exit(1);
5181
4980
  }
5182
- log3(`Scanning project: ${paint11("cyan", project.name)}`);
4981
+ log2(`Scanning project: ${paint10("cyan", project.name)}`);
5183
4982
  let projectPath = project.localPath;
5184
4983
  if (!projectPath) {
5185
4984
  for (const [dir, pid] of Object.entries(config.directories)) {
@@ -5195,12 +4994,12 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5195
4994
  let reportId;
5196
4995
  if (opts.report) {
5197
4996
  reportId = opts.report;
5198
- log3(`Using existing scan report ${paint11("yellow", reportId.slice(0, 8))}`);
4997
+ log2(`Using existing scan report ${paint10("yellow", reportId.slice(0, 8))}`);
5199
4998
  } else {
5200
4999
  try {
5201
5000
  const scans = await api.get(`/api/scans?projectId=${projectId}&status=processing`);
5202
5001
  if (scans.length > 0) {
5203
- logErr3("A scan is already in progress for this project. Wait for it to complete.");
5002
+ logErr2("A scan is already in progress for this project. Wait for it to complete.");
5204
5003
  process.exit(1);
5205
5004
  }
5206
5005
  } catch {
@@ -5211,9 +5010,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5211
5010
  status: "pending"
5212
5011
  });
5213
5012
  reportId = report.id;
5214
- log3(`Created scan report ${paint11("yellow", reportId.slice(0, 8))}`);
5013
+ log2(`Created scan report ${paint10("yellow", reportId.slice(0, 8))}`);
5215
5014
  } catch (err) {
5216
- logErr3(`Failed to create scan report: ${err.message}`);
5015
+ logErr2(`Failed to create scan report: ${err.message}`);
5217
5016
  process.exit(1);
5218
5017
  }
5219
5018
  }
@@ -5224,7 +5023,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5224
5023
  try {
5225
5024
  const current = await api.get(`/api/scans/${reportId}`);
5226
5025
  if (current.status === "cancelled") {
5227
- log3(paint11("yellow", "Scan was cancelled \u2014 aborting."));
5026
+ log2(paint10("yellow", "Scan was cancelled \u2014 aborting."));
5228
5027
  process.exit(0);
5229
5028
  }
5230
5029
  } catch {
@@ -5238,9 +5037,9 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5238
5037
  apiUrl: config.apiUrl,
5239
5038
  apiKey: config.apiKey,
5240
5039
  runBrowse: runBrowseCommand2,
5241
- onLog: log3,
5040
+ onLog: log2,
5242
5041
  onProgress: (phase, detail) => {
5243
- log3(`${paint11("dim", `[${phase}]`)} ${detail}`);
5042
+ log2(`${paint10("dim", `[${phase}]`)} ${detail}`);
5244
5043
  }
5245
5044
  });
5246
5045
  let wasCancelled = false;
@@ -5252,7 +5051,7 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5252
5051
  } catch {
5253
5052
  }
5254
5053
  if (wasCancelled) {
5255
- log3(paint11("yellow", "Scan was cancelled by user \u2014 discarding results."));
5054
+ log2(paint10("yellow", "Scan was cancelled by user \u2014 discarding results."));
5256
5055
  process.exit(0);
5257
5056
  }
5258
5057
  await api.patch(`/api/scans/${reportId}`, {
@@ -5263,37 +5062,37 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5263
5062
  scanDurationMs: result.scanDurationMs,
5264
5063
  routesCrawled: result.routesCrawled
5265
5064
  });
5266
- logOk2(`Scan complete \u2014 ${paint11("cyan", String(result.findings.length))} findings`);
5065
+ logOk(`Scan complete \u2014 ${paint10("cyan", String(result.findings.length))} findings`);
5267
5066
  console.log("");
5268
- console.log(` ${paint11("bold", "Summary:")} ${result.summary}`);
5067
+ console.log(` ${paint10("bold", "Summary:")} ${result.summary}`);
5269
5068
  console.log("");
5270
5069
  const high = result.findings.filter((f) => f.priority === "high");
5271
5070
  const medium = result.findings.filter((f) => f.priority === "medium");
5272
5071
  const low = result.findings.filter((f) => f.priority === "low");
5273
5072
  if (high.length > 0) {
5274
- console.log(` ${paint11("bold", paint11("red", `High Priority (${high.length})`))}`);
5073
+ console.log(` ${paint10("bold", paint10("red", `High Priority (${high.length})`))}`);
5275
5074
  for (const f of high) {
5276
- console.log(` ${paint11("red", "\u25CF")} [${f.type}] ${f.title}`);
5277
- console.log(` ${paint11("dim", f.description.slice(0, 120))}`);
5075
+ console.log(` ${paint10("red", "\u25CF")} [${f.type}] ${f.title}`);
5076
+ console.log(` ${paint10("dim", f.description.slice(0, 120))}`);
5278
5077
  }
5279
5078
  console.log("");
5280
5079
  }
5281
5080
  if (medium.length > 0) {
5282
- console.log(` ${paint11("bold", paint11("yellow", `Medium Priority (${medium.length})`))}`);
5081
+ console.log(` ${paint10("bold", paint10("yellow", `Medium Priority (${medium.length})`))}`);
5283
5082
  for (const f of medium) {
5284
- console.log(` ${paint11("yellow", "\u25CF")} [${f.type}] ${f.title}`);
5083
+ console.log(` ${paint10("yellow", "\u25CF")} [${f.type}] ${f.title}`);
5285
5084
  }
5286
5085
  console.log("");
5287
5086
  }
5288
5087
  if (low.length > 0) {
5289
- console.log(` ${paint11("dim", `Low Priority (${low.length})`)} `);
5088
+ console.log(` ${paint10("dim", `Low Priority (${low.length})`)} `);
5290
5089
  for (const f of low) {
5291
- console.log(` ${paint11("dim", `\u25CB [${f.type}] ${f.title}`)}`);
5090
+ console.log(` ${paint10("dim", `\u25CB [${f.type}] ${f.title}`)}`);
5292
5091
  }
5293
5092
  console.log("");
5294
5093
  }
5295
5094
  } catch (err) {
5296
- logErr3(`Scan failed: ${err.message}`);
5095
+ logErr2(`Scan failed: ${err.message}`);
5297
5096
  try {
5298
5097
  await api.patch(`/api/scans/${reportId}`, {
5299
5098
  status: "failed",
@@ -5306,8 +5105,8 @@ var scanCommand = new Command26("scan").description("Run a product scan on the c
5306
5105
  });
5307
5106
 
5308
5107
  // cli/commands/idea.ts
5309
- import { Command as Command27 } from "commander";
5310
- var c11 = {
5108
+ import { Command as Command26 } from "commander";
5109
+ var c10 = {
5311
5110
  reset: "\x1B[0m",
5312
5111
  bold: "\x1B[1m",
5313
5112
  dim: "\x1B[2m",
@@ -5319,27 +5118,27 @@ var c11 = {
5319
5118
  gray: "\x1B[90m",
5320
5119
  magenta: "\x1B[35m"
5321
5120
  };
5322
- function paint12(color, text) {
5323
- return `${c11[color]}${text}${c11.reset}`;
5121
+ function paint11(color, text) {
5122
+ return `${c10[color]}${text}${c10.reset}`;
5324
5123
  }
5325
5124
  function statusBadge2(status) {
5326
5125
  switch (status) {
5327
5126
  case "draft":
5328
- return paint12("gray", "\u25CB draft");
5127
+ return paint11("gray", "\u25CB draft");
5329
5128
  case "generating":
5330
- return paint12("cyan", "\u27F3 generating");
5129
+ return paint11("cyan", "\u27F3 generating");
5331
5130
  case "generated":
5332
- return paint12("green", "\u2713 generated");
5131
+ return paint11("green", "\u2713 generated");
5333
5132
  case "promoted":
5334
- return paint12("magenta", "\u2191 promoted");
5133
+ return paint11("magenta", "\u2191 promoted");
5335
5134
  case "archived":
5336
- return paint12("dim", "\u2298 archived");
5135
+ return paint11("dim", "\u2298 archived");
5337
5136
  default:
5338
- return paint12("gray", status);
5137
+ return paint11("gray", status);
5339
5138
  }
5340
5139
  }
5341
- var ideaCommand = new Command27("idea").description("Manage ideas \u2014 brainstorm, generate prototypes & plans").addCommand(
5342
- new Command27("list").description("List ideas for the linked project").option("--all", "Show ideas for all projects").option("--status <status>", "Filter by status").action(async (opts) => {
5140
+ var ideaCommand = new Command26("idea").description("Manage ideas \u2014 brainstorm, generate prototypes & plans").addCommand(
5141
+ new Command26("list").description("List ideas for the linked project").option("--all", "Show ideas for all projects").option("--status <status>", "Filter by status").action(async (opts) => {
5343
5142
  const params = new URLSearchParams();
5344
5143
  if (!opts.all) {
5345
5144
  const projectId = getLinkedProjectId();
@@ -5350,23 +5149,23 @@ var ideaCommand = new Command27("idea").description("Manage ideas \u2014 brainst
5350
5149
  if (opts.status) params.set("status", opts.status);
5351
5150
  const ideas = await api.get(`/api/ideas?${params.toString()}`);
5352
5151
  if (ideas.length === 0) {
5353
- console.log(paint12("gray", "No ideas found."));
5152
+ console.log(paint11("gray", "No ideas found."));
5354
5153
  return;
5355
5154
  }
5356
5155
  console.log();
5357
5156
  for (const idea of ideas) {
5358
5157
  const date = new Date(idea.createdAt).toLocaleDateString();
5359
5158
  console.log(
5360
- ` ${paint12("bold", idea.title)} ${statusBadge2(idea.status)} ${paint12("gray", idea.id.slice(0, 8))} ${paint12("dim", date)}`
5159
+ ` ${paint11("bold", idea.title)} ${statusBadge2(idea.status)} ${paint11("gray", idea.id.slice(0, 8))} ${paint11("dim", date)}`
5361
5160
  );
5362
5161
  if (idea.description) {
5363
- console.log(` ${paint12("dim", idea.description.slice(0, 80) + (idea.description.length > 80 ? "\u2026" : ""))}`);
5162
+ console.log(` ${paint11("dim", idea.description.slice(0, 80) + (idea.description.length > 80 ? "\u2026" : ""))}`);
5364
5163
  }
5365
5164
  console.log();
5366
5165
  }
5367
5166
  })
5368
5167
  ).addCommand(
5369
- new Command27("create").description("Create a new idea").argument("<title>", "Title of the idea").option("--description <desc>", "Description of the idea").option("--project <projectId>", "Project ID (defaults to linked project)").option("--generate", "Immediately start generating plan & prototype").action(async (title, opts) => {
5168
+ new Command26("create").description("Create a new idea").argument("<title>", "Title of the idea").option("--description <desc>", "Description of the idea").option("--project <projectId>", "Project ID (defaults to linked project)").option("--generate", "Immediately start generating plan & prototype").action(async (title, opts) => {
5370
5169
  const projectId = opts.project ?? getLinkedProjectId() ?? null;
5371
5170
  const idea = await api.post("/api/ideas", {
5372
5171
  title,
@@ -5374,59 +5173,59 @@ var ideaCommand = new Command27("idea").description("Manage ideas \u2014 brainst
5374
5173
  projectId
5375
5174
  });
5376
5175
  console.log();
5377
- console.log(` ${paint12("green", "\u2713")} Created idea: ${paint12("bold", idea.title)}`);
5378
- console.log(` ${paint12("gray", "ID:")} ${idea.id}`);
5176
+ console.log(` ${paint11("green", "\u2713")} Created idea: ${paint11("bold", idea.title)}`);
5177
+ console.log(` ${paint11("gray", "ID:")} ${idea.id}`);
5379
5178
  if (opts.generate) {
5380
5179
  await api.post(`/api/ideas/${idea.id}/generate`);
5381
- console.log(` ${paint12("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
5180
+ console.log(` ${paint11("cyan", "\u27F3")} Generation will begin automatically via the watch agent.`);
5382
5181
  }
5383
5182
  console.log();
5384
5183
  })
5385
5184
  ).addCommand(
5386
- new Command27("generate").description("Start generating plan & prototype for an idea").argument("<id>", "Idea ID").action(async (id) => {
5185
+ new Command26("generate").description("Start generating plan & prototype for an idea").argument("<id>", "Idea ID").action(async (id) => {
5387
5186
  const idea = await api.post(`/api/ideas/${id}/generate`);
5388
5187
  console.log();
5389
- console.log(` ${paint12("cyan", "\u27F3")} Generating: ${paint12("bold", idea.title)}`);
5390
- console.log(` ${paint12("gray", "The watch agent will pick this up shortly.")}`);
5188
+ console.log(` ${paint11("cyan", "\u27F3")} Generating: ${paint11("bold", idea.title)}`);
5189
+ console.log(` ${paint11("gray", "The watch agent will pick this up shortly.")}`);
5391
5190
  console.log();
5392
5191
  })
5393
5192
  ).addCommand(
5394
- new Command27("feedback").description("Send feedback to iterate on an idea's generated content").argument("<id>", "Idea ID").argument("<feedback>", "Feedback text").action(async (id, feedback) => {
5193
+ new Command26("feedback").description("Send feedback to iterate on an idea's generated content").argument("<id>", "Idea ID").argument("<feedback>", "Feedback text").action(async (id, feedback) => {
5395
5194
  const idea = await api.post(`/api/ideas/${id}/feedback`, { feedback });
5396
5195
  console.log();
5397
- console.log(` ${paint12("cyan", "\u27F3")} Feedback sent for: ${paint12("bold", idea.title)}`);
5398
- console.log(` ${paint12("gray", "The watch agent will re-generate with your feedback.")}`);
5196
+ console.log(` ${paint11("cyan", "\u27F3")} Feedback sent for: ${paint11("bold", idea.title)}`);
5197
+ console.log(` ${paint11("gray", "The watch agent will re-generate with your feedback.")}`);
5399
5198
  console.log();
5400
5199
  })
5401
5200
  ).addCommand(
5402
- new Command27("promote").description("Promote an idea to a task").argument("<id>", "Idea ID").action(async (id) => {
5201
+ new Command26("promote").description("Promote an idea to a task").argument("<id>", "Idea ID").action(async (id) => {
5403
5202
  const result = await api.post(`/api/ideas/${id}/promote`);
5404
5203
  console.log();
5405
- console.log(` ${paint12("green", "\u2713")} Promoted idea to task: ${paint12("bold", result.task.title)}`);
5406
- console.log(` ${paint12("gray", "Task ID:")} ${result.task.id}`);
5204
+ console.log(` ${paint11("green", "\u2713")} Promoted idea to task: ${paint11("bold", result.task.title)}`);
5205
+ console.log(` ${paint11("gray", "Task ID:")} ${result.task.id}`);
5407
5206
  console.log();
5408
5207
  })
5409
5208
  ).addCommand(
5410
- new Command27("spin-up").description("Spin up a new project with a GitHub repo from an idea").argument("<id>", "Idea ID").option("--name <name>", "Custom project name (defaults to idea title)").action(async (id, opts) => {
5209
+ new Command26("spin-up").description("Spin up a new project with a GitHub repo from an idea").argument("<id>", "Idea ID").option("--name <name>", "Custom project name (defaults to idea title)").action(async (id, opts) => {
5411
5210
  const body = {};
5412
5211
  if (opts.name) body.name = opts.name;
5413
5212
  const result = await api.post(`/api/ideas/${id}/spin-up`, body);
5414
5213
  console.log();
5415
- console.log(` ${paint12("green", "\u2713")} Spinning up project: ${paint12("bold", result.project.name)}`);
5416
- console.log(` ${paint12("gray", "Project ID:")} ${result.project.id}`);
5214
+ console.log(` ${paint11("green", "\u2713")} Spinning up project: ${paint11("bold", result.project.name)}`);
5215
+ console.log(` ${paint11("gray", "Project ID:")} ${result.project.id}`);
5417
5216
  if (result.tasks && result.tasks.length > 0) {
5418
- console.log(` ${paint12("green", "\u2713")} Created ${result.tasks.length} follow-up task(s):`);
5217
+ console.log(` ${paint11("green", "\u2713")} Created ${result.tasks.length} follow-up task(s):`);
5419
5218
  for (const task of result.tasks) {
5420
- console.log(` ${paint12("gray", "\u2022")} ${task.title}`);
5219
+ console.log(` ${paint11("gray", "\u2022")} ${task.title}`);
5421
5220
  }
5422
5221
  }
5423
- console.log(` ${paint12("cyan", "\u27F3")} Repo creation is queued \u2014 the watch daemon will pick it up.`);
5222
+ console.log(` ${paint11("cyan", "\u27F3")} Repo creation is queued \u2014 the watch daemon will pick it up.`);
5424
5223
  console.log();
5425
5224
  })
5426
5225
  );
5427
5226
 
5428
5227
  // cli/commands/doctor.ts
5429
- import { Command as Command28 } from "commander";
5228
+ import { Command as Command27 } from "commander";
5430
5229
  import { existsSync as existsSync14 } from "fs";
5431
5230
  import { homedir as homedir3 } from "os";
5432
5231
  import { join as join12 } from "path";
@@ -5474,12 +5273,12 @@ async function checkProjectLink() {
5474
5273
  optional: true
5475
5274
  };
5476
5275
  }
5477
- var doctorCommand = new Command28("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
5276
+ var doctorCommand = new Command27("doctor").description("Diagnose Mr. Manager CLI installation and environment").action(async () => {
5478
5277
  const banner = [
5479
5278
  ``,
5480
- paint7("cyan", ` MR DOCTOR`),
5481
- paint7("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`),
5482
- paint7("dim", ` diagnosing your mr environment`),
5279
+ paint6("cyan", ` MR DOCTOR`),
5280
+ paint6("dim", ` \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`),
5281
+ paint6("dim", ` diagnosing your mr environment`),
5483
5282
  ``
5484
5283
  ].join("\n");
5485
5284
  console.log(banner);
@@ -5501,15 +5300,15 @@ var doctorCommand = new Command28("doctor").description("Diagnose Mr. Manager CL
5501
5300
  const allOk = printResults(checks);
5502
5301
  console.log("");
5503
5302
  if (allOk) {
5504
- console.log(paint7("green", " Everything looks good!"));
5303
+ console.log(paint6("green", " Everything looks good!"));
5505
5304
  console.log("");
5506
5305
  return;
5507
5306
  }
5508
- const fixes = checks.filter((c12) => !c12.ok && c12.fix && !c12.optional);
5307
+ const fixes = checks.filter((c11) => !c11.ok && c11.fix && !c11.optional);
5509
5308
  if (fixes.length > 0) {
5510
- console.log(paint7("yellow", " To fix:"));
5309
+ console.log(paint6("yellow", " To fix:"));
5511
5310
  for (const fix of fixes) {
5512
- console.log(` ${paint7("dim", "\u2192")} ${paint7("bold", fix.name)}: ${fix.fix}`);
5311
+ console.log(` ${paint6("dim", "\u2192")} ${paint6("bold", fix.name)}: ${fix.fix}`);
5513
5312
  }
5514
5313
  console.log("");
5515
5314
  }
@@ -5523,7 +5322,7 @@ var userArgs = process.argv.slice(2);
5523
5322
  var bypassCommands = /* @__PURE__ */ new Set(["login", "init", "auth", "help", "--help", "-h", "--version", "-V", "doctor", "setup"]);
5524
5323
  var shouldBypass = userArgs.length > 0 && bypassCommands.has(userArgs[0]);
5525
5324
  if (isFirstRun && !shouldBypass) {
5526
- const c12 = {
5325
+ const c11 = {
5527
5326
  reset: "\x1B[0m",
5528
5327
  bold: "\x1B[1m",
5529
5328
  dim: "\x1B[2m",
@@ -5533,28 +5332,28 @@ if (isFirstRun && !shouldBypass) {
5533
5332
  magenta: "\x1B[35m"
5534
5333
  };
5535
5334
  console.log("");
5536
- console.log(`${c12.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${c12.reset}`);
5537
- console.log(`${c12.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${c12.reset}`);
5538
- console.log(`${c12.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c12.reset}`);
5539
- console.log(`${c12.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${c12.reset}`);
5335
+ console.log(`${c11.cyan} \u2554\u2566\u2557\u2566\u2550\u2557 \u2554\u2566\u2557\u2554\u2550\u2557\u2554\u2557\u2554\u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2550\u2557\u2566\u2550\u2557${c11.reset}`);
5336
+ console.log(`${c11.magenta} \u2551\u2551\u2551\u2560\u2566\u255D \u2551\u2551\u2551\u2560\u2550\u2563\u2551\u2551\u2551\u2560\u2550\u2563\u2551 \u2566\u2551\u2563 \u2560\u2566\u255D${c11.reset}`);
5337
+ console.log(`${c11.cyan} \u2569 \u2569\u2569\u255A\u2550 \u2569 \u2569\u2569 \u2569\u255D\u255A\u255D\u2569 \u2569\u255A\u2550\u255D\u255A\u2550\u255D\u2569\u255A\u2550${c11.reset}`);
5338
+ console.log(`${c11.dim} \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500${c11.reset}`);
5540
5339
  console.log("");
5541
- console.log(`${c12.bold} Welcome to Mr. Manager!${c12.reset}`);
5542
- console.log(`${c12.dim} Let's get you set up in a few quick steps.${c12.reset}`);
5340
+ console.log(`${c11.bold} Welcome to Mr. Manager!${c11.reset}`);
5341
+ console.log(`${c11.dim} Let's get you set up in a few quick steps.${c11.reset}`);
5543
5342
  console.log("");
5544
- console.log(` ${c12.yellow}Step 1:${c12.reset} Authenticate via Google OAuth`);
5545
- console.log(` ${c12.dim}Run:${c12.reset} ${c12.cyan}mr login${c12.reset}`);
5343
+ console.log(` ${c11.yellow}Step 1:${c11.reset} Authenticate via Google OAuth`);
5344
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr login${c11.reset}`);
5546
5345
  console.log("");
5547
- console.log(` ${c12.yellow}Step 2:${c12.reset} Verify your environment`);
5548
- console.log(` ${c12.dim}Run:${c12.reset} ${c12.cyan}mr setup${c12.reset}`);
5346
+ console.log(` ${c11.yellow}Step 2:${c11.reset} Verify your environment`);
5347
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr setup${c11.reset}`);
5549
5348
  console.log("");
5550
- console.log(` ${c12.yellow}Step 3:${c12.reset} Link a repo and start watching`);
5551
- console.log(` ${c12.dim}Run:${c12.reset} ${c12.cyan}mr link${c12.reset} ${c12.dim}&&${c12.reset} ${c12.cyan}mr watch${c12.reset}`);
5349
+ console.log(` ${c11.yellow}Step 3:${c11.reset} Link a repo and start watching`);
5350
+ console.log(` ${c11.dim}Run:${c11.reset} ${c11.cyan}mr link${c11.reset} ${c11.dim}&&${c11.reset} ${c11.cyan}mr watch${c11.reset}`);
5552
5351
  console.log("");
5553
- console.log(`${c12.dim} Or run ${c12.reset}${c12.cyan}mr login${c12.reset}${c12.dim} to get started now.${c12.reset}`);
5352
+ console.log(`${c11.dim} Or run ${c11.reset}${c11.cyan}mr login${c11.reset}${c11.dim} to get started now.${c11.reset}`);
5554
5353
  console.log("");
5555
5354
  process.exit(0);
5556
5355
  }
5557
- var program = new Command29();
5356
+ var program = new Command28();
5558
5357
  program.name("mr").description("Mr. Manager - Task and project management CLI").version("0.2.0");
5559
5358
  program.addCommand(initCommand);
5560
5359
  program.addCommand(authCommand);
@@ -5571,7 +5370,6 @@ program.addCommand(undelegateCommand);
5571
5370
  program.addCommand(createCommand);
5572
5371
  program.addCommand(completeCommand);
5573
5372
  program.addCommand(subtaskCompleteCommand);
5574
- program.addCommand(digestCommand);
5575
5373
  program.addCommand(upCommand);
5576
5374
  program.addCommand(prototypeCommand);
5577
5375
  program.addCommand(setupCommand);