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