wave-code 0.0.5 → 0.0.8

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 (93) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/components/App.d.ts +1 -0
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +4 -4
  8. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  9. package/dist/components/BashHistorySelector.js +17 -3
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +6 -24
  12. package/dist/components/CommandSelector.js +4 -4
  13. package/dist/components/Confirmation.d.ts +11 -0
  14. package/dist/components/Confirmation.d.ts.map +1 -0
  15. package/dist/components/Confirmation.js +148 -0
  16. package/dist/components/DiffDisplay.d.ts +8 -0
  17. package/dist/components/DiffDisplay.d.ts.map +1 -0
  18. package/dist/components/DiffDisplay.js +168 -0
  19. package/dist/components/FileSelector.d.ts +2 -4
  20. package/dist/components/FileSelector.d.ts.map +1 -1
  21. package/dist/components/FileSelector.js +2 -2
  22. package/dist/components/InputBox.d.ts.map +1 -1
  23. package/dist/components/InputBox.js +30 -50
  24. package/dist/components/Markdown.d.ts +6 -0
  25. package/dist/components/Markdown.d.ts.map +1 -0
  26. package/dist/components/Markdown.js +22 -0
  27. package/dist/components/MemoryDisplay.js +1 -1
  28. package/dist/components/MessageItem.d.ts +8 -0
  29. package/dist/components/MessageItem.d.ts.map +1 -0
  30. package/dist/components/MessageItem.js +15 -0
  31. package/dist/components/MessageList.d.ts +1 -1
  32. package/dist/components/MessageList.d.ts.map +1 -1
  33. package/dist/components/MessageList.js +33 -33
  34. package/dist/components/ReasoningDisplay.d.ts +8 -0
  35. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  36. package/dist/components/ReasoningDisplay.js +10 -0
  37. package/dist/components/SubagentBlock.d.ts +0 -1
  38. package/dist/components/SubagentBlock.d.ts.map +1 -1
  39. package/dist/components/SubagentBlock.js +29 -30
  40. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  41. package/dist/components/ToolResultDisplay.js +6 -5
  42. package/dist/contexts/useChat.d.ts +14 -2
  43. package/dist/contexts/useChat.d.ts.map +1 -1
  44. package/dist/contexts/useChat.js +128 -17
  45. package/dist/hooks/useInputManager.d.ts +6 -1
  46. package/dist/hooks/useInputManager.d.ts.map +1 -1
  47. package/dist/hooks/useInputManager.js +32 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +30 -5
  50. package/dist/managers/InputManager.d.ts +11 -1
  51. package/dist/managers/InputManager.d.ts.map +1 -1
  52. package/dist/managers/InputManager.js +77 -26
  53. package/dist/print-cli.d.ts +2 -0
  54. package/dist/print-cli.d.ts.map +1 -1
  55. package/dist/print-cli.js +121 -23
  56. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  57. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  58. package/dist/utils/toolParameterTransforms.js +77 -0
  59. package/dist/utils/usageSummary.d.ts +6 -0
  60. package/dist/utils/usageSummary.d.ts.map +1 -1
  61. package/dist/utils/usageSummary.js +72 -0
  62. package/package.json +13 -8
  63. package/src/cli.tsx +3 -1
  64. package/src/components/App.tsx +7 -3
  65. package/src/components/BashHistorySelector.tsx +26 -3
  66. package/src/components/ChatInterface.tsx +38 -54
  67. package/src/components/CommandSelector.tsx +5 -5
  68. package/src/components/Confirmation.tsx +253 -0
  69. package/src/components/DiffDisplay.tsx +300 -0
  70. package/src/components/FileSelector.tsx +4 -6
  71. package/src/components/InputBox.tsx +58 -87
  72. package/src/components/Markdown.tsx +29 -0
  73. package/src/components/MemoryDisplay.tsx +1 -1
  74. package/src/components/MessageItem.tsx +96 -0
  75. package/src/components/MessageList.tsx +140 -202
  76. package/src/components/ReasoningDisplay.tsx +33 -0
  77. package/src/components/SubagentBlock.tsx +56 -84
  78. package/src/components/ToolResultDisplay.tsx +9 -5
  79. package/src/contexts/useChat.tsx +194 -21
  80. package/src/hooks/useInputManager.ts +40 -3
  81. package/src/index.ts +45 -5
  82. package/src/managers/InputManager.ts +101 -27
  83. package/src/print-cli.ts +143 -21
  84. package/src/utils/toolParameterTransforms.ts +104 -0
  85. package/src/utils/usageSummary.ts +109 -0
  86. package/dist/components/DiffViewer.d.ts +0 -9
  87. package/dist/components/DiffViewer.d.ts.map +0 -1
  88. package/dist/components/DiffViewer.js +0 -221
  89. package/dist/utils/fileSearch.d.ts +0 -20
  90. package/dist/utils/fileSearch.d.ts.map +0 -1
  91. package/dist/utils/fileSearch.js +0 -102
  92. package/src/components/DiffViewer.tsx +0 -321
  93. package/src/utils/fileSearch.ts +0 -133
package/src/cli.tsx CHANGED
@@ -6,10 +6,11 @@ import { cleanupLogs } from "./utils/logger.js";
6
6
  export interface CliOptions {
7
7
  restoreSessionId?: string;
8
8
  continueLastSession?: boolean;
9
+ bypassPermissions?: boolean;
9
10
  }
10
11
 
11
12
  export async function startCli(options: CliOptions): Promise<void> {
12
- const { restoreSessionId, continueLastSession } = options;
13
+ const { restoreSessionId, continueLastSession, bypassPermissions } = options;
13
14
 
14
15
  // Continue with ink-based UI for normal mode
15
16
  // Global cleanup tracker
@@ -63,6 +64,7 @@ export async function startCli(options: CliOptions): Promise<void> {
63
64
  <App
64
65
  restoreSessionId={restoreSessionId}
65
66
  continueLastSession={continueLastSession}
67
+ bypassPermissions={bypassPermissions}
66
68
  />,
67
69
  );
68
70
 
@@ -6,11 +6,14 @@ import { AppProvider } from "../contexts/useAppConfig.js";
6
6
  interface AppProps {
7
7
  restoreSessionId?: string;
8
8
  continueLastSession?: boolean;
9
+ bypassPermissions?: boolean;
9
10
  }
10
11
 
11
- const AppWithProviders: React.FC = () => {
12
+ const AppWithProviders: React.FC<{ bypassPermissions?: boolean }> = ({
13
+ bypassPermissions,
14
+ }) => {
12
15
  return (
13
- <ChatProvider>
16
+ <ChatProvider bypassPermissions={bypassPermissions}>
14
17
  <ChatInterface />
15
18
  </ChatProvider>
16
19
  );
@@ -19,13 +22,14 @@ const AppWithProviders: React.FC = () => {
19
22
  export const App: React.FC<AppProps> = ({
20
23
  restoreSessionId,
21
24
  continueLastSession,
25
+ bypassPermissions,
22
26
  }) => {
23
27
  return (
24
28
  <AppProvider
25
29
  restoreSessionId={restoreSessionId}
26
30
  continueLastSession={continueLastSession}
27
31
  >
28
- <AppWithProviders />
32
+ <AppWithProviders bypassPermissions={bypassPermissions} />
29
33
  </AppProvider>
30
34
  );
31
35
  };
@@ -1,6 +1,10 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { Box, Text, useInput } from "ink";
3
- import { searchBashHistory, type BashHistoryEntry } from "wave-agent-sdk";
3
+ import {
4
+ searchBashHistory,
5
+ deleteBashCommandFromHistory,
6
+ type BashHistoryEntry,
7
+ } from "wave-agent-sdk";
4
8
  import { logger } from "../utils/logger.js";
5
9
 
6
10
  export interface BashHistorySelectorProps {
@@ -20,6 +24,7 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
20
24
  }) => {
21
25
  const [selectedIndex, setSelectedIndex] = useState(0);
22
26
  const [commands, setCommands] = useState<BashHistoryEntry[]>([]);
27
+ const [refreshCounter, setRefreshCounter] = useState(0);
23
28
 
24
29
  // Search bash history
25
30
  useEffect(() => {
@@ -30,8 +35,9 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
30
35
  searchQuery,
31
36
  workdir,
32
37
  resultCount: results.length,
38
+ refreshCounter,
33
39
  });
34
- }, [searchQuery, workdir]);
40
+ }, [searchQuery, workdir, refreshCounter]);
35
41
 
36
42
  useInput((input, key) => {
37
43
  logger.debug("BashHistorySelector useInput:", {
@@ -77,6 +83,22 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
77
83
  setSelectedIndex(Math.min(commands.length - 1, selectedIndex + 1));
78
84
  return;
79
85
  }
86
+
87
+ if (key.delete) {
88
+ if (commands.length > 0 && selectedIndex < commands.length) {
89
+ const selectedCommand = commands[selectedIndex];
90
+ deleteBashCommandFromHistory(
91
+ selectedCommand.command,
92
+ selectedCommand.workdir,
93
+ );
94
+ setRefreshCounter((prev) => prev + 1);
95
+ // Adjust selectedIndex if we deleted the last item
96
+ if (selectedIndex >= commands.length - 1 && selectedIndex > 0) {
97
+ setSelectedIndex(selectedIndex - 1);
98
+ }
99
+ }
100
+ return;
101
+ }
80
102
  });
81
103
 
82
104
  if (commands.length === 0) {
@@ -155,7 +177,8 @@ export const BashHistorySelector: React.FC<BashHistorySelectorProps> = ({
155
177
 
156
178
  <Box>
157
179
  <Text dimColor>
158
- Use ↑↓ to navigate, Enter to execute, Tab to insert, Escape to cancel
180
+ Use ↑↓ to navigate, Enter to execute, Tab to insert, Delete to remove,
181
+ Escape to cancel
159
182
  </Text>
160
183
  </Box>
161
184
  </Box>
@@ -1,9 +1,9 @@
1
- import React, { useRef, useEffect } from "react";
1
+ import React from "react";
2
2
  import { Box } from "ink";
3
3
  import { MessageList } from "./MessageList.js";
4
4
  import { InputBox } from "./InputBox.js";
5
+ import { Confirmation } from "./Confirmation.js";
5
6
  import { useChat } from "../contexts/useChat.js";
6
- import type { Message } from "wave-agent-sdk";
7
7
 
8
8
  export const ChatInterface: React.FC = () => {
9
9
  const {
@@ -19,70 +19,54 @@ export const ChatInterface: React.FC = () => {
19
19
  connectMcpServer,
20
20
  disconnectMcpServer,
21
21
  isExpanded,
22
+ sessionId,
22
23
  latestTotalTokens,
23
24
  slashCommands,
24
25
  hasSlashCommand,
26
+ isConfirmationVisible,
27
+ confirmingTool,
28
+ handleConfirmationDecision,
29
+ handleConfirmationCancel,
25
30
  } = useChat();
26
31
 
27
- // Create a ref to store messages in expanded mode
28
- const expandedMessagesRef = useRef<Message[]>([]);
29
-
30
- useEffect(() => {
31
- // Only sync when collapsed
32
- if (!isExpanded) {
33
- expandedMessagesRef.current = messages.map((message, index) => {
34
- // If it's the last message, deep copy its blocks
35
- if (index === messages.length - 1) {
36
- return {
37
- ...message,
38
- blocks: message.blocks.map((block) => ({ ...block })),
39
- };
40
- }
41
- return message;
42
- });
43
- }
44
- }, [isExpanded, messages]);
32
+ if (!sessionId) return null;
45
33
 
46
34
  return (
47
- <Box flexDirection="column" height="100%">
48
- <Box flexGrow={1} flexDirection="column" paddingX={1}>
49
- {isExpanded ? (
50
- // Expanded mode uses messages from ref, loading and tokens are hardcoded to false and 0
51
- <MessageList
52
- messages={expandedMessagesRef.current}
53
- isLoading={false}
54
- isCommandRunning={false}
55
- latestTotalTokens={0}
56
- isExpanded={true}
35
+ <Box flexDirection="column" height="100%" paddingY={1}>
36
+ <MessageList
37
+ messages={messages}
38
+ isLoading={isLoading}
39
+ isCommandRunning={isCommandRunning}
40
+ isCompressing={isCompressing}
41
+ latestTotalTokens={latestTotalTokens}
42
+ isExpanded={isExpanded}
43
+ key={String(isExpanded) + sessionId}
44
+ />
45
+
46
+ {!isExpanded &&
47
+ (isConfirmationVisible ? (
48
+ <Confirmation
49
+ toolName={confirmingTool!.name}
50
+ toolInput={confirmingTool!.input}
51
+ onDecision={handleConfirmationDecision}
52
+ onCancel={handleConfirmationCancel}
53
+ onAbort={abortMessage}
57
54
  />
58
55
  ) : (
59
- // Normal mode uses real-time state
60
- <MessageList
61
- messages={messages}
56
+ <InputBox
62
57
  isLoading={isLoading}
63
58
  isCommandRunning={isCommandRunning}
64
- isCompressing={isCompressing}
65
- latestTotalTokens={latestTotalTokens}
66
- isExpanded={false}
59
+ userInputHistory={userInputHistory}
60
+ sendMessage={sendMessage}
61
+ abortMessage={abortMessage}
62
+ saveMemory={saveMemory}
63
+ mcpServers={mcpServers}
64
+ connectMcpServer={connectMcpServer}
65
+ disconnectMcpServer={disconnectMcpServer}
66
+ slashCommands={slashCommands}
67
+ hasSlashCommand={hasSlashCommand}
67
68
  />
68
- )}
69
- </Box>
70
-
71
- {!isExpanded && (
72
- <InputBox
73
- isLoading={isLoading}
74
- isCommandRunning={isCommandRunning}
75
- userInputHistory={userInputHistory}
76
- sendMessage={sendMessage}
77
- abortMessage={abortMessage}
78
- saveMemory={saveMemory}
79
- mcpServers={mcpServers}
80
- connectMcpServer={connectMcpServer}
81
- disconnectMcpServer={disconnectMcpServer}
82
- slashCommands={slashCommands}
83
- hasSlashCommand={hasSlashCommand}
84
- />
85
- )}
69
+ ))}
86
70
  </Box>
87
71
  );
88
72
  };
@@ -41,7 +41,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
41
41
  const filteredCommands = allCommands.filter(
42
42
  (command) =>
43
43
  !searchQuery ||
44
- command.name.toLowerCase().includes(searchQuery.toLowerCase()),
44
+ command.id.toLowerCase().includes(searchQuery.toLowerCase()),
45
45
  );
46
46
 
47
47
  useInput((input, key) => {
@@ -50,7 +50,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
50
50
  filteredCommands.length > 0 &&
51
51
  selectedIndex < filteredCommands.length
52
52
  ) {
53
- const selectedCommand = filteredCommands[selectedIndex].name;
53
+ const selectedCommand = filteredCommands[selectedIndex].id;
54
54
  onSelect(selectedCommand);
55
55
  }
56
56
  return;
@@ -61,7 +61,7 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
61
61
  filteredCommands.length > 0 &&
62
62
  selectedIndex < filteredCommands.length
63
63
  ) {
64
- const selectedCommand = filteredCommands[selectedIndex].name;
64
+ const selectedCommand = filteredCommands[selectedIndex].id;
65
65
  onInsert(selectedCommand);
66
66
  }
67
67
  return;
@@ -116,12 +116,12 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
116
116
  </Box>
117
117
 
118
118
  {filteredCommands.map((command, index) => (
119
- <Box key={command.name} flexDirection="column">
119
+ <Box key={command.id} flexDirection="column">
120
120
  <Text
121
121
  color={index === selectedIndex ? "black" : "white"}
122
122
  backgroundColor={index === selectedIndex ? "magenta" : undefined}
123
123
  >
124
- {index === selectedIndex ? "▶ " : " "}/{command.name}
124
+ {index === selectedIndex ? "▶ " : " "}/{command.id}
125
125
  </Text>
126
126
  {index === selectedIndex && (
127
127
  <Box marginLeft={4}>
@@ -0,0 +1,253 @@
1
+ import React, { useState } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import type { PermissionDecision } from "wave-agent-sdk";
4
+
5
+ // Helper function to generate descriptive action text
6
+ const getActionDescription = (
7
+ toolName: string,
8
+ toolInput?: Record<string, unknown>,
9
+ ): string => {
10
+ if (!toolInput) {
11
+ return "Execute operation";
12
+ }
13
+
14
+ switch (toolName) {
15
+ case "Bash":
16
+ return `Execute command: ${toolInput.command || "unknown command"}`;
17
+ case "Edit":
18
+ return `Edit file: ${toolInput.file_path || "unknown file"}`;
19
+ case "MultiEdit":
20
+ return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
21
+ case "Delete":
22
+ return `Delete file: ${toolInput.target_file || "unknown file"}`;
23
+ case "Write":
24
+ return `Write to file: ${toolInput.file_path || "unknown file"}`;
25
+ default:
26
+ return "Execute operation";
27
+ }
28
+ };
29
+
30
+ export interface ConfirmationProps {
31
+ toolName: string;
32
+ toolInput?: Record<string, unknown>;
33
+ onDecision: (decision: PermissionDecision) => void;
34
+ onCancel: () => void;
35
+ onAbort: () => void;
36
+ }
37
+
38
+ interface ConfirmationState {
39
+ selectedOption: "allow" | "auto" | "alternative";
40
+ alternativeText: string;
41
+ hasUserInput: boolean; // to hide placeholder
42
+ }
43
+
44
+ export const Confirmation: React.FC<ConfirmationProps> = ({
45
+ toolName,
46
+ toolInput,
47
+ onDecision,
48
+ onCancel,
49
+ onAbort,
50
+ }) => {
51
+ const [state, setState] = useState<ConfirmationState>({
52
+ selectedOption: "allow",
53
+ alternativeText: "",
54
+ hasUserInput: false,
55
+ });
56
+
57
+ const getAutoOptionText = () => {
58
+ if (toolName === "Bash") {
59
+ return `Yes, and don't ask again for ${toolInput?.command || "this"} commands in this workdir`;
60
+ }
61
+ return "Yes, and auto-accept edits";
62
+ };
63
+
64
+ useInput((input, key) => {
65
+ // Handle ESC to cancel and abort
66
+ if (key.escape) {
67
+ onCancel();
68
+ onAbort();
69
+ return;
70
+ }
71
+
72
+ // Handle Enter to confirm selection
73
+ if (key.return) {
74
+ if (state.selectedOption === "allow") {
75
+ onDecision({ behavior: "allow" });
76
+ } else if (state.selectedOption === "auto") {
77
+ if (toolName === "Bash") {
78
+ onDecision({
79
+ behavior: "allow",
80
+ newPermissionRule: `Bash(${toolInput?.command})`,
81
+ });
82
+ } else {
83
+ onDecision({
84
+ behavior: "allow",
85
+ newPermissionMode: "acceptEdits",
86
+ });
87
+ }
88
+ } else {
89
+ // For alternative option, require text input
90
+ if (state.alternativeText.trim()) {
91
+ onDecision({
92
+ behavior: "deny",
93
+ message: state.alternativeText.trim(),
94
+ });
95
+ }
96
+ }
97
+ return;
98
+ }
99
+
100
+ // Handle numeric keys for quick selection (only if not typing in alternative)
101
+ if (state.selectedOption !== "alternative" || !state.hasUserInput) {
102
+ if (input === "1") {
103
+ onDecision({ behavior: "allow" });
104
+ return;
105
+ }
106
+ if (input === "2") {
107
+ if (toolName === "Bash") {
108
+ onDecision({
109
+ behavior: "allow",
110
+ newPermissionRule: `Bash(${toolInput?.command})`,
111
+ });
112
+ } else {
113
+ onDecision({
114
+ behavior: "allow",
115
+ newPermissionMode: "acceptEdits",
116
+ });
117
+ }
118
+ return;
119
+ }
120
+ if (input === "3") {
121
+ setState((prev) => ({ ...prev, selectedOption: "alternative" }));
122
+ return;
123
+ }
124
+ }
125
+
126
+ // Handle arrow keys for navigation
127
+ if (key.upArrow) {
128
+ setState((prev) => {
129
+ if (prev.selectedOption === "alternative")
130
+ return { ...prev, selectedOption: "auto" };
131
+ if (prev.selectedOption === "auto")
132
+ return { ...prev, selectedOption: "allow" };
133
+ return prev;
134
+ });
135
+ return;
136
+ }
137
+
138
+ if (key.downArrow) {
139
+ setState((prev) => {
140
+ if (prev.selectedOption === "allow")
141
+ return { ...prev, selectedOption: "auto" };
142
+ if (prev.selectedOption === "auto")
143
+ return { ...prev, selectedOption: "alternative" };
144
+ return prev;
145
+ });
146
+ return;
147
+ }
148
+
149
+ // Handle text input for alternative option
150
+ if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
151
+ // Focus on alternative option when user starts typing
152
+ setState((prev) => ({
153
+ selectedOption: "alternative",
154
+ alternativeText: prev.alternativeText + input,
155
+ hasUserInput: true,
156
+ }));
157
+ return;
158
+ }
159
+
160
+ // Handle backspace and delete (same behavior - delete one character)
161
+ if (key.backspace || key.delete) {
162
+ setState((prev) => {
163
+ const newText = prev.alternativeText.slice(0, -1);
164
+ return {
165
+ ...prev,
166
+ selectedOption: "alternative",
167
+ alternativeText: newText,
168
+ hasUserInput: newText.length > 0,
169
+ };
170
+ });
171
+ return;
172
+ }
173
+ });
174
+
175
+ const placeholderText = "Type here to tell Wave what to do differently";
176
+ const showPlaceholder =
177
+ state.selectedOption === "alternative" && !state.hasUserInput;
178
+
179
+ return (
180
+ <Box
181
+ flexDirection="column"
182
+ borderStyle="single"
183
+ borderColor="yellow"
184
+ padding={1}
185
+ marginBottom={1}
186
+ >
187
+ <Text color="yellow" bold>
188
+ Tool: {toolName}
189
+ </Text>
190
+ <Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
191
+
192
+ <Box marginTop={1}>
193
+ <Text>Do you want to proceed?</Text>
194
+ </Box>
195
+
196
+ <Box marginTop={1} flexDirection="column">
197
+ {/* Option 1: Yes */}
198
+ <Box key="allow-option">
199
+ <Text
200
+ color={state.selectedOption === "allow" ? "black" : "white"}
201
+ backgroundColor={
202
+ state.selectedOption === "allow" ? "yellow" : undefined
203
+ }
204
+ bold={state.selectedOption === "allow"}
205
+ >
206
+ {state.selectedOption === "allow" ? "> " : " "}1. Yes
207
+ </Text>
208
+ </Box>
209
+
210
+ {/* Option 2: Auto-accept/Persistent */}
211
+ <Box key="auto-option">
212
+ <Text
213
+ color={state.selectedOption === "auto" ? "black" : "white"}
214
+ backgroundColor={
215
+ state.selectedOption === "auto" ? "yellow" : undefined
216
+ }
217
+ bold={state.selectedOption === "auto"}
218
+ >
219
+ {state.selectedOption === "auto" ? "> " : " "}2.{" "}
220
+ {getAutoOptionText()}
221
+ </Text>
222
+ </Box>
223
+
224
+ {/* Option 3: Alternative */}
225
+ <Box key="alternative-option">
226
+ <Text
227
+ color={state.selectedOption === "alternative" ? "black" : "white"}
228
+ backgroundColor={
229
+ state.selectedOption === "alternative" ? "yellow" : undefined
230
+ }
231
+ bold={state.selectedOption === "alternative"}
232
+ >
233
+ {state.selectedOption === "alternative" ? "> " : " "}3.{" "}
234
+ {showPlaceholder ? (
235
+ <Text color="gray" dimColor>
236
+ {placeholderText}
237
+ </Text>
238
+ ) : (
239
+ <Text>
240
+ {state.alternativeText ||
241
+ "Type here to tell Wave what to do differently"}
242
+ </Text>
243
+ )}
244
+ </Text>
245
+ </Box>
246
+ </Box>
247
+
248
+ <Box marginTop={1}>
249
+ <Text dimColor>Use ↑↓ or 1-3 to navigate • ESC to cancel</Text>
250
+ </Box>
251
+ </Box>
252
+ );
253
+ };