@tarcisiopgs/lisa 1.39.1 → 1.40.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.
@@ -1138,6 +1138,13 @@ async function checkoutBaseBranches(config, workspace) {
1138
1138
  function sleep(ms) {
1139
1139
  return new Promise((resolve12) => setTimeout(resolve12, ms));
1140
1140
  }
1141
+ async function interruptibleSleep(ms) {
1142
+ const end = Date.now() + ms;
1143
+ while (Date.now() < end) {
1144
+ if (isShuttingDown() || hasUserQuitFromWatchPrompt()) return;
1145
+ await sleep(Math.min(1e3, end - Date.now()));
1146
+ }
1147
+ }
1141
1148
  async function waitIfPaused() {
1142
1149
  while (isLoopPaused()) {
1143
1150
  await sleep(500);
@@ -1191,24 +1198,56 @@ function emptyCommitFailure(result) {
1191
1198
  async function pullBaseBranch(config) {
1192
1199
  const workspace = resolve3(config.workspace);
1193
1200
  const baseBranch = config.base_branch;
1194
- const repoPaths = [workspace];
1201
+ const repoPaths = [];
1195
1202
  if (config.repos.length > 0) {
1196
1203
  for (const repo of config.repos) {
1197
1204
  repoPaths.push(resolve3(workspace, repo.path));
1198
1205
  }
1206
+ } else {
1207
+ repoPaths.push(workspace);
1199
1208
  }
1200
1209
  for (const repoPath of repoPaths) {
1201
1210
  try {
1211
+ await execa("git", ["checkout", baseBranch], {
1212
+ cwd: repoPath,
1213
+ reject: true,
1214
+ timeout: 15e3
1215
+ });
1202
1216
  await execa("git", ["pull", "--ff-only", "origin", baseBranch], {
1203
1217
  cwd: repoPath,
1204
1218
  reject: true,
1205
1219
  timeout: 3e4
1206
1220
  });
1207
- } catch (err) {
1208
- warn(`Failed to pull ${baseBranch} in ${repoPath}: ${formatError(err)}`);
1221
+ } catch {
1222
+ try {
1223
+ await execa("git", ["fetch", "origin", baseBranch], {
1224
+ cwd: repoPath,
1225
+ reject: true,
1226
+ timeout: 3e4
1227
+ });
1228
+ await execa("git", ["reset", "--hard", `origin/${baseBranch}`], {
1229
+ cwd: repoPath,
1230
+ reject: true,
1231
+ timeout: 15e3
1232
+ });
1233
+ } catch (err) {
1234
+ warn(`Failed to pull ${baseBranch} in ${repoPath}: ${formatError(err)}`);
1235
+ }
1209
1236
  }
1210
1237
  }
1211
1238
  }
1239
+ async function autoMergePr(prUrl, issueId, config) {
1240
+ if (!config.pr?.auto_merge) return;
1241
+ log(`Auto-merging PR for ${issueId}...`);
1242
+ const { mergePr } = await import("./merge-NWSEV3FR.js");
1243
+ const result = await mergePr(prUrl);
1244
+ if (result.success) {
1245
+ ok(`Auto-merged: ${prUrl}`);
1246
+ kanbanEmitter.emit("issue:merged", issueId);
1247
+ } else {
1248
+ warn(`Auto-merge failed for ${issueId}: ${result.error}`);
1249
+ }
1250
+ }
1212
1251
  function appendSessionLog(logFile, result) {
1213
1252
  try {
1214
1253
  appendFileSync(
@@ -1450,6 +1489,7 @@ async function recoverOrphanIssues(source, config) {
1450
1489
  );
1451
1490
  try {
1452
1491
  await source.updateStatus(orphan.id, config.source_config.pick_from, config.source_config);
1492
+ kanbanEmitter.emit("issue:reverted", orphan.id);
1453
1493
  ok(`Recovered orphan ${orphan.id}`);
1454
1494
  } catch (err) {
1455
1495
  error(`Failed to recover orphan ${orphan.id}: ${formatError(err)}`);
@@ -3213,6 +3253,7 @@ async function runNativeWorktreeSession(config, issue, logFile, session, models,
3213
3253
  }
3214
3254
  }
3215
3255
  if (worktreePath) await cleanupWorktree(repoPath, worktreePath);
3256
+ await autoMergePr(prUrl, issue.id, config);
3216
3257
  await reporter.finish([prUrl]);
3217
3258
  ok(`Session ${session} complete for ${issue.id}`);
3218
3259
  return {
@@ -3490,6 +3531,7 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
3490
3531
  }
3491
3532
  await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
3492
3533
  await cleanupWorktree(repoPath, worktreePath);
3534
+ await autoMergePr(prUrl, issue.id, config);
3493
3535
  await reporter.finish([prUrl]);
3494
3536
  updateSessionState(workspace, issue.id, "done");
3495
3537
  removeSessionRecord(workspace, issue.id);
@@ -3648,7 +3690,11 @@ async function runConcurrentLoop(config, source, models, workspace, opts) {
3648
3690
  );
3649
3691
  kanbanEmitter.emit("work:watching");
3650
3692
  setTitle("Lisa \u2014 watching...");
3651
- await sleep(WATCH_POLL_INTERVAL_MS);
3693
+ await interruptibleSleep(WATCH_POLL_INTERVAL_MS);
3694
+ if (hasUserQuitFromWatchPrompt() || isShuttingDown()) {
3695
+ noMoreIssues = true;
3696
+ break;
3697
+ }
3652
3698
  kanbanEmitter.emit("work:watch-resume");
3653
3699
  }
3654
3700
  break;
@@ -4019,6 +4065,7 @@ async function runBranchSession(config, issue, logFile, session, models, source,
4019
4065
  }
4020
4066
  }
4021
4067
  }
4068
+ await autoMergePr(prUrl, issue.id, config);
4022
4069
  await reporter.finish([prUrl]);
4023
4070
  ok(`Session ${session} complete for ${issue.id}`);
4024
4071
  return {
@@ -4121,7 +4168,10 @@ async function runSequentialLoop(config, source, models, workspace, opts) {
4121
4168
  ok(`Resuming watch mode (polling every ${WATCH_POLL_INTERVAL_MS / 1e3}s)...`);
4122
4169
  kanbanEmitter.emit("work:watching");
4123
4170
  setTitle("Lisa \u2014 watching...");
4124
- await sleep(WATCH_POLL_INTERVAL_MS);
4171
+ await interruptibleSleep(WATCH_POLL_INTERVAL_MS);
4172
+ if (hasUserQuitFromWatchPrompt() || isShuttingDown()) {
4173
+ break;
4174
+ }
4125
4175
  kanbanEmitter.emit("work:watch-resume");
4126
4176
  session--;
4127
4177
  continue;
@@ -4131,7 +4181,10 @@ async function runSequentialLoop(config, source, models, workspace, opts) {
4131
4181
  );
4132
4182
  kanbanEmitter.emit("work:watching");
4133
4183
  setTitle("Lisa \u2014 watching...");
4134
- await sleep(WATCH_POLL_INTERVAL_MS);
4184
+ await interruptibleSleep(WATCH_POLL_INTERVAL_MS);
4185
+ if (hasUserQuitFromWatchPrompt() || isShuttingDown()) {
4186
+ break;
4187
+ }
4135
4188
  kanbanEmitter.emit("work:watch-resume");
4136
4189
  session--;
4137
4190
  continue;
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  runLoop,
15
15
  saveConfig,
16
16
  validateConfig
17
- } from "./chunk-6ZSLFM6X.js";
17
+ } from "./chunk-C2Y2NO6I.js";
18
18
  import {
19
19
  CliError,
20
20
  buildExecutionWaves,
@@ -708,6 +708,12 @@ This will cause Lisa to re-pick the issue on recovery. Consider using a differen
708
708
  });
709
709
  prReviewers = Array.isArray(reviewerValue) ? reviewerValue : reviewerValue.split(",").map((s) => s.trim()).filter(Boolean);
710
710
  }
711
+ const wantAutoMerge = await clack.confirm({
712
+ message: "Auto-merge PRs after CI passes?",
713
+ initialValue: initial?.pr?.auto_merge ?? false
714
+ });
715
+ if (clack.isCancel(wantAutoMerge)) return process.exit(0);
716
+ const autoMerge = wantAutoMerge === true;
711
717
  const cfg = {
712
718
  provider: providerName,
713
719
  provider_options: {
@@ -734,7 +740,12 @@ This will cause Lisa to re-pick the issue on recovery. Consider using a differen
734
740
  repos,
735
741
  loop: { cooldown: 10, max_sessions: 0 },
736
742
  ...reviewMonitorEnabled ? { review_monitor: { enabled: true } } : {},
737
- ...prReviewers.length ? { pr: { reviewers: prReviewers } } : {}
743
+ ...prReviewers.length || autoMerge ? {
744
+ pr: {
745
+ ...prReviewers.length ? { reviewers: prReviewers } : {},
746
+ ...autoMerge ? { auto_merge: true } : {}
747
+ }
748
+ } : {}
738
749
  };
739
750
  saveConfig(cfg);
740
751
  clack.outro(
@@ -1850,7 +1861,7 @@ async function reviewAndCreate(plan2, planPath, opts) {
1850
1861
  log("Run `lisa run` when ready.");
1851
1862
  return;
1852
1863
  }
1853
- const { runLoop: runLoop2 } = await import("./loop-OVECVY75.js");
1864
+ const { runLoop: runLoop2 } = await import("./loop-Y6YS76TH.js");
1854
1865
  const waves = buildExecutionWaves(plan2.issues);
1855
1866
  const maxWaveSize = Math.max(...waves.map((w) => w.length));
1856
1867
  await runLoop2(config2, {
@@ -4,7 +4,7 @@ import {
4
4
  cleanupEventListeners,
5
5
  runDemoLoop,
6
6
  runLoop
7
- } from "./chunk-6ZSLFM6X.js";
7
+ } from "./chunk-C2Y2NO6I.js";
8
8
  import {
9
9
  WATCH_POLL_INTERVAL_MS
10
10
  } from "./chunk-YTUZFCU2.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.39.1",
3
+ "version": "1.40.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",