wave-code 0.4.0 → 0.5.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.
- package/dist/commands/plugin/uninstall.js +1 -1
- package/dist/components/CommandSelector.js +3 -3
- package/dist/components/Confirmation.d.ts.map +1 -1
- package/dist/components/Confirmation.js +72 -19
- package/dist/components/DiffDisplay.d.ts +0 -1
- package/dist/components/DiffDisplay.d.ts.map +1 -1
- package/dist/components/DiffDisplay.js +38 -59
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +6 -5
- package/dist/components/Markdown.d.ts.map +1 -1
- package/dist/components/Markdown.js +4 -3
- package/dist/components/PlanDisplay.d.ts.map +1 -1
- package/dist/components/PlanDisplay.js +2 -2
- package/dist/components/PluginDetail.js +1 -1
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +4 -0
- package/dist/components/TaskManager.d.ts +6 -0
- package/dist/components/TaskManager.d.ts.map +1 -0
- package/dist/components/TaskManager.js +114 -0
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +2 -1
- package/dist/contexts/useChat.d.ts +5 -4
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +16 -12
- package/dist/hooks/useInputManager.d.ts +2 -2
- package/dist/hooks/useInputManager.js +10 -10
- package/dist/hooks/usePluginManager.d.ts.map +1 -1
- package/dist/hooks/usePluginManager.js +8 -4
- package/dist/managers/InputManager.d.ts +7 -6
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +17 -12
- package/package.json +6 -6
- package/src/commands/plugin/uninstall.ts +1 -1
- package/src/components/CommandSelector.tsx +3 -3
- package/src/components/Confirmation.tsx +114 -24
- package/src/components/DiffDisplay.tsx +60 -106
- package/src/components/InputBox.tsx +9 -7
- package/src/components/Markdown.tsx +10 -4
- package/src/components/PlanDisplay.tsx +14 -19
- package/src/components/PluginDetail.tsx +1 -1
- package/src/components/SubagentBlock.tsx +5 -0
- package/src/components/{BashShellManager.tsx → TaskManager.tsx} +79 -75
- package/src/components/ToolResultDisplay.tsx +4 -0
- package/src/contexts/useChat.tsx +25 -20
- package/src/hooks/useInputManager.ts +10 -10
- package/src/hooks/usePluginManager.ts +10 -4
- package/src/managers/InputManager.ts +22 -15
- package/dist/components/BashShellManager.d.ts +0 -6
- package/dist/components/BashShellManager.d.ts.map +0 -1
- package/dist/components/BashShellManager.js +0 -116
|
@@ -2,55 +2,55 @@ import React, { useState, useEffect } from "react";
|
|
|
2
2
|
import { Box, Text, useInput } from "ink";
|
|
3
3
|
import { useChat } from "../contexts/useChat.js";
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface Task {
|
|
6
6
|
id: string;
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
type: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
status: "running" | "completed" | "failed" | "killed";
|
|
9
10
|
startTime: number;
|
|
10
11
|
exitCode?: number;
|
|
11
12
|
runtime?: number;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
|
-
export interface
|
|
15
|
+
export interface TaskManagerProps {
|
|
15
16
|
onCancel: () => void;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
export const
|
|
19
|
-
|
|
20
|
-
}) => {
|
|
21
|
-
const { backgroundShells, getBackgroundShellOutput, killBackgroundShell } =
|
|
19
|
+
export const TaskManager: React.FC<TaskManagerProps> = ({ onCancel }) => {
|
|
20
|
+
const { backgroundTasks, getBackgroundTaskOutput, stopBackgroundTask } =
|
|
22
21
|
useChat();
|
|
23
|
-
const [
|
|
22
|
+
const [tasks, setTasks] = useState<Task[]>([]);
|
|
24
23
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
25
24
|
const [viewMode, setViewMode] = useState<"list" | "detail">("list");
|
|
26
|
-
const [
|
|
25
|
+
const [detailTaskId, setDetailTaskId] = useState<string | null>(null);
|
|
27
26
|
const [detailOutput, setDetailOutput] = useState<{
|
|
28
27
|
stdout: string;
|
|
29
28
|
stderr: string;
|
|
30
29
|
status: string;
|
|
31
30
|
} | null>(null);
|
|
32
31
|
|
|
33
|
-
// Convert
|
|
32
|
+
// Convert backgroundTasks to local Task format
|
|
34
33
|
useEffect(() => {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
id:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
34
|
+
setTasks(
|
|
35
|
+
backgroundTasks.map((task) => ({
|
|
36
|
+
id: task.id,
|
|
37
|
+
type: task.type,
|
|
38
|
+
description: task.description,
|
|
39
|
+
status: task.status,
|
|
40
|
+
startTime: task.startTime,
|
|
41
|
+
exitCode: task.exitCode,
|
|
42
|
+
runtime: task.runtime,
|
|
43
43
|
})),
|
|
44
44
|
);
|
|
45
|
-
}, [
|
|
45
|
+
}, [backgroundTasks]);
|
|
46
46
|
|
|
47
|
-
// Load detail output for selected
|
|
47
|
+
// Load detail output for selected task
|
|
48
48
|
useEffect(() => {
|
|
49
|
-
if (viewMode === "detail" &&
|
|
50
|
-
const output =
|
|
49
|
+
if (viewMode === "detail" && detailTaskId) {
|
|
50
|
+
const output = getBackgroundTaskOutput(detailTaskId);
|
|
51
51
|
setDetailOutput(output);
|
|
52
52
|
}
|
|
53
|
-
}, [viewMode,
|
|
53
|
+
}, [viewMode, detailTaskId, getBackgroundTaskOutput]);
|
|
54
54
|
|
|
55
55
|
const formatDuration = (ms: number): string => {
|
|
56
56
|
if (ms < 1000) return `${ms}ms`;
|
|
@@ -64,17 +64,17 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
64
64
|
return new Date(timestamp).toLocaleTimeString();
|
|
65
65
|
};
|
|
66
66
|
|
|
67
|
-
const
|
|
68
|
-
|
|
67
|
+
const stopTask = (taskId: string) => {
|
|
68
|
+
stopBackgroundTask(taskId);
|
|
69
69
|
};
|
|
70
70
|
|
|
71
71
|
useInput((input, key) => {
|
|
72
72
|
if (viewMode === "list") {
|
|
73
73
|
// List mode navigation
|
|
74
74
|
if (key.return) {
|
|
75
|
-
if (
|
|
76
|
-
const
|
|
77
|
-
|
|
75
|
+
if (tasks.length > 0 && selectedIndex < tasks.length) {
|
|
76
|
+
const selectedTask = tasks[selectedIndex];
|
|
77
|
+
setDetailTaskId(selectedTask.id);
|
|
78
78
|
setViewMode("detail");
|
|
79
79
|
}
|
|
80
80
|
return;
|
|
@@ -91,14 +91,14 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
if (key.downArrow) {
|
|
94
|
-
setSelectedIndex(Math.min(
|
|
94
|
+
setSelectedIndex(Math.min(tasks.length - 1, selectedIndex + 1));
|
|
95
95
|
return;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
if (input === "k" &&
|
|
99
|
-
const
|
|
100
|
-
if (
|
|
101
|
-
|
|
98
|
+
if (input === "k" && tasks.length > 0 && selectedIndex < tasks.length) {
|
|
99
|
+
const selectedTask = tasks[selectedIndex];
|
|
100
|
+
if (selectedTask.status === "running") {
|
|
101
|
+
stopTask(selectedTask.id);
|
|
102
102
|
}
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
@@ -106,24 +106,24 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
106
106
|
// Detail mode navigation
|
|
107
107
|
if (key.escape) {
|
|
108
108
|
setViewMode("list");
|
|
109
|
-
|
|
109
|
+
setDetailTaskId(null);
|
|
110
110
|
setDetailOutput(null);
|
|
111
111
|
return;
|
|
112
112
|
}
|
|
113
113
|
|
|
114
|
-
if (input === "k" &&
|
|
115
|
-
const
|
|
116
|
-
if (
|
|
117
|
-
|
|
114
|
+
if (input === "k" && detailTaskId) {
|
|
115
|
+
const task = tasks.find((t) => t.id === detailTaskId);
|
|
116
|
+
if (task && task.status === "running") {
|
|
117
|
+
stopTask(detailTaskId);
|
|
118
118
|
}
|
|
119
119
|
return;
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
});
|
|
123
123
|
|
|
124
|
-
if (viewMode === "detail" &&
|
|
125
|
-
const
|
|
126
|
-
if (!
|
|
124
|
+
if (viewMode === "detail" && detailTaskId && detailOutput) {
|
|
125
|
+
const task = tasks.find((t) => t.id === detailTaskId);
|
|
126
|
+
if (!task) {
|
|
127
127
|
setViewMode("list");
|
|
128
128
|
return null;
|
|
129
129
|
}
|
|
@@ -141,31 +141,37 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
141
141
|
>
|
|
142
142
|
<Box>
|
|
143
143
|
<Text color="cyan" bold>
|
|
144
|
-
Background
|
|
144
|
+
Background Task Details: {task.id}
|
|
145
145
|
</Text>
|
|
146
146
|
</Box>
|
|
147
147
|
|
|
148
148
|
<Box flexDirection="column" gap={1}>
|
|
149
149
|
<Box>
|
|
150
150
|
<Text>
|
|
151
|
-
<Text color="blue">
|
|
151
|
+
<Text color="blue">Type:</Text> {task.type}
|
|
152
152
|
</Text>
|
|
153
153
|
</Box>
|
|
154
|
+
{task.description && (
|
|
155
|
+
<Box>
|
|
156
|
+
<Text>
|
|
157
|
+
<Text color="blue">Description:</Text> {task.description}
|
|
158
|
+
</Text>
|
|
159
|
+
</Box>
|
|
160
|
+
)}
|
|
154
161
|
<Box>
|
|
155
162
|
<Text>
|
|
156
|
-
<Text color="blue">Status:</Text> {
|
|
157
|
-
{
|
|
158
|
-
` (exit code: ${shell.exitCode})`}
|
|
163
|
+
<Text color="blue">Status:</Text> {task.status}
|
|
164
|
+
{task.exitCode !== undefined && ` (exit code: ${task.exitCode})`}
|
|
159
165
|
</Text>
|
|
160
166
|
</Box>
|
|
161
167
|
<Box>
|
|
162
168
|
<Text>
|
|
163
|
-
<Text color="blue">Started:</Text> {formatTime(
|
|
164
|
-
{
|
|
169
|
+
<Text color="blue">Started:</Text> {formatTime(task.startTime)}
|
|
170
|
+
{task.runtime !== undefined && (
|
|
165
171
|
<Text>
|
|
166
172
|
{" "}
|
|
167
173
|
| <Text color="blue">Runtime:</Text>{" "}
|
|
168
|
-
{formatDuration(
|
|
174
|
+
{formatDuration(task.runtime)}
|
|
169
175
|
</Text>
|
|
170
176
|
)}
|
|
171
177
|
</Text>
|
|
@@ -175,7 +181,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
175
181
|
{detailOutput.stdout && (
|
|
176
182
|
<Box flexDirection="column" marginTop={1}>
|
|
177
183
|
<Text color="green" bold>
|
|
178
|
-
|
|
184
|
+
OUTPUT (last 10 lines):
|
|
179
185
|
</Text>
|
|
180
186
|
<Box borderStyle="single" borderColor="green" padding={1}>
|
|
181
187
|
<Text>
|
|
@@ -188,7 +194,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
188
194
|
{detailOutput.stderr && (
|
|
189
195
|
<Box flexDirection="column" marginTop={1}>
|
|
190
196
|
<Text color="red" bold>
|
|
191
|
-
|
|
197
|
+
ERRORS:
|
|
192
198
|
</Text>
|
|
193
199
|
<Box borderStyle="single" borderColor="red" padding={1}>
|
|
194
200
|
<Text color="red">
|
|
@@ -200,14 +206,14 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
200
206
|
|
|
201
207
|
<Box marginTop={1}>
|
|
202
208
|
<Text dimColor>
|
|
203
|
-
{
|
|
209
|
+
{task.status === "running" ? "k to stop · " : ""}Esc to go back
|
|
204
210
|
</Text>
|
|
205
211
|
</Box>
|
|
206
212
|
</Box>
|
|
207
213
|
);
|
|
208
214
|
}
|
|
209
215
|
|
|
210
|
-
if (!
|
|
216
|
+
if (!backgroundTasks) {
|
|
211
217
|
return (
|
|
212
218
|
<Box
|
|
213
219
|
flexDirection="column"
|
|
@@ -219,15 +225,15 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
219
225
|
paddingTop={1}
|
|
220
226
|
>
|
|
221
227
|
<Text color="cyan" bold>
|
|
222
|
-
Background
|
|
228
|
+
Background Tasks
|
|
223
229
|
</Text>
|
|
224
|
-
<Text>Background
|
|
230
|
+
<Text>Background tasks not available</Text>
|
|
225
231
|
<Text dimColor>Press Escape to close</Text>
|
|
226
232
|
</Box>
|
|
227
233
|
);
|
|
228
234
|
}
|
|
229
235
|
|
|
230
|
-
if (
|
|
236
|
+
if (tasks.length === 0) {
|
|
231
237
|
return (
|
|
232
238
|
<Box
|
|
233
239
|
flexDirection="column"
|
|
@@ -239,9 +245,9 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
239
245
|
paddingTop={1}
|
|
240
246
|
>
|
|
241
247
|
<Text color="cyan" bold>
|
|
242
|
-
Background
|
|
248
|
+
Background Tasks
|
|
243
249
|
</Text>
|
|
244
|
-
<Text>No background
|
|
250
|
+
<Text>No background tasks found</Text>
|
|
245
251
|
<Text dimColor>Press Escape to close</Text>
|
|
246
252
|
</Box>
|
|
247
253
|
);
|
|
@@ -260,42 +266,40 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
260
266
|
>
|
|
261
267
|
<Box>
|
|
262
268
|
<Text color="cyan" bold>
|
|
263
|
-
Background
|
|
269
|
+
Background Tasks
|
|
264
270
|
</Text>
|
|
265
271
|
</Box>
|
|
266
|
-
<Text dimColor>Select a
|
|
272
|
+
<Text dimColor>Select a task to view details</Text>
|
|
267
273
|
|
|
268
|
-
{
|
|
269
|
-
<Box key={
|
|
274
|
+
{tasks.map((task, index) => (
|
|
275
|
+
<Box key={task.id} flexDirection="column">
|
|
270
276
|
<Text
|
|
271
277
|
color={index === selectedIndex ? "black" : "white"}
|
|
272
278
|
backgroundColor={index === selectedIndex ? "cyan" : undefined}
|
|
273
279
|
>
|
|
274
280
|
{index === selectedIndex ? "▶ " : " "}
|
|
275
|
-
{index + 1}.{
|
|
276
|
-
{
|
|
277
|
-
? shell.command.substring(0, 47) + "..."
|
|
278
|
-
: shell.command}
|
|
281
|
+
{index + 1}. [{task.id}] {task.type}
|
|
282
|
+
{task.description ? `: ${task.description}` : ""}
|
|
279
283
|
<Text
|
|
280
284
|
color={
|
|
281
|
-
|
|
285
|
+
task.status === "running"
|
|
282
286
|
? "green"
|
|
283
|
-
:
|
|
287
|
+
: task.status === "completed"
|
|
284
288
|
? "blue"
|
|
285
289
|
: "red"
|
|
286
290
|
}
|
|
287
291
|
>
|
|
288
292
|
{" "}
|
|
289
|
-
({
|
|
293
|
+
({task.status})
|
|
290
294
|
</Text>
|
|
291
295
|
</Text>
|
|
292
296
|
{index === selectedIndex && (
|
|
293
297
|
<Box marginLeft={4} flexDirection="column">
|
|
294
298
|
<Text color="gray" dimColor>
|
|
295
|
-
|
|
296
|
-
{
|
|
297
|
-
` | Runtime: ${formatDuration(
|
|
298
|
-
{
|
|
299
|
+
Started: {formatTime(task.startTime)}
|
|
300
|
+
{task.runtime !== undefined &&
|
|
301
|
+
` | Runtime: ${formatDuration(task.runtime)}`}
|
|
302
|
+
{task.exitCode !== undefined && ` | Exit: ${task.exitCode}`}
|
|
299
303
|
</Text>
|
|
300
304
|
</Box>
|
|
301
305
|
)}
|
|
@@ -305,7 +309,7 @@ export const BashShellManager: React.FC<BashShellManagerProps> = ({
|
|
|
305
309
|
<Box marginTop={1}>
|
|
306
310
|
<Text dimColor>
|
|
307
311
|
↑/↓ to select · Enter to view ·{" "}
|
|
308
|
-
{
|
|
312
|
+
{tasks[selectedIndex]?.status === "running" ? "k to stop · " : ""}Esc
|
|
309
313
|
to close
|
|
310
314
|
</Text>
|
|
311
315
|
</Box>
|
|
@@ -44,6 +44,9 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
44
44
|
|
|
45
45
|
const toolName = name ? String(name) : "Tool";
|
|
46
46
|
|
|
47
|
+
const isBackgroundable =
|
|
48
|
+
stage === "running" && (toolName === "Bash" || toolName === "Task");
|
|
49
|
+
|
|
47
50
|
// Get shortResult, if not available show last 5 lines of result
|
|
48
51
|
const getShortResult = () => {
|
|
49
52
|
if (block.shortResult) {
|
|
@@ -76,6 +79,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
76
79
|
<Text color={getStatusColor()}> {getStatusText()}</Text>
|
|
77
80
|
{/* Display image indicator */}
|
|
78
81
|
{hasImages() && <Text color="blue"> {getImageIndicator()}</Text>}
|
|
82
|
+
{isBackgroundable && <Text color="gray"> [Ctrl-B] Background</Text>}
|
|
79
83
|
</Box>
|
|
80
84
|
|
|
81
85
|
{/* Display shortResult in collapsed state */}
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -11,7 +11,7 @@ import { useAppConfig } from "./useAppConfig.js";
|
|
|
11
11
|
import type {
|
|
12
12
|
Message,
|
|
13
13
|
McpServerStatus,
|
|
14
|
-
|
|
14
|
+
BackgroundTask,
|
|
15
15
|
SlashCommand,
|
|
16
16
|
PermissionDecision,
|
|
17
17
|
PermissionMode,
|
|
@@ -47,12 +47,12 @@ export interface ChatContextType {
|
|
|
47
47
|
mcpServers: McpServerStatus[];
|
|
48
48
|
connectMcpServer: (serverName: string) => Promise<boolean>;
|
|
49
49
|
disconnectMcpServer: (serverName: string) => Promise<boolean>;
|
|
50
|
-
// Background
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
// Background tasks
|
|
51
|
+
backgroundTasks: BackgroundTask[];
|
|
52
|
+
getBackgroundTaskOutput: (
|
|
53
|
+
taskId: string,
|
|
54
54
|
) => { stdout: string; stderr: string; status: string } | null;
|
|
55
|
-
|
|
55
|
+
stopBackgroundTask: (taskId: string) => boolean;
|
|
56
56
|
// Slash Command functionality
|
|
57
57
|
slashCommands: SlashCommand[];
|
|
58
58
|
hasSlashCommand: (commandId: string) => boolean;
|
|
@@ -78,6 +78,8 @@ export interface ChatContextType {
|
|
|
78
78
|
hideConfirmation: () => void;
|
|
79
79
|
handleConfirmationDecision: (decision: PermissionDecision) => void;
|
|
80
80
|
handleConfirmationCancel: () => void;
|
|
81
|
+
// Background current task
|
|
82
|
+
backgroundCurrentTask: () => void;
|
|
81
83
|
// Rewind functionality
|
|
82
84
|
rewindId: number;
|
|
83
85
|
handleRewindSelect: (index: number) => Promise<void>;
|
|
@@ -121,10 +123,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
121
123
|
// MCP State
|
|
122
124
|
const [mcpServers, setMcpServers] = useState<McpServerStatus[]>([]);
|
|
123
125
|
|
|
124
|
-
// Background
|
|
125
|
-
const [
|
|
126
|
-
[],
|
|
127
|
-
);
|
|
126
|
+
// Background tasks state
|
|
127
|
+
const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
|
|
128
128
|
|
|
129
129
|
// Command state
|
|
130
130
|
const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
|
|
@@ -222,8 +222,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
222
222
|
onCompressionStateChange: (isCompressingState) => {
|
|
223
223
|
setIsCompressing(isCompressingState);
|
|
224
224
|
},
|
|
225
|
-
|
|
226
|
-
|
|
225
|
+
onTasksChange: (tasks) => {
|
|
226
|
+
setBackgroundTasks([...tasks]);
|
|
227
227
|
},
|
|
228
228
|
onSubagentMessagesChange: (subagentId, messages) => {
|
|
229
229
|
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
@@ -422,15 +422,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
422
422
|
return (await agentRef.current?.disconnectMcpServer(serverName)) ?? false;
|
|
423
423
|
}, []);
|
|
424
424
|
|
|
425
|
-
// Background
|
|
426
|
-
const
|
|
425
|
+
// Background task management methods - delegate to Agent
|
|
426
|
+
const getBackgroundTaskOutput = useCallback((taskId: string) => {
|
|
427
427
|
if (!agentRef.current) return null;
|
|
428
|
-
return agentRef.current.
|
|
428
|
+
return agentRef.current.getBackgroundTaskOutput(taskId);
|
|
429
429
|
}, []);
|
|
430
430
|
|
|
431
|
-
const
|
|
431
|
+
const stopBackgroundTask = useCallback((taskId: string) => {
|
|
432
432
|
if (!agentRef.current) return false;
|
|
433
|
-
return agentRef.current.
|
|
433
|
+
return agentRef.current.stopBackgroundTask(taskId);
|
|
434
434
|
}, []);
|
|
435
435
|
|
|
436
436
|
const hasSlashCommand = useCallback((commandId: string) => {
|
|
@@ -482,6 +482,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
482
482
|
hideConfirmation();
|
|
483
483
|
}, [currentConfirmation, hideConfirmation]);
|
|
484
484
|
|
|
485
|
+
const backgroundCurrentTask = useCallback(() => {
|
|
486
|
+
agentRef.current?.backgroundCurrentTask();
|
|
487
|
+
}, []);
|
|
488
|
+
|
|
485
489
|
const handleRewindSelect = useCallback(async (index: number) => {
|
|
486
490
|
if (agentRef.current) {
|
|
487
491
|
try {
|
|
@@ -530,9 +534,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
530
534
|
mcpServers,
|
|
531
535
|
connectMcpServer,
|
|
532
536
|
disconnectMcpServer,
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
537
|
+
backgroundTasks,
|
|
538
|
+
getBackgroundTaskOutput,
|
|
539
|
+
stopBackgroundTask,
|
|
536
540
|
slashCommands,
|
|
537
541
|
hasSlashCommand,
|
|
538
542
|
subagentMessages,
|
|
@@ -544,6 +548,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
|
544
548
|
hideConfirmation,
|
|
545
549
|
handleConfirmationDecision,
|
|
546
550
|
handleConfirmationCancel,
|
|
551
|
+
backgroundCurrentTask,
|
|
547
552
|
rewindId,
|
|
548
553
|
handleRewindSelect,
|
|
549
554
|
};
|
|
@@ -37,7 +37,7 @@ export const useInputManager = (
|
|
|
37
37
|
show: false,
|
|
38
38
|
message: "",
|
|
39
39
|
});
|
|
40
|
-
const [
|
|
40
|
+
const [showTaskManager, setShowTaskManager] = useState(false);
|
|
41
41
|
const [showMcpManager, setShowMcpManager] = useState(false);
|
|
42
42
|
const [showRewindManager, setShowRewindManager] = useState(false);
|
|
43
43
|
const [permissionMode, setPermissionModeState] =
|
|
@@ -64,8 +64,8 @@ export const useInputManager = (
|
|
|
64
64
|
onMemoryTypeSelectorStateChange: (show, message) => {
|
|
65
65
|
setMemoryTypeSelectorState({ show, message });
|
|
66
66
|
},
|
|
67
|
-
|
|
68
|
-
|
|
67
|
+
onTaskManagerStateChange: (show) => {
|
|
68
|
+
setShowTaskManager(show);
|
|
69
69
|
},
|
|
70
70
|
onMcpManagerStateChange: (show) => {
|
|
71
71
|
setShowMcpManager(show);
|
|
@@ -78,7 +78,7 @@ export const useInputManager = (
|
|
|
78
78
|
callbacks.onPermissionModeChange?.(mode);
|
|
79
79
|
},
|
|
80
80
|
onImagesStateChange: setAttachedImages,
|
|
81
|
-
|
|
81
|
+
onShowTaskManager: () => setShowTaskManager(true),
|
|
82
82
|
onShowMcpManager: () => setShowMcpManager(true),
|
|
83
83
|
onShowRewindManager: () => setShowRewindManager(true),
|
|
84
84
|
...callbacks,
|
|
@@ -104,8 +104,8 @@ export const useInputManager = (
|
|
|
104
104
|
onMemoryTypeSelectorStateChange: (show, message) => {
|
|
105
105
|
setMemoryTypeSelectorState({ show, message });
|
|
106
106
|
},
|
|
107
|
-
|
|
108
|
-
|
|
107
|
+
onTaskManagerStateChange: (show) => {
|
|
108
|
+
setShowTaskManager(show);
|
|
109
109
|
},
|
|
110
110
|
onMcpManagerStateChange: (show) => {
|
|
111
111
|
setShowMcpManager(show);
|
|
@@ -118,7 +118,7 @@ export const useInputManager = (
|
|
|
118
118
|
callbacks.onPermissionModeChange?.(mode);
|
|
119
119
|
},
|
|
120
120
|
onImagesStateChange: setAttachedImages,
|
|
121
|
-
|
|
121
|
+
onShowTaskManager: () => setShowTaskManager(true),
|
|
122
122
|
onShowMcpManager: () => setShowMcpManager(true),
|
|
123
123
|
onShowRewindManager: () => setShowRewindManager(true),
|
|
124
124
|
...callbacks,
|
|
@@ -319,7 +319,7 @@ export const useInputManager = (
|
|
|
319
319
|
historySearchQuery: historySearchState.query,
|
|
320
320
|
showMemoryTypeSelector: memoryTypeSelectorState.show,
|
|
321
321
|
memoryMessage: memoryTypeSelectorState.message,
|
|
322
|
-
|
|
322
|
+
showTaskManager,
|
|
323
323
|
showMcpManager,
|
|
324
324
|
showRewindManager,
|
|
325
325
|
permissionMode,
|
|
@@ -368,8 +368,8 @@ export const useInputManager = (
|
|
|
368
368
|
handleSpecialCharInput,
|
|
369
369
|
|
|
370
370
|
// Bash/MCP Manager
|
|
371
|
-
|
|
372
|
-
managerRef.current?.
|
|
371
|
+
setShowTaskManager: useCallback((show: boolean) => {
|
|
372
|
+
managerRef.current?.setShowTaskManager(show);
|
|
373
373
|
}, []),
|
|
374
374
|
setShowMcpManager: useCallback((show: boolean) => {
|
|
375
375
|
managerRef.current?.setShowMcpManager(show);
|
|
@@ -210,7 +210,8 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
210
210
|
}));
|
|
211
211
|
try {
|
|
212
212
|
const pluginId = `${name}@${marketplace}`;
|
|
213
|
-
|
|
213
|
+
const workdir = process.cwd();
|
|
214
|
+
await marketplaceService.installPlugin(pluginId, workdir);
|
|
214
215
|
await pluginScopeManager.enablePlugin(scope, pluginId);
|
|
215
216
|
await refresh();
|
|
216
217
|
} catch (error) {
|
|
@@ -233,11 +234,16 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
233
234
|
}));
|
|
234
235
|
try {
|
|
235
236
|
const pluginId = `${name}@${marketplace}`;
|
|
236
|
-
|
|
237
|
+
const workdir = process.cwd();
|
|
238
|
+
|
|
239
|
+
// 1. Remove from global registry and potentially clean up cache
|
|
240
|
+
await marketplaceService.uninstallPlugin(pluginId, workdir);
|
|
241
|
+
|
|
242
|
+
// 2. Find the scope where it's currently enabled and remove it from there
|
|
237
243
|
const scope = pluginScopeManager.findPluginScope(pluginId);
|
|
238
244
|
if (scope) {
|
|
239
245
|
await configurationService.removeEnabledPlugin(
|
|
240
|
-
|
|
246
|
+
workdir,
|
|
241
247
|
scope,
|
|
242
248
|
pluginId,
|
|
243
249
|
);
|
|
@@ -251,7 +257,7 @@ export function usePluginManager(): PluginManagerContextType {
|
|
|
251
257
|
}));
|
|
252
258
|
}
|
|
253
259
|
},
|
|
254
|
-
[configurationService, pluginScopeManager, refresh],
|
|
260
|
+
[configurationService, marketplaceService, pluginScopeManager, refresh],
|
|
255
261
|
);
|
|
256
262
|
|
|
257
263
|
const updatePlugin = useCallback(
|
|
@@ -30,11 +30,11 @@ export interface InputManagerCallbacks {
|
|
|
30
30
|
) => void;
|
|
31
31
|
onHistorySearchStateChange?: (show: boolean, query: string) => void;
|
|
32
32
|
onMemoryTypeSelectorStateChange?: (show: boolean, message: string) => void;
|
|
33
|
-
|
|
33
|
+
onShowTaskManager?: () => void;
|
|
34
|
+
onTaskManagerStateChange?: (show: boolean) => void;
|
|
34
35
|
onShowMcpManager?: () => void;
|
|
35
|
-
onShowRewindManager?: () => void;
|
|
36
|
-
onBashManagerStateChange?: (show: boolean) => void;
|
|
37
36
|
onMcpManagerStateChange?: (show: boolean) => void;
|
|
37
|
+
onShowRewindManager?: () => void;
|
|
38
38
|
onRewindManagerStateChange?: (show: boolean) => void;
|
|
39
39
|
onImagesStateChange?: (images: AttachedImage[]) => void;
|
|
40
40
|
onSendMessage?: (
|
|
@@ -44,6 +44,7 @@ export interface InputManagerCallbacks {
|
|
|
44
44
|
onHasSlashCommand?: (commandId: string) => boolean;
|
|
45
45
|
onSaveMemory?: (message: string, type: "project" | "user") => Promise<void>;
|
|
46
46
|
onAbortMessage?: () => void;
|
|
47
|
+
onBackgroundCurrentTask?: () => void;
|
|
47
48
|
onResetHistoryNavigation?: () => void;
|
|
48
49
|
onPermissionModeChange?: (mode: PermissionMode) => void;
|
|
49
50
|
logger?: Logger;
|
|
@@ -94,7 +95,7 @@ export class InputManager {
|
|
|
94
95
|
private imageIdCounter: number = 1;
|
|
95
96
|
|
|
96
97
|
// Additional UI state
|
|
97
|
-
private
|
|
98
|
+
private showTaskManager: boolean = false;
|
|
98
99
|
private showMcpManager: boolean = false;
|
|
99
100
|
private showRewindManager: boolean = false;
|
|
100
101
|
|
|
@@ -361,8 +362,8 @@ export class InputManager {
|
|
|
361
362
|
|
|
362
363
|
// If not an agent command or execution failed, check local commands
|
|
363
364
|
if (!commandExecuted) {
|
|
364
|
-
if (command === "
|
|
365
|
-
this.callbacks.
|
|
365
|
+
if (command === "tasks" && this.callbacks.onShowTaskManager) {
|
|
366
|
+
this.callbacks.onShowTaskManager();
|
|
366
367
|
commandExecuted = true;
|
|
367
368
|
} else if (command === "mcp" && this.callbacks.onShowMcpManager) {
|
|
368
369
|
this.callbacks.onShowMcpManager();
|
|
@@ -751,14 +752,14 @@ export class InputManager {
|
|
|
751
752
|
}
|
|
752
753
|
}
|
|
753
754
|
|
|
754
|
-
//
|
|
755
|
-
|
|
756
|
-
return this.
|
|
755
|
+
// Task manager state methods
|
|
756
|
+
getShowTaskManager(): boolean {
|
|
757
|
+
return this.showTaskManager;
|
|
757
758
|
}
|
|
758
759
|
|
|
759
|
-
|
|
760
|
-
this.
|
|
761
|
-
this.callbacks.
|
|
760
|
+
setShowTaskManager(show: boolean): void {
|
|
761
|
+
this.showTaskManager = show;
|
|
762
|
+
this.callbacks.onTaskManagerStateChange?.(show);
|
|
762
763
|
}
|
|
763
764
|
|
|
764
765
|
getShowMcpManager(): boolean {
|
|
@@ -1005,6 +1006,12 @@ export class InputManager {
|
|
|
1005
1006
|
return true;
|
|
1006
1007
|
}
|
|
1007
1008
|
|
|
1009
|
+
// Handle Ctrl+B for backgrounding current task
|
|
1010
|
+
if (key.ctrl && input === "b") {
|
|
1011
|
+
this.callbacks.onBackgroundCurrentTask?.();
|
|
1012
|
+
return true;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1008
1015
|
// Handle up/down keys for history navigation (only when no selector is active)
|
|
1009
1016
|
if (key.upArrow && !this.showFileSelector && !this.showCommandSelector) {
|
|
1010
1017
|
this.navigateHistory("up", this.inputText);
|
|
@@ -1072,17 +1079,17 @@ export class InputManager {
|
|
|
1072
1079
|
this.showCommandSelector ||
|
|
1073
1080
|
this.showHistorySearch ||
|
|
1074
1081
|
this.showMemoryTypeSelector ||
|
|
1075
|
-
this.
|
|
1082
|
+
this.showTaskManager ||
|
|
1076
1083
|
this.showMcpManager ||
|
|
1077
1084
|
this.showRewindManager
|
|
1078
1085
|
) {
|
|
1079
1086
|
if (
|
|
1080
1087
|
this.showMemoryTypeSelector ||
|
|
1081
|
-
this.
|
|
1088
|
+
this.showTaskManager ||
|
|
1082
1089
|
this.showMcpManager ||
|
|
1083
1090
|
this.showRewindManager
|
|
1084
1091
|
) {
|
|
1085
|
-
// Memory type selector,
|
|
1092
|
+
// Memory type selector, task manager, MCP manager and Rewind don't need to handle input, handled by component itself
|
|
1086
1093
|
return false;
|
|
1087
1094
|
}
|
|
1088
1095
|
|