snow-ai 0.2.25 → 0.2.27

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.
@@ -4,21 +4,16 @@ import { cpLen, cpSlice, visualWidth, toCodePoints } from './textUtils.js';
4
4
  */
5
5
  function sanitizeInput(str) {
6
6
  // Replace problematic characters but preserve basic formatting
7
- return str
7
+ return (str
8
8
  .replace(/\r\n/g, '\n') // Normalize line endings
9
9
  .replace(/\r/g, '\n') // Convert remaining \r to \n
10
10
  .replace(/\t/g, ' ') // Convert tabs to spaces
11
- // Remove focus events - complete escape sequences and standalone patterns
12
- // ESC[I and ESC[O are focus events from terminal focus in/out
13
- .replace(/\x1b\[I/g, '')
14
- .replace(/\x1b\[O/g, '')
15
- // Also remove standalone [I and [O if they appear at word boundaries
16
- // This catches cases where escape sequences arrive fragmented
17
- // But we preserve legitimate text like "FOO[I]BAR" or "[Inside]"
18
- .replace(/(?:^|\s)\[I(?:\s|$)/g, ' ')
19
- .replace(/(?:^|\s)\[O(?:\s|$)/g, ' ')
11
+ // Remove focus events emitted during terminal focus changes
12
+ .replace(/\x1b\[[IO]/g, '')
13
+ // Remove stray [I/[O] tokens that precede drag-and-drop payloads
14
+ .replace(/(^|\s+)\[(?:I|O)(?=(?:\s|$|["'~\\\/]|[A-Za-z]:))/g, '$1')
20
15
  // Remove control characters except newlines
21
- .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
16
+ .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ''));
22
17
  }
23
18
  export class TextBuffer {
24
19
  constructor(viewport, onUpdate) {
@@ -145,7 +140,9 @@ export class TextBuffer {
145
140
  let fullText = this.content;
146
141
  for (const placeholder of this.pasteStorage.values()) {
147
142
  if (placeholder.placeholder) {
148
- fullText = fullText.split(placeholder.placeholder).join(placeholder.content);
143
+ fullText = fullText
144
+ .split(placeholder.placeholder)
145
+ .join(placeholder.content);
149
146
  }
150
147
  }
151
148
  return fullText;
@@ -258,7 +255,7 @@ export class TextBuffer {
258
255
  content: this.pasteAccumulator,
259
256
  charCount: totalChars,
260
257
  index: this.pasteCounter,
261
- placeholder: placeholderText
258
+ placeholder: placeholderText,
262
259
  });
263
260
  // 在记录的位置插入占位符
264
261
  const before = cpSlice(this.content, 0, this.pastePlaceholderPosition);
@@ -271,7 +268,8 @@ export class TextBuffer {
271
268
  const before = cpSlice(this.content, 0, this.pastePlaceholderPosition);
272
269
  const after = cpSlice(this.content, this.pastePlaceholderPosition);
273
270
  this.content = before + this.pasteAccumulator + after;
274
- this.cursorIndex = this.pastePlaceholderPosition + cpLen(this.pasteAccumulator);
271
+ this.cursorIndex =
272
+ this.pastePlaceholderPosition + cpLen(this.pasteAccumulator);
275
273
  }
276
274
  // 清理状态
277
275
  this.pasteAccumulator = '';
@@ -501,7 +499,7 @@ export class TextBuffer {
501
499
  data: base64Data,
502
500
  mimeType: mimeType,
503
501
  index: this.imageCounter,
504
- placeholder: placeholderText
502
+ placeholder: placeholderText,
505
503
  });
506
504
  this.insertPlainText(placeholderText);
507
505
  this.scheduleUpdate();
@@ -280,16 +280,21 @@ class VSCodeConnectionManager {
280
280
  return;
281
281
  }
282
282
  const requestId = Math.random().toString(36).substring(7);
283
+ let isResolved = false;
283
284
  const timeout = setTimeout(() => {
284
- cleanup();
285
- resolve([]); // Timeout, return empty array
286
- }, 5000); // 5 second timeout
285
+ if (!isResolved) {
286
+ cleanup();
287
+ resolve([]); // Timeout, return empty array
288
+ }
289
+ }, 2000); // Reduce timeout from 5s to 2s to avoid long blocking
287
290
  const handler = (message) => {
288
291
  try {
289
292
  const data = JSON.parse(message.toString());
290
293
  if (data.type === 'diagnostics' && data.requestId === requestId) {
291
- cleanup();
292
- resolve(data.diagnostics || []);
294
+ if (!isResolved) {
295
+ cleanup();
296
+ resolve(data.diagnostics || []);
297
+ }
293
298
  }
294
299
  }
295
300
  catch (error) {
@@ -297,15 +302,25 @@ class VSCodeConnectionManager {
297
302
  }
298
303
  };
299
304
  const cleanup = () => {
305
+ isResolved = true;
300
306
  clearTimeout(timeout);
301
- this.client?.removeListener('message', handler);
307
+ if (this.client) {
308
+ this.client.off('message', handler);
309
+ }
302
310
  };
303
311
  this.client.on('message', handler);
304
- this.client.send(JSON.stringify({
305
- type: 'getDiagnostics',
306
- requestId,
307
- filePath,
308
- }));
312
+ // Add error handling for send operation
313
+ try {
314
+ this.client.send(JSON.stringify({
315
+ type: 'getDiagnostics',
316
+ requestId,
317
+ filePath,
318
+ }));
319
+ }
320
+ catch (error) {
321
+ cleanup();
322
+ resolve([]); // If send fails, return empty array
323
+ }
309
324
  });
310
325
  }
311
326
  /**
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
4
  import crypto from 'crypto';
5
+ import { logger } from '../utils/logger.js';
5
6
  /**
6
7
  * Workspace Snapshot Manager
7
8
  * Provides git-like version control for workspace files
@@ -196,7 +197,7 @@ class WorkspaceSnapshotManager {
196
197
  }
197
198
  }
198
199
  catch (error) {
199
- console.error('Failed to list snapshots:', error);
200
+ logger.error('Failed to list snapshots:', error);
200
201
  }
201
202
  return snapshots.sort((a, b) => b.messageIndex - a.messageIndex);
202
203
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.2.25",
3
+ "version": "0.2.27",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -47,6 +47,7 @@
47
47
  "cli-highlight": "^2.1.11",
48
48
  "diff": "^8.0.2",
49
49
  "figlet": "^1.8.2",
50
+ "fzf": "^0.5.2",
50
51
  "https-proxy-agent": "^7.0.6",
51
52
  "ink": "^5.2.1",
52
53
  "ink-gradient": "^3.0.0",