wave-code 0.2.1 → 0.5.0

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 (156) hide show
  1. package/dist/commands/plugin/disable.d.ts +2 -1
  2. package/dist/commands/plugin/disable.d.ts.map +1 -1
  3. package/dist/commands/plugin/disable.js +3 -2
  4. package/dist/commands/plugin/enable.d.ts +2 -1
  5. package/dist/commands/plugin/enable.d.ts.map +1 -1
  6. package/dist/commands/plugin/enable.js +3 -2
  7. package/dist/commands/plugin/install.d.ts +2 -1
  8. package/dist/commands/plugin/install.d.ts.map +1 -1
  9. package/dist/commands/plugin/list.d.ts.map +1 -1
  10. package/dist/commands/plugin/list.js +15 -3
  11. package/dist/commands/plugin/marketplace.d.ts +3 -0
  12. package/dist/commands/plugin/marketplace.d.ts.map +1 -1
  13. package/dist/commands/plugin/marketplace.js +15 -1
  14. package/dist/commands/plugin/uninstall.d.ts +4 -0
  15. package/dist/commands/plugin/uninstall.d.ts.map +1 -0
  16. package/dist/commands/plugin/uninstall.js +29 -0
  17. package/dist/commands/plugin/update.d.ts +4 -0
  18. package/dist/commands/plugin/update.d.ts.map +1 -0
  19. package/dist/commands/plugin/update.js +15 -0
  20. package/dist/components/ChatInterface.d.ts.map +1 -1
  21. package/dist/components/ChatInterface.js +2 -2
  22. package/dist/components/CommandSelector.d.ts.map +1 -1
  23. package/dist/components/CommandSelector.js +9 -3
  24. package/dist/components/Confirmation.d.ts.map +1 -1
  25. package/dist/components/Confirmation.js +73 -20
  26. package/dist/components/DiffDisplay.d.ts +0 -1
  27. package/dist/components/DiffDisplay.d.ts.map +1 -1
  28. package/dist/components/DiffDisplay.js +38 -59
  29. package/dist/components/DiscoverView.d.ts +3 -0
  30. package/dist/components/DiscoverView.d.ts.map +1 -0
  31. package/dist/components/DiscoverView.js +25 -0
  32. package/dist/components/FileSelector.js +1 -1
  33. package/dist/components/HistorySearch.d.ts +8 -0
  34. package/dist/components/HistorySearch.d.ts.map +1 -0
  35. package/dist/components/HistorySearch.js +67 -0
  36. package/dist/components/InputBox.d.ts +1 -1
  37. package/dist/components/InputBox.d.ts.map +1 -1
  38. package/dist/components/InputBox.js +29 -19
  39. package/dist/components/InstalledView.d.ts +3 -0
  40. package/dist/components/InstalledView.d.ts.map +1 -0
  41. package/dist/components/InstalledView.js +30 -0
  42. package/dist/components/Markdown.d.ts.map +1 -1
  43. package/dist/components/Markdown.js +24 -10
  44. package/dist/components/MarketplaceAddForm.d.ts +3 -0
  45. package/dist/components/MarketplaceAddForm.d.ts.map +1 -0
  46. package/dist/components/MarketplaceAddForm.js +26 -0
  47. package/dist/components/MarketplaceDetail.d.ts +3 -0
  48. package/dist/components/MarketplaceDetail.d.ts.map +1 -0
  49. package/dist/components/MarketplaceDetail.js +38 -0
  50. package/dist/components/MarketplaceList.d.ts +9 -0
  51. package/dist/components/MarketplaceList.d.ts.map +1 -0
  52. package/dist/components/MarketplaceList.js +16 -0
  53. package/dist/components/MarketplaceView.d.ts +3 -0
  54. package/dist/components/MarketplaceView.d.ts.map +1 -0
  55. package/dist/components/MarketplaceView.js +28 -0
  56. package/dist/components/PlanDisplay.d.ts.map +1 -1
  57. package/dist/components/PlanDisplay.js +2 -2
  58. package/dist/components/PluginDetail.d.ts +3 -0
  59. package/dist/components/PluginDetail.d.ts.map +1 -0
  60. package/dist/components/PluginDetail.js +63 -0
  61. package/dist/components/PluginList.d.ts +14 -0
  62. package/dist/components/PluginList.d.ts.map +1 -0
  63. package/dist/components/PluginList.js +12 -0
  64. package/dist/components/PluginManagerShell.d.ts +5 -0
  65. package/dist/components/PluginManagerShell.d.ts.map +1 -0
  66. package/dist/components/PluginManagerShell.js +89 -0
  67. package/dist/components/PluginManagerTypes.d.ts +33 -0
  68. package/dist/components/PluginManagerTypes.d.ts.map +1 -0
  69. package/dist/components/PluginManagerTypes.js +1 -0
  70. package/dist/components/RewindCommand.d.ts +9 -0
  71. package/dist/components/RewindCommand.d.ts.map +1 -0
  72. package/dist/components/RewindCommand.js +42 -0
  73. package/dist/components/SessionSelector.d.ts +11 -0
  74. package/dist/components/SessionSelector.d.ts.map +1 -0
  75. package/dist/components/SessionSelector.js +38 -0
  76. package/dist/components/SubagentBlock.d.ts.map +1 -1
  77. package/dist/components/SubagentBlock.js +24 -1
  78. package/dist/components/TaskManager.d.ts +6 -0
  79. package/dist/components/TaskManager.d.ts.map +1 -0
  80. package/dist/components/TaskManager.js +114 -0
  81. package/dist/components/ToolResultDisplay.d.ts.map +1 -1
  82. package/dist/components/ToolResultDisplay.js +2 -1
  83. package/dist/contexts/PluginManagerContext.d.ts +4 -0
  84. package/dist/contexts/PluginManagerContext.d.ts.map +1 -0
  85. package/dist/contexts/PluginManagerContext.js +9 -0
  86. package/dist/contexts/useChat.d.ts +7 -4
  87. package/dist/contexts/useChat.d.ts.map +1 -1
  88. package/dist/contexts/useChat.js +37 -12
  89. package/dist/hooks/useInputManager.d.ts +8 -16
  90. package/dist/hooks/useInputManager.d.ts.map +1 -1
  91. package/dist/hooks/useInputManager.js +39 -55
  92. package/dist/hooks/usePluginManager.d.ts +3 -0
  93. package/dist/hooks/usePluginManager.d.ts.map +1 -0
  94. package/dist/hooks/usePluginManager.js +227 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +150 -177
  97. package/dist/managers/InputManager.d.ts +18 -26
  98. package/dist/managers/InputManager.d.ts.map +1 -1
  99. package/dist/managers/InputManager.js +93 -119
  100. package/dist/plugin-manager-cli.d.ts +6 -0
  101. package/dist/plugin-manager-cli.d.ts.map +1 -0
  102. package/dist/plugin-manager-cli.js +12 -0
  103. package/dist/session-selector-cli.d.ts +2 -0
  104. package/dist/session-selector-cli.d.ts.map +1 -0
  105. package/dist/session-selector-cli.js +25 -0
  106. package/package.json +9 -5
  107. package/src/commands/plugin/disable.ts +7 -3
  108. package/src/commands/plugin/enable.ts +7 -3
  109. package/src/commands/plugin/install.ts +2 -1
  110. package/src/commands/plugin/list.ts +21 -3
  111. package/src/commands/plugin/marketplace.ts +17 -1
  112. package/src/commands/plugin/uninstall.ts +39 -0
  113. package/src/commands/plugin/update.ts +19 -0
  114. package/src/components/ChatInterface.tsx +2 -1
  115. package/src/components/CommandSelector.tsx +10 -3
  116. package/src/components/Confirmation.tsx +115 -25
  117. package/src/components/DiffDisplay.tsx +60 -106
  118. package/src/components/DiscoverView.tsx +31 -0
  119. package/src/components/FileSelector.tsx +1 -1
  120. package/src/components/HistorySearch.tsx +148 -0
  121. package/src/components/InputBox.tsx +51 -34
  122. package/src/components/InstalledView.tsx +61 -0
  123. package/src/components/Markdown.tsx +44 -28
  124. package/src/components/MarketplaceAddForm.tsx +39 -0
  125. package/src/components/MarketplaceDetail.tsx +79 -0
  126. package/src/components/MarketplaceList.tsx +52 -0
  127. package/src/components/MarketplaceView.tsx +43 -0
  128. package/src/components/PlanDisplay.tsx +14 -19
  129. package/src/components/PluginDetail.tsx +147 -0
  130. package/src/components/PluginList.tsx +51 -0
  131. package/src/components/PluginManagerShell.tsx +189 -0
  132. package/src/components/PluginManagerTypes.ts +47 -0
  133. package/src/components/RewindCommand.tsx +114 -0
  134. package/src/components/SessionSelector.tsx +127 -0
  135. package/src/components/SubagentBlock.tsx +34 -1
  136. package/src/components/{BashShellManager.tsx → TaskManager.tsx} +79 -75
  137. package/src/components/ToolResultDisplay.tsx +6 -2
  138. package/src/contexts/PluginManagerContext.ts +15 -0
  139. package/src/contexts/useChat.tsx +51 -20
  140. package/src/hooks/useInputManager.ts +39 -71
  141. package/src/hooks/usePluginManager.ts +302 -0
  142. package/src/index.ts +241 -280
  143. package/src/managers/InputManager.ts +113 -162
  144. package/src/plugin-manager-cli.tsx +13 -0
  145. package/src/session-selector-cli.tsx +37 -0
  146. package/dist/components/BashHistorySelector.d.ts +0 -11
  147. package/dist/components/BashHistorySelector.d.ts.map +0 -1
  148. package/dist/components/BashHistorySelector.js +0 -93
  149. package/dist/components/BashShellManager.d.ts +0 -6
  150. package/dist/components/BashShellManager.d.ts.map +0 -1
  151. package/dist/components/BashShellManager.js +0 -116
  152. package/dist/hooks/usePagination.d.ts +0 -20
  153. package/dist/hooks/usePagination.d.ts.map +0 -1
  154. package/dist/hooks/usePagination.js +0 -168
  155. package/src/components/BashHistorySelector.tsx +0 -181
  156. package/src/hooks/usePagination.ts +0 -203
@@ -11,7 +11,7 @@ import { useAppConfig } from "./useAppConfig.js";
11
11
  import type {
12
12
  Message,
13
13
  McpServerStatus,
14
- BackgroundShell,
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 bash shells
51
- backgroundShells: BackgroundShell[];
52
- getBackgroundShellOutput: (
53
- shellId: string,
50
+ // Background tasks
51
+ backgroundTasks: BackgroundTask[];
52
+ getBackgroundTaskOutput: (
53
+ taskId: string,
54
54
  ) => { stdout: string; stderr: string; status: string } | null;
55
- killBackgroundShell: (shellId: string) => boolean;
55
+ stopBackgroundTask: (taskId: string) => boolean;
56
56
  // Slash Command functionality
57
57
  slashCommands: SlashCommand[];
58
58
  hasSlashCommand: (commandId: string) => boolean;
@@ -78,6 +78,11 @@ export interface ChatContextType {
78
78
  hideConfirmation: () => void;
79
79
  handleConfirmationDecision: (decision: PermissionDecision) => void;
80
80
  handleConfirmationCancel: () => void;
81
+ // Background current task
82
+ backgroundCurrentTask: () => void;
83
+ // Rewind functionality
84
+ rewindId: number;
85
+ handleRewindSelect: (index: number) => Promise<void>;
81
86
  }
82
87
 
83
88
  const ChatContext = createContext<ChatContextType | null>(null);
@@ -118,10 +123,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
118
123
  // MCP State
119
124
  const [mcpServers, setMcpServers] = useState<McpServerStatus[]>([]);
120
125
 
121
- // Background bash shells state
122
- const [backgroundShells, setBackgroundShells] = useState<BackgroundShell[]>(
123
- [],
124
- );
126
+ // Background tasks state
127
+ const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
125
128
 
126
129
  // Command state
127
130
  const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
@@ -165,6 +168,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
165
168
  reject: () => void;
166
169
  } | null>(null);
167
170
 
171
+ // Rewind state
172
+ const [rewindId, setRewindId] = useState(0);
173
+
168
174
  const agentRef = useRef<Agent | null>(null);
169
175
 
170
176
  // Permission confirmation methods with queue support
@@ -216,8 +222,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
216
222
  onCompressionStateChange: (isCompressingState) => {
217
223
  setIsCompressing(isCompressingState);
218
224
  },
219
- onShellsChange: (shells) => {
220
- setBackgroundShells([...shells]);
225
+ onTasksChange: (tasks) => {
226
+ setBackgroundTasks([...tasks]);
221
227
  },
222
228
  onSubagentMessagesChange: (subagentId, messages) => {
223
229
  logger.debug("onSubagentMessagesChange", subagentId, messages.length);
@@ -229,6 +235,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
229
235
  onPermissionModeChange: (mode) => {
230
236
  setPermissionModeState(mode);
231
237
  },
238
+ onSlashCommandsChange: (commands) => {
239
+ setSlashCommands([...commands]);
240
+ },
232
241
  };
233
242
 
234
243
  try {
@@ -413,15 +422,15 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
413
422
  return (await agentRef.current?.disconnectMcpServer(serverName)) ?? false;
414
423
  }, []);
415
424
 
416
- // Background bash management methods - delegate to Agent
417
- const getBackgroundShellOutput = useCallback((shellId: string) => {
425
+ // Background task management methods - delegate to Agent
426
+ const getBackgroundTaskOutput = useCallback((taskId: string) => {
418
427
  if (!agentRef.current) return null;
419
- return agentRef.current.getBackgroundShellOutput(shellId);
428
+ return agentRef.current.getBackgroundTaskOutput(taskId);
420
429
  }, []);
421
430
 
422
- const killBackgroundShell = useCallback((shellId: string) => {
431
+ const stopBackgroundTask = useCallback((taskId: string) => {
423
432
  if (!agentRef.current) return false;
424
- return agentRef.current.killBackgroundShell(shellId);
433
+ return agentRef.current.stopBackgroundTask(taskId);
425
434
  }, []);
426
435
 
427
436
  const hasSlashCommand = useCallback((commandId: string) => {
@@ -473,6 +482,25 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
473
482
  hideConfirmation();
474
483
  }, [currentConfirmation, hideConfirmation]);
475
484
 
485
+ const backgroundCurrentTask = useCallback(() => {
486
+ agentRef.current?.backgroundCurrentTask();
487
+ }, []);
488
+
489
+ const handleRewindSelect = useCallback(async (index: number) => {
490
+ if (agentRef.current) {
491
+ try {
492
+ await agentRef.current.truncateHistory(index);
493
+
494
+ // Clear terminal screen after rewind
495
+ process.stdout.write("\x1Bc", () => {
496
+ setRewindId((prev) => prev + 1);
497
+ });
498
+ } catch (error) {
499
+ logger.error("Failed to rewind:", error);
500
+ }
501
+ }
502
+ }, []);
503
+
476
504
  // Listen for Ctrl+O hotkey to toggle collapse/expand state and ESC to cancel confirmation
477
505
  useInput((input, key) => {
478
506
  if (key.ctrl && input === "o") {
@@ -506,9 +534,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
506
534
  mcpServers,
507
535
  connectMcpServer,
508
536
  disconnectMcpServer,
509
- backgroundShells,
510
- getBackgroundShellOutput,
511
- killBackgroundShell,
537
+ backgroundTasks,
538
+ getBackgroundTaskOutput,
539
+ stopBackgroundTask,
512
540
  slashCommands,
513
541
  hasSlashCommand,
514
542
  subagentMessages,
@@ -520,6 +548,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
520
548
  hideConfirmation,
521
549
  handleConfirmationDecision,
522
550
  handleConfirmationCancel,
551
+ backgroundCurrentTask,
552
+ rewindId,
553
+ handleRewindSelect,
523
554
  };
524
555
 
525
556
  return (
@@ -29,17 +29,17 @@ export const useInputManager = (
29
29
  query: "",
30
30
  position: -1,
31
31
  });
32
- const [bashHistorySelectorState, setBashHistorySelectorState] = useState({
32
+ const [historySearchState, setHistorySearchState] = useState({
33
33
  show: false,
34
34
  query: "",
35
- position: -1,
36
35
  });
37
36
  const [memoryTypeSelectorState, setMemoryTypeSelectorState] = useState({
38
37
  show: false,
39
38
  message: "",
40
39
  });
41
- const [showBashManager, setShowBashManager] = useState(false);
40
+ const [showTaskManager, setShowTaskManager] = useState(false);
42
41
  const [showMcpManager, setShowMcpManager] = useState(false);
42
+ const [showRewindManager, setShowRewindManager] = useState(false);
43
43
  const [permissionMode, setPermissionModeState] =
44
44
  useState<PermissionMode>("default");
45
45
  const [attachedImages, setAttachedImages] = useState<AttachedImage[]>([]);
@@ -58,25 +58,29 @@ export const useInputManager = (
58
58
  onCommandSelectorStateChange: (show, query, position) => {
59
59
  setCommandSelectorState({ show, query, position });
60
60
  },
61
- onBashHistorySelectorStateChange: (show, query, position) => {
62
- setBashHistorySelectorState({ show, query, position });
61
+ onHistorySearchStateChange: (show, query) => {
62
+ setHistorySearchState({ show, query });
63
63
  },
64
64
  onMemoryTypeSelectorStateChange: (show, message) => {
65
65
  setMemoryTypeSelectorState({ show, message });
66
66
  },
67
- onBashManagerStateChange: (show) => {
68
- setShowBashManager(show);
67
+ onTaskManagerStateChange: (show) => {
68
+ setShowTaskManager(show);
69
69
  },
70
70
  onMcpManagerStateChange: (show) => {
71
71
  setShowMcpManager(show);
72
72
  },
73
+ onRewindManagerStateChange: (show) => {
74
+ setShowRewindManager(show);
75
+ },
73
76
  onPermissionModeChange: (mode) => {
74
77
  setPermissionModeState(mode);
75
78
  callbacks.onPermissionModeChange?.(mode);
76
79
  },
77
80
  onImagesStateChange: setAttachedImages,
78
- onShowBashManager: () => setShowBashManager(true),
81
+ onShowTaskManager: () => setShowTaskManager(true),
79
82
  onShowMcpManager: () => setShowMcpManager(true),
83
+ onShowRewindManager: () => setShowRewindManager(true),
80
84
  ...callbacks,
81
85
  });
82
86
 
@@ -94,25 +98,29 @@ export const useInputManager = (
94
98
  onCommandSelectorStateChange: (show, query, position) => {
95
99
  setCommandSelectorState({ show, query, position });
96
100
  },
97
- onBashHistorySelectorStateChange: (show, query, position) => {
98
- setBashHistorySelectorState({ show, query, position });
101
+ onHistorySearchStateChange: (show, query) => {
102
+ setHistorySearchState({ show, query });
99
103
  },
100
104
  onMemoryTypeSelectorStateChange: (show, message) => {
101
105
  setMemoryTypeSelectorState({ show, message });
102
106
  },
103
- onBashManagerStateChange: (show) => {
104
- setShowBashManager(show);
107
+ onTaskManagerStateChange: (show) => {
108
+ setShowTaskManager(show);
105
109
  },
106
110
  onMcpManagerStateChange: (show) => {
107
111
  setShowMcpManager(show);
108
112
  },
113
+ onRewindManagerStateChange: (show) => {
114
+ setShowRewindManager(show);
115
+ },
109
116
  onPermissionModeChange: (mode) => {
110
117
  setPermissionModeState(mode);
111
118
  callbacks.onPermissionModeChange?.(mode);
112
119
  },
113
120
  onImagesStateChange: setAttachedImages,
114
- onShowBashManager: () => setShowBashManager(true),
121
+ onShowTaskManager: () => setShowTaskManager(true),
115
122
  onShowMcpManager: () => setShowMcpManager(true),
123
+ onShowRewindManager: () => setShowRewindManager(true),
116
124
  ...callbacks,
117
125
  });
118
126
  }
@@ -235,37 +243,13 @@ export const useInputManager = (
235
243
  return managerRef.current?.checkForSlashDeletion(cursorPos) || false;
236
244
  }, []);
237
245
 
238
- // Bash history selector methods
239
- const activateBashHistorySelector = useCallback((position: number) => {
240
- managerRef.current?.activateBashHistorySelector(position);
241
- }, []);
242
-
243
- const handleBashHistorySelect = useCallback(
244
- (command: string) => {
245
- return (
246
- managerRef.current?.handleBashHistorySelect(command) || {
247
- newInput: inputText,
248
- newCursorPosition: cursorPosition,
249
- }
250
- );
251
- },
252
- [inputText, cursorPosition],
253
- );
254
-
255
- const handleCancelBashHistorySelect = useCallback(() => {
256
- managerRef.current?.handleCancelBashHistorySelect();
257
- }, []);
258
-
259
- const updateBashHistorySearchQuery = useCallback((query: string) => {
260
- managerRef.current?.updateBashHistorySearchQuery(query);
246
+ // History search methods
247
+ const handleHistorySearchSelect = useCallback((prompt: string) => {
248
+ managerRef.current?.handleHistorySearchSelect(prompt);
261
249
  }, []);
262
250
 
263
- const handleBashHistoryExecute = useCallback((command: string) => {
264
- return managerRef.current?.handleBashHistoryExecute(command) || command;
265
- }, []);
266
-
267
- const checkForExclamationDeletion = useCallback((cursorPos: number) => {
268
- return managerRef.current?.checkForExclamationDeletion(cursorPos) || false;
251
+ const handleCancelHistorySearch = useCallback(() => {
252
+ managerRef.current?.handleCancelHistorySearch();
269
253
  }, []);
270
254
 
271
255
  // Memory type selector methods
@@ -320,17 +304,6 @@ export const useInputManager = (
320
304
  }, []);
321
305
 
322
306
  // Complex handlers that combine multiple operations
323
- const handleBashHistoryExecuteAndSend = useCallback((command: string) => {
324
- managerRef.current?.handleBashHistoryExecuteAndSend(command);
325
- }, []);
326
-
327
- const handleBashHistoryDelete = useCallback(
328
- (command: string, workdir?: string) => {
329
- managerRef.current?.handleBashHistoryDelete(command, workdir);
330
- },
331
- [],
332
- );
333
-
334
307
  return {
335
308
  // State
336
309
  inputText,
@@ -342,13 +315,13 @@ export const useInputManager = (
342
315
  showCommandSelector: commandSelectorState.show,
343
316
  commandSearchQuery: commandSelectorState.query,
344
317
  slashPosition: commandSelectorState.position,
345
- showBashHistorySelector: bashHistorySelectorState.show,
346
- bashHistorySearchQuery: bashHistorySelectorState.query,
347
- exclamationPosition: bashHistorySelectorState.position,
318
+ showHistorySearch: historySearchState.show,
319
+ historySearchQuery: historySearchState.query,
348
320
  showMemoryTypeSelector: memoryTypeSelectorState.show,
349
321
  memoryMessage: memoryTypeSelectorState.message,
350
- showBashManager,
322
+ showTaskManager,
351
323
  showMcpManager,
324
+ showRewindManager,
352
325
  permissionMode,
353
326
  attachedImages,
354
327
  isManagerReady,
@@ -377,14 +350,9 @@ export const useInputManager = (
377
350
  updateCommandSearchQuery,
378
351
  checkForSlashDeletion,
379
352
 
380
- // Bash history selector
381
- activateBashHistorySelector,
382
- handleBashHistorySelect,
383
- handleCancelBashHistorySelect,
384
- updateBashHistorySearchQuery,
385
-
386
- handleBashHistoryExecute,
387
- checkForExclamationDeletion,
353
+ // History search
354
+ handleHistorySearchSelect,
355
+ handleCancelHistorySearch,
388
356
 
389
357
  // Memory type selector
390
358
  activateMemoryTypeSelector,
@@ -400,12 +368,16 @@ export const useInputManager = (
400
368
  handleSpecialCharInput,
401
369
 
402
370
  // Bash/MCP Manager
403
- setShowBashManager: useCallback((show: boolean) => {
404
- managerRef.current?.setShowBashManager(show);
371
+ setShowTaskManager: useCallback((show: boolean) => {
372
+ managerRef.current?.setShowTaskManager(show);
405
373
  }, []),
406
374
  setShowMcpManager: useCallback((show: boolean) => {
407
375
  managerRef.current?.setShowMcpManager(show);
408
376
  }, []),
377
+ setShowRewindManager: useCallback((show: boolean) => {
378
+ managerRef.current?.setShowRewindManager(show);
379
+ setShowRewindManager(show);
380
+ }, []),
409
381
  setPermissionMode: useCallback((mode: PermissionMode) => {
410
382
  setPermissionModeState(mode);
411
383
  managerRef.current?.setPermissionMode(mode);
@@ -450,10 +422,6 @@ export const useInputManager = (
450
422
  managerRef.current?.clearLongTextMap();
451
423
  }, []),
452
424
 
453
- // Complex handlers combining multiple operations
454
- handleBashHistoryExecuteAndSend,
455
- handleBashHistoryDelete,
456
-
457
425
  // Main input handler
458
426
  handleInput: useCallback(
459
427
  async (
@@ -0,0 +1,302 @@
1
+ import { useState, useCallback, useEffect, useMemo } from "react";
2
+ import {
3
+ MarketplaceService,
4
+ PluginScopeManager,
5
+ ConfigurationService,
6
+ PluginManager,
7
+ KnownMarketplace,
8
+ InstalledPlugin,
9
+ MarketplacePluginEntry,
10
+ } from "wave-agent-sdk";
11
+ import {
12
+ PluginManagerState,
13
+ ViewType,
14
+ PluginManagerContextType,
15
+ } from "../components/PluginManagerTypes.js";
16
+
17
+ export function usePluginManager(): PluginManagerContextType {
18
+ const [state, setState] = useState<PluginManagerState>({
19
+ currentView: "DISCOVER",
20
+ selectedId: null,
21
+ isLoading: true,
22
+ error: null,
23
+ searchQuery: "",
24
+ });
25
+
26
+ const [marketplaces, setMarketplaces] = useState<KnownMarketplace[]>([]);
27
+ const [installedPlugins, setInstalledPlugins] = useState<
28
+ (InstalledPlugin & { enabled: boolean })[]
29
+ >([]);
30
+ const [discoverablePlugins, setDiscoverablePlugins] = useState<
31
+ (MarketplacePluginEntry & {
32
+ marketplace: string;
33
+ installed: boolean;
34
+ version?: string;
35
+ })[]
36
+ >([]);
37
+
38
+ const marketplaceService = useMemo(() => new MarketplaceService(), []);
39
+ const configurationService = useMemo(() => new ConfigurationService(), []);
40
+ const pluginManager = useMemo(
41
+ () =>
42
+ new PluginManager({
43
+ workdir: process.cwd(),
44
+ configurationService,
45
+ }),
46
+ [configurationService],
47
+ );
48
+ const pluginScopeManager = useMemo(
49
+ () =>
50
+ new PluginScopeManager({
51
+ workdir: process.cwd(),
52
+ configurationService,
53
+ pluginManager,
54
+ }),
55
+ [configurationService, pluginManager],
56
+ );
57
+
58
+ const refresh = useCallback(async () => {
59
+ setState((prev: PluginManagerState) => ({
60
+ ...prev,
61
+ isLoading: true,
62
+ error: null,
63
+ }));
64
+ try {
65
+ const [mks, installed, enabledMap] = await Promise.all([
66
+ marketplaceService.listMarketplaces(),
67
+ marketplaceService.getInstalledPlugins(),
68
+ Promise.resolve(pluginScopeManager.getMergedEnabledPlugins()),
69
+ ]);
70
+
71
+ setMarketplaces(mks);
72
+ const allInstalledWithEnabled = installed.plugins.map((p) => {
73
+ const pluginId = `${p.name}@${p.marketplace}`;
74
+ return {
75
+ ...p,
76
+ enabled: !!enabledMap[pluginId],
77
+ scope: pluginScopeManager.findPluginScope(pluginId) || undefined,
78
+ };
79
+ });
80
+
81
+ // Only show enabled plugins in the "Installed" view
82
+ setInstalledPlugins(allInstalledWithEnabled.filter((p) => p.enabled));
83
+
84
+ const allDiscoverable: (MarketplacePluginEntry & {
85
+ marketplace: string;
86
+ installed: boolean;
87
+ version?: string;
88
+ })[] = [];
89
+ for (const mk of mks) {
90
+ try {
91
+ const manifest = await marketplaceService.loadMarketplaceManifest(
92
+ marketplaceService.getMarketplacePath(mk),
93
+ );
94
+ manifest.plugins.forEach((p) => {
95
+ const pluginId = `${p.name}@${mk.name}`;
96
+ const isInstalled = installed.plugins.find(
97
+ (ip) => ip.name === p.name && ip.marketplace === mk.name,
98
+ );
99
+ const isEnabled = !!enabledMap[pluginId];
100
+
101
+ // Show in Discover if not installed OR if installed but not enabled in current scope
102
+ if (!isInstalled || !isEnabled) {
103
+ allDiscoverable.push({
104
+ ...p,
105
+ marketplace: mk.name,
106
+ installed: !!isInstalled,
107
+ });
108
+ }
109
+ });
110
+ } catch {
111
+ // Skip marketplaces that fail to load
112
+ }
113
+ }
114
+ setDiscoverablePlugins(allDiscoverable);
115
+ setState((prev: PluginManagerState) => ({ ...prev, isLoading: false }));
116
+ } catch (error) {
117
+ setState((prev: PluginManagerState) => ({
118
+ ...prev,
119
+ isLoading: false,
120
+ error: error instanceof Error ? error.message : String(error),
121
+ }));
122
+ }
123
+ }, [marketplaceService, pluginScopeManager]);
124
+
125
+ useEffect(() => {
126
+ refresh();
127
+ }, [refresh]);
128
+
129
+ const setView = useCallback((view: ViewType) => {
130
+ setState((prev: PluginManagerState) => ({ ...prev, currentView: view }));
131
+ }, []);
132
+
133
+ const setSelectedId = useCallback((id: string | null) => {
134
+ setState((prev: PluginManagerState) => ({ ...prev, selectedId: id }));
135
+ }, []);
136
+
137
+ const addMarketplace = useCallback(
138
+ async (source: string) => {
139
+ setState((prev: PluginManagerState) => ({
140
+ ...prev,
141
+ isLoading: true,
142
+ error: null,
143
+ }));
144
+ try {
145
+ await marketplaceService.addMarketplace(source);
146
+ await refresh();
147
+ } catch (error) {
148
+ setState((prev: PluginManagerState) => ({
149
+ ...prev,
150
+ isLoading: false,
151
+ error: error instanceof Error ? error.message : String(error),
152
+ }));
153
+ }
154
+ },
155
+ [marketplaceService, refresh],
156
+ );
157
+
158
+ const removeMarketplace = useCallback(
159
+ async (name: string) => {
160
+ setState((prev: PluginManagerState) => ({
161
+ ...prev,
162
+ isLoading: true,
163
+ error: null,
164
+ }));
165
+ try {
166
+ await marketplaceService.removeMarketplace(name);
167
+ await refresh();
168
+ } catch (error) {
169
+ setState((prev: PluginManagerState) => ({
170
+ ...prev,
171
+ isLoading: false,
172
+ error: error instanceof Error ? error.message : String(error),
173
+ }));
174
+ }
175
+ },
176
+ [marketplaceService, refresh],
177
+ );
178
+
179
+ const updateMarketplace = useCallback(
180
+ async (name: string) => {
181
+ setState((prev: PluginManagerState) => ({
182
+ ...prev,
183
+ isLoading: true,
184
+ error: null,
185
+ }));
186
+ try {
187
+ await marketplaceService.updateMarketplace(name);
188
+ await refresh();
189
+ } catch (error) {
190
+ setState((prev: PluginManagerState) => ({
191
+ ...prev,
192
+ isLoading: false,
193
+ error: error instanceof Error ? error.message : String(error),
194
+ }));
195
+ }
196
+ },
197
+ [marketplaceService, refresh],
198
+ );
199
+
200
+ const installPlugin = useCallback(
201
+ async (
202
+ name: string,
203
+ marketplace: string,
204
+ scope: "user" | "project" | "local" = "project",
205
+ ) => {
206
+ setState((prev: PluginManagerState) => ({
207
+ ...prev,
208
+ isLoading: true,
209
+ error: null,
210
+ }));
211
+ try {
212
+ const pluginId = `${name}@${marketplace}`;
213
+ const workdir = process.cwd();
214
+ await marketplaceService.installPlugin(pluginId, workdir);
215
+ await pluginScopeManager.enablePlugin(scope, pluginId);
216
+ await refresh();
217
+ } catch (error) {
218
+ setState((prev: PluginManagerState) => ({
219
+ ...prev,
220
+ isLoading: false,
221
+ error: error instanceof Error ? error.message : String(error),
222
+ }));
223
+ }
224
+ },
225
+ [marketplaceService, pluginScopeManager, refresh],
226
+ );
227
+
228
+ const uninstallPlugin = useCallback(
229
+ async (name: string, marketplace: string) => {
230
+ setState((prev: PluginManagerState) => ({
231
+ ...prev,
232
+ isLoading: true,
233
+ error: null,
234
+ }));
235
+ try {
236
+ const pluginId = `${name}@${marketplace}`;
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
243
+ const scope = pluginScopeManager.findPluginScope(pluginId);
244
+ if (scope) {
245
+ await configurationService.removeEnabledPlugin(
246
+ workdir,
247
+ scope,
248
+ pluginId,
249
+ );
250
+ }
251
+ await refresh();
252
+ } catch (error) {
253
+ setState((prev: PluginManagerState) => ({
254
+ ...prev,
255
+ isLoading: false,
256
+ error: error instanceof Error ? error.message : String(error),
257
+ }));
258
+ }
259
+ },
260
+ [configurationService, marketplaceService, pluginScopeManager, refresh],
261
+ );
262
+
263
+ const updatePlugin = useCallback(
264
+ async (name: string, marketplace: string) => {
265
+ setState((prev: PluginManagerState) => ({
266
+ ...prev,
267
+ isLoading: true,
268
+ error: null,
269
+ }));
270
+ try {
271
+ const pluginId = `${name}@${marketplace}`;
272
+ await marketplaceService.updatePlugin(pluginId);
273
+ await refresh();
274
+ } catch (error) {
275
+ setState((prev: PluginManagerState) => ({
276
+ ...prev,
277
+ isLoading: false,
278
+ error: error instanceof Error ? error.message : String(error),
279
+ }));
280
+ }
281
+ },
282
+ [marketplaceService, refresh],
283
+ );
284
+
285
+ return {
286
+ state,
287
+ marketplaces,
288
+ installedPlugins,
289
+ discoverablePlugins,
290
+ actions: {
291
+ setView,
292
+ setSelectedId,
293
+ addMarketplace,
294
+ removeMarketplace,
295
+ updateMarketplace,
296
+ installPlugin,
297
+ uninstallPlugin,
298
+ updatePlugin,
299
+ refresh,
300
+ },
301
+ };
302
+ }