snow-ai 0.2.15 → 0.2.16

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 (65) hide show
  1. package/dist/api/anthropic.d.ts +1 -1
  2. package/dist/api/anthropic.js +52 -76
  3. package/dist/api/chat.d.ts +4 -4
  4. package/dist/api/chat.js +32 -17
  5. package/dist/api/gemini.d.ts +1 -1
  6. package/dist/api/gemini.js +20 -13
  7. package/dist/api/responses.d.ts +5 -5
  8. package/dist/api/responses.js +29 -27
  9. package/dist/app.js +4 -1
  10. package/dist/hooks/useClipboard.d.ts +4 -0
  11. package/dist/hooks/useClipboard.js +120 -0
  12. package/dist/hooks/useCommandHandler.d.ts +26 -0
  13. package/dist/hooks/useCommandHandler.js +158 -0
  14. package/dist/hooks/useCommandPanel.d.ts +16 -0
  15. package/dist/hooks/useCommandPanel.js +53 -0
  16. package/dist/hooks/useConversation.d.ts +9 -1
  17. package/dist/hooks/useConversation.js +152 -58
  18. package/dist/hooks/useFilePicker.d.ts +17 -0
  19. package/dist/hooks/useFilePicker.js +91 -0
  20. package/dist/hooks/useHistoryNavigation.d.ts +21 -0
  21. package/dist/hooks/useHistoryNavigation.js +50 -0
  22. package/dist/hooks/useInputBuffer.d.ts +6 -0
  23. package/dist/hooks/useInputBuffer.js +29 -0
  24. package/dist/hooks/useKeyboardInput.d.ts +51 -0
  25. package/dist/hooks/useKeyboardInput.js +272 -0
  26. package/dist/hooks/useSnapshotState.d.ts +12 -0
  27. package/dist/hooks/useSnapshotState.js +28 -0
  28. package/dist/hooks/useStreamingState.d.ts +24 -0
  29. package/dist/hooks/useStreamingState.js +96 -0
  30. package/dist/hooks/useVSCodeState.d.ts +8 -0
  31. package/dist/hooks/useVSCodeState.js +63 -0
  32. package/dist/mcp/filesystem.d.ts +24 -5
  33. package/dist/mcp/filesystem.js +52 -17
  34. package/dist/mcp/todo.js +4 -8
  35. package/dist/ui/components/ChatInput.js +68 -557
  36. package/dist/ui/components/DiffViewer.js +57 -30
  37. package/dist/ui/components/FileList.js +70 -26
  38. package/dist/ui/components/MessageList.d.ts +6 -0
  39. package/dist/ui/components/MessageList.js +47 -15
  40. package/dist/ui/components/ShimmerText.d.ts +9 -0
  41. package/dist/ui/components/ShimmerText.js +30 -0
  42. package/dist/ui/components/TodoTree.d.ts +1 -1
  43. package/dist/ui/components/TodoTree.js +0 -4
  44. package/dist/ui/components/ToolConfirmation.js +14 -6
  45. package/dist/ui/pages/ChatScreen.js +159 -359
  46. package/dist/ui/pages/CustomHeadersScreen.d.ts +6 -0
  47. package/dist/ui/pages/CustomHeadersScreen.js +104 -0
  48. package/dist/ui/pages/WelcomeScreen.js +5 -0
  49. package/dist/utils/apiConfig.d.ts +10 -0
  50. package/dist/utils/apiConfig.js +51 -0
  51. package/dist/utils/incrementalSnapshot.d.ts +8 -0
  52. package/dist/utils/incrementalSnapshot.js +63 -0
  53. package/dist/utils/mcpToolsManager.js +6 -1
  54. package/dist/utils/retryUtils.d.ts +22 -0
  55. package/dist/utils/retryUtils.js +180 -0
  56. package/dist/utils/sessionConverter.js +80 -17
  57. package/dist/utils/sessionManager.js +35 -4
  58. package/dist/utils/textUtils.d.ts +4 -0
  59. package/dist/utils/textUtils.js +19 -0
  60. package/dist/utils/todoPreprocessor.d.ts +1 -1
  61. package/dist/utils/todoPreprocessor.js +0 -1
  62. package/dist/utils/vscodeConnection.d.ts +8 -0
  63. package/dist/utils/vscodeConnection.js +44 -0
  64. package/package.json +1 -1
  65. package/readme.md +3 -1
@@ -0,0 +1,272 @@
1
+ import { useRef, useEffect } from 'react';
2
+ import { useInput } from 'ink';
3
+ import { executeCommand } from '../utils/commandExecutor.js';
4
+ export function useKeyboardInput(options) {
5
+ const { buffer, disabled, triggerUpdate, forceUpdate, showCommands, setShowCommands, commandSelectedIndex, setCommandSelectedIndex, getFilteredCommands, updateCommandPanelState, onCommand, showFilePicker, setShowFilePicker, fileSelectedIndex, setFileSelectedIndex, setFileQuery, setAtSymbolPosition, filteredFileCount, updateFilePickerState, handleFileSelect, fileListRef, showHistoryMenu, setShowHistoryMenu, historySelectedIndex, setHistorySelectedIndex, escapeKeyCount, setEscapeKeyCount, escapeKeyTimer, getUserMessages, handleHistorySelect, pasteFromClipboard, onSubmit, } = options;
6
+ // Track paste detection
7
+ const inputBuffer = useRef('');
8
+ const inputTimer = useRef(null);
9
+ // Cleanup timer on unmount
10
+ useEffect(() => {
11
+ return () => {
12
+ if (inputTimer.current) {
13
+ clearTimeout(inputTimer.current);
14
+ }
15
+ };
16
+ }, []);
17
+ // Force immediate state update for critical operations like backspace
18
+ const forceStateUpdate = () => {
19
+ const text = buffer.getFullText();
20
+ const cursorPos = buffer.getCursorPosition();
21
+ updateFilePickerState(text, cursorPos);
22
+ updateCommandPanelState(text);
23
+ forceUpdate({});
24
+ };
25
+ // Handle input using useInput hook
26
+ useInput((input, key) => {
27
+ if (disabled)
28
+ return;
29
+ // Handle escape key for double-ESC history navigation
30
+ if (key.escape) {
31
+ // Close file picker if open
32
+ if (showFilePicker) {
33
+ setShowFilePicker(false);
34
+ setFileSelectedIndex(0);
35
+ setFileQuery('');
36
+ setAtSymbolPosition(-1);
37
+ return;
38
+ }
39
+ // Don't interfere with existing ESC behavior if in command panel
40
+ if (showCommands) {
41
+ setShowCommands(false);
42
+ setCommandSelectedIndex(0);
43
+ return;
44
+ }
45
+ // Handle history navigation
46
+ if (showHistoryMenu) {
47
+ setShowHistoryMenu(false);
48
+ return;
49
+ }
50
+ // Count escape key presses for double-ESC detection
51
+ setEscapeKeyCount(prev => prev + 1);
52
+ // Clear any existing timer
53
+ if (escapeKeyTimer.current) {
54
+ clearTimeout(escapeKeyTimer.current);
55
+ }
56
+ // Set timer to reset count after 500ms
57
+ escapeKeyTimer.current = setTimeout(() => {
58
+ setEscapeKeyCount(0);
59
+ }, 500);
60
+ // Check for double escape
61
+ if (escapeKeyCount >= 1) {
62
+ // This will be 2 after increment
63
+ const userMessages = getUserMessages();
64
+ if (userMessages.length > 0) {
65
+ setShowHistoryMenu(true);
66
+ setHistorySelectedIndex(0); // Reset selection to first item
67
+ setEscapeKeyCount(0);
68
+ if (escapeKeyTimer.current) {
69
+ clearTimeout(escapeKeyTimer.current);
70
+ escapeKeyTimer.current = null;
71
+ }
72
+ }
73
+ }
74
+ return;
75
+ }
76
+ // Handle history menu navigation
77
+ if (showHistoryMenu) {
78
+ const userMessages = getUserMessages();
79
+ // Up arrow in history menu
80
+ if (key.upArrow) {
81
+ setHistorySelectedIndex(prev => Math.max(0, prev - 1));
82
+ return;
83
+ }
84
+ // Down arrow in history menu
85
+ if (key.downArrow) {
86
+ const maxIndex = Math.max(0, userMessages.length - 1);
87
+ setHistorySelectedIndex(prev => Math.min(maxIndex, prev + 1));
88
+ return;
89
+ }
90
+ // Enter - select history item
91
+ if (key.return) {
92
+ if (userMessages.length > 0 &&
93
+ historySelectedIndex < userMessages.length) {
94
+ const selectedMessage = userMessages[historySelectedIndex];
95
+ if (selectedMessage) {
96
+ handleHistorySelect(selectedMessage.value);
97
+ }
98
+ }
99
+ return;
100
+ }
101
+ // For any other key in history menu, just return to prevent interference
102
+ return;
103
+ }
104
+ // Ctrl+L - Delete from cursor to beginning
105
+ if (key.ctrl && input === 'l') {
106
+ const fullText = buffer.getFullText();
107
+ const cursorPos = buffer.getCursorPosition();
108
+ const afterCursor = fullText.slice(cursorPos);
109
+ buffer.setText(afterCursor);
110
+ forceStateUpdate();
111
+ return;
112
+ }
113
+ // Ctrl+R - Delete from cursor to end
114
+ if (key.ctrl && input === 'r') {
115
+ const fullText = buffer.getFullText();
116
+ const cursorPos = buffer.getCursorPosition();
117
+ const beforeCursor = fullText.slice(0, cursorPos);
118
+ buffer.setText(beforeCursor);
119
+ forceStateUpdate();
120
+ return;
121
+ }
122
+ // Windows: Alt+V, macOS: Ctrl+V - Paste from clipboard (including images)
123
+ const isPasteShortcut = process.platform === 'darwin'
124
+ ? key.ctrl && input === 'v'
125
+ : key.meta && input === 'v';
126
+ if (isPasteShortcut) {
127
+ pasteFromClipboard();
128
+ return;
129
+ }
130
+ // Backspace
131
+ if (key.backspace || key.delete) {
132
+ buffer.backspace();
133
+ forceStateUpdate();
134
+ return;
135
+ }
136
+ // Handle file picker navigation
137
+ if (showFilePicker) {
138
+ // Up arrow in file picker
139
+ if (key.upArrow) {
140
+ setFileSelectedIndex(prev => Math.max(0, prev - 1));
141
+ return;
142
+ }
143
+ // Down arrow in file picker
144
+ if (key.downArrow) {
145
+ const maxIndex = Math.max(0, filteredFileCount - 1);
146
+ setFileSelectedIndex(prev => Math.min(maxIndex, prev + 1));
147
+ return;
148
+ }
149
+ // Tab or Enter - select file
150
+ if (key.tab || key.return) {
151
+ if (filteredFileCount > 0 && fileSelectedIndex < filteredFileCount) {
152
+ const selectedFile = fileListRef.current?.getSelectedFile();
153
+ if (selectedFile) {
154
+ handleFileSelect(selectedFile);
155
+ }
156
+ }
157
+ return;
158
+ }
159
+ }
160
+ // Handle command panel navigation
161
+ if (showCommands) {
162
+ const filteredCommands = getFilteredCommands();
163
+ // Up arrow in command panel
164
+ if (key.upArrow) {
165
+ setCommandSelectedIndex(prev => Math.max(0, prev - 1));
166
+ return;
167
+ }
168
+ // Down arrow in command panel
169
+ if (key.downArrow) {
170
+ const maxIndex = Math.max(0, filteredCommands.length - 1);
171
+ setCommandSelectedIndex(prev => Math.min(maxIndex, prev + 1));
172
+ return;
173
+ }
174
+ // Enter - select command
175
+ if (key.return) {
176
+ if (filteredCommands.length > 0 &&
177
+ commandSelectedIndex < filteredCommands.length) {
178
+ const selectedCommand = filteredCommands[commandSelectedIndex];
179
+ if (selectedCommand) {
180
+ // Execute command instead of inserting text
181
+ executeCommand(selectedCommand.name).then(result => {
182
+ if (onCommand) {
183
+ onCommand(selectedCommand.name, result);
184
+ }
185
+ });
186
+ buffer.setText('');
187
+ setShowCommands(false);
188
+ setCommandSelectedIndex(0);
189
+ triggerUpdate();
190
+ return;
191
+ }
192
+ }
193
+ // If no commands available, fall through to normal Enter handling
194
+ }
195
+ }
196
+ // Enter - submit message
197
+ if (key.return) {
198
+ const message = buffer.getFullText().trim();
199
+ if (message) {
200
+ // Get images data, but only include images whose placeholders still exist
201
+ const currentText = buffer.text; // Use internal text (includes placeholders)
202
+ const allImages = buffer.getImages();
203
+ const validImages = allImages
204
+ .filter(img => currentText.includes(img.placeholder))
205
+ .map(img => ({
206
+ data: img.data,
207
+ mimeType: img.mimeType,
208
+ }));
209
+ buffer.setText('');
210
+ forceUpdate({});
211
+ onSubmit(message, validImages.length > 0 ? validImages : undefined);
212
+ }
213
+ return;
214
+ }
215
+ // Arrow keys for cursor movement
216
+ if (key.leftArrow) {
217
+ buffer.moveLeft();
218
+ const text = buffer.getFullText();
219
+ const cursorPos = buffer.getCursorPosition();
220
+ updateFilePickerState(text, cursorPos);
221
+ triggerUpdate();
222
+ return;
223
+ }
224
+ if (key.rightArrow) {
225
+ buffer.moveRight();
226
+ const text = buffer.getFullText();
227
+ const cursorPos = buffer.getCursorPosition();
228
+ updateFilePickerState(text, cursorPos);
229
+ triggerUpdate();
230
+ return;
231
+ }
232
+ if (key.upArrow && !showCommands && !showFilePicker) {
233
+ buffer.moveUp();
234
+ const text = buffer.getFullText();
235
+ const cursorPos = buffer.getCursorPosition();
236
+ updateFilePickerState(text, cursorPos);
237
+ triggerUpdate();
238
+ return;
239
+ }
240
+ if (key.downArrow && !showCommands && !showFilePicker) {
241
+ buffer.moveDown();
242
+ const text = buffer.getFullText();
243
+ const cursorPos = buffer.getCursorPosition();
244
+ updateFilePickerState(text, cursorPos);
245
+ triggerUpdate();
246
+ return;
247
+ }
248
+ // Regular character input
249
+ if (input && !key.ctrl && !key.meta && !key.escape) {
250
+ // Accumulate input for paste detection
251
+ inputBuffer.current += input;
252
+ // Clear existing timer
253
+ if (inputTimer.current) {
254
+ clearTimeout(inputTimer.current);
255
+ }
256
+ // Set timer to process accumulated input
257
+ inputTimer.current = setTimeout(() => {
258
+ const accumulated = inputBuffer.current;
259
+ inputBuffer.current = '';
260
+ // If we accumulated input, it's likely a paste
261
+ if (accumulated) {
262
+ buffer.insert(accumulated);
263
+ const text = buffer.getFullText();
264
+ const cursorPos = buffer.getCursorPosition();
265
+ updateCommandPanelState(text);
266
+ updateFilePickerState(text, cursorPos);
267
+ triggerUpdate();
268
+ }
269
+ }, 10); // Short delay to accumulate rapid input
270
+ }
271
+ });
272
+ }
@@ -0,0 +1,12 @@
1
+ export declare function useSnapshotState(messagesLength: number): {
2
+ snapshotFileCount: Map<number, number>;
3
+ setSnapshotFileCount: import("react").Dispatch<import("react").SetStateAction<Map<number, number>>>;
4
+ pendingRollback: {
5
+ messageIndex: number;
6
+ fileCount: number;
7
+ } | null;
8
+ setPendingRollback: import("react").Dispatch<import("react").SetStateAction<{
9
+ messageIndex: number;
10
+ fileCount: number;
11
+ } | null>>;
12
+ };
@@ -0,0 +1,28 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { sessionManager } from '../utils/sessionManager.js';
3
+ import { incrementalSnapshotManager } from '../utils/incrementalSnapshot.js';
4
+ export function useSnapshotState(messagesLength) {
5
+ const [snapshotFileCount, setSnapshotFileCount] = useState(new Map());
6
+ const [pendingRollback, setPendingRollback] = useState(null);
7
+ // Load snapshot file counts when session changes
8
+ useEffect(() => {
9
+ const loadSnapshotFileCounts = async () => {
10
+ const currentSession = sessionManager.getCurrentSession();
11
+ if (!currentSession)
12
+ return;
13
+ const snapshots = await incrementalSnapshotManager.listSnapshots(currentSession.id);
14
+ const counts = new Map();
15
+ for (const snapshot of snapshots) {
16
+ counts.set(snapshot.messageIndex, snapshot.fileCount);
17
+ }
18
+ setSnapshotFileCount(counts);
19
+ };
20
+ loadSnapshotFileCounts();
21
+ }, [messagesLength]); // Reload when messages change
22
+ return {
23
+ snapshotFileCount,
24
+ setSnapshotFileCount,
25
+ pendingRollback,
26
+ setPendingRollback,
27
+ };
28
+ }
@@ -0,0 +1,24 @@
1
+ import type { UsageInfo } from '../api/chat.js';
2
+ export type RetryStatus = {
3
+ isRetrying: boolean;
4
+ attempt: number;
5
+ nextDelay: number;
6
+ remainingSeconds?: number;
7
+ errorMessage?: string;
8
+ };
9
+ export declare function useStreamingState(): {
10
+ isStreaming: boolean;
11
+ setIsStreaming: import("react").Dispatch<import("react").SetStateAction<boolean>>;
12
+ streamTokenCount: number;
13
+ setStreamTokenCount: import("react").Dispatch<import("react").SetStateAction<number>>;
14
+ isReasoning: boolean;
15
+ setIsReasoning: import("react").Dispatch<import("react").SetStateAction<boolean>>;
16
+ abortController: AbortController | null;
17
+ setAbortController: import("react").Dispatch<import("react").SetStateAction<AbortController | null>>;
18
+ contextUsage: UsageInfo | null;
19
+ setContextUsage: import("react").Dispatch<import("react").SetStateAction<UsageInfo | null>>;
20
+ elapsedSeconds: number;
21
+ retryStatus: RetryStatus | null;
22
+ setRetryStatus: import("react").Dispatch<import("react").SetStateAction<RetryStatus | null>>;
23
+ animationFrame: number;
24
+ };
@@ -0,0 +1,96 @@
1
+ import { useState, useEffect } from 'react';
2
+ export function useStreamingState() {
3
+ const [isStreaming, setIsStreaming] = useState(false);
4
+ const [streamTokenCount, setStreamTokenCount] = useState(0);
5
+ const [isReasoning, setIsReasoning] = useState(false);
6
+ const [abortController, setAbortController] = useState(null);
7
+ const [contextUsage, setContextUsage] = useState(null);
8
+ const [elapsedSeconds, setElapsedSeconds] = useState(0);
9
+ const [timerStartTime, setTimerStartTime] = useState(null);
10
+ const [retryStatus, setRetryStatus] = useState(null);
11
+ const [animationFrame, setAnimationFrame] = useState(0);
12
+ // Animation for streaming/saving indicator
13
+ useEffect(() => {
14
+ if (!isStreaming)
15
+ return;
16
+ const interval = setInterval(() => {
17
+ setAnimationFrame(prev => (prev + 1) % 5);
18
+ }, 300);
19
+ return () => {
20
+ clearInterval(interval);
21
+ setAnimationFrame(0);
22
+ };
23
+ }, [isStreaming]);
24
+ // Timer for tracking request duration
25
+ useEffect(() => {
26
+ if (isStreaming && timerStartTime === null) {
27
+ // Start timer when streaming begins
28
+ setTimerStartTime(Date.now());
29
+ setElapsedSeconds(0);
30
+ }
31
+ else if (!isStreaming && timerStartTime !== null) {
32
+ // Stop timer when streaming ends
33
+ setTimerStartTime(null);
34
+ }
35
+ }, [isStreaming, timerStartTime]);
36
+ // Update elapsed time every second
37
+ useEffect(() => {
38
+ if (timerStartTime === null)
39
+ return;
40
+ const interval = setInterval(() => {
41
+ const elapsed = Math.floor((Date.now() - timerStartTime) / 1000);
42
+ setElapsedSeconds(elapsed);
43
+ }, 1000);
44
+ return () => clearInterval(interval);
45
+ }, [timerStartTime]);
46
+ // Countdown timer for retry delays
47
+ useEffect(() => {
48
+ if (!retryStatus || !retryStatus.isRetrying)
49
+ return;
50
+ // Initialize remaining seconds from nextDelay
51
+ if (retryStatus.remainingSeconds === undefined) {
52
+ setRetryStatus(prev => prev
53
+ ? {
54
+ ...prev,
55
+ remainingSeconds: Math.ceil(prev.nextDelay / 1000),
56
+ }
57
+ : null);
58
+ return;
59
+ }
60
+ // Countdown every second
61
+ const interval = setInterval(() => {
62
+ setRetryStatus(prev => {
63
+ if (!prev || prev.remainingSeconds === undefined)
64
+ return prev;
65
+ const newRemaining = prev.remainingSeconds - 1;
66
+ if (newRemaining <= 0) {
67
+ return {
68
+ ...prev,
69
+ remainingSeconds: 0,
70
+ };
71
+ }
72
+ return {
73
+ ...prev,
74
+ remainingSeconds: newRemaining,
75
+ };
76
+ });
77
+ }, 1000);
78
+ return () => clearInterval(interval);
79
+ }, [retryStatus]);
80
+ return {
81
+ isStreaming,
82
+ setIsStreaming,
83
+ streamTokenCount,
84
+ setStreamTokenCount,
85
+ isReasoning,
86
+ setIsReasoning,
87
+ abortController,
88
+ setAbortController,
89
+ contextUsage,
90
+ setContextUsage,
91
+ elapsedSeconds,
92
+ retryStatus,
93
+ setRetryStatus,
94
+ animationFrame,
95
+ };
96
+ }
@@ -0,0 +1,8 @@
1
+ import { type EditorContext } from '../utils/vscodeConnection.js';
2
+ export type VSCodeConnectionStatus = 'disconnected' | 'connecting' | 'connected' | 'error';
3
+ export declare function useVSCodeState(): {
4
+ vscodeConnected: boolean;
5
+ vscodeConnectionStatus: VSCodeConnectionStatus;
6
+ setVscodeConnectionStatus: import("react").Dispatch<import("react").SetStateAction<VSCodeConnectionStatus>>;
7
+ editorContext: EditorContext;
8
+ };
@@ -0,0 +1,63 @@
1
+ import { useState, useEffect } from 'react';
2
+ import { vscodeConnection } from '../utils/vscodeConnection.js';
3
+ export function useVSCodeState() {
4
+ const [vscodeConnected, setVscodeConnected] = useState(false);
5
+ const [vscodeConnectionStatus, setVscodeConnectionStatus] = useState('disconnected');
6
+ const [editorContext, setEditorContext] = useState({});
7
+ // Monitor VSCode connection status and editor context
8
+ useEffect(() => {
9
+ const checkConnectionInterval = setInterval(() => {
10
+ const isConnected = vscodeConnection.isConnected();
11
+ setVscodeConnected(isConnected);
12
+ // Update connection status based on actual connection state
13
+ if (isConnected && vscodeConnectionStatus !== 'connected') {
14
+ setVscodeConnectionStatus('connected');
15
+ }
16
+ else if (!isConnected && vscodeConnectionStatus === 'connected') {
17
+ setVscodeConnectionStatus('disconnected');
18
+ }
19
+ }, 1000);
20
+ const unsubscribe = vscodeConnection.onContextUpdate(context => {
21
+ setEditorContext(context);
22
+ // When we receive context, it means connection is successful
23
+ if (vscodeConnectionStatus !== 'connected') {
24
+ setVscodeConnectionStatus('connected');
25
+ }
26
+ });
27
+ return () => {
28
+ clearInterval(checkConnectionInterval);
29
+ unsubscribe();
30
+ };
31
+ }, [vscodeConnectionStatus]);
32
+ // Separate effect for handling connecting timeout
33
+ useEffect(() => {
34
+ if (vscodeConnectionStatus !== 'connecting') {
35
+ return;
36
+ }
37
+ // Set timeout for connecting state (30 seconds to allow for VSCode extension reconnection)
38
+ const connectingTimeout = setTimeout(() => {
39
+ const isConnected = vscodeConnection.isConnected();
40
+ const isServerRunning = vscodeConnection.isServerRunning();
41
+ // Only set error if still not connected after timeout
42
+ if (!isConnected) {
43
+ if (isServerRunning) {
44
+ // Server is running but no connection - show error with helpful message
45
+ setVscodeConnectionStatus('error');
46
+ }
47
+ else {
48
+ // Server not running - go back to disconnected
49
+ setVscodeConnectionStatus('disconnected');
50
+ }
51
+ }
52
+ }, 30000); // Increased to 30 seconds
53
+ return () => {
54
+ clearTimeout(connectingTimeout);
55
+ };
56
+ }, [vscodeConnectionStatus]);
57
+ return {
58
+ vscodeConnected,
59
+ vscodeConnectionStatus,
60
+ setVscodeConnectionStatus,
61
+ editorContext,
62
+ };
63
+ }
@@ -82,12 +82,12 @@ export declare class FilesystemMCPService {
82
82
  */
83
83
  createFile(filePath: string, content: string, createDirectories?: boolean): Promise<string>;
84
84
  /**
85
- * Delete a file
86
- * @param filePath - Path to the file to delete
87
- * @returns Success message
85
+ * Delete one or multiple files
86
+ * @param filePaths - Single file path or array of file paths to delete
87
+ * @returns Success message with details
88
88
  * @throws Error if file deletion fails
89
89
  */
90
- deleteFile(filePath: string): Promise<string>;
90
+ deleteFile(filePaths: string | string[]): Promise<string>;
91
91
  /**
92
92
  * List files in a directory
93
93
  * @param dirPath - Directory path relative to base path or absolute path
@@ -181,6 +181,7 @@ export declare const mcpTools: ({
181
181
  };
182
182
  content?: undefined;
183
183
  createDirectories?: undefined;
184
+ filePaths?: undefined;
184
185
  dirPath?: undefined;
185
186
  newContent?: undefined;
186
187
  contextLines?: undefined;
@@ -213,6 +214,7 @@ export declare const mcpTools: ({
213
214
  };
214
215
  startLine?: undefined;
215
216
  endLine?: undefined;
217
+ filePaths?: undefined;
216
218
  dirPath?: undefined;
217
219
  newContent?: undefined;
218
220
  contextLines?: undefined;
@@ -234,6 +236,20 @@ export declare const mcpTools: ({
234
236
  type: string;
235
237
  description: string;
236
238
  };
239
+ filePaths: {
240
+ oneOf: ({
241
+ type: string;
242
+ description: string;
243
+ items?: undefined;
244
+ } | {
245
+ type: string;
246
+ items: {
247
+ type: string;
248
+ };
249
+ description: string;
250
+ })[];
251
+ description: string;
252
+ };
237
253
  startLine?: undefined;
238
254
  endLine?: undefined;
239
255
  content?: undefined;
@@ -247,7 +263,7 @@ export declare const mcpTools: ({
247
263
  maxResults?: undefined;
248
264
  searchMode?: undefined;
249
265
  };
250
- required: string[];
266
+ required?: undefined;
251
267
  };
252
268
  } | {
253
269
  name: string;
@@ -265,6 +281,7 @@ export declare const mcpTools: ({
265
281
  endLine?: undefined;
266
282
  content?: undefined;
267
283
  createDirectories?: undefined;
284
+ filePaths?: undefined;
268
285
  newContent?: undefined;
269
286
  contextLines?: undefined;
270
287
  query?: undefined;
@@ -304,6 +321,7 @@ export declare const mcpTools: ({
304
321
  };
305
322
  content?: undefined;
306
323
  createDirectories?: undefined;
324
+ filePaths?: undefined;
307
325
  dirPath?: undefined;
308
326
  query?: undefined;
309
327
  fileExtensions?: undefined;
@@ -357,6 +375,7 @@ export declare const mcpTools: ({
357
375
  endLine?: undefined;
358
376
  content?: undefined;
359
377
  createDirectories?: undefined;
378
+ filePaths?: undefined;
360
379
  newContent?: undefined;
361
380
  contextLines?: undefined;
362
381
  };