@tarcisiopgs/lisa 1.40.1 → 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,
@@ -1238,16 +1238,53 @@ async function pullBaseBranch(config) {
1238
1238
  }
1239
1239
  }
1240
1240
  }
1241
- async function autoMergePr(prUrl, issueId, config) {
1242
- if (!config.pr?.auto_merge) return;
1243
- log(`Auto-merging PR for ${issueId}...`);
1244
- const { mergePr } = await import("./merge-NWSEV3FR.js");
1245
- const result = await mergePr(prUrl);
1246
- if (result.success) {
1247
- ok(`Auto-merged: ${prUrl}`);
1241
+ var AUTO_MERGE_CI_TIMEOUT_MS = 10 * 60 * 1e3;
1242
+ var AUTO_MERGE_POLL_MS = 15e3;
1243
+ async function autoMergeAllPrs(prUrls, issueId, config) {
1244
+ if (!config.pr?.auto_merge || prUrls.length === 0) return;
1245
+ const { checkPrCiStatus, mergePr } = await import("./merge-NWSEV3FR.js");
1246
+ kanbanEmitter.emit("issue:auto-merge-status", issueId, "waiting");
1247
+ kanbanEmitter.emit("issue:ci-status", issueId, "pending");
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" };
1256
+ }
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 };
1265
+ }
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");
1248
1280
  kanbanEmitter.emit("issue:merged", issueId);
1281
+ ok(`All ${prUrls.length} PRs auto-merged for ${issueId}`);
1249
1282
  } else {
1250
- warn(`Auto-merge failed for ${issueId}: ${result.error}`);
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}`);
1287
+ }
1251
1288
  }
1252
1289
  }
1253
1290
  function appendSessionLog(logFile, result) {
@@ -2743,6 +2780,33 @@ function extractPrUrlFromOutput(output) {
2743
2780
  }
2744
2781
  return null;
2745
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
+ }
2746
2810
  function readPlanFile(filePath) {
2747
2811
  if (!existsSync6(filePath)) return null;
2748
2812
  try {
@@ -3255,7 +3319,7 @@ async function runNativeWorktreeSession(config, issue, logFile, session, models,
3255
3319
  }
3256
3320
  }
3257
3321
  if (worktreePath) await cleanupWorktree(repoPath, worktreePath);
3258
- await autoMergePr(prUrl, issue.id, config);
3322
+ await autoMergeAllPrs([prUrl], issue.id, config);
3259
3323
  await reporter.finish([prUrl]);
3260
3324
  ok(`Session ${session} complete for ${issue.id}`);
3261
3325
  return {
@@ -3533,7 +3597,7 @@ async function runManualWorktreeSession(config, issue, logFile, session, models,
3533
3597
  }
3534
3598
  await executeHook("before_remove", config.hooks, worktreePath, hookEnv);
3535
3599
  await cleanupWorktree(repoPath, worktreePath);
3536
- await autoMergePr(prUrl, issue.id, config);
3600
+ await autoMergeAllPrs([prUrl], issue.id, config);
3537
3601
  await reporter.finish([prUrl]);
3538
3602
  updateSessionState(workspace, issue.id, "done");
3539
3603
  removeSessionRecord(workspace, issue.id);
@@ -3968,11 +4032,12 @@ async function runBranchSession(config, issue, logFile, session, models, source,
3968
4032
  return failureResult(result.providerUsed, result);
3969
4033
  }
3970
4034
  const manifest = readManifestFile(manifestPath);
4035
+ const allManifestPrUrls = readAllManifestPrUrls(manifestPath);
3971
4036
  try {
3972
4037
  unlinkSync4(manifestPath);
3973
4038
  } catch {
3974
4039
  }
3975
- let prUrl = manifest?.prUrl;
4040
+ let prUrl = manifest?.prUrl ?? allManifestPrUrls[0];
3976
4041
  if (!prUrl) {
3977
4042
  const extractedUrl = extractPrUrlFromOutput(result.output);
3978
4043
  if (extractedUrl) {
@@ -3980,6 +4045,12 @@ async function runBranchSession(config, issue, logFile, session, models, source,
3980
4045
  prUrl = extractedUrl;
3981
4046
  }
3982
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];
3983
4054
  if (!prUrl) {
3984
4055
  const hasChanges = await hasCodeChanges(workspace, config.base_branch);
3985
4056
  if (!hasChanges) {
@@ -4067,13 +4138,13 @@ async function runBranchSession(config, issue, logFile, session, models, source,
4067
4138
  }
4068
4139
  }
4069
4140
  }
4070
- await autoMergePr(prUrl, issue.id, config);
4071
- await reporter.finish([prUrl]);
4141
+ await autoMergeAllPrs(prUrls, issue.id, config);
4142
+ await reporter.finish(prUrls);
4072
4143
  ok(`Session ${session} complete for ${issue.id}`);
4073
4144
  return {
4074
4145
  success: true,
4075
4146
  providerUsed: result.providerUsed,
4076
- prUrls: [prUrl],
4147
+ prUrls,
4077
4148
  fallback: result
4078
4149
  };
4079
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-V65N7YWX.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-CSJRDKQU.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-V65N7YWX.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.1",
3
+ "version": "1.41.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",