@tarcisiopgs/lisa 1.22.2 → 1.23.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.
@@ -675,9 +675,17 @@ function useKanbanState(bellEnabled, initialCards = []) {
675
675
  const onQueued = (issue) => {
676
676
  setCards((prev) => {
677
677
  if (prev.some((c) => c.id === issue.id)) return prev;
678
+ const maxOrder = prev.reduce((max, c) => Math.max(max, c.queueOrder ?? 0), 0);
678
679
  return [
679
680
  ...prev,
680
- { id: issue.id, title: issue.title, column: "backlog", prUrls: [], outputLog: "" }
681
+ {
682
+ id: issue.id,
683
+ title: issue.title,
684
+ column: "backlog",
685
+ prUrls: [],
686
+ outputLog: "",
687
+ queueOrder: maxOrder + 1
688
+ }
681
689
  ];
682
690
  });
683
691
  };
package/dist/index.js CHANGED
@@ -47,7 +47,7 @@ import {
47
47
  setOutputMode,
48
48
  updateNotice,
49
49
  warn
50
- } from "./chunk-3QCZWKDJ.js";
50
+ } from "./chunk-W6CGBZPE.js";
51
51
  import {
52
52
  notify,
53
53
  resetTitle,
@@ -659,6 +659,7 @@ var AiderProvider = class {
659
659
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
660
660
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
661
661
  const chunks = [];
662
+ const stderrChunks = [];
662
663
  proc.stdout?.on("data", (chunk) => {
663
664
  const raw = chunk.toString();
664
665
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -678,6 +679,7 @@ var AiderProvider = class {
678
679
  const raw = chunk.toString();
679
680
  const text2 = isPty ? stripAnsi(raw) : raw;
680
681
  if (getOutputMode() !== "tui") process.stderr.write(raw);
682
+ stderrChunks.push(text2);
681
683
  try {
682
684
  appendFileSync2(opts.logFile, text2);
683
685
  } catch {
@@ -698,10 +700,15 @@ var AiderProvider = class {
698
700
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
699
701
  chunks.push(STUCK_MESSAGE);
700
702
  }
703
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
704
+ if (!success && stderrChunks.length > 0) {
705
+ chunks.push("\n[stderr]\n", ...stderrChunks);
706
+ }
701
707
  return {
702
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
708
+ success,
703
709
  output: chunks.join(""),
704
- duration: Date.now() - start
710
+ duration: Date.now() - start,
711
+ exitCode
705
712
  };
706
713
  } catch (err) {
707
714
  return {
@@ -775,6 +782,7 @@ var ClaudeProvider = class {
775
782
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
776
783
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
777
784
  const chunks = [];
785
+ const stderrChunks = [];
778
786
  proc.stdout?.on("data", (chunk) => {
779
787
  const raw = chunk.toString();
780
788
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -794,6 +802,7 @@ var ClaudeProvider = class {
794
802
  const raw = chunk.toString();
795
803
  const text2 = isPty ? stripAnsi(raw) : raw;
796
804
  if (getOutputMode() !== "tui") process.stderr.write(raw);
805
+ stderrChunks.push(text2);
797
806
  try {
798
807
  appendFileSync3(opts.logFile, text2);
799
808
  } catch {
@@ -814,10 +823,15 @@ var ClaudeProvider = class {
814
823
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
815
824
  chunks.push(STUCK_MESSAGE);
816
825
  }
826
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
827
+ if (!success && stderrChunks.length > 0) {
828
+ chunks.push("\n[stderr]\n", ...stderrChunks);
829
+ }
817
830
  return {
818
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
831
+ success,
819
832
  output: chunks.join(""),
820
- duration: Date.now() - start
833
+ duration: Date.now() - start,
834
+ exitCode
821
835
  };
822
836
  } catch (err) {
823
837
  return {
@@ -878,6 +892,7 @@ var CodexProvider = class {
878
892
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
879
893
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
880
894
  const chunks = [];
895
+ const stderrChunks = [];
881
896
  proc.stdout?.on("data", (chunk) => {
882
897
  const raw = chunk.toString();
883
898
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -897,6 +912,7 @@ var CodexProvider = class {
897
912
  const raw = chunk.toString();
898
913
  const text2 = isPty ? stripAnsi(raw) : raw;
899
914
  if (getOutputMode() !== "tui") process.stderr.write(raw);
915
+ stderrChunks.push(text2);
900
916
  try {
901
917
  appendFileSync4(opts.logFile, text2);
902
918
  } catch {
@@ -917,10 +933,15 @@ var CodexProvider = class {
917
933
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
918
934
  chunks.push(STUCK_MESSAGE);
919
935
  }
936
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
937
+ if (!success && stderrChunks.length > 0) {
938
+ chunks.push("\n[stderr]\n", ...stderrChunks);
939
+ }
920
940
  return {
921
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
941
+ success,
922
942
  output: chunks.join(""),
923
- duration: Date.now() - start
943
+ duration: Date.now() - start,
944
+ exitCode
924
945
  };
925
946
  } catch (err) {
926
947
  return {
@@ -981,6 +1002,7 @@ var CopilotProvider = class {
981
1002
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
982
1003
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
983
1004
  const chunks = [];
1005
+ const stderrChunks = [];
984
1006
  proc.stdout?.on("data", (chunk) => {
985
1007
  const raw = chunk.toString();
986
1008
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -1000,6 +1022,7 @@ var CopilotProvider = class {
1000
1022
  const raw = chunk.toString();
1001
1023
  const text2 = isPty ? stripAnsi(raw) : raw;
1002
1024
  if (getOutputMode() !== "tui") process.stderr.write(raw);
1025
+ stderrChunks.push(text2);
1003
1026
  try {
1004
1027
  appendFileSync5(opts.logFile, text2);
1005
1028
  } catch {
@@ -1020,10 +1043,15 @@ var CopilotProvider = class {
1020
1043
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
1021
1044
  chunks.push(STUCK_MESSAGE);
1022
1045
  }
1046
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
1047
+ if (!success && stderrChunks.length > 0) {
1048
+ chunks.push("\n[stderr]\n", ...stderrChunks);
1049
+ }
1023
1050
  return {
1024
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
1051
+ success,
1025
1052
  output: chunks.join(""),
1026
- duration: Date.now() - start
1053
+ duration: Date.now() - start,
1054
+ exitCode
1027
1055
  };
1028
1056
  } catch (err) {
1029
1057
  return {
@@ -1102,6 +1130,7 @@ var CursorProvider = class {
1102
1130
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
1103
1131
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
1104
1132
  const chunks = [];
1133
+ const stderrChunks = [];
1105
1134
  proc.stdout?.on("data", (chunk) => {
1106
1135
  const raw = chunk.toString();
1107
1136
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -1121,6 +1150,7 @@ var CursorProvider = class {
1121
1150
  const raw = chunk.toString();
1122
1151
  const text2 = isPty ? stripAnsi(raw) : raw;
1123
1152
  if (getOutputMode() !== "tui") process.stderr.write(raw);
1153
+ stderrChunks.push(text2);
1124
1154
  try {
1125
1155
  appendFileSync6(opts.logFile, text2);
1126
1156
  } catch {
@@ -1141,10 +1171,15 @@ var CursorProvider = class {
1141
1171
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
1142
1172
  chunks.push(STUCK_MESSAGE);
1143
1173
  }
1174
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
1175
+ if (!success && stderrChunks.length > 0) {
1176
+ chunks.push("\n[stderr]\n", ...stderrChunks);
1177
+ }
1144
1178
  return {
1145
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
1179
+ success,
1146
1180
  output: chunks.join(""),
1147
- duration: Date.now() - start
1181
+ duration: Date.now() - start,
1182
+ exitCode
1148
1183
  };
1149
1184
  } catch (err) {
1150
1185
  return {
@@ -1204,6 +1239,7 @@ var GeminiProvider = class {
1204
1239
  const errorLoopDetector = createErrorLoopDetector(proc, GEMINI_ERROR_PATTERN);
1205
1240
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
1206
1241
  const chunks = [];
1242
+ const stderrChunks = [];
1207
1243
  proc.stdout?.on("data", (chunk) => {
1208
1244
  const raw = chunk.toString();
1209
1245
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -1223,6 +1259,7 @@ var GeminiProvider = class {
1223
1259
  const raw = chunk.toString();
1224
1260
  const text2 = isPty ? stripAnsi(raw) : raw;
1225
1261
  if (getOutputMode() !== "tui") process.stderr.write(raw);
1262
+ stderrChunks.push(text2);
1226
1263
  try {
1227
1264
  appendFileSync7(opts.logFile, text2);
1228
1265
  } catch {
@@ -1243,10 +1280,15 @@ var GeminiProvider = class {
1243
1280
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
1244
1281
  chunks.push(STUCK_MESSAGE);
1245
1282
  }
1283
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
1284
+ if (!success && stderrChunks.length > 0) {
1285
+ chunks.push("\n[stderr]\n", ...stderrChunks);
1286
+ }
1246
1287
  return {
1247
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
1288
+ success,
1248
1289
  output: chunks.join(""),
1249
- duration: Date.now() - start
1290
+ duration: Date.now() - start,
1291
+ exitCode
1250
1292
  };
1251
1293
  } catch (err) {
1252
1294
  return {
@@ -1308,6 +1350,7 @@ var GooseProvider = class {
1308
1350
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
1309
1351
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
1310
1352
  const chunks = [];
1353
+ const stderrChunks = [];
1311
1354
  proc.stdout?.on("data", (chunk) => {
1312
1355
  const raw = chunk.toString();
1313
1356
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -1327,6 +1370,7 @@ var GooseProvider = class {
1327
1370
  const raw = chunk.toString();
1328
1371
  const text2 = isPty ? stripAnsi(raw) : raw;
1329
1372
  if (getOutputMode() !== "tui") process.stderr.write(raw);
1373
+ stderrChunks.push(text2);
1330
1374
  try {
1331
1375
  appendFileSync8(opts.logFile, text2);
1332
1376
  } catch {
@@ -1347,10 +1391,15 @@ var GooseProvider = class {
1347
1391
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
1348
1392
  chunks.push(STUCK_MESSAGE);
1349
1393
  }
1394
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
1395
+ if (!success && stderrChunks.length > 0) {
1396
+ chunks.push("\n[stderr]\n", ...stderrChunks);
1397
+ }
1350
1398
  return {
1351
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
1399
+ success,
1352
1400
  output: chunks.join(""),
1353
- duration: Date.now() - start
1401
+ duration: Date.now() - start,
1402
+ exitCode
1354
1403
  };
1355
1404
  } catch (err) {
1356
1405
  return {
@@ -1409,6 +1458,7 @@ var OpenCodeProvider = class {
1409
1458
  const errorLoopDetector = createErrorLoopDetector(proc, /^Error /);
1410
1459
  const outputStall = createOutputStallDetector(proc, opts.outputStallTimeout);
1411
1460
  const chunks = [];
1461
+ const stderrChunks = [];
1412
1462
  proc.stdout?.on("data", (chunk) => {
1413
1463
  const raw = chunk.toString();
1414
1464
  const text2 = isPty ? stripAnsi(raw) : raw;
@@ -1428,6 +1478,7 @@ var OpenCodeProvider = class {
1428
1478
  const raw = chunk.toString();
1429
1479
  const text2 = isPty ? stripAnsi(raw) : raw;
1430
1480
  if (getOutputMode() !== "tui") process.stderr.write(raw);
1481
+ stderrChunks.push(text2);
1431
1482
  try {
1432
1483
  appendFileSync9(opts.logFile, text2);
1433
1484
  } catch {
@@ -1448,10 +1499,15 @@ var OpenCodeProvider = class {
1448
1499
  } else if (overseer?.wasKilled() || errorLoopDetector.wasKilled()) {
1449
1500
  chunks.push(STUCK_MESSAGE);
1450
1501
  }
1502
+ const success = exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut();
1503
+ if (!success && stderrChunks.length > 0) {
1504
+ chunks.push("\n[stderr]\n", ...stderrChunks);
1505
+ }
1451
1506
  return {
1452
- success: exitCode === 0 && !overseer?.wasKilled() && !errorLoopDetector.wasKilled() && !outputStall.wasKilled() && !sessionTimeout.wasTimedOut(),
1507
+ success,
1453
1508
  output: chunks.join(""),
1454
- duration: Date.now() - start
1509
+ duration: Date.now() - start,
1510
+ exitCode
1455
1511
  };
1456
1512
  } catch (err) {
1457
1513
  return {
@@ -1517,10 +1573,21 @@ var ELIGIBLE_ERROR_PATTERNS = [
1517
1573
  /lisa-stall/i,
1518
1574
  /named models unavailable/i,
1519
1575
  /free plans can only use/i,
1520
- /empty commit/i
1576
+ /empty commit/i,
1577
+ // Process crash patterns (OOM, fatal errors, signals)
1578
+ /heap.*out of memory/i,
1579
+ /out of memory/i,
1580
+ /FATAL ERROR/,
1581
+ /allocation failed/i,
1582
+ /segmentation fault/i,
1583
+ /\bSIGKILL\b/,
1584
+ /\bSIGABRT\b/,
1585
+ /\bSIGSEGV\b/
1521
1586
  ];
1522
- function isEligibleForFallback(output) {
1523
- return ELIGIBLE_ERROR_PATTERNS.some((pattern) => pattern.test(output));
1587
+ function isEligibleForFallback(output, exitCode) {
1588
+ if (ELIGIBLE_ERROR_PATTERNS.some((pattern) => pattern.test(output))) return true;
1589
+ if (exitCode !== void 0 && exitCode > 128) return true;
1590
+ return false;
1524
1591
  }
1525
1592
  function isCompleteProviderExhaustion(attempts) {
1526
1593
  if (attempts.length === 0) return false;
@@ -1547,7 +1614,7 @@ async function runWithFallback(models, prompt, opts) {
1547
1614
  const guardrailsSection = opts.guardrailsDir ? buildGuardrailsSection(opts.guardrailsDir) : "";
1548
1615
  const fullPrompt = guardrailsSection ? `${prompt}${guardrailsSection}` : prompt;
1549
1616
  const result = await provider.run(fullPrompt, { ...opts, model: spec.model });
1550
- if (result.success) {
1617
+ if (result.success || opts.earlySuccess?.()) {
1551
1618
  attempts.push({
1552
1619
  provider: spec.provider,
1553
1620
  model: spec.model,
@@ -1572,7 +1639,7 @@ async function runWithFallback(models, prompt, opts) {
1572
1639
  context: extractContext(result.output)
1573
1640
  });
1574
1641
  }
1575
- const eligible = isEligibleForFallback(result.output);
1642
+ const eligible = isEligibleForFallback(result.output, result.exitCode);
1576
1643
  attempts.push({
1577
1644
  provider: spec.provider,
1578
1645
  model: spec.model,
@@ -5765,7 +5832,11 @@ async function runWorktreeMultiRepoSession(config2, issue2, logFile, session, mo
5765
5832
  onProcess: (pid) => {
5766
5833
  activeProviderPids.set(issue2.id, pid);
5767
5834
  },
5768
- shouldAbort: () => userKilledSet.has(issue2.id) || userSkippedSet.has(issue2.id)
5835
+ shouldAbort: () => userKilledSet.has(issue2.id) || userSkippedSet.has(issue2.id),
5836
+ earlySuccess: () => {
5837
+ const p = readPlanFile(planPath);
5838
+ return !!(p?.steps && p.steps.length > 0);
5839
+ }
5769
5840
  });
5770
5841
  stopSpinner();
5771
5842
  planProviderUsed = planResult.providerUsed;
@@ -5781,20 +5852,13 @@ ${planResult.output}
5781
5852
  );
5782
5853
  } catch {
5783
5854
  }
5784
- if (!planResult.success) {
5785
- error(`Planning phase failed for ${issue2.id}. Check ${logFile}`);
5786
- cleanupPlanFile(planPath);
5787
- activeProviderPids.delete(issue2.id);
5788
- return {
5789
- success: false,
5790
- providerUsed: planResult.providerUsed,
5791
- prUrls: [],
5792
- fallback: planResult
5793
- };
5794
- }
5795
5855
  const plan = readPlanFile(planPath);
5796
5856
  if (!plan?.steps || plan.steps.length === 0) {
5797
- error(`Agent did not produce a valid execution plan for ${issue2.id}. Aborting.`);
5857
+ if (!planResult.success) {
5858
+ error(`Planning phase failed for ${issue2.id}. Check ${logFile}`);
5859
+ } else {
5860
+ error(`Agent did not produce a valid execution plan for ${issue2.id}. Aborting.`);
5861
+ }
5798
5862
  cleanupPlanFile(planPath);
5799
5863
  activeProviderPids.delete(issue2.id);
5800
5864
  return {
@@ -5804,6 +5868,11 @@ ${planResult.output}
5804
5868
  fallback: planResult
5805
5869
  };
5806
5870
  }
5871
+ if (!planResult.success) {
5872
+ warn(
5873
+ `Planning provider exited with error but plan file was written for ${issue2.id}. Proceeding.`
5874
+ );
5875
+ }
5807
5876
  sortedSteps = [...plan.steps].sort((a, b) => a.order - b.order);
5808
5877
  ok(
5809
5878
  `Plan produced ${sortedSteps.length} step(s): ${sortedSteps.map((s) => s.repoPath).join(" \u2192 ")}`
@@ -7347,7 +7416,7 @@ var run = defineCommand6({
7347
7416
  if (isTTY) {
7348
7417
  const { render } = await import("ink");
7349
7418
  const { createElement } = await import("react");
7350
- const { KanbanApp } = await import("./kanban-GK6MHA5G.js");
7419
+ const { KanbanApp } = await import("./kanban-QJGXJJAR.js");
7351
7420
  const demoConfig = {
7352
7421
  provider: "claude",
7353
7422
  source: "linear",
@@ -7430,7 +7499,7 @@ Add them to your ${shell} and run: source ${shell}`));
7430
7499
  onBeforeExit = () => persistence.stop();
7431
7500
  const { render } = await import("ink");
7432
7501
  const { createElement } = await import("react");
7433
- const { KanbanApp } = await import("./kanban-GK6MHA5G.js");
7502
+ const { KanbanApp } = await import("./kanban-QJGXJJAR.js");
7434
7503
  render(createElement(KanbanApp, { config: merged, initialCards }), { exitOnCtrlC: false });
7435
7504
  }
7436
7505
  await runLoop(merged, {
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  kanbanEmitter,
4
4
  useKanbanState
5
- } from "./chunk-3QCZWKDJ.js";
5
+ } from "./chunk-W6CGBZPE.js";
6
6
  import {
7
7
  resetTitle,
8
8
  startSpinner,
@@ -188,22 +188,14 @@ function Column({
188
188
  const { columns: terminalCols, rows: terminalRows } = useTerminalSize();
189
189
  const visibleCount = calcVisibleCount(terminalRows);
190
190
  const cardWidth = calcCardWidth(terminalCols);
191
- const sortedCards = [...cards].sort((a, b) => {
192
- if (a.merged && !b.merged) return 1;
193
- if (!a.merged && b.merged) return -1;
194
- return 0;
195
- });
196
191
  let scrollOffset = 0;
197
192
  if (activeCardIndex >= visibleCount) {
198
193
  scrollOffset = activeCardIndex - visibleCount + 1;
199
194
  }
200
- scrollOffset = Math.max(
201
- 0,
202
- Math.min(scrollOffset, Math.max(0, sortedCards.length - visibleCount))
203
- );
204
- const visibleCards = sortedCards.slice(scrollOffset, scrollOffset + visibleCount);
195
+ scrollOffset = Math.max(0, Math.min(scrollOffset, Math.max(0, cards.length - visibleCount)));
196
+ const visibleCards = cards.slice(scrollOffset, scrollOffset + visibleCount);
205
197
  const hiddenAbove = scrollOffset;
206
- const hiddenBelow = Math.max(0, sortedCards.length - scrollOffset - visibleCount);
198
+ const hiddenBelow = Math.max(0, cards.length - scrollOffset - visibleCount);
207
199
  const borderColor = isFocused ? "yellow" : "gray";
208
200
  const headerColor = isFocused ? "yellow" : "white";
209
201
  const errorCount = cards.filter((c) => c.hasError).length;
@@ -732,8 +724,14 @@ function KanbanApp({ config, initialCards = [] }) {
732
724
  kanbanEmitter.off("tui:exit", onExit);
733
725
  };
734
726
  }, [exit]);
735
- const backlog = cards.filter((c) => c.column === "backlog");
736
- const inProgress = cards.filter((c) => c.column === "in_progress");
727
+ const backlog = [...cards.filter((c) => c.column === "backlog")].sort((a, b) => {
728
+ if (a.hasError && !b.hasError) return 1;
729
+ if (!a.hasError && b.hasError) return -1;
730
+ return (a.queueOrder ?? 0) - (b.queueOrder ?? 0);
731
+ });
732
+ const inProgress = [...cards.filter((c) => c.column === "in_progress")].sort(
733
+ (a, b) => (a.startedAt ?? 0) - (b.startedAt ?? 0)
734
+ );
737
735
  const hasInProgress = inProgress.length > 0;
738
736
  useEffect4(() => {
739
737
  if (workComplete) {
@@ -746,7 +744,11 @@ function KanbanApp({ config, initialCards = [] }) {
746
744
  }
747
745
  return () => resetTitle();
748
746
  }, [inProgress, workComplete]);
749
- const done = cards.filter((c) => c.column === "done");
747
+ const done = [...cards.filter((c) => c.column === "done")].sort((a, b) => {
748
+ if (a.merged && !b.merged) return 1;
749
+ if (!a.merged && b.merged) return -1;
750
+ return (b.finishedAt ?? 0) - (a.finishedAt ?? 0);
751
+ });
750
752
  const columnCards = [backlog, inProgress, done];
751
753
  useEffect4(() => {
752
754
  if (!selectedCardId || activeView !== "detail") return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tarcisiopgs/lisa",
3
- "version": "1.22.2",
3
+ "version": "1.23.0",
4
4
  "description": "Autonomous issue resolver",
5
5
  "keywords": [
6
6
  "loop",