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 +1 -1
- package/README.md +20 -25
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +181 -65
- package/package.json +3 -4
- package/FAST_APPLY_INSTRUCTIONS.md +0 -121
package/LICENSE
CHANGED
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.
|
|
17
|
+
### 1. Install from npm (Recommended)
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"
|
|
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
|
-
|
|
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
|
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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,
|
|
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, "<updated-code>")
|
|
@@ -45,9 +110,22 @@ function unescapeXmlTags(text) {
|
|
|
45
110
|
}
|
|
46
111
|
function extractUpdatedCode(raw) {
|
|
47
112
|
const stripped = raw.trim();
|
|
48
|
-
const
|
|
49
|
-
const
|
|
50
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
|
286
|
-
|
|
287
|
-
return
|
|
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.
|
|
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
|