wave-code 0.6.5 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) 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.map +1 -1
  5. package/dist/commands/plugin/disable.js +3 -10
  6. package/dist/commands/plugin/enable.d.ts.map +1 -1
  7. package/dist/commands/plugin/enable.js +3 -10
  8. package/dist/commands/plugin/install.d.ts.map +1 -1
  9. package/dist/commands/plugin/install.js +4 -11
  10. package/dist/commands/plugin/list.d.ts.map +1 -1
  11. package/dist/commands/plugin/list.js +5 -39
  12. package/dist/commands/plugin/marketplace.js +9 -9
  13. package/dist/commands/plugin/uninstall.d.ts.map +1 -1
  14. package/dist/commands/plugin/uninstall.js +4 -17
  15. package/dist/commands/plugin/update.js +3 -3
  16. package/dist/components/App.d.ts +1 -0
  17. package/dist/components/App.d.ts.map +1 -1
  18. package/dist/components/App.js +4 -4
  19. package/dist/components/BackgroundTaskManager.d.ts.map +1 -1
  20. package/dist/components/BackgroundTaskManager.js +34 -18
  21. package/dist/components/ChatInterface.d.ts.map +1 -1
  22. package/dist/components/ChatInterface.js +28 -15
  23. package/dist/components/ConfirmationDetails.d.ts +1 -0
  24. package/dist/components/ConfirmationDetails.d.ts.map +1 -1
  25. package/dist/components/ConfirmationDetails.js +7 -14
  26. package/dist/components/ConfirmationSelector.d.ts +1 -0
  27. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  28. package/dist/components/ConfirmationSelector.js +164 -117
  29. package/dist/components/DiffDisplay.d.ts +1 -0
  30. package/dist/components/DiffDisplay.d.ts.map +1 -1
  31. package/dist/components/DiffDisplay.js +94 -36
  32. package/dist/components/HistorySearch.d.ts.map +1 -1
  33. package/dist/components/HistorySearch.js +26 -20
  34. package/dist/components/Markdown.d.ts.map +1 -1
  35. package/dist/components/Markdown.js +3 -1
  36. package/dist/components/McpManager.d.ts.map +1 -1
  37. package/dist/components/McpManager.js +49 -52
  38. package/dist/components/MessageBlockItem.d.ts +9 -0
  39. package/dist/components/MessageBlockItem.d.ts.map +1 -0
  40. package/dist/components/MessageBlockItem.js +11 -0
  41. package/dist/components/MessageList.d.ts +2 -4
  42. package/dist/components/MessageList.d.ts.map +1 -1
  43. package/dist/components/MessageList.js +28 -23
  44. package/dist/components/PluginDetail.d.ts.map +1 -1
  45. package/dist/components/PluginDetail.js +19 -22
  46. package/dist/components/SessionSelector.d.ts.map +1 -1
  47. package/dist/components/SessionSelector.js +8 -5
  48. package/dist/components/TaskList.d.ts.map +1 -1
  49. package/dist/components/TaskList.js +2 -5
  50. package/dist/components/ToolDisplay.d.ts.map +1 -1
  51. package/dist/components/ToolDisplay.js +1 -1
  52. package/dist/contexts/useChat.d.ts +1 -0
  53. package/dist/contexts/useChat.d.ts.map +1 -1
  54. package/dist/contexts/useChat.js +20 -3
  55. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  56. package/dist/hooks/usePluginManager.js +20 -39
  57. package/dist/index.d.ts.map +1 -1
  58. package/dist/index.js +16 -0
  59. package/dist/print-cli.d.ts +1 -0
  60. package/dist/print-cli.d.ts.map +1 -1
  61. package/dist/print-cli.js +2 -1
  62. package/dist/utils/highlightUtils.d.ts +2 -0
  63. package/dist/utils/highlightUtils.d.ts.map +1 -0
  64. package/dist/utils/highlightUtils.js +69 -0
  65. package/dist/utils/toolParameterTransforms.d.ts +2 -6
  66. package/dist/utils/toolParameterTransforms.d.ts.map +1 -1
  67. package/dist/utils/toolParameterTransforms.js +10 -14
  68. package/package.json +4 -2
  69. package/src/cli.tsx +3 -0
  70. package/src/commands/plugin/disable.ts +3 -17
  71. package/src/commands/plugin/enable.ts +3 -17
  72. package/src/commands/plugin/install.ts +4 -18
  73. package/src/commands/plugin/list.ts +5 -55
  74. package/src/commands/plugin/marketplace.ts +9 -9
  75. package/src/commands/plugin/uninstall.ts +4 -26
  76. package/src/commands/plugin/update.ts +3 -3
  77. package/src/components/App.tsx +10 -2
  78. package/src/components/BackgroundTaskManager.tsx +69 -44
  79. package/src/components/ChatInterface.tsx +35 -23
  80. package/src/components/ConfirmationDetails.tsx +13 -15
  81. package/src/components/ConfirmationSelector.tsx +207 -128
  82. package/src/components/DiffDisplay.tsx +164 -75
  83. package/src/components/HistorySearch.tsx +31 -25
  84. package/src/components/Markdown.tsx +3 -1
  85. package/src/components/McpManager.tsx +51 -59
  86. package/src/components/MessageBlockItem.tsx +83 -0
  87. package/src/components/MessageList.tsx +55 -52
  88. package/src/components/PluginDetail.tsx +30 -31
  89. package/src/components/SessionSelector.tsx +8 -5
  90. package/src/components/TaskList.tsx +2 -5
  91. package/src/components/ToolDisplay.tsx +5 -1
  92. package/src/contexts/useChat.tsx +22 -2
  93. package/src/hooks/usePluginManager.ts +21 -57
  94. package/src/index.ts +17 -0
  95. package/src/print-cli.ts +3 -0
  96. package/src/utils/highlightUtils.ts +76 -0
  97. package/src/utils/toolParameterTransforms.ts +11 -20
  98. package/dist/components/MessageItem.d.ts +0 -8
  99. package/dist/components/MessageItem.d.ts.map +0 -1
  100. package/dist/components/MessageItem.js +0 -13
  101. package/src/components/MessageItem.tsx +0 -81
@@ -1,9 +1,9 @@
1
- import { MarketplaceService } from "wave-agent-sdk";
1
+ import { PluginCore } from "wave-agent-sdk";
2
2
 
3
3
  export async function addMarketplaceCommand(argv: { input: string }) {
4
- const service = new MarketplaceService();
4
+ const pluginCore = new PluginCore(process.cwd());
5
5
  try {
6
- const marketplace = await service.addMarketplace(argv.input);
6
+ const marketplace = await pluginCore.addMarketplace(argv.input);
7
7
  const source = marketplace.source;
8
8
  let sourceInfo = "";
9
9
  if (source.source === "directory") {
@@ -25,9 +25,9 @@ export async function addMarketplaceCommand(argv: { input: string }) {
25
25
  }
26
26
 
27
27
  export async function listMarketplacesCommand() {
28
- const service = new MarketplaceService();
28
+ const pluginCore = new PluginCore(process.cwd());
29
29
  try {
30
- const marketplaces = await service.listMarketplaces();
30
+ const marketplaces = await pluginCore.listMarketplaces();
31
31
  if (marketplaces.length === 0) {
32
32
  console.log("No marketplaces registered.");
33
33
  } else {
@@ -57,9 +57,9 @@ export async function listMarketplacesCommand() {
57
57
  }
58
58
 
59
59
  export async function removeMarketplaceCommand(argv: { name: string }) {
60
- const service = new MarketplaceService();
60
+ const pluginCore = new PluginCore(process.cwd());
61
61
  try {
62
- await service.removeMarketplace(argv.name);
62
+ await pluginCore.removeMarketplace(argv.name);
63
63
  console.log(`Successfully removed marketplace: ${argv.name}`);
64
64
  process.exit(0);
65
65
  } catch (error) {
@@ -70,14 +70,14 @@ export async function removeMarketplaceCommand(argv: { name: string }) {
70
70
  }
71
71
 
72
72
  export async function updateMarketplaceCommand(argv: { name?: string }) {
73
- const service = new MarketplaceService();
73
+ const pluginCore = new PluginCore(process.cwd());
74
74
  try {
75
75
  console.log(
76
76
  argv.name
77
77
  ? `Updating marketplace: ${argv.name}...`
78
78
  : "Updating all marketplaces...",
79
79
  );
80
- await service.updateMarketplace(argv.name);
80
+ await pluginCore.updateMarketplace(argv.name);
81
81
  console.log("Successfully updated.");
82
82
  process.exit(0);
83
83
  } catch (error) {
@@ -1,35 +1,13 @@
1
- import {
2
- MarketplaceService,
3
- ConfigurationService,
4
- PluginManager,
5
- PluginScopeManager,
6
- } from "wave-agent-sdk";
1
+ import { PluginCore } from "wave-agent-sdk";
7
2
 
8
3
  export async function uninstallPluginCommand(argv: { plugin: string }) {
9
- const marketplaceService = new MarketplaceService();
10
4
  const workdir = process.cwd();
5
+ const pluginCore = new PluginCore(workdir);
11
6
 
12
7
  try {
13
- await marketplaceService.uninstallPlugin(argv.plugin, workdir);
8
+ await pluginCore.uninstallPlugin(argv.plugin);
14
9
  console.log(`Successfully uninstalled plugin: ${argv.plugin}`);
15
-
16
- const configurationService = new ConfigurationService();
17
- const pluginManager = new PluginManager({ workdir });
18
- const scopeManager = new PluginScopeManager({
19
- workdir,
20
- configurationService,
21
- pluginManager,
22
- });
23
-
24
- try {
25
- await scopeManager.removePluginFromAllScopes(argv.plugin);
26
- console.log(`Cleaned up plugin configuration from all scopes`);
27
- } catch (error) {
28
- console.warn(
29
- `Warning: Could not clean up all plugin configurations: ${error instanceof Error ? error.message : String(error)}`,
30
- );
31
- }
32
-
10
+ console.log(`Cleaned up plugin configuration from all scopes`);
33
11
  process.exit(0);
34
12
  } catch (error) {
35
13
  const message = error instanceof Error ? error.message : String(error);
@@ -1,10 +1,10 @@
1
- import { MarketplaceService } from "wave-agent-sdk";
1
+ import { PluginCore } from "wave-agent-sdk";
2
2
 
3
3
  export async function updatePluginCommand(argv: { plugin: string }) {
4
- const marketplaceService = new MarketplaceService();
4
+ const pluginCore = new PluginCore(process.cwd());
5
5
 
6
6
  try {
7
- const updated = await marketplaceService.updatePlugin(argv.plugin);
7
+ const updated = await pluginCore.updatePlugin(argv.plugin);
8
8
  console.log(
9
9
  `Successfully updated plugin: ${updated.name} v${updated.version} from ${updated.marketplace}`,
10
10
  );
@@ -9,14 +9,20 @@ interface AppProps {
9
9
  continueLastSession?: boolean;
10
10
  bypassPermissions?: boolean;
11
11
  pluginDirs?: string[];
12
+ tools?: string[];
12
13
  }
13
14
 
14
15
  const AppWithProviders: React.FC<{
15
16
  bypassPermissions?: boolean;
16
17
  pluginDirs?: string[];
17
- }> = ({ bypassPermissions, pluginDirs }) => {
18
+ tools?: string[];
19
+ }> = ({ bypassPermissions, pluginDirs, tools }) => {
18
20
  return (
19
- <ChatProvider bypassPermissions={bypassPermissions} pluginDirs={pluginDirs}>
21
+ <ChatProvider
22
+ bypassPermissions={bypassPermissions}
23
+ pluginDirs={pluginDirs}
24
+ tools={tools}
25
+ >
20
26
  <ChatInterfaceWithRemount />
21
27
  </ChatProvider>
22
28
  );
@@ -74,6 +80,7 @@ export const App: React.FC<AppProps> = ({
74
80
  continueLastSession,
75
81
  bypassPermissions,
76
82
  pluginDirs,
83
+ tools,
77
84
  }) => {
78
85
  return (
79
86
  <AppProvider
@@ -83,6 +90,7 @@ export const App: React.FC<AppProps> = ({
83
90
  <AppWithProviders
84
91
  bypassPermissions={bypassPermissions}
85
92
  pluginDirs={pluginDirs}
93
+ tools={tools}
86
94
  />
87
95
  </AppProvider>
88
96
  );
@@ -23,6 +23,7 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
23
23
  useChat();
24
24
  const [tasks, setTasks] = useState<Task[]>([]);
25
25
  const [selectedIndex, setSelectedIndex] = useState(0);
26
+ const MAX_VISIBLE_ITEMS = 3;
26
27
  const [viewMode, setViewMode] = useState<"list" | "detail">("list");
27
28
  const [detailTaskId, setDetailTaskId] = useState<string | null>(null);
28
29
  const [detailOutput, setDetailOutput] = useState<{
@@ -70,15 +71,28 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
70
71
  stopBackgroundTask(taskId);
71
72
  };
72
73
 
74
+ // Calculate visible window
75
+ const startIndex = Math.max(
76
+ 0,
77
+ Math.min(
78
+ selectedIndex - Math.floor(MAX_VISIBLE_ITEMS / 2),
79
+ Math.max(0, tasks.length - MAX_VISIBLE_ITEMS),
80
+ ),
81
+ );
82
+ const visibleTasks = tasks.slice(startIndex, startIndex + MAX_VISIBLE_ITEMS);
83
+
73
84
  useInput((input, key) => {
74
85
  if (viewMode === "list") {
75
86
  // List mode navigation
76
87
  if (key.return) {
77
- if (tasks.length > 0 && selectedIndex < tasks.length) {
78
- const selectedTask = tasks[selectedIndex];
79
- setDetailTaskId(selectedTask.id);
80
- setViewMode("detail");
81
- }
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
+ });
82
96
  return;
83
97
  }
84
98
 
@@ -88,20 +102,25 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
88
102
  }
89
103
 
90
104
  if (key.upArrow) {
91
- setSelectedIndex(Math.max(0, selectedIndex - 1));
105
+ setSelectedIndex((prev) => Math.max(0, prev - 1));
92
106
  return;
93
107
  }
94
108
 
95
109
  if (key.downArrow) {
96
- setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
110
+ setSelectedIndex((prev) => Math.min(tasks.length - 1, prev + 1));
97
111
  return;
98
112
  }
99
113
 
100
- if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
101
- const selectedTask = tasks[selectedIndex];
102
- if (selectedTask.status === "running") {
103
- stopTask(selectedTask.id);
104
- }
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
+ });
105
124
  return;
106
125
  }
107
126
  } else if (viewMode === "detail") {
@@ -273,40 +292,46 @@ export const BackgroundTaskManager: React.FC<BackgroundTaskManagerProps> = ({
273
292
  </Box>
274
293
  <Text dimColor>Select a task to view details</Text>
275
294
 
276
- {tasks.map((task, index) => (
277
- <Box key={task.id} flexDirection="column">
278
- <Text
279
- color={index === selectedIndex ? "black" : "white"}
280
- backgroundColor={index === selectedIndex ? "cyan" : undefined}
281
- >
282
- {index === selectedIndex ? "▶ " : " "}
283
- {index + 1}. [{task.id}] {task.type}
284
- {task.description ? `: ${task.description}` : ""}
285
- <Text
286
- color={
287
- task.status === "running"
288
- ? "green"
289
- : task.status === "completed"
290
- ? "blue"
291
- : "red"
292
- }
293
- >
294
- {" "}
295
- ({task.status})
296
- </Text>
297
- </Text>
298
- {index === selectedIndex && (
299
- <Box marginLeft={4} flexDirection="column">
300
- <Text color="gray" dimColor>
301
- Started: {formatTime(task.startTime)}
302
- {task.runtime !== undefined &&
303
- ` | Runtime: ${formatDuration(task.runtime)}`}
304
- {task.exitCode !== undefined && ` | Exit: ${task.exitCode}`}
295
+ <Box flexDirection="column">
296
+ {visibleTasks.map((task, index) => {
297
+ const actualIndex = startIndex + index;
298
+ const isSelected = actualIndex === selectedIndex;
299
+ return (
300
+ <Box key={task.id} flexDirection="column">
301
+ <Text
302
+ color={isSelected ? "black" : "white"}
303
+ backgroundColor={isSelected ? "cyan" : undefined}
304
+ >
305
+ {isSelected ? "▶ " : " "}
306
+ {actualIndex + 1}. [{task.id}] {task.type}
307
+ {task.description ? `: ${task.description}` : ""}
308
+ <Text
309
+ color={
310
+ task.status === "running"
311
+ ? "green"
312
+ : task.status === "completed"
313
+ ? "blue"
314
+ : "red"
315
+ }
316
+ >
317
+ {" "}
318
+ ({task.status})
319
+ </Text>
305
320
  </Text>
321
+ {isSelected && (
322
+ <Box marginLeft={4} flexDirection="column">
323
+ <Text color="gray" dimColor>
324
+ Started: {formatTime(task.startTime)}
325
+ {task.runtime !== undefined &&
326
+ ` | Runtime: ${formatDuration(task.runtime)}`}
327
+ {task.exitCode !== undefined && ` | Exit: ${task.exitCode}`}
328
+ </Text>
329
+ </Box>
330
+ )}
306
331
  </Box>
307
- )}
308
- </Box>
309
- ))}
332
+ );
333
+ })}
334
+ </Box>
310
335
 
311
336
  <Box marginTop={1}>
312
337
  <Text dimColor>
@@ -1,4 +1,4 @@
1
- import React, { useState, useCallback } from "react";
1
+ import React, { useState, useCallback, useLayoutEffect } from "react";
2
2
  import { Box, useStdout } from "ink";
3
3
  import { MessageList } from "./MessageList.js";
4
4
  import { InputBox } from "./InputBox.js";
@@ -12,7 +12,9 @@ import type { PermissionDecision } from "wave-agent-sdk";
12
12
 
13
13
  export const ChatInterface: React.FC = () => {
14
14
  const { stdout } = useStdout();
15
- const [isDetailsTooTall, setIsDetailsTooTall] = useState(false);
15
+ const [detailsHeight, setDetailsHeight] = useState(0);
16
+ const [selectorHeight, setSelectorHeight] = useState(0);
17
+ const [isConfirmationTooTall, setIsConfirmationTooTall] = useState(false);
16
18
 
17
19
  const {
18
20
  messages,
@@ -36,39 +38,49 @@ export const ChatInterface: React.FC = () => {
36
38
  setWasLastDetailsTooTall,
37
39
  } = useChat();
38
40
 
39
- const handleHeightMeasured = useCallback(
40
- (height: number) => {
41
- const terminalHeight = stdout?.rows || 24;
42
- if (height > terminalHeight - 10) {
43
- setIsDetailsTooTall(true);
44
- } else {
45
- setIsDetailsTooTall(false);
46
- }
47
- },
48
- [stdout?.rows],
49
- );
41
+ const handleDetailsHeightMeasured = useCallback((height: number) => {
42
+ setDetailsHeight(height);
43
+ }, []);
44
+
45
+ const handleSelectorHeightMeasured = useCallback((height: number) => {
46
+ setSelectorHeight(height);
47
+ }, []);
48
+
49
+ useLayoutEffect(() => {
50
+ const terminalHeight = stdout?.rows || 24;
51
+ const totalHeight = detailsHeight + selectorHeight;
52
+ if (totalHeight > terminalHeight) {
53
+ setIsConfirmationTooTall(true);
54
+ } else {
55
+ setIsConfirmationTooTall(false);
56
+ }
57
+ }, [detailsHeight, selectorHeight, stdout?.rows]);
50
58
 
51
59
  const handleConfirmationCancel = useCallback(() => {
52
- if (isDetailsTooTall) {
60
+ if (isConfirmationTooTall) {
53
61
  setWasLastDetailsTooTall((prev) => prev + 1);
54
- setIsDetailsTooTall(false);
62
+ setIsConfirmationTooTall(false);
55
63
  }
56
64
  originalHandleConfirmationCancel();
57
65
  }, [
58
- isDetailsTooTall,
66
+ isConfirmationTooTall,
59
67
  originalHandleConfirmationCancel,
60
68
  setWasLastDetailsTooTall,
61
69
  ]);
62
70
 
63
71
  const wrappedHandleConfirmationDecision = useCallback(
64
72
  (decision: PermissionDecision) => {
65
- if (isDetailsTooTall) {
73
+ if (isConfirmationTooTall) {
66
74
  setWasLastDetailsTooTall((prev) => prev + 1);
67
- setIsDetailsTooTall(false);
75
+ setIsConfirmationTooTall(false);
68
76
  }
69
77
  handleConfirmationDecision(decision);
70
78
  },
71
- [isDetailsTooTall, handleConfirmationDecision, setWasLastDetailsTooTall],
79
+ [
80
+ isConfirmationTooTall,
81
+ handleConfirmationDecision,
82
+ setWasLastDetailsTooTall,
83
+ ],
72
84
  );
73
85
 
74
86
  if (!sessionId) return null;
@@ -77,10 +89,8 @@ export const ChatInterface: React.FC = () => {
77
89
  <Box flexDirection="column">
78
90
  <MessageList
79
91
  messages={messages}
80
- isLoading={isLoading}
81
- isCommandRunning={isCommandRunning}
82
92
  isExpanded={isExpanded}
83
- forceStaticLastMessage={isDetailsTooTall}
93
+ hideDynamicBlocks={isConfirmationVisible}
84
94
  />
85
95
 
86
96
  {(isLoading || isCommandRunning || isCompressing) &&
@@ -101,7 +111,8 @@ export const ChatInterface: React.FC = () => {
101
111
  toolName={confirmingTool!.name}
102
112
  toolInput={confirmingTool!.input}
103
113
  isExpanded={isExpanded}
104
- onHeightMeasured={handleHeightMeasured}
114
+ onHeightMeasured={handleDetailsHeightMeasured}
115
+ isStatic={isConfirmationTooTall}
105
116
  />
106
117
  <ConfirmationSelector
107
118
  toolName={confirmingTool!.name}
@@ -112,6 +123,7 @@ export const ChatInterface: React.FC = () => {
112
123
  onDecision={wrappedHandleConfirmationDecision}
113
124
  onCancel={handleConfirmationCancel}
114
125
  onAbort={abortMessage}
126
+ onHeightMeasured={handleSelectorHeightMeasured}
115
127
  />
116
128
  </>
117
129
  )}
@@ -1,10 +1,8 @@
1
- import React, { useLayoutEffect, useRef, useState } from "react";
1
+ import React, { useLayoutEffect, useRef } from "react";
2
2
  import { Box, Text, useStdout, measureElement, Static } from "ink";
3
3
  import {
4
4
  BASH_TOOL_NAME,
5
5
  EDIT_TOOL_NAME,
6
- MULTI_EDIT_TOOL_NAME,
7
- DELETE_FILE_TOOL_NAME,
8
6
  WRITE_TOOL_NAME,
9
7
  EXIT_PLAN_MODE_TOOL_NAME,
10
8
  ASK_USER_QUESTION_TOOL_NAME,
@@ -26,10 +24,6 @@ const getActionDescription = (
26
24
  return `Execute command: ${toolInput.command || "unknown command"}`;
27
25
  case EDIT_TOOL_NAME:
28
26
  return `Edit file: ${toolInput.file_path || "unknown file"}`;
29
- case MULTI_EDIT_TOOL_NAME:
30
- return `Edit multiple sections in: ${toolInput.file_path || "unknown file"}`;
31
- case DELETE_FILE_TOOL_NAME:
32
- return `Delete file: ${toolInput.target_file || "unknown file"}`;
33
27
  case WRITE_TOOL_NAME:
34
28
  return `Write to file: ${toolInput.file_path || "unknown file"}`;
35
29
  case EXIT_PLAN_MODE_TOOL_NAME:
@@ -46,6 +40,7 @@ export interface ConfirmationDetailsProps {
46
40
  toolInput?: Record<string, unknown>;
47
41
  isExpanded?: boolean;
48
42
  onHeightMeasured?: (height: number) => void;
43
+ isStatic?: boolean;
49
44
  }
50
45
 
51
46
  export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
@@ -53,21 +48,21 @@ export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
53
48
  toolInput,
54
49
  isExpanded = false,
55
50
  onHeightMeasured,
51
+ isStatic = false,
56
52
  }) => {
57
53
  const { stdout } = useStdout();
58
- const [isStatic, setIsStatic] = useState(false);
59
54
  const boxRef = useRef(null);
60
55
 
56
+ const startLineNumber =
57
+ (toolInput?.startLineNumber as number | undefined) ??
58
+ (toolName === WRITE_TOOL_NAME ? 1 : undefined);
59
+
61
60
  useLayoutEffect(() => {
62
61
  if (boxRef.current) {
63
62
  const { height } = measureElement(boxRef.current);
64
- const terminalHeight = stdout?.rows || 24;
65
- if (height > terminalHeight - 10) {
66
- setIsStatic(true);
67
- }
68
63
  onHeightMeasured?.(height);
69
64
  }
70
- }, [stdout?.rows, onHeightMeasured]);
65
+ }, [stdout?.rows, onHeightMeasured, toolInput, isExpanded]);
71
66
 
72
67
  const content = (
73
68
  <Box
@@ -78,14 +73,17 @@ export const ConfirmationDetails: React.FC<ConfirmationDetailsProps> = ({
78
73
  borderBottom={false}
79
74
  borderLeft={false}
80
75
  borderRight={false}
81
- paddingTop={1}
82
76
  >
83
77
  <Text color="yellow" bold>
84
78
  Tool: {toolName}
85
79
  </Text>
86
80
  <Text color="yellow">{getActionDescription(toolName, toolInput)}</Text>
87
81
 
88
- <DiffDisplay toolName={toolName} parameters={JSON.stringify(toolInput)} />
82
+ <DiffDisplay
83
+ toolName={toolName}
84
+ parameters={JSON.stringify(toolInput)}
85
+ startLineNumber={startLineNumber}
86
+ />
89
87
 
90
88
  {toolName !== ASK_USER_QUESTION_TOOL_NAME &&
91
89
  toolName === EXIT_PLAN_MODE_TOOL_NAME &&