wave-code 0.0.5 → 0.0.8
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/README.md +3 -3
- package/dist/cli.d.ts +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/components/App.d.ts +1 -0
- package/dist/components/App.d.ts.map +1 -1
- package/dist/components/App.js +4 -4
- package/dist/components/BashHistorySelector.d.ts.map +1 -1
- package/dist/components/BashHistorySelector.js +17 -3
- package/dist/components/ChatInterface.d.ts.map +1 -1
- package/dist/components/ChatInterface.js +6 -24
- package/dist/components/CommandSelector.js +4 -4
- package/dist/components/Confirmation.d.ts +11 -0
- package/dist/components/Confirmation.d.ts.map +1 -0
- package/dist/components/Confirmation.js +148 -0
- package/dist/components/DiffDisplay.d.ts +8 -0
- package/dist/components/DiffDisplay.d.ts.map +1 -0
- package/dist/components/DiffDisplay.js +168 -0
- package/dist/components/FileSelector.d.ts +2 -4
- package/dist/components/FileSelector.d.ts.map +1 -1
- package/dist/components/FileSelector.js +2 -2
- package/dist/components/InputBox.d.ts.map +1 -1
- package/dist/components/InputBox.js +30 -50
- package/dist/components/Markdown.d.ts +6 -0
- package/dist/components/Markdown.d.ts.map +1 -0
- package/dist/components/Markdown.js +22 -0
- package/dist/components/MemoryDisplay.js +1 -1
- package/dist/components/MessageItem.d.ts +8 -0
- package/dist/components/MessageItem.d.ts.map +1 -0
- package/dist/components/MessageItem.js +15 -0
- package/dist/components/MessageList.d.ts +1 -1
- package/dist/components/MessageList.d.ts.map +1 -1
- package/dist/components/MessageList.js +33 -33
- package/dist/components/ReasoningDisplay.d.ts +8 -0
- package/dist/components/ReasoningDisplay.d.ts.map +1 -0
- package/dist/components/ReasoningDisplay.js +10 -0
- package/dist/components/SubagentBlock.d.ts +0 -1
- package/dist/components/SubagentBlock.d.ts.map +1 -1
- package/dist/components/SubagentBlock.js +29 -30
- package/dist/components/ToolResultDisplay.d.ts.map +1 -1
- package/dist/components/ToolResultDisplay.js +6 -5
- package/dist/contexts/useChat.d.ts +14 -2
- package/dist/contexts/useChat.d.ts.map +1 -1
- package/dist/contexts/useChat.js +128 -17
- package/dist/hooks/useInputManager.d.ts +6 -1
- package/dist/hooks/useInputManager.d.ts.map +1 -1
- package/dist/hooks/useInputManager.js +32 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +30 -5
- package/dist/managers/InputManager.d.ts +11 -1
- package/dist/managers/InputManager.d.ts.map +1 -1
- package/dist/managers/InputManager.js +77 -26
- package/dist/print-cli.d.ts +2 -0
- package/dist/print-cli.d.ts.map +1 -1
- package/dist/print-cli.js +121 -23
- package/dist/utils/toolParameterTransforms.d.ts +23 -0
- package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
- package/dist/utils/toolParameterTransforms.js +77 -0
- package/dist/utils/usageSummary.d.ts +6 -0
- package/dist/utils/usageSummary.d.ts.map +1 -1
- package/dist/utils/usageSummary.js +72 -0
- package/package.json +13 -8
- package/src/cli.tsx +3 -1
- package/src/components/App.tsx +7 -3
- package/src/components/BashHistorySelector.tsx +26 -3
- package/src/components/ChatInterface.tsx +38 -54
- package/src/components/CommandSelector.tsx +5 -5
- package/src/components/Confirmation.tsx +253 -0
- package/src/components/DiffDisplay.tsx +300 -0
- package/src/components/FileSelector.tsx +4 -6
- package/src/components/InputBox.tsx +58 -87
- package/src/components/Markdown.tsx +29 -0
- package/src/components/MemoryDisplay.tsx +1 -1
- package/src/components/MessageItem.tsx +96 -0
- package/src/components/MessageList.tsx +140 -202
- package/src/components/ReasoningDisplay.tsx +33 -0
- package/src/components/SubagentBlock.tsx +56 -84
- package/src/components/ToolResultDisplay.tsx +9 -5
- package/src/contexts/useChat.tsx +194 -21
- package/src/hooks/useInputManager.ts +40 -3
- package/src/index.ts +45 -5
- package/src/managers/InputManager.ts +101 -27
- package/src/print-cli.ts +143 -21
- package/src/utils/toolParameterTransforms.ts +104 -0
- package/src/utils/usageSummary.ts +109 -0
- package/dist/components/DiffViewer.d.ts +0 -9
- package/dist/components/DiffViewer.d.ts.map +0 -1
- package/dist/components/DiffViewer.js +0 -221
- package/dist/utils/fileSearch.d.ts +0 -20
- package/dist/utils/fileSearch.d.ts.map +0 -1
- package/dist/utils/fileSearch.js +0 -102
- package/src/components/DiffViewer.tsx +0 -321
- package/src/utils/fileSearch.ts +0 -133
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Box, Text } from "ink";
|
|
3
3
|
import type { ToolBlock } from "wave-agent-sdk";
|
|
4
|
+
import { DiffDisplay } from "./DiffDisplay.js";
|
|
4
5
|
|
|
5
6
|
interface ToolResultDisplayProps {
|
|
6
7
|
block: ToolBlock;
|
|
@@ -11,23 +12,23 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
11
12
|
block,
|
|
12
13
|
isExpanded = false,
|
|
13
14
|
}) => {
|
|
14
|
-
const { parameters, result, compactParams,
|
|
15
|
+
const { parameters, result, compactParams, stage, success, error, name } =
|
|
15
16
|
block;
|
|
16
17
|
|
|
17
18
|
// Directly use compactParams
|
|
18
19
|
// (no change needed as we destructured it above)
|
|
19
20
|
|
|
20
21
|
const getStatusColor = () => {
|
|
21
|
-
if (
|
|
22
|
+
if (stage === "running") return "yellow";
|
|
22
23
|
if (success) return "green";
|
|
23
24
|
if (error || success === false) return "red";
|
|
24
25
|
return "gray"; // Unknown state or no state information
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
const getStatusText = () => {
|
|
28
|
-
if (
|
|
29
|
+
if (stage === "running") return "🔄";
|
|
29
30
|
if (success) return "";
|
|
30
|
-
if (error || success === false) return "❌
|
|
31
|
+
if (error || success === false) return "❌";
|
|
31
32
|
return ""; // Don't display text for unknown state
|
|
32
33
|
};
|
|
33
34
|
|
|
@@ -70,7 +71,7 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
70
71
|
<Text color="white">{toolName}</Text>
|
|
71
72
|
{/* Display compactParams in collapsed state */}
|
|
72
73
|
{!isExpanded && compactParams && (
|
|
73
|
-
<Text color="gray">
|
|
74
|
+
<Text color="gray"> {compactParams}</Text>
|
|
74
75
|
)}
|
|
75
76
|
<Text color={getStatusColor()}> {getStatusText()}</Text>
|
|
76
77
|
{/* Display image indicator */}
|
|
@@ -133,6 +134,9 @@ export const ToolResultDisplay: React.FC<ToolResultDisplayProps> = ({
|
|
|
133
134
|
</Text>
|
|
134
135
|
</Box>
|
|
135
136
|
)}
|
|
137
|
+
|
|
138
|
+
{/* Diff display - handled by DiffDisplay component */}
|
|
139
|
+
<DiffDisplay toolBlock={block} />
|
|
136
140
|
</Box>
|
|
137
141
|
);
|
|
138
142
|
};
|
package/src/contexts/useChat.tsx
CHANGED
|
@@ -13,9 +13,14 @@ import type {
|
|
|
13
13
|
McpServerStatus,
|
|
14
14
|
BackgroundShell,
|
|
15
15
|
SlashCommand,
|
|
16
|
-
|
|
16
|
+
PermissionDecision,
|
|
17
|
+
PermissionMode,
|
|
18
|
+
} from "wave-agent-sdk";
|
|
19
|
+
import {
|
|
20
|
+
Agent,
|
|
21
|
+
AgentCallbacks,
|
|
22
|
+
type ToolPermissionContext,
|
|
17
23
|
} from "wave-agent-sdk";
|
|
18
|
-
import { Agent, AgentCallbacks } from "wave-agent-sdk";
|
|
19
24
|
import { logger } from "../utils/logger.js";
|
|
20
25
|
import { displayUsageSummary } from "../utils/usageSummary.js";
|
|
21
26
|
|
|
@@ -51,8 +56,21 @@ export interface ChatContextType {
|
|
|
51
56
|
// Slash Command functionality
|
|
52
57
|
slashCommands: SlashCommand[];
|
|
53
58
|
hasSlashCommand: (commandId: string) => boolean;
|
|
54
|
-
//
|
|
55
|
-
|
|
59
|
+
// Subagent messages
|
|
60
|
+
subagentMessages: Record<string, Message[]>;
|
|
61
|
+
// Permission functionality
|
|
62
|
+
permissionMode: PermissionMode;
|
|
63
|
+
setPermissionMode: (mode: PermissionMode) => void;
|
|
64
|
+
// Permission confirmation state
|
|
65
|
+
isConfirmationVisible: boolean;
|
|
66
|
+
confirmingTool?: { name: string; input?: Record<string, unknown> };
|
|
67
|
+
showConfirmation: (
|
|
68
|
+
toolName: string,
|
|
69
|
+
toolInput?: Record<string, unknown>,
|
|
70
|
+
) => Promise<PermissionDecision>;
|
|
71
|
+
hideConfirmation: () => void;
|
|
72
|
+
handleConfirmationDecision: (decision: PermissionDecision) => void;
|
|
73
|
+
handleConfirmationCancel: () => void;
|
|
56
74
|
}
|
|
57
75
|
|
|
58
76
|
const ChatContext = createContext<ChatContextType | null>(null);
|
|
@@ -67,9 +85,13 @@ export const useChat = () => {
|
|
|
67
85
|
|
|
68
86
|
export interface ChatProviderProps {
|
|
69
87
|
children: React.ReactNode;
|
|
88
|
+
bypassPermissions?: boolean;
|
|
70
89
|
}
|
|
71
90
|
|
|
72
|
-
export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
91
|
+
export const ChatProvider: React.FC<ChatProviderProps> = ({
|
|
92
|
+
children,
|
|
93
|
+
bypassPermissions,
|
|
94
|
+
}) => {
|
|
73
95
|
const { restoreSessionId, continueLastSession } = useAppConfig();
|
|
74
96
|
|
|
75
97
|
// Message Display State
|
|
@@ -95,17 +117,57 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
95
117
|
// Command state
|
|
96
118
|
const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
|
|
97
119
|
|
|
98
|
-
//
|
|
99
|
-
const [
|
|
120
|
+
// Subagent messages state
|
|
121
|
+
const [subagentMessages, setSubagentMessages] = useState<
|
|
122
|
+
Record<string, Message[]>
|
|
123
|
+
>({});
|
|
124
|
+
|
|
125
|
+
// Permission state
|
|
126
|
+
const [permissionMode, setPermissionModeState] =
|
|
127
|
+
useState<PermissionMode>("default");
|
|
128
|
+
|
|
129
|
+
// Confirmation state with queue-based architecture
|
|
130
|
+
const [isConfirmationVisible, setIsConfirmationVisible] = useState(false);
|
|
131
|
+
const [confirmingTool, setConfirmingTool] = useState<
|
|
132
|
+
{ name: string; input?: Record<string, unknown> } | undefined
|
|
133
|
+
>();
|
|
134
|
+
const [confirmationQueue, setConfirmationQueue] = useState<
|
|
135
|
+
Array<{
|
|
136
|
+
toolName: string;
|
|
137
|
+
toolInput?: Record<string, unknown>;
|
|
138
|
+
resolver: (decision: PermissionDecision) => void;
|
|
139
|
+
reject: () => void;
|
|
140
|
+
}>
|
|
141
|
+
>([]);
|
|
142
|
+
const [currentConfirmation, setCurrentConfirmation] = useState<{
|
|
143
|
+
toolName: string;
|
|
144
|
+
toolInput?: Record<string, unknown>;
|
|
145
|
+
resolver: (decision: PermissionDecision) => void;
|
|
146
|
+
reject: () => void;
|
|
147
|
+
} | null>(null);
|
|
100
148
|
|
|
101
149
|
const agentRef = useRef<Agent | null>(null);
|
|
102
150
|
|
|
103
|
-
//
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
151
|
+
// Permission confirmation methods with queue support
|
|
152
|
+
const showConfirmation = useCallback(
|
|
153
|
+
async (
|
|
154
|
+
toolName: string,
|
|
155
|
+
toolInput?: Record<string, unknown>,
|
|
156
|
+
): Promise<PermissionDecision> => {
|
|
157
|
+
return new Promise<PermissionDecision>((resolve, reject) => {
|
|
158
|
+
const queueItem = {
|
|
159
|
+
toolName,
|
|
160
|
+
toolInput,
|
|
161
|
+
resolver: resolve,
|
|
162
|
+
reject,
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
setConfirmationQueue((prev) => [...prev, queueItem]);
|
|
166
|
+
// processNextConfirmation will be called via useEffect
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
[],
|
|
170
|
+
);
|
|
109
171
|
|
|
110
172
|
// Initialize AI manager
|
|
111
173
|
useEffect(() => {
|
|
@@ -118,7 +180,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
118
180
|
setMcpServers([...servers]);
|
|
119
181
|
},
|
|
120
182
|
onSessionIdChange: (sessionId) => {
|
|
121
|
-
|
|
183
|
+
process.stdout.write("\x1Bc", () => {
|
|
184
|
+
setSessionId(sessionId);
|
|
185
|
+
});
|
|
122
186
|
},
|
|
123
187
|
onLatestTotalTokensChange: (tokens) => {
|
|
124
188
|
setlatestTotalTokens(tokens);
|
|
@@ -132,17 +196,47 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
132
196
|
onShellsChange: (shells) => {
|
|
133
197
|
setBackgroundShells([...shells]);
|
|
134
198
|
},
|
|
135
|
-
|
|
136
|
-
|
|
199
|
+
onSubagentMessagesChange: (subagentId, messages) => {
|
|
200
|
+
logger.debug("onSubagentMessagesChange", subagentId, messages.length);
|
|
201
|
+
setSubagentMessages((prev) => ({
|
|
202
|
+
...prev,
|
|
203
|
+
[subagentId]: [...messages],
|
|
204
|
+
}));
|
|
205
|
+
},
|
|
206
|
+
onPermissionModeChange: (mode) => {
|
|
207
|
+
setPermissionModeState(mode);
|
|
137
208
|
},
|
|
138
209
|
};
|
|
139
210
|
|
|
140
211
|
try {
|
|
212
|
+
// Create the permission callback inside the try block to access showConfirmation
|
|
213
|
+
const permissionCallback = bypassPermissions
|
|
214
|
+
? undefined
|
|
215
|
+
: async (
|
|
216
|
+
context: ToolPermissionContext,
|
|
217
|
+
): Promise<PermissionDecision> => {
|
|
218
|
+
try {
|
|
219
|
+
return await showConfirmation(
|
|
220
|
+
context.toolName,
|
|
221
|
+
context.toolInput,
|
|
222
|
+
);
|
|
223
|
+
} catch {
|
|
224
|
+
// If confirmation was cancelled or failed, deny the operation
|
|
225
|
+
return {
|
|
226
|
+
behavior: "deny",
|
|
227
|
+
message: "Operation cancelled by user",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
141
232
|
const agent = await Agent.create({
|
|
142
233
|
callbacks,
|
|
143
234
|
restoreSessionId,
|
|
144
235
|
continueLastSession,
|
|
145
236
|
logger,
|
|
237
|
+
permissionMode: bypassPermissions ? "bypassPermissions" : undefined,
|
|
238
|
+
canUseTool: permissionCallback,
|
|
239
|
+
stream: false, // 关闭流式模式
|
|
146
240
|
});
|
|
147
241
|
|
|
148
242
|
agentRef.current = agent;
|
|
@@ -155,6 +249,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
155
249
|
setIsCommandRunning(agent.isCommandRunning);
|
|
156
250
|
setIsCompressing(agent.isCompressing);
|
|
157
251
|
setUserInputHistory(agent.userInputHistory);
|
|
252
|
+
setPermissionModeState(agent.getPermissionMode());
|
|
158
253
|
|
|
159
254
|
// Get initial MCP servers state
|
|
160
255
|
const mcpServers = agent.getMcpServers?.() || [];
|
|
@@ -163,16 +258,18 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
163
258
|
// Get initial commands
|
|
164
259
|
const agentSlashCommands = agent.getSlashCommands?.() || [];
|
|
165
260
|
setSlashCommands(agentSlashCommands);
|
|
166
|
-
|
|
167
|
-
// Get initial usages
|
|
168
|
-
setUsages(agent.usages);
|
|
169
261
|
} catch (error) {
|
|
170
262
|
console.error("Failed to initialize AI manager:", error);
|
|
171
263
|
}
|
|
172
264
|
};
|
|
173
265
|
|
|
174
266
|
initializeAgent();
|
|
175
|
-
}, [
|
|
267
|
+
}, [
|
|
268
|
+
restoreSessionId,
|
|
269
|
+
continueLastSession,
|
|
270
|
+
bypassPermissions,
|
|
271
|
+
showConfirmation,
|
|
272
|
+
]);
|
|
176
273
|
|
|
177
274
|
// Cleanup on unmount
|
|
178
275
|
useEffect(() => {
|
|
@@ -269,6 +366,17 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
269
366
|
[],
|
|
270
367
|
);
|
|
271
368
|
|
|
369
|
+
// Permission management methods
|
|
370
|
+
const setPermissionMode = useCallback((mode: PermissionMode) => {
|
|
371
|
+
setPermissionModeState((prev) => {
|
|
372
|
+
if (prev === mode) return prev;
|
|
373
|
+
if (agentRef.current && agentRef.current.getPermissionMode() !== mode) {
|
|
374
|
+
agentRef.current.setPermissionMode(mode);
|
|
375
|
+
}
|
|
376
|
+
return mode;
|
|
377
|
+
});
|
|
378
|
+
}, []);
|
|
379
|
+
|
|
272
380
|
// MCP management methods - delegate to Agent
|
|
273
381
|
const connectMcpServer = useCallback(async (serverName: string) => {
|
|
274
382
|
return (await agentRef.current?.connectMcpServer(serverName)) ?? false;
|
|
@@ -294,6 +402,63 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
294
402
|
return agentRef.current.hasSlashCommand(commandId);
|
|
295
403
|
}, []);
|
|
296
404
|
|
|
405
|
+
// Queue processing helper
|
|
406
|
+
const processNextConfirmation = useCallback(() => {
|
|
407
|
+
if (confirmationQueue.length > 0 && !isConfirmationVisible) {
|
|
408
|
+
const next = confirmationQueue[0];
|
|
409
|
+
setCurrentConfirmation(next);
|
|
410
|
+
setConfirmingTool({ name: next.toolName, input: next.toolInput });
|
|
411
|
+
setIsConfirmationVisible(true);
|
|
412
|
+
setConfirmationQueue((prev) => prev.slice(1));
|
|
413
|
+
}
|
|
414
|
+
}, [confirmationQueue, isConfirmationVisible]);
|
|
415
|
+
|
|
416
|
+
// Process queue when queue changes or confirmation is hidden
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
processNextConfirmation();
|
|
419
|
+
}, [processNextConfirmation]);
|
|
420
|
+
|
|
421
|
+
const hideConfirmation = useCallback(() => {
|
|
422
|
+
setIsConfirmationVisible(false);
|
|
423
|
+
setConfirmingTool(undefined);
|
|
424
|
+
setCurrentConfirmation(null);
|
|
425
|
+
}, []);
|
|
426
|
+
|
|
427
|
+
const handleConfirmationDecision = useCallback(
|
|
428
|
+
(decision: PermissionDecision) => {
|
|
429
|
+
if (currentConfirmation) {
|
|
430
|
+
currentConfirmation.resolver(decision);
|
|
431
|
+
}
|
|
432
|
+
hideConfirmation();
|
|
433
|
+
},
|
|
434
|
+
[currentConfirmation, hideConfirmation],
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
const handleConfirmationCancel = useCallback(() => {
|
|
438
|
+
if (currentConfirmation) {
|
|
439
|
+
currentConfirmation.reject();
|
|
440
|
+
}
|
|
441
|
+
hideConfirmation();
|
|
442
|
+
}, [currentConfirmation, hideConfirmation]);
|
|
443
|
+
|
|
444
|
+
// Listen for Ctrl+O hotkey to toggle collapse/expand state and ESC to cancel confirmation
|
|
445
|
+
useInput((input, key) => {
|
|
446
|
+
if (key.ctrl && input === "o") {
|
|
447
|
+
// Clear terminal screen when expanded state changes
|
|
448
|
+
process.stdout.write("\x1Bc", () => {
|
|
449
|
+
setIsExpanded((prev) => {
|
|
450
|
+
const newExpanded = !prev;
|
|
451
|
+
return newExpanded;
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
// Handle ESC key to cancel confirmation
|
|
457
|
+
if (key.escape && isConfirmationVisible) {
|
|
458
|
+
handleConfirmationCancel();
|
|
459
|
+
}
|
|
460
|
+
});
|
|
461
|
+
|
|
297
462
|
const contextValue: ChatContextType = {
|
|
298
463
|
messages,
|
|
299
464
|
isLoading,
|
|
@@ -314,7 +479,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({ children }) => {
|
|
|
314
479
|
killBackgroundShell,
|
|
315
480
|
slashCommands,
|
|
316
481
|
hasSlashCommand,
|
|
317
|
-
|
|
482
|
+
subagentMessages,
|
|
483
|
+
permissionMode,
|
|
484
|
+
setPermissionMode,
|
|
485
|
+
isConfirmationVisible,
|
|
486
|
+
confirmingTool,
|
|
487
|
+
showConfirmation,
|
|
488
|
+
hideConfirmation,
|
|
489
|
+
handleConfirmationDecision,
|
|
490
|
+
handleConfirmationCancel,
|
|
318
491
|
};
|
|
319
492
|
|
|
320
493
|
return (
|
|
@@ -6,11 +6,14 @@ import {
|
|
|
6
6
|
AttachedImage,
|
|
7
7
|
} from "../managers/InputManager.js";
|
|
8
8
|
import { FileItem } from "../components/FileSelector.js";
|
|
9
|
+
import { PermissionMode } from "wave-agent-sdk";
|
|
10
|
+
import { logger } from "../utils/logger.js";
|
|
9
11
|
|
|
10
12
|
export const useInputManager = (
|
|
11
13
|
callbacks: Partial<InputManagerCallbacks> = {},
|
|
12
14
|
) => {
|
|
13
15
|
const managerRef = useRef<InputManager | null>(null);
|
|
16
|
+
const [isManagerReady, setIsManagerReady] = useState(false);
|
|
14
17
|
|
|
15
18
|
// React state that mirrors InputManager state
|
|
16
19
|
const [inputText, setInputText] = useState("");
|
|
@@ -37,6 +40,8 @@ export const useInputManager = (
|
|
|
37
40
|
});
|
|
38
41
|
const [showBashManager, setShowBashManager] = useState(false);
|
|
39
42
|
const [showMcpManager, setShowMcpManager] = useState(false);
|
|
43
|
+
const [permissionMode, setPermissionModeState] =
|
|
44
|
+
useState<PermissionMode>("default");
|
|
40
45
|
const [attachedImages, setAttachedImages] = useState<AttachedImage[]>([]);
|
|
41
46
|
|
|
42
47
|
// Create InputManager on mount and update callbacks when they change
|
|
@@ -44,6 +49,7 @@ export const useInputManager = (
|
|
|
44
49
|
if (!managerRef.current) {
|
|
45
50
|
// Create InputManager on first mount
|
|
46
51
|
const manager = new InputManager({
|
|
52
|
+
logger,
|
|
47
53
|
onInputTextChange: setInputText,
|
|
48
54
|
onCursorPositionChange: setCursorPosition,
|
|
49
55
|
onFileSelectorStateChange: (show, files, query, position) => {
|
|
@@ -64,14 +70,22 @@ export const useInputManager = (
|
|
|
64
70
|
onMcpManagerStateChange: (show) => {
|
|
65
71
|
setShowMcpManager(show);
|
|
66
72
|
},
|
|
73
|
+
onPermissionModeChange: (mode) => {
|
|
74
|
+
setPermissionModeState(mode);
|
|
75
|
+
callbacks.onPermissionModeChange?.(mode);
|
|
76
|
+
},
|
|
67
77
|
onImagesStateChange: setAttachedImages,
|
|
78
|
+
onShowBashManager: () => setShowBashManager(true),
|
|
79
|
+
onShowMcpManager: () => setShowMcpManager(true),
|
|
68
80
|
...callbacks,
|
|
69
81
|
});
|
|
70
82
|
|
|
71
83
|
managerRef.current = manager;
|
|
84
|
+
setIsManagerReady(true);
|
|
72
85
|
} else {
|
|
73
86
|
// Update callbacks on existing manager
|
|
74
87
|
managerRef.current.updateCallbacks({
|
|
88
|
+
logger,
|
|
75
89
|
onInputTextChange: setInputText,
|
|
76
90
|
onCursorPositionChange: setCursorPosition,
|
|
77
91
|
onFileSelectorStateChange: (show, files, query, position) => {
|
|
@@ -92,7 +106,13 @@ export const useInputManager = (
|
|
|
92
106
|
onMcpManagerStateChange: (show) => {
|
|
93
107
|
setShowMcpManager(show);
|
|
94
108
|
},
|
|
109
|
+
onPermissionModeChange: (mode) => {
|
|
110
|
+
setPermissionModeState(mode);
|
|
111
|
+
callbacks.onPermissionModeChange?.(mode);
|
|
112
|
+
},
|
|
95
113
|
onImagesStateChange: setAttachedImages,
|
|
114
|
+
onShowBashManager: () => setShowBashManager(true),
|
|
115
|
+
onShowMcpManager: () => setShowMcpManager(true),
|
|
96
116
|
...callbacks,
|
|
97
117
|
});
|
|
98
118
|
}
|
|
@@ -253,9 +273,12 @@ export const useInputManager = (
|
|
|
253
273
|
managerRef.current?.activateMemoryTypeSelector(message);
|
|
254
274
|
}, []);
|
|
255
275
|
|
|
256
|
-
const handleMemoryTypeSelect = useCallback(
|
|
257
|
-
|
|
258
|
-
|
|
276
|
+
const handleMemoryTypeSelect = useCallback(
|
|
277
|
+
async (type: "project" | "user") => {
|
|
278
|
+
await managerRef.current?.handleMemoryTypeSelect(type);
|
|
279
|
+
},
|
|
280
|
+
[],
|
|
281
|
+
);
|
|
259
282
|
|
|
260
283
|
const handleCancelMemoryTypeSelect = useCallback(() => {
|
|
261
284
|
managerRef.current?.handleCancelMemoryTypeSelect();
|
|
@@ -296,6 +319,11 @@ export const useInputManager = (
|
|
|
296
319
|
managerRef.current?.setCursorPosition(position);
|
|
297
320
|
}, []);
|
|
298
321
|
|
|
322
|
+
// Complex handlers that combine multiple operations
|
|
323
|
+
const handleBashHistoryExecuteAndSend = useCallback((command: string) => {
|
|
324
|
+
managerRef.current?.handleBashHistoryExecuteAndSend(command);
|
|
325
|
+
}, []);
|
|
326
|
+
|
|
299
327
|
return {
|
|
300
328
|
// State
|
|
301
329
|
inputText,
|
|
@@ -314,7 +342,9 @@ export const useInputManager = (
|
|
|
314
342
|
memoryMessage: memoryTypeSelectorState.message,
|
|
315
343
|
showBashManager,
|
|
316
344
|
showMcpManager,
|
|
345
|
+
permissionMode,
|
|
317
346
|
attachedImages,
|
|
347
|
+
isManagerReady,
|
|
318
348
|
|
|
319
349
|
// Methods
|
|
320
350
|
insertTextAtCursor,
|
|
@@ -369,6 +399,10 @@ export const useInputManager = (
|
|
|
369
399
|
setShowMcpManager: useCallback((show: boolean) => {
|
|
370
400
|
managerRef.current?.setShowMcpManager(show);
|
|
371
401
|
}, []),
|
|
402
|
+
setPermissionMode: useCallback((mode: PermissionMode) => {
|
|
403
|
+
setPermissionModeState(mode);
|
|
404
|
+
managerRef.current?.setPermissionMode(mode);
|
|
405
|
+
}, []),
|
|
372
406
|
|
|
373
407
|
// Image management
|
|
374
408
|
addImage: useCallback((imagePath: string, mimeType: string) => {
|
|
@@ -409,6 +443,9 @@ export const useInputManager = (
|
|
|
409
443
|
managerRef.current?.clearLongTextMap();
|
|
410
444
|
}, []),
|
|
411
445
|
|
|
446
|
+
// Complex handlers combining multiple operations
|
|
447
|
+
handleBashHistoryExecuteAndSend,
|
|
448
|
+
|
|
412
449
|
// Main input handler
|
|
413
450
|
handleInput: useCallback(
|
|
414
451
|
async (
|
package/src/index.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import yargs from "yargs";
|
|
2
2
|
import { hideBin } from "yargs/helpers";
|
|
3
3
|
import { startCli } from "./cli.js";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
listSessions,
|
|
6
|
+
getSessionFilePath,
|
|
7
|
+
getFirstMessageContent,
|
|
8
|
+
} from "wave-agent-sdk";
|
|
5
9
|
|
|
6
10
|
// Export main function for external use
|
|
7
11
|
export async function main() {
|
|
@@ -21,17 +25,29 @@ export async function main() {
|
|
|
21
25
|
description: "Print response without interactive mode",
|
|
22
26
|
type: "string",
|
|
23
27
|
})
|
|
28
|
+
.option("show-stats", {
|
|
29
|
+
description: "Show timing and usage statistics in print mode",
|
|
30
|
+
type: "boolean",
|
|
31
|
+
})
|
|
24
32
|
.option("list-sessions", {
|
|
25
33
|
description: "List all available sessions",
|
|
26
34
|
type: "boolean",
|
|
27
35
|
})
|
|
36
|
+
.option("dangerously-skip-permissions", {
|
|
37
|
+
description: "Skip all permission checks (dangerous)",
|
|
38
|
+
type: "boolean",
|
|
39
|
+
default: false,
|
|
40
|
+
})
|
|
28
41
|
.version()
|
|
29
42
|
.alias("v", "version")
|
|
30
43
|
.example("$0", "Start CLI with default settings")
|
|
31
44
|
.example("$0 --restore session_123", "Restore specific session")
|
|
32
45
|
.example("$0 --continue", "Continue from last session")
|
|
33
46
|
.example("$0 --print 'Hello'", "Send message in print mode")
|
|
34
|
-
.example(
|
|
47
|
+
.example(
|
|
48
|
+
"$0 -p 'Hello' --show-stats",
|
|
49
|
+
"Send message in print mode with statistics",
|
|
50
|
+
)
|
|
35
51
|
.example("$0 --list-sessions", "List all available sessions")
|
|
36
52
|
.help("h")
|
|
37
53
|
.parseAsync();
|
|
@@ -50,18 +66,39 @@ export async function main() {
|
|
|
50
66
|
console.log(`Available sessions for: ${currentWorkdir}`);
|
|
51
67
|
console.log("==========================================");
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
69
|
+
// Get last 5 sessions
|
|
70
|
+
const lastSessions = sessions.slice(0, 5);
|
|
71
|
+
|
|
72
|
+
for (const session of lastSessions) {
|
|
55
73
|
const lastActiveAt = new Date(session.lastActiveAt).toLocaleString();
|
|
74
|
+
const filePath = await getSessionFilePath(session.id, session.workdir);
|
|
75
|
+
|
|
76
|
+
// Get first message content
|
|
77
|
+
const firstMessageContent = await getFirstMessageContent(
|
|
78
|
+
session.id,
|
|
79
|
+
session.workdir,
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Truncate content if too long
|
|
83
|
+
let truncatedContent =
|
|
84
|
+
firstMessageContent || "No first message content";
|
|
85
|
+
if (truncatedContent.length > 30) {
|
|
86
|
+
truncatedContent = truncatedContent.substring(0, 30) + "...";
|
|
87
|
+
}
|
|
56
88
|
|
|
57
89
|
console.log(`ID: ${session.id}`);
|
|
58
90
|
console.log(` Workdir: ${session.workdir}`);
|
|
59
|
-
console.log(`
|
|
91
|
+
console.log(` File Path: ${filePath}`);
|
|
60
92
|
console.log(` Last Active: ${lastActiveAt}`);
|
|
61
93
|
console.log(` Last Message Tokens: ${session.latestTotalTokens}`);
|
|
94
|
+
console.log(` First Message: ${truncatedContent}`);
|
|
62
95
|
console.log("");
|
|
63
96
|
}
|
|
64
97
|
|
|
98
|
+
if (sessions.length > 5) {
|
|
99
|
+
console.log(`... and ${sessions.length - 5} more sessions`);
|
|
100
|
+
}
|
|
101
|
+
|
|
65
102
|
return;
|
|
66
103
|
} catch (error) {
|
|
67
104
|
console.error("Failed to list sessions:", error);
|
|
@@ -76,12 +113,15 @@ export async function main() {
|
|
|
76
113
|
restoreSessionId: argv.restore,
|
|
77
114
|
continueLastSession: argv.continue,
|
|
78
115
|
message: argv.print,
|
|
116
|
+
showStats: argv.showStats,
|
|
117
|
+
bypassPermissions: argv.dangerouslySkipPermissions,
|
|
79
118
|
});
|
|
80
119
|
}
|
|
81
120
|
|
|
82
121
|
await startCli({
|
|
83
122
|
restoreSessionId: argv.restore,
|
|
84
123
|
continueLastSession: argv.continue,
|
|
124
|
+
bypassPermissions: argv.dangerouslySkipPermissions,
|
|
85
125
|
});
|
|
86
126
|
}
|
|
87
127
|
|