@dunnewold-labs/mr-manager 0.4.48 → 0.4.50

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 +79 -39
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -185,7 +185,7 @@ import { fileURLToPath } from "url";
185
185
  // cli/package.json
186
186
  var package_default = {
187
187
  name: "@dunnewold-labs/mr-manager",
188
- version: "0.4.48",
188
+ version: "0.4.50",
189
189
  description: "Mr. Manager - Task and project management CLI",
190
190
  bin: {
191
191
  mr: "./dist/index.mjs"
@@ -1133,7 +1133,7 @@ async function runTest(options) {
1133
1133
  }
1134
1134
 
1135
1135
  // lib/agent-fallback.ts
1136
- var AGENT_FALLBACK_ORDER = ["claude", "codex", "gemini"];
1136
+ var AGENT_FALLBACK_ORDER = ["claude", "codex", "antigravity"];
1137
1137
  function getAgentFallbackChain(agent) {
1138
1138
  const startIndex = AGENT_FALLBACK_ORDER.indexOf(agent);
1139
1139
  if (startIndex === -1) {
@@ -1194,6 +1194,36 @@ function isPrOrMrUrl(input) {
1194
1194
  }
1195
1195
  return false;
1196
1196
  }
1197
+ var MAX_SLUG_LENGTH = 30;
1198
+ function baseSlug(input) {
1199
+ return input.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, MAX_SLUG_LENGTH);
1200
+ }
1201
+ function extractTicketKey(input) {
1202
+ const match = input.match(/\b([A-Z][A-Z0-9]+-\d+)\b/);
1203
+ return match ? match[1].toLowerCase() : null;
1204
+ }
1205
+ function slugifyTitle(title) {
1206
+ const raw = (title ?? "").trim();
1207
+ if (!raw) return "";
1208
+ const ticketKey = extractTicketKey(raw);
1209
+ if (ticketKey) return baseSlug(ticketKey);
1210
+ const urlPathSlug = urlToPathSlug(raw);
1211
+ if (urlPathSlug) return urlPathSlug;
1212
+ return baseSlug(raw);
1213
+ }
1214
+ function urlToPathSlug(input) {
1215
+ let url;
1216
+ try {
1217
+ url = new URL(input);
1218
+ } catch {
1219
+ return null;
1220
+ }
1221
+ if (!/^https?:$/.test(url.protocol)) return null;
1222
+ const segments = url.pathname.split("/").filter(Boolean);
1223
+ if (segments.length === 0) return null;
1224
+ const slug = baseSlug(segments.slice(-2).join("-"));
1225
+ return slug || null;
1226
+ }
1197
1227
 
1198
1228
  // cli/browse-runner.ts
1199
1229
  import { execSync as execSync3, spawn as spawn3 } from "child_process";
@@ -1497,9 +1527,6 @@ function findDirectoryForProject(config, projectId, rootDir) {
1497
1527
  }
1498
1528
  return null;
1499
1529
  }
1500
- function slugify(title) {
1501
- return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 30);
1502
- }
1503
1530
  function shortId(id) {
1504
1531
  return id.slice(0, 8);
1505
1532
  }
@@ -1514,7 +1541,8 @@ function taskBranchName(task) {
1514
1541
  if (branch && !isPrOrMrUrl(branch)) {
1515
1542
  return branch;
1516
1543
  }
1517
- return `${ownerPrefix(task)}/${slugify(task.title)}`;
1544
+ const slug = slugifyTitle(task.title) || shortId(task.id);
1545
+ return `${ownerPrefix(task)}/${slug}`;
1518
1546
  }
1519
1547
  function resolveBranchFromPrUrl(prUrl, repoDir, vcs) {
1520
1548
  const cmd = vcs === "gitlab" ? `glab mr view "${prUrl}" --output json 2>/dev/null | jq -r '.source_branch // empty'` : `gh pr view "${prUrl}" --json headRefName -q .headRefName 2>/dev/null`;
@@ -1996,7 +2024,7 @@ function buildFeaturesSection(repoDir) {
1996
2024
  ].join("\n");
1997
2025
  }
1998
2026
  function buildExecutionPrompt(task, repoDir, subtasks, vcs = "github", protoRefs = [], feedbackUpdates = [], existingResources = [], skillRefs = [], executionDir, startWithoutWorktree = false, preparedBranchName) {
1999
- const slug = slugify(task.title);
2027
+ const slug = slugifyTitle(task.title) || shortId(task.id);
2000
2028
  const owner = ownerPrefix(task);
2001
2029
  const generatedBranchName = `${owner}/${slug}`;
2002
2030
  const branchName = taskBranchName(task);
@@ -2740,15 +2768,15 @@ ${systemPrompt}` : prompt2;
2740
2768
  args.push(fullPrompt);
2741
2769
  return { bin: "codex", args };
2742
2770
  }
2743
- if (agent === "gemini") {
2771
+ if (agent === "antigravity") {
2744
2772
  const fullPrompt = systemPrompt ? `${prompt2}
2745
2773
 
2746
2774
  ${systemPrompt}` : prompt2;
2747
2775
  const args = ["-p", fullPrompt];
2748
2776
  if (mode === "execute") {
2749
- args.push("--yolo");
2777
+ args.push("--dangerously-skip-permissions");
2750
2778
  }
2751
- return { bin: "gemini", args };
2779
+ return { bin: AGENT_BINARIES.antigravity, args };
2752
2780
  }
2753
2781
  const sessionArgs = sessionId ? resumeSession ? ["--resume", sessionId] : ["--session-id", sessionId] : [];
2754
2782
  const nameArgs = name ? ["--name", name] : [];
@@ -2761,6 +2789,11 @@ ${systemPrompt}` : prompt2;
2761
2789
  const permissionArgs = ["--dangerously-skip-permissions"];
2762
2790
  return { bin: "claude", args: [...sessionArgs, ...nameArgs, ...systemArgs, ...turnsArgs, ...modelArgs, ...permissionArgs, "-p", prompt2] };
2763
2791
  }
2792
+ var AGENT_BINARIES = {
2793
+ claude: "claude",
2794
+ codex: "codex",
2795
+ antigravity: "agy"
2796
+ };
2764
2797
  function commandExists(cmd) {
2765
2798
  return new Promise((resolve9) => {
2766
2799
  exec(`command -v ${cmd}`, (err) => resolve9(!err));
@@ -2777,8 +2810,11 @@ function resolveTaskAgentAndModel(delegatedModel, watchAgent) {
2777
2810
  return { agent: "claude", claudeModel: "claude-haiku-4-5-20251001" };
2778
2811
  case "codex":
2779
2812
  return { agent: "codex" };
2813
+ case "antigravity":
2814
+ return { agent: "antigravity" };
2815
+ // Legacy alias: tasks delegated before the Antigravity migration.
2780
2816
  case "gemini":
2781
- return { agent: "gemini" };
2817
+ return { agent: "antigravity" };
2782
2818
  default:
2783
2819
  return { agent: watchAgent };
2784
2820
  }
@@ -2866,12 +2902,12 @@ function spawnAgent(agent, repoDir, prompt2, prefix, onActivity, sessionId, name
2866
2902
  }
2867
2903
  var watchCommand = new Command9("watch").description(
2868
2904
  "Watch for in-progress tasks and autonomously dispatch an AI coding agent to work on them"
2869
- ).option("--interval <seconds>", "Polling interval in seconds", "15").option("--dry-run", "Show what would be dispatched without spawning the agent", false).option("--plan-approval", "Show the agent's plan and ask for approval before executing", false).option("--root <dir>", "Root directory filter for linked repos (default: cwd)").option("--agent <agent>", "AI agent to use: claude, codex, or gemini", "claude").option("--scan-at <HH:MM>", "Run a product scan daily at this time (e.g., 02:00)").action(async (opts) => {
2905
+ ).option("--interval <seconds>", "Polling interval in seconds", "15").option("--dry-run", "Show what would be dispatched without spawning the agent", false).option("--plan-approval", "Show the agent's plan and ask for approval before executing", false).option("--root <dir>", "Root directory filter for linked repos (default: cwd)").option("--agent <agent>", "AI agent to use: claude, codex, or antigravity", "claude").option("--scan-at <HH:MM>", "Run a product scan daily at this time (e.g., 02:00)").action(async (opts) => {
2870
2906
  const intervalMs = parseInt(opts.interval, 10) * 1e3;
2871
2907
  const dryRun = opts.dryRun;
2872
2908
  const planApproval = opts.planApproval;
2873
2909
  const rootDir = opts.root ? resolve2(opts.root) : process.cwd();
2874
- const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
2910
+ const agent = opts.agent === "codex" ? "codex" : opts.agent === "antigravity" ? "antigravity" : "claude";
2875
2911
  const scanAt = opts.scanAt;
2876
2912
  const taskStallTimeoutMs = getTaskStallTimeoutMs();
2877
2913
  const hungTaskTimeoutMinutes = Math.max(5, parseInt(process.env.MR_WATCH_HUNG_TASK_TIMEOUT_MINUTES ?? "60", 10) || 60);
@@ -2913,13 +2949,13 @@ var watchCommand = new Command9("watch").description(
2913
2949
  if (cached !== void 0) {
2914
2950
  return cached;
2915
2951
  }
2916
- const available = await commandExists(candidate);
2952
+ const available = await commandExists(AGENT_BINARIES[candidate]);
2917
2953
  agentAvailability.set(candidate, available);
2918
2954
  return available;
2919
2955
  }
2920
2956
  async function resolveAgentChain(preferred) {
2921
2957
  const availability = {};
2922
- for (const candidate of ["claude", "codex", "gemini"]) {
2958
+ for (const candidate of ["claude", "codex", "antigravity"]) {
2923
2959
  availability[candidate] = await isAgentAvailable(candidate);
2924
2960
  }
2925
2961
  return getAvailableAgentFallbackChain(preferred, availability);
@@ -3011,7 +3047,7 @@ var watchCommand = new Command9("watch").description(
3011
3047
  }
3012
3048
  async function dispatchTask(task, repoDir) {
3013
3049
  const sid = shortId(task.id);
3014
- const slug = slugify(task.title);
3050
+ const slug = slugifyTitle(task.title) || sid;
3015
3051
  const owner = ownerPrefix(task);
3016
3052
  let branchName = taskBranchName(task);
3017
3053
  const legacyBranchName = `mr/${sid}/${slug}`;
@@ -3504,7 +3540,7 @@ var watchCommand = new Command9("watch").description(
3504
3540
  if (refImagePaths.length > 0) {
3505
3541
  logDispatch(prefix, `${refImagePaths.length} reference image(s) attached`);
3506
3542
  }
3507
- const validAgents = ["claude", "codex", "gemini"];
3543
+ const validAgents = ["claude", "codex", "antigravity"];
3508
3544
  const requested = Array.isArray(proto.selectedAgents) ? proto.selectedAgents.filter(
3509
3545
  (a) => validAgents.includes(a)
3510
3546
  ) : [];
@@ -3596,14 +3632,16 @@ var watchCommand = new Command9("watch").description(
3596
3632
  const found = readdirSync(repoDir).filter((f) => protoPattern.test(f)).sort();
3597
3633
  const files = found.map((f) => ({
3598
3634
  name: f,
3599
- content: readFileSync5(resolve2(repoDir, f), "utf-8")
3635
+ content: readFileSync5(resolve2(repoDir, f), "utf-8"),
3636
+ agent: attemptAgent
3600
3637
  }));
3601
3638
  if (files.length === 0) {
3602
3639
  logError(prefix, `No prototype HTML files found in ${repoDir}`);
3603
3640
  await api.patch(`/api/prototypes/${proto.id}`, { status: "failed" });
3604
3641
  return;
3605
3642
  }
3606
- await api.patch(`/api/prototypes/${proto.id}`, { status: "completed", files });
3643
+ const variantModels = files.map((f) => f.agent ?? null);
3644
+ await api.patch(`/api/prototypes/${proto.id}`, { status: "completed", files, variantModels });
3607
3645
  logSuccess(prefix, `"${paint("bold", proto.title)}" uploaded ${files.length} file(s)`);
3608
3646
  for (const file of files) {
3609
3647
  try {
@@ -3727,7 +3765,7 @@ var watchCommand = new Command9("watch").description(
3727
3765
  for (const f of found) {
3728
3766
  try {
3729
3767
  const content = readFileSync5(resolve2(slice.dir, f), "utf-8");
3730
- collected.push({ name: f, content });
3768
+ collected.push({ name: f, content, agent: slice.agentLabel });
3731
3769
  } catch (err) {
3732
3770
  logError(prefix, `Failed reading ${f} from ${slice.dir}: ${err.message}`);
3733
3771
  }
@@ -3748,7 +3786,7 @@ var watchCommand = new Command9("watch").description(
3748
3786
  name = `prototype-${renumberCursor}.html`;
3749
3787
  }
3750
3788
  seen.add(name);
3751
- finalFiles.push({ name, content: f.content });
3789
+ finalFiles.push({ name, content: f.content, agent: f.agent });
3752
3790
  }
3753
3791
  const allOk = sliceResults.every((r) => r.ok);
3754
3792
  if (finalFiles.length === 0) {
@@ -3757,7 +3795,8 @@ var watchCommand = new Command9("watch").description(
3757
3795
  } else {
3758
3796
  await api.patch(`/api/prototypes/${proto.id}`, {
3759
3797
  status: "completed",
3760
- files: finalFiles
3798
+ files: finalFiles,
3799
+ variantModels: finalFiles.map((f) => f.agent ?? null)
3761
3800
  });
3762
3801
  if (allOk) {
3763
3802
  logSuccess(prefix, `"${paint("bold", proto.title)}" uploaded ${finalFiles.length} file(s) from ${slices.length} agent(s)`);
@@ -4582,8 +4621,9 @@ ${summaryBlock}${lines.join("\n")}`;
4582
4621
  }
4583
4622
  try {
4584
4623
  await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "merged" });
4585
- await postTaskUpdate(taskId, `Auto-merge complete \u2014 sanity review passed and ${prLabel} merged`, "system");
4586
- logSuccess(prefix, `Auto-merge complete for ${prLabel}`);
4624
+ await api.patch(`/api/tasks/${taskId}`, { status: "completed" });
4625
+ await postTaskUpdate(taskId, `Auto-merge complete \u2014 sanity review passed, ${prLabel} merged, and task moved to Done`, "system");
4626
+ logSuccess(prefix, `Auto-merge complete for ${prLabel} \u2014 task moved to Done`);
4587
4627
  } catch (err) {
4588
4628
  logError(prefix, `Failed to record auto-merge success: ${err.message}`);
4589
4629
  }
@@ -5045,30 +5085,30 @@ async function checkCodexAuth() {
5045
5085
  fix: hasKey ? void 0 : "Set OPENAI_API_KEY or CODEX_API_KEY environment variable"
5046
5086
  };
5047
5087
  }
5048
- async function checkGeminiInstalled() {
5049
- const exists = await commandExists2("gemini");
5088
+ async function checkAntigravityInstalled() {
5089
+ const exists = await commandExists2("agy");
5050
5090
  return {
5051
- name: "Gemini CLI (gemini)",
5091
+ name: "Antigravity CLI (agy)",
5052
5092
  ok: exists,
5053
5093
  message: exists ? "installed" : "not found",
5054
- fix: exists ? void 0 : "Install: npm install -g @google/gemini-cli"
5094
+ fix: exists ? void 0 : "Install: curl -fsSL https://antigravity.google/cli/install.sh | bash"
5055
5095
  };
5056
5096
  }
5057
- async function checkGeminiAuth() {
5058
- const exists = await commandExists2("gemini");
5097
+ async function checkAntigravityAuth() {
5098
+ const exists = await commandExists2("agy");
5059
5099
  if (!exists) {
5060
5100
  return {
5061
- name: "Gemini CLI auth",
5101
+ name: "Antigravity CLI auth",
5062
5102
  ok: false,
5063
- message: "skipped (gemini not installed)"
5103
+ message: "skipped (agy not installed)"
5064
5104
  };
5065
5105
  }
5066
- const hasKey = !!process.env.GEMINI_API_KEY || !!process.env.GOOGLE_API_KEY;
5106
+ const hasKey = !!process.env.ANTIGRAVITY_API_KEY;
5067
5107
  return {
5068
- name: "Gemini CLI auth",
5108
+ name: "Antigravity CLI auth",
5069
5109
  ok: hasKey,
5070
5110
  message: hasKey ? "API key configured" : "no API key found",
5071
- fix: hasKey ? void 0 : "Set GEMINI_API_KEY or GOOGLE_API_KEY environment variable, or run: gcloud auth login"
5111
+ fix: hasKey ? void 0 : "Set ANTIGRAVITY_API_KEY environment variable, or run `agy` once to sign in with Google"
5072
5112
  };
5073
5113
  }
5074
5114
  async function checkGlabInstalled() {
@@ -5200,8 +5240,8 @@ async function autoFix(checks, agent) {
5200
5240
  console.log("");
5201
5241
  }
5202
5242
  }
5203
- 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) => {
5204
- const agent = opts.agent === "codex" ? "codex" : opts.agent === "gemini" ? "gemini" : "claude";
5243
+ 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 antigravity (default: claude)", "claude").action(async (opts) => {
5244
+ const agent = opts.agent === "codex" ? "codex" : opts.agent === "antigravity" ? "antigravity" : "claude";
5205
5245
  const banner = [
5206
5246
  ``,
5207
5247
  paint5("cyan", ` \u2554\u2566\u2557\u2551\u2550\u2557 \u2554\u2550\u2557\u2554\u2550\u2557\u2554\u2566\u2557\u2551 \u2551\u2554\u2550\u2557`),
@@ -5212,7 +5252,7 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
5212
5252
  ``
5213
5253
  ].join("\n");
5214
5254
  console.log(banner);
5215
- const agentChecks = agent === "codex" ? [checkCodexInstalled(), checkCodexAuth()] : agent === "gemini" ? [checkGeminiInstalled(), checkGeminiAuth()] : [checkClaudeInstalled(), checkClaudeAuth()];
5255
+ const agentChecks = agent === "codex" ? [checkCodexInstalled(), checkCodexAuth()] : agent === "antigravity" ? [checkAntigravityInstalled(), checkAntigravityAuth()] : [checkClaudeInstalled(), checkClaudeAuth()];
5216
5256
  const checks = await Promise.all([
5217
5257
  checkGitInstalled(),
5218
5258
  checkNodeVersion(),
@@ -5230,7 +5270,7 @@ var setupCommand = new Command16("setup").description("Check that all dependenci
5230
5270
  if (allOk) {
5231
5271
  console.log(paint5("green", " All checks passed! You're ready to run mr watch."));
5232
5272
  if (agent === "claude") {
5233
- console.log(paint5("dim", " Tip: check other agents with --agent codex or --agent gemini"));
5273
+ console.log(paint5("dim", " Tip: check other agents with --agent codex or --agent antigravity"));
5234
5274
  }
5235
5275
  console.log("");
5236
5276
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.4.48",
3
+ "version": "0.4.50",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"