opencode-fast-apply 2.0.0 → 2.1.2

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 JRedeker
3
+ Copyright (c) 2025 tickernelz
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -14,33 +14,41 @@ OpenCode plugin for Fast Apply - High-performance code editing with OpenAI-compa
14
14
 
15
15
  ## Installation
16
16
 
17
- ### 1. Clone the repository
17
+ ### 1. Install from npm (Recommended)
18
18
 
19
19
  ```bash
20
- git clone https://github.com/tickernelz/opencode-fast-apply.git ~/dev/oc-plugins/fast-apply
21
- cd ~/dev/oc-plugins/fast-apply
22
- npm install
20
+ npm install -g opencode-fast-apply
21
+ ```
22
+
23
+ Or add to your OpenCode config to auto-install:
24
+
25
+ ```json
26
+ {
27
+ "plugin": [
28
+ "opencode-fast-apply"
29
+ ]
30
+ }
23
31
  ```
24
32
 
25
33
  ### 2. Configure your API endpoint
26
34
 
27
35
  For **LM Studio** (default):
28
36
  ```bash
29
- export FAST_APPLY_URL="http://localhost:1234/v1"
37
+ export FAST_APPLY_URL="http://localhost:1234"
30
38
  export FAST_APPLY_MODEL="fastapply-1.5b"
31
39
  export FAST_APPLY_API_KEY="optional-api-key"
32
40
  ```
33
41
 
34
42
  For **Ollama**:
35
43
  ```bash
36
- export FAST_APPLY_URL="http://localhost:11434/v1"
44
+ export FAST_APPLY_URL="http://localhost:11434"
37
45
  export FAST_APPLY_MODEL="codellama:7b"
38
46
  export FAST_APPLY_API_KEY="optional-api-key"
39
47
  ```
40
48
 
41
49
  For **OpenAI**:
42
50
  ```bash
43
- export FAST_APPLY_URL="https://api.openai.com/v1"
51
+ export FAST_APPLY_URL="https://api.openai.com"
44
52
  export FAST_APPLY_MODEL="gpt-4"
45
53
  export FAST_APPLY_API_KEY="sk-your-openai-key"
46
54
  ```
@@ -49,35 +57,22 @@ export FAST_APPLY_API_KEY="sk-your-openai-key"
49
57
 
50
58
  ### 3. Add the plugin to your OpenCode config
51
59
 
52
- Add to your global config (`~/.config/opencode/opencode.json`):
60
+ Add to your global config (`~/.config/opencode/opencode.json` or `opencode.jsonc`):
53
61
 
54
62
  ```json
55
63
  {
56
64
  "plugin": [
57
- "/path/to/fast-apply"
58
- ],
59
- "instructions": [
60
- "/path/to/fast-apply/FAST_APPLY_INSTRUCTIONS.md"
65
+ "opencode-pty",
66
+ "opencode-fast-apply"
61
67
  ]
62
68
  }
63
69
  ```
64
70
 
65
- Or in a project-local `.opencode/config.json`:
66
-
67
- ```json
68
- {
69
- "plugin": [
70
- "~/dev/oc-plugins/fast-apply"
71
- ],
72
- "instructions": [
73
- "~/dev/oc-plugins/fast-apply/FAST_APPLY_INSTRUCTIONS.md"
74
- ]
75
- }
76
- ```
71
+ **That's it!** The plugin automatically embeds all instructions - no additional configuration needed.
77
72
 
78
73
  ### 4. Restart OpenCode
79
74
 
80
- The `fast_apply_edit` tool will now be available.
75
+ The `fast_apply_edit` tool will now be available and configured as the default editing tool.
81
76
 
82
77
  ## Usage
83
78
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AA0MvD,eAAO,MAAM,eAAe,EAAE,MA4J7B,CAAA;AAGD,eAAe,eAAe,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,MAAM,EAAQ,MAAM,qBAAqB,CAAA;AA0YvD,eAAO,MAAM,eAAe,EAAE,MA2H7B,CAAA;AAGD,eAAe,eAAe,CAAA"}
package/dist/index.js CHANGED
@@ -18,13 +18,11 @@ const FAST_APPLY_MODEL = process.env.FAST_APPLY_MODEL || "fastapply-1.5b";
18
18
  const FAST_APPLY_TIMEOUT = parseInt(process.env.FAST_APPLY_TIMEOUT || "30000", 10);
19
19
  const FAST_APPLY_TEMPERATURE = parseFloat(process.env.FAST_APPLY_TEMPERATURE || "0.05");
20
20
  const FAST_APPLY_MAX_TOKENS = parseInt(process.env.FAST_APPLY_MAX_TOKENS || "8000", 10);
21
- const PLUGIN_VERSION = "2.0.0";
22
21
  const FAST_APPLY_SYSTEM_PROMPT = "You are a coding assistant that helps merge code updates, ensuring every modification is fully integrated.";
23
22
  const FAST_APPLY_USER_PROMPT = `Merge all changes from the <update> snippet into the <code> below.
24
- Instruction: {instruction}
25
23
  - Preserve the code's structure, order, comments, and indentation exactly.
26
24
  - Output only the updated code, enclosed within <updated-code> and </updated-code> tags.
27
- - Do not include any additional text, explanations, placeholders, markdown, ellipses, or code fences.
25
+ - Do not include any additional text, explanations, placeholders, ellipses, or code fences.
28
26
 
29
27
  <code>{original_code}</code>
30
28
 
@@ -33,6 +31,73 @@ Instruction: {instruction}
33
31
  Provide the complete updated code.`;
34
32
  const UPDATED_CODE_START = "<updated-code>";
35
33
  const UPDATED_CODE_END = "</updated-code>";
34
+ const TOOL_INSTRUCTIONS = `**DEFAULT tool for editing existing files. Use INSTEAD of native 'edit' tool.**
35
+
36
+ CRITICAL: For EXISTING files ONLY. Use 'write' for new files.
37
+
38
+ ## Priority
39
+ 1. \`fast_apply_edit\` - ALL file edits (default)
40
+ 2. \`edit\` - Fallback if API fails
41
+ 3. \`write\` - NEW files only
42
+
43
+ ## Format
44
+ Use \`// ... existing code ...\` for unchanged sections:
45
+
46
+ \`\`\`
47
+ // ... existing code ...
48
+ function updated() {
49
+ return "modified";
50
+ }
51
+ // ... existing code ...
52
+ \`\`\`
53
+
54
+ ## Rules
55
+ - MANDATORY: Use \`// ... existing code ...\` markers
56
+ - Include 2-3 lines context before/after edits
57
+ - Preserve exact indentation
58
+ - ONE edit block per call (multiple blocks = suboptimal results)
59
+ - Deletions: show context, omit deleted lines
60
+ - NEVER for new files
61
+
62
+ ## Examples
63
+
64
+ **Add function:**
65
+ \`\`\`
66
+ // ... existing code ...
67
+ import { newDep } from './newDep';
68
+ // ... existing code ...
69
+
70
+ function newFeature() {
71
+ return newDep.process();
72
+ }
73
+ // ... existing code ...
74
+ \`\`\`
75
+
76
+ **Modify:**
77
+ \`\`\`
78
+ // ... existing code ...
79
+ function existingFunc(param) {
80
+ const result = param * 2;
81
+ return result;
82
+ }
83
+ // ... existing code ...
84
+ \`\`\`
85
+
86
+ **Delete:**
87
+ \`\`\`
88
+ // ... existing code ...
89
+ function keepThis() {
90
+ return "stays";
91
+ }
92
+
93
+ function alsoKeepThis() {
94
+ return "stays";
95
+ }
96
+ // ... existing code ...
97
+ \`\`\`
98
+
99
+ ## Fallback
100
+ If API fails, use native \`edit\` tool with exact string matching.`;
36
101
  function escapeXmlTags(text) {
37
102
  return text
38
103
  .replace(/<updated-code>/g, "&lt;updated-code&gt;")
@@ -45,9 +110,22 @@ function unescapeXmlTags(text) {
45
110
  }
46
111
  function extractUpdatedCode(raw) {
47
112
  const stripped = raw.trim();
48
- const start = stripped.indexOf(UPDATED_CODE_START);
49
- const end = stripped.lastIndexOf(UPDATED_CODE_END);
50
- if (start === -1 || end === -1 || end <= start) {
113
+ const startTag = UPDATED_CODE_START;
114
+ const endTag = UPDATED_CODE_END;
115
+ let startIdx = stripped.indexOf(startTag);
116
+ if (startIdx === -1) {
117
+ startIdx = stripped.indexOf("<updated-code");
118
+ if (startIdx !== -1) {
119
+ const closeTagIdx = stripped.indexOf(">", startIdx);
120
+ if (closeTagIdx !== -1) {
121
+ startIdx = closeTagIdx + 1;
122
+ }
123
+ }
124
+ }
125
+ else {
126
+ startIdx += startTag.length;
127
+ }
128
+ if (startIdx === -1 || startIdx === startTag.length - 1) {
51
129
  if (stripped.startsWith("```") && stripped.endsWith("```")) {
52
130
  const lines = stripped.split("\n");
53
131
  if (lines.length >= 2) {
@@ -56,7 +134,19 @@ function extractUpdatedCode(raw) {
56
134
  }
57
135
  return unescapeXmlTags(stripped);
58
136
  }
59
- const inner = stripped.substring(start + UPDATED_CODE_START.length, end);
137
+ let endIdx = stripped.indexOf(endTag, startIdx);
138
+ if (endIdx === -1) {
139
+ endIdx = stripped.indexOf("</updated-code", startIdx);
140
+ }
141
+ if (endIdx === -1) {
142
+ const extracted = stripped.slice(startIdx).trim();
143
+ const lastCloseTag = extracted.lastIndexOf("</");
144
+ if (lastCloseTag !== -1 && extracted.slice(lastCloseTag).toLowerCase().includes("update")) {
145
+ return unescapeXmlTags(extracted.slice(0, lastCloseTag).trim());
146
+ }
147
+ return unescapeXmlTags(extracted);
148
+ }
149
+ const inner = stripped.substring(startIdx, endIdx);
60
150
  if (!inner || inner.trim().length === 0) {
61
151
  throw new Error("Empty updated-code block");
62
152
  }
@@ -86,6 +176,54 @@ function countChanges(diff) {
86
176
  }
87
177
  return { added, removed };
88
178
  }
179
+ function formatTokenCount(tokens) {
180
+ if (tokens >= 1000) {
181
+ return `${(tokens / 1000).toFixed(1)}K`.replace(".0K", "K");
182
+ }
183
+ return tokens.toString();
184
+ }
185
+ function shortenPath(filePath, workingDir) {
186
+ if (filePath.startsWith(workingDir + "/")) {
187
+ return filePath.slice(workingDir.length + 1);
188
+ }
189
+ if (filePath === workingDir) {
190
+ return ".";
191
+ }
192
+ return filePath;
193
+ }
194
+ function truncate(str, maxLen = 80) {
195
+ if (str.length <= maxLen)
196
+ return str;
197
+ return str.slice(0, maxLen - 3) + "...";
198
+ }
199
+ function estimateTokens(text) {
200
+ return Math.ceil(text.length / 4);
201
+ }
202
+ function formatFastApplyResult(filePath, workingDir, insertions, deletions, diffPreview, modifiedTokens) {
203
+ const shortPath = shortenPath(filePath, workingDir);
204
+ const tokenStr = formatTokenCount(modifiedTokens);
205
+ const lines = [
206
+ "✓ Fast Apply complete",
207
+ "",
208
+ `File: ${shortPath}`,
209
+ `Changes: +${insertions} -${deletions} (~${tokenStr} tokens)`,
210
+ "",
211
+ "Unified diff:",
212
+ diffPreview
213
+ ];
214
+ return lines.join("\n");
215
+ }
216
+ function formatErrorOutput(error, filePath, workingDir) {
217
+ const shortPath = shortenPath(filePath, workingDir);
218
+ return [
219
+ "✗ Fast Apply failed",
220
+ "",
221
+ `File: ${shortPath}`,
222
+ `Error: ${error}`,
223
+ "",
224
+ "Fallback: Use native 'edit' tool with exact string matching"
225
+ ].join("\n");
226
+ }
89
227
  /**
90
228
  * Call OpenAI's Fast Apply API to merge code edits
91
229
  */
@@ -102,7 +240,6 @@ async function callFastApply(originalCode, codeEdit, instructions) {
102
240
  const escapedOriginalCode = escapeXmlTags(originalCode);
103
241
  const escapedCodeEdit = escapeXmlTags(codeEdit);
104
242
  const userContent = FAST_APPLY_USER_PROMPT
105
- .replace("{instruction}", instructions || "Apply the requested code changes.")
106
243
  .replace("{original_code}", escapedOriginalCode)
107
244
  .replace("{update_snippet}", escapedCodeEdit);
108
245
  const response = await fetch(`${FAST_APPLY_URL}/v1/chat/completions`, {
@@ -165,7 +302,35 @@ async function callFastApply(originalCode, codeEdit, instructions) {
165
302
  };
166
303
  }
167
304
  }
168
- export const FastApplyPlugin = async ({ directory }) => {
305
+ async function sendTUINotification(client, sessionID, filePath, workingDir, insertions, deletions, modifiedTokens) {
306
+ const shortPath = shortenPath(filePath, workingDir);
307
+ const tokenStr = formatTokenCount(modifiedTokens);
308
+ const message = [
309
+ `▣ Fast Apply | ~${tokenStr} tokens modified`,
310
+ "",
311
+ "Applied changes:",
312
+ `→ ${shortPath}: +${insertions} -${deletions}`
313
+ ].join("\n");
314
+ try {
315
+ await client.session.prompt({
316
+ path: { id: sessionID },
317
+ body: {
318
+ noReply: true,
319
+ parts: [
320
+ {
321
+ type: "text",
322
+ text: message,
323
+ ignored: true,
324
+ },
325
+ ],
326
+ },
327
+ });
328
+ }
329
+ catch (error) {
330
+ console.error("[fast-apply] Failed to send TUI notification:", error.message);
331
+ }
332
+ }
333
+ export const FastApplyPlugin = async ({ directory, client }) => {
169
334
  if (!FAST_APPLY_API_KEY) {
170
335
  console.warn("[fast-apply] FAST_APPLY_API_KEY not set - fast_apply_edit tool will be disabled");
171
336
  }
@@ -175,43 +340,7 @@ export const FastApplyPlugin = async ({ directory }) => {
175
340
  return {
176
341
  tool: {
177
342
  fast_apply_edit: tool({
178
- description: `PRIMARY TOOL for all file editing operations. Use this INSTEAD of the native 'edit' tool.
179
-
180
- **CRITICAL: This tool is for EDITING EXISTING FILES ONLY. DO NOT use for creating new files.**
181
-
182
- Fast code editing using OpenAI-compatible Fast Apply API (10,500+ tokens/sec).
183
- Handles lazy edit markers so you don't need exact string matching.
184
-
185
- FORMAT:
186
- Use "// ... existing code ..." to represent unchanged code blocks.
187
- Include minimal surrounding context to locate each edit precisely.
188
-
189
- EXAMPLE:
190
- // ... existing code ...
191
- function updatedFunction() {
192
- // New implementation
193
- return "modified";
194
- }
195
- // ... existing code ...
196
-
197
- RULES:
198
- - MANDATORY: Use "// ... existing code ..." for unchanged sections
199
- - Include 2-3 lines of context before and after each edit
200
- - Preserve exact indentation from original file
201
- - For deletions: show context before/after, omit deleted lines
202
- - Batch multiple edits to same file in one call
203
- - NEVER use for new file creation - use 'write' tool instead
204
-
205
- WHEN TO USE:
206
- - ALL file editing operations (default choice)
207
- - Large files (any size)
208
- - Multiple scattered changes
209
- - Complex refactoring
210
- - When exact string matching would be fragile
211
-
212
- FALLBACK:
213
- If Fast Apply API fails or is unavailable, fall back to native 'edit' tool with exact string matching.
214
- For new files, ALWAYS use 'write' tool instead.`,
343
+ description: TOOL_INSTRUCTIONS,
215
344
  args: {
216
345
  target_filepath: tool.schema
217
346
  .string()
@@ -223,7 +352,7 @@ For new files, ALWAYS use 'write' tool instead.`,
223
352
  .string()
224
353
  .describe('The code changes with "// ... existing code ..." markers for unchanged sections'),
225
354
  },
226
- async execute(args) {
355
+ async execute(args, toolCtx) {
227
356
  const { target_filepath, instructions, code_edit } = args;
228
357
  // Resolve file path relative to project directory
229
358
  const filepath = target_filepath.startsWith("/")
@@ -263,34 +392,21 @@ write({
263
392
  // Call OpenAI API to merge the edit
264
393
  const result = await callFastApply(originalCode, code_edit, instructions);
265
394
  if (!result.success || !result.content) {
266
- // Return error with suggestion to use native edit
267
- return `OpenAI Fast Apply API failed: ${result.error}
268
-
269
- Suggestion: Try using the native 'edit' tool instead with exact string replacement.
270
- The edit tool requires matching the exact text in the file.`;
395
+ return formatErrorOutput(result.error || "Unknown error", target_filepath, directory);
271
396
  }
272
397
  const mergedCode = result.content;
273
- // Write the merged result
274
398
  try {
275
399
  await writeFile(filepath, mergedCode, "utf-8");
276
400
  }
277
401
  catch (err) {
278
402
  const error = err;
279
- return `Error writing file ${target_filepath}: ${error.message}`;
403
+ return formatErrorOutput(error.message, target_filepath, directory);
280
404
  }
281
- // Generate unified diff
282
405
  const diff = generateUnifiedDiff(target_filepath, originalCode, mergedCode);
283
- // Calculate change stats
284
406
  const { added, removed } = countChanges(diff);
285
- const originalLines = originalCode.split("\n").length;
286
- const mergedLines = mergedCode.split("\n").length;
287
- return `Applied edit to ${target_filepath}
288
-
289
- +${added} -${removed} lines | ${originalLines} -> ${mergedLines} total
290
-
291
- \`\`\`diff
292
- ${diff.slice(0, 3000)}${diff.length > 3000 ? "\n... (truncated)" : ""}
293
- \`\`\``;
407
+ const modifiedTokens = estimateTokens(diff);
408
+ await sendTUINotification(client, toolCtx.sessionID, target_filepath, directory, added, removed, modifiedTokens);
409
+ return formatFastApplyResult(target_filepath, directory, added, removed, diff, modifiedTokens);
294
410
  },
295
411
  }),
296
412
  },
package/package.json CHANGED
@@ -1,13 +1,12 @@
1
1
  {
2
2
  "name": "opencode-fast-apply",
3
- "version": "2.0.0",
3
+ "version": "2.1.2",
4
4
  "description": "OpenCode plugin for Fast Apply - High-performance code editing with OpenAI-compatible APIs (LM Studio, Ollama)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
7
  "types": "dist/index.d.ts",
8
8
  "files": [
9
9
  "dist",
10
- "FAST_APPLY_INSTRUCTIONS.md",
11
10
  "README.md",
12
11
  "LICENSE"
13
12
  ],
@@ -40,7 +39,7 @@
40
39
  "author": "tickernelz",
41
40
  "repository": {
42
41
  "type": "git",
43
- "url": "https://github.com/tickernelz/opencode-fast-apply.git"
42
+ "url": "git+https://github.com/tickernelz/opencode-fast-apply.git"
44
43
  },
45
44
  "bugs": {
46
45
  "url": "https://github.com/tickernelz/opencode-fast-apply/issues"
@@ -50,4 +49,4 @@
50
49
  "engines": {
51
50
  "node": ">=18.0.0"
52
51
  }
53
- }
52
+ }
@@ -1,121 +0,0 @@
1
- # Fast Apply - Tool Selection Guide
2
-
3
- **CRITICAL: Use `fast_apply_edit` as your DEFAULT tool for ALL file editing operations.**
4
-
5
- ## Tool Selection Priority
6
-
7
- | Priority | Tool | Use Case |
8
- |----------|------|----------|
9
- | **1st** | `fast_apply_edit` | **ALL file editing operations** (default choice) |
10
- | **2nd** | `edit` | Fallback only when `fast_apply_edit` fails |
11
- | **3rd** | `write` | **ONLY for creating NEW files** |
12
-
13
- ## Default Behavior
14
-
15
- **ALWAYS use `fast_apply_edit` for editing existing files unless:**
16
- - Fast Apply API is unavailable or returns error
17
- - You need guaranteed exact string replacement
18
- - The file doesn't exist yet (use `write` instead)
19
-
20
- ## Tool Selection Matrix
21
-
22
- | Situation | Tool | Reason |
23
- |-----------|------|--------|
24
- | Edit existing file (any size) | `fast_apply_edit` | **DEFAULT**, handles lazy markers, robust |
25
- | Small change (1-2 lines) | `fast_apply_edit` | **DEFAULT**, no need to switch tools |
26
- | Large file (500+ lines) | `fast_apply_edit` | **DEFAULT**, optimal performance with partial snippets |
27
- | Multiple scattered changes | `fast_apply_edit` | **DEFAULT**, batch changes efficiently |
28
- | Complex refactoring | `fast_apply_edit` | **DEFAULT**, better accuracy with context |
29
- | Whitespace-sensitive edits | `fast_apply_edit` | **DEFAULT**, forgiving with formatting |
30
- | Special characters in code | `fast_apply_edit` | **DEFAULT**, handles XML tags, regex, etc. |
31
- | Fast Apply API fails | `edit` | Fallback with exact string matching |
32
- | **New file creation** | `write` | **NEVER use fast_apply_edit for new files** |
33
-
34
- ## Using fast_apply_edit
35
-
36
- The `fast_apply_edit` tool uses **lazy edit markers** to represent unchanged code:
37
-
38
- ```javascript
39
- // ... existing code ...
40
- function updatedFunction() {
41
- // New implementation
42
- return "modified";
43
- }
44
- // ... existing code ...
45
- ```
46
-
47
- ### Parameters
48
-
49
- - `target_filepath`: Path to the file (relative to project root)
50
- - `instructions`: Brief description of changes (helps AI disambiguate)
51
- - `code_edit`: Code with `// ... existing code ...` markers
52
-
53
- ### Rules
54
-
55
- 1. **MANDATORY**: Use `// ... existing code ...` for unchanged sections
56
- 2. Include **2-3 lines of context** before and after each edit
57
- 3. Preserve **exact indentation** from original file
58
- 4. For **deletions**: show context before/after, omit the deleted lines
59
- 5. **Batch** multiple edits to the same file in one call
60
- 6. **NEVER** use for new file creation - use `write` tool instead
61
-
62
- ### Examples
63
-
64
- **Adding a function:**
65
- ```
66
- // ... existing code ...
67
- import { newDep } from './newDep';
68
- // ... existing code ...
69
-
70
- function newFeature() {
71
- return newDep.process();
72
- }
73
- // ... existing code ...
74
- ```
75
-
76
- **Modifying existing code:**
77
- ```
78
- // ... existing code ...
79
- function existingFunc(param) {
80
- // Updated implementation
81
- const result = param * 2; // Changed from * 1
82
- return result;
83
- }
84
- // ... existing code ...
85
- ```
86
-
87
- **Deleting code (show what remains):**
88
- ```
89
- // ... existing code ...
90
- function keepThis() {
91
- return "stays";
92
- }
93
-
94
- // The function between these two was removed
95
-
96
- function alsoKeepThis() {
97
- return "also stays";
98
- }
99
- // ... existing code ...
100
- ```
101
-
102
- ## Fallback Behavior
103
-
104
- If Fast Apply API fails (timeout, network error, etc.):
105
- 1. Tool returns error message with details
106
- 2. **Fallback to native `edit` tool** with exact string matching
107
- 3. The `edit` tool requires matching exact text from the file
108
-
109
- **Note:** API failures are rare. Always try `fast_apply_edit` first.
110
-
111
- ## When to Use Native 'edit' Tool
112
-
113
- - **ONLY as fallback** when `fast_apply_edit` fails
114
- - When Fast Apply API is unavailable
115
- - When you need guaranteed exact string replacement
116
-
117
- ## When to Use 'write' Tool
118
-
119
- - **ONLY for creating NEW files**
120
- - Never use `fast_apply_edit` for file creation
121
- - Provide complete file content without lazy markers