@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.
Files changed (2) hide show
  1. package/dist/index.mjs +104 -1
  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.44",
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 {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dunnewold-labs/mr-manager",
3
- "version": "0.4.44",
3
+ "version": "0.4.46",
4
4
  "description": "Mr. Manager - Task and project management CLI",
5
5
  "bin": {
6
6
  "mr": "./dist/index.mjs"