wave-code 0.7.1 → 0.8.0

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 (108) hide show
  1. package/dist/cli.d.ts +2 -4
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +24 -52
  4. package/dist/components/App.d.ts +3 -4
  5. package/dist/components/App.d.ts.map +1 -1
  6. package/dist/components/App.js +49 -6
  7. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  8. package/dist/components/BackgroundTaskManager.js +12 -20
  9. package/dist/components/BangDisplay.d.ts +9 -0
  10. package/dist/components/BangDisplay.d.ts.map +1 -0
  11. package/dist/components/{CommandOutputDisplay.js → BangDisplay.js} +1 -1
  12. package/dist/components/ChatInterface.d.ts.map +1 -1
  13. package/dist/components/ChatInterface.js +3 -2
  14. package/dist/components/CommandSelector.d.ts.map +1 -1
  15. package/dist/components/CommandSelector.js +18 -2
  16. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  17. package/dist/components/ConfirmationSelector.js +105 -8
  18. package/dist/components/HelpView.d.ts.map +1 -1
  19. package/dist/components/HelpView.js +2 -0
  20. package/dist/components/HistorySearch.d.ts.map +1 -1
  21. package/dist/components/HistorySearch.js +19 -25
  22. package/dist/components/InputBox.d.ts.map +1 -1
  23. package/dist/components/InputBox.js +9 -3
  24. package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
  25. package/dist/components/MarketplaceAddForm.js +13 -6
  26. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  27. package/dist/components/MarketplaceDetail.js +8 -3
  28. package/dist/components/MessageBlockItem.js +2 -2
  29. package/dist/components/MessageList.d.ts +4 -1
  30. package/dist/components/MessageList.d.ts.map +1 -1
  31. package/dist/components/MessageList.js +15 -8
  32. package/dist/components/PluginDetail.d.ts.map +1 -1
  33. package/dist/components/PluginDetail.js +14 -3
  34. package/dist/components/PluginManagerShell.d.ts.map +1 -1
  35. package/dist/components/PluginManagerShell.js +3 -3
  36. package/dist/components/PluginManagerTypes.d.ts +2 -0
  37. package/dist/components/PluginManagerTypes.d.ts.map +1 -1
  38. package/dist/components/SessionSelector.d.ts.map +1 -1
  39. package/dist/components/SessionSelector.js +10 -13
  40. package/dist/components/StatusCommand.d.ts +6 -0
  41. package/dist/components/StatusCommand.d.ts.map +1 -0
  42. package/dist/components/StatusCommand.js +28 -0
  43. package/dist/components/WorktreeExitPrompt.d.ts +13 -0
  44. package/dist/components/WorktreeExitPrompt.d.ts.map +1 -0
  45. package/dist/components/WorktreeExitPrompt.js +26 -0
  46. package/dist/contexts/useChat.d.ts +9 -5
  47. package/dist/contexts/useChat.d.ts.map +1 -1
  48. package/dist/contexts/useChat.js +38 -8
  49. package/dist/contracts/status.d.ts +8 -0
  50. package/dist/contracts/status.d.ts.map +1 -0
  51. package/dist/contracts/status.js +1 -0
  52. package/dist/hooks/useInputManager.d.ts +2 -0
  53. package/dist/hooks/useInputManager.d.ts.map +1 -1
  54. package/dist/hooks/useInputManager.js +12 -0
  55. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  56. package/dist/hooks/usePluginManager.js +41 -13
  57. package/dist/hooks/useTasks.js +2 -2
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +53 -4
  60. package/dist/managers/InputManager.d.ts +6 -0
  61. package/dist/managers/InputManager.d.ts.map +1 -1
  62. package/dist/managers/InputManager.js +32 -13
  63. package/dist/print-cli.d.ts +2 -4
  64. package/dist/print-cli.d.ts.map +1 -1
  65. package/dist/print-cli.js +31 -2
  66. package/dist/session-selector-cli.d.ts +3 -1
  67. package/dist/session-selector-cli.d.ts.map +1 -1
  68. package/dist/session-selector-cli.js +2 -2
  69. package/dist/types.d.ts +11 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +1 -0
  72. package/dist/utils/worktree.d.ts +23 -0
  73. package/dist/utils/worktree.d.ts.map +1 -0
  74. package/dist/utils/worktree.js +135 -0
  75. package/package.json +2 -2
  76. package/src/cli.tsx +36 -59
  77. package/src/components/App.tsx +99 -11
  78. package/src/components/BackgroundTaskManager.tsx +12 -20
  79. package/src/components/{CommandOutputDisplay.tsx → BangDisplay.tsx} +4 -4
  80. package/src/components/ChatInterface.tsx +8 -0
  81. package/src/components/CommandSelector.tsx +18 -1
  82. package/src/components/ConfirmationSelector.tsx +118 -9
  83. package/src/components/HelpView.tsx +2 -0
  84. package/src/components/HistorySearch.tsx +25 -30
  85. package/src/components/InputBox.tsx +11 -1
  86. package/src/components/MarketplaceAddForm.tsx +21 -8
  87. package/src/components/MarketplaceDetail.tsx +19 -4
  88. package/src/components/MessageBlockItem.tsx +3 -3
  89. package/src/components/MessageList.tsx +47 -23
  90. package/src/components/PluginDetail.tsx +30 -6
  91. package/src/components/PluginManagerShell.tsx +24 -6
  92. package/src/components/PluginManagerTypes.ts +2 -0
  93. package/src/components/SessionSelector.tsx +38 -24
  94. package/src/components/StatusCommand.tsx +94 -0
  95. package/src/components/WorktreeExitPrompt.tsx +86 -0
  96. package/src/contexts/useChat.tsx +57 -13
  97. package/src/contracts/status.ts +7 -0
  98. package/src/hooks/useInputManager.ts +12 -0
  99. package/src/hooks/usePluginManager.ts +47 -13
  100. package/src/hooks/useTasks.ts +2 -2
  101. package/src/index.ts +71 -12
  102. package/src/managers/InputManager.ts +37 -15
  103. package/src/print-cli.ts +48 -5
  104. package/src/session-selector-cli.tsx +6 -2
  105. package/src/types.ts +11 -0
  106. package/src/utils/worktree.ts +164 -0
  107. package/dist/components/CommandOutputDisplay.d.ts +0 -9
  108. package/dist/components/CommandOutputDisplay.d.ts.map +0 -1
@@ -85,14 +85,11 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
85
85
  if (viewMode === "list") {
86
86
  // List mode navigation
87
87
  if (key.return) {
88
- setSelectedIndex((prev) => {
89
- if (tasks.length > 0 && prev < tasks.length) {
90
- const selectedTask = tasks[prev];
91
- setDetailTaskId(selectedTask.id);
92
- setViewMode("detail");
93
- }
94
- return prev;
95
- });
88
+ if (tasks.length > 0 && selectedIndex < tasks.length) {
89
+ const selectedTask = tasks[selectedIndex];
90
+ setDetailTaskId(selectedTask.id);
91
+ setViewMode("detail");
92
+ }
96
93
  return;
97
94
  }
98
95
 
@@ -102,25 +99,20 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
102
99
  }
103
100
 
104
101
  if (key.upArrow) {
105
- setSelectedIndex((prev) => Math.max(0, prev - 1));
102
+ setSelectedIndex(Math.max(0, selectedIndex - 1));
106
103
  return;
107
104
  }
108
105
 
109
106
  if (key.downArrow) {
110
- setSelectedIndex((prev) => Math.min(tasks.length - 1, prev + 1));
107
+ setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
111
108
  return;
112
109
  }
113
110
 
114
- if (input === "k") {
115
- setSelectedIndex((prev) => {
116
- if (tasks.length > 0 && prev < tasks.length) {
117
- const selectedTask = tasks[prev];
118
- if (selectedTask.status === "running") {
119
- stopTask(selectedTask.id);
120
- }
121
- }
122
- return prev;
123
- });
111
+ if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
112
+ const selectedTask = tasks[selectedIndex];
113
+ if (selectedTask.status === "running") {
114
+ stopTask(selectedTask.id);
115
+ }
124
116
  return;
125
117
  }
126
118
  } else if (viewMode === "detail") {
@@ -1,13 +1,13 @@
1
1
  import React, { useState, useEffect } from "react";
2
2
  import { Box, Text } from "ink";
3
- import type { CommandOutputBlock } from "wave-agent-sdk";
3
+ import type { BangBlock } from "wave-agent-sdk";
4
4
 
5
- interface CommandOutputDisplayProps {
6
- block: CommandOutputBlock;
5
+ interface BangDisplayProps {
6
+ block: BangBlock;
7
7
  isExpanded?: boolean;
8
8
  }
9
9
 
10
- export const CommandOutputDisplay: React.FC<CommandOutputDisplayProps> = ({
10
+ export const BangDisplay: React.FC<BangDisplayProps> = ({
11
11
  block,
12
12
  isExpanded = false,
13
13
  }) => {
@@ -36,8 +36,13 @@ export const ChatInterface: React.FC = () => {
36
36
  handleConfirmationDecision,
37
37
  handleConfirmationCancel: originalHandleConfirmationCancel,
38
38
  setWasLastDetailsTooTall,
39
+ version,
40
+ workdir,
41
+ getModelConfig,
39
42
  } = useChat();
40
43
 
44
+ const model = getModelConfig().model;
45
+
41
46
  const handleDetailsHeightMeasured = useCallback((height: number) => {
42
47
  setDetailsHeight(height);
43
48
  }, []);
@@ -91,6 +96,9 @@ export const ChatInterface: React.FC = () => {
91
96
  messages={messages}
92
97
  isExpanded={isExpanded}
93
98
  hideDynamicBlocks={isConfirmationVisible}
99
+ version={version}
100
+ workdir={workdir}
101
+ model={model}
94
102
  />
95
103
 
96
104
  {(isLoading || isCommandRunning || isCompressing) &&
@@ -3,6 +3,12 @@ import { Box, Text, useInput } from "ink";
3
3
  import type { SlashCommand } from "wave-agent-sdk";
4
4
 
5
5
  const AVAILABLE_COMMANDS: SlashCommand[] = [
6
+ {
7
+ id: "clear",
8
+ name: "clear",
9
+ description: "Clear the chat session and terminal",
10
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
11
+ },
6
12
  {
7
13
  id: "tasks",
8
14
  name: "tasks",
@@ -28,6 +34,12 @@ const AVAILABLE_COMMANDS: SlashCommand[] = [
28
34
  description: "Show help and key bindings",
29
35
  handler: () => {}, // Handler here won't be used, actual processing is in the hook
30
36
  },
37
+ {
38
+ id: "status",
39
+ name: "status",
40
+ description: "Show agent status and configuration",
41
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
42
+ },
31
43
  ];
32
44
 
33
45
  export interface CommandSelectorProps {
@@ -48,8 +60,13 @@ export const CommandSelector: React.FC<CommandSelectorProps> = ({
48
60
  const MAX_VISIBLE_ITEMS = 3;
49
61
  const [selectedIndex, setSelectedIndex] = useState(0);
50
62
 
63
+ // Reset selected index when search query changes
64
+ React.useEffect(() => {
65
+ setSelectedIndex(0);
66
+ }, [searchQuery]);
67
+
51
68
  // Merge agent commands and local commands
52
- const allCommands = [...commands, ...AVAILABLE_COMMANDS];
69
+ const allCommands = [...AVAILABLE_COMMANDS, ...commands];
53
70
 
54
71
  // Filter command list
55
72
  const filteredCommands = allCommands.filter(
@@ -70,6 +70,15 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
70
70
  userAnswers: {} as Record<string, string>,
71
71
  otherText: "",
72
72
  otherCursorPosition: 0,
73
+ savedStates: {} as Record<
74
+ number,
75
+ {
76
+ selectedOptionIndex: number;
77
+ selectedOptionIndices: Set<number>;
78
+ otherText: string;
79
+ otherCursorPosition: number;
80
+ }
81
+ >,
73
82
  });
74
83
 
75
84
  const questions =
@@ -133,23 +142,75 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
133
142
  };
134
143
 
135
144
  if (prev.currentQuestionIndex < questions.length - 1) {
136
- return {
137
- ...prev,
138
- currentQuestionIndex: prev.currentQuestionIndex + 1,
145
+ const nextIndex = prev.currentQuestionIndex + 1;
146
+ const savedStates = {
147
+ ...prev.savedStates,
148
+ [prev.currentQuestionIndex]: {
149
+ selectedOptionIndex: prev.selectedOptionIndex,
150
+ selectedOptionIndices: prev.selectedOptionIndices,
151
+ otherText: prev.otherText,
152
+ otherCursorPosition: prev.otherCursorPosition,
153
+ },
154
+ };
155
+
156
+ const nextState = savedStates[nextIndex] || {
139
157
  selectedOptionIndex: 0,
140
- selectedOptionIndices: new Set(),
141
- userAnswers: newAnswers,
158
+ selectedOptionIndices: new Set<number>(),
142
159
  otherText: "",
143
160
  otherCursorPosition: 0,
144
161
  };
162
+
163
+ return {
164
+ ...prev,
165
+ currentQuestionIndex: nextIndex,
166
+ ...nextState,
167
+ userAnswers: newAnswers,
168
+ savedStates,
169
+ };
145
170
  } else {
171
+ const finalAnswers = { ...newAnswers };
172
+ // Also collect from savedStates for any questions that were skipped via Tab
173
+ for (const [idxStr, s] of Object.entries(prev.savedStates)) {
174
+ const idx = parseInt(idxStr);
175
+ const q = questions[idx];
176
+ if (q && !finalAnswers[q.question]) {
177
+ const opts = [...q.options, { label: "Other" }];
178
+ let a = "";
179
+ if (q.multiSelect) {
180
+ const selectedLabels = Array.from(s.selectedOptionIndices)
181
+ .filter((i) => i < q.options.length)
182
+ .map((i) => q.options[i].label);
183
+ const isOtherChecked = s.selectedOptionIndices.has(
184
+ opts.length - 1,
185
+ );
186
+ if (isOtherChecked && s.otherText.trim()) {
187
+ selectedLabels.push(s.otherText.trim());
188
+ }
189
+ a = selectedLabels.join(", ");
190
+ } else {
191
+ if (s.selectedOptionIndex === opts.length - 1) {
192
+ a = s.otherText.trim();
193
+ } else {
194
+ a = opts[s.selectedOptionIndex].label;
195
+ }
196
+ }
197
+ if (a) finalAnswers[q.question] = a;
198
+ }
199
+ }
200
+
201
+ // Only submit if all questions have been answered
202
+ const allAnswered = questions.every(
203
+ (q) => finalAnswers[q.question],
204
+ );
205
+ if (!allAnswered) return prev;
206
+
146
207
  onDecision({
147
208
  behavior: "allow",
148
- message: JSON.stringify(newAnswers),
209
+ message: JSON.stringify(finalAnswers),
149
210
  });
150
211
  return {
151
212
  ...prev,
152
- userAnswers: newAnswers,
213
+ userAnswers: finalAnswers,
153
214
  };
154
215
  }
155
216
  });
@@ -196,6 +257,41 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
196
257
  }));
197
258
  return;
198
259
  }
260
+ if (key.tab) {
261
+ setQuestionState((prev) => {
262
+ const direction = key.shift ? -1 : 1;
263
+ let nextIndex = prev.currentQuestionIndex + direction;
264
+ if (nextIndex < 0) nextIndex = questions.length - 1;
265
+ if (nextIndex >= questions.length) nextIndex = 0;
266
+
267
+ if (nextIndex === prev.currentQuestionIndex) return prev;
268
+
269
+ const savedStates = {
270
+ ...prev.savedStates,
271
+ [prev.currentQuestionIndex]: {
272
+ selectedOptionIndex: prev.selectedOptionIndex,
273
+ selectedOptionIndices: prev.selectedOptionIndices,
274
+ otherText: prev.otherText,
275
+ otherCursorPosition: prev.otherCursorPosition,
276
+ },
277
+ };
278
+
279
+ const nextState = savedStates[nextIndex] || {
280
+ selectedOptionIndex: 0,
281
+ selectedOptionIndices: new Set<number>(),
282
+ otherText: "",
283
+ otherCursorPosition: 0,
284
+ };
285
+
286
+ return {
287
+ ...prev,
288
+ currentQuestionIndex: nextIndex,
289
+ ...nextState,
290
+ savedStates,
291
+ };
292
+ });
293
+ return;
294
+ }
199
295
 
200
296
  setQuestionState((prev) => {
201
297
  const isOtherFocused = prev.selectedOptionIndex === options.length - 1;
@@ -321,6 +417,19 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
321
417
  return;
322
418
  }
323
419
 
420
+ if (key.tab) {
421
+ const currentIndex = availableOptions.indexOf(state.selectedOption);
422
+ const direction = key.shift ? -1 : 1;
423
+ let nextIndex = currentIndex + direction;
424
+ if (nextIndex < 0) nextIndex = availableOptions.length - 1;
425
+ if (nextIndex >= availableOptions.length) nextIndex = 0;
426
+ setState((prev) => ({
427
+ ...prev,
428
+ selectedOption: availableOptions[nextIndex],
429
+ }));
430
+ return;
431
+ }
432
+
324
433
  if (input && !key.ctrl && !key.meta && !("alt" in key && key.alt)) {
325
434
  setState((prev) => {
326
435
  const nextText =
@@ -436,7 +545,7 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
436
545
  Question {questionState.currentQuestionIndex + 1} of{" "}
437
546
  {questions.length} •
438
547
  {currentQuestion.multiSelect ? " Space to toggle •" : ""} Use ↑↓
439
- to navigate • Enter to confirm
548
+ or Tab to navigate • Enter to confirm
440
549
  </Text>
441
550
  </Box>
442
551
  </Box>
@@ -531,7 +640,7 @@ export const ConfirmationSelector: React.FC<ConfirmationSelectorProps> = ({
531
640
  </Box>
532
641
  </Box>
533
642
  <Box marginTop={1}>
534
- <Text dimColor>Use ↑↓ to navigate • ESC to cancel</Text>
643
+ <Text dimColor>Use ↑↓ or Tab to navigate • ESC to cancel</Text>
535
644
  </Box>
536
645
  </>
537
646
  )}
@@ -21,6 +21,8 @@ export const HelpView: React.FC<HelpViewProps> = ({ onCancel }) => {
21
21
  { key: "Ctrl+B", description: "Background current task" },
22
22
  { key: "Ctrl+V", description: "Paste image" },
23
23
  { key: "Shift+Tab", description: "Cycle permission mode" },
24
+ { key: "/status", description: "Show agent status and configuration" },
25
+ { key: "/clear", description: "Clear the chat session and terminal" },
24
26
  {
25
27
  key: "Esc",
26
28
  description: "Interrupt AI or command / Cancel selector / Close help",
@@ -14,34 +14,38 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
14
14
  onCancel,
15
15
  }) => {
16
16
  const MAX_VISIBLE_ITEMS = 5;
17
- const [state, setState] = useState({
18
- selectedIndex: 0,
19
- entries: [] as PromptEntry[],
20
- });
17
+ const [selectedIndex, setSelectedIndex] = useState(0);
18
+ const [entries, setEntries] = useState<PromptEntry[]>([]);
19
+
20
+ const entriesRef = React.useRef<PromptEntry[]>([]);
21
+ const selectedIndexRef = React.useRef(0);
22
+
23
+ useEffect(() => {
24
+ entriesRef.current = entries;
25
+ }, [entries]);
26
+
27
+ useEffect(() => {
28
+ selectedIndexRef.current = selectedIndex;
29
+ }, [selectedIndex]);
21
30
 
22
31
  useEffect(() => {
23
32
  const fetchHistory = async () => {
24
33
  const results = await PromptHistoryManager.searchHistory(searchQuery);
25
34
  const limitedResults = results.slice(0, 20);
26
- setState({
27
- entries: limitedResults,
28
- selectedIndex: 0,
29
- });
35
+ setEntries(limitedResults); // Limit to 20 results
36
+ setSelectedIndex(0);
30
37
  };
31
38
  fetchHistory();
32
39
  }, [searchQuery]);
33
40
 
34
41
  useInput((input, key) => {
35
42
  if (key.return) {
36
- setState((prev) => {
37
- if (
38
- prev.entries.length > 0 &&
39
- prev.selectedIndex < prev.entries.length
40
- ) {
41
- onSelect(prev.entries[prev.selectedIndex].prompt);
42
- }
43
- return prev;
44
- });
43
+ if (
44
+ entriesRef.current.length > 0 &&
45
+ selectedIndexRef.current < entriesRef.current.length
46
+ ) {
47
+ onSelect(entriesRef.current[selectedIndexRef.current].prompt);
48
+ }
45
49
  return;
46
50
  }
47
51
 
@@ -51,27 +55,18 @@ export const HistorySearch: React.FC<HistorySearchProps> = ({
51
55
  }
52
56
 
53
57
  if (key.upArrow) {
54
- setState((prev) => ({
55
- ...prev,
56
- selectedIndex: Math.max(0, prev.selectedIndex - 1),
57
- }));
58
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
58
59
  return;
59
60
  }
60
61
 
61
62
  if (key.downArrow) {
62
- setState((prev) => ({
63
- ...prev,
64
- selectedIndex: Math.min(
65
- prev.entries.length - 1,
66
- prev.selectedIndex + 1,
67
- ),
68
- }));
63
+ setSelectedIndex((prev) =>
64
+ Math.min(entriesRef.current.length - 1, prev + 1),
65
+ );
69
66
  return;
70
67
  }
71
68
  });
72
69
 
73
- const { entries, selectedIndex } = state;
74
-
75
70
  if (entries.length === 0) {
76
71
  return (
77
72
  <Box
@@ -8,6 +8,7 @@ import { BackgroundTaskManager } from "./BackgroundTaskManager.js";
8
8
  import { McpManager } from "./McpManager.js";
9
9
  import { RewindCommand } from "./RewindCommand.js";
10
10
  import { HelpView } from "./HelpView.js";
11
+ import { StatusCommand } from "./StatusCommand.js";
11
12
  import { useInputManager } from "../hooks/useInputManager.js";
12
13
  import { useChat } from "../contexts/useChat.js";
13
14
 
@@ -57,6 +58,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
57
58
  backgroundCurrentTask,
58
59
  messages,
59
60
  getFullMessageThread,
61
+ clearMessages,
60
62
  } = useChat();
61
63
 
62
64
  // Input manager with all input state and functionality (including images)
@@ -88,10 +90,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
88
90
  showMcpManager,
89
91
  showRewindManager,
90
92
  showHelp,
93
+ showStatusCommand,
91
94
  setShowBackgroundTaskManager,
92
95
  setShowMcpManager,
93
96
  setShowRewindManager,
94
97
  setShowHelp,
98
+ setShowStatusCommand,
95
99
  // Permission mode
96
100
  permissionMode,
97
101
  setPermissionMode,
@@ -105,6 +109,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
105
109
  onAbortMessage: abortMessage,
106
110
  onBackgroundCurrentTask: backgroundCurrentTask,
107
111
  onPermissionModeChange: setChatPermissionMode,
112
+ onClearMessages: clearMessages,
108
113
  });
109
114
 
110
115
  // Sync permission mode from useChat to InputManager
@@ -172,6 +177,10 @@ export const InputBox: React.FC<InputBoxProps> = ({
172
177
  return <HelpView onCancel={() => setShowHelp(false)} />;
173
178
  }
174
179
 
180
+ if (showStatusCommand) {
181
+ return <StatusCommand onCancel={() => setShowStatusCommand(false)} />;
182
+ }
183
+
175
184
  return (
176
185
  <Box flexDirection="column">
177
186
  {showFileSelector && (
@@ -219,7 +228,8 @@ export const InputBox: React.FC<InputBoxProps> = ({
219
228
  {showBackgroundTaskManager ||
220
229
  showMcpManager ||
221
230
  showRewindManager ||
222
- showHelp || (
231
+ showHelp ||
232
+ showStatusCommand || (
223
233
  <Box flexDirection="column">
224
234
  <Box
225
235
  borderStyle="single"
@@ -3,17 +3,21 @@ import { Box, Text, useInput } from "ink";
3
3
  import { usePluginManagerContext } from "../contexts/PluginManagerContext.js";
4
4
 
5
5
  export const MarketplaceAddForm: React.FC = () => {
6
- const { actions } = usePluginManagerContext();
6
+ const { state, actions } = usePluginManagerContext();
7
7
  const [source, setSource] = useState("");
8
8
 
9
9
  useInput((input, key) => {
10
10
  if (key.escape) {
11
11
  actions.setView("MARKETPLACES");
12
+ } else if (state.isLoading) {
13
+ return;
12
14
  } else if (key.return) {
13
- if (source.trim()) {
14
- actions.addMarketplace(source.trim());
15
- actions.setView("MARKETPLACES");
16
- }
15
+ setSource((prev) => {
16
+ if (prev.trim()) {
17
+ actions.addMarketplace(prev.trim());
18
+ }
19
+ return prev;
20
+ });
17
21
  } else if (key.backspace || key.delete) {
18
22
  setSource((prev) => prev.slice(0, -1));
19
23
  } else if (input.length === 1) {
@@ -28,11 +32,20 @@ export const MarketplaceAddForm: React.FC = () => {
28
32
  </Text>
29
33
  <Box marginTop={1}>
30
34
  <Text>Source (URL or Path): </Text>
31
- <Text color="yellow">{source}</Text>
32
- <Text color="yellow">_</Text>
35
+ <Text color={state.isLoading ? "gray" : "yellow"}>{source}</Text>
36
+ {!state.isLoading && <Text color="yellow">_</Text>}
33
37
  </Box>
38
+ {state.isLoading && (
39
+ <Box marginTop={1}>
40
+ <Text color="yellow">⌛ Adding marketplace...</Text>
41
+ </Box>
42
+ )}
34
43
  <Box marginTop={1}>
35
- <Text dimColor>Press Enter to add, Esc to cancel</Text>
44
+ <Text dimColor>
45
+ {state.isLoading
46
+ ? "Please wait..."
47
+ : "Press Enter to add, Esc to cancel"}
48
+ </Text>
36
49
  </Box>
37
50
  </Box>
38
51
  );
@@ -24,14 +24,13 @@ export const MarketplaceDetail: React.FC = () => {
24
24
  setSelectedActionIndex((prev) =>
25
25
  prev < ACTIONS.length - 1 ? prev + 1 : 0,
26
26
  );
27
- } else if (key.return && marketplace) {
27
+ } else if (key.return && marketplace && !state.isLoading) {
28
28
  const action = ACTIONS[selectedActionIndex].id;
29
29
  if (action === "update") {
30
30
  actions.updateMarketplace(marketplace.name);
31
31
  } else {
32
32
  actions.removeMarketplace(marketplace.name);
33
33
  }
34
- actions.setView("MARKETPLACES");
35
34
  }
36
35
  });
37
36
 
@@ -56,19 +55,35 @@ export const MarketplaceDetail: React.FC = () => {
56
55
  <Text>Source: {JSON.stringify(marketplace.source)}</Text>
57
56
  </Box>
58
57
 
58
+ {state.isLoading && (
59
+ <Box marginBottom={1}>
60
+ <Text color="yellow">⌛ Processing operation...</Text>
61
+ </Box>
62
+ )}
63
+
59
64
  <Box marginTop={1} flexDirection="column">
60
65
  <Text bold>Marketplace Actions:</Text>
61
66
  {ACTIONS.map((action, index) => (
62
67
  <Text
63
68
  key={action.id}
64
- color={index === selectedActionIndex ? "yellow" : undefined}
69
+ color={
70
+ index === selectedActionIndex
71
+ ? state.isLoading
72
+ ? "gray"
73
+ : "yellow"
74
+ : undefined
75
+ }
65
76
  >
66
77
  {index === selectedActionIndex ? "> " : " "}
67
78
  {action.label}
68
79
  </Text>
69
80
  ))}
70
81
  <Box marginTop={1}>
71
- <Text dimColor>Use ↑/↓ to select, Enter to confirm</Text>
82
+ <Text dimColor>
83
+ {state.isLoading
84
+ ? "Please wait..."
85
+ : "Use ↑/↓ to select, Enter to confirm"}
86
+ </Text>
72
87
  </Box>
73
88
  <Box marginTop={1}>
74
89
  <Text dimColor>Press Esc to go back</Text>
@@ -2,7 +2,7 @@ import React from "react";
2
2
  import { Box, Text } from "ink";
3
3
  import type { Message, MessageBlock } from "wave-agent-sdk";
4
4
  import { MessageSource } from "wave-agent-sdk";
5
- import { CommandOutputDisplay } from "./CommandOutputDisplay.js";
5
+ import { BangDisplay } from "./BangDisplay.js";
6
6
  import { ToolDisplay } from "./ToolDisplay.js";
7
7
  import { CompressDisplay } from "./CompressDisplay.js";
8
8
  import { ReasoningDisplay } from "./ReasoningDisplay.js";
@@ -51,8 +51,8 @@ export const MessageBlockItem = ({
51
51
  </Box>
52
52
  )}
53
53
 
54
- {block.type === "command_output" && (
55
- <CommandOutputDisplay block={block} isExpanded={isExpanded} />
54
+ {block.type === "bang" && (
55
+ <BangDisplay block={block} isExpanded={isExpanded} />
56
56
  )}
57
57
 
58
58
  {block.type === "tool" && (