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
@@ -3,44 +3,9 @@ import { Box, Text, useInput } from "ink";
3
3
  import type { PermissionDecision, AskUserQuestionInput } from "wave-agent-sdk";
4
4
  import {
5
5
  BASH_TOOL_NAME,
6
- EDIT_TOOL_NAME,
7
- MULTI_EDIT_TOOL_NAME,
8
- DELETE_FILE_TOOL_NAME,
9
- WRITE_TOOL_NAME,
10
6
  EXIT_PLAN_MODE_TOOL_NAME,
11
7
  ASK_USER_QUESTION_TOOL_NAME,
12
8
  } from "wave-agent-sdk";
13
- import { DiffDisplay } from "./DiffDisplay.js";
14
- import { PlanDisplay } from "./PlanDisplay.js";
15
-
16
- // Helper function to generate descriptive action text
17
- const getActionDescription = (
18
- toolName: string,
19
- toolInput?: Record<string, unknown>,
20
- ): string => {
21
- if (!toolInput) {
22
- return "Execute operation";
23
- }
24
-
25
- switch (toolName) {
26
- case BASH_TOOL_NAME:
27
- return `Execute command: ${toolInput.command || "unknown command"}`;
28
- case EDIT_TOOL_NAME:
29
- return `Edit file: ${toolInput.file_path || "unknown file"}`;
30
- case MULTI_EDIT_TOOL_NAME:
31
- return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
32
- case DELETE_FILE_TOOL_NAME:
33
- return `Delete file: ${toolInput.target_file || "unknown file"}`;
34
- case WRITE_TOOL_NAME:
35
- return `Write to file: ${toolInput.file_path || "unknown file"}`;
36
- case EXIT_PLAN_MODE_TOOL_NAME:
37
- return "Review and approve the plan";
38
- case ASK_USER_QUESTION_TOOL_NAME:
39
- return "Answer questions to clarify intent";
40
- default:
41
- return "Execute operation";
42
- }
43
- };
44
9
 
45
10
  const getHeaderColor = (header: string) => {
46
11
  const colors = ["red", "green", "blue", "magenta", "cyan"] as const;
@@ -51,7 +16,7 @@ const getHeaderColor = (header: string) => {
51
16
  return colors[Math.abs(hash) % colors.length];
52
17
  };
53
18
 
54
- export interface ConfirmationProps {
19
+ export interface ConfirmationSelectorProps {
55
20
  toolName: string;
56
21
  toolInput?: Record<string, unknown>;
57
22
  suggestedPrefix?: string;
@@ -66,10 +31,10 @@ interface ConfirmationState {
66
31
  selectedOption: "allow" | "auto" | "alternative";
67
32
  alternativeText: string;
68
33
  alternativeCursorPosition: number;
69
- hasUserInput: boolean; // to hide placeholder
34
+ hasUserInput: boolean;
70
35
  }
71
36
 
72
- export const Confirmation: React.FC<ConfirmationProps> = ({
37
+ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
73
38
  toolName,
74
39
  toolInput,
75
40
  suggestedPrefix,
@@ -86,7 +51,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
86
51
  hasUserInput: false,
87
52
  });
88
53
 
89
- // Specialized state for AskUserQuestion
90
54
  const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
91
55
  const [selectedOptionIndex, setSelectedOptionIndex] = useState(0);
92
56
  const [selectedOptionIndices, setSelectedOptionIndices] = useState<
@@ -114,7 +78,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
114
78
  };
115
79
 
116
80
  useInput((input, key) => {
117
- // Handle ESC to cancel and abort
118
81
  if (key.escape) {
119
82
  onCancel();
120
83
  onAbort();
@@ -123,10 +86,8 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
123
86
 
124
87
  if (toolName === ASK_USER_QUESTION_TOOL_NAME) {
125
88
  if (!currentQuestion) return;
126
-
127
89
  const options = [...currentQuestion.options, { label: "Other" }];
128
- const isMultiSelect = !!currentQuestion.multiSelect;
129
-
90
+ const isMultiSelect = currentQuestion.multiSelect;
130
91
  const isOtherFocused = selectedOptionIndex === options.length - 1;
131
92
 
132
93
  if (key.return) {
@@ -135,7 +96,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
135
96
  const selectedLabels = Array.from(selectedOptionIndices)
136
97
  .filter((i) => i < currentQuestion.options.length)
137
98
  .map((i) => currentQuestion.options[i].label);
138
-
139
99
  const isOtherChecked = selectedOptionIndices.has(options.length - 1);
140
100
  if (isOtherChecked && otherText.trim()) {
141
101
  selectedLabels.push(otherText.trim());
@@ -148,15 +108,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
148
108
  answer = options[selectedOptionIndex].label;
149
109
  }
150
110
  }
151
-
152
111
  if (!answer) return;
153
-
154
112
  const newAnswers = {
155
113
  ...userAnswers,
156
114
  [currentQuestion.question]: answer,
157
115
  };
158
116
  setUserAnswers(newAnswers);
159
-
160
117
  if (currentQuestionIndex < questions.length - 1) {
161
118
  setCurrentQuestionIndex(currentQuestionIndex + 1);
162
119
  setSelectedOptionIndex(0);
@@ -164,7 +121,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
164
121
  setOtherText("");
165
122
  setOtherCursorPosition(0);
166
123
  } else {
167
- // All questions answered
168
124
  onDecision({
169
125
  behavior: "allow",
170
126
  message: JSON.stringify(newAnswers),
@@ -180,33 +136,23 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
180
136
  ) {
181
137
  setSelectedOptionIndices((prev) => {
182
138
  const next = new Set(prev);
183
- if (next.has(selectedOptionIndex)) {
184
- next.delete(selectedOptionIndex);
185
- } else {
186
- next.add(selectedOptionIndex);
187
- }
139
+ if (next.has(selectedOptionIndex)) next.delete(selectedOptionIndex);
140
+ else next.add(selectedOptionIndex);
188
141
  return next;
189
142
  });
190
143
  return;
191
144
  }
192
-
193
- if (!isOtherFocused) {
194
- return;
195
- }
196
- // If isOtherFocused is true, fall through to handle space as text input
145
+ if (!isOtherFocused) return;
197
146
  }
198
147
 
199
148
  if (key.upArrow) {
200
- if (selectedOptionIndex > 0) {
149
+ if (selectedOptionIndex > 0)
201
150
  setSelectedOptionIndex(selectedOptionIndex - 1);
202
- }
203
151
  return;
204
152
  }
205
-
206
153
  if (key.downArrow) {
207
- if (selectedOptionIndex < options.length - 1) {
154
+ if (selectedOptionIndex < options.length - 1)
208
155
  setSelectedOptionIndex(selectedOptionIndex + 1);
209
- }
210
156
  return;
211
157
  }
212
158
 
@@ -223,34 +169,29 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
223
169
  }
224
170
  if (key.backspace || key.delete) {
225
171
  if (otherCursorPosition > 0) {
226
- setOtherText((prev) => {
227
- const next =
172
+ setOtherText(
173
+ (prev) =>
228
174
  prev.slice(0, otherCursorPosition - 1) +
229
- prev.slice(otherCursorPosition);
230
- return next;
231
- });
175
+ prev.slice(otherCursorPosition),
176
+ );
232
177
  setOtherCursorPosition((prev) => prev - 1);
233
178
  }
234
179
  return;
235
180
  }
236
181
  if (input && !key.ctrl && !key.meta) {
237
- setOtherText((prev) => {
238
- const next =
182
+ setOtherText(
183
+ (prev) =>
239
184
  prev.slice(0, otherCursorPosition) +
240
185
  input +
241
- prev.slice(otherCursorPosition);
242
- return next;
243
- });
186
+ prev.slice(otherCursorPosition),
187
+ );
244
188
  setOtherCursorPosition((prev) => prev + input.length);
245
189
  return;
246
190
  }
247
- return;
248
191
  }
249
-
250
192
  return;
251
193
  }
252
194
 
253
- // Handle Enter to confirm selection
254
195
  if (key.return) {
255
196
  if (state.selectedOption === "allow") {
256
197
  if (toolName === EXIT_PLAN_MODE_TOOL_NAME) {
@@ -263,24 +204,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
263
204
  const rule = suggestedPrefix
264
205
  ? `Bash(${suggestedPrefix}*)`
265
206
  : `Bash(${toolInput?.command})`;
266
- onDecision({
267
- behavior: "allow",
268
- newPermissionRule: rule,
269
- });
207
+ onDecision({ behavior: "allow", newPermissionRule: rule });
270
208
  } else {
271
- onDecision({
272
- behavior: "allow",
273
- newPermissionMode: "acceptEdits",
274
- });
275
- }
276
- } else {
277
- // For alternative option, require text input
278
- if (state.alternativeText.trim()) {
279
- onDecision({
280
- behavior: "deny",
281
- message: state.alternativeText.trim(),
282
- });
209
+ onDecision({ behavior: "allow", newPermissionMode: "acceptEdits" });
283
210
  }
211
+ } else if (state.alternativeText.trim()) {
212
+ onDecision({ behavior: "deny", message: state.alternativeText.trim() });
284
213
  }
285
214
  return;
286
215
  }
@@ -308,15 +237,13 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
308
237
  }
309
238
  }
310
239
 
311
- // Handle arrow keys for navigation
312
240
  if (key.upArrow) {
313
241
  setState((prev) => {
314
- if (prev.selectedOption === "alternative") {
242
+ if (prev.selectedOption === "alternative")
315
243
  return {
316
244
  ...prev,
317
245
  selectedOption: hidePersistentOption ? "allow" : "auto",
318
246
  };
319
- }
320
247
  if (prev.selectedOption === "auto")
321
248
  return { ...prev, selectedOption: "allow" };
322
249
  return prev;
@@ -326,12 +253,11 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
326
253
 
327
254
  if (key.downArrow) {
328
255
  setState((prev) => {
329
- if (prev.selectedOption === "allow") {
256
+ if (prev.selectedOption === "allow")
330
257
  return {
331
258
  ...prev,
332
259
  selectedOption: hidePersistentOption ? "alternative" : "auto",
333
260
  };
334
- }
335
261
  if (prev.selectedOption === "auto")
336
262
  return { ...prev, selectedOption: "alternative" };
337
263
  return prev;
@@ -339,9 +265,7 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
339
265
  return;
340
266
  }
341
267
 
342
- // Handle text input for alternative option
343
268
  if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
344
- // Focus on alternative option when user starts typing
345
269
  setState((prev) => {
346
270
  const nextText =
347
271
  prev.alternativeText.slice(0, prev.alternativeCursorPosition) +
@@ -359,7 +283,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
359
283
  return;
360
284
  }
361
285
 
362
- // Handle backspace and delete
363
286
  if (key.backspace || key.delete) {
364
287
  setState((prev) => {
365
288
  if (prev.alternativeCursorPosition > 0) {
@@ -385,93 +308,65 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
385
308
  state.selectedOption === "alternative" && !state.hasUserInput;
386
309
 
387
310
  return (
388
- <Box
389
- flexDirection="column"
390
- borderStyle="single"
391
- borderColor="yellow"
392
- borderBottom={false}
393
- borderLeft={false}
394
- borderRight={false}
395
- paddingTop={1}
396
- >
397
- <Text color="yellow" bold>
398
- Tool: {toolName}
399
- </Text>
400
- <Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
401
-
402
- <DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
403
-
311
+ <Box flexDirection="column">
404
312
  {toolName === ASK_USER_QUESTION_TOOL_NAME &&
405
313
  currentQuestion &&
406
314
  !isExpanded && (
407
315
  <Box flexDirection="column" marginTop={1}>
408
316
  <Box marginBottom={1}>
409
- <Box
410
- backgroundColor={getHeaderColor(currentQuestion.header)}
411
- paddingX={1}
412
- marginRight={1}
413
- >
414
- <Text color="black" bold>
415
- {currentQuestion.header.slice(0, 12).toUpperCase()}
416
- </Text>
317
+ <Text color={getHeaderColor(currentQuestion.header)} bold>
318
+ {currentQuestion.header.slice(0, 12).toUpperCase()}
319
+ </Text>
320
+ <Box marginLeft={1}>
321
+ <Text bold>{currentQuestion.question}</Text>
417
322
  </Box>
418
- <Text bold>{currentQuestion.question}</Text>
419
323
  </Box>
420
-
421
324
  <Box flexDirection="column">
422
- {(() => {
423
- const isMultiSelect = !!currentQuestion.multiSelect;
424
- return [...currentQuestion.options, { label: "Other" }].map(
425
- (option, index) => {
426
- const isSelected = selectedOptionIndex === index;
427
- const isChecked = isMultiSelect
428
- ? selectedOptionIndices.has(index)
429
- : isSelected;
430
- const isOther = index === currentQuestion.options.length;
431
- const isRecommended = !isOther && option.isRecommended;
432
-
433
- return (
434
- <Box key={index}>
435
- <Text
436
- color={isSelected ? "black" : "white"}
437
- backgroundColor={isSelected ? "yellow" : undefined}
438
- >
439
- {isSelected ? "> " : " "}
440
- {isMultiSelect ? (isChecked ? "[x] " : "[ ] ") : ""}
441
- {option.label}
442
- {isRecommended && (
443
- <Text color="green" bold>
444
- {" "}
445
- (Recommended)
446
- </Text>
447
- )}
448
- {option.description ? ` - ${option.description}` : ""}
449
- {isOther && isSelected && (
450
- <Text>
451
- :{" "}
452
- {otherText ? (
453
- <>
454
- {otherText.slice(0, otherCursorPosition)}
455
- <Text backgroundColor="white" color="black">
456
- {otherText[otherCursorPosition] || " "}
457
- </Text>
458
- {otherText.slice(otherCursorPosition + 1)}
459
- </>
460
- ) : (
461
- <Text color="gray" dimColor>
462
- [Type your answer...]
325
+ {[...currentQuestion.options, { label: "Other" }].map(
326
+ (option, index) => {
327
+ const isSelected = selectedOptionIndex === index;
328
+ const isChecked = currentQuestion.multiSelect
329
+ ? selectedOptionIndices.has(index)
330
+ : isSelected;
331
+ const isOther = index === currentQuestion.options.length;
332
+ return (
333
+ <Box key={index}>
334
+ <Text
335
+ color={isSelected ? "black" : "white"}
336
+ backgroundColor={isSelected ? "yellow" : undefined}
337
+ >
338
+ {isSelected ? "> " : " "}
339
+ {currentQuestion.multiSelect
340
+ ? isChecked
341
+ ? "[x] "
342
+ : "[ ] "
343
+ : ""}
344
+ {option.label}
345
+ {option.description ? ` - ${option.description}` : ""}
346
+ {isOther && isSelected && (
347
+ <Text>
348
+ :{" "}
349
+ {otherText ? (
350
+ <>
351
+ {otherText.slice(0, otherCursorPosition)}
352
+ <Text backgroundColor="white" color="black">
353
+ {otherText[otherCursorPosition] || " "}
463
354
  </Text>
464
- )}
465
- </Text>
466
- )}
467
- </Text>
468
- </Box>
469
- );
470
- },
471
- );
472
- })()}
355
+ {otherText.slice(otherCursorPosition + 1)}
356
+ </>
357
+ ) : (
358
+ <Text color="gray" dimColor>
359
+ [Type your answer...]
360
+ </Text>
361
+ )}
362
+ </Text>
363
+ )}
364
+ </Text>
365
+ </Box>
366
+ );
367
+ },
368
+ )}
473
369
  </Box>
474
-
475
370
  <Box marginTop={1}>
476
371
  <Text dimColor>
477
372
  Question {currentQuestionIndex + 1} of {questions.length} •
@@ -482,23 +377,12 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
482
377
  </Box>
483
378
  )}
484
379
 
485
- {toolName !== ASK_USER_QUESTION_TOOL_NAME &&
486
- toolName === EXIT_PLAN_MODE_TOOL_NAME &&
487
- !!toolInput?.plan_content && (
488
- <PlanDisplay
489
- plan={toolInput.plan_content as string}
490
- isExpanded={isExpanded}
491
- />
492
- )}
493
-
494
380
  {toolName !== ASK_USER_QUESTION_TOOL_NAME && !isExpanded && (
495
381
  <>
496
382
  <Box marginTop={1}>
497
383
  <Text>Do you want to proceed?</Text>
498
384
  </Box>
499
-
500
385
  <Box marginTop={1} flexDirection="column">
501
- {/* Option 1: Yes */}
502
386
  <Box key="allow-option">
503
387
  <Text
504
388
  color={state.selectedOption === "allow" ? "black" : "white"}
@@ -513,8 +397,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
513
397
  : "Yes"}
514
398
  </Text>
515
399
  </Box>
516
-
517
- {/* Option 2: Auto-accept/Persistent */}
518
400
  {!hidePersistentOption && (
519
401
  <Box key="auto-option">
520
402
  <Text
@@ -529,8 +411,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
529
411
  </Text>
530
412
  </Box>
531
413
  )}
532
-
533
- {/* Option 3: Alternative */}
534
414
  <Box key="alternative-option">
535
415
  <Text
536
416
  color={
@@ -571,7 +451,6 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
571
451
  </Text>
572
452
  </Box>
573
453
  </Box>
574
-
575
454
  <Box marginTop={1}>
576
455
  <Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
577
456
  </Box>
@@ -580,3 +459,5 @@ export const Confirmation: React.FC<ConfirmationProps> = ({
580
459
  </Box>
581
460
  );
582
461
  };
462
+
463
+ ConfirmationSelector.displayName = "ConfirmationSelector";
@@ -109,6 +109,15 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
109
109
 
110
110
  changes.forEach((change, changeIndex) => {
111
111
  try {
112
+ // Add ellipsis between non-contiguous edits in MultiEdit
113
+ if (toolName === MULTI_EDIT_TOOL_NAME && changeIndex > 0) {
114
+ allElements.push(
115
+ <Box key={`multi-edit-separator-${changeIndex}`}>
116
+ <Text color="gray">...</Text>
117
+ </Box>,
118
+ );
119
+ }
120
+
112
121
  // Get line-level diff to understand the structure
113
122
  const lineDiffs = diffLines(
114
123
  change.oldContent || "",
@@ -153,7 +162,60 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
153
162
  const lines = part.value
154
163
  .split("\n")
155
164
  .filter((line) => line !== "");
156
- lines.forEach((line, lineIndex) => {
165
+
166
+ const isFirstBlock = partIndex === 0;
167
+ const isLastBlock = partIndex === lineDiffs.length - 1;
168
+
169
+ let linesToDisplay = lines;
170
+ let showEllipsisTop = false;
171
+ let showEllipsisBottom = false;
172
+
173
+ if (isFirstBlock && !isLastBlock) {
174
+ // First block: keep last 3
175
+ if (lines.length > 3) {
176
+ linesToDisplay = lines.slice(-3);
177
+ showEllipsisTop = true;
178
+ }
179
+ } else if (isLastBlock && !isFirstBlock) {
180
+ // Last block: keep first 3
181
+ if (lines.length > 3) {
182
+ linesToDisplay = lines.slice(0, 3);
183
+ showEllipsisBottom = true;
184
+ }
185
+ } else if (!isFirstBlock && !isLastBlock) {
186
+ // Middle block: keep first 3 and last 3
187
+ if (lines.length > 6) {
188
+ linesToDisplay = [...lines.slice(0, 3), ...lines.slice(-3)];
189
+ showEllipsisTop = false; // We'll put ellipsis in the middle
190
+ }
191
+ } else if (isFirstBlock && isLastBlock) {
192
+ // Only one block (no changes?) - keep all or apply a general limit
193
+ // For now, let's keep all if it's the only block
194
+ }
195
+
196
+ if (showEllipsisTop) {
197
+ diffElements.push(
198
+ <Box key={`ellipsis-top-${changeIndex}-${partIndex}`}>
199
+ <Text color="gray"> ...</Text>
200
+ </Box>,
201
+ );
202
+ }
203
+
204
+ linesToDisplay.forEach((line, lineIndex) => {
205
+ // If it's a middle block and we are at the split point
206
+ if (
207
+ !isFirstBlock &&
208
+ !isLastBlock &&
209
+ lines.length > 6 &&
210
+ lineIndex === 3
211
+ ) {
212
+ diffElements.push(
213
+ <Box key={`ellipsis-mid-${changeIndex}-${partIndex}`}>
214
+ <Text color="gray"> ...</Text>
215
+ </Box>,
216
+ );
217
+ }
218
+
157
219
  diffElements.push(
158
220
  <Box
159
221
  key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
@@ -164,6 +226,14 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
164
226
  </Box>,
165
227
  );
166
228
  });
229
+
230
+ if (showEllipsisBottom) {
231
+ diffElements.push(
232
+ <Box key={`ellipsis-bottom-${changeIndex}-${partIndex}`}>
233
+ <Text color="gray"> ...</Text>
234
+ </Box>,
235
+ );
236
+ }
167
237
  }
168
238
  });
169
239
 
@@ -52,7 +52,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
52
52
  borderBottom={false}
53
53
  borderLeft={false}
54
54
  borderRight={false}
55
- paddingTop={1}
56
55
  >
57
56
  <Text color="yellow">📁 No files found for "{searchQuery}"</Text>
58
57
  <Text dimColor>Press Escape to cancel</Text>
@@ -91,7 +90,6 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
91
90
  borderBottom={false}
92
91
  borderLeft={false}
93
92
  borderRight={false}
94
- paddingTop={1}
95
93
  >
96
94
  <Text color="cyan" bold>
97
95
  📁 Select File/Directory{" "}
@@ -0,0 +1,59 @@
1
+ import React from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+
4
+ export interface HelpViewProps {
5
+ onCancel: () => void;
6
+ }
7
+
8
+ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
9
+ useInput((_, key) => {
10
+ if (key.escape || key.return) {
11
+ onCancel();
12
+ }
13
+ });
14
+
15
+ const helpItems = [
16
+ { key: "@", description: "Reference files" },
17
+ { key: "/", description: "Commands" },
18
+ { key: "Ctrl+R", description: "Search history" },
19
+ { key: "Ctrl+O", description: "Expand/collapse messages" },
20
+ { key: "Ctrl+T", description: "Toggle task list" },
21
+ { key: "Ctrl+B", description: "Background current task" },
22
+ { key: "Ctrl+V", description: "Paste image" },
23
+ { key: "Shift+Tab", description: "Cycle permission mode" },
24
+ {
25
+ key: "Esc",
26
+ description: "Interrupt AI or command / Cancel selector / Close help",
27
+ },
28
+ ];
29
+
30
+ return (
31
+ <Box
32
+ flexDirection="column"
33
+ borderStyle="single"
34
+ borderColor="cyan"
35
+ borderLeft={false}
36
+ borderRight={false}
37
+ paddingX={1}
38
+ >
39
+ <Box marginBottom={1}>
40
+ <Text color="cyan" bold underline>
41
+ Help & Key Bindings
42
+ </Text>
43
+ </Box>
44
+
45
+ {helpItems.map((item, index) => (
46
+ <Box key={index}>
47
+ <Box width={20}>
48
+ <Text color="yellow">{item.key}</Text>
49
+ </Box>
50
+ <Text color="white">{item.description}</Text>
51
+ </Box>
52
+ ))}
53
+
54
+ <Box marginTop={1}>
55
+ <Text dimColor>Press Esc or Enter to close</Text>
56
+ </Box>
57
+ </Box>
58
+ );
59
+ };