snow-ai 0.3.1 → 0.3.3

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.
@@ -2,6 +2,7 @@ import fs from 'fs/promises';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
4
  import { randomUUID } from 'crypto';
5
+ import { summaryAgent } from '../agents/summaryAgent.js';
5
6
  class SessionManager {
6
7
  constructor() {
7
8
  Object.defineProperty(this, "sessionsDir", {
@@ -16,6 +17,18 @@ class SessionManager {
16
17
  writable: true,
17
18
  value: null
18
19
  });
20
+ Object.defineProperty(this, "summaryAbortController", {
21
+ enumerable: true,
22
+ configurable: true,
23
+ writable: true,
24
+ value: null
25
+ });
26
+ Object.defineProperty(this, "summaryTimeoutId", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: null
31
+ });
19
32
  this.sessionsDir = path.join(os.homedir(), '.snow', 'sessions');
20
33
  }
21
34
  async ensureSessionsDir() {
@@ -29,6 +42,20 @@ class SessionManager {
29
42
  getSessionPath(sessionId) {
30
43
  return path.join(this.sessionsDir, `${sessionId}.json`);
31
44
  }
45
+ /**
46
+ * Cancel any ongoing summary generation
47
+ * This prevents wasted resources and race conditions
48
+ */
49
+ cancelOngoingSummaryGeneration() {
50
+ if (this.summaryAbortController) {
51
+ this.summaryAbortController.abort();
52
+ this.summaryAbortController = null;
53
+ }
54
+ if (this.summaryTimeoutId) {
55
+ clearTimeout(this.summaryTimeoutId);
56
+ this.summaryTimeoutId = null;
57
+ }
58
+ }
32
59
  async createNewSession() {
33
60
  await this.ensureSessionsDir();
34
61
  // 使用 UUID v4 生成唯一会话 ID,避免并发冲突
@@ -40,7 +67,7 @@ class SessionManager {
40
67
  createdAt: Date.now(),
41
68
  updatedAt: Date.now(),
42
69
  messages: [],
43
- messageCount: 0
70
+ messageCount: 0,
44
71
  };
45
72
  this.currentSession = session;
46
73
  await this.saveSession(session);
@@ -80,7 +107,7 @@ class SessionManager {
80
107
  summary: session.summary,
81
108
  createdAt: session.createdAt,
82
109
  updatedAt: session.updatedAt,
83
- messageCount: session.messageCount
110
+ messageCount: session.messageCount,
84
111
  });
85
112
  }
86
113
  catch (error) {
@@ -142,10 +169,59 @@ class SessionManager {
142
169
  this.currentSession.messages.push(message);
143
170
  this.currentSession.messageCount = this.currentSession.messages.length;
144
171
  this.currentSession.updatedAt = Date.now();
145
- // Simple title generation from first user message (no API call)
172
+ // Generate summary from first user message using summaryAgent (parallel, non-blocking)
146
173
  if (this.currentSession.messageCount === 1 && message.role === 'user') {
174
+ // Set temporary title immediately (synchronous)
147
175
  this.currentSession.title = message.content.slice(0, 50);
148
176
  this.currentSession.summary = message.content.slice(0, 100);
177
+ // Cancel any previous summary generation (防呆机制)
178
+ this.cancelOngoingSummaryGeneration();
179
+ // Create new AbortController for this summary generation
180
+ this.summaryAbortController = new AbortController();
181
+ const currentSessionId = this.currentSession.id;
182
+ const abortSignal = this.summaryAbortController.signal;
183
+ // Set timeout to cancel summary generation after 30 seconds (防呆机制)
184
+ this.summaryTimeoutId = setTimeout(() => {
185
+ if (this.summaryAbortController) {
186
+ console.warn('Summary generation timeout after 30s, aborting...');
187
+ this.summaryAbortController.abort();
188
+ this.summaryAbortController = null;
189
+ }
190
+ }, 30000);
191
+ // Generate better summary in parallel (non-blocking)
192
+ // This won't delay the main conversation flow
193
+ summaryAgent
194
+ .generateSummary(message.content, abortSignal)
195
+ .then(summary => {
196
+ // 防呆检查:确保会话没有被切换,且仍然是第一条消息
197
+ if (this.currentSession &&
198
+ this.currentSession.id === currentSessionId &&
199
+ this.currentSession.messageCount === 1) {
200
+ // Only update if this is still the first message in the same session
201
+ this.currentSession.title = summary;
202
+ this.currentSession.summary = summary;
203
+ this.saveSession(this.currentSession).catch(error => {
204
+ console.error('Failed to save session with generated summary:', error);
205
+ });
206
+ }
207
+ // Clean up
208
+ this.cancelOngoingSummaryGeneration();
209
+ })
210
+ .catch(error => {
211
+ // Clean up on error
212
+ this.cancelOngoingSummaryGeneration();
213
+ // Silently fail if aborted (expected behavior)
214
+ if (error.name === 'AbortError' || abortSignal.aborted) {
215
+ console.log('Summary generation cancelled (expected)');
216
+ return;
217
+ }
218
+ // Log other errors - we already have a fallback title/summary
219
+ console.warn('Summary generation failed, using fallback:', error);
220
+ });
221
+ }
222
+ else if (this.currentSession.messageCount > 1) {
223
+ // 防呆机制:如果不是第一条消息,取消任何正在进行的摘要生成
224
+ this.cancelOngoingSummaryGeneration();
149
225
  }
150
226
  await this.saveSession(this.currentSession);
151
227
  }
@@ -153,9 +229,13 @@ class SessionManager {
153
229
  return this.currentSession;
154
230
  }
155
231
  setCurrentSession(session) {
232
+ // 防呆机制:切换会话时取消正在进行的摘要生成
233
+ this.cancelOngoingSummaryGeneration();
156
234
  this.currentSession = session;
157
235
  }
158
236
  clearCurrentSession() {
237
+ // 防呆机制:清除会话时取消正在进行的摘要生成
238
+ this.cancelOngoingSummaryGeneration();
159
239
  this.currentSession = null;
160
240
  }
161
241
  async deleteSession(sessionId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -39,8 +39,6 @@
39
39
  "dist"
40
40
  ],
41
41
  "dependencies": {
42
- "@anthropic-ai/sdk": "^0.65.0",
43
- "@google/genai": "^1.23.0",
44
42
  "@inkjs/ui": "^2.0.0",
45
43
  "@modelcontextprotocol/sdk": "^1.17.3",
46
44
  "chalk-template": "^1.1.2",
@@ -57,7 +55,6 @@
57
55
  "ink-text-input": "^6.0.0",
58
56
  "ink-tree-select": "^2.3.1",
59
57
  "meow": "^11.0.0",
60
- "openai": "^6.1.0",
61
58
  "puppeteer-core": "^24.25.0",
62
59
  "react": "^18.2.0",
63
60
  "string-width": "^7.2.0",
package/readme.md CHANGED
@@ -6,97 +6,118 @@
6
6
 
7
7
  **English** | [中文](readme_zh.md)
8
8
 
9
- *An intelligent AI-powered CLI tool for developers*
9
+ _An intelligent AI-powered CLI tool for developers_
10
10
 
11
11
  </div>
12
12
 
13
13
  ---
14
14
 
15
-
16
- ## Install
15
+ ## Installation
17
16
 
18
17
  ```bash
19
- $ npm install --global snow-ai
18
+ $ npm install -g snow-ai
20
19
  ```
21
20
 
22
- ## Start
23
- ```bash
24
- $ snow
25
- ```
21
+ You can also clone and build from source: https://github.com/MayDay-wpf/snow-cli
26
22
 
27
- ## Update
28
- ```bash
29
- $ snow --update
30
- ```
23
+ ### Install VSCode Extension
31
24
 
32
- ## Config example `./User/.snow/config.json`
33
- ```json
34
- {
35
- "snowcfg": {
36
- "baseUrl": "https://api.openai.com/v1",//Gemini:https://generativelanguage.googleapis.com Anthropic:https://api.anthropic.com
37
- "apiKey": "your-api-key",
38
- "requestMethod": "responses",
39
- "advancedModel": "gpt-5-codex",
40
- "basicModel": "gpt-5-codex",
41
- "maxContextTokens": 32000, //The maximum context length of the model
42
- "maxTokens": 4096, // The maximum generation length of the model
43
- "anthropicBeta": false,
44
- "compactModel": {
45
- "baseUrl": "https://api.opeai.com/v1",
46
- "apiKey": "your-api-key",
47
- "modelName": "gpt-4.1-mini"
48
- }
49
- }
50
- }
51
- ```
25
+ - Download [VSIX/snow-cli-x.x.x.vsix](https://github.com/MayDay-wpf/snow-cli/blob/main/VSIX/)
52
26
 
53
- ## Uninstall
54
- ```bash
55
- $ npm uninstall --global snow-ai
56
- ```
27
+ - Open VSCode, click `Extensions` -> `Install from VSIX...` -> select `snow-cli-0.2.6.vsix`
57
28
 
58
- ## Install VSCode Extension
29
+ ### Install JetBrains Plugin
59
30
 
60
- * download [VSIX/snow-cli-x.x.x.vsix](https://github.com/MayDay-wpf/snow-cli/blob/main/VSIX/)
31
+ - Download [JetBrains/build/distributions](https://github.com/MayDay-wpf/snow-cli/tree/main/JetBrains/build/distributions)
61
32
 
62
- * open VSCode, click `Extensions` -> `Install from VSIX...` -> select `snow-cli-0.2.6.vsix`
33
+ ## Available Commands
63
34
 
64
- ## Install JetBrains plugin
35
+ - **Start**: `$ snow`
36
+ - **Update**: `$ snow --update`
37
+ - **Version**: `$ snow --version`
38
+ - **Resume**: `$ snow -c` - Restore the latest conversation history (fully compatible with Claude Code)
65
39
 
66
- * download [JetBrains/build/distributions](https://github.com/MayDay-wpf/snow-cli/tree/main/JetBrains/build/distributions)
40
+ ## API & Model Settings
67
41
 
68
- * File > Settings > Plugins
42
+ In version `v0.3.2` and later, all official SDKs have been removed (they were too heavy), so the configuration is slightly different. After starting, enter `API & Model Settings` to see the following options:
69
43
 
70
- ## Live View
71
- * **Welcome & Settings**
44
+ - **Profile** - Switch or create new configurations. Snow now supports saving multiple API and model schemes
45
+ - **Base URL** - Request endpoint. Since official SDKs were removed, OpenAI and Anthropic require `/v1` suffix, Gemini requires `/v1beta`
46
+ - **API Key** - Your API key
47
+ - **Request Method** - Choose based on your needs: `Chat Completions`, `Responses`, `Gemini`, or `Anthropic`
48
+ - **Anthropic Beta** - When checked, Anthropic requests will automatically include `beta=true` parameter
49
+ - **Advanced Model**, **Basic Model**, **Compact Model** - Set the high-performance model for tasks, small model for summarization, and compact model for context compression. All three models use the configured `BaseURL` and `API Key`. The system automatically fetches available models from the `/models` endpoint with filtering support. For APIs with incomplete model lists, use `Manual Input (Enter model name)` to specify the model name
50
+ - **Max Context Tokens** - The model's maximum context window, used for calculating context percentage. For example, Gemini typically has 1M context, so enter `1000000`. This parameter only affects UI calculations, not actual model context
51
+ - **Max Tokens** - This is critical and will be directly added to API requests as the `max_tokens` parameter
72
52
 
73
53
  ![alt text](image.png)
74
54
 
75
- * **Agent**
55
+ ## Proxy & Browser Settings
56
+
57
+ Configure system proxy port and search engine for web search. In most cases, this doesn't need modification as the app will automatically use system proxy. The app automatically detects available search engines (Edge/Chrome) unless you've manually changed their installation paths.
76
58
 
77
59
  ![alt text](image-1.png)
78
- * In the middle of the conversation: click ESC to stop AI generation
79
60
 
80
- * When mounting: double-click ESC, view the dialogue recorder, select rollback, including file checkpoints
61
+ ## System Prompt Settings
81
62
 
82
- * MacOS:`ctrl + v` Paste image
83
- * Windows:`alt + v` Paste image
63
+ Customize your system prompt. Note that this supplements Snow's built-in system prompt rather than replacing it. When you set a custom system prompt, Snow's default prompt is downgraded to a user message and appended to the first user message. On Windows, the app automatically opens Notepad; on macOS/Linux, it uses the system's default terminal text editor. After editing and saving, Snow will close and prompt you to restart: `Custom system prompt saved successfully! Please use 'snow' to restart!`
84
64
 
65
+ ## Custom Headers Settings
85
66
 
86
- * **Commands**
67
+ Add custom request headers. Note that you can only add headers, not override Snow's built-in headers.
68
+
69
+ ## MCP Settings
70
+
71
+ Configure MCP services. The method is identical to setting system prompts, and the JSON format matches Cursor's format.
72
+
73
+ ## Getting Started - Start Conversation
74
+
75
+ Once everything is configured, enter the conversation page by clicking `Start`.
76
+
77
+ - If you launch Snow from VSCode or other editors, Snow will automatically connect to the IDE using the `Snow CLI` plugin. You'll see a connection message. The plugins are published online - search for `Snow CLI` in the plugin marketplace to install.
87
78
 
88
79
  ![alt text](image-2.png)
89
- - /clear - Create a new session
90
80
 
91
- - /resume - The recovery history has
81
+ ### File Selection & Commands
82
+
83
+ - Use `@` to select files. In VSCode, you can also hold `Shift` and drag files for the same effect
84
+ - Use `/` to view available commands:
85
+ - `/init` - Build project documentation `SNOW.md`
86
+ - `/clear` - Create a new session
87
+ - `/resume` - Restore conversation history
88
+ - `/mcp` - Check MCP connection status and reconnect
89
+ - `/yolo` - Unattended mode (all tool calls execute without confirmation - use with caution)
90
+ - `/ide` - Manually connect to IDE (usually automatic if plugin is installed)
91
+ - `/compact` - Compress context (rarely used as compression reduces AI quality)
92
+
93
+ ### Keyboard Shortcuts
94
+
95
+ - **Windows**: `Alt+V` - Paste image; **macOS/Linux**: `Ctrl+V` - Paste image (with prompt)
96
+ - `Ctrl+L` - Clear input from cursor position to the left
97
+ - `Ctrl+R` - Clear input from cursor position to the right
98
+ - `Shift+Tab` - Toggle Yolo mode on/off
99
+ - `ESC` - Stop AI generation
100
+ - **Double-click `ESC`** - Rollback conversation (with file checkpoints)
101
+
102
+ ### Token Usage
92
103
 
93
- - /mcp - Check the status of MCP service
104
+ The input area displays context usage percentage, token count, cache hit tokens, and cache creation tokens.
94
105
 
95
- - /yolo - Unattended mode, all tools automatically agree to execute
106
+ ![alt text](image-3.png)
96
107
 
97
- - /init - Initialize the project and generate the SNOW.md description document
108
+ ## Snow System Files
98
109
 
99
- - /ide - Connect to VSCode, you need to install the plug-in
110
+ All Snow files are stored in the `.snow` folder in your user directory. Here's what each file/folder contains:
100
111
 
101
- - /compact - compress the context into a sentence
112
+ ![alt text](image-4.png)
102
113
 
114
+ - **log** - Runtime logs (not uploaded anywhere, kept locally for debugging). Safe to delete
115
+ - **profiles** - Multiple configuration files for switching between different API/model setups
116
+ - **sessions** - All conversation history (required for `/resume` and other features, not uploaded)
117
+ - **snapshots** - File snapshots before AI edits (used for rollback). Automatic management, no manual intervention needed
118
+ - **todo** - Persisted todo lists from each conversation (prevents AI from forgetting tasks if app exits unexpectedly)
119
+ - **active-profile.txt** - Identifies the currently active profile (for backward compatibility with early versions)
120
+ - **config.json** - Main API configuration file
121
+ - **custom-headers.json** - Custom request headers
122
+ - **mcp-config.json** - MCP service configuration
123
+ - **system-prompt.txt** - Custom system prompt content