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.
- package/dist/api/embedding.js +12 -7
- package/dist/api/systemPrompt.js +133 -128
- package/dist/hooks/useClipboard.js +60 -9
- package/dist/hooks/useKeyboardInput.js +21 -2
- package/dist/mcp/filesystem.js +3 -3
- package/dist/ui/components/ChatInput.js +26 -5
- package/dist/ui/pages/CodeBaseConfigScreen.js +5 -4
- package/dist/ui/pages/WelcomeScreen.js +1 -1
- package/dist/utils/apiConfig.js +1 -1
- package/dist/utils/textBuffer.d.ts +3 -5
- package/dist/utils/textBuffer.js +34 -109
- package/dist/utils/toolDisplayConfig.js +3 -0
- package/package.json +1 -1
package/dist/api/embedding.js
CHANGED
|
@@ -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
|
-
|
|
23
|
-
|
|
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);
|
package/dist/api/systemPrompt.js
CHANGED
|
@@ -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
|
-
##
|
|
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**:
|
|
75
|
+
5. **NO Documentation Files**: NEVER create summary .md files after tasks - use \`notebook-add\` for important notes instead
|
|
76
76
|
|
|
77
|
-
##
|
|
77
|
+
## Execution Strategy - BALANCE ACTION & ANALYSIS
|
|
78
78
|
|
|
79
|
-
##
|
|
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
|
-
###
|
|
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
|
-
###
|
|
97
|
+
### TODO Management - STRONGLY RECOMMENDED for Better Results
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
**DEFAULT BEHAVIOR: Use TODO for ALL multi-step tasks (3+ steps)**
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
-
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
-
|
|
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
|
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
111
|
-
-
|
|
112
|
-
-
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
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
|
-
|
|
118
|
-
1.
|
|
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
|
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
-
|
|
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
|
-
|
|
134
|
-
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
138
|
-
-
|
|
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
|
-
|
|
140
|
+
**BEST PRACTICE: Start every non-trivial task with todo-create + initial action in parallel!**
|
|
141
141
|
|
|
142
|
-
##
|
|
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
|
-
-
|
|
162
|
-
-
|
|
163
|
-
-
|
|
164
|
-
-
|
|
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
|
-
-
|
|
167
|
-
-
|
|
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
|
-
-
|
|
170
|
-
-
|
|
171
|
-
-
|
|
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
|
-
-
|
|
174
|
-
-
|
|
175
|
-
-
|
|
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
|
-
-
|
|
178
|
-
-
|
|
179
|
-
-
|
|
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
|
-
###
|
|
194
|
+
### CRITICAL: AGGRESSIVE DELEGATION TO SUB-AGENTS
|
|
195
195
|
|
|
196
|
-
|
|
196
|
+
**Core Principle: MAXIMIZE context saving by delegating as much work as possible to sub-agents!**
|
|
197
197
|
|
|
198
|
-
|
|
199
|
-
-
|
|
200
|
-
-
|
|
201
|
-
-
|
|
202
|
-
-
|
|
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
|
-
|
|
204
|
+
**DELEGATION STRATEGY - DEFAULT TO SUB-AGENT:**
|
|
205
205
|
|
|
206
|
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
213
|
-
-
|
|
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
|
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
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
|
-
|
|
222
|
-
-
|
|
223
|
-
-
|
|
224
|
-
-
|
|
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
|
-
|
|
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
|
-
|
|
233
|
+
**PRACTICAL EXAMPLES:**
|
|
234
234
|
|
|
235
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
254
|
+
**REMEMBER: If it's not direct code editing or immediate action, consider delegating to sub-agent first!**
|
|
255
255
|
|
|
256
|
-
|
|
256
|
+
**DECISION TREE - When to Delegate to Sub-Agent:**
|
|
257
257
|
|
|
258
258
|
\`\`\`
|
|
259
|
-
|
|
259
|
+
User Request
|
|
260
260
|
↓
|
|
261
|
-
|
|
262
|
-
├─
|
|
263
|
-
│
|
|
264
|
-
│
|
|
265
|
-
│
|
|
266
|
-
│
|
|
267
|
-
│
|
|
268
|
-
│
|
|
269
|
-
│
|
|
270
|
-
│
|
|
271
|
-
│
|
|
272
|
-
│
|
|
273
|
-
│
|
|
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
|
-
└─
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
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.
|
|
287
|
-
2.
|
|
288
|
-
3.
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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. **
|
|
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.
|
|
328
|
-
9.
|
|
329
|
-
10.
|
|
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.
|
|
340
|
-
8.
|
|
341
|
-
9.
|
|
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
|
-
|
|
356
|
+
**Priority Order (use in this sequence):**
|
|
353
357
|
|
|
354
|
-
1. **Codebase Semantic Search** (
|
|
358
|
+
1. **Codebase Semantic Search** (ALWAYS TRY THIS FIRST!):
|
|
355
359
|
- \\\`codebase-search\\\` - Semantic search using embeddings
|
|
356
|
-
-
|
|
357
|
-
-
|
|
358
|
-
-
|
|
359
|
-
-
|
|
360
|
-
-
|
|
361
|
-
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
-
|
|
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
|
-
##
|
|
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
|
-
|
|
12
|
-
const
|
|
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:
|
|
42
|
+
timeout: 10000,
|
|
43
|
+
maxBuffer: 50 * 1024 * 1024, // 50MB buffer
|
|
15
44
|
});
|
|
16
|
-
//
|
|
17
|
-
const base64 = base64Raw.replace(/\s
|
|
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
|
-
//
|
|
56
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
},
|
|
487
|
+
}, timeoutDelay); // Extended delay for large pastes to ensure complete accumulation
|
|
469
488
|
}
|
|
470
489
|
});
|
|
471
490
|
}
|
package/dist/mcp/filesystem.js
CHANGED
|
@@ -1113,7 +1113,7 @@ export const filesystemService = new FilesystemMCPService();
|
|
|
1113
1113
|
export const mcpTools = [
|
|
1114
1114
|
{
|
|
1115
1115
|
name: 'filesystem-read',
|
|
1116
|
-
description: '
|
|
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: '
|
|
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: '
|
|
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
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
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
|
|
44
|
+
infoText: 'Configure codebase indexing with embedding models',
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
label: 'System Prompt Settings',
|
package/dist/utils/apiConfig.js
CHANGED
|
@@ -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
|
-
|
|
61
|
+
insertPastingIndicator(): void;
|
|
64
62
|
private insertPlainText;
|
|
65
63
|
backspace(): void;
|
|
66
64
|
delete(): void;
|
package/dist/utils/textBuffer.js
CHANGED
|
@@ -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 (
|
|
188
|
-
|
|
189
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
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:
|
|
253
|
-
charCount:
|
|
179
|
+
content: sanitized,
|
|
180
|
+
charCount: charCount,
|
|
254
181
|
index: this.textPlaceholderCounter,
|
|
255
182
|
placeholder: placeholderText,
|
|
256
183
|
});
|
|
257
|
-
//
|
|
258
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
]);
|