wave-code 0.0.16 → 0.0.17

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 (90) hide show
  1. package/dist/cli.d.ts +1 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +2 -2
  4. package/dist/commands/plugin/disable.d.ts +5 -0
  5. package/dist/commands/plugin/disable.d.ts.map +1 -0
  6. package/dist/commands/plugin/disable.js +21 -0
  7. package/dist/commands/plugin/enable.d.ts +5 -0
  8. package/dist/commands/plugin/enable.d.ts.map +1 -0
  9. package/dist/commands/plugin/enable.js +21 -0
  10. package/dist/commands/plugin/install.d.ts +5 -0
  11. package/dist/commands/plugin/install.d.ts.map +1 -0
  12. package/dist/commands/plugin/install.js +28 -0
  13. package/dist/commands/plugin/list.d.ts +2 -0
  14. package/dist/commands/plugin/list.d.ts.map +1 -0
  15. package/dist/commands/plugin/list.js +53 -0
  16. package/dist/commands/plugin/marketplace.d.ts +8 -0
  17. package/dist/commands/plugin/marketplace.d.ts.map +1 -0
  18. package/dist/commands/plugin/marketplace.js +73 -0
  19. package/dist/components/App.d.ts +1 -0
  20. package/dist/components/App.d.ts.map +1 -1
  21. package/dist/components/App.js +4 -4
  22. package/dist/components/BashHistorySelector.d.ts +1 -0
  23. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  24. package/dist/components/BashHistorySelector.js +15 -5
  25. package/dist/components/BashShellManager.d.ts.map +1 -1
  26. package/dist/components/BashShellManager.js +4 -4
  27. package/dist/components/ChatInterface.d.ts.map +1 -1
  28. package/dist/components/ChatInterface.js +1 -2
  29. package/dist/components/CommandSelector.d.ts.map +1 -1
  30. package/dist/components/CommandSelector.js +2 -2
  31. package/dist/components/Confirmation.d.ts +1 -0
  32. package/dist/components/Confirmation.d.ts.map +1 -1
  33. package/dist/components/Confirmation.js +151 -48
  34. package/dist/components/DiffDisplay.d.ts +3 -2
  35. package/dist/components/DiffDisplay.d.ts.map +1 -1
  36. package/dist/components/DiffDisplay.js +87 -82
  37. package/dist/components/FileSelector.d.ts.map +1 -1
  38. package/dist/components/FileSelector.js +2 -2
  39. package/dist/components/InputBox.d.ts.map +1 -1
  40. package/dist/components/InputBox.js +2 -2
  41. package/dist/components/McpManager.d.ts.map +1 -1
  42. package/dist/components/McpManager.js +3 -3
  43. package/dist/components/MemoryTypeSelector.d.ts.map +1 -1
  44. package/dist/components/MemoryTypeSelector.js +1 -1
  45. package/dist/components/MessageList.js +1 -1
  46. package/dist/components/PlanDisplay.d.ts +8 -0
  47. package/dist/components/PlanDisplay.d.ts.map +1 -0
  48. package/dist/components/PlanDisplay.js +14 -0
  49. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  50. package/dist/components/ToolResultDisplay.js +1 -1
  51. package/dist/contexts/useChat.d.ts +1 -0
  52. package/dist/contexts/useChat.d.ts.map +1 -1
  53. package/dist/contexts/useChat.js +3 -1
  54. package/dist/hooks/useInputManager.d.ts +1 -0
  55. package/dist/hooks/useInputManager.d.ts.map +1 -1
  56. package/dist/hooks/useInputManager.js +4 -0
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +103 -0
  59. package/dist/managers/InputManager.d.ts +1 -0
  60. package/dist/managers/InputManager.d.ts.map +1 -1
  61. package/dist/managers/InputManager.js +7 -2
  62. package/dist/print-cli.d.ts +1 -0
  63. package/dist/print-cli.d.ts.map +1 -1
  64. package/dist/print-cli.js +2 -1
  65. package/package.json +2 -2
  66. package/src/cli.tsx +8 -1
  67. package/src/commands/plugin/disable.ts +31 -0
  68. package/src/commands/plugin/enable.ts +31 -0
  69. package/src/commands/plugin/install.ts +42 -0
  70. package/src/commands/plugin/list.ts +64 -0
  71. package/src/commands/plugin/marketplace.ts +72 -0
  72. package/src/components/App.tsx +11 -5
  73. package/src/components/BashHistorySelector.tsx +25 -7
  74. package/src/components/BashShellManager.tsx +16 -8
  75. package/src/components/ChatInterface.tsx +29 -27
  76. package/src/components/CommandSelector.tsx +8 -4
  77. package/src/components/Confirmation.tsx +312 -106
  78. package/src/components/DiffDisplay.tsx +167 -149
  79. package/src/components/FileSelector.tsx +8 -4
  80. package/src/components/InputBox.tsx +14 -4
  81. package/src/components/McpManager.tsx +12 -6
  82. package/src/components/MemoryTypeSelector.tsx +4 -2
  83. package/src/components/MessageList.tsx +1 -1
  84. package/src/components/PlanDisplay.tsx +46 -0
  85. package/src/components/ToolResultDisplay.tsx +4 -2
  86. package/src/contexts/useChat.tsx +4 -0
  87. package/src/hooks/useInputManager.ts +8 -0
  88. package/src/index.ts +178 -0
  89. package/src/managers/InputManager.ts +12 -1
  90. package/src/print-cli.ts +3 -0
@@ -1,30 +1,44 @@
1
1
  import React, { useMemo } from "react";
2
- import { Box, Text } from "ink";
2
+ import { Box, Text, useStdout } from "ink";
3
+ import {
4
+ WRITE_TOOL_NAME,
5
+ EDIT_TOOL_NAME,
6
+ MULTI_EDIT_TOOL_NAME,
7
+ } from "wave-agent-sdk";
3
8
  import { transformToolBlockToChanges } from "../utils/toolParameterTransforms.js";
4
9
  import { diffLines, diffWords } from "diff";
5
- import type { ToolBlock } from "wave-agent-sdk";
6
10
 
7
11
  interface DiffDisplayProps {
8
- toolBlock: ToolBlock;
12
+ toolName?: string;
13
+ parameters?: string;
14
+ isExpanded?: boolean;
9
15
  }
10
16
 
11
- export const DiffDisplay: React.FC<DiffDisplayProps> = ({ toolBlock }) => {
17
+ export const DiffDisplay: React.FC<DiffDisplayProps> = ({
18
+ toolName,
19
+ parameters,
20
+ isExpanded = false,
21
+ }) => {
22
+ const { stdout } = useStdout();
23
+ const maxHeight = useMemo(() => {
24
+ return Math.max(5, (stdout?.rows || 24) - 20);
25
+ }, [stdout?.rows]);
26
+
12
27
  const showDiff =
13
- ["running", "end"].includes(toolBlock.stage) &&
14
- toolBlock.name &&
15
- ["Write", "Edit", "MultiEdit"].includes(toolBlock.name);
28
+ toolName &&
29
+ [WRITE_TOOL_NAME, EDIT_TOOL_NAME, MULTI_EDIT_TOOL_NAME].includes(toolName);
16
30
 
17
31
  // Diff detection and transformation using typed parameters
18
32
  const changes = useMemo(() => {
19
- if (!showDiff || !toolBlock.name || !toolBlock.parameters) return [];
33
+ if (!showDiff || !toolName || !parameters) return [];
20
34
  try {
21
35
  // Use local transformation with JSON parsing and type guards
22
- return transformToolBlockToChanges(toolBlock.name, toolBlock.parameters);
36
+ return transformToolBlockToChanges(toolName, parameters);
23
37
  } catch (error) {
24
38
  console.warn("Error transforming tool block to changes:", error);
25
39
  return [];
26
40
  }
27
- }, [toolBlock.name, toolBlock.parameters, showDiff]);
41
+ }, [toolName, parameters, showDiff]);
28
42
 
29
43
  // Render word-level diff between two lines of text
30
44
  const renderWordLevelDiff = (
@@ -98,158 +112,162 @@ export const DiffDisplay: React.FC<DiffDisplayProps> = ({ toolBlock }) => {
98
112
  try {
99
113
  if (changes.length === 0) return null;
100
114
 
101
- return (
102
- <Box flexDirection="column">
103
- {changes.map((change, changeIndex) => {
104
- try {
105
- // Get line-level diff to understand the structure
106
- const lineDiffs = diffLines(
107
- change.oldContent || "",
108
- change.newContent || "",
109
- );
115
+ const allElements: React.ReactNode[] = [];
116
+
117
+ changes.forEach((change, changeIndex) => {
118
+ try {
119
+ // Get line-level diff to understand the structure
120
+ const lineDiffs = diffLines(
121
+ change.oldContent || "",
122
+ change.newContent || "",
123
+ );
110
124
 
111
- const diffElements: React.ReactNode[] = [];
125
+ const diffElements: React.ReactNode[] = [];
112
126
 
113
- // Process line diffs and apply word-level diff to changed lines
114
- lineDiffs.forEach((part, partIndex) => {
115
- if (part.added) {
116
- const lines = part.value
117
- .split("\n")
118
- .filter((line) => line !== "");
119
- lines.forEach((line, lineIndex) => {
120
- diffElements.push(
121
- <Box
122
- key={`add-${changeIndex}-${partIndex}-${lineIndex}`}
123
- flexDirection="row"
124
- >
125
- <Text color="green">+</Text>
126
- <Text color="green">{line}</Text>
127
- </Box>,
128
- );
129
- });
130
- } else if (part.removed) {
131
- const lines = part.value
132
- .split("\n")
133
- .filter((line) => line !== "");
134
- lines.forEach((line, lineIndex) => {
135
- diffElements.push(
136
- <Box
137
- key={`remove-${changeIndex}-${partIndex}-${lineIndex}`}
138
- flexDirection="row"
139
- >
140
- <Text color="red">-</Text>
141
- <Text color="red">{line}</Text>
142
- </Box>,
143
- );
144
- });
145
- } else {
146
- // Context lines - show unchanged content
147
- const lines = part.value
148
- .split("\n")
149
- .filter((line) => line !== "");
150
- lines.forEach((line, lineIndex) => {
151
- diffElements.push(
152
- <Box
153
- key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
154
- flexDirection="row"
155
- >
156
- <Text color="white"> </Text>
157
- <Text color="white">{line}</Text>
158
- </Box>,
159
- );
160
- });
161
- }
127
+ // Process line diffs and apply word-level diff to changed lines
128
+ lineDiffs.forEach((part, partIndex) => {
129
+ if (part.added) {
130
+ const lines = part.value
131
+ .split("\n")
132
+ .filter((line) => line !== "");
133
+ lines.forEach((line, lineIndex) => {
134
+ diffElements.push(
135
+ <Box
136
+ key={`add-${changeIndex}-${partIndex}-${lineIndex}`}
137
+ flexDirection="row"
138
+ >
139
+ <Text color="green">+</Text>
140
+ <Text color="green">{line}</Text>
141
+ </Box>,
142
+ );
162
143
  });
144
+ } else if (part.removed) {
145
+ const lines = part.value
146
+ .split("\n")
147
+ .filter((line) => line !== "");
148
+ lines.forEach((line, lineIndex) => {
149
+ diffElements.push(
150
+ <Box
151
+ key={`remove-${changeIndex}-${partIndex}-${lineIndex}`}
152
+ flexDirection="row"
153
+ >
154
+ <Text color="red">-</Text>
155
+ <Text color="red">{line}</Text>
156
+ </Box>,
157
+ );
158
+ });
159
+ } else {
160
+ // Context lines - show unchanged content
161
+ const lines = part.value
162
+ .split("\n")
163
+ .filter((line) => line !== "");
164
+ lines.forEach((line, lineIndex) => {
165
+ diffElements.push(
166
+ <Box
167
+ key={`context-${changeIndex}-${partIndex}-${lineIndex}`}
168
+ flexDirection="row"
169
+ >
170
+ <Text color="white"> </Text>
171
+ <Text color="white">{line}</Text>
172
+ </Box>,
173
+ );
174
+ });
175
+ }
176
+ });
163
177
 
164
- // Now look for pairs of removed/added lines that can be word-diffed
165
- const processedElements: React.ReactNode[] = [];
166
- let i = 0;
178
+ // Now look for pairs of removed/added lines that can be word-diffed
179
+ let i = 0;
167
180
 
168
- while (i < diffElements.length) {
169
- const current = diffElements[i];
170
- const next =
171
- i + 1 < diffElements.length ? diffElements[i + 1] : null;
181
+ while (i < diffElements.length) {
182
+ const current = diffElements[i];
183
+ const next =
184
+ i + 1 < diffElements.length ? diffElements[i + 1] : null;
172
185
 
173
- // Check if we have a removed line followed by an added line
174
- const currentKey = React.isValidElement(current)
175
- ? current.key
176
- : "";
177
- const nextKey = React.isValidElement(next) ? next.key : "";
186
+ // Check if we have a removed line followed by an added line
187
+ const currentKey = React.isValidElement(current) ? current.key : "";
188
+ const nextKey = React.isValidElement(next) ? next.key : "";
178
189
 
179
- const isCurrentRemoved =
180
- typeof currentKey === "string" &&
181
- currentKey.includes("remove-");
182
- const isNextAdded =
183
- typeof nextKey === "string" && nextKey.includes("add-");
190
+ const isCurrentRemoved =
191
+ typeof currentKey === "string" && currentKey.includes("remove-");
192
+ const isNextAdded =
193
+ typeof nextKey === "string" && nextKey.includes("add-");
184
194
 
185
- if (
186
- isCurrentRemoved &&
187
- isNextAdded &&
188
- React.isValidElement(current) &&
189
- React.isValidElement(next)
190
- ) {
191
- // Extract the text content from the removed and added lines
192
- const removedText = extractTextFromElement(current);
193
- const addedText = extractTextFromElement(next);
195
+ if (
196
+ isCurrentRemoved &&
197
+ isNextAdded &&
198
+ React.isValidElement(current) &&
199
+ React.isValidElement(next)
200
+ ) {
201
+ // Extract the text content from the removed and added lines
202
+ const removedText = extractTextFromElement(current);
203
+ const addedText = extractTextFromElement(next);
194
204
 
195
- if (removedText && addedText) {
196
- // Apply word-level diff
197
- const { removedParts, addedParts } = renderWordLevelDiff(
198
- removedText,
199
- addedText,
200
- `word-${changeIndex}-${i}`,
201
- );
205
+ if (removedText && addedText) {
206
+ // Apply word-level diff
207
+ const { removedParts, addedParts } = renderWordLevelDiff(
208
+ removedText,
209
+ addedText,
210
+ `word-${changeIndex}-${i}`,
211
+ );
202
212
 
203
- processedElements.push(
204
- <Box
205
- key={`word-diff-removed-${changeIndex}-${i}`}
206
- flexDirection="row"
207
- >
208
- <Text color="red">-</Text>
209
- {removedParts}
210
- </Box>,
211
- );
212
- processedElements.push(
213
- <Box
214
- key={`word-diff-added-${changeIndex}-${i}`}
215
- flexDirection="row"
216
- >
217
- <Text color="green">+</Text>
218
- {addedParts}
219
- </Box>,
220
- );
213
+ allElements.push(
214
+ <Box
215
+ key={`word-diff-removed-${changeIndex}-${i}`}
216
+ flexDirection="row"
217
+ >
218
+ <Text color="red">-</Text>
219
+ {removedParts}
220
+ </Box>,
221
+ );
222
+ allElements.push(
223
+ <Box
224
+ key={`word-diff-added-${changeIndex}-${i}`}
225
+ flexDirection="row"
226
+ >
227
+ <Text color="green">+</Text>
228
+ {addedParts}
229
+ </Box>,
230
+ );
221
231
 
222
- i += 2; // Skip the next element since we processed it
223
- } else {
224
- // Fallback to original elements
225
- processedElements.push(current);
226
- i += 1;
227
- }
228
- } else {
229
- processedElements.push(current);
230
- i += 1;
231
- }
232
+ i += 2; // Skip the next element since we processed it
233
+ } else {
234
+ // Fallback to original elements
235
+ allElements.push(current);
236
+ i += 1;
232
237
  }
233
-
234
- return (
235
- <Box key={changeIndex} flexDirection="column">
236
- {processedElements}
237
- </Box>
238
- );
239
- } catch (error) {
240
- console.warn(
241
- `Error rendering diff for change ${changeIndex}:`,
242
- error,
243
- );
244
- // Fallback to simple display
245
- return (
246
- <Box key={changeIndex} flexDirection="column">
247
- <Text color="red">-{change.oldContent || ""}</Text>
248
- <Text color="green">+{change.newContent || ""}</Text>
249
- </Box>
250
- );
238
+ } else {
239
+ allElements.push(current);
240
+ i += 1;
251
241
  }
252
- })}
242
+ }
243
+ } catch (error) {
244
+ console.warn(
245
+ `Error rendering diff for change ${changeIndex}:`,
246
+ error,
247
+ );
248
+ // Fallback to simple display
249
+ allElements.push(
250
+ <Box key={`fallback-${changeIndex}`} flexDirection="column">
251
+ <Text color="red">-{change.oldContent || ""}</Text>
252
+ <Text color="green">+{change.newContent || ""}</Text>
253
+ </Box>,
254
+ );
255
+ }
256
+ });
257
+
258
+ const isTruncated = !isExpanded && allElements.length > maxHeight;
259
+ const displayElements = isTruncated
260
+ ? allElements.slice(0, maxHeight - 1)
261
+ : allElements;
262
+
263
+ return (
264
+ <Box flexDirection="column">
265
+ {displayElements}
266
+ {isTruncated && (
267
+ <Text color="yellow" dimColor>
268
+ ... (truncated {allElements.length - (maxHeight - 1)} more lines)
269
+ </Text>
270
+ )}
253
271
  </Box>
254
272
  );
255
273
  } catch (error) {
@@ -49,8 +49,10 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
49
49
  flexDirection="column"
50
50
  borderStyle="single"
51
51
  borderColor="yellow"
52
- padding={1}
53
- marginBottom={1}
52
+ borderBottom={false}
53
+ borderLeft={false}
54
+ borderRight={false}
55
+ paddingTop={1}
54
56
  >
55
57
  <Text color="yellow">📁 No files found for "{searchQuery}"</Text>
56
58
  <Text dimColor>Press Escape to cancel</Text>
@@ -86,8 +88,10 @@ export const FileSelector: React.FC<FileSelectorProps> = ({
86
88
  flexDirection="column"
87
89
  borderStyle="single"
88
90
  borderColor="cyan"
89
- padding={1}
90
- marginBottom={1}
91
+ borderBottom={false}
92
+ borderLeft={false}
93
+ borderRight={false}
94
+ paddingTop={1}
91
95
  >
92
96
  <Text color="cyan" bold>
93
97
  📁 Select File/Directory{" "}
@@ -103,6 +103,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
103
103
  setUserInputHistory,
104
104
  // Complex handlers combining multiple operations
105
105
  handleBashHistoryExecuteAndSend,
106
+ handleBashHistoryDelete,
106
107
  // Main handler
107
108
  handleInput,
108
109
  // Manager ready state
@@ -188,6 +189,7 @@ export const InputBox: React.FC<InputBoxProps> = ({
188
189
  workdir={currentWorkdir}
189
190
  onSelect={handleBashHistorySelect}
190
191
  onExecute={handleBashHistoryExecuteAndSend}
192
+ onDelete={handleBashHistoryDelete}
191
193
  onCancel={handleCancelBashHistorySelect}
192
194
  />
193
195
  )}
@@ -214,7 +216,12 @@ export const InputBox: React.FC<InputBoxProps> = ({
214
216
  )}
215
217
  {showBashManager || showMcpManager || (
216
218
  <Box flexDirection="column">
217
- <Box borderStyle="single" borderColor="gray" paddingX={1}>
219
+ <Box
220
+ borderStyle="single"
221
+ borderColor="gray"
222
+ borderLeft={false}
223
+ borderRight={false}
224
+ >
218
225
  <Text color={isPlaceholder ? "gray" : "white"}>
219
226
  {shouldShowCursor ? (
220
227
  <>
@@ -229,10 +236,13 @@ export const InputBox: React.FC<InputBoxProps> = ({
229
236
  )}
230
237
  </Text>
231
238
  </Box>
232
- <Box paddingX={1}>
239
+ <Box paddingRight={1}>
233
240
  <Text color="gray">
234
- Mode: <Text color="cyan">{permissionMode}</Text> (Shift+Tab to
235
- cycle)
241
+ Mode:{" "}
242
+ <Text color={permissionMode === "plan" ? "yellow" : "cyan"}>
243
+ {permissionMode}
244
+ </Text>{" "}
245
+ (Shift+Tab to cycle)
236
246
  </Text>
237
247
  </Box>
238
248
  </Box>
@@ -144,9 +144,11 @@ export const McpManager: React.FC<McpManagerProps> = ({
144
144
  flexDirection="column"
145
145
  borderStyle="single"
146
146
  borderColor="cyan"
147
- padding={1}
147
+ borderBottom={false}
148
+ borderLeft={false}
149
+ borderRight={false}
150
+ paddingTop={1}
148
151
  gap={1}
149
- marginBottom={1}
150
152
  >
151
153
  <Box>
152
154
  <Text color="cyan" bold>
@@ -247,8 +249,10 @@ export const McpManager: React.FC<McpManagerProps> = ({
247
249
  flexDirection="column"
248
250
  borderStyle="single"
249
251
  borderColor="cyan"
250
- padding={1}
251
- marginBottom={1}
252
+ borderBottom={false}
253
+ borderLeft={false}
254
+ borderRight={false}
255
+ paddingTop={1}
252
256
  >
253
257
  <Text color="cyan" bold>
254
258
  Manage MCP servers
@@ -267,9 +271,11 @@ export const McpManager: React.FC<McpManagerProps> = ({
267
271
  flexDirection="column"
268
272
  borderStyle="single"
269
273
  borderColor="cyan"
270
- padding={1}
274
+ borderBottom={false}
275
+ borderLeft={false}
276
+ borderRight={false}
277
+ paddingTop={1}
271
278
  gap={1}
272
- marginBottom={1}
273
279
  >
274
280
  <Box>
275
281
  <Text color="cyan" bold>
@@ -55,9 +55,11 @@ export const MemoryTypeSelector: React.FC<MemoryTypeSelectorProps> = ({
55
55
  flexDirection="column"
56
56
  borderStyle="single"
57
57
  borderColor="green"
58
- padding={1}
58
+ borderBottom={false}
59
+ borderLeft={false}
60
+ borderRight={false}
61
+ paddingTop={1}
59
62
  gap={1}
60
- marginBottom={1}
61
63
  >
62
64
  <Box>
63
65
  <Text color="green" bold>
@@ -52,7 +52,7 @@ export const MessageList = React.memo(
52
52
  : [];
53
53
 
54
54
  return (
55
- <Box flexDirection="column" paddingX={1} gap={1}>
55
+ <Box flexDirection="column" gap={1}>
56
56
  {/* Show omitted message count when limiting */}
57
57
  {omittedCount > 0 && (
58
58
  <Box>
@@ -0,0 +1,46 @@
1
+ import React, { useMemo } from "react";
2
+ import { Box, Text, useStdout } from "ink";
3
+ import { Markdown } from "./Markdown.js";
4
+
5
+ interface PlanDisplayProps {
6
+ plan: string;
7
+ isExpanded?: boolean;
8
+ }
9
+
10
+ export const PlanDisplay: React.FC<PlanDisplayProps> = ({
11
+ plan,
12
+ isExpanded = false,
13
+ }) => {
14
+ const { stdout } = useStdout();
15
+ const maxHeight = useMemo(() => {
16
+ // Similar to DiffDisplay.tsx maxHeight calculation
17
+ return Math.max(5, (stdout?.rows || 24) - 20);
18
+ }, [stdout?.rows]);
19
+
20
+ const lines = useMemo(() => plan.split("\n"), [plan]);
21
+ const isOverflowing = !isExpanded && lines.length > maxHeight;
22
+
23
+ return (
24
+ <Box flexDirection="column" marginTop={1}>
25
+ <Box paddingLeft={2} borderLeft borderColor="cyan" flexDirection="column">
26
+ <Text color="cyan" bold>
27
+ Plan Content:
28
+ </Text>
29
+ <Box
30
+ flexDirection="column"
31
+ height={isOverflowing ? maxHeight : undefined}
32
+ overflow="hidden"
33
+ >
34
+ <Markdown>{plan}</Markdown>
35
+ </Box>
36
+ {isOverflowing && (
37
+ <Box marginTop={1}>
38
+ <Text color="yellow" dimColor>
39
+ ... (plan truncated, {lines.length} lines total)
40
+ </Text>
41
+ </Box>
42
+ )}
43
+ </Box>
44
+ </Box>
45
+ );
46
+ };
@@ -135,8 +135,10 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
135
135
  </Box>
136
136
  )}
137
137
 
138
- {/* Diff display - handled by DiffDisplay component */}
139
- <DiffDisplay toolBlock={block} />
138
+ {/* Diff display - only show after tool execution completes */}
139
+ {stage === "end" && (
140
+ <DiffDisplay toolName={name} parameters={parameters} />
141
+ )}
140
142
  </Box>
141
143
  );
142
144
  };
@@ -93,11 +93,13 @@ export const useChat = () => {
93
93
  export interface ChatProviderProps {
94
94
  children: React.ReactNode;
95
95
  bypassPermissions?: boolean;
96
+ pluginDirs?: string[];
96
97
  }
97
98
 
98
99
  export const ChatProvider: React.FC<ChatProviderProps> = ({
99
100
  children,
100
101
  bypassPermissions,
102
+ pluginDirs,
101
103
  }) => {
102
104
  const { restoreSessionId, continueLastSession } = useAppConfig();
103
105
 
@@ -260,6 +262,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
260
262
  permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
261
263
  canUseTool: permissionCallback,
262
264
  stream: false, // 关闭流式模式
265
+ plugins: pluginDirs?.map((path) => ({ type: "local", path })),
263
266
  });
264
267
 
265
268
  agentRef.current = agent;
@@ -292,6 +295,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
292
295
  continueLastSession,
293
296
  bypassPermissions,
294
297
  showConfirmation,
298
+ pluginDirs,
295
299
  ]);
296
300
 
297
301
  // Cleanup on unmount
@@ -324,6 +324,13 @@ export const useInputManager = (
324
324
  managerRef.current?.handleBashHistoryExecuteAndSend(command);
325
325
  }, []);
326
326
 
327
+ const handleBashHistoryDelete = useCallback(
328
+ (command: string, workdir?: string) => {
329
+ managerRef.current?.handleBashHistoryDelete(command, workdir);
330
+ },
331
+ [],
332
+ );
333
+
327
334
  return {
328
335
  // State
329
336
  inputText,
@@ -445,6 +452,7 @@ export const useInputManager = (
445
452
 
446
453
  // Complex handlers combining multiple operations
447
454
  handleBashHistoryExecuteAndSend,
455
+ handleBashHistoryDelete,
448
456
 
449
457
  // Main input handler
450
458
  handleInput: useCallback(