wave-code 0.17.1 → 0.17.2

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.
Files changed (57) hide show
  1. package/dist/acp/agent.js +1 -1
  2. package/dist/components/ChatInterface.d.ts.map +1 -1
  3. package/dist/components/ChatInterface.js +5 -2
  4. package/dist/components/InputBox.d.ts +2 -0
  5. package/dist/components/InputBox.d.ts.map +1 -1
  6. package/dist/components/InputBox.js +29 -8
  7. package/dist/components/LoadingIndicator.d.ts +2 -1
  8. package/dist/components/LoadingIndicator.d.ts.map +1 -1
  9. package/dist/components/LoadingIndicator.js +2 -2
  10. package/dist/components/QueuedMessageList.d.ts.map +1 -1
  11. package/dist/components/QueuedMessageList.js +10 -10
  12. package/dist/components/StatusLine.d.ts +2 -0
  13. package/dist/components/StatusLine.d.ts.map +1 -1
  14. package/dist/components/StatusLine.js +6 -6
  15. package/dist/components/TaskList.d.ts +3 -0
  16. package/dist/components/TaskList.d.ts.map +1 -1
  17. package/dist/components/TaskList.js +171 -28
  18. package/dist/components/TaskNotificationMessage.d.ts.map +1 -1
  19. package/dist/components/TaskNotificationMessage.js +1 -0
  20. package/dist/components/WorkflowManager.d.ts +6 -0
  21. package/dist/components/WorkflowManager.d.ts.map +1 -0
  22. package/dist/components/WorkflowManager.js +102 -0
  23. package/dist/constants/commands.d.ts.map +1 -1
  24. package/dist/constants/commands.js +6 -0
  25. package/dist/contexts/useChat.d.ts +8 -1
  26. package/dist/contexts/useChat.d.ts.map +1 -1
  27. package/dist/contexts/useChat.js +68 -4
  28. package/dist/hooks/useInputManager.d.ts +2 -0
  29. package/dist/hooks/useInputManager.d.ts.map +1 -1
  30. package/dist/hooks/useInputManager.js +22 -13
  31. package/dist/managers/inputHandlers.d.ts.map +1 -1
  32. package/dist/managers/inputHandlers.js +6 -3
  33. package/dist/managers/inputReducer.d.ts +10 -0
  34. package/dist/managers/inputReducer.d.ts.map +1 -1
  35. package/dist/managers/inputReducer.js +41 -4
  36. package/dist/print-cli.d.ts.map +1 -1
  37. package/dist/print-cli.js +3 -31
  38. package/dist/reducers/workflowManagerReducer.d.ts +41 -0
  39. package/dist/reducers/workflowManagerReducer.d.ts.map +1 -0
  40. package/dist/reducers/workflowManagerReducer.js +100 -0
  41. package/package.json +2 -2
  42. package/src/acp/agent.ts +1 -1
  43. package/src/components/ChatInterface.tsx +10 -1
  44. package/src/components/InputBox.tsx +43 -3
  45. package/src/components/LoadingIndicator.tsx +4 -1
  46. package/src/components/QueuedMessageList.tsx +7 -3
  47. package/src/components/StatusLine.tsx +28 -16
  48. package/src/components/TaskList.tsx +213 -19
  49. package/src/components/TaskNotificationMessage.tsx +1 -0
  50. package/src/components/WorkflowManager.tsx +320 -0
  51. package/src/constants/commands.ts +6 -0
  52. package/src/contexts/useChat.tsx +83 -4
  53. package/src/hooks/useInputManager.ts +25 -13
  54. package/src/managers/inputHandlers.ts +5 -4
  55. package/src/managers/inputReducer.ts +54 -6
  56. package/src/print-cli.ts +3 -37
  57. package/src/reducers/workflowManagerReducer.ts +143 -0
package/dist/acp/agent.js CHANGED
@@ -40,7 +40,7 @@ export class WaveAcpAgent {
40
40
  }
41
41
  getSessionConfigOptions(agent) {
42
42
  const configuredModels = agent.getConfiguredModels();
43
- const currentModel = agent.getModelConfig().model;
43
+ const currentModel = agent.getModelConfig().model || "";
44
44
  return [
45
45
  {
46
46
  id: "permission_mode",
@@ -1 +1 @@
1
- {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAkIjC,CAAC"}
1
+ {"version":3,"file":"ChatInterface.d.ts","sourceRoot":"","sources":["../../src/components/ChatInterface.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAsC,MAAM,OAAO,CAAC;AAa3D,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EA2IjC,CAAC"}
@@ -10,7 +10,7 @@ import { ConfirmationDetails } from "./ConfirmationDetails.js";
10
10
  import { ConfirmationSelector } from "./ConfirmationSelector.js";
11
11
  import { useChat } from "../contexts/useChat.js";
12
12
  export const ChatInterface = () => {
13
- const { messages, isLoading, isCommandRunning, isCompacting, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, maxInputTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, } = useChat();
13
+ const { messages, isLoading, isCommandRunning, isCompacting, sendMessage, abortMessage, mcpServers, connectMcpServer, disconnectMcpServer, isExpanded, sessionId, latestTotalTokens, maxInputTokens, slashCommands, hasSlashCommand, isConfirmationVisible, hasPendingConfirmations, confirmingTool, handleConfirmationDecision, handleConfirmationCancel, version, workdir, remountKey, requestRemount, isGoalActive, goalElapsed, isGoalEvaluating, } = useChat();
14
14
  const displayMessages = messages;
15
15
  const [forceStatic, setForceStatic] = useState(false);
16
16
  const { stdout } = useStdout();
@@ -37,5 +37,8 @@ export const ChatInterface = () => {
37
37
  ]);
38
38
  if (!sessionId)
39
39
  return null;
40
- return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading || isCommandRunning || isCompacting) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompacting: isCompacting, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }, "confirmation-details")) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel })] }))] }));
40
+ return (_jsxs(Box, { ref: chatInterfaceRef, flexDirection: "column", children: [_jsx(MessageList, { messages: displayMessages, isExpanded: isExpanded, version: version, workdir: workdir, forceStatic: forceStatic }, remountKey), !isConfirmationVisible && !isExpanded && (_jsxs(_Fragment, { children: [(isLoading ||
41
+ isCommandRunning ||
42
+ isCompacting ||
43
+ isGoalEvaluating) && (_jsx(LoadingIndicator, { isLoading: isLoading, isCommandRunning: isCommandRunning, isCompacting: isCompacting, isGoalEvaluating: isGoalEvaluating, latestTotalTokens: latestTotalTokens })), _jsx(TaskList, {}), _jsx(QueuedMessageList, {}), _jsx(InputBox, { isLoading: isLoading, isCommandRunning: isCommandRunning, sendMessage: sendMessage, abortMessage: abortMessage, mcpServers: mcpServers, connectMcpServer: connectMcpServer, disconnectMcpServer: disconnectMcpServer, slashCommands: slashCommands, hasSlashCommand: hasSlashCommand, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens, isGoalActive: isGoalActive, goalElapsed: goalElapsed })] })), isConfirmationVisible && (_jsxs(_Fragment, { children: [forceStatic ? (_jsx(Static, { items: [{ key: "confirmation-details" }], children: () => (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded }, "confirmation-details")) })) : (_jsx(ConfirmationDetails, { toolName: confirmingTool.name, toolInput: confirmingTool.input, planContent: confirmingTool.planContent, isExpanded: isExpanded })), _jsx(ConfirmationSelector, { toolName: confirmingTool.name, toolInput: confirmingTool.input, suggestedPrefix: confirmingTool.suggestedPrefix, hidePersistentOption: confirmingTool.hidePersistentOption, isExpanded: isExpanded, onDecision: handleConfirmationDecision, onCancel: handleConfirmationCancel })] }))] }));
41
44
  };
@@ -18,6 +18,8 @@ export interface InputBoxProps {
18
18
  hasSlashCommand?: (commandId: string) => boolean;
19
19
  latestTotalTokens?: number;
20
20
  maxInputTokens?: number;
21
+ isGoalActive?: boolean;
22
+ goalElapsed?: string;
21
23
  }
22
24
  export declare const InputBox: React.FC<InputBoxProps>;
23
25
  //# sourceMappingURL=InputBox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AAmBzC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAgS5C,CAAC"}
1
+ {"version":3,"file":"InputBox.d.ts","sourceRoot":"","sources":["../../src/components/InputBox.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyC,MAAM,OAAO,CAAC;AAoB9D,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEpE,eAAO,MAAM,sBAAsB,mDACe,CAAC;AAEnD,eAAO,MAAM,6BAA6B,QAGzC,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,CACZ,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EAClD,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KACjC,IAAI,CAAC;IACV,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAE1B,UAAU,CAAC,EAAE,eAAe,EAAE,CAAC;IAC/B,gBAAgB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5D,mBAAmB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAE/D,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAEjD,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAAE,CAAC,aAAa,CAoU5C,CAAC"}
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useEffect } from "react";
2
+ import { useEffect, useRef, useCallback } from "react";
3
3
  import { Box, Text } from "ink";
4
4
  import { useInput } from "ink";
5
5
  import { FileSelector } from "./FileSelector.js";
@@ -13,14 +13,25 @@ import { StatusCommand } from "./StatusCommand.js";
13
13
  import { LoginCommand } from "./LoginCommand.js";
14
14
  import { PluginManagerShell } from "./PluginManagerShell.js";
15
15
  import { ModelSelector } from "./ModelSelector.js";
16
+ import { WorkflowManager } from "./WorkflowManager.js";
16
17
  import { StatusLine } from "./StatusLine.js";
17
18
  import { BtwDisplay } from "./BtwDisplay.js";
18
19
  import { useInputManager } from "../hooks/useInputManager.js";
19
20
  import { useChat } from "../contexts/useChat.js";
20
21
  export const INPUT_PLACEHOLDER_TEXT = "Type your message (use /help for more info)...";
21
22
  export const INPUT_PLACEHOLDER_TEXT_PREFIX = INPUT_PLACEHOLDER_TEXT.substring(0, 10);
22
- export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
23
- const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, } = useChat();
23
+ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mcpServers = [], connectMcpServer = async () => false, disconnectMcpServer = async () => false, slashCommands = [], hasSlashCommand = () => false, latestTotalTokens = 0, maxInputTokens = 128000, isGoalActive, goalElapsed, }) => {
24
+ const { permissionMode: chatPermissionMode, setPermissionMode: setChatPermissionMode, handleRewindSelect, backgroundCurrentTask, messages, getFullMessageThread, sessionId, workingDirectory, askBtw, currentModel, configuredModels, setModel, recreateAgent, recallQueuedMessage, queuedMessages, } = useChat();
25
+ // Ref to hold setInputText so queue callbacks can access it before useInputManager returns
26
+ const setInputTextRef = useRef(() => { });
27
+ const hasQueuedMessages = (queuedMessages?.length ?? 0) > 0;
28
+ const onRecallQueuedMessage = useCallback(() => {
29
+ const msg = recallQueuedMessage();
30
+ if (msg) {
31
+ const prefix = msg.type === "bang" ? "!" : "";
32
+ setInputTextRef.current(prefix + msg.content);
33
+ }
34
+ }, [recallQueuedMessage]);
24
35
  // Input manager with all input state and functionality (including images)
25
36
  const { inputText, cursorPosition,
26
37
  // Image management
@@ -32,7 +43,7 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
32
43
  // History search
33
44
  showHistorySearch, historySearchQuery,
34
45
  // Task/MCP Manager
35
- showBackgroundTaskManager, showMcpManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, setShowBackgroundTaskManager, setShowMcpManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector,
46
+ showBackgroundTaskManager, showMcpManager, showRewindManager, showHelp, showStatusCommand, showLoginCommand, showPluginManager, showModelSelector, showWorkflowManager, setShowBackgroundTaskManager, setShowMcpManager, setShowRewindManager, setShowHelp, setShowStatusCommand, setShowLoginCommand, setShowPluginManager, setShowModelSelector, setShowWorkflowManager,
36
47
  // Permission mode
37
48
  permissionMode, setPermissionMode,
38
49
  // BTW state
@@ -40,7 +51,9 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
40
51
  // Main handler
41
52
  handleInput,
42
53
  // Manager ready state
43
- isManagerReady, } = useInputManager({
54
+ isManagerReady,
55
+ // Direct state setters
56
+ setInputText, } = useInputManager({
44
57
  onSendMessage: sendMessage,
45
58
  onAskBtw: askBtw,
46
59
  onHasSlashCommand: hasSlashCommand,
@@ -50,7 +63,13 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
50
63
  sessionId,
51
64
  workdir: workingDirectory,
52
65
  getFullMessageThread,
66
+ hasQueuedMessages,
67
+ onRecallQueuedMessage,
53
68
  });
69
+ // Keep setInputText ref updated for queue callbacks
70
+ useEffect(() => {
71
+ setInputTextRef.current = setInputText;
72
+ }, [setInputText]);
54
73
  // Sync permission mode from useChat to InputManager
55
74
  useEffect(() => {
56
75
  setPermissionMode(chatPermissionMode);
@@ -67,7 +86,8 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
67
86
  showPluginManager ||
68
87
  showModelSelector ||
69
88
  showBackgroundTaskManager ||
70
- showMcpManager) {
89
+ showMcpManager ||
90
+ showWorkflowManager) {
71
91
  return;
72
92
  }
73
93
  await handleInput(input, key, attachedImages, clearImages);
@@ -116,11 +136,12 @@ export const InputBox = ({ sendMessage = () => { }, abortMessage = () => { }, mc
116
136
  if (showModelSelector) {
117
137
  return (_jsx(ModelSelector, { onCancel: () => setShowModelSelector(false), currentModel: currentModel, configuredModels: configuredModels, onSelectModel: setModel }));
118
138
  }
119
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showBackgroundTaskManager ||
139
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(BtwDisplay, { btwState: btwState }), showFileSelector && (_jsx(FileSelector, { files: filteredFiles, searchQuery: searchQuery, isLoading: isFileSearching, onSelect: handleFileSelect, onCancel: handleCancelFileSelect })), showCommandSelector && (_jsx(CommandSelector, { searchQuery: commandSearchQuery, onSelect: handleCommandSelect, onInsert: handleCommandInsert, onCancel: handleCancelCommandSelect, commands: slashCommands })), showHistorySearch && (_jsx(HistorySearch, { searchQuery: historySearchQuery, onSelect: handleHistorySearchSelect, onCancel: handleCancelHistorySearch })), showBackgroundTaskManager && (_jsx(BackgroundTaskManager, { onCancel: () => setShowBackgroundTaskManager(false) })), showMcpManager && (_jsx(McpManager, { onCancel: () => setShowMcpManager(false), servers: mcpServers, onConnectServer: connectMcpServer, onDisconnectServer: disconnectMcpServer })), showWorkflowManager && (_jsx(WorkflowManager, { onCancel: () => setShowWorkflowManager(false) })), showBackgroundTaskManager ||
120
140
  showMcpManager ||
121
141
  showRewindManager ||
122
142
  showHelp ||
123
143
  showStatusCommand ||
124
144
  showLoginCommand ||
125
- showPluginManager || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: btwState.isActive ? "cyan" : "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: btwState.isActive && isPlaceholder ? (_jsxs(_Fragment, { children: [_jsx(Text, { backgroundColor: "white", color: "black", children: " " }), _jsx(Text, { color: "cyan", children: "Type your side question..." })] })) : shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand, isBtwActive: btwState.isActive, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] }))] }));
145
+ showPluginManager ||
146
+ showWorkflowManager || (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { borderStyle: "single", borderColor: btwState.isActive ? "cyan" : "gray", borderLeft: false, borderRight: false, children: _jsx(Text, { color: isPlaceholder ? "gray" : "white", children: btwState.isActive && isPlaceholder ? (_jsxs(_Fragment, { children: [_jsx(Text, { backgroundColor: "white", color: "black", children: " " }), _jsx(Text, { color: "cyan", children: "Type your side question..." })] })) : shouldShowCursor ? (_jsxs(_Fragment, { children: [beforeCursor, _jsx(Text, { backgroundColor: "white", color: "black", children: atCursor }), afterCursor] })) : (displayText) }) }), _jsx(StatusLine, { permissionMode: permissionMode, isShellCommand: isShellCommand, isBtwActive: btwState.isActive, isGoalActive: isGoalActive, goalElapsed: goalElapsed, latestTotalTokens: latestTotalTokens, maxInputTokens: maxInputTokens })] }))] }));
126
147
  };
@@ -2,10 +2,11 @@ export interface LoadingIndicatorProps {
2
2
  isLoading?: boolean;
3
3
  isCommandRunning?: boolean;
4
4
  isCompacting?: boolean;
5
+ isGoalEvaluating?: boolean;
5
6
  latestTotalTokens?: number;
6
7
  }
7
8
  export declare const LoadingIndicator: {
8
- ({ isLoading, isCommandRunning, isCompacting, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
9
+ ({ isLoading, isCommandRunning, isCompacting, isGoalEvaluating, latestTotalTokens, }: LoadingIndicatorProps): import("react/jsx-runtime").JSX.Element;
9
10
  displayName: string;
10
11
  };
11
12
  //# sourceMappingURL=LoadingIndicator.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;wEAK1B,qBAAqB;;CAsCvB,CAAC"}
1
+ {"version":3,"file":"LoadingIndicator.d.ts","sourceRoot":"","sources":["../../src/components/LoadingIndicator.tsx"],"names":[],"mappings":"AAGA,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,gBAAgB;0FAM1B,qBAAqB;;CAuCvB,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
- export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompacting = false, latestTotalTokens = 0, }) => {
4
- return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompacting && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompacting && (_jsx(Text, { color: "magenta", children: "\u273B Compacting message history..." }))] }));
3
+ export const LoadingIndicator = ({ isLoading = false, isCommandRunning = false, isCompacting = false, isGoalEvaluating = false, latestTotalTokens = 0, }) => {
4
+ return (_jsxs(Box, { flexDirection: "column", children: [isLoading && !isCompacting && !isGoalEvaluating && (_jsxs(Box, { children: [_jsx(Text, { color: "yellow", children: "\u273B AI is thinking... " }), latestTotalTokens > 0 && (_jsxs(_Fragment, { children: [_jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "blue", bold: true, children: latestTotalTokens.toLocaleString() }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "tokens", " "] })] })), _jsxs(Text, { color: "gray", dimColor: true, children: ["|", " "] }), _jsx(Text, { color: "red", bold: true, children: "Esc" }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "to abort"] })] })), isCommandRunning && _jsx(Text, { color: "blue", children: "\u273B Command is running..." }), isCompacting && (_jsx(Text, { color: "magenta", children: "\u273B Compacting message history..." })), isGoalEvaluating && _jsx(Text, { color: "cyan", children: "\u273B Evaluating goal..." })] }));
5
5
  };
6
6
  LoadingIndicator.displayName = "LoadingIndicator";
@@ -1 +1 @@
1
- {"version":3,"file":"QueuedMessageList.d.ts","sourceRoot":"","sources":["../../src/components/QueuedMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EA2BrC,CAAC"}
1
+ {"version":3,"file":"QueuedMessageList.d.ts","sourceRoot":"","sources":["../../src/components/QueuedMessageList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,eAAO,MAAM,iBAAiB,EAAE,KAAK,CAAC,EA+BrC,CAAC"}
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useChat } from "../contexts/useChat.js";
3
3
  import { Box, Text } from "ink";
4
4
  export const QueuedMessageList = () => {
@@ -6,13 +6,13 @@ export const QueuedMessageList = () => {
6
6
  if (queuedMessages.length === 0) {
7
7
  return null;
8
8
  }
9
- return (_jsx(Box, { flexDirection: "column", children: queuedMessages.map((msg, index) => {
10
- const content = msg.content.trim();
11
- const hasImages = msg.images && msg.images.length > 0;
12
- const prefix = msg.type === "bang" ? "! " : "";
13
- const displayText = prefix + (content || (hasImages ? "[Images]" : ""));
14
- return (_jsx(Box, { children: _jsx(Text, { color: "gray", italic: true, children: displayText.length > 60
15
- ? `${displayText.substring(0, 57)}...`
16
- : displayText }) }, index));
17
- }) }));
9
+ return (_jsxs(Box, { flexDirection: "column", children: [queuedMessages.map((msg, index) => {
10
+ const content = msg.content.trim();
11
+ const hasImages = msg.images && msg.images.length > 0;
12
+ const prefix = msg.type === "bang" ? "! " : "";
13
+ const displayText = prefix + (content || (hasImages ? "[Images]" : ""));
14
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ["[", index + 1, "] "] }), _jsx(Text, { color: "gray", italic: true, children: displayText.length > 55
15
+ ? `${displayText.substring(0, 52)}...`
16
+ : displayText })] }, msg.id ?? index));
17
+ }), _jsxs(Text, { color: "gray", dimColor: true, children: [" ", "\u2191 recall"] })] }));
18
18
  };
@@ -3,6 +3,8 @@ export interface StatusLineProps {
3
3
  permissionMode: string;
4
4
  isShellCommand: boolean;
5
5
  isBtwActive: boolean;
6
+ isGoalActive?: boolean;
7
+ goalElapsed?: string;
6
8
  latestTotalTokens?: number;
7
9
  maxInputTokens?: number;
8
10
  }
@@ -1 +1 @@
1
- {"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../src/components/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CAgDhD,CAAC"}
1
+ {"version":3,"file":"StatusLine.d.ts","sourceRoot":"","sources":["../../src/components/StatusLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,OAAO,CAAC;IACxB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC,eAAe,CA0DhD,CAAC"}
@@ -1,13 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from "ink";
3
- export const StatusLine = ({ permissionMode, isShellCommand, isBtwActive, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
3
+ export const StatusLine = ({ permissionMode, isShellCommand, isBtwActive, isGoalActive, goalElapsed, latestTotalTokens = 0, maxInputTokens = 128000, }) => {
4
4
  const percentage = latestTotalTokens > 0
5
5
  ? Math.min(Math.round((latestTotalTokens / maxInputTokens) * 100), 100)
6
6
  : 0;
7
7
  const contextColor = percentage > 95 ? "red" : percentage > 80 ? "yellow" : "gray";
8
- return (_jsxs(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: [isBtwActive ? (_jsxs(Text, { color: "gray", children: ["Mode: ", _jsx(Text, { color: "cyan", children: "BTW" }), " (ESC to dismiss)"] })) : isShellCommand ? (_jsxs(Text, { color: "gray", children: ["Shell: ", _jsx(Text, { color: "yellow", children: "Run shell command" })] })) : (_jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan"
9
- ? "yellow"
10
- : permissionMode === "bypassPermissions"
11
- ? "red"
12
- : "cyan", bold: permissionMode === "bypassPermissions", children: permissionMode }), " ", "(Shift+Tab to cycle)"] })), percentage > 0 && (_jsxs(Text, { color: contextColor, children: [percentage, "% context"] }))] }));
8
+ return (_jsxs(Box, { paddingRight: 1, justifyContent: "space-between", width: "100%", children: [isBtwActive ? (_jsxs(Text, { color: "gray", children: ["Mode: ", _jsx(Text, { color: "cyan", children: "BTW" }), " (ESC to dismiss)"] })) : isShellCommand ? (_jsxs(Text, { color: "gray", children: ["Shell: ", _jsx(Text, { color: "yellow", children: "Run shell command" })] })) : (_jsxs(Box, { children: [isGoalActive && (_jsxs(Text, { color: "gray", children: [_jsx(Text, { color: "cyan", children: "\u25CE /goal" }), " active", " ", goalElapsed && _jsxs(Text, { children: ["(", goalElapsed, ")"] }), " |", " "] })), _jsxs(Text, { color: "gray", children: ["Mode:", " ", _jsx(Text, { color: permissionMode === "plan"
9
+ ? "yellow"
10
+ : permissionMode === "bypassPermissions"
11
+ ? "red"
12
+ : "cyan", bold: permissionMode === "bypassPermissions", children: permissionMode }), " ", "(Shift+Tab to cycle)"] })] })), percentage > 0 && (_jsxs(Text, { color: contextColor, children: [percentage, "% context"] }))] }));
13
13
  };
@@ -1,3 +1,6 @@
1
1
  import React from "react";
2
+ import type { Task, TaskStatus } from "wave-agent-sdk";
3
+ export declare const getStatusIcon: (status: TaskStatus) => React.ReactNode;
4
+ export declare function sortTasksByPriority(tasks: Task[], completionTimestamps: Map<string, number>, now: number, needsTruncation: boolean): Task[];
2
5
  export declare const TaskList: React.FC;
3
6
  //# sourceMappingURL=TaskList.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../src/components/TaskList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAK1B,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EAiD5B,CAAC"}
1
+ {"version":3,"file":"TaskList.d.ts","sourceRoot":"","sources":["../../src/components/TaskList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAKvD,eAAO,MAAM,aAAa,GAAI,QAAQ,UAAU,KAAG,KAAK,CAAC,SAaxD,CAAC;AAWF,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,IAAI,EAAE,EACb,oBAAoB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACzC,GAAG,EAAE,MAAM,EACX,eAAe,EAAE,OAAO,GACvB,IAAI,EAAE,CA8CR;AAOD,eAAO,MAAM,QAAQ,EAAE,KAAK,CAAC,EA6J5B,CAAC"}
@@ -1,37 +1,180 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import React from "react";
2
3
  import { useChat } from "../contexts/useChat.js";
3
- import { Box, Text } from "ink";
4
+ import { Box, Text, useStdout } from "ink";
4
5
  import { useTasks } from "../hooks/useTasks.js";
6
+ const RECENTLY_COMPLETED_TTL_MS = 30000;
7
+ const AUTO_HIDE_DELAY_MS = 5000;
8
+ export const getStatusIcon = (status) => {
9
+ switch (status) {
10
+ case "pending":
11
+ return _jsx(Text, { color: "gray", children: "\u25A1" });
12
+ case "in_progress":
13
+ return _jsx(Text, { color: "yellow", children: "\u25A0" });
14
+ case "completed":
15
+ return _jsx(Text, { color: "green", children: "\u2713" });
16
+ case "deleted":
17
+ return _jsx(Text, { color: "red", children: "\u2715" });
18
+ default:
19
+ return _jsx(Text, { color: "gray", children: "?" });
20
+ }
21
+ };
22
+ function byIdAsc(a, b) {
23
+ const aNum = parseInt(a.id, 10);
24
+ const bNum = parseInt(b.id, 10);
25
+ if (!isNaN(aNum) && !isNaN(bNum)) {
26
+ return aNum - bNum;
27
+ }
28
+ return a.id.localeCompare(b.id);
29
+ }
30
+ export function sortTasksByPriority(tasks, completionTimestamps, now, needsTruncation) {
31
+ if (!needsTruncation) {
32
+ // No truncation needed — sort by ID for stable ordering
33
+ return [...tasks].sort(byIdAsc);
34
+ }
35
+ // When truncation is needed, prioritize:
36
+ // recently completed > in_progress > pending (unblocked first) > older completed > deleted
37
+ const recentCompleted = [];
38
+ const olderCompleted = [];
39
+ for (const task of tasks.filter((t) => t.status === "completed")) {
40
+ const ts = completionTimestamps.get(task.id);
41
+ if (ts && now - ts < RECENTLY_COMPLETED_TTL_MS) {
42
+ recentCompleted.push(task);
43
+ }
44
+ else {
45
+ olderCompleted.push(task);
46
+ }
47
+ }
48
+ recentCompleted.sort(byIdAsc);
49
+ olderCompleted.sort(byIdAsc);
50
+ const inProgress = tasks
51
+ .filter((t) => t.status === "in_progress")
52
+ .sort(byIdAsc);
53
+ const unresolvedTaskIds = new Set(tasks.filter((t) => t.status !== "completed").map((t) => t.id));
54
+ const pending = tasks
55
+ .filter((t) => t.status === "pending")
56
+ .sort((a, b) => {
57
+ const aBlocked = a.blockedBy.some((id) => unresolvedTaskIds.has(id));
58
+ const bBlocked = b.blockedBy.some((id) => unresolvedTaskIds.has(id));
59
+ if (aBlocked !== bBlocked)
60
+ return aBlocked ? 1 : -1;
61
+ return byIdAsc(a, b);
62
+ });
63
+ const deleted = tasks.filter((t) => t.status === "deleted").sort(byIdAsc);
64
+ return [
65
+ ...recentCompleted,
66
+ ...inProgress,
67
+ ...pending,
68
+ ...olderCompleted,
69
+ ...deleted,
70
+ ];
71
+ }
72
+ function getDisplayLimit(rows) {
73
+ const available = rows ?? 24;
74
+ return available <= 10 ? 0 : Math.min(10, Math.max(3, available - 14));
75
+ }
5
76
  export const TaskList = () => {
6
77
  const tasks = useTasks();
7
78
  const { isTaskListVisible } = useChat();
8
- if (tasks.length === 0 || !isTaskListVisible) {
9
- return null;
79
+ const { stdout } = useStdout();
80
+ const completionTimestampsRef = React.useRef(new Map());
81
+ const previousCompletedIdsRef = React.useRef(null);
82
+ const autoHideTimerRef = React.useRef(null);
83
+ const [autoHidden, setAutoHidden] = React.useState(() => {
84
+ // If all tasks are already completed on mount (e.g. session restore),
85
+ // start hidden immediately instead of flashing for 5 seconds.
86
+ const active = tasks.filter((t) => t.status !== "deleted");
87
+ return active.length > 0 && active.every((t) => t.status === "completed");
88
+ });
89
+ const [, forceUpdate] = React.useState(0);
90
+ const now = Date.now();
91
+ const activeTasks = tasks.filter((t) => t.status !== "deleted");
92
+ // Track completion timestamps: only set when a task newly transitions to completed
93
+ if (previousCompletedIdsRef.current === null) {
94
+ previousCompletedIdsRef.current = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
10
95
  }
11
- const getStatusIcon = (status) => {
12
- switch (status) {
13
- case "pending":
14
- return _jsx(Text, { color: "gray", children: "\u25A1" });
15
- case "in_progress":
16
- return _jsx(Text, { color: "yellow", children: "\u25A0" });
17
- case "completed":
18
- return _jsx(Text, { color: "green", children: "\u2713" });
19
- case "deleted":
20
- return _jsx(Text, { color: "red", children: "\u2715" });
21
- default:
22
- return _jsx(Text, { color: "gray", children: "?" });
96
+ const currentCompletedIds = new Set(tasks.filter((t) => t.status === "completed").map((t) => t.id));
97
+ for (const id of currentCompletedIds) {
98
+ if (!previousCompletedIdsRef.current.has(id)) {
99
+ completionTimestampsRef.current.set(id, now);
23
100
  }
24
- };
25
- return (_jsx(Box, { flexDirection: "column", children: tasks.map((task) => {
26
- const isDimmed = task.status === "completed" || task.status === "deleted";
27
- const isBlocked = task.blockedBy && task.blockedBy.length > 0;
28
- const blockingTaskIds = isBlocked
29
- ? task.blockedBy.map((id) => `#${id}`)
30
- : [];
31
- const blockedByText = isBlocked && blockingTaskIds.length > 0
32
- ? ` (Blocked by: ${blockingTaskIds.join(", ")})`
33
- : "";
34
- const fullText = `${task.subject}${blockedByText}`;
35
- return (_jsxs(Box, { gap: 1, children: [getStatusIcon(task.status), _jsx(Text, { dimColor: isDimmed, children: fullText })] }, task.id));
36
- }) }));
101
+ }
102
+ for (const id of completionTimestampsRef.current.keys()) {
103
+ if (!currentCompletedIds.has(id)) {
104
+ completionTimestampsRef.current.delete(id);
105
+ }
106
+ }
107
+ previousCompletedIdsRef.current = currentCompletedIds;
108
+ // Schedule re-render when the next recent completion expires
109
+ React.useEffect(() => {
110
+ if (completionTimestampsRef.current.size === 0)
111
+ return;
112
+ const currentNow = Date.now();
113
+ let earliestExpiry = Infinity;
114
+ for (const ts of completionTimestampsRef.current.values()) {
115
+ const expiry = ts + RECENTLY_COMPLETED_TTL_MS;
116
+ if (expiry > currentNow && expiry < earliestExpiry) {
117
+ earliestExpiry = expiry;
118
+ }
119
+ }
120
+ if (earliestExpiry === Infinity)
121
+ return;
122
+ const timer = setTimeout(() => forceUpdate((n) => n + 1), earliestExpiry - currentNow);
123
+ return () => clearTimeout(timer);
124
+ }, [tasks]);
125
+ // Auto-hide logic: when all active tasks are completed
126
+ const allCompleted = activeTasks.length > 0 &&
127
+ activeTasks.every((t) => t.status === "completed");
128
+ React.useEffect(() => {
129
+ if (allCompleted && !autoHidden) {
130
+ autoHideTimerRef.current = setTimeout(() => {
131
+ setAutoHidden(true);
132
+ }, AUTO_HIDE_DELAY_MS);
133
+ }
134
+ if (!allCompleted && autoHidden) {
135
+ setAutoHidden(false);
136
+ }
137
+ return () => {
138
+ if (autoHideTimerRef.current) {
139
+ clearTimeout(autoHideTimerRef.current);
140
+ autoHideTimerRef.current = null;
141
+ }
142
+ };
143
+ }, [allCompleted, autoHidden]);
144
+ if (tasks.length === 0 || !isTaskListVisible || autoHidden) {
145
+ return null;
146
+ }
147
+ const displayLimit = getDisplayLimit(stdout?.rows);
148
+ const needsTruncation = tasks.length > displayLimit;
149
+ const sorted = sortTasksByPriority(tasks, completionTimestampsRef.current, now, needsTruncation);
150
+ const displayed = displayLimit > 0 ? sorted.slice(0, displayLimit) : [];
151
+ const hidden = displayLimit > 0 ? sorted.slice(displayLimit) : sorted;
152
+ const doneCount = tasks.filter((t) => t.status === "completed").length;
153
+ const inProgressCount = tasks.filter((t) => t.status === "in_progress").length;
154
+ const openCount = tasks.filter((t) => t.status === "pending").length;
155
+ // Summary parts for hidden tasks
156
+ const hiddenInProgress = hidden.filter((t) => t.status === "in_progress").length;
157
+ const hiddenPending = hidden.filter((t) => t.status === "pending").length;
158
+ const hiddenCompleted = hidden.filter((t) => t.status === "completed").length;
159
+ const summaryParts = [];
160
+ if (hiddenInProgress > 0)
161
+ summaryParts.push(`${hiddenInProgress} in progress`);
162
+ if (hiddenPending > 0)
163
+ summaryParts.push(`${hiddenPending} pending`);
164
+ if (hiddenCompleted > 0)
165
+ summaryParts.push(`${hiddenCompleted} completed`);
166
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [tasks.length, " tasks (", doneCount, " done, ", inProgressCount, " in progress,", " ", openCount, " open)"] }), displayed.map((task) => {
167
+ const isDimmed = task.status === "completed" || task.status === "deleted";
168
+ const unresolvedTaskIds = new Set(tasks.filter((t) => t.status !== "completed").map((t) => t.id));
169
+ const isBlocked = task.blockedBy &&
170
+ task.blockedBy.some((id) => unresolvedTaskIds.has(id));
171
+ const blockingTaskIds = isBlocked
172
+ ? task.blockedBy.map((id) => `#${id}`)
173
+ : [];
174
+ const blockedByText = isBlocked && blockingTaskIds.length > 0
175
+ ? ` (Blocked by: ${blockingTaskIds.join(", ")})`
176
+ : "";
177
+ const fullText = `${task.subject}${blockedByText}`;
178
+ return (_jsxs(Box, { gap: 1, children: [getStatusIcon(task.status), _jsx(Text, { dimColor: isDimmed, wrap: "truncate-end", children: fullText })] }, task.id));
179
+ }), displayLimit > 0 && hidden.length > 0 && (_jsxs(Text, { dimColor: true, children: [" \u2026 +", summaryParts.join(", ")] }))] }));
37
180
  };
@@ -1 +1 @@
1
- {"version":3,"file":"TaskNotificationMessage.d.ts","sourceRoot":"","sources":["../../src/components/TaskNotificationMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AAQD,eAAO,MAAM,uBAAuB,GAAI,YAErC,4BAA4B,4CAW9B,CAAC"}
1
+ {"version":3,"file":"TaskNotificationMessage.d.ts","sourceRoot":"","sources":["../../src/components/TaskNotificationMessage.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAC;AAE5D,MAAM,WAAW,4BAA4B;IAC3C,KAAK,EAAE,qBAAqB,CAAC;CAC9B;AASD,eAAO,MAAM,uBAAuB,GAAI,YAErC,4BAA4B,4CAW9B,CAAC"}
@@ -4,6 +4,7 @@ const statusColor = {
4
4
  completed: "green",
5
5
  failed: "red",
6
6
  killed: "yellow",
7
+ aborted: "gray",
7
8
  };
8
9
  export const TaskNotificationMessage = ({ block, }) => {
9
10
  const color = statusColor[block.status];
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ export interface WorkflowManagerProps {
3
+ onCancel: () => void;
4
+ }
5
+ export declare const WorkflowManager: React.FC<WorkflowManagerProps>;
6
+ //# sourceMappingURL=WorkflowManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WorkflowManager.d.ts","sourceRoot":"","sources":["../../src/components/WorkflowManager.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAgC,MAAM,OAAO,CAAC;AASrD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAmCD,eAAO,MAAM,eAAe,EAAE,KAAK,CAAC,EAAE,CAAC,oBAAoB,CAiR1D,CAAC"}