@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.
- package/dist/index.mjs +79 -39
- 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.
|
|
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", "
|
|
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
|
-
|
|
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 =
|
|
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 === "
|
|
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("--
|
|
2777
|
+
args.push("--dangerously-skip-permissions");
|
|
2750
2778
|
}
|
|
2751
|
-
return { bin:
|
|
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: "
|
|
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
|
|
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 === "
|
|
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", "
|
|
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 =
|
|
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", "
|
|
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
|
-
|
|
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
|
|
4586
|
-
|
|
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
|
|
5049
|
-
const exists = await commandExists2("
|
|
5088
|
+
async function checkAntigravityInstalled() {
|
|
5089
|
+
const exists = await commandExists2("agy");
|
|
5050
5090
|
return {
|
|
5051
|
-
name: "
|
|
5091
|
+
name: "Antigravity CLI (agy)",
|
|
5052
5092
|
ok: exists,
|
|
5053
5093
|
message: exists ? "installed" : "not found",
|
|
5054
|
-
fix: exists ? void 0 : "Install:
|
|
5094
|
+
fix: exists ? void 0 : "Install: curl -fsSL https://antigravity.google/cli/install.sh | bash"
|
|
5055
5095
|
};
|
|
5056
5096
|
}
|
|
5057
|
-
async function
|
|
5058
|
-
const exists = await commandExists2("
|
|
5097
|
+
async function checkAntigravityAuth() {
|
|
5098
|
+
const exists = await commandExists2("agy");
|
|
5059
5099
|
if (!exists) {
|
|
5060
5100
|
return {
|
|
5061
|
-
name: "
|
|
5101
|
+
name: "Antigravity CLI auth",
|
|
5062
5102
|
ok: false,
|
|
5063
|
-
message: "skipped (
|
|
5103
|
+
message: "skipped (agy not installed)"
|
|
5064
5104
|
};
|
|
5065
5105
|
}
|
|
5066
|
-
const hasKey = !!process.env.
|
|
5106
|
+
const hasKey = !!process.env.ANTIGRAVITY_API_KEY;
|
|
5067
5107
|
return {
|
|
5068
|
-
name: "
|
|
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
|
|
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
|
|
5204
|
-
const agent = opts.agent === "codex" ? "codex" : opts.agent === "
|
|
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 === "
|
|
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
|
|
5273
|
+
console.log(paint5("dim", " Tip: check other agents with --agent codex or --agent antigravity"));
|
|
5234
5274
|
}
|
|
5235
5275
|
console.log("");
|
|
5236
5276
|
return;
|