opencode-gbk-tools 0.1.10 → 0.1.12
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 +38 -1
- package/dist/opencode-tools/gbk_edit.js +157 -13
- package/dist/opencode-tools/text_edit.js +238 -38
- package/dist/opencode-tools/text_write.js +8 -0
- package/dist/plugin/index.js +391 -51
- package/dist/plugins/opencode-gbk-tools.js +2 -0
- package/dist/release-manifest.json +5 -5
- package/package.json +1 -1
package/dist/plugin/index.js
CHANGED
|
@@ -16306,6 +16306,82 @@ async function assertPathAllowed(filePath, context, allowExternal = false) {
|
|
|
16306
16306
|
|
|
16307
16307
|
// src/lib/gbk-file.ts
|
|
16308
16308
|
var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16309
|
+
function assertStringArgument(value, name) {
|
|
16310
|
+
if (typeof value !== "string") {
|
|
16311
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
16312
|
+
}
|
|
16313
|
+
}
|
|
16314
|
+
function assertReplaceArguments(input) {
|
|
16315
|
+
assertStringArgument(input.oldString, "oldString");
|
|
16316
|
+
assertStringArgument(input.newString, "newString");
|
|
16317
|
+
}
|
|
16318
|
+
function assertInsertArguments(input) {
|
|
16319
|
+
assertStringArgument(input.anchor, "anchor");
|
|
16320
|
+
assertStringArgument(input.content, "content");
|
|
16321
|
+
if (input.content.length === 0) {
|
|
16322
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16323
|
+
}
|
|
16324
|
+
if (input.replaceAll !== void 0) {
|
|
16325
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 replaceAll");
|
|
16326
|
+
}
|
|
16327
|
+
if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
|
|
16328
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
|
|
16329
|
+
}
|
|
16330
|
+
}
|
|
16331
|
+
function findOccurrenceIndex(text, token, occurrence) {
|
|
16332
|
+
assertStringArgument(text, "text");
|
|
16333
|
+
assertStringArgument(token, "anchor");
|
|
16334
|
+
assertPositiveInteger(occurrence, "occurrence");
|
|
16335
|
+
if (token.length === 0) {
|
|
16336
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", "anchor \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16337
|
+
}
|
|
16338
|
+
let count = 0;
|
|
16339
|
+
let searchFrom = 0;
|
|
16340
|
+
while (true) {
|
|
16341
|
+
const index = text.indexOf(token, searchFrom);
|
|
16342
|
+
if (index === -1) {
|
|
16343
|
+
break;
|
|
16344
|
+
}
|
|
16345
|
+
count += 1;
|
|
16346
|
+
if (count === occurrence) {
|
|
16347
|
+
return { index, total: countOccurrences(text, token) };
|
|
16348
|
+
}
|
|
16349
|
+
searchFrom = index + token.length;
|
|
16350
|
+
}
|
|
16351
|
+
if (count === 0) {
|
|
16352
|
+
throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\u70B9: ${token}`);
|
|
16353
|
+
}
|
|
16354
|
+
throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\u5230 ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\u7B2C ${occurrence} \u5904`);
|
|
16355
|
+
}
|
|
16356
|
+
function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
|
|
16357
|
+
const alignedContent = newlineStyle === "crlf" ? content.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") : newlineStyle === "lf" ? content.replace(/\r\n/g, "\n") : content;
|
|
16358
|
+
const located = findOccurrenceIndex(text, anchor, occurrence);
|
|
16359
|
+
const insertionPoint = mode === "insertAfter" ? located.index + anchor.length : located.index;
|
|
16360
|
+
const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
|
|
16361
|
+
if (alreadyExists) {
|
|
16362
|
+
if (ifExists === "error") {
|
|
16363
|
+
throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\u5BB9");
|
|
16364
|
+
}
|
|
16365
|
+
if (ifExists === "skip") {
|
|
16366
|
+
return {
|
|
16367
|
+
outputText: text,
|
|
16368
|
+
inserted: false,
|
|
16369
|
+
skipped: true,
|
|
16370
|
+
anchorMatches: located.total,
|
|
16371
|
+
occurrence,
|
|
16372
|
+
anchor
|
|
16373
|
+
};
|
|
16374
|
+
}
|
|
16375
|
+
}
|
|
16376
|
+
return {
|
|
16377
|
+
outputText: `${text.slice(0, insertionPoint)}${alignedContent}${text.slice(insertionPoint)}`,
|
|
16378
|
+
inserted: true,
|
|
16379
|
+
skipped: false,
|
|
16380
|
+
anchorMatches: located.total,
|
|
16381
|
+
occurrence,
|
|
16382
|
+
anchor
|
|
16383
|
+
};
|
|
16384
|
+
}
|
|
16309
16385
|
function assertEncodingSupported(encoding) {
|
|
16310
16386
|
if (encoding !== "gbk" && encoding !== "gb18030") {
|
|
16311
16387
|
throw createGbkError("GBK_INVALID_ENCODING", `\u4E0D\u652F\u6301\u7684\u7F16\u7801: ${encoding}`);
|
|
@@ -16475,9 +16551,11 @@ function resolveEditScope(text, input) {
|
|
|
16475
16551
|
};
|
|
16476
16552
|
}
|
|
16477
16553
|
function normalizeNewlines(text) {
|
|
16554
|
+
assertStringArgument(text, "text");
|
|
16478
16555
|
return text.replace(/\r\n/g, "\n");
|
|
16479
16556
|
}
|
|
16480
16557
|
function trimRightSpaces(text) {
|
|
16558
|
+
assertStringArgument(text, "text");
|
|
16481
16559
|
return text.replace(/[ \t]+$/g, "");
|
|
16482
16560
|
}
|
|
16483
16561
|
function splitNormalizedLines(text) {
|
|
@@ -16573,6 +16651,8 @@ function tryLooseBlockReplace(content, oldString, newString) {
|
|
|
16573
16651
|
return null;
|
|
16574
16652
|
}
|
|
16575
16653
|
function countOccurrences(text, target) {
|
|
16654
|
+
assertStringArgument(text, "text");
|
|
16655
|
+
assertStringArgument(target, "oldString");
|
|
16576
16656
|
if (target.length === 0) {
|
|
16577
16657
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16578
16658
|
}
|
|
@@ -16888,11 +16968,56 @@ async function readGbkFile(input) {
|
|
|
16888
16968
|
};
|
|
16889
16969
|
}
|
|
16890
16970
|
async function replaceGbkFileText(input) {
|
|
16971
|
+
const mode = input.mode ?? "replace";
|
|
16891
16972
|
const normalizedInput = {
|
|
16892
16973
|
...input,
|
|
16893
16974
|
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
16894
16975
|
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
16895
16976
|
};
|
|
16977
|
+
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
16978
|
+
assertInsertArguments(input);
|
|
16979
|
+
const resolved2 = await resolveReadableGbkFile(normalizedInput);
|
|
16980
|
+
const current2 = await readWholeGbkTextFile(resolved2);
|
|
16981
|
+
const occurrence = normalizeOptionalPositiveInteger(input.occurrence, "occurrence") ?? 1;
|
|
16982
|
+
const insertResult = insertByAnchor(
|
|
16983
|
+
current2.content,
|
|
16984
|
+
mode,
|
|
16985
|
+
input.anchor,
|
|
16986
|
+
input.content,
|
|
16987
|
+
occurrence,
|
|
16988
|
+
input.ifExists ?? "skip",
|
|
16989
|
+
detectNewlineStyle(current2.content)
|
|
16990
|
+
);
|
|
16991
|
+
if (insertResult.skipped) {
|
|
16992
|
+
return {
|
|
16993
|
+
mode,
|
|
16994
|
+
filePath: current2.filePath,
|
|
16995
|
+
encoding: current2.encoding,
|
|
16996
|
+
anchor: insertResult.anchor,
|
|
16997
|
+
occurrence: insertResult.occurrence,
|
|
16998
|
+
anchorMatches: insertResult.anchorMatches,
|
|
16999
|
+
inserted: false,
|
|
17000
|
+
skipped: true,
|
|
17001
|
+
bytesRead: current2.bytesRead,
|
|
17002
|
+
bytesWritten: 0
|
|
17003
|
+
};
|
|
17004
|
+
}
|
|
17005
|
+
const buffer2 = import_iconv_lite.default.encode(insertResult.outputText, current2.encoding);
|
|
17006
|
+
await fs2.writeFile(current2.filePath, buffer2);
|
|
17007
|
+
return {
|
|
17008
|
+
mode,
|
|
17009
|
+
filePath: current2.filePath,
|
|
17010
|
+
encoding: current2.encoding,
|
|
17011
|
+
anchor: insertResult.anchor,
|
|
17012
|
+
occurrence: insertResult.occurrence,
|
|
17013
|
+
anchorMatches: insertResult.anchorMatches,
|
|
17014
|
+
inserted: true,
|
|
17015
|
+
skipped: false,
|
|
17016
|
+
bytesRead: current2.bytesRead,
|
|
17017
|
+
bytesWritten: buffer2.byteLength
|
|
17018
|
+
};
|
|
17019
|
+
}
|
|
17020
|
+
assertReplaceArguments(input);
|
|
16896
17021
|
if (input.oldString.length === 0) {
|
|
16897
17022
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
16898
17023
|
}
|
|
@@ -16909,14 +17034,15 @@ async function replaceGbkFileText(input) {
|
|
|
16909
17034
|
}
|
|
16910
17035
|
const current = await readWholeGbkTextFile(resolved);
|
|
16911
17036
|
const scope = resolveEditScope(current.content, normalizedInput);
|
|
16912
|
-
const occurrencesBefore = countOccurrences(scope.selectedText,
|
|
17037
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, input.oldString);
|
|
16913
17038
|
if (!replaceAll && occurrencesBefore === 0) {
|
|
16914
|
-
const loose = tryLooseBlockReplace(scope.selectedText,
|
|
17039
|
+
const loose = tryLooseBlockReplace(scope.selectedText, input.oldString, input.newString);
|
|
16915
17040
|
if (loose !== null) {
|
|
16916
17041
|
const outputText2 = `${current.content.slice(0, scope.rangeStart)}${loose.content}${current.content.slice(scope.rangeEnd)}`;
|
|
16917
17042
|
const buffer2 = import_iconv_lite.default.encode(outputText2, current.encoding);
|
|
16918
17043
|
await fs2.writeFile(current.filePath, buffer2);
|
|
16919
17044
|
return {
|
|
17045
|
+
mode: "replace",
|
|
16920
17046
|
filePath: current.filePath,
|
|
16921
17047
|
encoding: current.encoding,
|
|
16922
17048
|
replacements: 1,
|
|
@@ -16928,20 +17054,21 @@ async function replaceGbkFileText(input) {
|
|
|
16928
17054
|
}
|
|
16929
17055
|
if (replaceAll) {
|
|
16930
17056
|
if (occurrencesBefore === 0) {
|
|
16931
|
-
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText,
|
|
17057
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, input.oldString));
|
|
16932
17058
|
}
|
|
16933
17059
|
} else if (occurrencesBefore === 0) {
|
|
16934
|
-
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText,
|
|
17060
|
+
throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, input.oldString));
|
|
16935
17061
|
} else if (occurrencesBefore > 1) {
|
|
16936
|
-
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${
|
|
17062
|
+
throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${input.oldString}`);
|
|
16937
17063
|
}
|
|
16938
17064
|
const fileNewlineStyle = detectNewlineStyle(current.content);
|
|
16939
|
-
const alignedNewString = fileNewlineStyle === "crlf" ?
|
|
16940
|
-
const replaced = replaceAll ? scope.selectedText.split(
|
|
17065
|
+
const alignedNewString = fileNewlineStyle === "crlf" ? input.newString.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") : fileNewlineStyle === "lf" ? input.newString.replace(/\r\n/g, "\n") : input.newString;
|
|
17066
|
+
const replaced = replaceAll ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
16941
17067
|
const outputText = `${current.content.slice(0, scope.rangeStart)}${replaced}${current.content.slice(scope.rangeEnd)}`;
|
|
16942
17068
|
const buffer = import_iconv_lite.default.encode(outputText, current.encoding);
|
|
16943
17069
|
await fs2.writeFile(current.filePath, buffer);
|
|
16944
17070
|
return {
|
|
17071
|
+
mode: "replace",
|
|
16945
17072
|
filePath: current.filePath,
|
|
16946
17073
|
encoding: current.encoding,
|
|
16947
17074
|
replacements: replaceAll ? occurrencesBefore : 1,
|
|
@@ -17105,11 +17232,20 @@ Recommended workflow for large files (when gbk_read returned truncated=true):
|
|
|
17105
17232
|
3. gbk_edit(oldString=<content without prefixes>, newString=<new content>)
|
|
17106
17233
|
|
|
17107
17234
|
For large files, use 'startLine'/'endLine' or 'startAnchor'/'endAnchor' to narrow the search scope
|
|
17108
|
-
and avoid false matches. Scoped edits also improve performance on very large files
|
|
17235
|
+
and avoid false matches. Scoped edits also improve performance on very large files.
|
|
17236
|
+
|
|
17237
|
+
Insert mode:
|
|
17238
|
+
- Use mode=insertAfter or mode=insertBefore with anchor/content
|
|
17239
|
+
- This is recommended when the intent is "insert after label" instead of exact replacement.`,
|
|
17109
17240
|
args: {
|
|
17110
17241
|
filePath: tool.schema.string().describe("Target file path"),
|
|
17111
|
-
|
|
17112
|
-
|
|
17242
|
+
mode: tool.schema.enum(["replace", "insertAfter", "insertBefore"]).optional().describe("Edit mode, default replace"),
|
|
17243
|
+
oldString: tool.schema.string().optional().describe("Exact text to replace in replace mode"),
|
|
17244
|
+
newString: tool.schema.string().optional().describe("Replacement text in replace mode"),
|
|
17245
|
+
anchor: tool.schema.string().optional().describe("Anchor text used by insertAfter/insertBefore"),
|
|
17246
|
+
content: tool.schema.string().optional().describe("Inserted content used by insertAfter/insertBefore"),
|
|
17247
|
+
occurrence: tool.schema.number().int().positive().optional().describe("1-based anchor occurrence for insert mode"),
|
|
17248
|
+
ifExists: tool.schema.enum(["allow", "skip", "error"]).optional().describe("What to do when inserted content already exists at target position"),
|
|
17113
17249
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences (default: false, requires unique match)"),
|
|
17114
17250
|
startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based start line (inclusive)"),
|
|
17115
17251
|
endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based end line (inclusive)"),
|
|
@@ -17120,13 +17256,21 @@ and avoid false matches. Scoped edits also improve performance on very large fil
|
|
|
17120
17256
|
},
|
|
17121
17257
|
async execute(args, context) {
|
|
17122
17258
|
const result = await replaceGbkFileText({ ...args, context });
|
|
17259
|
+
const isReplace = !("mode" in result) || result.mode === "replace";
|
|
17260
|
+
const title = isReplace ? `GBK \u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}` : result.inserted ? `GBK \u63D2\u5165 ${result.encoding.toUpperCase()} #${result.occurrence}` : `GBK \u8DF3\u8FC7 ${result.encoding.toUpperCase()} #${result.occurrence}`;
|
|
17123
17261
|
context.metadata({
|
|
17124
|
-
title
|
|
17262
|
+
title,
|
|
17125
17263
|
metadata: {
|
|
17126
17264
|
filePath: result.filePath,
|
|
17127
17265
|
encoding: result.encoding,
|
|
17128
|
-
|
|
17129
|
-
|
|
17266
|
+
mode: isReplace ? "replace" : result.mode,
|
|
17267
|
+
replacements: isReplace ? result.replacements : void 0,
|
|
17268
|
+
occurrencesBefore: isReplace ? result.occurrencesBefore : void 0,
|
|
17269
|
+
anchor: isReplace ? void 0 : result.anchor,
|
|
17270
|
+
occurrence: isReplace ? void 0 : result.occurrence,
|
|
17271
|
+
anchorMatches: isReplace ? void 0 : result.anchorMatches,
|
|
17272
|
+
inserted: isReplace ? void 0 : result.inserted,
|
|
17273
|
+
skipped: isReplace ? void 0 : result.skipped,
|
|
17130
17274
|
bytesRead: result.bytesRead,
|
|
17131
17275
|
bytesWritten: result.bytesWritten
|
|
17132
17276
|
}
|
|
@@ -17265,6 +17409,8 @@ var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
|
17265
17409
|
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
17266
17410
|
"- \u5904\u7406\u6587\u672C\u6587\u4EF6\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 text_read\u3001text_write\u3001text_edit\u3002",
|
|
17267
17411
|
"- text_* \u9ED8\u8BA4\u4F1A\u81EA\u52A8\u8BC6\u522B\u73B0\u6709\u6587\u4EF6\u7F16\u7801\uFF0C\u5E76\u5728\u4FEE\u6539\u65F6\u5C3D\u91CF\u4FDD\u6301\u539F\u7F16\u7801\u3001BOM \u548C\u6362\u884C\u98CE\u683C\u3002",
|
|
17412
|
+
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
17413
|
+
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
17268
17414
|
"- \u82E5\u68C0\u6D4B\u7F6E\u4FE1\u5EA6\u4E0D\u8DB3\u6216\u51FA\u73B0 TEXT_UNKNOWN_ENCODING\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding \u540E\u91CD\u8BD5\u3002",
|
|
17269
17415
|
"- \u5904\u7406\u660E\u786E\u7684 GBK/GB18030 \u6587\u4EF6\u65F6\uFF0C\u4E5F\u53EF\u7EE7\u7EED\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\u3002"
|
|
17270
17416
|
].join("\n");
|
|
@@ -17281,6 +17427,25 @@ import fs3 from "fs/promises";
|
|
|
17281
17427
|
import path3 from "path";
|
|
17282
17428
|
var TEXT_STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
17283
17429
|
var UTF8_DECODER = new TextDecoder("utf-8", { fatal: true });
|
|
17430
|
+
function assertStringArgument2(value, name) {
|
|
17431
|
+
if (typeof value !== "string") {
|
|
17432
|
+
throw createTextError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u5B57\u7B26\u4E32`);
|
|
17433
|
+
}
|
|
17434
|
+
}
|
|
17435
|
+
function assertReplaceArguments2(input) {
|
|
17436
|
+
assertStringArgument2(input.oldString, "oldString");
|
|
17437
|
+
assertStringArgument2(input.newString, "newString");
|
|
17438
|
+
}
|
|
17439
|
+
function resolveEditMode(mode) {
|
|
17440
|
+
return mode ?? "replace";
|
|
17441
|
+
}
|
|
17442
|
+
function resolveInsertIfExists(value) {
|
|
17443
|
+
return value ?? "skip";
|
|
17444
|
+
}
|
|
17445
|
+
function resolveExplicitTextEncoding(value, fallback) {
|
|
17446
|
+
const requested = value ?? "auto";
|
|
17447
|
+
return requested === "auto" ? fallback : requested;
|
|
17448
|
+
}
|
|
17284
17449
|
function isSupportedEncoding(value) {
|
|
17285
17450
|
return value === "utf8" || value === "utf8-bom" || value === "utf16le" || value === "utf16be" || value === "gbk" || value === "gb18030";
|
|
17286
17451
|
}
|
|
@@ -17399,6 +17564,8 @@ function assertLikelyTextBuffer(buffer) {
|
|
|
17399
17564
|
throw createTextError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309\u6587\u672C\u5904\u7406");
|
|
17400
17565
|
}
|
|
17401
17566
|
function getNearestContext2(content, oldString) {
|
|
17567
|
+
assertStringArgument2(content, "content");
|
|
17568
|
+
assertStringArgument2(oldString, "oldString");
|
|
17402
17569
|
const lines = content.split(/\r?\n/);
|
|
17403
17570
|
const oldLines = oldString.replace(/\r\n/g, "\n").split("\n").filter(Boolean);
|
|
17404
17571
|
const firstToken = oldLines[0] || oldString.trim();
|
|
@@ -17410,6 +17577,8 @@ function getNearestContext2(content, oldString) {
|
|
|
17410
17577
|
return lines.slice(0, 4).join("\n");
|
|
17411
17578
|
}
|
|
17412
17579
|
function buildNoMatchMessage2(content, oldString) {
|
|
17580
|
+
assertStringArgument2(content, "content");
|
|
17581
|
+
assertStringArgument2(oldString, "oldString");
|
|
17413
17582
|
return [
|
|
17414
17583
|
"\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
|
|
17415
17584
|
"oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\u3002",
|
|
@@ -17417,6 +17586,105 @@ function buildNoMatchMessage2(content, oldString) {
|
|
|
17417
17586
|
getNearestContext2(content, oldString)
|
|
17418
17587
|
].join("\n");
|
|
17419
17588
|
}
|
|
17589
|
+
function findOccurrenceIndex2(text, token, occurrence) {
|
|
17590
|
+
assertStringArgument2(text, "text");
|
|
17591
|
+
assertStringArgument2(token, "anchor");
|
|
17592
|
+
assertPositiveInteger(occurrence, "occurrence");
|
|
17593
|
+
if (token.length === 0) {
|
|
17594
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "anchor \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17595
|
+
}
|
|
17596
|
+
let count = 0;
|
|
17597
|
+
let searchFrom = 0;
|
|
17598
|
+
while (true) {
|
|
17599
|
+
const index = text.indexOf(token, searchFrom);
|
|
17600
|
+
if (index === -1) {
|
|
17601
|
+
break;
|
|
17602
|
+
}
|
|
17603
|
+
count += 1;
|
|
17604
|
+
if (count === occurrence) {
|
|
17605
|
+
return { index, total: countOccurrences(text, token) };
|
|
17606
|
+
}
|
|
17607
|
+
searchFrom = index + token.length;
|
|
17608
|
+
}
|
|
17609
|
+
if (count === 0) {
|
|
17610
|
+
throw createTextError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\u70B9: ${token}`);
|
|
17611
|
+
}
|
|
17612
|
+
throw createTextError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\u5230 ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\u7B2C ${occurrence} \u5904`);
|
|
17613
|
+
}
|
|
17614
|
+
function assertInsertArguments2(input) {
|
|
17615
|
+
assertStringArgument2(input.anchor, "anchor");
|
|
17616
|
+
assertStringArgument2(input.content, "content");
|
|
17617
|
+
if (input.content.length === 0) {
|
|
17618
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17619
|
+
}
|
|
17620
|
+
if (input.replaceAll !== void 0) {
|
|
17621
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 replaceAll");
|
|
17622
|
+
}
|
|
17623
|
+
if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
|
|
17624
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
|
|
17625
|
+
}
|
|
17626
|
+
}
|
|
17627
|
+
function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
|
|
17628
|
+
const beforeLines = normalizeNewlines2(beforeText).split("\n");
|
|
17629
|
+
const afterLines = normalizeNewlines2(afterText).split("\n");
|
|
17630
|
+
const maxLines = Math.max(beforeLines.length, afterLines.length);
|
|
17631
|
+
const lines = [
|
|
17632
|
+
`--- ${path3.basename(filePath)} (${encoding})`,
|
|
17633
|
+
`+++ ${path3.basename(filePath)} (${encoding})`
|
|
17634
|
+
];
|
|
17635
|
+
for (let index = 0; index < maxLines; index += 1) {
|
|
17636
|
+
const before = beforeLines[index];
|
|
17637
|
+
const after = afterLines[index];
|
|
17638
|
+
if (before !== void 0 && after !== void 0 && before === after) {
|
|
17639
|
+
lines.push(` ${before}`);
|
|
17640
|
+
continue;
|
|
17641
|
+
}
|
|
17642
|
+
if (before !== void 0) {
|
|
17643
|
+
lines.push(`-${before}`);
|
|
17644
|
+
}
|
|
17645
|
+
if (after !== void 0) {
|
|
17646
|
+
lines.push(`+${after}`);
|
|
17647
|
+
}
|
|
17648
|
+
}
|
|
17649
|
+
return lines.join("\n");
|
|
17650
|
+
}
|
|
17651
|
+
function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
|
|
17652
|
+
const alignedContent = alignTextToNewlineStyle(content, newlineStyle);
|
|
17653
|
+
const located = findOccurrenceIndex2(text, anchor, occurrence);
|
|
17654
|
+
const insertionPoint = mode === "insertAfter" ? located.index + anchor.length : located.index;
|
|
17655
|
+
const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
|
|
17656
|
+
if (alreadyExists) {
|
|
17657
|
+
if (ifExists === "error") {
|
|
17658
|
+
throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\u5BB9");
|
|
17659
|
+
}
|
|
17660
|
+
if (ifExists === "skip") {
|
|
17661
|
+
return {
|
|
17662
|
+
outputText: text,
|
|
17663
|
+
inserted: false,
|
|
17664
|
+
skipped: true,
|
|
17665
|
+
anchorMatches: located.total,
|
|
17666
|
+
occurrence,
|
|
17667
|
+
anchor,
|
|
17668
|
+
previewBefore: text.slice(Math.max(0, located.index - 80), Math.min(text.length, located.index + anchor.length + 80)),
|
|
17669
|
+
previewAfter: text.slice(Math.max(0, located.index - 80), Math.min(text.length, located.index + anchor.length + 80))
|
|
17670
|
+
};
|
|
17671
|
+
}
|
|
17672
|
+
}
|
|
17673
|
+
const outputText = `${text.slice(0, insertionPoint)}${alignedContent}${text.slice(insertionPoint)}`;
|
|
17674
|
+
const previewStart = Math.max(0, insertionPoint - 80);
|
|
17675
|
+
const previewBeforeEnd = Math.min(text.length, insertionPoint + 80);
|
|
17676
|
+
const previewAfterEnd = Math.min(outputText.length, insertionPoint + alignedContent.length + 80);
|
|
17677
|
+
return {
|
|
17678
|
+
outputText,
|
|
17679
|
+
inserted: true,
|
|
17680
|
+
skipped: false,
|
|
17681
|
+
anchorMatches: located.total,
|
|
17682
|
+
occurrence,
|
|
17683
|
+
anchor,
|
|
17684
|
+
previewBefore: text.slice(previewStart, previewBeforeEnd),
|
|
17685
|
+
previewAfter: outputText.slice(previewStart, previewAfterEnd)
|
|
17686
|
+
};
|
|
17687
|
+
}
|
|
17420
17688
|
function detectTextEncodingFromBuffer(buffer, requestedEncoding = "auto") {
|
|
17421
17689
|
if (requestedEncoding !== "auto") {
|
|
17422
17690
|
assertTextEncodingSupported(requestedEncoding);
|
|
@@ -17607,6 +17875,7 @@ function resolveEditScope2(text, input) {
|
|
|
17607
17875
|
};
|
|
17608
17876
|
}
|
|
17609
17877
|
function alignTextToNewlineStyle(text, newlineStyle) {
|
|
17878
|
+
assertStringArgument2(text, "text");
|
|
17610
17879
|
const normalized = text.replace(/\r\n/g, "\n");
|
|
17611
17880
|
if (newlineStyle === "crlf") {
|
|
17612
17881
|
return normalized.replace(/\n/g, "\r\n");
|
|
@@ -17617,6 +17886,7 @@ function alignTextToNewlineStyle(text, newlineStyle) {
|
|
|
17617
17886
|
return text;
|
|
17618
17887
|
}
|
|
17619
17888
|
function ensureLossless(input, encoding, hasBom = encoding === "utf8-bom") {
|
|
17889
|
+
assertStringArgument2(input, "content");
|
|
17620
17890
|
const buffer = encodeText(input, encoding, hasBom);
|
|
17621
17891
|
const roundTrip = decodeText(buffer, encoding);
|
|
17622
17892
|
if (roundTrip !== input) {
|
|
@@ -17668,6 +17938,7 @@ async function readTextFile(input) {
|
|
|
17668
17938
|
};
|
|
17669
17939
|
}
|
|
17670
17940
|
async function writeTextFile(input) {
|
|
17941
|
+
assertStringArgument2(input.content, "content");
|
|
17671
17942
|
const requestedEncoding = input.encoding ?? "auto";
|
|
17672
17943
|
assertTextEncodingSupported(requestedEncoding);
|
|
17673
17944
|
const preserveEncoding = input.preserveEncoding ?? true;
|
|
@@ -17717,43 +17988,100 @@ async function writeTextFile(input) {
|
|
|
17717
17988
|
};
|
|
17718
17989
|
}
|
|
17719
17990
|
async function replaceTextFileText(input) {
|
|
17991
|
+
const mode = resolveEditMode(input.mode);
|
|
17720
17992
|
const normalizedInput = {
|
|
17721
17993
|
...input,
|
|
17722
17994
|
startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
|
|
17723
17995
|
endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
|
|
17724
17996
|
};
|
|
17725
|
-
if (input.oldString.length === 0) {
|
|
17726
|
-
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
17727
|
-
}
|
|
17728
17997
|
const loaded = await readWholeTextFile({
|
|
17729
17998
|
filePath: input.filePath,
|
|
17730
17999
|
encoding: input.encoding ?? "auto",
|
|
17731
18000
|
allowExternal: input.allowExternal,
|
|
17732
18001
|
context: input.context
|
|
17733
18002
|
});
|
|
18003
|
+
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
18004
|
+
assertInsertArguments2(normalizedInput);
|
|
18005
|
+
const occurrence = normalizeOptionalPositiveInteger(normalizedInput.occurrence, "occurrence") ?? 1;
|
|
18006
|
+
const insertResult = buildInsertOutput(
|
|
18007
|
+
loaded.content,
|
|
18008
|
+
mode,
|
|
18009
|
+
normalizedInput.anchor,
|
|
18010
|
+
normalizedInput.content,
|
|
18011
|
+
occurrence,
|
|
18012
|
+
resolveInsertIfExists(normalizedInput.ifExists),
|
|
18013
|
+
loaded.newlineStyle
|
|
18014
|
+
);
|
|
18015
|
+
if (insertResult.skipped) {
|
|
18016
|
+
return {
|
|
18017
|
+
mode,
|
|
18018
|
+
filePath: loaded.filePath,
|
|
18019
|
+
encoding: loaded.encoding,
|
|
18020
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
18021
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
18022
|
+
confidence: loaded.confidence,
|
|
18023
|
+
hasBom: loaded.hasBom,
|
|
18024
|
+
anchor: insertResult.anchor,
|
|
18025
|
+
occurrence: insertResult.occurrence,
|
|
18026
|
+
anchorMatches: insertResult.anchorMatches,
|
|
18027
|
+
inserted: false,
|
|
18028
|
+
skipped: true,
|
|
18029
|
+
bytesRead: loaded.bytesRead,
|
|
18030
|
+
bytesWritten: 0,
|
|
18031
|
+
newlineStyle: loaded.newlineStyle
|
|
18032
|
+
};
|
|
18033
|
+
}
|
|
18034
|
+
const targetEncoding2 = (normalizedInput.preserveEncoding ?? true) || (normalizedInput.encoding ?? "auto") === "auto" ? loaded.encoding : resolveExplicitTextEncoding(normalizedInput.encoding, loaded.encoding);
|
|
18035
|
+
const targetHasBom2 = normalizedInput.preserveEncoding === false ? targetEncoding2 === "utf8-bom" || targetEncoding2 === "utf16le" || targetEncoding2 === "utf16be" : loaded.hasBom;
|
|
18036
|
+
ensureLossless(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
18037
|
+
const buffer2 = encodeText(insertResult.outputText, targetEncoding2, targetHasBom2);
|
|
18038
|
+
await fs3.writeFile(loaded.filePath, buffer2);
|
|
18039
|
+
return {
|
|
18040
|
+
mode,
|
|
18041
|
+
filePath: loaded.filePath,
|
|
18042
|
+
encoding: targetEncoding2,
|
|
18043
|
+
requestedEncoding: loaded.requestedEncoding,
|
|
18044
|
+
detectedEncoding: loaded.detectedEncoding,
|
|
18045
|
+
confidence: loaded.confidence,
|
|
18046
|
+
hasBom: targetHasBom2,
|
|
18047
|
+
anchor: insertResult.anchor,
|
|
18048
|
+
occurrence: insertResult.occurrence,
|
|
18049
|
+
anchorMatches: insertResult.anchorMatches,
|
|
18050
|
+
inserted: true,
|
|
18051
|
+
skipped: false,
|
|
18052
|
+
bytesRead: loaded.bytesRead,
|
|
18053
|
+
bytesWritten: buffer2.byteLength,
|
|
18054
|
+
newlineStyle: detectNewlineStyle(insertResult.outputText)
|
|
18055
|
+
};
|
|
18056
|
+
}
|
|
18057
|
+
assertReplaceArguments2(input);
|
|
18058
|
+
if (input.oldString.length === 0) {
|
|
18059
|
+
throw createTextError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
18060
|
+
}
|
|
17734
18061
|
const scope = resolveEditScope2(loaded.content, normalizedInput);
|
|
17735
18062
|
const replaceAll = normalizedInput.replaceAll ?? false;
|
|
17736
18063
|
const preserveEncoding = normalizedInput.preserveEncoding ?? true;
|
|
17737
18064
|
const requestedEncoding = normalizedInput.encoding ?? "auto";
|
|
17738
|
-
const occurrencesBefore = countOccurrences(scope.selectedText,
|
|
18065
|
+
const occurrencesBefore = countOccurrences(scope.selectedText, input.oldString);
|
|
17739
18066
|
if (replaceAll) {
|
|
17740
18067
|
if (occurrencesBefore === 0) {
|
|
17741
|
-
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText,
|
|
18068
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText, input.oldString));
|
|
17742
18069
|
}
|
|
17743
18070
|
} else if (occurrencesBefore === 0) {
|
|
17744
|
-
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText,
|
|
18071
|
+
throw createTextError("GBK_NO_MATCH", buildNoMatchMessage2(scope.selectedText, input.oldString));
|
|
17745
18072
|
} else if (occurrencesBefore > 1) {
|
|
17746
|
-
throw createTextError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${
|
|
18073
|
+
throw createTextError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${input.oldString}`);
|
|
17747
18074
|
}
|
|
17748
|
-
const alignedNewString = normalizedInput.preserveNewlineStyle === false ?
|
|
17749
|
-
const replaced = replaceAll ? scope.selectedText.split(
|
|
18075
|
+
const alignedNewString = normalizedInput.preserveNewlineStyle === false ? input.newString : alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
18076
|
+
const replaced = replaceAll ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
17750
18077
|
const outputText = `${loaded.content.slice(0, scope.rangeStart)}${replaced}${loaded.content.slice(scope.rangeEnd)}`;
|
|
17751
|
-
const targetEncoding = preserveEncoding || requestedEncoding === "auto" ? loaded.encoding : requestedEncoding;
|
|
18078
|
+
const targetEncoding = preserveEncoding || requestedEncoding === "auto" ? loaded.encoding : resolveExplicitTextEncoding(requestedEncoding, loaded.encoding);
|
|
17752
18079
|
const targetHasBom = preserveEncoding ? loaded.hasBom : targetEncoding === "utf8-bom" || targetEncoding === "utf16le" || targetEncoding === "utf16be";
|
|
17753
18080
|
ensureLossless(outputText, targetEncoding, targetHasBom);
|
|
17754
18081
|
const buffer = encodeText(outputText, targetEncoding, targetHasBom);
|
|
17755
18082
|
await fs3.writeFile(loaded.filePath, buffer);
|
|
17756
18083
|
return {
|
|
18084
|
+
mode: "replace",
|
|
17757
18085
|
filePath: loaded.filePath,
|
|
17758
18086
|
encoding: targetEncoding,
|
|
17759
18087
|
requestedEncoding: loaded.requestedEncoding,
|
|
@@ -17768,40 +18096,39 @@ async function replaceTextFileText(input) {
|
|
|
17768
18096
|
};
|
|
17769
18097
|
}
|
|
17770
18098
|
async function createTextDiffPreview(input) {
|
|
18099
|
+
const mode = resolveEditMode(input.mode);
|
|
17771
18100
|
const loaded = await readWholeTextFile({
|
|
17772
18101
|
filePath: input.filePath,
|
|
17773
18102
|
encoding: input.encoding ?? "auto",
|
|
17774
18103
|
allowExternal: input.allowExternal,
|
|
17775
18104
|
context: input.context
|
|
17776
18105
|
});
|
|
18106
|
+
if (mode === "insertAfter" || mode === "insertBefore") {
|
|
18107
|
+
assertInsertArguments2(input);
|
|
18108
|
+
const occurrence = normalizeOptionalPositiveInteger(input.occurrence, "occurrence") ?? 1;
|
|
18109
|
+
const insertResult = buildInsertOutput(
|
|
18110
|
+
loaded.content,
|
|
18111
|
+
mode,
|
|
18112
|
+
input.anchor,
|
|
18113
|
+
input.content,
|
|
18114
|
+
occurrence,
|
|
18115
|
+
resolveInsertIfExists(input.ifExists),
|
|
18116
|
+
loaded.newlineStyle
|
|
18117
|
+
);
|
|
18118
|
+
return {
|
|
18119
|
+
filePath: loaded.filePath,
|
|
18120
|
+
encoding: loaded.encoding,
|
|
18121
|
+
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, insertResult.previewBefore, insertResult.previewAfter)
|
|
18122
|
+
};
|
|
18123
|
+
}
|
|
18124
|
+
assertReplaceArguments2(input);
|
|
17777
18125
|
const scope = resolveEditScope2(loaded.content, input);
|
|
17778
18126
|
const alignedNewString = alignTextToNewlineStyle(input.newString, loaded.newlineStyle);
|
|
17779
18127
|
const replaced = input.replaceAll ?? false ? scope.selectedText.split(input.oldString).join(alignedNewString) : scope.selectedText.replace(input.oldString, alignedNewString);
|
|
17780
|
-
const beforeLines = normalizeNewlines2(scope.selectedText).split("\n");
|
|
17781
|
-
const afterLines = normalizeNewlines2(replaced).split("\n");
|
|
17782
|
-
const maxLines = Math.max(beforeLines.length, afterLines.length);
|
|
17783
|
-
const lines = [
|
|
17784
|
-
`--- ${path3.basename(loaded.filePath)} (${loaded.encoding})`,
|
|
17785
|
-
`+++ ${path3.basename(loaded.filePath)} (${loaded.encoding})`
|
|
17786
|
-
];
|
|
17787
|
-
for (let index = 0; index < maxLines; index += 1) {
|
|
17788
|
-
const before = beforeLines[index];
|
|
17789
|
-
const after = afterLines[index];
|
|
17790
|
-
if (before !== void 0 && after !== void 0 && before === after) {
|
|
17791
|
-
lines.push(` ${before}`);
|
|
17792
|
-
continue;
|
|
17793
|
-
}
|
|
17794
|
-
if (before !== void 0) {
|
|
17795
|
-
lines.push(`-${before}`);
|
|
17796
|
-
}
|
|
17797
|
-
if (after !== void 0) {
|
|
17798
|
-
lines.push(`+${after}`);
|
|
17799
|
-
}
|
|
17800
|
-
}
|
|
17801
18128
|
return {
|
|
17802
18129
|
filePath: loaded.filePath,
|
|
17803
18130
|
encoding: loaded.encoding,
|
|
17804
|
-
preview:
|
|
18131
|
+
preview: buildLineDiffPreview(loaded.filePath, loaded.encoding, scope.selectedText, replaced)
|
|
17805
18132
|
};
|
|
17806
18133
|
}
|
|
17807
18134
|
|
|
@@ -17811,11 +18138,17 @@ var text_edit_default = tool({
|
|
|
17811
18138
|
|
|
17812
18139
|
- Existing files keep their original encoding and BOM by default.
|
|
17813
18140
|
- Existing newline style is preserved by default.
|
|
17814
|
-
-
|
|
18141
|
+
- Replace mode uses oldString/newString.
|
|
18142
|
+
- Insert mode uses mode=insertAfter or insertBefore with anchor/content.`,
|
|
17815
18143
|
args: {
|
|
17816
18144
|
filePath: tool.schema.string().describe("Target file path"),
|
|
17817
|
-
|
|
17818
|
-
|
|
18145
|
+
mode: tool.schema.enum(["replace", "insertAfter", "insertBefore"]).optional().describe("Edit mode, default replace"),
|
|
18146
|
+
oldString: tool.schema.string().optional().describe("Exact text to replace when mode=replace"),
|
|
18147
|
+
newString: tool.schema.string().optional().describe("Replacement text when mode=replace"),
|
|
18148
|
+
anchor: tool.schema.string().optional().describe("Anchor text used by insertAfter/insertBefore"),
|
|
18149
|
+
content: tool.schema.string().optional().describe("Inserted content used by insertAfter/insertBefore"),
|
|
18150
|
+
occurrence: tool.schema.number().int().positive().optional().describe("1-based anchor occurrence for insert mode"),
|
|
18151
|
+
ifExists: tool.schema.enum(["allow", "skip", "error"]).optional().describe("What to do when inserted content already exists at target position"),
|
|
17819
18152
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
|
|
17820
18153
|
startLine: tool.schema.number().int().positive().optional().describe("Start line for scoped edit"),
|
|
17821
18154
|
endLine: tool.schema.number().int().positive().optional().describe("End line for scoped edit"),
|
|
@@ -17829,16 +18162,23 @@ var text_edit_default = tool({
|
|
|
17829
18162
|
async execute(args, context) {
|
|
17830
18163
|
const result = await replaceTextFileText({ ...args, context });
|
|
17831
18164
|
const preview = await createTextDiffPreview({ ...args, context });
|
|
18165
|
+
const title = result.mode === "replace" ? `\u6587\u672C\u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}` : result.inserted ? `\u6587\u672C\u63D2\u5165 ${result.encoding.toUpperCase()} #${result.occurrence}` : `\u6587\u672C\u8DF3\u8FC7 ${result.encoding.toUpperCase()} #${result.occurrence}`;
|
|
17832
18166
|
context.metadata({
|
|
17833
|
-
title
|
|
18167
|
+
title,
|
|
17834
18168
|
metadata: {
|
|
17835
18169
|
filePath: result.filePath,
|
|
17836
18170
|
encoding: result.encoding,
|
|
17837
18171
|
requestedEncoding: result.requestedEncoding,
|
|
17838
18172
|
confidence: result.confidence,
|
|
17839
18173
|
hasBom: result.hasBom,
|
|
17840
|
-
|
|
17841
|
-
|
|
18174
|
+
mode: result.mode,
|
|
18175
|
+
replacements: result.mode === "replace" ? result.replacements : void 0,
|
|
18176
|
+
occurrencesBefore: result.mode === "replace" ? result.occurrencesBefore : void 0,
|
|
18177
|
+
anchor: result.mode === "replace" ? void 0 : result.anchor,
|
|
18178
|
+
occurrence: result.mode === "replace" ? void 0 : result.occurrence,
|
|
18179
|
+
anchorMatches: result.mode === "replace" ? void 0 : result.anchorMatches,
|
|
18180
|
+
inserted: result.mode === "replace" ? void 0 : result.inserted,
|
|
18181
|
+
skipped: result.mode === "replace" ? void 0 : result.skipped,
|
|
17842
18182
|
newlineStyle: result.newlineStyle,
|
|
17843
18183
|
diffPreview: preview.preview
|
|
17844
18184
|
}
|
|
@@ -5,6 +5,8 @@ var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
|
5
5
|
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
6
6
|
"- \u5904\u7406\u6587\u672C\u6587\u4EF6\u65F6\uFF0C\u4F18\u5148\u4F7F\u7528 text_read\u3001text_write\u3001text_edit\u3002",
|
|
7
7
|
"- text_* \u9ED8\u8BA4\u4F1A\u81EA\u52A8\u8BC6\u522B\u73B0\u6709\u6587\u4EF6\u7F16\u7801\uFF0C\u5E76\u5728\u4FEE\u6539\u65F6\u5C3D\u91CF\u4FDD\u6301\u539F\u7F16\u7801\u3001BOM \u548C\u6362\u884C\u98CE\u683C\u3002",
|
|
8
|
+
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
9
|
+
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
8
10
|
"- \u82E5\u68C0\u6D4B\u7F6E\u4FE1\u5EA6\u4E0D\u8DB3\u6216\u51FA\u73B0 TEXT_UNKNOWN_ENCODING\uFF0C\u8BF7\u663E\u5F0F\u6307\u5B9A encoding \u540E\u91CD\u8BD5\u3002",
|
|
9
11
|
"- \u5904\u7406\u660E\u786E\u7684 GBK/GB18030 \u6587\u4EF6\u65F6\uFF0C\u4E5F\u53EF\u7EE7\u7EED\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\u3002"
|
|
10
12
|
].join("\n");
|