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
@@ -101,10 +101,41 @@ class SessionManager {
101
101
  this.currentSession = await this.createNewSession();
102
102
  }
103
103
  // Check if this exact message already exists to prevent duplicates
104
- const existingMessage = this.currentSession.messages.find(m => m.role === message.role &&
105
- m.content === message.content &&
106
- Math.abs(m.timestamp - message.timestamp) < 5000 // Within 5 seconds
107
- );
104
+ // For assistant messages with tool_calls, also compare tool_call_id to ensure uniqueness
105
+ const existingMessage = this.currentSession.messages.find(m => {
106
+ if (m.role !== message.role)
107
+ return false;
108
+ if (m.content !== message.content)
109
+ return false;
110
+ if (Math.abs(m.timestamp - message.timestamp) >= 5000)
111
+ return false;
112
+ // If both messages have tool_calls, compare tool call IDs
113
+ if (m.tool_calls && message.tool_calls) {
114
+ // Create sets of tool call IDs for comparison
115
+ const existingIds = new Set(m.tool_calls.map(tc => tc.id));
116
+ const newIds = new Set(message.tool_calls.map(tc => tc.id));
117
+ // If IDs are different, these are different messages
118
+ if (existingIds.size !== newIds.size)
119
+ return false;
120
+ for (const id of newIds) {
121
+ if (!existingIds.has(id))
122
+ return false;
123
+ }
124
+ }
125
+ else if (m.tool_calls || message.tool_calls) {
126
+ // One has tool_calls, the other doesn't - different messages
127
+ return false;
128
+ }
129
+ // If both have tool_call_id (tool response), compare them
130
+ if (m.tool_call_id && message.tool_call_id) {
131
+ return m.tool_call_id === message.tool_call_id;
132
+ }
133
+ else if (m.tool_call_id || message.tool_call_id) {
134
+ // One has tool_call_id, the other doesn't - different messages
135
+ return false;
136
+ }
137
+ return true;
138
+ });
108
139
  if (existingMessage) {
109
140
  return; // Don't add duplicate message
110
141
  }
@@ -31,3 +31,7 @@ export declare function codePointToVisualPos(str: string, codePointIndex: number
31
31
  * Convert visual position to code point index.
32
32
  */
33
33
  export declare function visualPosToCodePoint(str: string, visualPos: number): number;
34
+ /**
35
+ * Format elapsed time to human readable format.
36
+ */
37
+ export declare function formatElapsedTime(seconds: number): string;
@@ -81,3 +81,22 @@ export function visualPosToCodePoint(str, visualPos) {
81
81
  }
82
82
  return codePoints.length;
83
83
  }
84
+ /**
85
+ * Format elapsed time to human readable format.
86
+ */
87
+ export function formatElapsedTime(seconds) {
88
+ if (seconds < 60) {
89
+ return `${seconds}s`;
90
+ }
91
+ else if (seconds < 3600) {
92
+ const minutes = Math.floor(seconds / 60);
93
+ const remainingSeconds = seconds % 60;
94
+ return `${minutes}m ${remainingSeconds}s`;
95
+ }
96
+ else {
97
+ const hours = Math.floor(seconds / 3600);
98
+ const remainingMinutes = Math.floor((seconds % 3600) / 60);
99
+ const remainingSeconds = seconds % 60;
100
+ return `${hours}h ${remainingMinutes}m ${remainingSeconds}s`;
101
+ }
102
+ }
@@ -1,5 +1,5 @@
1
1
  export declare function formatTodoContext(todos: Array<{
2
2
  id: string;
3
3
  content: string;
4
- status: 'pending' | 'in_progress' | 'completed';
4
+ status: 'pending' | 'completed';
5
5
  }>): string;
@@ -4,7 +4,6 @@ export function formatTodoContext(todos) {
4
4
  }
5
5
  const statusSymbol = {
6
6
  pending: '[ ]',
7
- in_progress: '[~]',
8
7
  completed: '[x]',
9
8
  };
10
9
  const lines = [
@@ -36,6 +36,14 @@ declare class VSCodeConnectionManager {
36
36
  * @returns Promise that resolves with diagnostics array
37
37
  */
38
38
  requestDiagnostics(filePath: string): Promise<Diagnostic[]>;
39
+ /**
40
+ * Request DIFF+APPLY view for file changes in VS Code
41
+ * @param filePath - The file path for the diff
42
+ * @param oldContent - Original content with line numbers
43
+ * @param newContent - Modified content with line numbers
44
+ * @returns Promise that resolves with user's approval response
45
+ */
46
+ requestDiffApply(filePath: string, oldContent: string, newContent: string): Promise<'approve' | 'approve_always' | 'reject'>;
39
47
  }
40
48
  export declare const vscodeConnection: VSCodeConnectionManager;
41
49
  export type { EditorContext, Diagnostic };
@@ -160,5 +160,49 @@ class VSCodeConnectionManager {
160
160
  }));
161
161
  });
162
162
  }
163
+ /**
164
+ * Request DIFF+APPLY view for file changes in VS Code
165
+ * @param filePath - The file path for the diff
166
+ * @param oldContent - Original content with line numbers
167
+ * @param newContent - Modified content with line numbers
168
+ * @returns Promise that resolves with user's approval response
169
+ */
170
+ async requestDiffApply(filePath, oldContent, newContent) {
171
+ return new Promise((resolve) => {
172
+ if (!this.client || this.client.readyState !== WebSocket.OPEN) {
173
+ resolve('approve'); // If not connected, default to approve (fallback to CLI confirmation)
174
+ return;
175
+ }
176
+ const requestId = Math.random().toString(36).substring(7);
177
+ const timeout = setTimeout(() => {
178
+ cleanup();
179
+ resolve('approve'); // Timeout, default to approve
180
+ }, 30000); // 30 second timeout for user decision
181
+ const handler = (message) => {
182
+ try {
183
+ const data = JSON.parse(message.toString());
184
+ if (data.type === 'diffApplyResult' && data.requestId === requestId) {
185
+ cleanup();
186
+ resolve(data.result || 'approve');
187
+ }
188
+ }
189
+ catch (error) {
190
+ // Ignore invalid JSON
191
+ }
192
+ };
193
+ const cleanup = () => {
194
+ clearTimeout(timeout);
195
+ this.client?.removeListener('message', handler);
196
+ };
197
+ this.client.on('message', handler);
198
+ this.client.send(JSON.stringify({
199
+ type: 'diffApply',
200
+ requestId,
201
+ filePath,
202
+ oldContent,
203
+ newContent
204
+ }));
205
+ });
206
+ }
163
207
  }
164
208
  export const vscodeConnection = new VSCodeConnectionManager();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
package/readme.md CHANGED
@@ -73,7 +73,9 @@ $ npm uninstall --global snow-ai
73
73
 
74
74
  * When mounting: double-click ESC, view the dialogue recorder, select rollback, including file checkpoints
75
75
 
76
- * Windows & MacOS:`ctrl + v` Paste image
76
+ * MacOS:`ctrl + v` Paste image
77
+ * Windows:`alt + v` Paste image
78
+
77
79
 
78
80
  * **Commands**
79
81