@tarcisiopgs/lisa 1.40.2 → 1.41.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.
@@ -19,10 +19,10 @@ import {
19
19
  readContext,
20
20
  resolveModels,
21
21
  runWithFallback
22
- } from "./chunk-YTUZFCU2.js";
22
+ } from "./chunk-LTVASWBM.js";
23
23
  import {
24
24
  kanbanEmitter
25
- } from "./chunk-SOV2YONG.js";
25
+ } from "./chunk-ZHB4S7SP.js";
26
26
  import {
27
27
  appendPlatformAttribution,
28
28
  appendPlatformProofOfWork,
@@ -1240,46 +1240,52 @@ async function pullBaseBranch(config) {
1240
1240
  }
1241
1241
  var AUTO_MERGE_CI_TIMEOUT_MS = 10 * 60 * 1e3;
1242
1242
  var AUTO_MERGE_POLL_MS = 15e3;
1243
- async function autoMergePr(prUrl, issueId, config) {
1244
- if (!config.pr?.auto_merge) return;
1243
+ async function autoMergeAllPrs(prUrls, issueId, config) {
1244
+ if (!config.pr?.auto_merge || prUrls.length === 0) return;
1245
1245
  const { checkPrCiStatus, mergePr } = await import("./merge-NWSEV3FR.js");
1246
- log(`Waiting for CI before auto-merge of ${issueId}...`);
1246
+ kanbanEmitter.emit("issue:auto-merge-status", issueId, "waiting");
1247
1247
  kanbanEmitter.emit("issue:ci-status", issueId, "pending");
1248
- const deadline = Date.now() + AUTO_MERGE_CI_TIMEOUT_MS;
1249
- while (Date.now() < deadline) {
1250
- if (isShuttingDown() || hasUserQuitFromWatchPrompt()) return;
1251
- const ciStatus = await checkPrCiStatus(prUrl);
1252
- if (ciStatus === "passing") {
1253
- kanbanEmitter.emit("issue:ci-status", issueId, "passing");
1254
- log(`CI passed for ${issueId}. Auto-merging...`);
1255
- const result = await mergePr(prUrl);
1256
- if (result.success) {
1257
- ok(`Auto-merged: ${prUrl}`);
1258
- kanbanEmitter.emit("issue:merged", issueId);
1259
- } else {
1260
- warn(`Auto-merge failed for ${issueId}: ${result.error}`);
1248
+ log(
1249
+ `Waiting for CI before auto-merge of ${issueId} (${prUrls.length} PR${prUrls.length > 1 ? "s" : ""})...`
1250
+ );
1251
+ const mergeOnePr = async (prUrl) => {
1252
+ const deadline = Date.now() + AUTO_MERGE_CI_TIMEOUT_MS;
1253
+ while (Date.now() < deadline) {
1254
+ if (isShuttingDown() || hasUserQuitFromWatchPrompt()) {
1255
+ return { url: prUrl, success: false, error: "shutdown" };
1261
1256
  }
1262
- return;
1263
- }
1264
- if (ciStatus === "failing") {
1265
- kanbanEmitter.emit("issue:ci-status", issueId, "failing");
1266
- warn(`CI failed for ${issueId}. Skipping auto-merge.`);
1267
- return;
1268
- }
1269
- if (ciStatus === "unknown") {
1270
- log(`No CI checks found for ${issueId}. Auto-merging...`);
1271
- const result = await mergePr(prUrl);
1272
- if (result.success) {
1273
- ok(`Auto-merged: ${prUrl}`);
1274
- kanbanEmitter.emit("issue:merged", issueId);
1275
- } else {
1276
- warn(`Auto-merge failed for ${issueId}: ${result.error}`);
1257
+ const ciStatus = await checkPrCiStatus(prUrl);
1258
+ if (ciStatus === "passing" || ciStatus === "unknown") {
1259
+ const result = await mergePr(prUrl);
1260
+ if (result.success) {
1261
+ ok(`Merged: ${prUrl}`);
1262
+ return { url: prUrl, success: true };
1263
+ }
1264
+ return { url: prUrl, success: false, error: result.error };
1277
1265
  }
1278
- return;
1266
+ if (ciStatus === "failing") {
1267
+ return { url: prUrl, success: false, error: "CI failed" };
1268
+ }
1269
+ await sleep(AUTO_MERGE_POLL_MS);
1270
+ }
1271
+ return { url: prUrl, success: false, error: "CI timeout" };
1272
+ };
1273
+ kanbanEmitter.emit("issue:auto-merge-status", issueId, "merging");
1274
+ const results = await Promise.all(prUrls.map(mergeOnePr));
1275
+ const allMerged = results.every((r) => r.success);
1276
+ const failed = results.filter((r) => !r.success);
1277
+ if (allMerged) {
1278
+ kanbanEmitter.emit("issue:ci-status", issueId, "passing");
1279
+ kanbanEmitter.emit("issue:auto-merge-status", issueId, "merged");
1280
+ kanbanEmitter.emit("issue:merged", issueId);
1281
+ ok(`All ${prUrls.length} PRs auto-merged for ${issueId}`);
1282
+ } else {
1283
+ kanbanEmitter.emit("issue:ci-status", issueId, "failing");
1284
+ kanbanEmitter.emit("issue:auto-merge-status", issueId, "failed");
1285
+ for (const f of failed) {
1286
+ warn(`Auto-merge failed for ${f.url}: ${f.error}`);
1279
1287
  }
1280
- await sleep(AUTO_MERGE_POLL_MS);
1281
1288
  }
1282
- warn(`CI did not complete within timeout for ${issueId}. Skipping auto-merge.`);
1283
1289
  }
1284
1290
  function appendSessionLog(logFile, result) {
1285
1291
  try {
@@ -2774,6 +2780,33 @@ function extractPrUrlFromOutput(output) {
2774
2780
  }
2775
2781
  return null;
2776
2782
  }
2783
+ function extractAllPrUrlsFromOutput(output) {
2784
+ const patterns = [
2785
+ /https?:\/\/github\.com\/[^/]+\/[^/]+\/pull\/\d+/g,
2786
+ /https?:\/\/[^/]*gitlab[^/]*\/[^/].+?\/-\/merge_requests\/\d+/g,
2787
+ /https?:\/\/bitbucket\.org\/[^/]+\/[^/]+\/pull-requests\/\d+/g
2788
+ ];
2789
+ const urls = /* @__PURE__ */ new Set();
2790
+ for (const pattern of patterns) {
2791
+ for (const match of output.matchAll(pattern)) {
2792
+ urls.add(match[0]);
2793
+ }
2794
+ }
2795
+ return [...urls];
2796
+ }
2797
+ function readAllManifestPrUrls(filePath) {
2798
+ if (!existsSync6(filePath)) return [];
2799
+ try {
2800
+ const parsed = JSON.parse(readFileSync6(filePath, "utf-8").trim());
2801
+ if (Array.isArray(parsed)) {
2802
+ return parsed.map((m) => m.prUrl).filter((url) => !!url);
2803
+ }
2804
+ const single = parsed;
2805
+ return single.prUrl ? [single.prUrl] : [];
2806
+ } catch {
2807
+ return [];
2808
+ }
2809
+ }
2777
2810
  function readPlanFile(filePath) {
2778
2811
  if (!existsSync6(filePath)) return null;
2779
2812
  try {
@@ -3286,7 +3319,7 @@ async function runNativeWorktreeSession(config, issue, logFile, session, models,
3286
3319
  }
3287
3320
  }
3288
3321
  if (worktreePath) await cleanupWorktree(repoPath, worktreePath);
3289
- await autoMergePr(prUrl, issue.id, config);
3322
+ await autoMergeAllPrs([prUrl], issue.id, config);
3290
3323
  await reporter.finish([prUrl]);
3291
3324
  ok(`Session ${session} complete for ${issue.id}`);
3292
3325
  return {
@@ -3564,7 +3597,7 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
3564
3597
  }
3565
3598
  await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
3566
3599
  await cleanupWorktree(repoPath, worktreePath);
3567
- await autoMergePr(prUrl, issue.id, config);
3600
+ await autoMergeAllPrs([prUrl], issue.id, config);
3568
3601
  await reporter.finish([prUrl]);
3569
3602
  updateSessionState(workspace, issue.id, "done");
3570
3603
  removeSessionRecord(workspace, issue.id);
@@ -3999,11 +4032,12 @@ async function runBranchSession(config, issue, logFile, session, models, source,
3999
4032
  return failureResult(result.providerUsed, result);
4000
4033
  }
4001
4034
  const manifest = readManifestFile(manifestPath);
4035
+ const allManifestPrUrls = readAllManifestPrUrls(manifestPath);
4002
4036
  try {
4003
4037
  unlinkSync4(manifestPath);
4004
4038
  } catch {
4005
4039
  }
4006
- let prUrl = manifest?.prUrl;
4040
+ let prUrl = manifest?.prUrl ?? allManifestPrUrls[0];
4007
4041
  if (!prUrl) {
4008
4042
  const extractedUrl = extractPrUrlFromOutput(result.output);
4009
4043
  if (extractedUrl) {
@@ -4011,6 +4045,12 @@ async function runBranchSession(config, issue, logFile, session, models, source,
4011
4045
  prUrl = extractedUrl;
4012
4046
  }
4013
4047
  }
4048
+ const allPrUrls = new Set(allManifestPrUrls);
4049
+ for (const url of extractAllPrUrlsFromOutput(result.output)) {
4050
+ allPrUrls.add(url);
4051
+ }
4052
+ if (prUrl) allPrUrls.add(prUrl);
4053
+ const prUrls = [...allPrUrls];
4014
4054
  if (!prUrl) {
4015
4055
  const hasChanges = await hasCodeChanges(workspace, config.base_branch);
4016
4056
  if (!hasChanges) {
@@ -4098,13 +4138,13 @@ async function runBranchSession(config, issue, logFile, session, models, source,
4098
4138
  }
4099
4139
  }
4100
4140
  }
4101
- await autoMergePr(prUrl, issue.id, config);
4102
- await reporter.finish([prUrl]);
4141
+ await autoMergeAllPrs(prUrls, issue.id, config);
4142
+ await reporter.finish(prUrls);
4103
4143
  ok(`Session ${session} complete for ${issue.id}`);
4104
4144
  return {
4105
4145
  success: true,
4106
4146
  providerUsed: result.providerUsed,
4107
- prUrls: [prUrl],
4147
+ prUrls,
4108
4148
  fallback: result
4109
4149
  };
4110
4150
  }
@@ -5,10 +5,10 @@ import {
5
5
  resolveModels,
6
6
  runWithFallback,
7
7
  saveLineage
8
- } from "./chunk-YTUZFCU2.js";
8
+ } from "./chunk-LTVASWBM.js";
9
9
  import {
10
10
  normalizeLabels
11
- } from "./chunk-SOV2YONG.js";
11
+ } from "./chunk-ZHB4S7SP.js";
12
12
  import {
13
13
  error,
14
14
  log,
@@ -6,7 +6,7 @@ import {
6
6
  createApiClient,
7
7
  kanbanEmitter,
8
8
  normalizeLabels
9
- } from "./chunk-SOV2YONG.js";
9
+ } from "./chunk-ZHB4S7SP.js";
10
10
  import {
11
11
  buildPrCreateInstruction
12
12
  } from "./chunk-ZOVVFU7B.js";
@@ -894,6 +894,11 @@ function useKanbanState(bellEnabled, initialCards = []) {
894
894
  const onCiStatus = (issueId, ciStatus) => {
895
895
  setCards((prev) => prev.map((c) => c.id === issueId ? { ...c, ciStatus } : c));
896
896
  };
897
+ const onAutoMergeStatus = (issueId, status) => {
898
+ setCards(
899
+ (prev) => prev.map((c) => c.id === issueId ? { ...c, autoMergeStatus: status } : c)
900
+ );
901
+ };
897
902
  const onReviewersUpdated = (issueId, reviewers) => {
898
903
  setCards((prev) => prev.map((c) => c.id === issueId ? { ...c, reviewers } : c));
899
904
  };
@@ -951,6 +956,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
951
956
  kanbanEmitter.on("issue:log-file", onLogFile);
952
957
  kanbanEmitter.on("issue:substatus", onSubstatus);
953
958
  kanbanEmitter.on("issue:ci-status", onCiStatus);
959
+ kanbanEmitter.on("issue:auto-merge-status", onAutoMergeStatus);
954
960
  kanbanEmitter.on("issue:reviewers-updated", onReviewersUpdated);
955
961
  kanbanEmitter.on("issue:available-reviewers", onAvailableReviewers);
956
962
  kanbanEmitter.on("issue:output", onOutput);
@@ -1000,6 +1006,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
1000
1006
  kanbanEmitter.off("issue:log-file", onLogFile);
1001
1007
  kanbanEmitter.off("issue:substatus", onSubstatus);
1002
1008
  kanbanEmitter.off("issue:ci-status", onCiStatus);
1009
+ kanbanEmitter.off("issue:auto-merge-status", onAutoMergeStatus);
1003
1010
  kanbanEmitter.off("issue:reviewers-updated", onReviewersUpdated);
1004
1011
  kanbanEmitter.off("issue:available-reviewers", onAvailableReviewers);
1005
1012
  kanbanEmitter.off("issue:output", onOutput);
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ import {
14
14
  runLoop,
15
15
  saveConfig,
16
16
  validateConfig
17
- } from "./chunk-BI6MR2UP.js";
17
+ } from "./chunk-4TXH7PTM.js";
18
18
  import {
19
19
  CliError,
20
20
  buildExecutionWaves,
@@ -24,7 +24,7 @@ import {
24
24
  parseStructuredOutput,
25
25
  runPlanWizard,
26
26
  savePlan
27
- } from "./chunk-RZ53EOAY.js";
27
+ } from "./chunk-H2ATCMBI.js";
28
28
  import {
29
29
  buildContextMdBlock,
30
30
  createProvider,
@@ -34,10 +34,10 @@ import {
34
34
  readContext,
35
35
  resolveModels,
36
36
  runWithFallback
37
- } from "./chunk-YTUZFCU2.js";
37
+ } from "./chunk-LTVASWBM.js";
38
38
  import {
39
39
  kanbanEmitter
40
- } from "./chunk-SOV2YONG.js";
40
+ } from "./chunk-ZHB4S7SP.js";
41
41
  import {
42
42
  isProofOfWorkEnabled,
43
43
  isSpecComplianceEnabled,
@@ -1861,7 +1861,7 @@ async function reviewAndCreate(plan2, planPath, opts) {
1861
1861
  log("Run `lisa run` when ready.");
1862
1862
  return;
1863
1863
  }
1864
- const { runLoop: runLoop2 } = await import("./loop-RWZJG7BG.js");
1864
+ const { runLoop: runLoop2 } = await import("./loop-VITV7WGU.js");
1865
1865
  const waves = buildExecutionWaves(plan2.issues);
1866
1866
  const maxWaveSize = Math.max(...waves.map((w) => w.length));
1867
1867
  await runLoop2(config2, {
@@ -1957,6 +1957,7 @@ function resolveCard(card) {
1957
1957
  prUrls: card.prUrls,
1958
1958
  merged: card.merged,
1959
1959
  ciStatus: card.ciStatus,
1960
+ autoMergeStatus: card.autoMergeStatus,
1960
1961
  logFile: card.logFile,
1961
1962
  outputLog: card.outputLogTail.join("\n")
1962
1963
  };
@@ -1984,6 +1985,7 @@ function resolveCard(card) {
1984
1985
  killed: card.killed,
1985
1986
  merged: card.merged,
1986
1987
  ciStatus: card.ciStatus,
1988
+ autoMergeStatus: card.autoMergeStatus,
1987
1989
  logFile: card.logFile,
1988
1990
  outputLog: card.outputLogTail.join("\n")
1989
1991
  };
@@ -2072,6 +2074,12 @@ var KanbanPersistence = class {
2072
2074
  this.updateCard(issueId, { ciStatus });
2073
2075
  this.scheduleFlush();
2074
2076
  });
2077
+ on("issue:auto-merge-status", (issueId, autoMergeStatus) => {
2078
+ this.updateCard(issueId, {
2079
+ autoMergeStatus
2080
+ });
2081
+ this.scheduleFlush();
2082
+ });
2075
2083
  on("issue:reconcile-remove", (issueId) => {
2076
2084
  this.removeCard(issueId);
2077
2085
  this.scheduleFlush();
@@ -2237,7 +2245,7 @@ async function executeRun(args) {
2237
2245
  if (isTTY) {
2238
2246
  const { render } = await import("ink");
2239
2247
  const { createElement } = await import("react");
2240
- const { KanbanApp } = await import("./kanban-XCQ6QNOL.js");
2248
+ const { KanbanApp } = await import("./kanban-QQ47VJUY.js");
2241
2249
  const demoConfig = {
2242
2250
  provider: "claude",
2243
2251
  source: "linear",
@@ -2337,7 +2345,7 @@ Add them to your ${shell} and run: source ${shell}`));
2337
2345
  const initialCards = persistence.load();
2338
2346
  persistedCards = initialCards;
2339
2347
  persistence.start();
2340
- const { registerPlanBridge } = await import("./tui-bridge-GGH36LIH.js");
2348
+ const { registerPlanBridge } = await import("./tui-bridge-EFC4OZZU.js");
2341
2349
  const cleanupPlan = registerPlanBridge(merged);
2342
2350
  onBeforeExit = () => {
2343
2351
  persistence.stop();
@@ -2345,7 +2353,7 @@ Add them to your ${shell} and run: source ${shell}`));
2345
2353
  };
2346
2354
  const { render } = await import("ink");
2347
2355
  const { createElement } = await import("react");
2348
- const { KanbanApp } = await import("./kanban-XCQ6QNOL.js");
2356
+ const { KanbanApp } = await import("./kanban-QQ47VJUY.js");
2349
2357
  render(createElement(KanbanApp, { config: merged, initialCards }), { exitOnCtrlC: false });
2350
2358
  }
2351
2359
  await runLoop(merged, {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  kanbanEmitter,
4
4
  useKanbanState
5
- } from "./chunk-SOV2YONG.js";
5
+ } from "./chunk-ZHB4S7SP.js";
6
6
  import {
7
7
  resetTitle,
8
8
  startSpinner,
@@ -141,10 +141,14 @@ function Card({
141
141
  "\u2714 ",
142
142
  formatElapsed(card.finishedAt - card.startedAt)
143
143
  ] }),
144
- card.prUrls.length > 0 && /* @__PURE__ */ jsxs(Text, { color: card.merged ? "magenta" : "yellow", dimColor: true, children: [
145
- card.merged ? "PR\u2714" : "PR",
146
- card.ciStatus === "pending" ? "\u23F3" : card.ciStatus === "passing" ? "\u2714" : card.ciStatus === "failing" ? "\u2716" : ""
147
- ] })
144
+ card.prUrls.length > 0 && /* @__PURE__ */ jsx(
145
+ Text,
146
+ {
147
+ color: card.autoMergeStatus === "failed" ? "red" : card.merged || card.autoMergeStatus === "merged" ? "magenta" : "yellow",
148
+ dimColor: true,
149
+ children: card.merged || card.autoMergeStatus === "merged" ? "PR\u2714" : card.autoMergeStatus === "waiting" || card.autoMergeStatus === "merging" ? "PR\u23F3" : card.autoMergeStatus === "failed" ? "PR\u2716" : "PR"
150
+ }
151
+ )
148
152
  ] }) : card.killed ? /* @__PURE__ */ jsx(Text, { color: "red", children: "KILLED" }) : card.skipped ? /* @__PURE__ */ jsx(Text, { color: "gray", children: "SKIPPED" }) : card.hasError && !card.killed && !card.skipped ? /* @__PURE__ */ jsx(Text, { color: "red", children: "FAILED" }) : (
149
153
  // Empty row for backlog and done-without-timing — maintains CARD_HEIGHT
150
154
  /* @__PURE__ */ jsx(Text, { children: " ".repeat(cardWidth) })
@@ -534,7 +538,8 @@ function IssueDetail({
534
538
  const prMetaRow = hasPrMeta ? 1 : 0;
535
539
  const pickerRows = showReviewerPicker ? Math.min((card.availableReviewers ?? []).length + 2, 12) : 0;
536
540
  const ciRow = card.ciStatus ? 1 : 0;
537
- const headerOverhead = 6 + prCount + logFileRow + ciRow + prMetaRow + pickerRows;
541
+ const autoMergeRow = card.autoMergeStatus ? 1 : 0;
542
+ const headerOverhead = 6 + prCount + logFileRow + ciRow + autoMergeRow + prMetaRow + pickerRows;
538
543
  const bodyRows = Math.max(1, terminalRows - headerOverhead);
539
544
  const lines = useMemo(() => processOutputLines(card.outputLog), [card.outputLog]);
540
545
  const startLine = Math.max(0, lines.length - bodyRows - logScrollOffset);
@@ -609,6 +614,16 @@ function IssueDetail({
609
614
  }
610
615
  )
611
616
  ] }),
617
+ card.autoMergeStatus && /* @__PURE__ */ jsxs4(Box4, { marginTop: 0, children: [
618
+ /* @__PURE__ */ jsx4(Text4, { color: "gray", dimColor: true, children: "MERGE: " }),
619
+ /* @__PURE__ */ jsx4(
620
+ Text4,
621
+ {
622
+ color: card.autoMergeStatus === "merged" ? "magenta" : card.autoMergeStatus === "failed" ? "red" : "yellow",
623
+ children: card.autoMergeStatus === "merged" ? `\u2714 merged (${card.prUrls.length} PR${card.prUrls.length > 1 ? "s" : ""})` : card.autoMergeStatus === "failed" ? "\u2716 failed" : card.autoMergeStatus === "merging" ? "\u23F3 merging..." : "\u23F3 waiting for CI"
624
+ }
625
+ )
626
+ ] }),
612
627
  hasPrMeta && /* @__PURE__ */ jsxs4(Box4, { marginTop: 0, flexDirection: "row", children: [
613
628
  cardReviewers.length > 0 ? /* @__PURE__ */ jsxs4(Fragment2, { children: [
614
629
  /* @__PURE__ */ jsx4(Text4, { color: "cyan", dimColor: true, children: "REVIEWERS: " }),
@@ -4,11 +4,11 @@ import {
4
4
  cleanupEventListeners,
5
5
  runDemoLoop,
6
6
  runLoop
7
- } from "./chunk-BI6MR2UP.js";
7
+ } from "./chunk-4TXH7PTM.js";
8
8
  import {
9
9
  WATCH_POLL_INTERVAL_MS
10
- } from "./chunk-YTUZFCU2.js";
11
- import "./chunk-SOV2YONG.js";
10
+ } from "./chunk-LTVASWBM.js";
11
+ import "./chunk-ZHB4S7SP.js";
12
12
  import "./chunk-ZOVVFU7B.js";
13
13
  import "./chunk-3EOEDL3T.js";
14
14
  import "./chunk-7OCDGYDM.js";
@@ -6,15 +6,15 @@ import {
6
6
  markdownToIssue,
7
7
  parseStructuredOutput,
8
8
  savePlan
9
- } from "./chunk-RZ53EOAY.js";
9
+ } from "./chunk-H2ATCMBI.js";
10
10
  import {
11
11
  createSource,
12
12
  resolveModels,
13
13
  runWithFallback
14
- } from "./chunk-YTUZFCU2.js";
14
+ } from "./chunk-LTVASWBM.js";
15
15
  import {
16
16
  kanbanEmitter
17
- } from "./chunk-SOV2YONG.js";
17
+ } from "./chunk-ZHB4S7SP.js";
18
18
  import "./chunk-ZOVVFU7B.js";
19
19
  import "./chunk-3EOEDL3T.js";
20
20
  import "./chunk-7OCDGYDM.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.40.2",
3
+ "version": "1.41.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",