wave-code 0.7.2 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. package/dist/cli.d.ts +2 -4
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +24 -52
  4. package/dist/components/App.d.ts +3 -4
  5. package/dist/components/App.d.ts.map +1 -1
  6. package/dist/components/App.js +49 -6
  7. package/dist/components/BangDisplay.d.ts +9 -0
  8. package/dist/components/BangDisplay.d.ts.map +1 -0
  9. package/dist/components/{CommandOutputDisplay.js → BangDisplay.js} +1 -1
  10. package/dist/components/ChatInterface.d.ts.map +1 -1
  11. package/dist/components/ChatInterface.js +3 -2
  12. package/dist/components/CommandSelector.d.ts.map +1 -1
  13. package/dist/components/CommandSelector.js +7 -28
  14. package/dist/components/ConfirmationSelector.d.ts.map +1 -1
  15. package/dist/components/ConfirmationSelector.js +116 -11
  16. package/dist/components/HelpView.d.ts +2 -0
  17. package/dist/components/HelpView.d.ts.map +1 -1
  18. package/dist/components/HelpView.js +49 -3
  19. package/dist/components/InputBox.d.ts.map +1 -1
  20. package/dist/components/InputBox.js +10 -4
  21. package/dist/components/MarketplaceAddForm.d.ts.map +1 -1
  22. package/dist/components/MarketplaceAddForm.js +13 -6
  23. package/dist/components/MarketplaceDetail.d.ts.map +1 -1
  24. package/dist/components/MarketplaceDetail.js +8 -3
  25. package/dist/components/MessageBlockItem.js +2 -2
  26. package/dist/components/MessageList.d.ts +4 -1
  27. package/dist/components/MessageList.d.ts.map +1 -1
  28. package/dist/components/MessageList.js +15 -8
  29. package/dist/components/PluginDetail.d.ts.map +1 -1
  30. package/dist/components/PluginDetail.js +14 -3
  31. package/dist/components/PluginManagerShell.d.ts.map +1 -1
  32. package/dist/components/PluginManagerShell.js +3 -3
  33. package/dist/components/PluginManagerTypes.d.ts +2 -0
  34. package/dist/components/PluginManagerTypes.d.ts.map +1 -1
  35. package/dist/components/SessionSelector.d.ts.map +1 -1
  36. package/dist/components/SessionSelector.js +5 -5
  37. package/dist/components/StatusCommand.d.ts +6 -0
  38. package/dist/components/StatusCommand.d.ts.map +1 -0
  39. package/dist/components/StatusCommand.js +28 -0
  40. package/dist/components/WorktreeExitPrompt.d.ts +13 -0
  41. package/dist/components/WorktreeExitPrompt.d.ts.map +1 -0
  42. package/dist/components/WorktreeExitPrompt.js +26 -0
  43. package/dist/constants/commands.d.ts +3 -0
  44. package/dist/constants/commands.d.ts.map +1 -0
  45. package/dist/constants/commands.js +38 -0
  46. package/dist/contexts/useChat.d.ts +9 -5
  47. package/dist/contexts/useChat.d.ts.map +1 -1
  48. package/dist/contexts/useChat.js +38 -8
  49. package/dist/contracts/status.d.ts +8 -0
  50. package/dist/contracts/status.d.ts.map +1 -0
  51. package/dist/contracts/status.js +1 -0
  52. package/dist/hooks/useInputManager.d.ts +2 -0
  53. package/dist/hooks/useInputManager.d.ts.map +1 -1
  54. package/dist/hooks/useInputManager.js +12 -0
  55. package/dist/hooks/usePluginManager.d.ts.map +1 -1
  56. package/dist/hooks/usePluginManager.js +41 -13
  57. package/dist/hooks/useTasks.js +2 -2
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +53 -4
  60. package/dist/managers/InputManager.d.ts +6 -0
  61. package/dist/managers/InputManager.d.ts.map +1 -1
  62. package/dist/managers/InputManager.js +32 -13
  63. package/dist/print-cli.d.ts +2 -4
  64. package/dist/print-cli.d.ts.map +1 -1
  65. package/dist/print-cli.js +31 -2
  66. package/dist/session-selector-cli.d.ts +3 -1
  67. package/dist/session-selector-cli.d.ts.map +1 -1
  68. package/dist/session-selector-cli.js +2 -2
  69. package/dist/types.d.ts +11 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +1 -0
  72. package/dist/utils/highlightUtils.d.ts.map +1 -1
  73. package/dist/utils/highlightUtils.js +66 -42
  74. package/dist/utils/worktree.d.ts +23 -0
  75. package/dist/utils/worktree.d.ts.map +1 -0
  76. package/dist/utils/worktree.js +135 -0
  77. package/package.json +2 -2
  78. package/src/cli.tsx +36 -59
  79. package/src/components/App.tsx +99 -11
  80. package/src/components/{CommandOutputDisplay.tsx → BangDisplay.tsx} +4 -4
  81. package/src/components/ChatInterface.tsx +8 -0
  82. package/src/components/CommandSelector.tsx +7 -29
  83. package/src/components/ConfirmationSelector.tsx +131 -12
  84. package/src/components/HelpView.tsx +129 -14
  85. package/src/components/InputBox.tsx +14 -2
  86. package/src/components/MarketplaceAddForm.tsx +21 -8
  87. package/src/components/MarketplaceDetail.tsx +19 -4
  88. package/src/components/MessageBlockItem.tsx +3 -3
  89. package/src/components/MessageList.tsx +47 -23
  90. package/src/components/PluginDetail.tsx +30 -6
  91. package/src/components/PluginManagerShell.tsx +24 -6
  92. package/src/components/PluginManagerTypes.ts +2 -0
  93. package/src/components/SessionSelector.tsx +33 -16
  94. package/src/components/StatusCommand.tsx +94 -0
  95. package/src/components/WorktreeExitPrompt.tsx +86 -0
  96. package/src/constants/commands.ts +41 -0
  97. package/src/contexts/useChat.tsx +57 -13
  98. package/src/contracts/status.ts +7 -0
  99. package/src/hooks/useInputManager.ts +12 -0
  100. package/src/hooks/usePluginManager.ts +47 -13
  101. package/src/hooks/useTasks.ts +2 -2
  102. package/src/index.ts +71 -12
  103. package/src/managers/InputManager.ts +37 -15
  104. package/src/print-cli.ts +48 -5
  105. package/src/session-selector-cli.tsx +6 -2
  106. package/src/types.ts +11 -0
  107. package/src/utils/highlightUtils.ts +66 -42
  108. package/src/utils/worktree.ts +164 -0
  109. package/dist/components/CommandOutputDisplay.d.ts +0 -9
  110. package/dist/components/CommandOutputDisplay.d.ts.map +0 -1
@@ -0,0 +1,94 @@
1
+ import React from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+ import { useChat } from "../contexts/useChat.js";
4
+ import fs from "fs";
5
+ import path from "path";
6
+ import { fileURLToPath } from "url";
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = path.dirname(__filename);
10
+
11
+ export interface StatusCommandProps {
12
+ onCancel: () => void;
13
+ }
14
+
15
+ export const StatusCommand: React.FC<StatusCommandProps> = ({ onCancel }) => {
16
+ const { sessionId, workingDirectory, getGatewayConfig, getModelConfig } =
17
+ useChat();
18
+
19
+ useInput((_, key) => {
20
+ if (key.escape) {
21
+ onCancel();
22
+ }
23
+ });
24
+
25
+ const gatewayConfig = getGatewayConfig();
26
+ const modelConfig = getModelConfig();
27
+
28
+ let version = "unknown";
29
+ try {
30
+ const pkgPath = path.resolve(__dirname, "../../package.json");
31
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
32
+ version = pkg.version;
33
+ } catch {
34
+ // Fallback if package.json cannot be read
35
+ }
36
+
37
+ return (
38
+ <Box
39
+ flexDirection="column"
40
+ borderStyle="round"
41
+ borderColor="cyan"
42
+ borderLeft={false}
43
+ borderRight={false}
44
+ paddingX={1}
45
+ >
46
+ <Box marginBottom={1}>
47
+ <Text color="cyan" bold underline>
48
+ Agent Status
49
+ </Text>
50
+ </Box>
51
+
52
+ <Box>
53
+ <Box width={20}>
54
+ <Text color="yellow">Version:</Text>
55
+ </Box>
56
+ <Text color="white">{version}</Text>
57
+ </Box>
58
+
59
+ <Box>
60
+ <Box width={20}>
61
+ <Text color="yellow">Session ID:</Text>
62
+ </Box>
63
+ <Text color="white">{sessionId}</Text>
64
+ </Box>
65
+
66
+ <Box>
67
+ <Box width={20}>
68
+ <Text color="yellow">cwd:</Text>
69
+ </Box>
70
+ <Text color="white" wrap="wrap">
71
+ {workingDirectory}
72
+ </Text>
73
+ </Box>
74
+
75
+ <Box>
76
+ <Box width={20}>
77
+ <Text color="yellow">Wave base URL:</Text>
78
+ </Box>
79
+ <Text color="white">{gatewayConfig.baseURL}</Text>
80
+ </Box>
81
+
82
+ <Box>
83
+ <Box width={20}>
84
+ <Text color="yellow">Model:</Text>
85
+ </Box>
86
+ <Text color="white">{modelConfig.model}</Text>
87
+ </Box>
88
+
89
+ <Box marginTop={1}>
90
+ <Text dimColor>Esc to cancel</Text>
91
+ </Box>
92
+ </Box>
93
+ );
94
+ };
@@ -0,0 +1,86 @@
1
+ import React, { useState } from "react";
2
+ import { Box, Text, useInput } from "ink";
3
+
4
+ interface WorktreeExitPromptProps {
5
+ name: string;
6
+ path: string;
7
+ hasUncommittedChanges: boolean;
8
+ hasNewCommits: boolean;
9
+ onKeep: () => void;
10
+ onRemove: () => void;
11
+ onCancel: () => void;
12
+ }
13
+
14
+ export const WorktreeExitPrompt: React.FC<WorktreeExitPromptProps> = ({
15
+ name,
16
+ path: worktreePath,
17
+ hasUncommittedChanges,
18
+ hasNewCommits,
19
+ onKeep,
20
+ onRemove,
21
+ onCancel,
22
+ }) => {
23
+ const [selectedIndex, setSelectedIndex] = useState(0);
24
+
25
+ useInput((input, key) => {
26
+ if (key.upArrow) {
27
+ setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
28
+ }
29
+ if (key.downArrow) {
30
+ setSelectedIndex((prev) => (prev === 1 ? 0 : 1));
31
+ }
32
+ if (key.return) {
33
+ if (selectedIndex === 0) {
34
+ onKeep();
35
+ } else {
36
+ onRemove();
37
+ }
38
+ }
39
+ if (key.escape) {
40
+ onCancel();
41
+ }
42
+ });
43
+
44
+ return (
45
+ <Box flexDirection="column" paddingX={1} marginY={1}>
46
+ <Box marginBottom={1}>
47
+ <Text bold>Exiting worktree session</Text>
48
+ </Box>
49
+ <Box marginBottom={1}>
50
+ {hasUncommittedChanges && hasNewCommits ? (
51
+ <Text>
52
+ You have uncommitted changes and new commits. These will be lost if
53
+ you remove the worktree.
54
+ </Text>
55
+ ) : hasUncommittedChanges ? (
56
+ <Text>
57
+ You have uncommitted changes. These will be lost if you remove the
58
+ worktree.
59
+ </Text>
60
+ ) : (
61
+ <Text>
62
+ You have new commits on worktree-{name}. The branch will be deleted
63
+ if you remove the worktree.
64
+ </Text>
65
+ )}
66
+ </Box>
67
+ <Box flexDirection="column">
68
+ <Box>
69
+ <Text color={selectedIndex === 0 ? "cyan" : undefined}>
70
+ {selectedIndex === 0 ? "❯ " : " "}Keep worktree Stays at{" "}
71
+ {worktreePath}
72
+ </Text>
73
+ </Box>
74
+ <Box>
75
+ <Text color={selectedIndex === 1 ? "cyan" : undefined}>
76
+ {selectedIndex === 1 ? "❯ " : " "}Remove worktree All changes and
77
+ commits will be lost.
78
+ </Text>
79
+ </Box>
80
+ </Box>
81
+ <Box marginTop={1}>
82
+ <Text dimColor>Enter to confirm · Esc to cancel</Text>
83
+ </Box>
84
+ </Box>
85
+ );
86
+ };
@@ -0,0 +1,41 @@
1
+ import type { SlashCommand } from "wave-agent-sdk";
2
+
3
+ export const AVAILABLE_COMMANDS: SlashCommand[] = [
4
+ {
5
+ id: "clear",
6
+ name: "clear",
7
+ description: "Clear the chat session and terminal",
8
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
9
+ },
10
+ {
11
+ id: "tasks",
12
+ name: "tasks",
13
+ description: "View and manage background tasks (shells and subagents)",
14
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
15
+ },
16
+ {
17
+ id: "mcp",
18
+ name: "mcp",
19
+ description: "View and manage MCP servers",
20
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
21
+ },
22
+ {
23
+ id: "rewind",
24
+ name: "rewind",
25
+ description:
26
+ "Revert conversation and file changes to a previous checkpoint",
27
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
28
+ },
29
+ {
30
+ id: "help",
31
+ name: "help",
32
+ description: "Show help and key bindings",
33
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
34
+ },
35
+ {
36
+ id: "status",
37
+ name: "status",
38
+ description: "Show agent status and configuration",
39
+ handler: () => {}, // Handler here won't be used, actual processing is in the hook
40
+ },
41
+ ];
@@ -25,6 +25,8 @@ import {
25
25
  import { logger } from "../utils/logger.js";
26
26
  import { displayUsageSummary } from "../utils/usageSummary.js";
27
27
 
28
+ import { BaseAppProps } from "../types.js";
29
+
28
30
  // Main Chat Context
29
31
  export interface ChatContextType {
30
32
  messages: Message[];
@@ -43,14 +45,15 @@ export interface ChatContextType {
43
45
  ) => Promise<void>;
44
46
  abortMessage: () => void;
45
47
  latestTotalTokens: number;
48
+ clearMessages: () => void;
46
49
  // MCP functionality
47
50
  mcpServers: McpServerStatus[];
48
51
  connectMcpServer: (serverName: string) => Promise<boolean>;
49
52
  disconnectMcpServer: (serverName: string) => Promise<boolean>;
50
53
  // Background tasks
51
54
  backgroundTasks: BackgroundTask[];
52
- // Session tasks
53
- sessionTasks: Task[];
55
+ // Tasks
56
+ tasks: Task[];
54
57
  getBackgroundTaskOutput: (
55
58
  taskId: string,
56
59
  ) => { stdout: string; stderr: string; status: string } | null;
@@ -92,6 +95,12 @@ export interface ChatContextType {
92
95
  }>;
93
96
  wasLastDetailsTooTall: number;
94
97
  setWasLastDetailsTooTall: React.Dispatch<React.SetStateAction<number>>;
98
+ // Status metadata
99
+ getGatewayConfig: () => import("wave-agent-sdk").GatewayConfig;
100
+ getModelConfig: () => import("wave-agent-sdk").ModelConfig;
101
+ workingDirectory: string;
102
+ version?: string;
103
+ workdir?: string;
95
104
  }
96
105
 
97
106
  const ChatContext = createContext<ChatContextType | null>(null);
@@ -104,11 +113,8 @@ export const useChat = () => {
104
113
  return context;
105
114
  };
106
115
 
107
- export interface ChatProviderProps {
116
+ export interface ChatProviderProps extends BaseAppProps {
108
117
  children: React.ReactNode;
109
- bypassPermissions?: boolean;
110
- pluginDirs?: string[];
111
- tools?: string[];
112
118
  }
113
119
 
114
120
  export const ChatProvider: React.FC<ChatProviderProps> = ({
@@ -116,6 +122,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
116
122
  bypassPermissions,
117
123
  pluginDirs,
118
124
  tools,
125
+ workdir,
126
+ worktreeSession,
127
+ version,
128
+ model,
119
129
  }) => {
120
130
  const { restoreSessionId, continueLastSession } = useAppConfig();
121
131
 
@@ -144,8 +154,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
144
154
 
145
155
  // Background tasks state
146
156
  const [backgroundTasks, setBackgroundTasks] = useState<BackgroundTask[]>([]);
147
- // Session tasks state
148
- const [sessionTasks, setSessionTasks] = useState<Task[]>([]);
157
+ // Tasks state
158
+ const [tasks, setTasks] = useState<Task[]>([]);
149
159
 
150
160
  // Command state
151
161
  const [slashCommands, setSlashCommands] = useState<SlashCommand[]>([]);
@@ -195,6 +205,9 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
195
205
  // Rewind state
196
206
  const [rewindId, setRewindId] = useState(0);
197
207
 
208
+ // Status metadata state
209
+ const [workingDirectory, setWorkingDirectory] = useState("");
210
+
198
211
  // Confirmation too tall state
199
212
  const [wasLastDetailsTooTall, setWasLastDetailsTooTall] = useState(0);
200
213
 
@@ -237,7 +250,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
237
250
  setMessages([...agentRef.current.messages]);
238
251
  }
239
252
  messagesUpdateTimerRef.current = null;
240
- }, 50);
253
+ }, 100);
241
254
  }
242
255
  }
243
256
  },
@@ -253,11 +266,11 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
253
266
  onCompressionStateChange: (isCompressingState) => {
254
267
  setIsCompressing(isCompressingState);
255
268
  },
256
- onTasksChange: (tasks) => {
269
+ onBackgroundTasksChange: (tasks) => {
257
270
  setBackgroundTasks([...tasks]);
258
271
  },
259
- onSessionTasksChange: (tasks) => {
260
- setSessionTasks([...tasks]);
272
+ onTasksChange: (tasks) => {
273
+ setTasks([...tasks]);
261
274
  },
262
275
  onSubagentMessagesChange: (subagentId: string, messages: Message[]) => {
263
276
  logger.debug("onSubagentMessagesChange", subagentId, messages.length);
@@ -316,6 +329,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
316
329
  stream: false, // 关闭流式模式
317
330
  plugins: pluginDirs?.map((path) => ({ type: "local", path })),
318
331
  tools,
332
+ workdir,
333
+ worktreeName: worktreeSession?.name,
334
+ isNewWorktree: worktreeSession?.isNew,
335
+ model,
319
336
  });
320
337
 
321
338
  agentRef.current = agent;
@@ -328,6 +345,7 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
328
345
  setIsCommandRunning(agent.isCommandRunning);
329
346
  setIsCompressing(agent.isCompressing);
330
347
  setPermissionModeState(agent.getPermissionMode());
348
+ setWorkingDirectory(agent.workingDirectory);
331
349
 
332
350
  // Get initial MCP servers state
333
351
  const mcpServers = agent.getMcpServers?.() || [];
@@ -349,6 +367,8 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
349
367
  showConfirmation,
350
368
  pluginDirs,
351
369
  tools,
370
+ workdir,
371
+ worktreeSession,
352
372
  ]);
353
373
 
354
374
  // Cleanup on unmount
@@ -431,6 +451,10 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
431
451
  agentRef.current?.abortMessage();
432
452
  }, []);
433
453
 
454
+ const clearMessages = useCallback(() => {
455
+ agentRef.current?.clearMessages();
456
+ }, []);
457
+
434
458
  // Permission management methods
435
459
  const setPermissionMode = useCallback((mode: PermissionMode) => {
436
460
  setPermissionModeState((prev) => {
@@ -535,6 +559,20 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
535
559
  return { messages: [], sessionIds: [] };
536
560
  }, []);
537
561
 
562
+ const getGatewayConfig = useCallback(() => {
563
+ if (!agentRef.current) {
564
+ return { baseURL: "" };
565
+ }
566
+ return agentRef.current.getGatewayConfig();
567
+ }, []);
568
+
569
+ const getModelConfig = useCallback(() => {
570
+ if (!agentRef.current) {
571
+ return { model: "", fastModel: "" };
572
+ }
573
+ return agentRef.current.getModelConfig();
574
+ }, []);
575
+
538
576
  // Listen for Ctrl+O hotkey to toggle collapse/expand state and ESC to cancel confirmation
539
577
  useInput((input, key) => {
540
578
  if (key.ctrl && input === "o") {
@@ -584,13 +622,14 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
584
622
  sessionId,
585
623
  sendMessage,
586
624
  abortMessage,
625
+ clearMessages,
587
626
  latestTotalTokens,
588
627
  isCompressing,
589
628
  mcpServers,
590
629
  connectMcpServer,
591
630
  disconnectMcpServer,
592
631
  backgroundTasks,
593
- sessionTasks,
632
+ tasks,
594
633
  getBackgroundTaskOutput,
595
634
  stopBackgroundTask,
596
635
  slashCommands,
@@ -611,6 +650,11 @@ export const ChatProvider: React.FC<ChatProviderProps> = ({
611
650
  getFullMessageThread,
612
651
  wasLastDetailsTooTall,
613
652
  setWasLastDetailsTooTall,
653
+ getGatewayConfig,
654
+ getModelConfig,
655
+ workingDirectory,
656
+ version,
657
+ workdir,
614
658
  };
615
659
 
616
660
  return (
@@ -0,0 +1,7 @@
1
+ export interface AgentStatus {
2
+ version: string;
3
+ sessionId: string;
4
+ cwd: string;
5
+ baseURL: string;
6
+ model: string;
7
+ }
@@ -38,6 +38,7 @@ export const useInputManager = (
38
38
  const [showMcpManager, setShowMcpManager] = useState(false);
39
39
  const [showRewindManager, setShowRewindManager] = useState(false);
40
40
  const [showHelp, setShowHelp] = useState(false);
41
+ const [showStatusCommand, setShowStatusCommand] = useState(false);
41
42
  const [permissionMode, setPermissionModeState] =
42
43
  useState<PermissionMode>("default");
43
44
  const [attachedImages, setAttachedImages] = useState<AttachedImage[]>([]);
@@ -71,6 +72,9 @@ export const useInputManager = (
71
72
  onHelpStateChange: (show) => {
72
73
  setShowHelp(show);
73
74
  },
75
+ onStatusCommandStateChange: (show) => {
76
+ setShowStatusCommand(show);
77
+ },
74
78
  onPermissionModeChange: (mode) => {
75
79
  setPermissionModeState(mode);
76
80
  callbacks.onPermissionModeChange?.(mode);
@@ -108,6 +112,9 @@ export const useInputManager = (
108
112
  onHelpStateChange: (show) => {
109
113
  setShowHelp(show);
110
114
  },
115
+ onStatusCommandStateChange: (show) => {
116
+ setShowStatusCommand(show);
117
+ },
111
118
  onPermissionModeChange: (mode) => {
112
119
  setPermissionModeState(mode);
113
120
  callbacks.onPermissionModeChange?.(mode);
@@ -268,6 +275,7 @@ export const useInputManager = (
268
275
  showMcpManager,
269
276
  showRewindManager,
270
277
  showHelp,
278
+ showStatusCommand,
271
279
  permissionMode,
272
280
  attachedImages,
273
281
  isManagerReady,
@@ -316,6 +324,10 @@ export const useInputManager = (
316
324
  managerRef.current?.setShowHelp(show);
317
325
  setShowHelp(show);
318
326
  }, []),
327
+ setShowStatusCommand: useCallback((show: boolean) => {
328
+ managerRef.current?.setShowStatusCommand(show);
329
+ setShowStatusCommand(show);
330
+ }, []),
319
331
  setPermissionMode: useCallback((mode: PermissionMode) => {
320
332
  setPermissionModeState(mode);
321
333
  managerRef.current?.setPermissionMode(mode);
@@ -17,6 +17,7 @@ export function usePluginManager(): PluginManagerContextType {
17
17
  selectedId: null,
18
18
  isLoading: true,
19
19
  error: null,
20
+ successMessage: null,
20
21
  searchQuery: "",
21
22
  });
22
23
 
@@ -34,11 +35,37 @@ export function usePluginManager(): PluginManagerContextType {
34
35
 
35
36
  const pluginCore = useMemo(() => new PluginCore(), []);
36
37
 
38
+ const clearPluginFeedback = useCallback(() => {
39
+ setState((prev: PluginManagerState) => ({
40
+ ...prev,
41
+ error: null,
42
+ successMessage: null,
43
+ }));
44
+ }, []);
45
+
46
+ const setSuccessMessage = useCallback(
47
+ (message: string) => {
48
+ setState((prev: PluginManagerState) => ({
49
+ ...prev,
50
+ successMessage: message,
51
+ error: null,
52
+ }));
53
+ setTimeout(() => {
54
+ setState((prev: PluginManagerState) => ({
55
+ ...prev,
56
+ successMessage:
57
+ prev.successMessage === message ? null : prev.successMessage,
58
+ }));
59
+ }, 5000);
60
+ },
61
+ [setState],
62
+ );
63
+
37
64
  const refresh = useCallback(async () => {
65
+ clearPluginFeedback();
38
66
  setState((prev: PluginManagerState) => ({
39
67
  ...prev,
40
68
  isLoading: true,
41
- error: null,
42
69
  }));
43
70
  try {
44
71
  const [mks, installed, enabledMap] = await Promise.all([
@@ -115,14 +142,15 @@ export function usePluginManager(): PluginManagerContextType {
115
142
 
116
143
  const addMarketplace = useCallback(
117
144
  async (source: string) => {
145
+ clearPluginFeedback();
118
146
  setState((prev: PluginManagerState) => ({
119
147
  ...prev,
120
148
  isLoading: true,
121
- error: null,
122
149
  }));
123
150
  try {
124
151
  await pluginCore.addMarketplace(source);
125
152
  await refresh();
153
+ setSuccessMessage(`Marketplace added successfully`);
126
154
  } catch (error) {
127
155
  setState((prev: PluginManagerState) => ({
128
156
  ...prev,
@@ -131,19 +159,20 @@ export function usePluginManager(): PluginManagerContextType {
131
159
  }));
132
160
  }
133
161
  },
134
- [pluginCore, refresh],
162
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
135
163
  );
136
164
 
137
165
  const removeMarketplace = useCallback(
138
166
  async (name: string) => {
167
+ clearPluginFeedback();
139
168
  setState((prev: PluginManagerState) => ({
140
169
  ...prev,
141
170
  isLoading: true,
142
- error: null,
143
171
  }));
144
172
  try {
145
173
  await pluginCore.removeMarketplace(name);
146
174
  await refresh();
175
+ setSuccessMessage(`Marketplace '${name}' removed successfully`);
147
176
  } catch (error) {
148
177
  setState((prev: PluginManagerState) => ({
149
178
  ...prev,
@@ -152,19 +181,20 @@ export function usePluginManager(): PluginManagerContextType {
152
181
  }));
153
182
  }
154
183
  },
155
- [pluginCore, refresh],
184
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
156
185
  );
157
186
 
158
187
  const updateMarketplace = useCallback(
159
188
  async (name: string) => {
189
+ clearPluginFeedback();
160
190
  setState((prev: PluginManagerState) => ({
161
191
  ...prev,
162
192
  isLoading: true,
163
- error: null,
164
193
  }));
165
194
  try {
166
195
  await pluginCore.updateMarketplace(name);
167
196
  await refresh();
197
+ setSuccessMessage(`Marketplace '${name}' updated successfully`);
168
198
  } catch (error) {
169
199
  setState((prev: PluginManagerState) => ({
170
200
  ...prev,
@@ -173,7 +203,7 @@ export function usePluginManager(): PluginManagerContextType {
173
203
  }));
174
204
  }
175
205
  },
176
- [pluginCore, refresh],
206
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
177
207
  );
178
208
 
179
209
  const installPlugin = useCallback(
@@ -182,15 +212,16 @@ export function usePluginManager(): PluginManagerContextType {
182
212
  marketplace: string,
183
213
  scope: "user" | "project" | "local" = "project",
184
214
  ) => {
215
+ clearPluginFeedback();
185
216
  setState((prev: PluginManagerState) => ({
186
217
  ...prev,
187
218
  isLoading: true,
188
- error: null,
189
219
  }));
190
220
  try {
191
221
  const pluginId = `${name}@${marketplace}`;
192
222
  await pluginCore.installPlugin(pluginId, scope);
193
223
  await refresh();
224
+ setSuccessMessage(`Plugin '${name}' installed successfully`);
194
225
  } catch (error) {
195
226
  setState((prev: PluginManagerState) => ({
196
227
  ...prev,
@@ -199,20 +230,21 @@ export function usePluginManager(): PluginManagerContextType {
199
230
  }));
200
231
  }
201
232
  },
202
- [pluginCore, refresh],
233
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
203
234
  );
204
235
 
205
236
  const uninstallPlugin = useCallback(
206
237
  async (name: string, marketplace: string) => {
238
+ clearPluginFeedback();
207
239
  setState((prev: PluginManagerState) => ({
208
240
  ...prev,
209
241
  isLoading: true,
210
- error: null,
211
242
  }));
212
243
  try {
213
244
  const pluginId = `${name}@${marketplace}`;
214
245
  await pluginCore.uninstallPlugin(pluginId);
215
246
  await refresh();
247
+ setSuccessMessage(`Plugin '${name}' uninstalled successfully`);
216
248
  } catch (error) {
217
249
  setState((prev: PluginManagerState) => ({
218
250
  ...prev,
@@ -221,20 +253,21 @@ export function usePluginManager(): PluginManagerContextType {
221
253
  }));
222
254
  }
223
255
  },
224
- [pluginCore, refresh],
256
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
225
257
  );
226
258
 
227
259
  const updatePlugin = useCallback(
228
260
  async (name: string, marketplace: string) => {
261
+ clearPluginFeedback();
229
262
  setState((prev: PluginManagerState) => ({
230
263
  ...prev,
231
264
  isLoading: true,
232
- error: null,
233
265
  }));
234
266
  try {
235
267
  const pluginId = `${name}@${marketplace}`;
236
268
  await pluginCore.updatePlugin(pluginId);
237
269
  await refresh();
270
+ setSuccessMessage(`Plugin '${name}' updated successfully`);
238
271
  } catch (error) {
239
272
  setState((prev: PluginManagerState) => ({
240
273
  ...prev,
@@ -243,7 +276,7 @@ export function usePluginManager(): PluginManagerContextType {
243
276
  }));
244
277
  }
245
278
  },
246
- [pluginCore, refresh],
279
+ [pluginCore, refresh, clearPluginFeedback, setSuccessMessage],
247
280
  );
248
281
 
249
282
  return {
@@ -261,6 +294,7 @@ export function usePluginManager(): PluginManagerContextType {
261
294
  uninstallPlugin,
262
295
  updatePlugin,
263
296
  refresh,
297
+ clearPluginFeedback,
264
298
  },
265
299
  };
266
300
  }
@@ -1,6 +1,6 @@
1
1
  import { useChat } from "../contexts/useChat.js";
2
2
 
3
3
  export const useTasks = () => {
4
- const { sessionTasks } = useChat();
5
- return sessionTasks;
4
+ const { tasks } = useChat();
5
+ return tasks;
6
6
  };