@tarcisiopgs/lisa 1.26.2 → 1.28.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.
@@ -1,20 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ getCachedUpdateInfo
4
+ } from "./chunk-7CIXBENY.js";
2
5
  import {
3
6
  kanbanEmitter,
4
7
  useKanbanState
5
- } from "./chunk-235DMOXJ.js";
8
+ } from "./chunk-5N4BWHIT.js";
6
9
  import {
7
10
  resetTitle,
8
11
  startSpinner,
9
12
  stopSpinner
10
13
  } from "./chunk-72CYGBT4.js";
11
- import {
12
- getCachedUpdateInfo
13
- } from "./chunk-7CIXBENY.js";
14
14
 
15
15
  // src/ui/kanban.tsx
16
- import { Box as Box6, useApp, useInput as useInput2 } from "ink";
17
- import { useEffect as useEffect4, useState as useState4 } from "react";
16
+ import { Box as Box9, useApp, useInput as useInput4 } from "ink";
17
+ import { useEffect as useEffect4, useState as useState6 } from "react";
18
18
 
19
19
  // src/ui/board.tsx
20
20
  import { Box as Box3, Text as Text3 } from "ink";
@@ -26,11 +26,8 @@ import { Box as Box2, Text as Text2 } from "ink";
26
26
  import { Box, Text } from "ink";
27
27
  import Spinner from "ink-spinner";
28
28
  import { useEffect, useState } from "react";
29
- import { jsx, jsxs } from "react/jsx-runtime";
30
- var WIDE_CHAR_RE = /[\u1100-\u115F\u2E80-\u303E\u3041-\u33BF\u3400-\u4DBF\u4E00-\uA4CF\uAC00-\uD7FF\uF900-\uFAFF\uFE10-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]|[\u{1F000}-\u{1FFFF}]|[\u{20000}-\u{2FA20}]/gu;
31
- function stripDoubleWidth(str) {
32
- return str.replace(WIDE_CHAR_RE, "?");
33
- }
29
+
30
+ // src/ui/format.ts
34
31
  function formatElapsed(ms) {
35
32
  const seconds = Math.floor(ms / 1e3);
36
33
  const minutes = Math.floor(seconds / 60);
@@ -38,6 +35,13 @@ function formatElapsed(ms) {
38
35
  if (minutes > 0) return `${minutes}m ${remainingSeconds}s`;
39
36
  return `${seconds}s`;
40
37
  }
38
+
39
+ // src/ui/card.tsx
40
+ import { jsx, jsxs } from "react/jsx-runtime";
41
+ var WIDE_CHAR_RE = /[\u1100-\u115F\u2E80-\u303E\u3041-\u33BF\u3400-\u4DBF\u4E00-\uA4CF\uAC00-\uD7FF\uF900-\uFAFF\uFE10-\uFE6F\uFF00-\uFF60\uFFE0-\uFFE6]|[\u{1F000}-\u{1FFFF}]|[\u{20000}-\u{2FA20}]/gu;
42
+ function stripDoubleWidth(str) {
43
+ return str.replace(WIDE_CHAR_RE, "?");
44
+ }
41
45
  function wrapTitle(title, maxWidth) {
42
46
  if (title.length <= maxWidth) return [title, ""];
43
47
  const words = title.split(" ");
@@ -245,15 +249,9 @@ function Column({
245
249
 
246
250
  // src/ui/board.tsx
247
251
  import { Fragment, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
248
- function formatDuration(ms) {
249
- const totalSeconds = Math.floor(ms / 1e3);
250
- const minutes = Math.floor(totalSeconds / 60);
251
- const seconds = totalSeconds % 60;
252
- if (minutes > 0) return `${minutes}m ${seconds}s`;
253
- return `${seconds}s`;
254
- }
255
252
  function Board({
256
253
  cards,
254
+ columns,
257
255
  labels,
258
256
  isEmpty,
259
257
  isWatching = false,
@@ -263,9 +261,7 @@ function Board({
263
261
  activeCardIndex = 0,
264
262
  paused = false
265
263
  }) {
266
- const backlog = cards.filter((c) => c.column === "backlog");
267
- const inProgress = cards.filter((c) => c.column === "in_progress");
268
- const done = cards.filter((c) => c.column === "done");
264
+ const { backlog, inProgress, done } = columns;
269
265
  if (isWatching) {
270
266
  return /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs3(
271
267
  Box3,
@@ -314,7 +310,7 @@ function Board({
314
310
  }
315
311
  ) });
316
312
  }
317
- if (isEmpty) {
313
+ if (isEmpty && cards.length === 0) {
318
314
  return /* @__PURE__ */ jsx3(Box3, { flexGrow: 1, alignItems: "center", justifyContent: "center", children: /* @__PURE__ */ jsxs3(
319
315
  Box3,
320
316
  {
@@ -328,7 +324,7 @@ function Board({
328
324
  /* @__PURE__ */ jsx3(Box3, { height: 1 }),
329
325
  /* @__PURE__ */ jsx3(Text3, { color: "white", dimColor: true, children: "No issues match the current filters." }),
330
326
  /* @__PURE__ */ jsx3(Box3, { height: 1 }),
331
- /* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: "Check source config \xB7 labels \xB7 status" })
327
+ /* @__PURE__ */ jsx3(Text3, { color: "gray", dimColor: true, children: "Press [n] to plan and create new issues" })
332
328
  ]
333
329
  }
334
330
  ) });
@@ -339,7 +335,7 @@ function Board({
339
335
  /* @__PURE__ */ jsx3(Text3, { color: "white", bold: true, children: workComplete.total }),
340
336
  /* @__PURE__ */ jsx3(Text3, { color: "white", children: ` issue${workComplete.total !== 1 ? "s" : ""} resolved` }),
341
337
  /* @__PURE__ */ jsx3(Text3, { color: "green", children: " \xB7 " }),
342
- /* @__PURE__ */ jsx3(Text3, { color: "green", bold: true, children: formatDuration(workComplete.duration) })
338
+ /* @__PURE__ */ jsx3(Text3, { color: "green", bold: true, children: formatElapsed(workComplete.duration) })
343
339
  ] }),
344
340
  /* @__PURE__ */ jsxs3(Box3, { flexGrow: 1, flexDirection: "row", children: [
345
341
  /* @__PURE__ */ jsx3(
@@ -413,13 +409,6 @@ function openUrl(url) {
413
409
  }
414
410
  });
415
411
  }
416
- function formatElapsed2(ms) {
417
- const seconds = Math.floor(ms / 1e3);
418
- const minutes = Math.floor(seconds / 60);
419
- const remainingSeconds = seconds % 60;
420
- if (minutes > 0) return `${minutes}m ${remainingSeconds}s`;
421
- return `${seconds}s`;
422
- }
423
412
  function hyperlink(url, text) {
424
413
  return `\x1B]8;;${url}\x07${text}\x1B]8;;\x07`;
425
414
  }
@@ -495,8 +484,8 @@ function IssueDetail({ card, onBack }) {
495
484
  }
496
485
  });
497
486
  const { columns: terminalCols, rows: terminalRows } = useTerminalSize();
498
- const SIDEBAR_TOTAL_WIDTH = 30;
499
- const maxLineWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH - 4);
487
+ const SIDEBAR_TOTAL_WIDTH4 = 30;
488
+ const maxLineWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH4 - 4);
500
489
  const prCount = card.prUrls.length > 0 ? card.prUrls.length : 0;
501
490
  const logFileRow = card.logFile ? 1 : 0;
502
491
  const headerOverhead = 6 + prCount + logFileRow;
@@ -512,13 +501,13 @@ function IssueDetail({ card, onBack }) {
512
501
  let isRunning = false;
513
502
  if (card.column === "in_progress" && card.startedAt !== void 0) {
514
503
  const pauseOffset = (card.pauseAccumulated ?? 0) + (card.pausedAt ? now - card.pausedAt : 0);
515
- elapsedDisplay = formatElapsed2(Math.max(0, now - card.startedAt - pauseOffset));
504
+ elapsedDisplay = formatElapsed(Math.max(0, now - card.startedAt - pauseOffset));
516
505
  isRunning = !isPausedInProgress;
517
506
  } else if (card.column === "done" && card.startedAt !== void 0 && card.finishedAt !== void 0) {
518
- elapsedDisplay = formatElapsed2(card.finishedAt - card.startedAt);
507
+ elapsedDisplay = formatElapsed(card.finishedAt - card.startedAt);
519
508
  }
520
509
  const separator = useMemo(() => {
521
- const separatorInner = Math.max(0, terminalCols - SIDEBAR_TOTAL_WIDTH - 4);
510
+ const separatorInner = Math.max(0, terminalCols - SIDEBAR_TOTAL_WIDTH4 - 4);
522
511
  return `\u2560${"\u2550".repeat(Math.max(0, separatorInner - 2))}\u2563`;
523
512
  }, [terminalCols]);
524
513
  const totalLines = lines.length;
@@ -526,7 +515,7 @@ function IssueDetail({ card, onBack }) {
526
515
  return /* @__PURE__ */ jsxs4(
527
516
  Box4,
528
517
  {
529
- width: terminalCols - SIDEBAR_TOTAL_WIDTH,
518
+ width: terminalCols - SIDEBAR_TOTAL_WIDTH4,
530
519
  flexDirection: "column",
531
520
  borderStyle: "single",
532
521
  borderColor: "yellow",
@@ -584,11 +573,253 @@ function IssueDetail({ card, onBack }) {
584
573
  );
585
574
  }
586
575
 
576
+ // src/ui/plan-chat.tsx
577
+ import { Box as Box5, Text as Text5, useInput as useInput2 } from "ink";
578
+ import Spinner3 from "ink-spinner";
579
+ import { useState as useState4 } from "react";
580
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
581
+ var SIDEBAR_TOTAL_WIDTH = 30;
582
+ function PlanChat({ messages, isThinking, onSend, onCancel }) {
583
+ const [inputBuffer, setInputBuffer] = useState4("");
584
+ const { columns: terminalCols, rows: terminalRows } = useTerminalSize();
585
+ const maxWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH - 4);
586
+ const messageAreaHeight = Math.max(1, terminalRows - 6);
587
+ useInput2((input, key) => {
588
+ if (key.escape) {
589
+ onCancel();
590
+ return;
591
+ }
592
+ if (key.return) {
593
+ const trimmed = inputBuffer.trim();
594
+ if (trimmed.length > 0) {
595
+ onSend(trimmed);
596
+ setInputBuffer("");
597
+ }
598
+ return;
599
+ }
600
+ if (key.backspace || key.delete) {
601
+ setInputBuffer((prev) => prev.slice(0, -1));
602
+ return;
603
+ }
604
+ if (input && !key.ctrl && !key.meta && !key.upArrow && !key.downArrow && !key.leftArrow && !key.rightArrow && !key.tab) {
605
+ setInputBuffer((prev) => prev + input);
606
+ }
607
+ });
608
+ const displayLines = [];
609
+ for (const msg of messages) {
610
+ const lines = msg.content.split("\n");
611
+ for (let i = 0; i < lines.length; i++) {
612
+ displayLines.push({ role: msg.role, text: lines[i] ?? "", isFirst: i === 0 });
613
+ }
614
+ }
615
+ const visibleMessages = displayLines.slice(-messageAreaHeight);
616
+ return /* @__PURE__ */ jsxs5(
617
+ Box5,
618
+ {
619
+ width: terminalCols - SIDEBAR_TOTAL_WIDTH,
620
+ flexDirection: "column",
621
+ borderStyle: "single",
622
+ borderColor: "yellow",
623
+ paddingX: 1,
624
+ paddingY: 0,
625
+ children: [
626
+ /* @__PURE__ */ jsxs5(Box5, { children: [
627
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", bold: true, children: "PLAN" }),
628
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", children: " \u2014 " }),
629
+ /* @__PURE__ */ jsx5(Text5, { color: "white", children: "Describe your goal" })
630
+ ] }),
631
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", dimColor: true, children: "\u2500".repeat(Math.max(0, maxWidth)) }),
632
+ /* @__PURE__ */ jsxs5(Box5, { height: messageAreaHeight, flexDirection: "column", overflow: "hidden", children: [
633
+ visibleMessages.length === 0 && !isThinking && /* @__PURE__ */ jsx5(Box5, { marginTop: 1, children: /* @__PURE__ */ jsx5(Text5, { color: "gray", dimColor: true, children: "What would you like to plan? Describe the feature or goal." }) }),
634
+ visibleMessages.map((line, i) => {
635
+ const isNewMessage = line.isFirst && (i === 0 || visibleMessages[i - 1]?.role !== line.role);
636
+ return (
637
+ // biome-ignore lint/suspicious/noArrayIndexKey: chat lines have no stable key
638
+ /* @__PURE__ */ jsx5(Box5, { flexDirection: "row", marginTop: isNewMessage && i > 0 ? 1 : 0, children: /* @__PURE__ */ jsx5(
639
+ Text5,
640
+ {
641
+ color: line.role === "user" ? "cyan" : "white",
642
+ dimColor: line.role === "ai",
643
+ wrap: "truncate",
644
+ children: line.text
645
+ }
646
+ ) }, i)
647
+ );
648
+ }),
649
+ isThinking && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", marginTop: 0, children: [
650
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: /* @__PURE__ */ jsx5(Spinner3, { type: "dots" }) }),
651
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", dimColor: true, children: " Analyzing..." })
652
+ ] })
653
+ ] }),
654
+ /* @__PURE__ */ jsx5(Text5, { color: "yellow", dimColor: true, children: "\u2500".repeat(Math.max(0, maxWidth)) }),
655
+ /* @__PURE__ */ jsxs5(Box5, { flexDirection: "row", children: [
656
+ /* @__PURE__ */ jsx5(Text5, { color: "cyan", bold: true, children: "> " }),
657
+ /* @__PURE__ */ jsx5(Text5, { color: "white", children: inputBuffer }),
658
+ /* @__PURE__ */ jsx5(Text5, { color: "gray", children: "_" })
659
+ ] })
660
+ ]
661
+ }
662
+ );
663
+ }
664
+
665
+ // src/ui/plan-detail.tsx
666
+ import { Box as Box6, Text as Text6, useInput as useInput3 } from "ink";
667
+ import { useState as useState5 } from "react";
668
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
669
+ var SIDEBAR_TOTAL_WIDTH2 = 30;
670
+ function PlanDetail({ issue, onBack, onEdit }) {
671
+ const { columns: terminalCols, rows: terminalRows } = useTerminalSize();
672
+ const maxWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH2 - 4);
673
+ const [scrollOffset, setScrollOffset] = useState5(0);
674
+ useInput3((input, key) => {
675
+ if (key.escape) {
676
+ onBack();
677
+ return;
678
+ }
679
+ if (input === "e") {
680
+ onEdit();
681
+ return;
682
+ }
683
+ if (key.upArrow) {
684
+ setScrollOffset((prev) => Math.max(0, prev - 1));
685
+ }
686
+ if (key.downArrow) {
687
+ setScrollOffset((prev) => prev + 1);
688
+ }
689
+ });
690
+ const contentLines = [];
691
+ if (issue.description) {
692
+ const descLines = issue.description.split("\n");
693
+ for (const line of descLines) {
694
+ contentLines.push({ text: line, color: "white" });
695
+ }
696
+ contentLines.push({ text: "", color: "white" });
697
+ }
698
+ if (issue.acceptanceCriteria.length > 0) {
699
+ contentLines.push({ text: "ACCEPTANCE CRITERIA", color: "yellow", bold: true });
700
+ for (const criterion of issue.acceptanceCriteria) {
701
+ contentLines.push({ text: `\u2610 ${criterion}`, color: "white" });
702
+ }
703
+ contentLines.push({ text: "", color: "white" });
704
+ }
705
+ if (issue.dependsOn.length > 0) {
706
+ contentLines.push({ text: "DEPENDENCIES", color: "yellow", bold: true });
707
+ contentLines.push({
708
+ text: `Depends on: ${issue.dependsOn.map((d) => `#${d}`).join(", ")}`,
709
+ color: "gray",
710
+ dimColor: true
711
+ });
712
+ contentLines.push({ text: "", color: "white" });
713
+ }
714
+ if (issue.relevantFiles.length > 0) {
715
+ contentLines.push({ text: "RELEVANT FILES", color: "yellow", bold: true });
716
+ for (const file of issue.relevantFiles) {
717
+ contentLines.push({ text: ` ${file}`, color: "gray", dimColor: true });
718
+ }
719
+ }
720
+ const bodyHeight = Math.max(1, terminalRows - 5);
721
+ const clampedOffset = Math.min(scrollOffset, Math.max(0, contentLines.length - bodyHeight));
722
+ const visibleLines = contentLines.slice(clampedOffset, clampedOffset + bodyHeight);
723
+ return /* @__PURE__ */ jsxs6(
724
+ Box6,
725
+ {
726
+ width: terminalCols - SIDEBAR_TOTAL_WIDTH2,
727
+ flexDirection: "column",
728
+ borderStyle: "single",
729
+ borderColor: "yellow",
730
+ paddingX: 1,
731
+ paddingY: 0,
732
+ children: [
733
+ /* @__PURE__ */ jsxs6(Box6, { children: [
734
+ /* @__PURE__ */ jsx6(Text6, { color: "yellow", bold: true, children: `#${issue.order} ` }),
735
+ /* @__PURE__ */ jsx6(Text6, { color: "white", bold: true, wrap: "truncate", children: issue.title })
736
+ ] }),
737
+ issue.dependsOn.length > 0 && /* @__PURE__ */ jsx6(Box6, { children: /* @__PURE__ */ jsxs6(Text6, { color: "gray", dimColor: true, children: [
738
+ "\u2192 depends on: ",
739
+ issue.dependsOn.map((d) => `#${d}`).join(", ")
740
+ ] }) }),
741
+ /* @__PURE__ */ jsx6(Text6, { color: "yellow", dimColor: true, children: "\u2500".repeat(Math.max(0, maxWidth)) }),
742
+ /* @__PURE__ */ jsx6(Box6, { height: bodyHeight, flexDirection: "column", overflow: "hidden", children: visibleLines.map((line, i) => {
743
+ const { text, color, bold: isBold, dimColor: isDim } = line;
744
+ return (
745
+ // biome-ignore lint/suspicious/noArrayIndexKey: content lines have no stable key
746
+ /* @__PURE__ */ jsx6(Text6, { color, bold: isBold, dimColor: isDim, wrap: "truncate", children: text }, i)
747
+ );
748
+ }) })
749
+ ]
750
+ }
751
+ );
752
+ }
753
+
754
+ // src/ui/plan-review.tsx
755
+ import { Box as Box7, Text as Text7 } from "ink";
756
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
757
+ var SIDEBAR_TOTAL_WIDTH3 = 30;
758
+ var MAX_VISIBLE_FILES = 3;
759
+ function PlanReview({ goal, issues, selectedIndex }) {
760
+ const { columns: terminalCols } = useTerminalSize();
761
+ const maxWidth = Math.max(1, terminalCols - SIDEBAR_TOTAL_WIDTH3 - 4);
762
+ return /* @__PURE__ */ jsxs7(
763
+ Box7,
764
+ {
765
+ width: terminalCols - SIDEBAR_TOTAL_WIDTH3,
766
+ flexDirection: "column",
767
+ borderStyle: "single",
768
+ borderColor: "yellow",
769
+ paddingX: 1,
770
+ paddingY: 0,
771
+ children: [
772
+ /* @__PURE__ */ jsxs7(Box7, { children: [
773
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", bold: true, children: "PLAN: " }),
774
+ /* @__PURE__ */ jsx7(Text7, { color: "white", bold: true, wrap: "truncate", children: goal })
775
+ ] }),
776
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", dimColor: true, children: "\u2500".repeat(Math.max(0, maxWidth)) }),
777
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "column", flexGrow: 1, children: [
778
+ issues.length === 0 && /* @__PURE__ */ jsx7(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx7(Text7, { color: "gray", dimColor: true, children: "No issues in the plan." }) }),
779
+ issues.map((issue, i) => {
780
+ const isSelected = i === selectedIndex;
781
+ const extraFiles = issue.relevantFiles.length - MAX_VISIBLE_FILES;
782
+ const visibleFiles = issue.relevantFiles.slice(0, MAX_VISIBLE_FILES);
783
+ return /* @__PURE__ */ jsxs7(
784
+ Box7,
785
+ {
786
+ flexDirection: "column",
787
+ borderStyle: isSelected ? "single" : void 0,
788
+ borderColor: isSelected ? "yellow" : void 0,
789
+ paddingX: isSelected ? 1 : 0,
790
+ marginLeft: isSelected ? 0 : 2,
791
+ marginTop: i === 0 ? 0 : 0,
792
+ children: [
793
+ /* @__PURE__ */ jsxs7(Box7, { flexDirection: "row", children: [
794
+ /* @__PURE__ */ jsx7(Text7, { color: isSelected ? "yellow" : "gray", children: isSelected ? "\u25B8 " : " " }),
795
+ /* @__PURE__ */ jsx7(Text7, { color: "yellow", bold: true, children: `${issue.order}. ` }),
796
+ /* @__PURE__ */ jsx7(Text7, { color: "gray", children: "\u25CB " }),
797
+ /* @__PURE__ */ jsx7(Text7, { color: "white", bold: isSelected, wrap: "truncate", children: issue.title })
798
+ ] }),
799
+ issue.dependsOn.length > 0 && /* @__PURE__ */ jsx7(Box7, { marginLeft: 5, children: /* @__PURE__ */ jsxs7(Text7, { color: "gray", dimColor: true, children: [
800
+ "\u2192 depends on: ",
801
+ issue.dependsOn.map((d) => `#${d}`).join(", ")
802
+ ] }) }),
803
+ visibleFiles.length > 0 && /* @__PURE__ */ jsx7(Box7, { marginLeft: 5, flexDirection: "row", children: /* @__PURE__ */ jsxs7(Text7, { color: "gray", dimColor: true, children: [
804
+ visibleFiles.join(", "),
805
+ extraFiles > 0 ? ` +${extraFiles} more` : ""
806
+ ] }) })
807
+ ]
808
+ },
809
+ issue.order
810
+ );
811
+ })
812
+ ] })
813
+ ]
814
+ }
815
+ );
816
+ }
817
+
587
818
  // src/ui/sidebar.tsx
588
819
  import { existsSync } from "fs";
589
820
  import { basename, join } from "path";
590
- import { Box as Box5, Text as Text5 } from "ink";
591
- import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
821
+ import { Box as Box8, Text as Text8 } from "ink";
822
+ import { Fragment as Fragment2, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
592
823
  function Sidebar({
593
824
  provider,
594
825
  model,
@@ -599,12 +830,13 @@ function Sidebar({
599
830
  paused = false,
600
831
  hasInProgress = false,
601
832
  hasPrUrl = false,
602
- updateInfo = null
833
+ updateInfo = null,
834
+ workComplete = null
603
835
  }) {
604
836
  const dir = basename(cwd).toUpperCase();
605
837
  const cwdLabel = existsSync(join(cwd, ".git")) ? "REPOSITORY" : "WORKSPACE";
606
- return /* @__PURE__ */ jsxs5(
607
- Box5,
838
+ return /* @__PURE__ */ jsxs8(
839
+ Box8,
608
840
  {
609
841
  flexDirection: "column",
610
842
  width: 28,
@@ -614,89 +846,115 @@ function Sidebar({
614
846
  paddingX: 1,
615
847
  paddingY: 0,
616
848
  children: [
617
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
618
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557" }),
619
- /* @__PURE__ */ jsxs5(Text5, { color: "yellow", children: [
849
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
850
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u2554\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2557" }),
851
+ /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
620
852
  "\u2551 ",
621
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: "L I S A " }),
622
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", bold: true, children: "\u266A" }),
623
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: " " }),
853
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: "L I S A " }),
854
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", bold: true, children: "\u266A" }),
855
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: " " }),
624
856
  "\u2551"
625
857
  ] }),
626
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D" })
627
- ] }),
628
- /* @__PURE__ */ jsxs5(Box5, { marginBottom: 1, children: [
629
- /* @__PURE__ */ jsx5(Text5, { color: paused ? "yellow" : "green", children: paused ? "\u23F8 " : "\u25B6 " }),
630
- /* @__PURE__ */ jsx5(Text5, { color: paused ? "yellow" : "green", bold: true, children: paused ? "PAUSED" : "RUNNING" })
858
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u255D" })
631
859
  ] }),
632
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
633
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
634
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: "PROVIDER" }),
635
- /* @__PURE__ */ jsxs5(Box5, { children: [
636
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B8 " }),
637
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: provider.toUpperCase() })
860
+ /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: activeView === "idle" || activeView === "empty" ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
861
+ /* @__PURE__ */ jsx8(Text8, { color: "gray", children: "\u25C7 " }),
862
+ /* @__PURE__ */ jsx8(Text8, { color: "gray", bold: true, children: "IDLE" })
863
+ ] }) : /* @__PURE__ */ jsxs8(Fragment2, { children: [
864
+ /* @__PURE__ */ jsx8(Text8, { color: paused ? "yellow" : "green", children: paused ? "\u23F8 " : "\u25B6 " }),
865
+ /* @__PURE__ */ jsx8(Text8, { color: paused ? "yellow" : "green", bold: true, children: paused ? "PAUSED" : "RUNNING" })
866
+ ] }) }),
867
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
868
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
869
+ /* @__PURE__ */ jsx8(Text8, { color: "white", dimColor: true, children: "PROVIDER" }),
870
+ /* @__PURE__ */ jsxs8(Box8, { children: [
871
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 " }),
872
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: provider.toUpperCase() })
638
873
  ] })
639
874
  ] }),
640
- models.length <= 1 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
641
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: "MODEL" }),
642
- /* @__PURE__ */ jsxs5(Box5, { children: [
643
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B8 " }),
644
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: (() => {
875
+ models.length <= 1 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
876
+ /* @__PURE__ */ jsx8(Text8, { color: "white", dimColor: true, children: "MODEL" }),
877
+ /* @__PURE__ */ jsxs8(Box8, { children: [
878
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 " }),
879
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: (() => {
645
880
  const m = (model ?? "default").toUpperCase();
646
881
  return m.length > 19 ? `${m.slice(0, 18)}\u2026` : m;
647
882
  })() })
648
883
  ] })
649
884
  ] }),
650
- models.length > 1 && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
651
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: "MODEL QUEUE" }),
652
- models.map((m, i) => /* @__PURE__ */ jsxs5(Box5, { paddingLeft: 1, flexDirection: "row", children: [
653
- /* @__PURE__ */ jsx5(Text5, { color: m === model ? "yellow" : "gray", children: m === model ? "\u25CF " : `${i + 1}. ` }),
654
- /* @__PURE__ */ jsx5(Text5, { color: m === model ? "yellow" : "white", bold: m === model, children: (() => {
885
+ models.length > 1 && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
886
+ /* @__PURE__ */ jsx8(Text8, { color: "white", dimColor: true, children: "MODEL QUEUE" }),
887
+ models.map((m, i) => /* @__PURE__ */ jsxs8(Box8, { paddingLeft: 1, flexDirection: "row", children: [
888
+ /* @__PURE__ */ jsx8(Text8, { color: m === model ? "yellow" : "gray", children: m === model ? "\u25CF " : `${i + 1}. ` }),
889
+ /* @__PURE__ */ jsx8(Text8, { color: m === model ? "yellow" : "white", bold: m === model, children: (() => {
655
890
  const display = m.toUpperCase();
656
891
  return display.length > 19 ? `${display.slice(0, 18)}\u2026` : display;
657
892
  })() })
658
893
  ] }, m))
659
894
  ] }),
660
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
661
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: "SOURCE" }),
662
- /* @__PURE__ */ jsxs5(Box5, { children: [
663
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B8 " }),
664
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: source.toUpperCase() })
895
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
896
+ /* @__PURE__ */ jsx8(Text8, { color: "white", dimColor: true, children: "SOURCE" }),
897
+ /* @__PURE__ */ jsxs8(Box8, { children: [
898
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 " }),
899
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: source.toUpperCase() })
665
900
  ] })
666
901
  ] }),
667
- /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginTop: 1, children: [
668
- /* @__PURE__ */ jsx5(Text5, { color: "white", dimColor: true, children: cwdLabel }),
669
- /* @__PURE__ */ jsxs5(Box5, { children: [
670
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u25B8 " }),
671
- /* @__PURE__ */ jsx5(Text5, { color: "white", bold: true, children: dir.length > 18 ? `${dir.slice(0, 15)}\u2026` : dir })
902
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginTop: 1, children: [
903
+ /* @__PURE__ */ jsx8(Text8, { color: "white", dimColor: true, children: cwdLabel }),
904
+ /* @__PURE__ */ jsxs8(Box8, { children: [
905
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u25B8 " }),
906
+ /* @__PURE__ */ jsx8(Text8, { color: "white", bold: true, children: dir.length > 18 ? `${dir.slice(0, 15)}\u2026` : dir })
672
907
  ] })
673
908
  ] }),
674
- /* @__PURE__ */ jsx5(Box5, { flexGrow: 1 }),
675
- updateInfo && /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", marginBottom: 1, children: [
676
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
677
- /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
678
- /* @__PURE__ */ jsx5(Text5, { color: "green", bold: true, children: "UPDATE AVAILABLE" }),
679
- /* @__PURE__ */ jsxs5(Text5, { dimColor: true, children: [
909
+ /* @__PURE__ */ jsx8(Box8, { flexGrow: 1 }),
910
+ updateInfo && /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", marginBottom: 1, children: [
911
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
912
+ /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
913
+ /* @__PURE__ */ jsx8(Text8, { color: "green", bold: true, children: "UPDATE AVAILABLE" }),
914
+ /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
680
915
  updateInfo.currentVersion,
681
916
  " \u2192 ",
682
- /* @__PURE__ */ jsx5(Text5, { color: "green", children: updateInfo.latestVersion })
917
+ /* @__PURE__ */ jsx8(Text8, { color: "green", children: updateInfo.latestVersion })
683
918
  ] }),
684
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "npm i -g @tarcisiopgs/lisa" })
919
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "npm i -g @tarcisiopgs/lisa" })
685
920
  ] })
686
921
  ] }),
687
- /* @__PURE__ */ jsx5(Text5, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
688
- activeView === "board" ? /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
689
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2190\u2192] columns" }),
690
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2191\u2193] navigate" }),
691
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u21B5] detail" }),
692
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: paused ? "[p] resume" : "[p] pause" }),
693
- hasInProgress && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[k] kill" }),
694
- hasInProgress && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[s] skip" }),
695
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[q] quit" })
696
- ] }) : /* @__PURE__ */ jsxs5(Box5, { marginTop: 1, flexDirection: "column", children: [
697
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[\u2191\u2193] scroll" }),
698
- hasPrUrl && /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[o] open PR(s)" }),
699
- /* @__PURE__ */ jsx5(Text5, { dimColor: true, children: "[Esc] board" })
922
+ /* @__PURE__ */ jsx8(Text8, { color: "yellow", children: "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500" }),
923
+ activeView === "board" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
924
+ hasInProgress && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[k] kill" }),
925
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[n] plan" }),
926
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" })
927
+ ] }),
928
+ activeView === "detail" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
929
+ hasPrUrl && /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[o] open PR" }),
930
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[Esc] back" })
931
+ ] }),
932
+ activeView === "watching" && /* @__PURE__ */ jsx8(Box8, { marginTop: 1, flexDirection: "column", children: /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" }) }),
933
+ activeView === "watch-prompt" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
934
+ workComplete && /* @__PURE__ */ jsx8(Text8, { color: "green", bold: true, children: `${workComplete.total} issue${workComplete.total !== 1 ? "s" : ""} \xB7 ${formatElapsed(workComplete.duration)}` }),
935
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[w] watch" }),
936
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" })
937
+ ] }),
938
+ activeView === "empty" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
939
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[n] plan" }),
940
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" })
941
+ ] }),
942
+ activeView === "idle" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
943
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[r] run" }),
944
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[n] plan" }),
945
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[q] quit" })
946
+ ] }),
947
+ activeView === "plan-chat" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
948
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[\u21B5] send" }),
949
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[Esc] cancel" })
950
+ ] }),
951
+ activeView === "plan-review" && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, flexDirection: "column", children: [
952
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[\u2191\u2193] navigate" }),
953
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[\u21B5] detail" }),
954
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[e] edit" }),
955
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[d] delete" }),
956
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[a] approve" }),
957
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "[Esc] cancel" })
700
958
  ] })
701
959
  ]
702
960
  }
@@ -704,7 +962,7 @@ function Sidebar({
704
962
  }
705
963
 
706
964
  // src/ui/kanban.tsx
707
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
965
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
708
966
  function KanbanApp({ config, initialCards = [] }) {
709
967
  const { exit } = useApp();
710
968
  const { cards, isEmpty, isWatching, isWatchPrompt, workComplete, modelInUse } = useKanbanState(
@@ -712,12 +970,17 @@ function KanbanApp({ config, initialCards = [] }) {
712
970
  initialCards
713
971
  );
714
972
  const { rows } = useTerminalSize();
715
- const [activeView, setActiveView] = useState4("board");
716
- const [activeColIndex, setActiveColIndex] = useState4(0);
717
- const [activeCardIndex, setActiveCardIndex] = useState4(0);
718
- const [paused, setPaused] = useState4(false);
719
- const [selectedCardId, setSelectedCardId] = useState4(null);
720
- const [updateInfo] = useState4(() => getCachedUpdateInfo());
973
+ const [activeView, setActiveView] = useState6("board");
974
+ const [activeColIndex, setActiveColIndex] = useState6(0);
975
+ const [activeCardIndex, setActiveCardIndex] = useState6(0);
976
+ const [paused, setPaused] = useState6(false);
977
+ const [selectedCardId, setSelectedCardId] = useState6(null);
978
+ const [updateInfo] = useState6(() => getCachedUpdateInfo());
979
+ const [planMessages, setPlanMessages] = useState6([]);
980
+ const [planIssues, setPlanIssues] = useState6([]);
981
+ const [planGoal, setPlanGoal] = useState6("");
982
+ const [planThinking, setPlanThinking] = useState6(false);
983
+ const [planSelectedIndex, setPlanSelectedIndex] = useState6(0);
721
984
  useEffect4(() => {
722
985
  const initialModel = config.provider_options?.[config.provider]?.models?.[0];
723
986
  if (!modelInUse && initialModel) {
@@ -731,6 +994,39 @@ function KanbanApp({ config, initialCards = [] }) {
731
994
  kanbanEmitter.off("tui:exit", onExit);
732
995
  };
733
996
  }, [exit]);
997
+ useEffect4(() => {
998
+ const onAiMessage = (content) => {
999
+ setPlanMessages((prev) => [...prev, { role: "ai", content }]);
1000
+ setPlanThinking(false);
1001
+ };
1002
+ const onThinking = () => setPlanThinking(true);
1003
+ const onIssuesReady = (issues) => {
1004
+ setPlanIssues(issues);
1005
+ setPlanSelectedIndex(0);
1006
+ setActiveView("plan-review");
1007
+ setPlanThinking(false);
1008
+ };
1009
+ const onEditResult = (index, updated) => {
1010
+ if (!updated) return;
1011
+ setPlanIssues((prev) => {
1012
+ const next = [...prev];
1013
+ if (next[index]) {
1014
+ next[index] = { ...next[index], ...updated };
1015
+ }
1016
+ return next;
1017
+ });
1018
+ };
1019
+ kanbanEmitter.on("plan:ai-message", onAiMessage);
1020
+ kanbanEmitter.on("plan:thinking", onThinking);
1021
+ kanbanEmitter.on("plan:issues-ready", onIssuesReady);
1022
+ kanbanEmitter.on("plan:edit-result", onEditResult);
1023
+ return () => {
1024
+ kanbanEmitter.off("plan:ai-message", onAiMessage);
1025
+ kanbanEmitter.off("plan:thinking", onThinking);
1026
+ kanbanEmitter.off("plan:issues-ready", onIssuesReady);
1027
+ kanbanEmitter.off("plan:edit-result", onEditResult);
1028
+ };
1029
+ }, []);
734
1030
  const backlog = [...cards.filter((c) => c.column === "backlog")].sort((a, b) => {
735
1031
  if (a.hasError && !b.hasError) return 1;
736
1032
  if (!a.hasError && b.hasError) return -1;
@@ -775,7 +1071,7 @@ function KanbanApp({ config, initialCards = [] }) {
775
1071
  setActiveColIndex(newColIndex);
776
1072
  setActiveCardIndex(newCardIndex);
777
1073
  }, [cards, selectedCardId, activeView]);
778
- useInput2((input, key) => {
1074
+ useInput4((input, key) => {
779
1075
  if (isWatchPrompt) {
780
1076
  if (input === "w") {
781
1077
  kanbanEmitter.emit("work:watch-prompt-resolved");
@@ -788,8 +1084,63 @@ function KanbanApp({ config, initialCards = [] }) {
788
1084
  }
789
1085
  return;
790
1086
  }
791
- if (input === "q") {
792
- process.emit("SIGINT");
1087
+ if (activeView === "plan-chat") {
1088
+ if (key.escape) {
1089
+ setActiveView("board");
1090
+ }
1091
+ return;
1092
+ }
1093
+ if (activeView === "plan-review") {
1094
+ if (key.escape) {
1095
+ setActiveView("board");
1096
+ return;
1097
+ }
1098
+ if (key.upArrow) {
1099
+ setPlanSelectedIndex((prev) => Math.max(0, prev - 1));
1100
+ return;
1101
+ }
1102
+ if (key.downArrow) {
1103
+ setPlanSelectedIndex((prev) => Math.min(planIssues.length - 1, prev + 1));
1104
+ return;
1105
+ }
1106
+ if (key.return) {
1107
+ setActiveView("plan-detail");
1108
+ return;
1109
+ }
1110
+ if (input === "a") {
1111
+ kanbanEmitter.emit("plan:approved", planIssues, planGoal);
1112
+ setActiveView("board");
1113
+ return;
1114
+ }
1115
+ if (input === "d" && planIssues.length > 0) {
1116
+ const newIssues = [...planIssues];
1117
+ const removed = newIssues.splice(planSelectedIndex, 1)[0];
1118
+ for (const issue of newIssues) {
1119
+ issue.dependsOn = issue.dependsOn.filter((d) => d !== removed?.order);
1120
+ }
1121
+ setPlanIssues(newIssues);
1122
+ if (newIssues.length === 0) {
1123
+ setActiveView("plan-chat");
1124
+ } else {
1125
+ setPlanSelectedIndex(Math.min(planSelectedIndex, Math.max(0, newIssues.length - 1)));
1126
+ }
1127
+ return;
1128
+ }
1129
+ if (input === "e") {
1130
+ kanbanEmitter.emit("plan:edit-issue", planSelectedIndex, planIssues[planSelectedIndex]);
1131
+ return;
1132
+ }
1133
+ return;
1134
+ }
1135
+ if (activeView === "plan-detail") {
1136
+ if (key.escape) {
1137
+ setActiveView("plan-review");
1138
+ return;
1139
+ }
1140
+ if (input === "e") {
1141
+ kanbanEmitter.emit("plan:edit-issue", planSelectedIndex, planIssues[planSelectedIndex]);
1142
+ return;
1143
+ }
793
1144
  return;
794
1145
  }
795
1146
  if (activeView === "detail") {
@@ -799,6 +1150,10 @@ function KanbanApp({ config, initialCards = [] }) {
799
1150
  }
800
1151
  return;
801
1152
  }
1153
+ if (input === "q") {
1154
+ process.emit("SIGINT");
1155
+ return;
1156
+ }
802
1157
  if (input === "p") {
803
1158
  const next = !paused;
804
1159
  setPaused(next);
@@ -824,6 +1179,27 @@ function KanbanApp({ config, initialCards = [] }) {
824
1179
  }
825
1180
  return;
826
1181
  }
1182
+ if (input === "r" && isEmpty && backlog.length > 0) {
1183
+ kanbanEmitter.emit("loop:run");
1184
+ return;
1185
+ }
1186
+ if (input === "n") {
1187
+ setActiveView("plan-chat");
1188
+ setPlanMessages([]);
1189
+ setPlanIssues([]);
1190
+ setPlanGoal("");
1191
+ setPlanThinking(false);
1192
+ return;
1193
+ }
1194
+ if (input === "1" || input === "2" || input === "3") {
1195
+ const targetCol = Number(input) - 1;
1196
+ if (targetCol !== activeColIndex) {
1197
+ setActiveColIndex(targetCol);
1198
+ const colLen = columnCards[targetCol]?.length ?? 0;
1199
+ setActiveCardIndex(Math.min(activeCardIndex, Math.max(0, colLen - 1)));
1200
+ }
1201
+ return;
1202
+ }
827
1203
  if (key.rightArrow) {
828
1204
  const nextCol = (activeColIndex + 1) % 3;
829
1205
  setActiveColIndex(nextCol);
@@ -864,8 +1240,18 @@ function KanbanApp({ config, initialCards = [] }) {
864
1240
  const hasPrUrl = (selectedCard?.prUrls.length ?? 0) > 0;
865
1241
  const providerOptions = config.provider_options?.[config.provider];
866
1242
  const models = providerOptions?.models || (providerOptions?.model ? [providerOptions.model] : []);
867
- return /* @__PURE__ */ jsxs6(Box6, { flexDirection: "row", height: rows, children: [
868
- /* @__PURE__ */ jsx6(
1243
+ let sidebarMode = "board";
1244
+ if (isWatchPrompt) sidebarMode = "watch-prompt";
1245
+ else if (isWatching) sidebarMode = "watching";
1246
+ else if (isEmpty && activeView === "board" && cards.length === 0) sidebarMode = "empty";
1247
+ else if (isEmpty && activeView === "board" && cards.length > 0) sidebarMode = "idle";
1248
+ else if (activeView === "plan-chat") sidebarMode = "plan-chat";
1249
+ else if (activeView === "plan-review" || activeView === "plan-detail")
1250
+ sidebarMode = "plan-review";
1251
+ else if (activeView === "detail") sidebarMode = "detail";
1252
+ else if (activeView === "board") sidebarMode = "board";
1253
+ return /* @__PURE__ */ jsxs9(Box9, { flexDirection: "row", height: rows, children: [
1254
+ /* @__PURE__ */ jsx9(
869
1255
  Sidebar,
870
1256
  {
871
1257
  provider: config.provider,
@@ -873,17 +1259,66 @@ function KanbanApp({ config, initialCards = [] }) {
873
1259
  models,
874
1260
  source: config.source,
875
1261
  cwd: process.cwd(),
876
- activeView,
1262
+ activeView: sidebarMode,
877
1263
  paused,
878
1264
  hasInProgress,
879
1265
  hasPrUrl,
880
- updateInfo
1266
+ updateInfo,
1267
+ workComplete
881
1268
  }
882
1269
  ),
883
- activeView === "board" || !selectedCard ? /* @__PURE__ */ jsx6(
1270
+ activeView === "plan-chat" ? /* @__PURE__ */ jsx9(
1271
+ PlanChat,
1272
+ {
1273
+ messages: planMessages,
1274
+ isThinking: planThinking,
1275
+ onSend: (msg) => {
1276
+ setPlanMessages((prev) => [...prev, { role: "user", content: msg }]);
1277
+ if (!planGoal) setPlanGoal(msg);
1278
+ kanbanEmitter.emit("plan:user-message", msg);
1279
+ },
1280
+ onCancel: () => setActiveView("board")
1281
+ }
1282
+ ) : activeView === "plan-review" ? /* @__PURE__ */ jsx9(
1283
+ PlanReview,
1284
+ {
1285
+ goal: planGoal,
1286
+ issues: planIssues,
1287
+ selectedIndex: planSelectedIndex,
1288
+ onNavigate: setPlanSelectedIndex,
1289
+ onViewDetail: (idx) => {
1290
+ setPlanSelectedIndex(idx);
1291
+ setActiveView("plan-detail");
1292
+ },
1293
+ onEdit: (idx) => kanbanEmitter.emit("plan:edit-issue", idx, planIssues[idx]),
1294
+ onDelete: (idx) => {
1295
+ const newIssues = [...planIssues];
1296
+ newIssues.splice(idx, 1);
1297
+ setPlanIssues(newIssues);
1298
+ if (newIssues.length === 0) {
1299
+ setActiveView("plan-chat");
1300
+ } else {
1301
+ setPlanSelectedIndex(Math.min(idx, Math.max(0, newIssues.length - 1)));
1302
+ }
1303
+ },
1304
+ onApprove: () => {
1305
+ kanbanEmitter.emit("plan:approved", planIssues, planGoal);
1306
+ setActiveView("board");
1307
+ },
1308
+ onCancel: () => setActiveView("board")
1309
+ }
1310
+ ) : activeView === "plan-detail" && planIssues[planSelectedIndex] ? /* @__PURE__ */ jsx9(
1311
+ PlanDetail,
1312
+ {
1313
+ issue: planIssues[planSelectedIndex],
1314
+ onBack: () => setActiveView("plan-review"),
1315
+ onEdit: () => kanbanEmitter.emit("plan:edit-issue", planSelectedIndex, planIssues[planSelectedIndex])
1316
+ }
1317
+ ) : activeView === "board" || !selectedCard ? /* @__PURE__ */ jsx9(
884
1318
  Board,
885
1319
  {
886
1320
  cards,
1321
+ columns: { backlog, inProgress, done },
887
1322
  labels,
888
1323
  isEmpty,
889
1324
  isWatching,
@@ -893,7 +1328,7 @@ function KanbanApp({ config, initialCards = [] }) {
893
1328
  activeCardIndex,
894
1329
  paused
895
1330
  }
896
- ) : /* @__PURE__ */ jsx6(
1331
+ ) : /* @__PURE__ */ jsx9(
897
1332
  IssueDetail,
898
1333
  {
899
1334
  card: selectedCard,