wave-code 0.5.0 → 0.6.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 (112) hide show
  1. package/dist/components/App.d.ts.map +1 -1
  2. package/dist/components/App.js +40 -2
  3. package/dist/components/BackgroundTaskManager.d.ts +6 -0
  4. package/dist/components/BackgroundTaskManager.d.ts.map +1 -0
  5. package/dist/components/{TaskManager.js → BackgroundTaskManager.js} +1 -1
  6. package/dist/components/ChatInterface.d.ts.map +1 -1
  7. package/dist/components/ChatInterface.js +40 -5
  8. package/dist/components/CommandOutputDisplay.d.ts.map +1 -1
  9. package/dist/components/CommandOutputDisplay.js +6 -17
  10. package/dist/components/CommandSelector.d.ts.map +1 -1
  11. package/dist/components/CommandSelector.js +16 -2
  12. package/dist/components/CompressDisplay.d.ts.map +1 -1
  13. package/dist/components/CompressDisplay.js +6 -10
  14. package/dist/components/ConfirmationDetails.d.ts +9 -0
  15. package/dist/components/ConfirmationDetails.d.ts.map +1 -0
  16. package/dist/components/ConfirmationDetails.js +53 -0
  17. package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
  18. package/dist/components/ConfirmationSelector.d.ts.map +1 -0
  19. package/dist/components/{Confirmation.js → ConfirmationSelector.js} +34 -96
  20. package/dist/components/DiffDisplay.d.ts.map +1 -1
  21. package/dist/components/DiffDisplay.js +48 -1
  22. package/dist/components/FileSelector.d.ts.map +1 -1
  23. package/dist/components/FileSelector.js +2 -2
  24. package/dist/components/HelpView.d.ts +6 -0
  25. package/dist/components/HelpView.d.ts.map +1 -0
  26. package/dist/components/HelpView.js +24 -0
  27. package/dist/components/HistorySearch.d.ts.map +1 -1
  28. package/dist/components/HistorySearch.js +12 -4
  29. package/dist/components/InputBox.d.ts +1 -3
  30. package/dist/components/InputBox.d.ts.map +1 -1
  31. package/dist/components/InputBox.js +14 -17
  32. package/dist/components/LoadingIndicator.d.ts +11 -0
  33. package/dist/components/LoadingIndicator.d.ts.map +1 -0
  34. package/dist/components/LoadingIndicator.js +6 -0
  35. package/dist/components/Markdown.d.ts.map +1 -1
  36. package/dist/components/Markdown.js +114 -121
  37. package/dist/components/MessageItem.d.ts +1 -1
  38. package/dist/components/MessageItem.d.ts.map +1 -1
  39. package/dist/components/MessageItem.js +3 -5
  40. package/dist/components/MessageList.d.ts +2 -3
  41. package/dist/components/MessageList.d.ts.map +1 -1
  42. package/dist/components/MessageList.js +29 -12
  43. package/dist/components/PlanDisplay.d.ts.map +1 -1
  44. package/dist/components/PlanDisplay.js +4 -12
  45. package/dist/components/RewindCommand.d.ts +4 -0
  46. package/dist/components/RewindCommand.d.ts.map +1 -1
  47. package/dist/components/RewindCommand.js +20 -3
  48. package/dist/components/TaskList.d.ts +3 -0
  49. package/dist/components/TaskList.d.ts.map +1 -0
  50. package/dist/components/TaskList.js +40 -0
  51. package/dist/components/ToolDisplay.d.ts +9 -0
  52. package/dist/components/ToolDisplay.d.ts.map +1 -0
  53. package/dist/components/ToolDisplay.js +44 -0
  54. package/dist/contexts/useChat.d.ts +11 -3
  55. package/dist/contexts/useChat.d.ts.map +1 -1
  56. package/dist/contexts/useChat.js +51 -32
  57. package/dist/hooks/useInputManager.d.ts +4 -15
  58. package/dist/hooks/useInputManager.d.ts.map +1 -1
  59. package/dist/hooks/useInputManager.js +20 -65
  60. package/dist/hooks/useTasks.d.ts +2 -0
  61. package/dist/hooks/useTasks.d.ts.map +1 -0
  62. package/dist/hooks/useTasks.js +5 -0
  63. package/dist/managers/InputManager.d.ts +8 -30
  64. package/dist/managers/InputManager.d.ts.map +1 -1
  65. package/dist/managers/InputManager.js +38 -144
  66. package/dist/print-cli.d.ts.map +1 -1
  67. package/dist/print-cli.js +11 -30
  68. package/package.json +5 -6
  69. package/src/components/App.tsx +51 -3
  70. package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
  71. package/src/components/ChatInterface.tsx +80 -23
  72. package/src/components/CommandOutputDisplay.tsx +16 -38
  73. package/src/components/CommandSelector.tsx +41 -17
  74. package/src/components/CompressDisplay.tsx +5 -22
  75. package/src/components/ConfirmationDetails.tsx +108 -0
  76. package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +74 -193
  77. package/src/components/DiffDisplay.tsx +71 -1
  78. package/src/components/FileSelector.tsx +0 -2
  79. package/src/components/HelpView.tsx +59 -0
  80. package/src/components/HistorySearch.tsx +45 -21
  81. package/src/components/InputBox.tsx +51 -63
  82. package/src/components/LoadingIndicator.tsx +56 -0
  83. package/src/components/Markdown.tsx +126 -323
  84. package/src/components/MessageItem.tsx +13 -24
  85. package/src/components/MessageList.tsx +48 -82
  86. package/src/components/PlanDisplay.tsx +4 -27
  87. package/src/components/RewindCommand.tsx +39 -2
  88. package/src/components/TaskList.tsx +58 -0
  89. package/src/components/{ToolResultDisplay.tsx → ToolDisplay.tsx} +8 -18
  90. package/src/contexts/useChat.tsx +73 -41
  91. package/src/hooks/useInputManager.ts +21 -83
  92. package/src/hooks/useTasks.ts +6 -0
  93. package/src/managers/InputManager.ts +43 -179
  94. package/src/print-cli.ts +17 -35
  95. package/dist/components/Confirmation.d.ts.map +0 -1
  96. package/dist/components/MemoryDisplay.d.ts +0 -8
  97. package/dist/components/MemoryDisplay.d.ts.map +0 -1
  98. package/dist/components/MemoryDisplay.js +0 -25
  99. package/dist/components/MemoryTypeSelector.d.ts +0 -8
  100. package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
  101. package/dist/components/MemoryTypeSelector.js +0 -38
  102. package/dist/components/SubagentBlock.d.ts +0 -8
  103. package/dist/components/SubagentBlock.d.ts.map +0 -1
  104. package/dist/components/SubagentBlock.js +0 -70
  105. package/dist/components/TaskManager.d.ts +0 -6
  106. package/dist/components/TaskManager.d.ts.map +0 -1
  107. package/dist/components/ToolResultDisplay.d.ts +0 -9
  108. package/dist/components/ToolResultDisplay.d.ts.map +0 -1
  109. package/dist/components/ToolResultDisplay.js +0 -54
  110. package/src/components/MemoryDisplay.tsx +0 -62
  111. package/src/components/MemoryTypeSelector.tsx +0 -98
  112. package/src/components/SubagentBlock.tsx +0 -143
@@ -1,15 +1,20 @@
1
1
  import React from "react";
2
2
  import { Box, Text, Static } from "ink";
3
3
  import type { Message } from "wave-agent-sdk";
4
+ import {
5
+ TASK_CREATE_TOOL_NAME,
6
+ TASK_GET_TOOL_NAME,
7
+ TASK_UPDATE_TOOL_NAME,
8
+ TASK_LIST_TOOL_NAME,
9
+ } from "wave-agent-sdk";
4
10
  import { MessageItem } from "./MessageItem.js";
5
11
 
6
12
  export interface MessageListProps {
7
13
  messages: Message[];
8
14
  isLoading?: boolean;
9
15
  isCommandRunning?: boolean;
10
- isCompressing?: boolean;
11
- latestTotalTokens?: number;
12
16
  isExpanded?: boolean;
17
+ forceStaticLastMessage?: boolean;
13
18
  }
14
19
 
15
20
  export const MessageList = React.memo(
@@ -17,15 +22,16 @@ export const MessageList = React.memo(
17
22
  messages,
18
23
  isLoading = false,
19
24
  isCommandRunning = false,
20
- isCompressing = false,
21
- latestTotalTokens = 0,
22
25
  isExpanded = false,
26
+ forceStaticLastMessage = false,
23
27
  }: MessageListProps) => {
24
28
  // Empty message state
25
29
  if (messages.length === 0) {
26
30
  return (
27
- <Box flexDirection="column" paddingY={1}>
28
- <Text color="gray">Welcome to WAVE Code Assistant!</Text>
31
+ <Box flexDirection="column" gap={1}>
32
+ <Box flexDirection="column" paddingY={1}>
33
+ <Text color="gray">Welcome to WAVE Code Assistant!</Text>
34
+ </Box>
29
35
  </Box>
30
36
  );
31
37
  }
@@ -34,15 +40,43 @@ export const MessageList = React.memo(
34
40
  const maxExpandedMessages = 20;
35
41
  const shouldLimitMessages =
36
42
  isExpanded && messages.length > maxExpandedMessages;
43
+
44
+ // Filter out task management tools and empty messages
45
+ const taskMgmtTools = [
46
+ TASK_CREATE_TOOL_NAME,
47
+ TASK_GET_TOOL_NAME,
48
+ TASK_UPDATE_TOOL_NAME,
49
+ TASK_LIST_TOOL_NAME,
50
+ ];
51
+ const filteredMessages = messages
52
+ .map((message) => ({
53
+ ...message,
54
+ blocks: message.blocks.filter(
55
+ (block) =>
56
+ !(
57
+ block.type === "tool" &&
58
+ typeof block.name === "string" &&
59
+ taskMgmtTools.includes(block.name)
60
+ ),
61
+ ),
62
+ }))
63
+ .filter((message) => message.blocks.length > 0);
64
+
37
65
  const displayMessages = shouldLimitMessages
38
- ? messages.slice(-maxExpandedMessages)
39
- : messages;
40
- const omittedCount = shouldLimitMessages
41
- ? messages.length - maxExpandedMessages
42
- : 0;
66
+ ? filteredMessages.slice(-maxExpandedMessages)
67
+ : filteredMessages;
43
68
 
44
69
  // Compute which messages to render statically vs dynamically
45
- const shouldRenderLastDynamic = isLoading || isCommandRunning;
70
+ const lastMessage = displayMessages[displayMessages.length - 1];
71
+ const hasNonEndTool = lastMessage?.blocks.some(
72
+ (block) => block.type === "tool" && block.stage !== "end",
73
+ );
74
+ const hasRunningCommand = lastMessage?.blocks.some(
75
+ (block) => block.type === "command_output" && block.isRunning,
76
+ );
77
+ const shouldRenderLastDynamic =
78
+ !forceStaticLastMessage &&
79
+ (isLoading || isCommandRunning || hasNonEndTool || hasRunningCommand);
46
80
  const staticMessages = shouldRenderLastDynamic
47
81
  ? displayMessages.slice(0, -1)
48
82
  : displayMessages;
@@ -52,17 +86,7 @@ export const MessageList = React.memo(
52
86
  : [];
53
87
 
54
88
  return (
55
- <Box flexDirection="column" gap={1}>
56
- {/* Show omitted message count when limiting */}
57
- {omittedCount > 0 && (
58
- <Box>
59
- <Text color="gray" dimColor>
60
- ... {omittedCount} earlier message{omittedCount !== 1 ? "s" : ""}{" "}
61
- omitted (showing latest {maxExpandedMessages})
62
- </Text>
63
- </Box>
64
- )}
65
-
89
+ <Box flexDirection="column" paddingBottom={1}>
66
90
  {/* Static messages */}
67
91
  <Static items={staticMessages}>
68
92
  {(message, key) => {
@@ -86,7 +110,7 @@ export const MessageList = React.memo(
86
110
  const previousMessage =
87
111
  messageIndex > 0 ? displayMessages[messageIndex - 1] : undefined;
88
112
  return (
89
- <Box key={`dynamic-${index}`} marginTop={-1}>
113
+ <Box key={`dynamic-${index}`}>
90
114
  <MessageItem
91
115
  message={message}
92
116
  shouldShowHeader={previousMessage?.role !== message.role}
@@ -95,64 +119,6 @@ export const MessageList = React.memo(
95
119
  </Box>
96
120
  );
97
121
  })}
98
-
99
- {(isLoading || isCommandRunning || isCompressing) && (
100
- <Box flexDirection="column" gap={1}>
101
- {isLoading && (
102
- <Box>
103
- <Text color="yellow">💭 AI is thinking... </Text>
104
- <Text color="gray" dimColor>
105
- |{" "}
106
- </Text>
107
- <Text color="red" bold>
108
- Esc
109
- </Text>
110
- <Text color="gray" dimColor>
111
- {" "}
112
- to abort
113
- </Text>
114
- </Box>
115
- )}
116
- {isCommandRunning && (
117
- <Text color="blue">🚀 Command is running...</Text>
118
- )}
119
- {isCompressing && (
120
- <Text color="magenta">🗜️ Compressing message history...</Text>
121
- )}
122
- </Box>
123
- )}
124
-
125
- {/* Bottom info and shortcut key hints */}
126
- {messages.length > 0 && (
127
- <Box>
128
- <Box justifyContent="space-between" width="100%">
129
- <Box>
130
- <Text color="gray">
131
- Messages {messages.length}
132
- {latestTotalTokens > 0 && (
133
- <>
134
- <Text color="gray" dimColor>
135
- {" "}
136
- |{" "}
137
- </Text>
138
- <Text color="blue" bold>
139
- {latestTotalTokens.toLocaleString()}
140
- </Text>
141
- <Text color="gray" dimColor>
142
- {" "}
143
- tokens
144
- </Text>
145
- </>
146
- )}
147
- </Text>
148
- </Box>
149
- <Text color="gray" dimColor>
150
- <Text color="cyan">Ctrl+O</Text> Toggle{" "}
151
- {isExpanded ? "Collapse" : "Expand"}
152
- </Text>
153
- </Box>
154
- </Box>
155
- )}
156
122
  </Box>
157
123
  );
158
124
  },
@@ -1,5 +1,5 @@
1
- import React, { useMemo } from "react";
2
- import { Box, Text, useStdout } from "ink";
1
+ import React from "react";
2
+ import { Box } from "ink";
3
3
  import { Markdown } from "./Markdown.js";
4
4
 
5
5
  interface PlanDisplayProps {
@@ -7,35 +7,12 @@ interface PlanDisplayProps {
7
7
  isExpanded?: boolean;
8
8
  }
9
9
 
10
- export const PlanDisplay: React.FC<PlanDisplayProps> = ({
11
- plan,
12
- isExpanded = false,
13
- }) => {
14
- const { stdout } = useStdout();
15
- const maxHeight = useMemo(() => {
16
- // Similar to DiffDisplay.tsx maxHeight calculation
17
- return Math.max(5, (stdout?.rows || 24) - 25);
18
- }, [stdout?.rows]);
19
-
20
- const lines = useMemo(() => plan.split("\n"), [plan]);
21
- const isOverflowing = !isExpanded && lines.length > maxHeight;
22
-
10
+ export const PlanDisplay: React.FC<PlanDisplayProps> = ({ plan }) => {
23
11
  return (
24
12
  <Box flexDirection="column" marginTop={1}>
25
- <Box
26
- flexDirection="column"
27
- height={isOverflowing ? maxHeight : undefined}
28
- overflow="hidden"
29
- >
13
+ <Box flexDirection="column">
30
14
  <Markdown>{plan}</Markdown>
31
15
  </Box>
32
- {isOverflowing && (
33
- <Box marginTop={1}>
34
- <Text color="yellow" dimColor>
35
- ... (plan truncated, {lines.length} lines total)
36
- </Text>
37
- </Box>
38
- )}
39
16
  </Box>
40
17
  );
41
18
  };
@@ -6,13 +6,30 @@ export interface RewindCommandProps {
6
6
  messages: Message[];
7
7
  onSelect: (index: number) => void;
8
8
  onCancel: () => void;
9
+ getFullMessageThread?: () => Promise<{
10
+ messages: Message[];
11
+ sessionIds: string[];
12
+ }>;
9
13
  }
10
14
 
11
15
  export const RewindCommand: React.FC<RewindCommandProps> = ({
12
- messages,
16
+ messages: initialMessages,
13
17
  onSelect,
14
18
  onCancel,
19
+ getFullMessageThread,
15
20
  }) => {
21
+ const [messages, setMessages] = useState<Message[]>(initialMessages);
22
+ const [isLoading, setIsLoading] = useState(!!getFullMessageThread);
23
+
24
+ React.useEffect(() => {
25
+ if (getFullMessageThread) {
26
+ getFullMessageThread().then(({ messages: fullMessages }) => {
27
+ setMessages(fullMessages);
28
+ setIsLoading(false);
29
+ });
30
+ }
31
+ }, [getFullMessageThread]);
32
+
16
33
  // Filter user messages as checkpoints
17
34
  const checkpoints = messages
18
35
  .map((msg, index) => ({ msg, index }))
@@ -20,6 +37,11 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
20
37
 
21
38
  const [selectedIndex, setSelectedIndex] = useState(checkpoints.length - 1);
22
39
 
40
+ // Update selectedIndex when checkpoints change (after loading full thread)
41
+ React.useEffect(() => {
42
+ setSelectedIndex(checkpoints.length - 1);
43
+ }, [checkpoints.length]);
44
+
23
45
  useInput((input, key) => {
24
46
  if (key.return) {
25
47
  if (checkpoints.length > 0 && selectedIndex >= 0) {
@@ -44,6 +66,21 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
44
66
  }
45
67
  });
46
68
 
69
+ if (isLoading) {
70
+ return (
71
+ <Box
72
+ flexDirection="column"
73
+ paddingX={1}
74
+ borderStyle="single"
75
+ borderColor="cyan"
76
+ borderLeft={false}
77
+ borderRight={false}
78
+ >
79
+ <Text color="cyan">Loading full message thread...</Text>
80
+ </Box>
81
+ );
82
+ }
83
+
47
84
  if (checkpoints.length === 0) {
48
85
  return (
49
86
  <Box
@@ -106,7 +143,7 @@ export const RewindCommand: React.FC<RewindCommandProps> = ({
106
143
  <Box>
107
144
  <Text color="red" dimColor>
108
145
  ⚠️ Warning: This will delete all subsequent messages and revert file
109
- changes.
146
+ and task list changes.
110
147
  </Text>
111
148
  </Box>
112
149
  </Box>
@@ -0,0 +1,58 @@
1
+ import React from "react";
2
+ import { useChat } from "../contexts/useChat.js";
3
+ import { Box, Text } from "ink";
4
+ import { useTasks } from "../hooks/useTasks.js";
5
+
6
+ export const TaskList: React.FC = () => {
7
+ const tasks = useTasks();
8
+ const { isTaskListVisible } = useChat();
9
+
10
+ if (tasks.length === 0 || !isTaskListVisible) {
11
+ return null;
12
+ }
13
+
14
+ const getStatusIcon = (status: string, isBlocked: boolean) => {
15
+ if (isBlocked) {
16
+ return <Text color="red">🔒</Text>;
17
+ }
18
+ switch (status) {
19
+ case "pending":
20
+ return <Text color="gray">□</Text>;
21
+ case "in_progress":
22
+ return <Text color="yellow">■</Text>;
23
+ case "completed":
24
+ return <Text color="green">✓</Text>;
25
+ case "deleted":
26
+ return <Text color="red">✕</Text>;
27
+ default:
28
+ return <Text color="gray">?</Text>;
29
+ }
30
+ };
31
+
32
+ return (
33
+ <Box flexDirection="column">
34
+ {tasks.map((task) => {
35
+ const isDimmed =
36
+ task.status === "completed" || task.status === "deleted";
37
+ const isBlocked = task.blockedBy && task.blockedBy.length > 0;
38
+ const blockingTaskIds = isBlocked
39
+ ? task.blockedBy.map((id) => `#${id}`)
40
+ : [];
41
+
42
+ const blockedByText =
43
+ isBlocked && blockingTaskIds.length > 0
44
+ ? ` (Blocked by: ${blockingTaskIds.join(", ")})`
45
+ : "";
46
+
47
+ const fullText = `${task.subject}${blockedByText}`;
48
+
49
+ return (
50
+ <Box key={task.id} gap={1}>
51
+ {getStatusIcon(task.status, isBlocked)}
52
+ <Text dimColor={isDimmed}>{fullText}</Text>
53
+ </Box>
54
+ );
55
+ })}
56
+ </Box>
57
+ );
58
+ };
@@ -3,12 +3,12 @@ import { Box, Text } from "ink";
3
3
  import type { ToolBlock } from "wave-agent-sdk";
4
4
  import { DiffDisplay } from "./DiffDisplay.js";
5
5
 
6
- interface ToolResultDisplayProps {
6
+ interface ToolDisplayProps {
7
7
  block: ToolBlock;
8
8
  isExpanded?: boolean;
9
9
  }
10
10
 
11
- export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
11
+ export const ToolDisplay: React.FC<ToolDisplayProps> = ({
12
12
  block,
13
13
  isExpanded = false,
14
14
  }) => {
@@ -25,13 +25,6 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
25
25
  return "gray"; // Unknown state or no state information
26
26
  };
27
27
 
28
- const getStatusText = () => {
29
- if (stage === "running") return "🔄";
30
- if (success) return "";
31
- if (error || success === false) return "❌";
32
- return ""; // Don't display text for unknown state
33
- };
34
-
35
28
  const hasImages = () => {
36
29
  return block.images && block.images.length > 0;
37
30
  };
@@ -44,9 +37,6 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
44
37
 
45
38
  const toolName = name ? String(name) : "Tool";
46
39
 
47
- const isBackgroundable =
48
- stage === "running" && (toolName === "Bash" || toolName === "Task");
49
-
50
40
  // Get shortResult, if not available show last 5 lines of result
51
41
  const getShortResult = () => {
52
42
  if (block.shortResult) {
@@ -68,18 +58,18 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
68
58
  const shortResult = getShortResult();
69
59
 
70
60
  return (
71
- <Box flexDirection="column" gap={1}>
61
+ <Box flexDirection="column">
72
62
  <Box>
73
- <Text color="magenta">🔧 </Text>
74
- <Text color="white">{toolName}</Text>
63
+ <Box flexShrink={0}>
64
+ <Text color={getStatusColor()}>● </Text>
65
+ <Text color="white">{toolName}</Text>
66
+ </Box>
75
67
  {/* Display compactParams in collapsed state */}
76
68
  {!isExpanded && compactParams && (
77
69
  <Text color="gray"> {compactParams}</Text>
78
70
  )}
79
- <Text color={getStatusColor()}> {getStatusText()}</Text>
80
71
  {/* Display image indicator */}
81
72
  {hasImages() && <Text color="blue"> {getImageIndicator()}</Text>}
82
- {isBackgroundable && <Text color="gray"> [Ctrl-B] Background</Text>}
83
73
  </Box>
84
74
 
85
75
  {/* Display shortResult in collapsed state */}
@@ -91,7 +81,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
91
81
  flexDirection="column"
92
82
  >
93
83
  {shortResult.split("\n").map((line, index) => (
94
- <Text key={index} color="white">
84
+ <Text key={index} color="gray">
95
85
  {line}
96
86
  </Text>
97
87
  ))}