@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.
- package/README.md +21 -5
- package/dist/chunk-2TW2MJXF.js +116 -0
- package/dist/{chunk-CDI22S63.js → chunk-3EOEDL3T.js} +2 -2
- package/dist/{chunk-AGYOJQBR.js → chunk-42NKASE3.js} +8 -106
- package/dist/{chunk-235DMOXJ.js → chunk-5N4BWHIT.js} +186 -116
- package/dist/chunk-FCEUJ7VK.js +407 -0
- package/dist/chunk-UXVSQQID.js +3924 -0
- package/dist/chunk-W73XGHD4.js +3059 -0
- package/dist/{detection-OWEDBW7B.js → detection-JT7HSKSX.js} +2 -1
- package/dist/{guardrails-6IG2C4KJ.js → guardrails-IX3VVUO5.js} +1 -1
- package/dist/index.js +1474 -7340
- package/dist/{kanban-PSOTFEQI.js → kanban-2WMA5VPQ.js} +551 -116
- package/dist/loop-KNJIRK7T.js +21 -0
- package/dist/tui-bridge-DCC4JAPM.js +164 -0
- package/package.json +5 -4
|
@@ -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-
|
|
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
|
|
17
|
-
import { useEffect as useEffect4, useState as
|
|
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
|
-
|
|
30
|
-
|
|
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
|
|
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: "
|
|
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:
|
|
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
|
|
499
|
-
const maxLineWidth = Math.max(1, terminalCols -
|
|
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 =
|
|
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 =
|
|
507
|
+
elapsedDisplay = formatElapsed(card.finishedAt - card.startedAt);
|
|
519
508
|
}
|
|
520
509
|
const separator = useMemo(() => {
|
|
521
|
-
const separatorInner = Math.max(0, terminalCols -
|
|
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 -
|
|
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
|
|
591
|
-
import { jsx as
|
|
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__ */
|
|
607
|
-
|
|
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__ */
|
|
618
|
-
/* @__PURE__ */
|
|
619
|
-
/* @__PURE__ */
|
|
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__ */
|
|
622
|
-
/* @__PURE__ */
|
|
623
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
633
|
-
|
|
634
|
-
/* @__PURE__ */
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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__ */
|
|
641
|
-
/* @__PURE__ */
|
|
642
|
-
/* @__PURE__ */
|
|
643
|
-
/* @__PURE__ */
|
|
644
|
-
/* @__PURE__ */
|
|
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__ */
|
|
651
|
-
/* @__PURE__ */
|
|
652
|
-
models.map((m, i) => /* @__PURE__ */
|
|
653
|
-
/* @__PURE__ */
|
|
654
|
-
/* @__PURE__ */
|
|
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__ */
|
|
661
|
-
/* @__PURE__ */
|
|
662
|
-
/* @__PURE__ */
|
|
663
|
-
/* @__PURE__ */
|
|
664
|
-
/* @__PURE__ */
|
|
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__ */
|
|
668
|
-
/* @__PURE__ */
|
|
669
|
-
/* @__PURE__ */
|
|
670
|
-
/* @__PURE__ */
|
|
671
|
-
/* @__PURE__ */
|
|
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__ */
|
|
675
|
-
updateInfo && /* @__PURE__ */
|
|
676
|
-
/* @__PURE__ */
|
|
677
|
-
/* @__PURE__ */
|
|
678
|
-
/* @__PURE__ */
|
|
679
|
-
/* @__PURE__ */
|
|
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__ */
|
|
917
|
+
/* @__PURE__ */ jsx8(Text8, { color: "green", children: updateInfo.latestVersion })
|
|
683
918
|
] }),
|
|
684
|
-
/* @__PURE__ */
|
|
919
|
+
/* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "npm i -g @tarcisiopgs/lisa" })
|
|
685
920
|
] })
|
|
686
921
|
] }),
|
|
687
|
-
/* @__PURE__ */
|
|
688
|
-
activeView === "board"
|
|
689
|
-
/* @__PURE__ */
|
|
690
|
-
/* @__PURE__ */
|
|
691
|
-
/* @__PURE__ */
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
/* @__PURE__ */
|
|
696
|
-
] })
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
/* @__PURE__ */
|
|
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
|
|
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] =
|
|
716
|
-
const [activeColIndex, setActiveColIndex] =
|
|
717
|
-
const [activeCardIndex, setActiveCardIndex] =
|
|
718
|
-
const [paused, setPaused] =
|
|
719
|
-
const [selectedCardId, setSelectedCardId] =
|
|
720
|
-
const [updateInfo] =
|
|
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
|
-
|
|
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 (
|
|
792
|
-
|
|
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
|
-
|
|
868
|
-
|
|
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 === "
|
|
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__ */
|
|
1331
|
+
) : /* @__PURE__ */ jsx9(
|
|
897
1332
|
IssueDetail,
|
|
898
1333
|
{
|
|
899
1334
|
card: selectedCard,
|