opencode-fast-apply 2.1.5 → 2.1.7
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/README.md +12 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -29
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,8 +9,9 @@ OpenCode plugin for Fast Apply - High-performance code editing with OpenAI-compa
|
|
|
9
9
|
- **Unified diff output** with context for easy review
|
|
10
10
|
- **Graceful fallback** - suggests native `edit` tool on API failure
|
|
11
11
|
- **Multi-backend support** - LM Studio, Ollama, OpenAI, and any OpenAI-compatible endpoint
|
|
12
|
-
- **
|
|
13
|
-
- **
|
|
12
|
+
- **Unique delimiter system** - generates random delimiters per request (`<<<RESULT_xxxx>>>`) to eliminate parsing conflicts
|
|
13
|
+
- **Zero escaping overhead** - no XML tag processing needed, handles all code patterns safely
|
|
14
|
+
- **Collision-resistant parsing** - 1.6M+ unique delimiter combinations prevent conflicts with file content
|
|
14
15
|
|
|
15
16
|
## Installation
|
|
16
17
|
|
|
@@ -103,13 +104,15 @@ function validateToken(token) {
|
|
|
103
104
|
## How It Works
|
|
104
105
|
|
|
105
106
|
1. Reads the original file content
|
|
106
|
-
2.
|
|
107
|
-
3. Sends system prompt + user prompt with
|
|
107
|
+
2. Generates unique random delimiters for this request (e.g., `<<<RESULT_a3f9>>>`)
|
|
108
|
+
3. Sends system prompt + user prompt with unique delimiters to OpenAI-compatible API
|
|
108
109
|
4. API intelligently merges the lazy edit markers with original code
|
|
109
|
-
5. Extracts result
|
|
110
|
+
5. Extracts result using the unique delimiters with fallback pattern detection
|
|
110
111
|
6. Writes the merged result back to the file
|
|
111
112
|
7. Returns a unified diff showing what changed
|
|
112
113
|
|
|
114
|
+
**Delimiter Design:** Each request generates unique 4-character alphanumeric IDs (1,679,616 combinations) for delimiters like `<<<RESULT_xxxx>>>` and `<<<END_RESULT_xxxx>>>`. This eliminates parsing conflicts even if your code contains similar patterns. Fallback logic detects any `<<<RESULT_*>>>` pattern for maximum robustness.
|
|
115
|
+
|
|
113
116
|
## Performance
|
|
114
117
|
|
|
115
118
|
Performance varies based on your setup:
|
|
@@ -136,7 +139,8 @@ Performance varies based on your setup:
|
|
|
136
139
|
|
|
137
140
|
## Edge Cases Handled
|
|
138
141
|
|
|
139
|
-
- ✅
|
|
142
|
+
- ✅ Code containing XML-like tags (`<update>`, `<code>`, `<result>`) in strings
|
|
143
|
+
- ✅ Code containing triple-angle-bracket patterns (`<<<RESULT>>>`) in comments or strings
|
|
140
144
|
- ✅ Multiple XML-like tags in regex patterns
|
|
141
145
|
- ✅ Special characters (quotes, backslashes, unicode, SQL, HTML entities)
|
|
142
146
|
- ✅ Large files (500+ lines)
|
|
@@ -144,6 +148,8 @@ Performance varies based on your setup:
|
|
|
144
148
|
- ✅ Complex nested structures
|
|
145
149
|
- ✅ Template strings with `${variable}`
|
|
146
150
|
- ✅ Whitespace and indentation preservation
|
|
151
|
+
- ✅ Unique delimiters per request prevent all parsing conflicts (1.6M+ combinations)
|
|
152
|
+
- ✅ Fallback pattern detection for robust delimiter parsing
|
|
147
153
|
|
|
148
154
|
## Troubleshooting
|
|
149
155
|
|
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;AAgbvD,eAAO,MAAM,eAAe,EAAE,MA4J7B,CAAA;AAGD,eAAe,eAAe,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -18,18 +18,6 @@ const FAST_APPLY_URL = (process.env.FAST_APPLY_URL || "http://localhost:1234/v1"
|
|
|
18
18
|
const FAST_APPLY_MODEL = process.env.FAST_APPLY_MODEL || "fastapply-1.5b";
|
|
19
19
|
const FAST_APPLY_TEMPERATURE = parseFloat(process.env.FAST_APPLY_TEMPERATURE || "0.05");
|
|
20
20
|
const FAST_APPLY_SYSTEM_PROMPT = "You are a coding assistant that helps merge code updates, ensuring every modification is fully integrated.";
|
|
21
|
-
const FAST_APPLY_USER_PROMPT = `Merge all changes from the <<<UPDATE>>> snippet into the <<<CODE>>> below.
|
|
22
|
-
- Preserve the code's structure, order, comments, and indentation exactly.
|
|
23
|
-
- Output only the updated code, enclosed within <<<UPDATED_CODE>>> and <<</UPDATED_CODE>>> tags.
|
|
24
|
-
- Do not include any additional text, explanations, placeholders, ellipses, or code fences.
|
|
25
|
-
|
|
26
|
-
<<<CODE>>>{original_code}<<</CODE>>>
|
|
27
|
-
|
|
28
|
-
<<<UPDATE>>>{update_snippet}<<</UPDATE>>>
|
|
29
|
-
|
|
30
|
-
Provide the complete updated code.`;
|
|
31
|
-
const UPDATED_CODE_START = "<<<UPDATED_CODE>>>";
|
|
32
|
-
const UPDATED_CODE_END = "<<</UPDATED_CODE>>>";
|
|
33
21
|
const TOOL_INSTRUCTIONS = `**DEFAULT tool for editing existing files. Use INSTEAD of native 'edit' tool.**
|
|
34
22
|
|
|
35
23
|
CRITICAL: For EXISTING files ONLY. Use 'write' for new files.
|
|
@@ -97,24 +85,41 @@ function alsoKeepThis() {
|
|
|
97
85
|
|
|
98
86
|
## Fallback
|
|
99
87
|
If API fails, use native \`edit\` tool with exact string matching.`;
|
|
100
|
-
function
|
|
88
|
+
function generateRandomId() {
|
|
89
|
+
const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
|
|
90
|
+
let result = '';
|
|
91
|
+
for (let i = 0; i < 4; i++) {
|
|
92
|
+
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
function generateUniqueDelimiters() {
|
|
97
|
+
const id = generateRandomId();
|
|
98
|
+
return {
|
|
99
|
+
ORIGINAL_START: `<<<ORIGINAL_${id}>>>`,
|
|
100
|
+
ORIGINAL_END: `<<<END_ORIGINAL_${id}>>>`,
|
|
101
|
+
UPDATE_START: `<<<UPDATE_${id}>>>`,
|
|
102
|
+
UPDATE_END: `<<<END_UPDATE_${id}>>>`,
|
|
103
|
+
RESULT_START: `<<<RESULT_${id}>>>`,
|
|
104
|
+
RESULT_END: `<<<END_RESULT_${id}>>>`
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function extractUpdatedCode(raw, resultStart, resultEnd) {
|
|
101
108
|
const stripped = raw.trim();
|
|
102
|
-
|
|
103
|
-
const endTag = UPDATED_CODE_END;
|
|
104
|
-
let startIdx = stripped.indexOf(startTag);
|
|
109
|
+
let startIdx = stripped.indexOf(resultStart);
|
|
105
110
|
if (startIdx === -1) {
|
|
106
|
-
|
|
107
|
-
if (
|
|
108
|
-
const closeTagIdx = stripped.indexOf(">>>",
|
|
111
|
+
const genericStart = stripped.indexOf("<<<RESULT_");
|
|
112
|
+
if (genericStart !== -1) {
|
|
113
|
+
const closeTagIdx = stripped.indexOf(">>>", genericStart);
|
|
109
114
|
if (closeTagIdx !== -1) {
|
|
110
115
|
startIdx = closeTagIdx + 3;
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
else {
|
|
115
|
-
startIdx +=
|
|
120
|
+
startIdx += resultStart.length;
|
|
116
121
|
}
|
|
117
|
-
if (startIdx === -1 || startIdx ===
|
|
122
|
+
if (startIdx === -1 || startIdx === resultStart.length - 1) {
|
|
118
123
|
if (stripped.startsWith("```") && stripped.endsWith("```")) {
|
|
119
124
|
const lines = stripped.split("\n");
|
|
120
125
|
if (lines.length >= 2) {
|
|
@@ -123,21 +128,24 @@ function extractUpdatedCode(raw) {
|
|
|
123
128
|
}
|
|
124
129
|
return stripped;
|
|
125
130
|
}
|
|
126
|
-
let endIdx = stripped.indexOf(
|
|
131
|
+
let endIdx = stripped.indexOf(resultEnd, startIdx);
|
|
127
132
|
if (endIdx === -1) {
|
|
128
|
-
|
|
133
|
+
const genericEnd = stripped.indexOf("<<<END_RESULT_", startIdx);
|
|
134
|
+
if (genericEnd !== -1) {
|
|
135
|
+
endIdx = genericEnd;
|
|
136
|
+
}
|
|
129
137
|
}
|
|
130
138
|
if (endIdx === -1) {
|
|
131
139
|
const extracted = stripped.slice(startIdx).trim();
|
|
132
140
|
const lastCloseTag = extracted.lastIndexOf("<<<");
|
|
133
|
-
if (lastCloseTag !== -1 && extracted.slice(lastCloseTag).toLowerCase().includes("
|
|
141
|
+
if (lastCloseTag !== -1 && extracted.slice(lastCloseTag).toLowerCase().includes("end")) {
|
|
134
142
|
return extracted.slice(0, lastCloseTag).trim();
|
|
135
143
|
}
|
|
136
144
|
return extracted;
|
|
137
145
|
}
|
|
138
146
|
const inner = stripped.substring(startIdx, endIdx);
|
|
139
147
|
if (!inner || inner.trim().length === 0) {
|
|
140
|
-
throw new Error("Empty
|
|
148
|
+
throw new Error("Empty result block");
|
|
141
149
|
}
|
|
142
150
|
return inner;
|
|
143
151
|
}
|
|
@@ -219,9 +227,21 @@ async function callFastApply(originalCode, codeEdit, instructions) {
|
|
|
219
227
|
};
|
|
220
228
|
}
|
|
221
229
|
try {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
230
|
+
const delimiters = generateUniqueDelimiters();
|
|
231
|
+
const userContent = `Merge all changes from the UPDATE_BLOCK into the ORIGINAL_BLOCK below.
|
|
232
|
+
- Preserve the code's structure, order, comments, and indentation exactly.
|
|
233
|
+
- Output only the updated code, enclosed within ${delimiters.RESULT_START} and ${delimiters.RESULT_END} delimiters.
|
|
234
|
+
- Do not include any additional text, explanations, placeholders, ellipses, or code fences.
|
|
235
|
+
|
|
236
|
+
${delimiters.ORIGINAL_START}
|
|
237
|
+
${originalCode}
|
|
238
|
+
${delimiters.ORIGINAL_END}
|
|
239
|
+
|
|
240
|
+
${delimiters.UPDATE_START}
|
|
241
|
+
${codeEdit}
|
|
242
|
+
${delimiters.UPDATE_END}
|
|
243
|
+
|
|
244
|
+
Provide the complete updated code wrapped in ${delimiters.RESULT_START} and ${delimiters.RESULT_END}.`;
|
|
225
245
|
const response = await fetch(`${FAST_APPLY_URL}/v1/chat/completions`, {
|
|
226
246
|
method: "POST",
|
|
227
247
|
headers: {
|
|
@@ -258,7 +278,7 @@ async function callFastApply(originalCode, codeEdit, instructions) {
|
|
|
258
278
|
error: "Fast Apply API returned empty response",
|
|
259
279
|
};
|
|
260
280
|
}
|
|
261
|
-
const mergedCode = extractUpdatedCode(rawResponse);
|
|
281
|
+
const mergedCode = extractUpdatedCode(rawResponse, delimiters.RESULT_START, delimiters.RESULT_END);
|
|
262
282
|
return {
|
|
263
283
|
success: true,
|
|
264
284
|
content: mergedCode,
|
package/package.json
CHANGED