opencode-gbk-tools 0.1.2 → 0.1.4

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.
@@ -16315,11 +16315,32 @@ function assertPositiveInteger(value, name) {
16315
16315
  throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
16316
16316
  }
16317
16317
  }
16318
+ function normalizeOptionalPositiveInteger(value, name) {
16319
+ if (value === void 0 || value === -1) {
16320
+ return void 0;
16321
+ }
16322
+ assertPositiveInteger(value, name);
16323
+ return Number(value);
16324
+ }
16318
16325
  function assertNotBinary(buffer) {
16319
16326
  if (buffer.includes(0)) {
16320
16327
  throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
16321
16328
  }
16322
16329
  }
16330
+ function detectNewlineStyle(text) {
16331
+ const crlfCount = (text.match(/\r\n/g) ?? []).length;
16332
+ const lfCount = (text.match(/(^|[^\r])\n/g) ?? []).length;
16333
+ if (crlfCount > 0 && lfCount === 0) {
16334
+ return "crlf";
16335
+ }
16336
+ if (lfCount > 0 && crlfCount === 0) {
16337
+ return "lf";
16338
+ }
16339
+ if (crlfCount > 0 && lfCount > 0) {
16340
+ return "mixed";
16341
+ }
16342
+ return "none";
16343
+ }
16323
16344
  function applyLineRange(text, startLine, endLine) {
16324
16345
  if (startLine === void 0 && endLine === void 0) {
16325
16346
  return {
@@ -16401,6 +16422,84 @@ function resolveEditScope(text, input) {
16401
16422
  rangeEnd: anchored.rangeStart + lineRanged.rangeEnd
16402
16423
  };
16403
16424
  }
16425
+ function normalizeNewlines(text) {
16426
+ return text.replace(/\r\n/g, "\n");
16427
+ }
16428
+ function trimRightSpaces(text) {
16429
+ return text.replace(/[ \t]+$/g, "");
16430
+ }
16431
+ function splitNormalizedLines(text) {
16432
+ return normalizeNewlines(text).split("\n");
16433
+ }
16434
+ function trimTrailingEmptyLines(lines) {
16435
+ const result = [...lines];
16436
+ while (result.length > 0 && trimRightSpaces(result[result.length - 1]) === "") {
16437
+ result.pop();
16438
+ }
16439
+ return result;
16440
+ }
16441
+ function getNearestContext(content, oldString) {
16442
+ const lines = content.split(/\r?\n/);
16443
+ const oldLines = normalizeNewlines(oldString).split("\n").filter(Boolean);
16444
+ const firstToken = oldLines[0] || oldString.trim();
16445
+ for (let index = 0; index < lines.length; index += 1) {
16446
+ if (lines[index].includes(firstToken)) {
16447
+ return lines.slice(index, index + 4).join("\n");
16448
+ }
16449
+ }
16450
+ return lines.slice(0, 4).join("\n");
16451
+ }
16452
+ function buildNoMatchMessage(content, oldString) {
16453
+ return [
16454
+ "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
16455
+ "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\u884C/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\u3002",
16456
+ "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFF1A",
16457
+ getNearestContext(content, oldString)
16458
+ ].join("\n");
16459
+ }
16460
+ function tryLooseBlockReplace(content, oldString, newString) {
16461
+ const normalizedContent = normalizeNewlines(content);
16462
+ const contentLines = splitNormalizedLines(content);
16463
+ const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
16464
+ const newLines = splitNormalizedLines(newString);
16465
+ if (oldLines.length === 0) {
16466
+ return null;
16467
+ }
16468
+ for (let start = 0; start < contentLines.length; start += 1) {
16469
+ let contentIndex = start;
16470
+ let oldIndex = 0;
16471
+ while (oldIndex < oldLines.length && contentIndex < contentLines.length) {
16472
+ const expected = trimRightSpaces(oldLines[oldIndex]);
16473
+ const actual = trimRightSpaces(contentLines[contentIndex]);
16474
+ if (expected === actual) {
16475
+ oldIndex += 1;
16476
+ contentIndex += 1;
16477
+ continue;
16478
+ }
16479
+ if (actual === "") {
16480
+ contentIndex += 1;
16481
+ continue;
16482
+ }
16483
+ if (expected === "") {
16484
+ oldIndex += 1;
16485
+ continue;
16486
+ }
16487
+ break;
16488
+ }
16489
+ if (oldIndex === oldLines.length) {
16490
+ const replacedNormalized = [
16491
+ ...contentLines.slice(0, start),
16492
+ ...newLines,
16493
+ ...contentLines.slice(contentIndex)
16494
+ ].join("\n");
16495
+ return {
16496
+ occurrencesBefore: 1,
16497
+ content: detectNewlineStyle(content) === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
16498
+ };
16499
+ }
16500
+ }
16501
+ return null;
16502
+ }
16404
16503
  function countOccurrences(text, target) {
16405
16504
  if (target.length === 0) {
16406
16505
  throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
@@ -16558,12 +16657,17 @@ async function replaceLargeGbkFileText(input) {
16558
16657
  }
16559
16658
  }
16560
16659
  async function replaceGbkFileText(input) {
16660
+ const normalizedInput = {
16661
+ ...input,
16662
+ startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
16663
+ endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
16664
+ };
16561
16665
  if (input.oldString.length === 0) {
16562
16666
  throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
16563
16667
  }
16564
- const replaceAll = input.replaceAll ?? false;
16565
- const resolved = await resolveReadableGbkFile(input);
16566
- const hasScopedRange = input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0;
16668
+ const replaceAll = normalizedInput.replaceAll ?? false;
16669
+ const resolved = await resolveReadableGbkFile(normalizedInput);
16670
+ const hasScopedRange = normalizedInput.startLine !== void 0 || normalizedInput.endLine !== void 0 || normalizedInput.startAnchor !== void 0 || normalizedInput.endAnchor !== void 0;
16567
16671
  if (resolved.stat.size >= STREAMING_FILE_SIZE_THRESHOLD_BYTES && !hasScopedRange) {
16568
16672
  return await replaceLargeGbkFileText({
16569
16673
  ...resolved,
@@ -16573,18 +16677,34 @@ async function replaceGbkFileText(input) {
16573
16677
  });
16574
16678
  }
16575
16679
  const current = await readWholeGbkTextFile(resolved);
16576
- const scope = resolveEditScope(current.content, input);
16577
- const occurrencesBefore = countOccurrences(scope.selectedText, input.oldString);
16680
+ const scope = resolveEditScope(current.content, normalizedInput);
16681
+ const occurrencesBefore = countOccurrences(scope.selectedText, normalizedInput.oldString);
16682
+ if (!replaceAll && occurrencesBefore === 0) {
16683
+ const loose = tryLooseBlockReplace(scope.selectedText, normalizedInput.oldString, normalizedInput.newString);
16684
+ if (loose !== null) {
16685
+ const outputText2 = `${current.content.slice(0, scope.rangeStart)}${loose.content}${current.content.slice(scope.rangeEnd)}`;
16686
+ const buffer2 = import_iconv_lite.default.encode(outputText2, current.encoding);
16687
+ await fs2.writeFile(current.filePath, buffer2);
16688
+ return {
16689
+ filePath: current.filePath,
16690
+ encoding: current.encoding,
16691
+ replacements: 1,
16692
+ occurrencesBefore: loose.occurrencesBefore,
16693
+ bytesRead: current.bytesRead,
16694
+ bytesWritten: buffer2.byteLength
16695
+ };
16696
+ }
16697
+ }
16578
16698
  if (replaceAll) {
16579
16699
  if (occurrencesBefore === 0) {
16580
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\u5BB9: ${input.oldString}`);
16700
+ throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
16581
16701
  }
16582
16702
  } else if (occurrencesBefore === 0) {
16583
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\u5BB9: ${input.oldString}`);
16703
+ throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
16584
16704
  } else if (occurrencesBefore > 1) {
16585
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${input.oldString}`);
16705
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${normalizedInput.oldString}`);
16586
16706
  }
16587
- const replaced = replaceAll ? scope.selectedText.split(input.oldString).join(input.newString) : scope.selectedText.replace(input.oldString, input.newString);
16707
+ const replaced = replaceAll ? scope.selectedText.split(normalizedInput.oldString).join(normalizedInput.newString) : scope.selectedText.replace(normalizedInput.oldString, normalizedInput.newString);
16588
16708
  const outputText = `${current.content.slice(0, scope.rangeStart)}${replaced}${current.content.slice(scope.rangeEnd)}`;
16589
16709
  const buffer = import_iconv_lite.default.encode(outputText, current.encoding);
16590
16710
  await fs2.writeFile(current.filePath, buffer);
@@ -16606,8 +16726,8 @@ var gbk_edit_default = tool({
16606
16726
  oldString: tool.schema.string().describe("Text to replace"),
16607
16727
  newString: tool.schema.string().describe("Replacement text"),
16608
16728
  replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
16609
- startLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based start line"),
16610
- endLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based end line"),
16729
+ startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based start line"),
16730
+ endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based end line"),
16611
16731
  startAnchor: tool.schema.string().optional().describe("Restrict edit to content after this anchor"),
16612
16732
  endAnchor: tool.schema.string().optional().describe("Restrict edit to content before this anchor"),
16613
16733
  encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
@@ -16315,6 +16315,13 @@ function assertPositiveInteger(value, name) {
16315
16315
  throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
16316
16316
  }
16317
16317
  }
16318
+ function normalizeOptionalPositiveInteger(value, name) {
16319
+ if (value === void 0 || value === -1) {
16320
+ return void 0;
16321
+ }
16322
+ assertPositiveInteger(value, name);
16323
+ return Number(value);
16324
+ }
16318
16325
  function assertNotBinary(buffer) {
16319
16326
  if (buffer.includes(0)) {
16320
16327
  throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
@@ -16556,8 +16563,8 @@ function createTailCollector(limit) {
16556
16563
  };
16557
16564
  }
16558
16565
  async function readGbkFile(input) {
16559
- const offset = input.offset ?? 1;
16560
- const limit = input.limit ?? 2e3;
16566
+ const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
16567
+ const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
16561
16568
  const tail = input.tail ?? false;
16562
16569
  const resolved = await resolveReadableGbkFile(input);
16563
16570
  if (resolved.stat.size < STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
@@ -16600,8 +16607,8 @@ var gbk_read_default = tool({
16600
16607
  description: "Read GBK encoded text files",
16601
16608
  args: {
16602
16609
  filePath: tool.schema.string().describe("Target file path"),
16603
- offset: tool.schema.number().int().positive().optional().describe("1-based start line"),
16604
- limit: tool.schema.number().int().positive().optional().describe("Number of lines to read"),
16610
+ offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line"),
16611
+ limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read"),
16605
16612
  tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
16606
16613
  encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
16607
16614
  allowExternal: tool.schema.boolean().optional().describe("Allow paths outside workspace root")
@@ -16315,6 +16315,13 @@ function assertPositiveInteger(value, name) {
16315
16315
  throw createGbkError("GBK_INVALID_ARGUMENT", `${name} \u5FC5\u987B\u662F\u6B63\u6574\u6570`);
16316
16316
  }
16317
16317
  }
16318
+ function normalizeOptionalPositiveInteger(value, name) {
16319
+ if (value === void 0 || value === -1) {
16320
+ return void 0;
16321
+ }
16322
+ assertPositiveInteger(value, name);
16323
+ return Number(value);
16324
+ }
16318
16325
  function assertNotBinary(buffer) {
16319
16326
  if (buffer.includes(0)) {
16320
16327
  throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u6309 GBK \u6587\u672C\u5904\u7406");
@@ -16466,6 +16473,84 @@ function resolveEditScope(text, input) {
16466
16473
  rangeEnd: anchored.rangeStart + lineRanged.rangeEnd
16467
16474
  };
16468
16475
  }
16476
+ function normalizeNewlines(text) {
16477
+ return text.replace(/\r\n/g, "\n");
16478
+ }
16479
+ function trimRightSpaces(text) {
16480
+ return text.replace(/[ \t]+$/g, "");
16481
+ }
16482
+ function splitNormalizedLines(text) {
16483
+ return normalizeNewlines(text).split("\n");
16484
+ }
16485
+ function trimTrailingEmptyLines(lines) {
16486
+ const result = [...lines];
16487
+ while (result.length > 0 && trimRightSpaces(result[result.length - 1]) === "") {
16488
+ result.pop();
16489
+ }
16490
+ return result;
16491
+ }
16492
+ function getNearestContext(content, oldString) {
16493
+ const lines = content.split(/\r?\n/);
16494
+ const oldLines = normalizeNewlines(oldString).split("\n").filter(Boolean);
16495
+ const firstToken = oldLines[0] || oldString.trim();
16496
+ for (let index = 0; index < lines.length; index += 1) {
16497
+ if (lines[index].includes(firstToken)) {
16498
+ return lines.slice(index, index + 4).join("\n");
16499
+ }
16500
+ }
16501
+ return lines.slice(0, 4).join("\n");
16502
+ }
16503
+ function buildNoMatchMessage(content, oldString) {
16504
+ return [
16505
+ "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\u3002",
16506
+ "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\u884C/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\u3002",
16507
+ "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFF1A",
16508
+ getNearestContext(content, oldString)
16509
+ ].join("\n");
16510
+ }
16511
+ function tryLooseBlockReplace(content, oldString, newString) {
16512
+ const normalizedContent = normalizeNewlines(content);
16513
+ const contentLines = splitNormalizedLines(content);
16514
+ const oldLines = trimTrailingEmptyLines(splitNormalizedLines(oldString));
16515
+ const newLines = splitNormalizedLines(newString);
16516
+ if (oldLines.length === 0) {
16517
+ return null;
16518
+ }
16519
+ for (let start = 0; start < contentLines.length; start += 1) {
16520
+ let contentIndex = start;
16521
+ let oldIndex = 0;
16522
+ while (oldIndex < oldLines.length && contentIndex < contentLines.length) {
16523
+ const expected = trimRightSpaces(oldLines[oldIndex]);
16524
+ const actual = trimRightSpaces(contentLines[contentIndex]);
16525
+ if (expected === actual) {
16526
+ oldIndex += 1;
16527
+ contentIndex += 1;
16528
+ continue;
16529
+ }
16530
+ if (actual === "") {
16531
+ contentIndex += 1;
16532
+ continue;
16533
+ }
16534
+ if (expected === "") {
16535
+ oldIndex += 1;
16536
+ continue;
16537
+ }
16538
+ break;
16539
+ }
16540
+ if (oldIndex === oldLines.length) {
16541
+ const replacedNormalized = [
16542
+ ...contentLines.slice(0, start),
16543
+ ...newLines,
16544
+ ...contentLines.slice(contentIndex)
16545
+ ].join("\n");
16546
+ return {
16547
+ occurrencesBefore: 1,
16548
+ content: detectNewlineStyle(content) === "crlf" ? replacedNormalized.replace(/\n/g, "\r\n") : replacedNormalized
16549
+ };
16550
+ }
16551
+ }
16552
+ return null;
16553
+ }
16469
16554
  function countOccurrences(text, target) {
16470
16555
  if (target.length === 0) {
16471
16556
  throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
@@ -16727,8 +16812,8 @@ async function replaceLargeGbkFileText(input) {
16727
16812
  }
16728
16813
  }
16729
16814
  async function readGbkFile(input) {
16730
- const offset = input.offset ?? 1;
16731
- const limit = input.limit ?? 2e3;
16815
+ const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
16816
+ const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
16732
16817
  const tail = input.tail ?? false;
16733
16818
  const resolved = await resolveReadableGbkFile(input);
16734
16819
  if (resolved.stat.size < STREAMING_FILE_SIZE_THRESHOLD_BYTES) {
@@ -16766,12 +16851,17 @@ async function readGbkFile(input) {
16766
16851
  };
16767
16852
  }
16768
16853
  async function replaceGbkFileText(input) {
16854
+ const normalizedInput = {
16855
+ ...input,
16856
+ startLine: normalizeOptionalPositiveInteger(input.startLine, "startLine"),
16857
+ endLine: normalizeOptionalPositiveInteger(input.endLine, "endLine")
16858
+ };
16769
16859
  if (input.oldString.length === 0) {
16770
16860
  throw createGbkError("GBK_EMPTY_OLD_STRING", "oldString \u4E0D\u80FD\u4E3A\u7A7A");
16771
16861
  }
16772
- const replaceAll = input.replaceAll ?? false;
16773
- const resolved = await resolveReadableGbkFile(input);
16774
- const hasScopedRange = input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0;
16862
+ const replaceAll = normalizedInput.replaceAll ?? false;
16863
+ const resolved = await resolveReadableGbkFile(normalizedInput);
16864
+ const hasScopedRange = normalizedInput.startLine !== void 0 || normalizedInput.endLine !== void 0 || normalizedInput.startAnchor !== void 0 || normalizedInput.endAnchor !== void 0;
16775
16865
  if (resolved.stat.size >= STREAMING_FILE_SIZE_THRESHOLD_BYTES && !hasScopedRange) {
16776
16866
  return await replaceLargeGbkFileText({
16777
16867
  ...resolved,
@@ -16781,18 +16871,34 @@ async function replaceGbkFileText(input) {
16781
16871
  });
16782
16872
  }
16783
16873
  const current = await readWholeGbkTextFile(resolved);
16784
- const scope = resolveEditScope(current.content, input);
16785
- const occurrencesBefore = countOccurrences(scope.selectedText, input.oldString);
16874
+ const scope = resolveEditScope(current.content, normalizedInput);
16875
+ const occurrencesBefore = countOccurrences(scope.selectedText, normalizedInput.oldString);
16876
+ if (!replaceAll && occurrencesBefore === 0) {
16877
+ const loose = tryLooseBlockReplace(scope.selectedText, normalizedInput.oldString, normalizedInput.newString);
16878
+ if (loose !== null) {
16879
+ const outputText2 = `${current.content.slice(0, scope.rangeStart)}${loose.content}${current.content.slice(scope.rangeEnd)}`;
16880
+ const buffer2 = import_iconv_lite.default.encode(outputText2, current.encoding);
16881
+ await fs2.writeFile(current.filePath, buffer2);
16882
+ return {
16883
+ filePath: current.filePath,
16884
+ encoding: current.encoding,
16885
+ replacements: 1,
16886
+ occurrencesBefore: loose.occurrencesBefore,
16887
+ bytesRead: current.bytesRead,
16888
+ bytesWritten: buffer2.byteLength
16889
+ };
16890
+ }
16891
+ }
16786
16892
  if (replaceAll) {
16787
16893
  if (occurrencesBefore === 0) {
16788
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\u5BB9: ${input.oldString}`);
16894
+ throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
16789
16895
  }
16790
16896
  } else if (occurrencesBefore === 0) {
16791
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\u5BB9: ${input.oldString}`);
16897
+ throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, normalizedInput.oldString));
16792
16898
  } else if (occurrencesBefore > 1) {
16793
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${input.oldString}`);
16899
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\u9879: ${normalizedInput.oldString}`);
16794
16900
  }
16795
- const replaced = replaceAll ? scope.selectedText.split(input.oldString).join(input.newString) : scope.selectedText.replace(input.oldString, input.newString);
16901
+ const replaced = replaceAll ? scope.selectedText.split(normalizedInput.oldString).join(normalizedInput.newString) : scope.selectedText.replace(normalizedInput.oldString, normalizedInput.newString);
16796
16902
  const outputText = `${current.content.slice(0, scope.rangeStart)}${replaced}${current.content.slice(scope.rangeEnd)}`;
16797
16903
  const buffer = import_iconv_lite.default.encode(outputText, current.encoding);
16798
16904
  await fs2.writeFile(current.filePath, buffer);
@@ -16871,8 +16977,8 @@ var gbk_edit_default = tool({
16871
16977
  oldString: tool.schema.string().describe("Text to replace"),
16872
16978
  newString: tool.schema.string().describe("Replacement text"),
16873
16979
  replaceAll: tool.schema.boolean().optional().describe("Replace all occurrences"),
16874
- startLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based start line"),
16875
- endLine: tool.schema.number().int().positive().optional().describe("Restrict edit to 1-based end line"),
16980
+ startLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based start line"),
16981
+ endLine: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Restrict edit to 1-based end line"),
16876
16982
  startAnchor: tool.schema.string().optional().describe("Restrict edit to content after this anchor"),
16877
16983
  endAnchor: tool.schema.string().optional().describe("Restrict edit to content before this anchor"),
16878
16984
  encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
@@ -16889,8 +16995,8 @@ var gbk_read_default = tool({
16889
16995
  description: "Read GBK encoded text files",
16890
16996
  args: {
16891
16997
  filePath: tool.schema.string().describe("Target file path"),
16892
- offset: tool.schema.number().int().positive().optional().describe("1-based start line"),
16893
- limit: tool.schema.number().int().positive().optional().describe("Number of lines to read"),
16998
+ offset: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("1-based start line"),
16999
+ limit: tool.schema.union([tool.schema.number().int().positive(), tool.schema.literal(-1)]).optional().describe("Number of lines to read"),
16894
17000
  tail: tool.schema.boolean().optional().describe("Read last N lines instead of offset-based window"),
16895
17001
  encoding: tool.schema.enum(["gbk", "gb18030"]).optional().describe("Text encoding"),
16896
17002
  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.2",
4
+ "packageVersion": "0.1.4",
5
5
  "artifacts": [
6
6
  {
7
7
  "relativePath": "tools/gbk_edit.js",
8
8
  "kind": "tool",
9
- "expectedHash": "c90cdc75ece2c66d72750cb9243d20235a7dad82829e8f519a7c31f7432ef3ff",
9
+ "expectedHash": "6a8a26850a21e47a9ececc2b8606452491a654d5fc79ef908cfbcea9e6c5b7e3",
10
10
  "hashAlgorithm": "sha256"
11
11
  },
12
12
  {
13
13
  "relativePath": "tools/gbk_read.js",
14
14
  "kind": "tool",
15
- "expectedHash": "4ed95d744e6d5a70f578908fe0efc7b70b4a8c416e9a42fd2ed8726c02562cbe",
15
+ "expectedHash": "c61e5ee95e05e96965c76e5dc2f18b3943bc3c2a76567b415839442358024d46",
16
16
  "hashAlgorithm": "sha256"
17
17
  },
18
18
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gbk-tools",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "GBK/GB18030 custom tools and agent for OpenCode",
5
5
  "type": "module",
6
6
  "license": "MIT",