opencode-fast-apply 2.1.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/README.md +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +73 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,21 +34,21 @@ Or add to your OpenCode config to auto-install:
|
|
|
34
34
|
|
|
35
35
|
For **LM Studio** (default):
|
|
36
36
|
```bash
|
|
37
|
-
export FAST_APPLY_URL="http://localhost:1234
|
|
37
|
+
export FAST_APPLY_URL="http://localhost:1234"
|
|
38
38
|
export FAST_APPLY_MODEL="fastapply-1.5b"
|
|
39
39
|
export FAST_APPLY_API_KEY="optional-api-key"
|
|
40
40
|
```
|
|
41
41
|
|
|
42
42
|
For **Ollama**:
|
|
43
43
|
```bash
|
|
44
|
-
export FAST_APPLY_URL="http://localhost:11434
|
|
44
|
+
export FAST_APPLY_URL="http://localhost:11434"
|
|
45
45
|
export FAST_APPLY_MODEL="codellama:7b"
|
|
46
46
|
export FAST_APPLY_API_KEY="optional-api-key"
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
For **OpenAI**:
|
|
50
50
|
```bash
|
|
51
|
-
export FAST_APPLY_URL="https://api.openai.com
|
|
51
|
+
export FAST_APPLY_URL="https://api.openai.com"
|
|
52
52
|
export FAST_APPLY_MODEL="gpt-4"
|
|
53
53
|
export FAST_APPLY_API_KEY="sk-your-openai-key"
|
|
54
54
|
```
|
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,17 @@ 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 FAST_APPLY_SYSTEM_PROMPT = "
|
|
22
|
-
const FAST_APPLY_USER_PROMPT = `
|
|
21
|
+
const FAST_APPLY_SYSTEM_PROMPT = "You are a coding assistant that helps merge code updates, ensuring every modification is fully integrated.";
|
|
22
|
+
const FAST_APPLY_USER_PROMPT = `Merge all changes from the <update> snippet into the <code> below.
|
|
23
|
+
- Preserve the code's structure, order, comments, and indentation exactly.
|
|
24
|
+
- Output only the updated code, enclosed within <updated-code> and </updated-code> tags.
|
|
25
|
+
- Do not include any additional text, explanations, placeholders, ellipses, or code fences.
|
|
23
26
|
|
|
24
27
|
<code>{original_code}</code>
|
|
28
|
+
|
|
25
29
|
<update>{update_snippet}</update>
|
|
26
30
|
|
|
27
|
-
|
|
31
|
+
Provide the complete updated code.`;
|
|
28
32
|
const UPDATED_CODE_START = "<updated-code>";
|
|
29
33
|
const UPDATED_CODE_END = "</updated-code>";
|
|
30
34
|
const TOOL_INSTRUCTIONS = `**DEFAULT tool for editing existing files. Use INSTEAD of native 'edit' tool.**
|
|
@@ -106,9 +110,22 @@ function unescapeXmlTags(text) {
|
|
|
106
110
|
}
|
|
107
111
|
function extractUpdatedCode(raw) {
|
|
108
112
|
const stripped = raw.trim();
|
|
109
|
-
const
|
|
110
|
-
const
|
|
111
|
-
|
|
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) {
|
|
112
129
|
if (stripped.startsWith("```") && stripped.endsWith("```")) {
|
|
113
130
|
const lines = stripped.split("\n");
|
|
114
131
|
if (lines.length >= 2) {
|
|
@@ -117,7 +134,19 @@ function extractUpdatedCode(raw) {
|
|
|
117
134
|
}
|
|
118
135
|
return unescapeXmlTags(stripped);
|
|
119
136
|
}
|
|
120
|
-
|
|
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);
|
|
121
150
|
if (!inner || inner.trim().length === 0) {
|
|
122
151
|
throw new Error("Empty updated-code block");
|
|
123
152
|
}
|
|
@@ -173,19 +202,14 @@ function estimateTokens(text) {
|
|
|
173
202
|
function formatFastApplyResult(filePath, workingDir, insertions, deletions, diffPreview, modifiedTokens) {
|
|
174
203
|
const shortPath = shortenPath(filePath, workingDir);
|
|
175
204
|
const tokenStr = formatTokenCount(modifiedTokens);
|
|
176
|
-
const diffLines = diffPreview.split("\n");
|
|
177
|
-
const previewLines = diffLines.slice(0, 10);
|
|
178
|
-
const truncatedDiff = previewLines.join("\n");
|
|
179
|
-
const hasMore = diffLines.length > 10;
|
|
180
205
|
const lines = [
|
|
181
206
|
"✓ Fast Apply complete",
|
|
182
207
|
"",
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
` +${insertions} lines, -${deletions} lines (~${tokenStr} tokens modified)`,
|
|
208
|
+
`File: ${shortPath}`,
|
|
209
|
+
`Changes: +${insertions} -${deletions} (~${tokenStr} tokens)`,
|
|
186
210
|
"",
|
|
187
|
-
"
|
|
188
|
-
|
|
211
|
+
"Unified diff:",
|
|
212
|
+
diffPreview
|
|
189
213
|
];
|
|
190
214
|
return lines.join("\n");
|
|
191
215
|
}
|
|
@@ -194,8 +218,8 @@ function formatErrorOutput(error, filePath, workingDir) {
|
|
|
194
218
|
return [
|
|
195
219
|
"✗ Fast Apply failed",
|
|
196
220
|
"",
|
|
197
|
-
|
|
198
|
-
`
|
|
221
|
+
`File: ${shortPath}`,
|
|
222
|
+
`Error: ${error}`,
|
|
199
223
|
"",
|
|
200
224
|
"Fallback: Use native 'edit' tool with exact string matching"
|
|
201
225
|
].join("\n");
|
|
@@ -216,7 +240,6 @@ async function callFastApply(originalCode, codeEdit, instructions) {
|
|
|
216
240
|
const escapedOriginalCode = escapeXmlTags(originalCode);
|
|
217
241
|
const escapedCodeEdit = escapeXmlTags(codeEdit);
|
|
218
242
|
const userContent = FAST_APPLY_USER_PROMPT
|
|
219
|
-
.replace("{instruction}", instructions || "Apply the requested code changes.")
|
|
220
243
|
.replace("{original_code}", escapedOriginalCode)
|
|
221
244
|
.replace("{update_snippet}", escapedCodeEdit);
|
|
222
245
|
const response = await fetch(`${FAST_APPLY_URL}/v1/chat/completions`, {
|
|
@@ -279,7 +302,35 @@ async function callFastApply(originalCode, codeEdit, instructions) {
|
|
|
279
302
|
};
|
|
280
303
|
}
|
|
281
304
|
}
|
|
282
|
-
|
|
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 }) => {
|
|
283
334
|
if (!FAST_APPLY_API_KEY) {
|
|
284
335
|
console.warn("[fast-apply] FAST_APPLY_API_KEY not set - fast_apply_edit tool will be disabled");
|
|
285
336
|
}
|
|
@@ -301,7 +352,7 @@ export const FastApplyPlugin = async ({ directory }) => {
|
|
|
301
352
|
.string()
|
|
302
353
|
.describe('The code changes with "// ... existing code ..." markers for unchanged sections'),
|
|
303
354
|
},
|
|
304
|
-
async execute(args) {
|
|
355
|
+
async execute(args, toolCtx) {
|
|
305
356
|
const { target_filepath, instructions, code_edit } = args;
|
|
306
357
|
// Resolve file path relative to project directory
|
|
307
358
|
const filepath = target_filepath.startsWith("/")
|
|
@@ -354,6 +405,7 @@ write({
|
|
|
354
405
|
const diff = generateUnifiedDiff(target_filepath, originalCode, mergedCode);
|
|
355
406
|
const { added, removed } = countChanges(diff);
|
|
356
407
|
const modifiedTokens = estimateTokens(diff);
|
|
408
|
+
await sendTUINotification(client, toolCtx.sessionID, target_filepath, directory, added, removed, modifiedTokens);
|
|
357
409
|
return formatFastApplyResult(target_filepath, directory, added, removed, diff, modifiedTokens);
|
|
358
410
|
},
|
|
359
411
|
}),
|
package/package.json
CHANGED