wave-code 0.5.0 → 0.6.1

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 (95) hide show
  1. package/dist/components/App.d.ts.map +1 -1
  2. package/dist/components/App.js +38 -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 +39 -5
  8. package/dist/components/CommandSelector.d.ts.map +1 -1
  9. package/dist/components/CommandSelector.js +10 -2
  10. package/dist/components/CompressDisplay.d.ts.map +1 -1
  11. package/dist/components/CompressDisplay.js +6 -10
  12. package/dist/components/ConfirmationDetails.d.ts +9 -0
  13. package/dist/components/ConfirmationDetails.d.ts.map +1 -0
  14. package/dist/components/ConfirmationDetails.js +53 -0
  15. package/dist/components/{Confirmation.d.ts → ConfirmationSelector.d.ts} +3 -3
  16. package/dist/components/ConfirmationSelector.d.ts.map +1 -0
  17. package/dist/components/{Confirmation.js → ConfirmationSelector.js} +34 -96
  18. package/dist/components/DiffDisplay.d.ts.map +1 -1
  19. package/dist/components/DiffDisplay.js +44 -1
  20. package/dist/components/FileSelector.d.ts.map +1 -1
  21. package/dist/components/FileSelector.js +2 -2
  22. package/dist/components/HistorySearch.d.ts.map +1 -1
  23. package/dist/components/HistorySearch.js +12 -4
  24. package/dist/components/InputBox.d.ts +1 -3
  25. package/dist/components/InputBox.d.ts.map +1 -1
  26. package/dist/components/InputBox.js +7 -17
  27. package/dist/components/LoadingIndicator.d.ts +11 -0
  28. package/dist/components/LoadingIndicator.d.ts.map +1 -0
  29. package/dist/components/LoadingIndicator.js +6 -0
  30. package/dist/components/Markdown.d.ts.map +1 -1
  31. package/dist/components/Markdown.js +114 -121
  32. package/dist/components/MessageItem.d.ts.map +1 -1
  33. package/dist/components/MessageItem.js +1 -2
  34. package/dist/components/MessageList.d.ts +2 -3
  35. package/dist/components/MessageList.d.ts.map +1 -1
  36. package/dist/components/MessageList.js +7 -7
  37. package/dist/components/PlanDisplay.d.ts.map +1 -1
  38. package/dist/components/PlanDisplay.js +4 -12
  39. package/dist/components/RewindCommand.d.ts +4 -0
  40. package/dist/components/RewindCommand.d.ts.map +1 -1
  41. package/dist/components/RewindCommand.js +19 -2
  42. package/dist/components/SubagentBlock.d.ts.map +1 -1
  43. package/dist/components/SubagentBlock.js +9 -6
  44. package/dist/components/TaskList.d.ts +3 -0
  45. package/dist/components/TaskList.d.ts.map +1 -0
  46. package/dist/components/TaskList.js +49 -0
  47. package/dist/components/ToolResultDisplay.js +1 -1
  48. package/dist/contexts/useChat.d.ts +11 -3
  49. package/dist/contexts/useChat.d.ts.map +1 -1
  50. package/dist/contexts/useChat.js +36 -31
  51. package/dist/hooks/useInputManager.d.ts +2 -13
  52. package/dist/hooks/useInputManager.d.ts.map +1 -1
  53. package/dist/hooks/useInputManager.js +8 -57
  54. package/dist/hooks/useTasks.d.ts +2 -0
  55. package/dist/hooks/useTasks.d.ts.map +1 -0
  56. package/dist/hooks/useTasks.js +5 -0
  57. package/dist/managers/InputManager.d.ts +4 -28
  58. package/dist/managers/InputManager.d.ts.map +1 -1
  59. package/dist/managers/InputManager.js +22 -128
  60. package/package.json +5 -6
  61. package/src/components/App.tsx +50 -3
  62. package/src/components/{TaskManager.tsx → BackgroundTaskManager.tsx} +4 -2
  63. package/src/components/ChatInterface.tsx +79 -23
  64. package/src/components/CommandSelector.tsx +35 -17
  65. package/src/components/CompressDisplay.tsx +5 -22
  66. package/src/components/ConfirmationDetails.tsx +108 -0
  67. package/src/components/{Confirmation.tsx → ConfirmationSelector.tsx} +69 -184
  68. package/src/components/DiffDisplay.tsx +62 -1
  69. package/src/components/FileSelector.tsx +0 -2
  70. package/src/components/HistorySearch.tsx +45 -21
  71. package/src/components/InputBox.tsx +11 -33
  72. package/src/components/LoadingIndicator.tsx +56 -0
  73. package/src/components/Markdown.tsx +126 -323
  74. package/src/components/MessageItem.tsx +1 -3
  75. package/src/components/MessageList.tsx +10 -67
  76. package/src/components/PlanDisplay.tsx +4 -27
  77. package/src/components/RewindCommand.tsx +38 -1
  78. package/src/components/SubagentBlock.tsx +25 -16
  79. package/src/components/TaskList.tsx +70 -0
  80. package/src/components/ToolResultDisplay.tsx +2 -2
  81. package/src/contexts/useChat.tsx +57 -40
  82. package/src/hooks/useInputManager.ts +9 -73
  83. package/src/hooks/useTasks.ts +6 -0
  84. package/src/managers/InputManager.ts +25 -159
  85. package/dist/components/Confirmation.d.ts.map +0 -1
  86. package/dist/components/MemoryDisplay.d.ts +0 -8
  87. package/dist/components/MemoryDisplay.d.ts.map +0 -1
  88. package/dist/components/MemoryDisplay.js +0 -25
  89. package/dist/components/MemoryTypeSelector.d.ts +0 -8
  90. package/dist/components/MemoryTypeSelector.d.ts.map +0 -1
  91. package/dist/components/MemoryTypeSelector.js +0 -38
  92. package/dist/components/TaskManager.d.ts +0 -6
  93. package/dist/components/TaskManager.d.ts.map +0 -1
  94. package/src/components/MemoryDisplay.tsx +0 -62
  95. package/src/components/MemoryTypeSelector.tsx +0 -98
@@ -4,7 +4,6 @@ import type { Message } from "wave-agent-sdk";
4
4
  import { MessageSource } from "wave-agent-sdk";
5
5
  import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
6
6
  import { ToolResultDisplay } from "./ToolResultDisplay.js";
7
- import { MemoryDisplay } from "./MemoryDisplay.js";
8
7
  import { CompressDisplay } from "./CompressDisplay.js";
9
8
  import { SubagentBlock } from "./SubagentBlock.js";
10
9
  import { ReasoningDisplay } from "./ReasoningDisplay.js";
@@ -22,6 +21,7 @@ export const MessageItem = ({
22
21
  shouldShowHeader,
23
22
  }: MessageItemProps) => {
24
23
  if (message.blocks.length === 0) return null;
24
+
25
25
  return (
26
26
  <Box flexDirection="column" gap={1} marginTop={1}>
27
27
  {shouldShowHeader && (
@@ -79,8 +79,6 @@ export const MessageItem = ({
79
79
  </Box>
80
80
  )}
81
81
 
82
- {block.type === "memory" && <MemoryDisplay block={block} />}
83
-
84
82
  {block.type === "compress" && (
85
83
  <CompressDisplay block={block} isExpanded={isExpanded} />
86
84
  )}
@@ -7,9 +7,8 @@ export interface MessageListProps {
7
7
  messages: Message[];
8
8
  isLoading?: boolean;
9
9
  isCommandRunning?: boolean;
10
- isCompressing?: boolean;
11
- latestTotalTokens?: number;
12
10
  isExpanded?: boolean;
11
+ forceStaticLastMessage?: boolean;
13
12
  }
14
13
 
15
14
  export const MessageList = React.memo(
@@ -17,15 +16,16 @@ export const MessageList = React.memo(
17
16
  messages,
18
17
  isLoading = false,
19
18
  isCommandRunning = false,
20
- isCompressing = false,
21
- latestTotalTokens = 0,
22
19
  isExpanded = false,
20
+ forceStaticLastMessage = false,
23
21
  }: MessageListProps) => {
24
22
  // Empty message state
25
23
  if (messages.length === 0) {
26
24
  return (
27
- <Box flexDirection="column" paddingY={1}>
28
- <Text color="gray">Welcome to WAVE Code Assistant!</Text>
25
+ <Box flexDirection="column" gap={1}>
26
+ <Box flexDirection="column" paddingY={1}>
27
+ <Text color="gray">Welcome to WAVE Code Assistant!</Text>
28
+ </Box>
29
29
  </Box>
30
30
  );
31
31
  }
@@ -42,7 +42,8 @@ export const MessageList = React.memo(
42
42
  : 0;
43
43
 
44
44
  // Compute which messages to render statically vs dynamically
45
- const shouldRenderLastDynamic = isLoading || isCommandRunning;
45
+ const shouldRenderLastDynamic =
46
+ !forceStaticLastMessage && (isLoading || isCommandRunning);
46
47
  const staticMessages = shouldRenderLastDynamic
47
48
  ? displayMessages.slice(0, -1)
48
49
  : displayMessages;
@@ -52,7 +53,7 @@ export const MessageList = React.memo(
52
53
  : [];
53
54
 
54
55
  return (
55
- <Box flexDirection="column" gap={1}>
56
+ <Box flexDirection="column" gap={1} paddingBottom={1}>
56
57
  {/* Show omitted message count when limiting */}
57
58
  {omittedCount > 0 && (
58
59
  <Box>
@@ -86,7 +87,7 @@ export const MessageList = React.memo(
86
87
  const previousMessage =
87
88
  messageIndex > 0 ? displayMessages[messageIndex - 1] : undefined;
88
89
  return (
89
- <Box key={`dynamic-${index}`} marginTop={-1}>
90
+ <Box key={`dynamic-${index}`}>
90
91
  <MessageItem
91
92
  message={message}
92
93
  shouldShowHeader={previousMessage?.role !== message.role}
@@ -95,64 +96,6 @@ export const MessageList = React.memo(
95
96
  </Box>
96
97
  );
97
98
  })}
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
99
  </Box>
157
100
  );
158
101
  },
@@ -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
@@ -1,4 +1,4 @@
1
- import React from "react";
1
+ import React, { useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import type { SubagentBlock as SubagentBlockType } from "wave-agent-sdk";
4
4
  import { useChat } from "../contexts/useChat.js";
@@ -9,16 +9,22 @@ interface SubagentBlockProps {
9
9
  }
10
10
 
11
11
  export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
12
- const { subagentMessages } = useChat();
12
+ const { subagentMessages, subagentLatestTokens } = useChat();
13
+
14
+ // Get messages for this subagent from context
15
+ const messages = useMemo(
16
+ () => subagentMessages[block.subagentId] || [],
17
+ [subagentMessages, block.subagentId],
18
+ );
19
+
20
+ // Get latest turn tokens for this subagent
21
+ const latestTurnTokens = subagentLatestTokens[block.subagentId] || 0;
13
22
 
14
23
  // If the subagent is running in the background, don't show the block
15
24
  if (block.runInBackground) {
16
25
  return null;
17
26
  }
18
27
 
19
- // Get messages for this subagent from context
20
- const messages = subagentMessages[block.subagentId] || [];
21
-
22
28
  // Status indicator mapping
23
29
  const getStatusIndicator = (status: SubagentBlockType["status"]) => {
24
30
  switch (status) {
@@ -63,7 +69,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
63
69
  return { tools: tools.reverse(), totalToolCount }; // Reverse to show oldest first, newest last
64
70
  };
65
71
 
66
- const { tools: lastTwoTools, totalToolCount } = getLastTwoTools();
72
+ const { tools: lastTwoTools } = getLastTwoTools();
67
73
 
68
74
  // Get the last text message content if completed
69
75
  const getLastTextMessage = () => {
@@ -93,9 +99,7 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
93
99
  borderStyle="classic"
94
100
  borderColor="magenta"
95
101
  paddingX={1}
96
- paddingY={0}
97
102
  flexDirection="column"
98
- marginBottom={1}
99
103
  >
100
104
  {/* Header Section */}
101
105
  <Box flexDirection="row" gap={1}>
@@ -107,26 +111,31 @@ export const SubagentBlock: React.FC<SubagentBlockProps> = ({ block }) => {
107
111
  </Text>
108
112
  <Text color="gray" dimColor>
109
113
  {" "}
110
- ({messages.length} messages)
114
+ ({messages.length} messages
115
+ {latestTurnTokens > 0 && (
116
+ <>
117
+ {" | "}
118
+ <Text color="blue" bold>
119
+ {latestTurnTokens.toLocaleString()}
120
+ </Text>
121
+ {" tokens"}
122
+ </>
123
+ )}
124
+ )
111
125
  </Text>
112
126
  </Box>
113
127
  </Box>
114
128
 
115
129
  {/* Last Text Message Section */}
116
130
  {lastTextMessage && (
117
- <Box marginTop={1}>
131
+ <Box>
118
132
  <Markdown>{lastTextMessage}</Markdown>
119
133
  </Box>
120
134
  )}
121
135
 
122
136
  {/* Tool Names Section - Vertical List */}
123
137
  {block.status !== "completed" && lastTwoTools.length > 0 && (
124
- <Box flexDirection="column" marginTop={1} gap={1}>
125
- {totalToolCount > 2 && (
126
- <Text color="gray" dimColor>
127
- ...
128
- </Text>
129
- )}
138
+ <Box flexDirection="column">
130
139
  {lastTwoTools.map((tool, index) => (
131
140
  <Box key={index} flexDirection="row">
132
141
  <Text color="magenta">🔧 </Text>
@@ -0,0 +1,70 @@
1
+ import React from "react";
2
+ import { useChat } from "../contexts/useChat.js";
3
+ import { Box, Text, useStdout } 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
+ const { stdout } = useStdout();
10
+ const terminalWidth = stdout?.columns ?? 80;
11
+ const maxSubjectWidth = Math.max(20, terminalWidth - 10);
12
+
13
+ if (tasks.length === 0 || !isTaskListVisible) {
14
+ return null;
15
+ }
16
+
17
+ const getStatusIcon = (status: string, isBlocked: boolean) => {
18
+ if (isBlocked) {
19
+ return <Text color="red">🔒</Text>;
20
+ }
21
+ switch (status) {
22
+ case "pending":
23
+ return <Text color="gray">○</Text>;
24
+ case "in_progress":
25
+ return <Text color="yellow">●</Text>;
26
+ case "completed":
27
+ return <Text color="green">✓</Text>;
28
+ case "deleted":
29
+ return <Text color="red">✕</Text>;
30
+ default:
31
+ return <Text color="gray">?</Text>;
32
+ }
33
+ };
34
+
35
+ const truncate = (text: string, maxWidth: number) => {
36
+ if (text.length <= maxWidth) {
37
+ return text;
38
+ }
39
+ return text.slice(0, maxWidth - 3) + "...";
40
+ };
41
+
42
+ return (
43
+ <Box flexDirection="column">
44
+ {tasks.map((task) => {
45
+ const isDimmed =
46
+ task.status === "completed" || task.status === "deleted";
47
+ const isBlocked = task.blockedBy && task.blockedBy.length > 0;
48
+ const blockingTaskIds = isBlocked
49
+ ? task.blockedBy.map((id) => `#${id}`)
50
+ : [];
51
+
52
+ const blockedByText =
53
+ isBlocked && blockingTaskIds.length > 0
54
+ ? ` (Blocked by: ${blockingTaskIds.join(", ")})`
55
+ : "";
56
+
57
+ const fullText = `${task.subject}${blockedByText}`;
58
+
59
+ return (
60
+ <Box key={task.id} gap={1}>
61
+ {getStatusIcon(task.status, isBlocked)}
62
+ <Text dimColor={isDimmed}>
63
+ {truncate(fullText, maxSubjectWidth)}
64
+ </Text>
65
+ </Box>
66
+ );
67
+ })}
68
+ </Box>
69
+ );
70
+ };
@@ -68,7 +68,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
68
68
  const shortResult = getShortResult();
69
69
 
70
70
  return (
71
- <Box flexDirection="column" gap={1}>
71
+ <Box flexDirection="column">
72
72
  <Box>
73
73
  <Text color="magenta">🔧 </Text>
74
74
  <Text color="white">{toolName}</Text>
@@ -91,7 +91,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
91
91
  flexDirection="column"
92
92
  >
93
93
  {shortResult.split("\n").map((line, index) => (
94
- <Text key={index} color="white">
94
+ <Text key={index} color="gray">
95
95
  {line}
96
96
  </Text>
97
97
  ))}