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
@@ -1,20 +1,26 @@
1
- import React from "react";
2
- import { Box } from "ink";
1
+ import React, { useState, useCallback } from "react";
2
+ import { Box, useStdout } from "ink";
3
3
  import { MessageList } from "./MessageList.js";
4
4
  import { InputBox } from "./InputBox.js";
5
- import { Confirmation } from "./Confirmation.js";
5
+ import { LoadingIndicator } from "./LoadingIndicator.js";
6
+ import { TaskList } from "./TaskList.js";
7
+ import { ConfirmationDetails } from "./ConfirmationDetails.js";
8
+ import { ConfirmationSelector } from "./ConfirmationSelector.js";
9
+
6
10
  import { useChat } from "../contexts/useChat.js";
11
+ import type { PermissionDecision } from "wave-agent-sdk";
7
12
 
8
13
  export const ChatInterface: React.FC = () => {
14
+ const { stdout } = useStdout();
15
+ const [isDetailsTooTall, setIsDetailsTooTall] = useState(false);
16
+
9
17
  const {
10
18
  messages,
11
19
  isLoading,
12
20
  isCommandRunning,
13
- userInputHistory,
14
21
  isCompressing,
15
22
  sendMessage,
16
23
  abortMessage,
17
- saveMemory,
18
24
  mcpServers,
19
25
  connectMcpServer,
20
26
  disconnectMcpServer,
@@ -26,45 +32,95 @@ export const ChatInterface: React.FC = () => {
26
32
  isConfirmationVisible,
27
33
  confirmingTool,
28
34
  handleConfirmationDecision,
29
- handleConfirmationCancel,
30
- rewindId,
35
+ handleConfirmationCancel: originalHandleConfirmationCancel,
36
+ setWasLastDetailsTooTall,
31
37
  } = useChat();
32
38
 
39
+ const handleHeightMeasured = useCallback(
40
+ (height: number) => {
41
+ const terminalHeight = stdout?.rows || 24;
42
+ if (height > terminalHeight - 10) {
43
+ setIsDetailsTooTall(true);
44
+ } else {
45
+ setIsDetailsTooTall(false);
46
+ }
47
+ },
48
+ [stdout?.rows],
49
+ );
50
+
51
+ const handleConfirmationCancel = useCallback(() => {
52
+ if (isDetailsTooTall) {
53
+ setWasLastDetailsTooTall((prev) => prev + 1);
54
+ setIsDetailsTooTall(false);
55
+ }
56
+ originalHandleConfirmationCancel();
57
+ }, [
58
+ isDetailsTooTall,
59
+ originalHandleConfirmationCancel,
60
+ setWasLastDetailsTooTall,
61
+ ]);
62
+
63
+ const wrappedHandleConfirmationDecision = useCallback(
64
+ (decision: PermissionDecision) => {
65
+ if (isDetailsTooTall) {
66
+ setWasLastDetailsTooTall((prev) => prev + 1);
67
+ setIsDetailsTooTall(false);
68
+ }
69
+ handleConfirmationDecision(decision);
70
+ },
71
+ [isDetailsTooTall, handleConfirmationDecision, setWasLastDetailsTooTall],
72
+ );
73
+
33
74
  if (!sessionId) return null;
34
75
 
35
76
  return (
36
- <Box flexDirection="column" height="100%" paddingY={1} paddingRight={1}>
77
+ <Box flexDirection="column">
37
78
  <MessageList
38
79
  messages={messages}
39
80
  isLoading={isLoading}
40
81
  isCommandRunning={isCommandRunning}
41
- isCompressing={isCompressing}
42
- latestTotalTokens={latestTotalTokens}
43
82
  isExpanded={isExpanded}
44
- key={String(isExpanded) + sessionId + rewindId}
83
+ forceStaticLastMessage={isDetailsTooTall}
45
84
  />
46
85
 
86
+ {(isLoading || isCommandRunning || isCompressing) &&
87
+ !isConfirmationVisible && (
88
+ <LoadingIndicator
89
+ isLoading={isLoading}
90
+ isCommandRunning={isCommandRunning}
91
+ isCompressing={isCompressing}
92
+ latestTotalTokens={latestTotalTokens}
93
+ />
94
+ )}
95
+ {!isConfirmationVisible && <TaskList />}
96
+
47
97
  {isConfirmationVisible && (
48
- <Confirmation
49
- toolName={confirmingTool!.name}
50
- toolInput={confirmingTool!.input}
51
- suggestedPrefix={confirmingTool!.suggestedPrefix}
52
- hidePersistentOption={confirmingTool!.hidePersistentOption}
53
- isExpanded={isExpanded}
54
- onDecision={handleConfirmationDecision}
55
- onCancel={handleConfirmationCancel}
56
- onAbort={abortMessage}
57
- />
98
+ <>
99
+ <ConfirmationDetails
100
+ toolName={confirmingTool!.name}
101
+ toolInput={confirmingTool!.input}
102
+ isExpanded={isExpanded}
103
+ onHeightMeasured={handleHeightMeasured}
104
+ />
105
+ <ConfirmationSelector
106
+ toolName={confirmingTool!.name}
107
+ toolInput={confirmingTool!.input}
108
+ suggestedPrefix={confirmingTool!.suggestedPrefix}
109
+ hidePersistentOption={confirmingTool!.hidePersistentOption}
110
+ isExpanded={isExpanded}
111
+ onDecision={wrappedHandleConfirmationDecision}
112
+ onCancel={handleConfirmationCancel}
113
+ onAbort={abortMessage}
114
+ />
115
+ </>
58
116
  )}
59
117
 
60
118
  {!isConfirmationVisible && !isExpanded && (
61
119
  <InputBox
62
120
  isLoading={isLoading}
63
121
  isCommandRunning={isCommandRunning}
64
- userInputHistory={userInputHistory}
65
122
  sendMessage={sendMessage}
66
123
  abortMessage={abortMessage}
67
- saveMemory={saveMemory}
68
124
  mcpServers={mcpServers}
69
125
  connectMcpServer={connectMcpServer}
70
126
  disconnectMcpServer={disconnectMcpServer}
@@ -39,6 +39,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
39
39
  onCancel,
40
40
  commands = [], // Default to empty array
41
41
  }) => {
42
+ const MAX_VISIBLE_ITEMS = 3;
42
43
  const [selectedIndex, setSelectedIndex] = useState(0);
43
44
 
44
45
  // Merge agent commands and local commands
@@ -51,6 +52,19 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
51
52
  command.id.toLowerCase().includes(searchQuery.toLowerCase()),
52
53
  );
53
54
 
55
+ // Calculate visible window
56
+ const startIndex = Math.max(
57
+ 0,
58
+ Math.min(
59
+ selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
60
+ Math.max(0, filteredCommands.length - MAX_VISIBLE_ITEMS),
61
+ ),
62
+ );
63
+ const visibleCommands = filteredCommands.slice(
64
+ startIndex,
65
+ startIndex + MAX_VISIBLE_ITEMS,
66
+ );
67
+
54
68
  useInput((input, key) => {
55
69
  if (key.return) {
56
70
  if (
@@ -101,7 +115,6 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
101
115
  borderBottom={false}
102
116
  borderLeft={false}
103
117
  borderRight={false}
104
- paddingTop={1}
105
118
  >
106
119
  <Text color="yellow">No commands found for "{searchQuery}"</Text>
107
120
  <Text dimColor>Press Escape to cancel</Text>
@@ -117,7 +130,6 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
117
130
  borderBottom={false}
118
131
  borderLeft={false}
119
132
  borderRight={false}
120
- paddingTop={1}
121
133
  gap={1}
122
134
  >
123
135
  <Box>
@@ -126,23 +138,29 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
126
138
  </Text>
127
139
  </Box>
128
140
 
129
- {filteredCommands.map((command, index) => (
130
- <Box key={command.id} flexDirection="column">
131
- <Text
132
- color={index === selectedIndex ? "black" : "white"}
133
- backgroundColor={index === selectedIndex ? "magenta" : undefined}
134
- >
135
- {index === selectedIndex ? "▶ " : " "}/{command.id}
136
- </Text>
137
- {index === selectedIndex && (
138
- <Box marginLeft={4}>
139
- <Text color="gray" dimColor>
140
- {command.description}
141
+ <Box flexDirection="column">
142
+ {visibleCommands.map((command, index) => {
143
+ const actualIndex = startIndex + index;
144
+ const isSelected = actualIndex === selectedIndex;
145
+ return (
146
+ <Box key={command.id} flexDirection="column">
147
+ <Text
148
+ color={isSelected ? "black" : "white"}
149
+ backgroundColor={isSelected ? "magenta" : undefined}
150
+ >
151
+ {isSelected ? "" : " "}/{command.id}
141
152
  </Text>
153
+ {isSelected && (
154
+ <Box marginLeft={4}>
155
+ <Text color="gray" dimColor>
156
+ {command.description}
157
+ </Text>
158
+ </Box>
159
+ )}
142
160
  </Box>
143
- )}
144
- </Box>
145
- ))}
161
+ );
162
+ })}
163
+ </Box>
146
164
 
147
165
  <Box>
148
166
  <Text dimColor>
@@ -7,25 +7,16 @@ interface CompressDisplayProps {
7
7
  isExpanded?: boolean;
8
8
  }
9
9
 
10
- export const CompressDisplay: React.FC<CompressDisplayProps> = ({
11
- block,
12
- isExpanded = false,
13
- }) => {
10
+ export const CompressDisplay: React.FC<CompressDisplayProps> = ({ block }) => {
14
11
  const { content } = block;
15
- const MAX_LINES = 3; // Set maximum display lines for compressed content
16
12
 
17
- const { displayContent, isOverflowing } = useMemo(() => {
13
+ const { displayContent } = useMemo(() => {
18
14
  if (!content) {
19
- return { displayContent: "", isOverflowing: false };
15
+ return { displayContent: "" };
20
16
  }
21
17
 
22
- const lines = content.split("\n");
23
- const overflow = !isExpanded && lines.length > MAX_LINES;
24
-
25
- const display = overflow ? lines.slice(0, MAX_LINES).join("\n") : content;
26
-
27
- return { displayContent: display, isOverflowing: overflow };
28
- }, [content, isExpanded]);
18
+ return { displayContent: content };
19
+ }, [content]);
29
20
 
30
21
  return (
31
22
  <Box flexDirection="column">
@@ -43,14 +34,6 @@ export const CompressDisplay: React.FC<CompressDisplayProps> = ({
43
34
  >
44
35
  <Text color="white">{displayContent}</Text>
45
36
  </Box>
46
- {isOverflowing && (
47
- <Box paddingLeft={2} marginTop={1}>
48
- <Text color="yellow" dimColor>
49
- Content truncated ({content.split("\n").length} lines total,
50
- showing first {MAX_LINES} lines. Press Ctrl+O to expand.
51
- </Text>
52
- </Box>
53
- )}
54
37
  </Box>
55
38
  )}
56
39
  </Box>
@@ -0,0 +1,108 @@
1
+ import React, { useLayoutEffect, useRef, useState } from "react";
2
+ import { Box, Text, useStdout, measureElement, Static } from "ink";
3
+ import {
4
+ BASH_TOOL_NAME,
5
+ EDIT_TOOL_NAME,
6
+ MULTI_EDIT_TOOL_NAME,
7
+ DELETE_FILE_TOOL_NAME,
8
+ WRITE_TOOL_NAME,
9
+ EXIT_PLAN_MODE_TOOL_NAME,
10
+ ASK_USER_QUESTION_TOOL_NAME,
11
+ } from "wave-agent-sdk";
12
+ import { DiffDisplay } from "./DiffDisplay.js";
13
+ import { PlanDisplay } from "./PlanDisplay.js";
14
+
15
+ // Helper function to generate descriptive action text
16
+ const getActionDescription = (
17
+ toolName: string,
18
+ toolInput?: Record<string, unknown>,
19
+ ): string => {
20
+ if (!toolInput) {
21
+ return "Execute operation";
22
+ }
23
+
24
+ switch (toolName) {
25
+ case BASH_TOOL_NAME:
26
+ return `Execute command: ${toolInput.command || "unknown command"}`;
27
+ case EDIT_TOOL_NAME:
28
+ return `Edit file: ${toolInput.file_path || "unknown file"}`;
29
+ case MULTI_EDIT_TOOL_NAME:
30
+ return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
31
+ case DELETE_FILE_TOOL_NAME:
32
+ return `Delete file: ${toolInput.target_file || "unknown file"}`;
33
+ case WRITE_TOOL_NAME:
34
+ return `Write to file: ${toolInput.file_path || "unknown file"}`;
35
+ case EXIT_PLAN_MODE_TOOL_NAME:
36
+ return "Review and approve the plan";
37
+ case ASK_USER_QUESTION_TOOL_NAME:
38
+ return "Answer questions to clarify intent";
39
+ default:
40
+ return "Execute operation";
41
+ }
42
+ };
43
+
44
+ export interface ConfirmationDetailsProps {
45
+ toolName: string;
46
+ toolInput?: Record<string, unknown>;
47
+ isExpanded?: boolean;
48
+ onHeightMeasured?: (height: number) => void;
49
+ }
50
+
51
+ export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
52
+ toolName,
53
+ toolInput,
54
+ isExpanded = false,
55
+ onHeightMeasured,
56
+ }) => {
57
+ const { stdout } = useStdout();
58
+ const [isStatic, setIsStatic] = useState(false);
59
+ const boxRef = useRef(null);
60
+
61
+ useLayoutEffect(() => {
62
+ if (boxRef.current) {
63
+ const { height } = measureElement(boxRef.current);
64
+ const terminalHeight = stdout?.rows || 24;
65
+ if (height > terminalHeight - 10) {
66
+ setIsStatic(true);
67
+ }
68
+ onHeightMeasured?.(height);
69
+ }
70
+ }, [stdout?.rows, onHeightMeasured]);
71
+
72
+ const content = (
73
+ <Box
74
+ ref={boxRef}
75
+ flexDirection="column"
76
+ borderStyle="single"
77
+ borderColor="yellow"
78
+ borderBottom={false}
79
+ borderLeft={false}
80
+ borderRight={false}
81
+ paddingTop={1}
82
+ >
83
+ <Text color="yellow" bold>
84
+ Tool: {toolName}
85
+ </Text>
86
+ <Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
87
+
88
+ <DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
89
+
90
+ {toolName !== ASK_USER_QUESTION_TOOL_NAME &&
91
+ toolName === EXIT_PLAN_MODE_TOOL_NAME &&
92
+ !!toolInput?.plan_content && (
93
+ <PlanDisplay
94
+ plan={toolInput.plan_content as string}
95
+ isExpanded={isExpanded}
96
+ />
97
+ )}
98
+ </Box>
99
+ );
100
+
101
+ if (isStatic) {
102
+ return <Static items={[1]}>{() => content}</Static>;
103
+ }
104
+
105
+ return content;
106
+ };
107
+
108
+ ConfirmationDetails.displayName = "ConfirmationDetails";