snow-ai 0.3.33 → 0.3.35

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.
@@ -19,9 +19,10 @@ export async function createEmbeddings(options) {
19
19
  if (!baseUrl) {
20
20
  throw new Error('Embedding base URL is required');
21
21
  }
22
- if (!apiKey) {
23
- throw new Error('Embedding API key is required');
24
- }
22
+ // API key is optional for local deployments (e.g., Ollama)
23
+ // if (!apiKey) {
24
+ // throw new Error('Embedding API key is required');
25
+ // }
25
26
  if (!input || input.length === 0) {
26
27
  throw new Error('Input texts are required');
27
28
  }
@@ -40,12 +41,16 @@ export async function createEmbeddings(options) {
40
41
  const url = baseUrl.endsWith('/embeddings')
41
42
  ? baseUrl
42
43
  : `${baseUrl.replace(/\/$/, '')}/embeddings`;
44
+ // Build headers - only include Authorization if API key is provided
45
+ const headers = {
46
+ 'Content-Type': 'application/json',
47
+ };
48
+ if (apiKey) {
49
+ headers['Authorization'] = `Bearer ${apiKey}`;
50
+ }
43
51
  const fetchOptions = addProxyToFetchOptions(url, {
44
52
  method: 'POST',
45
- headers: {
46
- 'Content-Type': 'application/json',
47
- Authorization: `Bearer ${apiKey}`,
48
- },
53
+ headers,
49
54
  body: JSON.stringify(requestBody),
50
55
  });
51
56
  const response = await fetch(url, fetchOptions);
@@ -66,23 +66,23 @@ Working Directory: ${workingDirectory}`;
66
66
  }
67
67
  const SYSTEM_PROMPT_TEMPLATE = `You are Snow AI CLI, an intelligent command-line assistant.
68
68
 
69
- ## 🎯 Core Principles
69
+ ## Core Principles
70
70
 
71
71
  1. **Language Adaptation**: ALWAYS respond in the SAME language as the user's query
72
72
  2. **ACTION FIRST**: Write code immediately when task is clear - stop overthinking
73
73
  3. **Smart Context**: Read what's needed for correctness, skip excessive exploration
74
74
  4. **Quality Verification**: run build/test after changes
75
- 5. **NO Documentation Files**: NEVER create summary .md files after tasks - use \`notebook-add\` for important notes instead
75
+ 5. **NO Documentation Files**: NEVER create summary .md files after tasks - use \`notebook-add\` for important notes instead
76
76
 
77
- ## 🚀 Execution Strategy - BALANCE ACTION & ANALYSIS
77
+ ## Execution Strategy - BALANCE ACTION & ANALYSIS
78
78
 
79
- ## 🤖 Rigorous coding habits
79
+ ## Rigorous coding habits
80
80
  - In any programming language or business logic, which is usually accompanied by many-to-many references to files, you also need to think about the impact of the modification and whether it will conflict with the user's original business.
81
81
  - Using the optimal solution principle, you cannot choose risk scenarios such as hardcoding, logic simplification, etc., unless the user asks you to do so.
82
82
  - Avoid duplication, users may have encapsulated some reusable functions, and you should try to find them instead of creating a new function right away.
83
83
  - Compilable principle, you should not have low-level errors such as syntax errors, use tools to check for syntax errors, non-compilable code is meaningless.
84
84
 
85
- ### Smart Action Mode
85
+ ### Smart Action Mode
86
86
  **Principle: Understand enough to code correctly, but don't over-investigate**
87
87
 
88
88
  **Examples:**
@@ -94,28 +94,28 @@ PLACEHOLDER_FOR_WORKFLOW_SECTION
94
94
 
95
95
  **Golden Rule: Read what you need to write correct code, nothing more.**
96
96
 
97
- ### 📋 TODO Management - STRONGLY RECOMMENDED for Better Results!
97
+ ### TODO Management - STRONGLY RECOMMENDED for Better Results
98
98
 
99
- **🎯 DEFAULT BEHAVIOR: Use TODO for ALL multi-step tasks (3+ steps)**
99
+ **DEFAULT BEHAVIOR: Use TODO for ALL multi-step tasks (3+ steps)**
100
100
 
101
- **✨ WHY TODO IS ESSENTIAL:**
102
- - 📊 **Track progress** - Never lose your place in complex work
103
- - **Ensure completeness** - Verify all steps are done
104
- - 🎯 **Stay focused** - Clear roadmap prevents confusion
105
- - 💪 **Build confidence** - Users see structured progress
106
- - 🚀 **Better quality** - Systematic approach reduces errors
101
+ **WHY TODO IS ESSENTIAL:**
102
+ - **Track progress** - Never lose your place in complex work
103
+ - **Ensure completeness** - Verify all steps are done
104
+ - **Stay focused** - Clear roadmap prevents confusion
105
+ - **Build confidence** - Users see structured progress
106
+ - **Better quality** - Systematic approach reduces errors
107
107
 
108
- **⚡ WHEN TO USE TODO (Default for most tasks):**
109
- - **ANY multi-file modification** (always use)
110
- - **ANY feature implementation** (always use)
111
- - **ANY refactoring task** (always use)
112
- - **Bug fixes touching 2+ files** (recommended)
113
- - **User requests with multiple requirements** (always use)
114
- - **Unfamiliar codebase changes** (recommended)
115
- - ⚠️ **SKIP ONLY for**: Single-file trivial edits (1-2 lines)
108
+ **WHEN TO USE TODO (Default for most tasks):**
109
+ - **ANY multi-file modification** (always use)
110
+ - **ANY feature implementation** (always use)
111
+ - **ANY refactoring task** (always use)
112
+ - **Bug fixes touching 2+ files** (recommended)
113
+ - **User requests with multiple requirements** (always use)
114
+ - **Unfamiliar codebase changes** (recommended)
115
+ - **SKIP ONLY for**: Single-file trivial edits (1-2 lines)
116
116
 
117
- **🔧 USAGE RULES (Critical):**
118
- 1. **⚠️ PARALLEL CALLS ONLY**: ALWAYS call TODO tools with action tools in the SAME function call block
117
+ **USAGE RULES (Critical):**
118
+ 1. **PARALLEL CALLS ONLY**: ALWAYS call TODO tools with action tools in the SAME function call block
119
119
  2. **Immediate updates**: Mark completed while performing work (not after)
120
120
  3. **Right sizing**: 3-7 main tasks, add subtasks if needed
121
121
  4. **Lifecycle Management**:
@@ -124,22 +124,22 @@ PLACEHOLDER_FOR_WORKFLOW_SECTION
124
124
  - Minor adjustment = Use todo-add or todo-update
125
125
  - **CRITICAL**: Keep using TODO throughout the entire conversation!
126
126
 
127
- **✅ CORRECT PATTERNS (Do this):**
128
- - todo-create + filesystem-read → Plan while gathering info
129
- - todo-update(completed) + filesystem-edit → Update as you work
130
- - todo-get + filesystem-read → Check status while reading
131
- - todo-add + filesystem-edit → Add new task while working
127
+ **CORRECT PATTERNS (Do this):**
128
+ - todo-create + filesystem-read → Plan while gathering info
129
+ - todo-update(completed) + filesystem-edit → Update as you work
130
+ - todo-get + filesystem-read → Check status while reading
131
+ - todo-add + filesystem-edit → Add new task while working
132
132
 
133
- **❌ FORBIDDEN PATTERNS (NEVER do this - WILL FAIL):**
134
- - todo-create alone, wait for result, then work → VIOLATION! Call together!
135
- - todo-update alone, wait, then continue → VIOLATION! Update while working!
136
- - todo-get alone just to check → VIOLATION! Call with other tools!
137
- - Skipping TODO for multi-file tasks → VIOLATION! Always use TODO!
138
- - **Abandoning TODO mid-conversation** → VIOLATION! Keep using throughout dialogue!
133
+ **FORBIDDEN PATTERNS (NEVER do this - WILL FAIL):**
134
+ - todo-create alone, wait for result, then work → VIOLATION! Call together!
135
+ - todo-update alone, wait, then continue → VIOLATION! Update while working!
136
+ - todo-get alone just to check → VIOLATION! Call with other tools!
137
+ - Skipping TODO for multi-file tasks → VIOLATION! Always use TODO!
138
+ - **Abandoning TODO mid-conversation** → VIOLATION! Keep using throughout dialogue!
139
139
 
140
- **💡 BEST PRACTICE: Start every non-trivial task with todo-create + initial action in parallel!**
140
+ **BEST PRACTICE: Start every non-trivial task with todo-create + initial action in parallel!**
141
141
 
142
- ## 🛠️ Available Tools
142
+ ## Available Tools
143
143
 
144
144
  **Filesystem:**
145
145
  - \`filesystem-read\` - Read files before editing
@@ -158,25 +158,25 @@ PLACEHOLDER_FOR_CODE_SEARCH_SECTION
158
158
 
159
159
  **Notebook (Code Memory):**
160
160
  - \`notebook-add\` - Record fragile code that new features might break during iteration
161
- - 🎯 Core purpose: Prevent new functionality from breaking old functionality
162
- - 📝 Record: Bugs that recurred, fragile dependencies, critical constraints
163
- - ⚠️ Examples: "validateInput() must run first - broke twice", "null return required by X"
164
- - 📌 **IMPORTANT**: Use notebook for documentation, NOT separate .md files
161
+ - Core purpose: Prevent new functionality from breaking old functionality
162
+ - Record: Bugs that recurred, fragile dependencies, critical constraints
163
+ - Examples: "validateInput() must run first - broke twice", "null return required by X"
164
+ - **IMPORTANT**: Use notebook for documentation, NOT separate .md files
165
165
  - \`notebook-query\` - Manual search (rarely needed, auto-shown when reading files)
166
- - 🔍 Auto-attached: Last 10 notebooks appear when reading ANY file
167
- - 💡 Use before: Adding features that might affect existing behavior
166
+ - Auto-attached: Last 10 notebooks appear when reading ANY file
167
+ - Use before: Adding features that might affect existing behavior
168
168
  - \`notebook-update\` - Update existing note to fix mistakes or refine information
169
- - ✏️ Fix errors in previously recorded notes
170
- - 📝 Clarify or improve wording after better understanding
171
- - 🔄 Update note when code changes but constraint still applies
169
+ - Fix errors in previously recorded notes
170
+ - Clarify or improve wording after better understanding
171
+ - Update note when code changes but constraint still applies
172
172
  - \`notebook-delete\` - Remove outdated or incorrect notes
173
- - 🗑️ Delete when code is refactored and note is obsolete
174
- - Remove notes recorded by mistake
175
- - 🧹 Clean up after workarounds are properly fixed
173
+ - Delete when code is refactored and note is obsolete
174
+ - Remove notes recorded by mistake
175
+ - Clean up after workarounds are properly fixed
176
176
  - \`notebook-list\` - View all notes for a specific file
177
- - 📋 List all constraints for a file before making changes
178
- - 🔍 Find note IDs for update/delete operations
179
- - 🧐 Review all warnings before refactoring
177
+ - List all constraints for a file before making changes
178
+ - Find note IDs for update/delete operations
179
+ - Review all warnings before refactoring
180
180
 
181
181
  **Web Search:**
182
182
  - \`websearch-search\` - Search web for latest docs/solutions
@@ -191,111 +191,111 @@ system administration and data processing challenges.
191
191
 
192
192
  **Sub-Agent:**
193
193
 
194
- ### 🎯 CRITICAL: AGGRESSIVE DELEGATION TO SUB-AGENTS
194
+ ### CRITICAL: AGGRESSIVE DELEGATION TO SUB-AGENTS
195
195
 
196
- **⚡ Core Principle: MAXIMIZE context saving by delegating as much work as possible to sub-agents!**
196
+ **Core Principle: MAXIMIZE context saving by delegating as much work as possible to sub-agents!**
197
197
 
198
- **🔥 WHY DELEGATE AGGRESSIVELY:**
199
- - 💾 **Save Main Context** - Each delegated task saves thousands of tokens in the main session
200
- - 🚀 **Parallel Processing** - Sub-agents work independently without cluttering main context
201
- - 🎯 **Focused Sessions** - Sub-agents have dedicated context for specific tasks
202
- - 🔄 **Scalability** - Main agent stays lean and efficient even for complex projects
198
+ **WHY DELEGATE AGGRESSIVELY:**
199
+ - **Save Main Context** - Each delegated task saves thousands of tokens in the main session
200
+ - **Parallel Processing** - Sub-agents work independently without cluttering main context
201
+ - **Focused Sessions** - Sub-agents have dedicated context for specific tasks
202
+ - **Scalability** - Main agent stays lean and efficient even for complex projects
203
203
 
204
- **📋 DELEGATION STRATEGY - DEFAULT TO SUB-AGENT:**
204
+ **DELEGATION STRATEGY - DEFAULT TO SUB-AGENT:**
205
205
 
206
- **✅ ALWAYS DELEGATE (High Priority):**
207
- - 🔍 **Code Analysis & Planning** - File structure analysis, architecture review, impact analysis
208
- - 📊 **Research Tasks** - Investigating patterns, finding similar code, exploring codebase
209
- - 🗺️ **Work Planning** - Breaking down requirements, creating task plans, designing solutions
210
- - 📝 **Documentation Review** - Reading and summarizing large files, extracting key information
211
- - 🔎 **Dependency Mapping** - Finding all imports, exports, references across files
212
- - 🧪 **Test Planning** - Analyzing what needs testing, planning test cases
213
- - 🔧 **Refactoring Analysis** - Identifying refactoring opportunities, impact assessment
206
+ **ALWAYS DELEGATE (High Priority):**
207
+ - **Code Analysis & Planning** - File structure analysis, architecture review, impact analysis
208
+ - **Research Tasks** - Investigating patterns, finding similar code, exploring codebase
209
+ - **Work Planning** - Breaking down requirements, creating task plans, designing solutions
210
+ - **Documentation Review** - Reading and summarizing large files, extracting key information
211
+ - **Dependency Mapping** - Finding all imports, exports, references across files
212
+ - **Test Planning** - Analyzing what needs testing, planning test cases
213
+ - **Refactoring Analysis** - Identifying refactoring opportunities, impact assessment
214
214
 
215
- **✅ STRONGLY CONSIDER DELEGATING:**
216
- - 🐛 **Bug Investigation** - Root cause analysis, reproduction steps, related code search
217
- - 🔄 **Migration Planning** - Planning API changes, version upgrades, dependency updates
218
- - 📐 **Design Reviews** - Evaluating architectural decisions, pattern consistency
219
- - 🔍 **Code Quality Checks** - Finding code smells, inconsistencies, potential issues
215
+ **STRONGLY CONSIDER DELEGATING:**
216
+ - **Bug Investigation** - Root cause analysis, reproduction steps, related code search
217
+ - **Migration Planning** - Planning API changes, version upgrades, dependency updates
218
+ - **Design Reviews** - Evaluating architectural decisions, pattern consistency
219
+ - **Code Quality Checks** - Finding code smells, inconsistencies, potential issues
220
220
 
221
- **⚠️ KEEP IN MAIN AGENT (Low Volume):**
222
- - ✏️ **Direct Code Edits** - Simple, well-understood modifications
223
- - 🔨 **Quick Fixes** - Single-file changes with clear context
224
- - **Immediate Actions** - Terminal commands, file operations
221
+ **KEEP IN MAIN AGENT (Low Volume):**
222
+ - **Direct Code Edits** - Simple, well-understood modifications
223
+ - **Quick Fixes** - Single-file changes with clear context
224
+ - **Immediate Actions** - Terminal commands, file operations
225
225
 
226
- **🎯 DELEGATION WORKFLOW:**
226
+ **DELEGATION WORKFLOW:**
227
227
 
228
228
  1. **Receive User Request** → Immediately consider: "Can a sub-agent handle the analysis/planning?"
229
229
  2. **Complex Task** → Delegate research/planning to sub-agent, wait for result, then execute
230
230
  3. **Multi-Step Task** → Delegate planning to sub-agent, receive roadmap, execute in main
231
231
  4. **Unfamiliar Code** → Delegate exploration to sub-agent, get summary, then modify
232
232
 
233
- **💡 PRACTICAL EXAMPLES:**
233
+ **PRACTICAL EXAMPLES:**
234
234
 
235
- **BAD - Doing everything in main agent:**
235
+ **BAD - Doing everything in main agent:**
236
236
  - User: "Add user authentication"
237
237
  - Main: *reads 20 files, analyzes auth patterns, plans implementation, writes code*
238
238
  - Result: Main context bloated with analysis that won't be reused
239
239
 
240
- **GOOD - Aggressive delegation:**
240
+ **GOOD - Aggressive delegation:**
241
241
  - User: "Add user authentication"
242
242
  - Main: Delegate to sub-agent → "Analyze current auth patterns and create implementation plan"
243
243
  - Sub-agent: *analyzes, returns concise plan*
244
244
  - Main: Execute plan with focused context
245
245
  - Result: Main context stays lean, only contains execution context
246
246
 
247
- **🔧 USAGE RULES:**
247
+ **USAGE RULES:**
248
248
 
249
249
  1. **When tool available**: Check if you have \`subagent-agent_*\` tools in your toolkit
250
250
  2. **Explicit user request**: User message contains \`#agent_*\` → MUST use that specific sub-agent
251
251
  3. **Implicit delegation**: Even without \`#agent_*\`, proactively delegate analysis/planning tasks
252
252
  4. **Return focus**: After sub-agent responds, main agent focuses purely on execution
253
253
 
254
- **📌 REMEMBER: If it's not direct code editing or immediate action, consider delegating to sub-agent first!**
254
+ **REMEMBER: If it's not direct code editing or immediate action, consider delegating to sub-agent first!**
255
255
 
256
- **🌲 DECISION TREE - When to Delegate to Sub-Agent:**
256
+ **DECISION TREE - When to Delegate to Sub-Agent:**
257
257
 
258
258
  \`\`\`
259
- 📥 User Request
259
+ User Request
260
260
 
261
- Can a sub-agent handle this task?
262
- ├─ YES → 🚀 DELEGATE to sub-agent
263
- ├─ Code search/exploration
264
- ├─ Analysis & planning
265
- ├─ Research & investigation
266
- ├─ Architecture review
267
- ├─ Impact assessment
268
- ├─ Dependency mapping
269
- ├─ Documentation review
270
- ├─ Test planning
271
- ├─ Bug investigation
272
- ├─ Pattern finding
273
- └─ ANY task sub-agent can do
261
+ Can a sub-agent handle this task?
262
+ ├─ YES → DELEGATE to sub-agent
263
+ ├─ Code search/exploration
264
+ ├─ Analysis & planning
265
+ ├─ Research & investigation
266
+ ├─ Architecture review
267
+ ├─ Impact assessment
268
+ ├─ Dependency mapping
269
+ ├─ Documentation review
270
+ ├─ Test planning
271
+ ├─ Bug investigation
272
+ ├─ Pattern finding
273
+ └─ ANY task sub-agent can do
274
274
 
275
- └─ NO → Execute directly in main agent
276
- ├─ Direct code editing (clear target)
277
- ├─ File operations (create/delete)
278
- ├─ Simple terminal commands
279
- └─ Immediate actions (no research needed)
275
+ └─ NO → Execute directly in main agent
276
+ ├─ Direct code editing (clear target)
277
+ ├─ File operations (create/delete)
278
+ ├─ Simple terminal commands
279
+ └─ Immediate actions (no research needed)
280
280
  \`\`\`
281
281
 
282
- **🎯 Golden Rule:**
282
+ **Golden Rule:**
283
283
  **"If sub-agent CAN do it → sub-agent SHOULD do it"**
284
284
 
285
285
  **Decision in 3 seconds:**
286
- 1. Does this need research/exploration/planning? → **Delegate**
287
- 2. Is this a straightforward code edit? → **Execute directly**
288
- 3. ⚠️ **When in doubt** → **Delegate to sub-agent** (safer default)
286
+ 1. Does this need research/exploration/planning? → **Delegate**
287
+ 2. Is this a straightforward code edit? → **Execute directly**
288
+ 3. **When in doubt** → **Delegate to sub-agent** (safer default)
289
289
 
290
290
 
291
- ## 🔍 Quality Assurance
291
+ ## Quality Assurance
292
292
 
293
293
  Guidance and recommendations:
294
294
  1. Run build: \`npm run build\` or \`tsc\`
295
295
  2. Fix any errors immediately
296
296
  3. Never leave broken code
297
297
 
298
- ## 📚 Project Context (SNOW.md)
298
+ ## Project Context (SNOW.md)
299
299
 
300
300
  - Contains: project overview, architecture, tech stack.
301
301
  - Generally located in the project root directory.
@@ -317,16 +317,20 @@ function hasCodebaseSearchTool(tools) {
317
317
  function getWorkflowSection(hasCodebase) {
318
318
  if (hasCodebase) {
319
319
  return `**Your workflow:**
320
- 1. **Understand the task** - For conceptual questions, try \\\`codebase-search\\\` FIRST (semantic search)
320
+ 1. **START WITH SEMANTIC SEARCH** - Use \\\`codebase-search\\\` as your PRIMARY exploration tool
321
+ - ALWAYS try \\\`codebase-search\\\` FIRST for ANY code understanding task
322
+ - Examples: "authentication logic", "error handling", "user validation", "database queries"
323
+ - Dramatically faster than reading multiple files manually
324
+ - Returns relevant code snippets with context - read results to understand the codebase
321
325
  2. Read the primary file(s) mentioned (or files found by codebase search)
322
326
  3. Check dependencies/imports that directly impact the change
323
- 4. For precise symbol lookup, use \\\`ace-search-symbols\\\`, \\\`ace-find-definition\\\`, or \\\`ace-find-references\\\`
327
+ 4. For precise symbol lookup AFTER understanding context, use \\\`ace-search-symbols\\\`, \\\`ace-find-definition\\\`, or \\\`ace-find-references\\\`
324
328
  5. Read related files ONLY if they're critical to understanding the task
325
329
  6. Write/modify code with proper context
326
330
  7. Verify with build
327
- 8. NO excessive exploration beyond what's needed
328
- 9. NO reading entire modules "for reference"
329
- 10. NO over-planning multi-step workflows for simple tasks`;
331
+ 8. NO excessive exploration beyond what's needed
332
+ 9. NO reading entire modules "for reference"
333
+ 10. NO over-planning multi-step workflows for simple tasks`;
330
334
  }
331
335
  else {
332
336
  return `**Your workflow:**
@@ -336,9 +340,9 @@ function getWorkflowSection(hasCodebase) {
336
340
  4. Read related files ONLY if they're critical to understanding the task
337
341
  5. Write/modify code with proper context
338
342
  6. Verify with build
339
- 7. NO excessive exploration beyond what's needed
340
- 8. NO reading entire modules "for reference"
341
- 9. NO over-planning multi-step workflows for simple tasks`;
343
+ 7. NO excessive exploration beyond what's needed
344
+ 8. NO reading entire modules "for reference"
345
+ 9. NO over-planning multi-step workflows for simple tasks`;
342
346
  }
343
347
  }
344
348
  /**
@@ -349,23 +353,24 @@ function getCodeSearchSection(hasCodebase) {
349
353
  // When codebase tool is available, prioritize it
350
354
  return `**Code Search:**
351
355
 
352
- 🎯 **Priority Order (use in this sequence):**
356
+ **Priority Order (use in this sequence):**
353
357
 
354
- 1. **Codebase Semantic Search** ( HIGHEST PRIORITY):
358
+ 1. **Codebase Semantic Search** (ALWAYS TRY THIS FIRST!):
355
359
  - \\\`codebase-search\\\` - Semantic search using embeddings
356
- - 🔍 Find code by MEANING, not just keywords
357
- - 🎯 Best for: "how is authentication handled", "error handling patterns"
358
- - 📊 Returns: Full code content + similarity scores + file locations
359
- - 💡 **IMPORTANT**: Always try this FIRST for conceptual queries!
360
- - 🚀 **When to use**: Understanding concepts, finding similar code, pattern discovery
361
- - **When to skip**: Exact symbol names, file-specific searches (use ACE instead)
362
-
363
- 2. **ACE Code Search** (Fallback for precise lookups):
360
+ - Find code by MEANING, not just keywords
361
+ - Best for: "how is authentication handled", "error handling patterns", "validation logic"
362
+ - Returns: Full code content + similarity scores + file locations
363
+ - **CRITICAL**: Use this as your PRIMARY tool for understanding codebase
364
+ - **When to use**: ANY code understanding task, finding implementations, pattern discovery
365
+ - **Example queries**: "user authentication", "database connection", "API error handling"
366
+ - **When to skip**: ONLY skip if you need exact symbol names or regex patterns
367
+
368
+ 2. **ACE Code Search** (Use AFTER semantic search for precise lookups):
364
369
  - \\\`ace-search-symbols\\\` - Find functions/classes/variables by exact name
365
370
  - \\\`ace-find-definition\\\` - Go to definition of a symbol
366
371
  - \\\`ace-find-references\\\` - Find all usages of a symbol
367
372
  - \\\`ace-text-search\\\` - Fast text/regex search across files
368
- - 💡 **When to use**: Exact symbol lookup, reference finding, regex patterns`;
373
+ - **When to use**: Exact symbol lookup, reference finding, regex patterns`;
369
374
  }
370
375
  else {
371
376
  // When codebase tool is NOT available, only show ACE
@@ -390,7 +395,7 @@ export function getSystemPrompt(tools) {
390
395
  .replace('PLACEHOLDER_FOR_CODE_SEARCH_SECTION', codeSearchSection);
391
396
  return `${finalPrompt}
392
397
 
393
- ## 💻 System Environment
398
+ ## System Environment
394
399
 
395
400
  ${systemEnv}`;
396
401
  }
@@ -8,13 +8,42 @@ export function useClipboard(buffer, updateCommandPanelState, updateFilePickerSt
8
8
  if (process.platform === 'win32') {
9
9
  // Windows: Use PowerShell to read image from clipboard
10
10
  try {
11
- const psScript = `Add-Type -AssemblyName System.Windows.Forms; Add-Type -AssemblyName System.Drawing; $clipboard = [System.Windows.Forms.Clipboard]::GetImage(); if ($clipboard -ne $null) { $ms = New-Object System.IO.MemoryStream; $clipboard.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); $bytes = $ms.ToArray(); $ms.Close(); [Convert]::ToBase64String($bytes) }`;
12
- const base64Raw = execSync(`powershell -Command "${psScript}"`, {
11
+ // Optimized PowerShell script with compression for large images
12
+ const psScript = 'Add-Type -AssemblyName System.Windows.Forms; ' +
13
+ 'Add-Type -AssemblyName System.Drawing; ' +
14
+ '$clipboard = [System.Windows.Forms.Clipboard]::GetImage(); ' +
15
+ 'if ($clipboard -ne $null) { ' +
16
+ '$ms = New-Object System.IO.MemoryStream; ' +
17
+ '$width = $clipboard.Width; ' +
18
+ '$height = $clipboard.Height; ' +
19
+ '$maxSize = 2048; ' +
20
+ 'if ($width -gt $maxSize -or $height -gt $maxSize) { ' +
21
+ '$ratio = [Math]::Min($maxSize / $width, $maxSize / $height); ' +
22
+ '$newWidth = [int]($width * $ratio); ' +
23
+ '$newHeight = [int]($height * $ratio); ' +
24
+ '$resized = New-Object System.Drawing.Bitmap($newWidth, $newHeight); ' +
25
+ '$graphics = [System.Drawing.Graphics]::FromImage($resized); ' +
26
+ '$graphics.CompositingQuality = [System.Drawing.Drawing2D.CompositingQuality]::HighQuality; ' +
27
+ '$graphics.InterpolationMode = [System.Drawing.Drawing2D.InterpolationMode]::HighQualityBicubic; ' +
28
+ '$graphics.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality; ' +
29
+ '$graphics.DrawImage($clipboard, 0, 0, $newWidth, $newHeight); ' +
30
+ '$resized.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); ' +
31
+ '$graphics.Dispose(); ' +
32
+ '$resized.Dispose(); ' +
33
+ '} else { ' +
34
+ '$clipboard.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); ' +
35
+ '}; ' +
36
+ '$bytes = $ms.ToArray(); ' +
37
+ '$ms.Close(); ' +
38
+ '[Convert]::ToBase64String($bytes); ' +
39
+ '}';
40
+ const base64Raw = execSync(`powershell -NoProfile -Command "${psScript}"`, {
13
41
  encoding: 'utf-8',
14
- timeout: 5000,
42
+ timeout: 10000,
43
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer
15
44
  });
16
- // 清理所有空白字符(包括换行符)
17
- const base64 = base64Raw.replace(/\s+/g, '');
45
+ // 高效清理:一次性移除所有空白字符
46
+ const base64 = base64Raw.replace(/\s/g, '');
18
47
  if (base64 && base64.length > 100) {
19
48
  // 直接传入 base64 数据,不需要 data URL 前缀
20
49
  buffer.insertImage(base64, 'image/png');
@@ -28,6 +57,7 @@ export function useClipboard(buffer, updateCommandPanelState, updateFilePickerSt
28
57
  }
29
58
  catch (imgError) {
30
59
  // No image in clipboard or error, fall through to text
60
+ logger.error('Failed to read image from Windows clipboard:', imgError);
31
61
  }
32
62
  }
33
63
  else if (process.platform === 'darwin') {
@@ -52,13 +82,34 @@ end try'`;
52
82
  encoding: 'utf-8',
53
83
  timeout: 3000,
54
84
  });
55
- // Read the file as base64
56
- const base64Raw = execSync(`base64 -i "${tmpFile}"`, {
85
+ // Use sips to resize if needed, then convert to base64
86
+ // First check image size
87
+ const sizeCheck = execSync(`sips -g pixelWidth -g pixelHeight "${tmpFile}" | grep -E "pixelWidth|pixelHeight" | awk '{print $2}'`, {
57
88
  encoding: 'utf-8',
58
89
  timeout: 2000,
59
90
  });
60
- // 清理所有空白字符(包括换行符)
61
- const base64 = base64Raw.replace(/\s+/g, '');
91
+ const [widthStr, heightStr] = sizeCheck.trim().split('\n');
92
+ const width = parseInt(widthStr || '0', 10);
93
+ const height = parseInt(heightStr || '0', 10);
94
+ const maxSize = 2048;
95
+ // Resize if too large
96
+ if (width > maxSize || height > maxSize) {
97
+ const ratio = Math.min(maxSize / width, maxSize / height);
98
+ const newWidth = Math.floor(width * ratio);
99
+ const newHeight = Math.floor(height * ratio);
100
+ execSync(`sips -z ${newHeight} ${newWidth} "${tmpFile}" --out "${tmpFile}"`, {
101
+ encoding: 'utf-8',
102
+ timeout: 5000,
103
+ });
104
+ }
105
+ // Read the file as base64 with optimized buffer
106
+ const base64Raw = execSync(`base64 -i "${tmpFile}"`, {
107
+ encoding: 'utf-8',
108
+ timeout: 5000,
109
+ maxBuffer: 50 * 1024 * 1024, // 50MB buffer
110
+ });
111
+ // 高效清理:一次性移除所有空白字符
112
+ const base64 = base64Raw.replace(/\s/g, '');
62
113
  // Clean up temp file
63
114
  try {
64
115
  execSync(`rm "${tmpFile}"`, { timeout: 1000 });
@@ -9,6 +9,7 @@ export function useKeyboardInput(options) {
9
9
  // Track paste detection
10
10
  const inputBuffer = useRef('');
11
11
  const inputTimer = useRef(null);
12
+ const isPasting = useRef(false); // Track if we're in pasting mode
12
13
  // Cleanup timer on unmount
13
14
  useEffect(() => {
14
15
  return () => {
@@ -452,11 +453,29 @@ export function useKeyboardInput(options) {
452
453
  if (inputTimer.current) {
453
454
  clearTimeout(inputTimer.current);
454
455
  }
456
+ // Detect large paste: if accumulated buffer is getting large, extend timeout
457
+ // This prevents splitting large pastes into multiple insert() calls
458
+ const currentLength = inputBuffer.current.length;
459
+ const timeoutDelay = currentLength > 200 ? 150 : 10;
460
+ // Show pasting indicator for large text (>300 chars)
461
+ // Simple static message - no progress animation
462
+ if (currentLength > 300 && !isPasting.current) {
463
+ isPasting.current = true;
464
+ buffer.insertPastingIndicator();
465
+ // Trigger UI update to show the indicator
466
+ const text = buffer.getFullText();
467
+ const cursorPos = buffer.getCursorPosition();
468
+ updateCommandPanelState(text);
469
+ updateFilePickerState(text, cursorPos);
470
+ triggerUpdate();
471
+ }
455
472
  // Set timer to process accumulated input
456
473
  inputTimer.current = setTimeout(() => {
457
474
  const accumulated = inputBuffer.current;
458
475
  inputBuffer.current = '';
459
- // If we accumulated input, it's likely a paste
476
+ isPasting.current = false; // Reset pasting state
477
+ // If we accumulated input, insert it as a single operation
478
+ // The insert() method will automatically remove the pasting indicator
460
479
  if (accumulated) {
461
480
  buffer.insert(accumulated);
462
481
  const text = buffer.getFullText();
@@ -465,7 +484,7 @@ export function useKeyboardInput(options) {
465
484
  updateFilePickerState(text, cursorPos);
466
485
  triggerUpdate();
467
486
  }
468
- }, 10); // Short delay to accumulate rapid input
487
+ }, timeoutDelay); // Extended delay for large pastes to ensure complete accumulation
469
488
  }
470
489
  });
471
490
  }
@@ -1113,7 +1113,7 @@ export const filesystemService = new FilesystemMCPService();
1113
1113
  export const mcpTools = [
1114
1114
  {
1115
1115
  name: 'filesystem-read',
1116
- description: '📖 Read file content with line numbers. **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=50 reads lines 1-50 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:30}, {path:"b.ts", startLine:100, endLine:150}] reads different ranges from each file.',
1116
+ description: 'Read file content with line numbers. **SUPPORTS MULTIPLE FILES WITH FLEXIBLE LINE RANGES**: Pass either (1) a single file path (string), (2) array of file paths (strings) with unified startLine/endLine, or (3) array of file config objects with per-file line ranges. ⚠️ **IMPORTANT WORKFLOW**: (1) ALWAYS use ACE search tools FIRST (ace-text_search/ace-search_symbols/ace-file_outline) to locate the relevant code, (2) ONLY use filesystem-read when you know the approximate location and need precise line numbers for editing. **ANTI-PATTERN**: Reading files line-by-line from the top wastes tokens - use search instead! **USAGE**: Call without parameters to read entire file(s), or specify startLine/endLine for partial reads. Returns content with line numbers (format: "123→code") for precise editing. **EXAMPLES**: (A) Unified: filePath=["a.ts", "b.ts"], startLine=1, endLine=50 reads lines 1-50 from both. (B) Per-file: filePath=[{path:"a.ts", startLine:1, endLine:30}, {path:"b.ts", startLine:100, endLine:150}] reads different ranges from each file.',
1117
1117
  inputSchema: {
1118
1118
  type: 'object',
1119
1119
  properties: {
@@ -1236,7 +1236,7 @@ export const mcpTools = [
1236
1236
  },
1237
1237
  {
1238
1238
  name: 'filesystem-edit_search',
1239
- description: '🎯 **RECOMMENDED** for most edits: Search-and-replace with SMART FUZZY MATCHING. **SUPPORTS BATCH EDITING**: Pass (1) single file with search/replace, (2) array of file paths with unified search/replace, or (3) array of {path, searchContent, replaceContent, occurrence?} for per-file edits. **WORKFLOW**: (1) Use ace-text_search/ace-search_symbols to locate code, (2) Use filesystem-read to view content, (3) Copy code blocks (without line numbers), (4) Use THIS tool. **WHY**: No line tracking, auto-handles spacing/tabs, finds best match. **BATCH EXAMPLE**: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}]',
1239
+ description: '**RECOMMENDED** for most edits: Search-and-replace with SMART FUZZY MATCHING. **SUPPORTS BATCH EDITING**: Pass (1) single file with search/replace, (2) array of file paths with unified search/replace, or (3) array of {path, searchContent, replaceContent, occurrence?} for per-file edits. **WORKFLOW**: (1) Use ace-text_search/ace-search_symbols to locate code, (2) Use filesystem-read to view content, (3) Copy code blocks (without line numbers), (4) Use THIS tool. **WHY**: No line tracking, auto-handles spacing/tabs, finds best match. **BATCH EXAMPLE**: filePath=[{path:"a.ts", searchContent:"old1", replaceContent:"new1"}, {path:"b.ts", searchContent:"old2", replaceContent:"new2"}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
1240
1240
  inputSchema: {
1241
1241
  type: 'object',
1242
1242
  properties: {
@@ -1306,7 +1306,7 @@ export const mcpTools = [
1306
1306
  },
1307
1307
  {
1308
1308
  name: 'filesystem-edit',
1309
- description: '🔧 Line-based editing for precise control. **SUPPORTS BATCH EDITING**: Pass (1) single file with line range, (2) array of file paths with unified line range, or (3) array of {path, startLine, endLine, newContent} for per-file edits. **WHEN TO USE**: (1) Adding new code sections, (2) Deleting specific line ranges, (3) When search-replace not suitable. **WORKFLOW**: (1) Use ace-text_search/ace-file_outline to locate area, (2) Use filesystem-read to get line numbers, (3) Use THIS tool. **RECOMMENDATION**: For modifying existing code, use filesystem-edit_search - safer. **BATCH EXAMPLE**: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}]',
1309
+ description: 'Line-based editing for precise control. **SUPPORTS BATCH EDITING**: Pass (1) single file with line range, (2) array of file paths with unified line range, or (3) array of {path, startLine, endLine, newContent} for per-file edits. **WHEN TO USE**: (1) Adding new code sections, (2) Deleting specific line ranges, (3) When search-replace not suitable. **WORKFLOW**: (1) Use ace-text_search/ace-file_outline to locate area, (2) Use filesystem-read to get line numbers, (3) Use THIS tool. **RECOMMENDATION**: For modifying existing code, use filesystem-edit_search - safer. **BATCH EXAMPLE**: filePath=[{path:"a.ts", startLine:10, endLine:20, newContent:"..."}, {path:"b.ts", startLine:50, endLine:60, newContent:"..."}] **It is very important to use the filesystem-read tool to determine the code boundaries of the area that need to be modified first to avoid syntactic errors**',
1310
1310
  inputSchema: {
1311
1311
  type: 'object',
1312
1312
  properties: {
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useRef, useMemo } from 'react';
2
2
  import { Box, Text } from 'ink';
3
- import { cpSlice } from '../../utils/textUtils.js';
3
+ import { cpSlice, cpLen } from '../../utils/textUtils.js';
4
4
  import CommandPanel from './CommandPanel.js';
5
5
  import FileList from './FileList.js';
6
6
  import AgentPickerPanel from './AgentPickerPanel.js';
@@ -218,10 +218,31 @@ export default function ChatInput({ onSubmit, onCommand, placeholder = 'Type you
218
218
  const cursorPos = buffer.getCursorPosition();
219
219
  const charInfo = buffer.getCharAtCursor();
220
220
  const atCursor = charInfo.char === '\n' ? ' ' : charInfo.char;
221
- return (React.createElement(Text, null,
222
- cpSlice(displayText, 0, cursorPos),
223
- renderCursor(atCursor),
224
- cpSlice(displayText, cursorPos + 1)));
221
+ // Split text into lines for proper multi-line rendering
222
+ const lines = displayText.split('\n');
223
+ const renderedLines = [];
224
+ let currentPos = 0;
225
+ for (let i = 0; i < lines.length; i++) {
226
+ const line = lines[i] || '';
227
+ const lineStart = currentPos;
228
+ const lineEnd = lineStart + cpLen(line);
229
+ // Check if cursor is in this line
230
+ if (cursorPos >= lineStart && cursorPos <= lineEnd) {
231
+ const beforeCursor = cpSlice(line, 0, cursorPos - lineStart);
232
+ const afterCursor = cpSlice(line, cursorPos - lineStart + 1);
233
+ renderedLines.push(React.createElement(Text, { key: i },
234
+ beforeCursor,
235
+ renderCursor(atCursor),
236
+ afterCursor));
237
+ }
238
+ else {
239
+ // No cursor in this line
240
+ renderedLines.push(React.createElement(Text, { key: i }, line || ' '));
241
+ }
242
+ // Account for newline character
243
+ currentPos = lineEnd + 1;
244
+ }
245
+ return React.createElement(Box, { flexDirection: "column" }, renderedLines);
225
246
  }
226
247
  else {
227
248
  return (React.createElement(React.Fragment, null,
@@ -85,9 +85,10 @@ export default function CodeBaseConfigScreen({ onBack, onSave, inlineMode = fals
85
85
  }
86
86
  if (!embeddingBaseUrl.trim()) {
87
87
  validationErrors.push('Embedding base URL is required when enabled');
88
- }
89
- if (!embeddingApiKey.trim()) {
90
- validationErrors.push('Embedding API key is required when enabled');
88
+ // Embedding API key is optional (for local deployments like Ollama)
89
+ // if (!embeddingApiKey.trim()) {
90
+ // validationErrors.push('Embedding API key is required when enabled');
91
+ // }
91
92
  }
92
93
  if (embeddingDimensions <= 0) {
93
94
  validationErrors.push('Embedding dimensions must be greater than 0');
@@ -175,7 +176,7 @@ export default function CodeBaseConfigScreen({ onBack, onSave, inlineMode = fals
175
176
  return (React.createElement(Box, { key: field, flexDirection: "column" },
176
177
  React.createElement(Text, { color: isActive ? 'green' : 'white' },
177
178
  isActive ? '❯ ' : ' ',
178
- "Embedding API Key:"),
179
+ "Embedding API Key (Optional for local):"),
179
180
  isCurrentlyEditing && (React.createElement(Box, { marginLeft: 3 },
180
181
  React.createElement(Text, { color: "cyan" },
181
182
  React.createElement(TextInput, { value: embeddingApiKey, onChange: value => setEmbeddingApiKey(stripFocusArtifacts(value)), onSubmit: () => setIsEditing(false), mask: "*" })))),
@@ -41,7 +41,7 @@ export default function WelcomeScreen({ version = '1.0.0', onMenuSelect, }) {
41
41
  {
42
42
  label: 'CodeBase Settings',
43
43
  value: 'codebase',
44
- infoText: 'Configure codebase indexing with embedding and LLM models',
44
+ infoText: 'Configure codebase indexing with embedding models',
45
45
  },
46
46
  {
47
47
  label: 'System Prompt Settings',
@@ -260,7 +260,7 @@ function migrateSystemPromptFromTxt() {
260
260
  writeFileSync(SYSTEM_PROMPT_JSON_FILE, JSON.stringify(config, null, 2), 'utf8');
261
261
  // 删除旧文件
262
262
  unlinkSync(SYSTEM_PROMPT_FILE);
263
- console.log('✅ Migrated system prompt from txt to json format.');
263
+ // console.log('✅ Migrated system prompt from txt to json format.');
264
264
  }
265
265
  catch (error) {
266
266
  console.error('Failed to migrate system prompt:', error);
@@ -31,11 +31,9 @@ export declare class TextBuffer {
31
31
  private placeholderStorage;
32
32
  private textPlaceholderCounter;
33
33
  private imagePlaceholderCounter;
34
- private pasteAccumulator;
35
- private pasteTimer;
36
- private pastePlaceholderPosition;
37
34
  private onUpdateCallback?;
38
35
  private isDestroyed;
36
+ private tempPastingPlaceholder;
39
37
  private visualLines;
40
38
  private visualLineStarts;
41
39
  private visualCursorPos;
@@ -58,9 +56,9 @@ export declare class TextBuffer {
58
56
  setText(text: string): void;
59
57
  insert(input: string): void;
60
58
  /**
61
- * 完成粘贴操作,创建占位符
59
+ * 插入临时"粘贴中"占位符,用于大文本粘贴时的用户反馈
62
60
  */
63
- private finalizePaste;
61
+ insertPastingIndicator(): void;
64
62
  private insertPlainText;
65
63
  backspace(): void;
66
64
  delete(): void;
@@ -53,24 +53,6 @@ export class TextBuffer {
53
53
  writable: true,
54
54
  value: 0
55
55
  }); // 图片占位符计数器
56
- Object.defineProperty(this, "pasteAccumulator", {
57
- enumerable: true,
58
- configurable: true,
59
- writable: true,
60
- value: ''
61
- }); // 累积粘贴内容
62
- Object.defineProperty(this, "pasteTimer", {
63
- enumerable: true,
64
- configurable: true,
65
- writable: true,
66
- value: null
67
- }); // 粘贴完成检测定时器
68
- Object.defineProperty(this, "pastePlaceholderPosition", {
69
- enumerable: true,
70
- configurable: true,
71
- writable: true,
72
- value: -1
73
- }); // 占位符插入位置
74
56
  Object.defineProperty(this, "onUpdateCallback", {
75
57
  enumerable: true,
76
58
  configurable: true,
@@ -83,6 +65,12 @@ export class TextBuffer {
83
65
  writable: true,
84
66
  value: false
85
67
  }); // 标记是否已销毁
68
+ Object.defineProperty(this, "tempPastingPlaceholder", {
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ value: null
73
+ }); // 临时"粘贴中"占位符文本
86
74
  Object.defineProperty(this, "visualLines", {
87
75
  enumerable: true,
88
76
  configurable: true,
@@ -116,10 +104,6 @@ export class TextBuffer {
116
104
  */
117
105
  destroy() {
118
106
  this.isDestroyed = true;
119
- if (this.pasteTimer) {
120
- clearTimeout(this.pasteTimer);
121
- this.pasteTimer = null;
122
- }
123
107
  this.placeholderStorage.clear();
124
108
  this.onUpdateCallback = undefined;
125
109
  }
@@ -167,12 +151,6 @@ export class TextBuffer {
167
151
  this.placeholderStorage.clear();
168
152
  this.textPlaceholderCounter = 0;
169
153
  this.imagePlaceholderCounter = 0;
170
- this.pasteAccumulator = '';
171
- if (this.pasteTimer) {
172
- clearTimeout(this.pasteTimer);
173
- this.pasteTimer = null;
174
- }
175
- this.pastePlaceholderPosition = -1;
176
154
  }
177
155
  this.recalculateVisualState();
178
156
  this.scheduleUpdate();
@@ -183,99 +161,46 @@ export class TextBuffer {
183
161
  return;
184
162
  }
185
163
  const charCount = sanitized.length;
186
- // 检测是否是大文本输入(可能是粘贴操作的一部分)
187
- if (charCount > 200) {
188
- // 清除之前的定时器
189
- if (this.pasteTimer) {
190
- clearTimeout(this.pasteTimer);
191
- }
192
- // 如果是第一批数据,记录插入位置并清空内容
193
- const isFirstBatch = !this.pasteAccumulator;
194
- if (isFirstBatch) {
195
- this.pastePlaceholderPosition = this.cursorIndex;
196
- // 保存粘贴位置前后的内容,避免后续计算错误
197
- this.content = cpSlice(this.content, 0, this.pastePlaceholderPosition) +
198
- cpSlice(this.content, this.pastePlaceholderPosition);
199
- }
200
- // 累积数据
201
- this.pasteAccumulator += sanitized;
202
- // 移除所有旧的临时占位符(使用全局替换)
203
- if (!isFirstBatch) {
204
- const tempPlaceholderPattern = /\[Pasting\.\.\. \d+ chars\]/g;
205
- this.content = this.content.replace(tempPlaceholderPattern, '');
206
- }
207
- // 显示更新后的临时占位符
208
- const tempPlaceholder = `[Pasting... ${this.pasteAccumulator.length} chars]`;
209
- const before = cpSlice(this.content, 0, this.pastePlaceholderPosition);
210
- const after = cpSlice(this.content, this.pastePlaceholderPosition);
211
- this.content = before + tempPlaceholder + after;
212
- this.cursorIndex = this.pastePlaceholderPosition + cpLen(tempPlaceholder);
213
- // 设置150ms的定时器,如果150ms内没有新数据,则认为粘贴完成
214
- this.pasteTimer = setTimeout(() => {
215
- if (!this.isDestroyed) {
216
- this.finalizePaste();
217
- }
218
- }, 150);
219
- this.recalculateVisualState();
220
- this.scheduleUpdate();
221
- return;
164
+ // 如果存在临时"粘贴中"占位符,先移除它
165
+ if (this.tempPastingPlaceholder) {
166
+ this.content = this.content.replace(this.tempPastingPlaceholder, '');
167
+ this.tempPastingPlaceholder = null;
222
168
  }
223
- // 普通输入(小于200字符)
224
- // 如果有累积的粘贴数据,先完成粘贴
225
- if (this.pasteAccumulator) {
226
- this.finalizePaste();
227
- }
228
- // 正常插入文本
229
- this.insertPlainText(sanitized);
230
- this.scheduleUpdate();
231
- }
232
- /**
233
- * 完成粘贴操作,创建占位符
234
- */
235
- finalizePaste() {
236
- if (!this.pasteAccumulator) {
237
- return;
238
- }
239
- const totalChars = this.pasteAccumulator.length;
240
- // 移除所有临时占位符(使用全局替换)
241
- // 临时占位符格式: [Pasting... XXX chars]
242
- const tempPlaceholderPattern = /\[Pasting\.\.\. \d+ chars\]/g;
243
- this.content = this.content.replace(tempPlaceholderPattern, '');
244
- // 只有当累积的字符数超过300时才创建占位符
245
- if (totalChars > 300) {
169
+ // 如果是大文本(>300字符),直接创建占位符
170
+ if (charCount > 300) {
246
171
  this.textPlaceholderCounter++;
247
172
  const pasteId = `paste_${Date.now()}_${this.textPlaceholderCounter}`;
248
- const placeholderText = `[Paste ${totalChars} characters #${this.textPlaceholderCounter}]`;
173
+ // 计算行数
174
+ const lineCount = (sanitized.match(/\n/g) || []).length + 1;
175
+ const placeholderText = `[Paste ${lineCount} lines #${this.textPlaceholderCounter}]`;
249
176
  this.placeholderStorage.set(pasteId, {
250
177
  id: pasteId,
251
178
  type: 'text',
252
- content: this.pasteAccumulator,
253
- charCount: totalChars,
179
+ content: sanitized,
180
+ charCount: charCount,
254
181
  index: this.textPlaceholderCounter,
255
182
  placeholder: placeholderText,
256
183
  });
257
- // 在记录的位置插入占位符
258
- const before = cpSlice(this.content, 0, this.pastePlaceholderPosition);
259
- const after = cpSlice(this.content, this.pastePlaceholderPosition);
260
- this.content = before + placeholderText + after;
261
- this.cursorIndex = this.pastePlaceholderPosition + cpLen(placeholderText);
184
+ // 插入占位符而不是原文本
185
+ this.insertPlainText(placeholderText);
262
186
  }
263
187
  else {
264
- // 如果总字符数不够,直接插入原文本
265
- const before = cpSlice(this.content, 0, this.pastePlaceholderPosition);
266
- const after = cpSlice(this.content, this.pastePlaceholderPosition);
267
- this.content = before + this.pasteAccumulator + after;
268
- this.cursorIndex =
269
- this.pastePlaceholderPosition + cpLen(this.pasteAccumulator);
270
- }
271
- // 清理状态
272
- this.pasteAccumulator = '';
273
- this.pastePlaceholderPosition = -1;
274
- if (this.pasteTimer) {
275
- clearTimeout(this.pasteTimer);
276
- this.pasteTimer = null;
188
+ // 普通输入,直接插入文本
189
+ this.insertPlainText(sanitized);
277
190
  }
278
- this.recalculateVisualState();
191
+ this.scheduleUpdate();
192
+ }
193
+ /**
194
+ * 插入临时"粘贴中"占位符,用于大文本粘贴时的用户反馈
195
+ */
196
+ insertPastingIndicator() {
197
+ // 如果已经有临时占位符,不需要重复插入
198
+ if (this.tempPastingPlaceholder) {
199
+ return;
200
+ }
201
+ // 创建静态的临时占位符(简单明了)
202
+ this.tempPastingPlaceholder = `[Pasting...]`;
203
+ this.insertPlainText(this.tempPastingPlaceholder);
279
204
  this.scheduleUpdate();
280
205
  }
281
206
  insertPlainText(text) {
@@ -15,6 +15,9 @@ const TWO_STEP_TOOLS = new Set([
15
15
  'terminal-execute',
16
16
  // 代码库搜索工具 - 需要生成 embedding 和搜索,耗时较长
17
17
  'codebase-search',
18
+ // 联网搜索工具 - 需要启动浏览器、网络请求、内容处理,耗时较长
19
+ 'websearch-search',
20
+ 'websearch-fetch',
18
21
  // 子代理工具 - 执行复杂任务,需要显示进度
19
22
  // 所有以 'subagent-' 开头的工具都需要两步显示
20
23
  ]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snow-ai",
3
- "version": "0.3.33",
3
+ "version": "0.3.35",
4
4
  "description": "Intelligent Command Line Assistant powered by AI",
5
5
  "license": "MIT",
6
6
  "bin": {