@dunnewold-labs/mr-manager 0.4.44 → 0.4.46
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 +104 -1
- 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.46",
|
|
189
189
|
description: "Mr. Manager - Task and project management CLI",
|
|
190
190
|
bin: {
|
|
191
191
|
mr: "./dist/index.mjs"
|
|
@@ -1826,6 +1826,20 @@ function checkPrStatus(prUrl, repoDir, vcs = "github") {
|
|
|
1826
1826
|
});
|
|
1827
1827
|
});
|
|
1828
1828
|
}
|
|
1829
|
+
function mergePrViaCli(prUrl, repoDir, vcs = "github") {
|
|
1830
|
+
const cmd = vcs === "gitlab" ? `glab mr merge "${prUrl}" --yes --squash --remove-source-branch 2>&1` : `gh pr merge "${prUrl}" --squash --delete-branch 2>&1`;
|
|
1831
|
+
return new Promise((resolve9) => {
|
|
1832
|
+
exec(cmd, { cwd: repoDir }, (err, stdout, stderr) => {
|
|
1833
|
+
if (err) {
|
|
1834
|
+
const raw = (stderr || stdout || err.message).toString().trim();
|
|
1835
|
+
const tail = raw.split("\n").slice(-3).join(" ").trim();
|
|
1836
|
+
resolve9({ ok: false, error: tail || err.message });
|
|
1837
|
+
return;
|
|
1838
|
+
}
|
|
1839
|
+
resolve9({ ok: true });
|
|
1840
|
+
});
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1829
1843
|
function buildPrototypeSection(protoRefs, workingDir) {
|
|
1830
1844
|
if (protoRefs.length === 0) return "";
|
|
1831
1845
|
const sections = [
|
|
@@ -3194,6 +3208,15 @@ var watchCommand = new Command9("watch").description(
|
|
|
3194
3208
|
...prUrl ? { link: prUrl } : {}
|
|
3195
3209
|
});
|
|
3196
3210
|
logSuccess(prefix, `"${paint("bold", task.title)}" marked ready for review`);
|
|
3211
|
+
if (prUrl && currentTask.autoMergeEnabled) {
|
|
3212
|
+
try {
|
|
3213
|
+
await api.post(`/api/tasks/${task.id}/auto-merge`, {});
|
|
3214
|
+
await api.patch(`/api/tasks/${task.id}`, { autoMergeEnabled: false });
|
|
3215
|
+
logDispatch(prefix, `Auto-merge preference detected \u2014 sanity review queued`);
|
|
3216
|
+
} catch (err) {
|
|
3217
|
+
logError(prefix, `Failed to queue auto-merge: ${err.message}`);
|
|
3218
|
+
}
|
|
3219
|
+
}
|
|
3197
3220
|
if (noMrRequested) {
|
|
3198
3221
|
await postTaskUpdate(task.id, `No ${prLabel} required: ${noMrDescription}`, "system");
|
|
3199
3222
|
}
|
|
@@ -4370,6 +4393,86 @@ ${divider}`);
|
|
|
4370
4393
|
} catch (err) {
|
|
4371
4394
|
logError(prefix, `Failed to re-queue task for conflict resolution: ${err.message}`);
|
|
4372
4395
|
}
|
|
4396
|
+
continue;
|
|
4397
|
+
}
|
|
4398
|
+
if (task.autoMergeEnabled) {
|
|
4399
|
+
try {
|
|
4400
|
+
await api.post(`/api/tasks/${task.id}/auto-merge`, {});
|
|
4401
|
+
await api.patch(`/api/tasks/${task.id}`, { autoMergeEnabled: false });
|
|
4402
|
+
logDispatch(prefix, `Auto-merge preference detected \u2014 sanity review queued`);
|
|
4403
|
+
} catch (err) {
|
|
4404
|
+
logError(prefix, `Failed to queue auto-merge: ${err.message}`);
|
|
4405
|
+
}
|
|
4406
|
+
}
|
|
4407
|
+
}
|
|
4408
|
+
let completedAutoMergeReviews = [];
|
|
4409
|
+
try {
|
|
4410
|
+
completedAutoMergeReviews = await api.get("/api/reviews?status=completed&limit=20");
|
|
4411
|
+
} catch (err) {
|
|
4412
|
+
logError(watchTag(), `Failed to fetch completed reviews: ${err.message}`);
|
|
4413
|
+
}
|
|
4414
|
+
for (const review of completedAutoMergeReviews) {
|
|
4415
|
+
if (!review.autoMergeTaskId || review.autoMergeStatus !== "pending") continue;
|
|
4416
|
+
const taskId = review.autoMergeTaskId;
|
|
4417
|
+
const sid = shortId(review.id);
|
|
4418
|
+
const prefix = `${paint("blue", `[auto-merge:${sid}]`)}`;
|
|
4419
|
+
const repoDir = findDirectoryForProject(config, review.projectId, rootDir);
|
|
4420
|
+
if (!repoDir) {
|
|
4421
|
+
logWarn(prefix, `No local repo for project ${review.projectId} \u2014 cannot auto-merge`);
|
|
4422
|
+
try {
|
|
4423
|
+
await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "blocked" });
|
|
4424
|
+
await postTaskUpdate(taskId, `Auto-merge blocked: no local checkout found for this project`, "system");
|
|
4425
|
+
} catch {
|
|
4426
|
+
}
|
|
4427
|
+
continue;
|
|
4428
|
+
}
|
|
4429
|
+
const findings = review.findings ?? [];
|
|
4430
|
+
const blocking = findings.filter((f) => f.severity === "critical" || f.severity === "high");
|
|
4431
|
+
if (blocking.length > 0) {
|
|
4432
|
+
logWarn(prefix, `Auto-merge blocked \u2014 ${blocking.length} ${blocking.length === 1 ? "issue" : "issues"} flagged`);
|
|
4433
|
+
const lines = blocking.slice(0, 10).map((f) => `- **${f.severity}**: ${f.title}${f.file ? ` (\`${f.file}${f.line ? `:${f.line}` : ""}\`)` : ""}`);
|
|
4434
|
+
const summaryBlock = review.summary ? `${review.summary}
|
|
4435
|
+
|
|
4436
|
+
` : "";
|
|
4437
|
+
const message = `Auto-merge blocked \u2014 sanity review flagged ${blocking.length} blocking issue${blocking.length === 1 ? "" : "s"}.
|
|
4438
|
+
|
|
4439
|
+
${summaryBlock}${lines.join("\n")}`;
|
|
4440
|
+
try {
|
|
4441
|
+
await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "blocked" });
|
|
4442
|
+
await postTaskUpdate(taskId, message, "system");
|
|
4443
|
+
} catch (err) {
|
|
4444
|
+
logError(prefix, `Failed to record auto-merge block: ${err.message}`);
|
|
4445
|
+
}
|
|
4446
|
+
continue;
|
|
4447
|
+
}
|
|
4448
|
+
if (!review.branchUrl) {
|
|
4449
|
+
logWarn(prefix, `No PR/MR URL on review \u2014 cannot auto-merge`);
|
|
4450
|
+
try {
|
|
4451
|
+
await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "blocked" });
|
|
4452
|
+
await postTaskUpdate(taskId, `Auto-merge blocked: review is missing the PR/MR URL`, "system");
|
|
4453
|
+
} catch {
|
|
4454
|
+
}
|
|
4455
|
+
continue;
|
|
4456
|
+
}
|
|
4457
|
+
const vcs = review.branchUrl.includes("gitlab") ? "gitlab" : "github";
|
|
4458
|
+
const prLabel = vcs === "gitlab" ? "MR" : "PR";
|
|
4459
|
+
logDispatch(prefix, `Sanity review clean \u2014 merging ${prLabel} ${paint("cyan", review.branchUrl)}`);
|
|
4460
|
+
const mergeResult = await mergePrViaCli(review.branchUrl, repoDir, vcs);
|
|
4461
|
+
if (!mergeResult.ok) {
|
|
4462
|
+
logError(prefix, `Auto-merge failed: ${mergeResult.error}`);
|
|
4463
|
+
try {
|
|
4464
|
+
await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "failed" });
|
|
4465
|
+
await postTaskUpdate(taskId, `Auto-merge failed: ${mergeResult.error}`, "system");
|
|
4466
|
+
} catch {
|
|
4467
|
+
}
|
|
4468
|
+
continue;
|
|
4469
|
+
}
|
|
4470
|
+
try {
|
|
4471
|
+
await api.patch(`/api/reviews/${review.id}`, { autoMergeStatus: "merged" });
|
|
4472
|
+
await postTaskUpdate(taskId, `Auto-merge complete \u2014 sanity review passed and ${prLabel} merged`, "system");
|
|
4473
|
+
logSuccess(prefix, `Auto-merge complete for ${prLabel}`);
|
|
4474
|
+
} catch (err) {
|
|
4475
|
+
logError(prefix, `Failed to record auto-merge success: ${err.message}`);
|
|
4373
4476
|
}
|
|
4374
4477
|
}
|
|
4375
4478
|
} finally {
|