opencode-gbk-tools 0.1.6 → 0.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.
|
@@ -16457,14 +16457,14 @@ function buildNoMatchMessage(content, oldString) {
|
|
|
16457
16457
|
getNearestContext(content, oldString)
|
|
16458
16458
|
].join("\n");
|
|
16459
16459
|
}
|
|
16460
|
-
function
|
|
16461
|
-
const
|
|
16462
|
-
|
|
16463
|
-
|
|
16464
|
-
|
|
16465
|
-
|
|
16466
|
-
|
|
16467
|
-
|
|
16460
|
+
function hasLineNumberPrefixes(lines) {
|
|
16461
|
+
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
16462
|
+
return nonEmpty.length > 0 && nonEmpty.every((l) => /^\d+: /.test(l));
|
|
16463
|
+
}
|
|
16464
|
+
function stripLineNumberPrefixes(lines) {
|
|
16465
|
+
return lines.map((l) => l.replace(/^\d+: /, ""));
|
|
16466
|
+
}
|
|
16467
|
+
function matchLooseBlock(contentLines, oldLines, newLines, newlineStyle, content) {
|
|
16468
16468
|
for (let start = 0; start < contentLines.length; start += 1) {
|
|
16469
16469
|
let contentIndex = start;
|
|
16470
16470
|
let oldIndex = 0;
|
|
@@ -16494,12 +16494,32 @@ function tryLooseBlockReplace(content, oldString, newString) {
|
|
|
16494
16494
|
].join("\n");
|
|
16495
16495
|
return {
|
|
16496
16496
|
occurrencesBefore: 1,
|
|
16497
|
-
content:
|
|
16497
|
+
content: newlineStyle === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
|
|
16498
16498
|
};
|
|
16499
16499
|
}
|
|
16500
16500
|
}
|
|
16501
16501
|
return null;
|
|
16502
16502
|
}
|
|
16503
|
+
function tryLooseBlockReplace(content, oldString, newString) {
|
|
16504
|
+
const contentLines = splitNormalizedLines(content);
|
|
16505
|
+
const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
|
|
16506
|
+
const newLines = splitNormalizedLines(newString);
|
|
16507
|
+
const newlineStyle = detectNewlineStyle(content);
|
|
16508
|
+
if (oldLines.length === 0) {
|
|
16509
|
+
return null;
|
|
16510
|
+
}
|
|
16511
|
+
const result = matchLooseBlock(contentLines, oldLines, newLines, newlineStyle, content);
|
|
16512
|
+
if (result !== null) {
|
|
16513
|
+
return result;
|
|
16514
|
+
}
|
|
16515
|
+
if (hasLineNumberPrefixes(oldLines)) {
|
|
16516
|
+
const strippedOldLines = trimTrailingEmptyLines(stripLineNumberPrefixes(oldLines));
|
|
16517
|
+
if (strippedOldLines.length > 0) {
|
|
16518
|
+
return matchLooseBlock(contentLines, strippedOldLines, newLines, newlineStyle, content);
|
|
16519
|
+
}
|
|
16520
|
+
}
|
|
16521
|
+
return null;
|
|
16522
|
+
}
|
|
16503
16523
|
function countOccurrences(text, target) {
|
|
16504
16524
|
if (target.length === 0) {
|
|
16505
16525
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -16725,12 +16745,22 @@ var gbk_edit_default = tool({
|
|
|
16725
16745
|
Reads the FULL file content regardless of file size \u2014 not limited by gbk_read's line window.
|
|
16726
16746
|
Safe to use on files with more than 2000 lines.
|
|
16727
16747
|
|
|
16748
|
+
CRITICAL \u2014 do NOT include line number prefixes in oldString or newString:
|
|
16749
|
+
gbk_read output looks like "3787: SENDMSG 0 content". The "3787: " is a navigation prefix, NOT file content.
|
|
16750
|
+
oldString must be the raw file content: "SENDMSG 0 content" (no line number prefix).
|
|
16751
|
+
Including line numbers in oldString will cause GBK_NO_MATCH even if the content exists.
|
|
16752
|
+
|
|
16753
|
+
Recommended workflow for large files (when gbk_read returned truncated=true):
|
|
16754
|
+
1. gbk_search(pattern) \u2192 find exact lineNumber
|
|
16755
|
+
2. gbk_read(offset=<lineNumber>, limit=20) \u2192 get the exact block (strip "N: " prefixes)
|
|
16756
|
+
3. gbk_edit(oldString=<content without prefixes>, newString=<new content>)
|
|
16757
|
+
|
|
16728
16758
|
For large files, use 'startLine'/'endLine' or 'startAnchor'/'endAnchor' to narrow the search scope
|
|
16729
16759
|
and avoid false matches. Scoped edits also improve performance on very large files.`,
|
|
16730
16760
|
args: {
|
|
16731
16761
|
filePath: tool.schema.string().describe("Target file path"),
|
|
16732
|
-
oldString: tool.schema.string().describe("Exact text to replace
|
|
16733
|
-
newString: tool.schema.string().describe("Replacement text"),
|
|
16762
|
+
oldString: tool.schema.string().describe("Exact text to replace \u2014 raw file content only, no 'N: ' line number prefixes from gbk_read output"),
|
|
16763
|
+
newString: tool.schema.string().describe("Replacement text \u2014 raw content only, no line number prefixes"),
|
|
16734
16764
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences (default: false, requires unique match)"),
|
|
16735
16765
|
startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based start line (inclusive)"),
|
|
16736
16766
|
endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based end line (inclusive)"),
|
|
@@ -16609,14 +16609,23 @@ var gbk_read_default = tool({
|
|
|
16609
16609
|
Returns up to 'limit' lines (default 2000) starting from 'offset'.
|
|
16610
16610
|
When the file has more lines than the window, 'truncated' is true and 'totalLines' shows the full count.
|
|
16611
16611
|
|
|
16612
|
-
IMPORTANT:
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16612
|
+
IMPORTANT \u2014 line number format: each output line is prefixed with "N: " (e.g. "3787: content").
|
|
16613
|
+
These prefixes are for navigation only. Strip them before using any line as 'oldString' in gbk_edit.
|
|
16614
|
+
|
|
16615
|
+
IMPORTANT \u2014 do NOT use large limits to read the whole file:
|
|
16616
|
+
- Setting limit=40000 or similar to "read everything" is WRONG. It produces an enormous response
|
|
16617
|
+
that is unreliable to process and may be truncated by the protocol.
|
|
16618
|
+
- To find content in a large file, use gbk_search instead.
|
|
16619
|
+
- To read a specific section, set offset=<lineNumber> and limit=<small number like 10-30>.
|
|
16620
|
+
|
|
16621
|
+
Workflow when truncated=true:
|
|
16622
|
+
1. gbk_search(pattern) \u2192 get exact lineNumber
|
|
16623
|
+
2. gbk_read(offset=<lineNumber>, limit=20) \u2192 get the exact block
|
|
16624
|
+
3. gbk_edit(oldString=<exact content without line prefixes>) \u2192 edit reliably`,
|
|
16616
16625
|
args: {
|
|
16617
16626
|
filePath: tool.schema.string().describe("Target file path"),
|
|
16618
16627
|
offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line (default: 1)"),
|
|
16619
|
-
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read (default: 2000). Use -1 to apply the default."),
|
|
16628
|
+
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read (default: 2000, max recommended: 200). Use -1 to apply the default."),
|
|
16620
16629
|
tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
|
|
16621
16630
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding (default: gbk)"),
|
|
16622
16631
|
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
package/dist/plugin/index.js
CHANGED
|
@@ -16508,14 +16508,14 @@ function buildNoMatchMessage(content, oldString) {
|
|
|
16508
16508
|
getNearestContext(content, oldString)
|
|
16509
16509
|
].join("\n");
|
|
16510
16510
|
}
|
|
16511
|
-
function
|
|
16512
|
-
const
|
|
16513
|
-
|
|
16514
|
-
|
|
16515
|
-
|
|
16516
|
-
|
|
16517
|
-
|
|
16518
|
-
|
|
16511
|
+
function hasLineNumberPrefixes(lines) {
|
|
16512
|
+
const nonEmpty = lines.filter((l) => l.trim().length > 0);
|
|
16513
|
+
return nonEmpty.length > 0 && nonEmpty.every((l) => /^\d+: /.test(l));
|
|
16514
|
+
}
|
|
16515
|
+
function stripLineNumberPrefixes(lines) {
|
|
16516
|
+
return lines.map((l) => l.replace(/^\d+: /, ""));
|
|
16517
|
+
}
|
|
16518
|
+
function matchLooseBlock(contentLines, oldLines, newLines, newlineStyle, content) {
|
|
16519
16519
|
for (let start = 0; start < contentLines.length; start += 1) {
|
|
16520
16520
|
let contentIndex = start;
|
|
16521
16521
|
let oldIndex = 0;
|
|
@@ -16545,12 +16545,32 @@ function tryLooseBlockReplace(content, oldString, newString) {
|
|
|
16545
16545
|
].join("\n");
|
|
16546
16546
|
return {
|
|
16547
16547
|
occurrencesBefore: 1,
|
|
16548
|
-
content:
|
|
16548
|
+
content: newlineStyle === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
|
|
16549
16549
|
};
|
|
16550
16550
|
}
|
|
16551
16551
|
}
|
|
16552
16552
|
return null;
|
|
16553
16553
|
}
|
|
16554
|
+
function tryLooseBlockReplace(content, oldString, newString) {
|
|
16555
|
+
const contentLines = splitNormalizedLines(content);
|
|
16556
|
+
const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
|
|
16557
|
+
const newLines = splitNormalizedLines(newString);
|
|
16558
|
+
const newlineStyle = detectNewlineStyle(content);
|
|
16559
|
+
if (oldLines.length === 0) {
|
|
16560
|
+
return null;
|
|
16561
|
+
}
|
|
16562
|
+
const result = matchLooseBlock(contentLines, oldLines, newLines, newlineStyle, content);
|
|
16563
|
+
if (result !== null) {
|
|
16564
|
+
return result;
|
|
16565
|
+
}
|
|
16566
|
+
if (hasLineNumberPrefixes(oldLines)) {
|
|
16567
|
+
const strippedOldLines = trimTrailingEmptyLines(stripLineNumberPrefixes(oldLines));
|
|
16568
|
+
if (strippedOldLines.length > 0) {
|
|
16569
|
+
return matchLooseBlock(contentLines, strippedOldLines, newLines, newlineStyle, content);
|
|
16570
|
+
}
|
|
16571
|
+
}
|
|
16572
|
+
return null;
|
|
16573
|
+
}
|
|
16554
16574
|
function countOccurrences(text, target) {
|
|
16555
16575
|
if (target.length === 0) {
|
|
16556
16576
|
throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
|
|
@@ -17025,12 +17045,22 @@ var gbk_edit_default = tool({
|
|
|
17025
17045
|
Reads the FULL file content regardless of file size \u2014 not limited by gbk_read's line window.
|
|
17026
17046
|
Safe to use on files with more than 2000 lines.
|
|
17027
17047
|
|
|
17048
|
+
CRITICAL \u2014 do NOT include line number prefixes in oldString or newString:
|
|
17049
|
+
gbk_read output looks like "3787: SENDMSG 0 content". The "3787: " is a navigation prefix, NOT file content.
|
|
17050
|
+
oldString must be the raw file content: "SENDMSG 0 content" (no line number prefix).
|
|
17051
|
+
Including line numbers in oldString will cause GBK_NO_MATCH even if the content exists.
|
|
17052
|
+
|
|
17053
|
+
Recommended workflow for large files (when gbk_read returned truncated=true):
|
|
17054
|
+
1. gbk_search(pattern) \u2192 find exact lineNumber
|
|
17055
|
+
2. gbk_read(offset=<lineNumber>, limit=20) \u2192 get the exact block (strip "N: " prefixes)
|
|
17056
|
+
3. gbk_edit(oldString=<content without prefixes>, newString=<new content>)
|
|
17057
|
+
|
|
17028
17058
|
For large files, use 'startLine'/'endLine' or 'startAnchor'/'endAnchor' to narrow the search scope
|
|
17029
17059
|
and avoid false matches. Scoped edits also improve performance on very large files.`,
|
|
17030
17060
|
args: {
|
|
17031
17061
|
filePath: tool.schema.string().describe("Target file path"),
|
|
17032
|
-
oldString: tool.schema.string().describe("Exact text to replace
|
|
17033
|
-
newString: tool.schema.string().describe("Replacement text"),
|
|
17062
|
+
oldString: tool.schema.string().describe("Exact text to replace \u2014 raw file content only, no 'N: ' line number prefixes from gbk_read output"),
|
|
17063
|
+
newString: tool.schema.string().describe("Replacement text \u2014 raw content only, no line number prefixes"),
|
|
17034
17064
|
replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences (default: false, requires unique match)"),
|
|
17035
17065
|
startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based start line (inclusive)"),
|
|
17036
17066
|
endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit scope to 1-based end line (inclusive)"),
|
|
@@ -17052,14 +17082,23 @@ var gbk_read_default = tool({
|
|
|
17052
17082
|
Returns up to 'limit' lines (default 2000) starting from 'offset'.
|
|
17053
17083
|
When the file has more lines than the window, 'truncated' is true and 'totalLines' shows the full count.
|
|
17054
17084
|
|
|
17055
|
-
IMPORTANT:
|
|
17056
|
-
|
|
17057
|
-
|
|
17058
|
-
|
|
17085
|
+
IMPORTANT \u2014 line number format: each output line is prefixed with "N: " (e.g. "3787: content").
|
|
17086
|
+
These prefixes are for navigation only. Strip them before using any line as 'oldString' in gbk_edit.
|
|
17087
|
+
|
|
17088
|
+
IMPORTANT \u2014 do NOT use large limits to read the whole file:
|
|
17089
|
+
- Setting limit=40000 or similar to "read everything" is WRONG. It produces an enormous response
|
|
17090
|
+
that is unreliable to process and may be truncated by the protocol.
|
|
17091
|
+
- To find content in a large file, use gbk_search instead.
|
|
17092
|
+
- To read a specific section, set offset=<lineNumber> and limit=<small number like 10-30>.
|
|
17093
|
+
|
|
17094
|
+
Workflow when truncated=true:
|
|
17095
|
+
1. gbk_search(pattern) \u2192 get exact lineNumber
|
|
17096
|
+
2. gbk_read(offset=<lineNumber>, limit=20) \u2192 get the exact block
|
|
17097
|
+
3. gbk_edit(oldString=<exact content without line prefixes>) \u2192 edit reliably`,
|
|
17059
17098
|
args: {
|
|
17060
17099
|
filePath: tool.schema.string().describe("Target file path"),
|
|
17061
17100
|
offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line (default: 1)"),
|
|
17062
|
-
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read (default: 2000). Use -1 to apply the default."),
|
|
17101
|
+
limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read (default: 2000, max recommended: 200). Use -1 to apply the default."),
|
|
17063
17102
|
tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
|
|
17064
17103
|
encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding (default: gbk)"),
|
|
17065
17104
|
allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
3
|
"packageName": "opencode-gbk-tools",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.7",
|
|
5
5
|
"artifacts": [
|
|
6
6
|
{
|
|
7
7
|
"relativePath": "tools/gbk_edit.js",
|
|
8
8
|
"kind": "tool",
|
|
9
|
-
"expectedHash": "
|
|
9
|
+
"expectedHash": "783a97cc6f7d1378f0b841ce0c6809e93db104aa192a64f72878ce6d432c997e",
|
|
10
10
|
"hashAlgorithm": "sha256"
|
|
11
11
|
},
|
|
12
12
|
{
|
|
13
13
|
"relativePath": "tools/gbk_read.js",
|
|
14
14
|
"kind": "tool",
|
|
15
|
-
"expectedHash": "
|
|
15
|
+
"expectedHash": "90c0c3ca519000bcc1fc65f84615a5a280866a45104d08678f3ed92dbfda9f45",
|
|
16
16
|
"hashAlgorithm": "sha256"
|
|
17
17
|
},
|
|
18
18
|
{
|