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.
Files changed (93) hide show
  1. package/README.md +3 -3
  2. package/dist/cli.d.ts +1 -0
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +2 -2
  5. package/dist/components/App.d.ts +1 -0
  6. package/dist/components/App.d.ts.map +1 -1
  7. package/dist/components/App.js +4 -4
  8. package/dist/components/BashHistorySelector.d.ts.map +1 -1
  9. package/dist/components/BashHistorySelector.js +17 -3
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +6 -24
  12. package/dist/components/CommandSelector.js +4 -4
  13. package/dist/components/Confirmation.d.ts +11 -0
  14. package/dist/components/Confirmation.d.ts.map +1 -0
  15. package/dist/components/Confirmation.js +148 -0
  16. package/dist/components/DiffDisplay.d.ts +8 -0
  17. package/dist/components/DiffDisplay.d.ts.map +1 -0
  18. package/dist/components/DiffDisplay.js +168 -0
  19. package/dist/components/FileSelector.d.ts +2 -4
  20. package/dist/components/FileSelector.d.ts.map +1 -1
  21. package/dist/components/FileSelector.js +2 -2
  22. package/dist/components/InputBox.d.ts.map +1 -1
  23. package/dist/components/InputBox.js +30 -50
  24. package/dist/components/Markdown.d.ts +6 -0
  25. package/dist/components/Markdown.d.ts.map +1 -0
  26. package/dist/components/Markdown.js +22 -0
  27. package/dist/components/MemoryDisplay.js +1 -1
  28. package/dist/components/MessageItem.d.ts +8 -0
  29. package/dist/components/MessageItem.d.ts.map +1 -0
  30. package/dist/components/MessageItem.js +15 -0
  31. package/dist/components/MessageList.d.ts +1 -1
  32. package/dist/components/MessageList.d.ts.map +1 -1
  33. package/dist/components/MessageList.js +33 -33
  34. package/dist/components/ReasoningDisplay.d.ts +8 -0
  35. package/dist/components/ReasoningDisplay.d.ts.map +1 -0
  36. package/dist/components/ReasoningDisplay.js +10 -0
  37. package/dist/components/SubagentBlock.d.ts +0 -1
  38. package/dist/components/SubagentBlock.d.ts.map +1 -1
  39. package/dist/components/SubagentBlock.js +29 -30
  40. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  41. package/dist/components/ToolResultDisplay.js +6 -5
  42. package/dist/contexts/useChat.d.ts +14 -2
  43. package/dist/contexts/useChat.d.ts.map +1 -1
  44. package/dist/contexts/useChat.js +128 -17
  45. package/dist/hooks/useInputManager.d.ts +6 -1
  46. package/dist/hooks/useInputManager.d.ts.map +1 -1
  47. package/dist/hooks/useInputManager.js +32 -2
  48. package/dist/index.d.ts.map +1 -1
  49. package/dist/index.js +30 -5
  50. package/dist/managers/InputManager.d.ts +11 -1
  51. package/dist/managers/InputManager.d.ts.map +1 -1
  52. package/dist/managers/InputManager.js +77 -26
  53. package/dist/print-cli.d.ts +2 -0
  54. package/dist/print-cli.d.ts.map +1 -1
  55. package/dist/print-cli.js +121 -23
  56. package/dist/utils/toolParameterTransforms.d.ts +23 -0
  57. package/dist/utils/toolParameterTransforms.d.ts.map +1 -0
  58. package/dist/utils/toolParameterTransforms.js +77 -0
  59. package/dist/utils/usageSummary.d.ts +6 -0
  60. package/dist/utils/usageSummary.d.ts.map +1 -1
  61. package/dist/utils/usageSummary.js +72 -0
  62. package/package.json +13 -8
  63. package/src/cli.tsx +3 -1
  64. package/src/components/App.tsx +7 -3
  65. package/src/components/BashHistorySelector.tsx +26 -3
  66. package/src/components/ChatInterface.tsx +38 -54
  67. package/src/components/CommandSelector.tsx +5 -5
  68. package/src/components/Confirmation.tsx +253 -0
  69. package/src/components/DiffDisplay.tsx +300 -0
  70. package/src/components/FileSelector.tsx +4 -6
  71. package/src/components/InputBox.tsx +58 -87
  72. package/src/components/Markdown.tsx +29 -0
  73. package/src/components/MemoryDisplay.tsx +1 -1
  74. package/src/components/MessageItem.tsx +96 -0
  75. package/src/components/MessageList.tsx +140 -202
  76. package/src/components/ReasoningDisplay.tsx +33 -0
  77. package/src/components/SubagentBlock.tsx +56 -84
  78. package/src/components/ToolResultDisplay.tsx +9 -5
  79. package/src/contexts/useChat.tsx +194 -21
  80. package/src/hooks/useInputManager.ts +40 -3
  81. package/src/index.ts +45 -5
  82. package/src/managers/InputManager.ts +101 -27
  83. package/src/print-cli.ts +143 -21
  84. package/src/utils/toolParameterTransforms.ts +104 -0
  85. package/src/utils/usageSummary.ts +109 -0
  86. package/dist/components/DiffViewer.d.ts +0 -9
  87. package/dist/components/DiffViewer.d.ts.map +0 -1
  88. package/dist/components/DiffViewer.js +0 -221
  89. package/dist/utils/fileSearch.d.ts +0 -20
  90. package/dist/utils/fileSearch.d.ts.map +0 -1
  91. package/dist/utils/fileSearch.js +0 -102
  92. package/src/components/DiffViewer.tsx +0 -321
  93. 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, isRunning, success, error, name } =
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 (isRunning) return "yellow";
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 (isRunning) return "🔄";
29
+ if (stage === "running") return "🔄";
29
30
  if (success) return "";
30
- if (error || success === false) return "❌ Failed";
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"> ({compactParams})</Text>
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
  };
@@ -13,9 +13,14 @@ import type {
13
13
  McpServerStatus,
14
14
  BackgroundShell,
15
15
  SlashCommand,
16
- Usage,
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
- // Usage tracking
55
- usages: Usage[];
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> = ({ children }) => {
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
- // Usage tracking state
99
- const [usages, setUsages] = useState<Usage[]>([]);
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
- // Listen for Ctrl+O hotkey to toggle collapse/expand state
104
- useInput((input, key) => {
105
- if (key.ctrl && input === "o") {
106
- setIsExpanded((prev) => !prev);
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
- setSessionId(sessionId);
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
- onUsagesChange: (newUsages) => {
136
- setUsages([...newUsages]);
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
- }, [restoreSessionId, continueLastSession]);
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
- usages,
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((type: "project" | "user") => {
257
- managerRef.current?.handleMemoryTypeSelect(type);
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 { listSessions } from "wave-agent-sdk";
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("$0 -p 'Hello'", "Send message in print mode (short)")
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
- for (const session of sessions) {
54
- const startedAt = new Date(session.startedAt).toLocaleString();
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(` Started: ${startedAt}`);
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