opencode-gbk-tools 0.1.21 → 0.1.23

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.
@@ -16311,29 +16311,36 @@ var ANSI_RED = "\x1B[31m";
16311
16311
  var ANSI_GREEN = "\x1B[32m";
16312
16312
  var ANSI_DIM = "\x1B[2m";
16313
16313
  var ANSI_RESET = "\x1B[0m";
16314
+ var MAX_DIFF_LINES = 80;
16314
16315
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16315
16316
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16316
16317
  const afterLines = normalizeNewlines(afterText).split("\n");
16317
16318
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16318
- const lines = [
16319
+ const header = [
16319
16320
  `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16320
16321
  `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16321
16322
  ];
16323
+ const body = [];
16322
16324
  for (let index = 0; index < maxLines; index += 1) {
16323
16325
  const before = beforeLines[index];
16324
16326
  const after = afterLines[index];
16325
16327
  if (before !== void 0 && after !== void 0 && before === after) {
16326
- lines.push(` ${before}`);
16328
+ body.push(` ${before}`);
16327
16329
  continue;
16328
16330
  }
16329
16331
  if (before !== void 0) {
16330
- lines.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16332
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16331
16333
  }
16332
16334
  if (after !== void 0) {
16333
- lines.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16335
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16334
16336
  }
16335
16337
  }
16336
- return "\n" + lines.join("\n");
16338
+ if (header.length + body.length <= MAX_DIFF_LINES) {
16339
+ return "\n" + [...header, ...body].join("\n");
16340
+ }
16341
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED) || l.startsWith(ANSI_GREEN));
16342
+ const notice = `${ANSI_DIM}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET}`;
16343
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
16337
16344
  }
16338
16345
  function toSafeNumber(value) {
16339
16346
  return typeof value === "bigint" ? Number(value) : value;
@@ -16357,10 +16364,10 @@ function assertInsertArguments(input) {
16357
16364
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16358
16365
  }
16359
16366
  if (input.replaceAll !== void 0) {
16360
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? replaceAll");
16367
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? replaceAll");
16361
16368
  }
16362
16369
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16363
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? startLine/endLine/startAnchor/endAnchor");
16370
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? startLine/endLine/startAnchor/endAnchor");
16364
16371
  }
16365
16372
  }
16366
16373
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16384,9 +16391,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16384
16391
  searchFrom = index + token.length;
16385
16392
  }
16386
16393
  if (count === 0) {
16387
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16394
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16388
16395
  }
16389
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${occurrence} \u5904`);
16396
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${occurrence} \u5904`);
16390
16397
  }
16391
16398
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16392
16399
  const alignedContent = newlineStyle === "crlf" ? content.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") : newlineStyle === "lf" ? content.replace(/\r\n/g, "\n") : content;
@@ -16395,7 +16402,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16395
16402
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16396
16403
  if (alreadyExists) {
16397
16404
  if (ifExists === "error") {
16398
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
16405
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
16399
16406
  }
16400
16407
  if (ifExists === "skip") {
16401
16408
  return {
@@ -16436,7 +16443,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16436
16443
  }
16437
16444
  function assertNotBinary(buffer) {
16438
16445
  if (buffer.includes(0)) {
16439
- throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD\uFFFD? GBK \u6587\u672C\u5904\u7406");
16446
+ throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD? GBK \u6587\u672C\u5904\u7406");
16440
16447
  }
16441
16448
  }
16442
16449
  function detectNewlineStyle(text) {
@@ -16517,14 +16524,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16517
16524
  if (startAnchor) {
16518
16525
  const found = text.indexOf(startAnchor);
16519
16526
  if (found === -1) {
16520
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD\uFFFD?: ${startAnchor}`);
16527
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD?: ${startAnchor}`);
16521
16528
  }
16522
16529
  rangeStart = found + startAnchor.length;
16523
16530
  }
16524
16531
  if (endAnchor) {
16525
16532
  const found = text.indexOf(endAnchor, rangeStart);
16526
16533
  if (found === -1) {
16527
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD\uFFFD?: ${endAnchor}`);
16534
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD?: ${endAnchor}`);
16528
16535
  }
16529
16536
  rangeEnd = found;
16530
16537
  }
@@ -16577,9 +16584,9 @@ function getNearestContext(content, oldString) {
16577
16584
  }
16578
16585
  function buildNoMatchMessage(content, oldString) {
16579
16586
  return [
16580
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16581
- "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD\uFFFD?",
16582
- "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD\uFFFD?",
16587
+ "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD?",
16588
+ "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD?",
16589
+ "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD?",
16583
16590
  getNearestContext(content, oldString)
16584
16591
  ].join("\n");
16585
16592
  }
@@ -16702,10 +16709,10 @@ async function resolveReadableGbkFile(input) {
16702
16709
  try {
16703
16710
  stat = await fs2.stat(candidatePath);
16704
16711
  } catch (error45) {
16705
- throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD\uFFFD?: ${candidatePath}`, error45);
16712
+ throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD?: ${candidatePath}`, error45);
16706
16713
  }
16707
16714
  if (stat.isDirectory()) {
16708
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16715
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16709
16716
  }
16710
16717
  return {
16711
16718
  filePath: candidatePath,
@@ -16902,7 +16909,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16902
16909
  } else if (occurrencesBefore === 0) {
16903
16910
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16904
16911
  } else if (occurrencesBefore > 1) {
16905
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${oldString}`);
16912
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${oldString}`);
16906
16913
  }
16907
16914
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16908
16915
  return {
@@ -17007,7 +17014,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17007
17014
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17008
17015
  if (alreadyExists) {
17009
17016
  if (input.ifExists === "error") {
17010
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17017
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17011
17018
  }
17012
17019
  if (input.ifExists === "skip") {
17013
17020
  skipped = true;
@@ -17054,7 +17061,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17054
17061
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17055
17062
  if (alreadyExists) {
17056
17063
  if (input.ifExists === "error") {
17057
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17064
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17058
17065
  }
17059
17066
  if (input.ifExists === "skip") {
17060
17067
  skipped = true;
@@ -17068,10 +17075,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17068
17075
  }
17069
17076
  }
17070
17077
  if (!inserted && totalMatches === 0) {
17071
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${input.anchor}`);
17078
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${input.anchor}`);
17072
17079
  }
17073
17080
  if (!inserted && totalMatches > 0) {
17074
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${input.occurrence} \u5904`);
17081
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${input.occurrence} \u5904`);
17075
17082
  }
17076
17083
  await finalizeInserted();
17077
17084
  await handle.close();
@@ -17134,10 +17141,10 @@ async function replaceLargeGbkFileText(input) {
17134
17141
  });
17135
17142
  await flushText("", true);
17136
17143
  if (occurrencesBefore === 0) {
17137
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD\uFFFD?: ${input.oldString}`);
17144
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD?: ${input.oldString}`);
17138
17145
  }
17139
17146
  if (!input.replaceAll && occurrencesBefore > 1) {
17140
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${input.oldString}`);
17147
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${input.oldString}`);
17141
17148
  }
17142
17149
  await handle.close();
17143
17150
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17286,7 +17293,7 @@ async function replaceGbkFileText(input) {
17286
17293
  } else if (occurrencesBefore === 0) {
17287
17294
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17288
17295
  } else if (occurrencesBefore > 1) {
17289
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${effectiveOldString}`);
17296
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${effectiveOldString}`);
17290
17297
  }
17291
17298
  const fileNewlineStyle = detectNewlineStyle(current.content);
17292
17299
  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;
@@ -17307,7 +17314,22 @@ async function replaceGbkFileText(input) {
17307
17314
  };
17308
17315
  }
17309
17316
 
17317
+ // src/lib/model-context.ts
17318
+ var _currentContextTokens = null;
17319
+ function getMaxOutputChars(fallback = 8e3) {
17320
+ if (_currentContextTokens === null) return fallback;
17321
+ const computed = Math.round(_currentContextTokens * 0.01 * 4);
17322
+ return Math.max(4e3, Math.min(32e3, computed));
17323
+ }
17324
+
17310
17325
  // src/tools/gbk_edit.ts
17326
+ function truncateToolOutput(content) {
17327
+ const maxChars = getMaxOutputChars();
17328
+ if (content.length <= maxChars) return content;
17329
+ const truncated = content.slice(0, maxChars);
17330
+ return truncated + `
17331
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
17332
+ }
17311
17333
  var gbk_edit_default = tool({
17312
17334
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17313
17335
 
@@ -17377,8 +17399,8 @@ Insert mode:
17377
17399
  diffPreview
17378
17400
  }
17379
17401
  });
17380
- if (diffPreview) return diffPreview;
17381
- return JSON.stringify(result, null, 2);
17402
+ if (diffPreview) return truncateToolOutput(diffPreview);
17403
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17382
17404
  }
17383
17405
  });
17384
17406
  export {
@@ -16329,7 +16329,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16329
16329
  }
16330
16330
  function assertNotBinary(buffer) {
16331
16331
  if (buffer.includes(0)) {
16332
- throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD\uFFFD? GBK \u6587\u672C\u5904\u7406");
16332
+ throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD? GBK \u6587\u672C\u5904\u7406");
16333
16333
  }
16334
16334
  }
16335
16335
  function splitLinesWithNumbers(text, offset = 1, limit = 2e3) {
@@ -16420,10 +16420,10 @@ async function resolveReadableGbkFile(input) {
16420
16420
  try {
16421
16421
  stat = await fs2.stat(candidatePath);
16422
16422
  } catch (error45) {
16423
- throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD\uFFFD?: ${candidatePath}`, error45);
16423
+ throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD?: ${candidatePath}`, error45);
16424
16424
  }
16425
16425
  if (stat.isDirectory()) {
16426
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16426
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16427
16427
  }
16428
16428
  return {
16429
16429
  filePath: candidatePath,
@@ -16313,7 +16313,7 @@ function assertEncodingSupported(encoding) {
16313
16313
  }
16314
16314
  function assertNotBinary(buffer) {
16315
16315
  if (buffer.includes(0)) {
16316
- throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD\uFFFD? GBK \u6587\u672C\u5904\u7406");
16316
+ throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD? GBK \u6587\u672C\u5904\u7406");
16317
16317
  }
16318
16318
  }
16319
16319
  async function resolveReadableGbkFile(input) {
@@ -16324,10 +16324,10 @@ async function resolveReadableGbkFile(input) {
16324
16324
  try {
16325
16325
  stat = await fs2.stat(candidatePath);
16326
16326
  } catch (error45) {
16327
- throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD\uFFFD?: ${candidatePath}`, error45);
16327
+ throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD?: ${candidatePath}`, error45);
16328
16328
  }
16329
16329
  if (stat.isDirectory()) {
16330
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16330
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16331
16331
  }
16332
16332
  return {
16333
16333
  filePath: candidatePath,
@@ -16370,10 +16370,10 @@ async function writeGbkFile(input) {
16370
16370
  try {
16371
16371
  const stat = await fs2.stat(candidatePath);
16372
16372
  if (stat.isDirectory()) {
16373
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16373
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16374
16374
  }
16375
16375
  if (!overwrite) {
16376
- throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD\uFFFD?: ${candidatePath}`);
16376
+ throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD?: ${candidatePath}`);
16377
16377
  }
16378
16378
  } catch (error45) {
16379
16379
  if (error45 instanceof Error && "code" in error45) {
@@ -16581,29 +16581,36 @@ var ANSI_RED = "\x1B[31m";
16581
16581
  var ANSI_GREEN = "\x1B[32m";
16582
16582
  var ANSI_DIM = "\x1B[2m";
16583
16583
  var ANSI_RESET = "\x1B[0m";
16584
+ var MAX_DIFF_LINES = 80;
16584
16585
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
16585
16586
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16586
16587
  const afterLines = normalizeNewlines(afterText).split("\n");
16587
16588
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16588
- const lines = [
16589
+ const header = [
16589
16590
  `${ANSI_DIM}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET}`,
16590
16591
  `${ANSI_DIM}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET}`
16591
16592
  ];
16593
+ const body = [];
16592
16594
  for (let index = 0; index < maxLines; index += 1) {
16593
16595
  const before = beforeLines[index];
16594
16596
  const after = afterLines[index];
16595
16597
  if (before !== void 0 && after !== void 0 && before === after) {
16596
- lines.push(` ${before}`);
16598
+ body.push(` ${before}`);
16597
16599
  continue;
16598
16600
  }
16599
16601
  if (before !== void 0) {
16600
- lines.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16602
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16601
16603
  }
16602
16604
  if (after !== void 0) {
16603
- lines.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16605
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16604
16606
  }
16605
16607
  }
16606
- return "\n" + lines.join("\n");
16608
+ if (header.length + body.length <= MAX_DIFF_LINES) {
16609
+ return "\n" + [...header, ...body].join("\n");
16610
+ }
16611
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED) || l.startsWith(ANSI_GREEN));
16612
+ const notice = `${ANSI_DIM}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET}`;
16613
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
16607
16614
  }
16608
16615
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16609
16616
  const alignedContent = alignTextToNewlineStyle(content, newlineStyle);
@@ -16979,11 +16986,26 @@ async function replaceTextFileText(input) {
16979
16986
  bytesRead: loaded.bytesRead,
16980
16987
  bytesWritten: buffer.byteLength,
16981
16988
  newlineStyle: detectNewlineStyle(outputText),
16982
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
16989
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
16983
16990
  };
16984
16991
  }
16985
16992
 
16993
+ // src/lib/model-context.ts
16994
+ var _currentContextTokens = null;
16995
+ function getMaxOutputChars(fallback = 8e3) {
16996
+ if (_currentContextTokens === null) return fallback;
16997
+ const computed = Math.round(_currentContextTokens * 0.01 * 4);
16998
+ return Math.max(4e3, Math.min(32e3, computed));
16999
+ }
17000
+
16986
17001
  // src/tools/text_edit.ts
17002
+ function truncateToolOutput(content) {
17003
+ const maxChars = getMaxOutputChars();
17004
+ if (content.length <= maxChars) return content;
17005
+ const truncated = content.slice(0, maxChars);
17006
+ return truncated + `
17007
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
17008
+ }
16987
17009
  var text_edit_default = tool({
16988
17010
  description: `Edit text files with automatic encoding detection and preservation.
16989
17011
 
@@ -17034,8 +17056,8 @@ var text_edit_default = tool({
17034
17056
  diffPreview
17035
17057
  }
17036
17058
  });
17037
- if (diffPreview) return diffPreview;
17038
- return JSON.stringify(result, null, 2);
17059
+ if (diffPreview) return truncateToolOutput(diffPreview);
17060
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17039
17061
  }
17040
17062
  });
17041
17063
  export {
@@ -16312,29 +16312,36 @@ var ANSI_RED = "\x1B[31m";
16312
16312
  var ANSI_GREEN = "\x1B[32m";
16313
16313
  var ANSI_DIM = "\x1B[2m";
16314
16314
  var ANSI_RESET = "\x1B[0m";
16315
+ var MAX_DIFF_LINES = 80;
16315
16316
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16316
16317
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16317
16318
  const afterLines = normalizeNewlines(afterText).split("\n");
16318
16319
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16319
- const lines = [
16320
+ const header = [
16320
16321
  `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16321
16322
  `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16322
16323
  ];
16324
+ const body = [];
16323
16325
  for (let index = 0; index < maxLines; index += 1) {
16324
16326
  const before = beforeLines[index];
16325
16327
  const after = afterLines[index];
16326
16328
  if (before !== void 0 && after !== void 0 && before === after) {
16327
- lines.push(` ${before}`);
16329
+ body.push(` ${before}`);
16328
16330
  continue;
16329
16331
  }
16330
16332
  if (before !== void 0) {
16331
- lines.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16333
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16332
16334
  }
16333
16335
  if (after !== void 0) {
16334
- lines.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16336
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16335
16337
  }
16336
16338
  }
16337
- return "\n" + lines.join("\n");
16339
+ if (header.length + body.length <= MAX_DIFF_LINES) {
16340
+ return "\n" + [...header, ...body].join("\n");
16341
+ }
16342
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED) || l.startsWith(ANSI_GREEN));
16343
+ const notice = `${ANSI_DIM}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET}`;
16344
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
16338
16345
  }
16339
16346
  function toSafeNumber(value) {
16340
16347
  return typeof value === "bigint" ? Number(value) : value;
@@ -16358,10 +16365,10 @@ function assertInsertArguments(input) {
16358
16365
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16359
16366
  }
16360
16367
  if (input.replaceAll !== void 0) {
16361
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? replaceAll");
16368
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? replaceAll");
16362
16369
  }
16363
16370
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16364
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? startLine/endLine/startAnchor/endAnchor");
16371
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? startLine/endLine/startAnchor/endAnchor");
16365
16372
  }
16366
16373
  }
16367
16374
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16385,9 +16392,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16385
16392
  searchFrom = index + token.length;
16386
16393
  }
16387
16394
  if (count === 0) {
16388
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16395
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16389
16396
  }
16390
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${occurrence} \u5904`);
16397
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${occurrence} \u5904`);
16391
16398
  }
16392
16399
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16393
16400
  const alignedContent = newlineStyle === "crlf" ? content.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") : newlineStyle === "lf" ? content.replace(/\r\n/g, "\n") : content;
@@ -16396,7 +16403,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16396
16403
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16397
16404
  if (alreadyExists) {
16398
16405
  if (ifExists === "error") {
16399
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
16406
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
16400
16407
  }
16401
16408
  if (ifExists === "skip") {
16402
16409
  return {
@@ -16437,7 +16444,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16437
16444
  }
16438
16445
  function assertNotBinary(buffer) {
16439
16446
  if (buffer.includes(0)) {
16440
- throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD\uFFFD? GBK \u6587\u672C\u5904\u7406");
16447
+ throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD? GBK \u6587\u672C\u5904\u7406");
16441
16448
  }
16442
16449
  }
16443
16450
  function splitLinesWithNumbers(text, offset = 1, limit = 2e3) {
@@ -16567,14 +16574,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16567
16574
  if (startAnchor) {
16568
16575
  const found = text.indexOf(startAnchor);
16569
16576
  if (found === -1) {
16570
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD\uFFFD?: ${startAnchor}`);
16577
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD?: ${startAnchor}`);
16571
16578
  }
16572
16579
  rangeStart = found + startAnchor.length;
16573
16580
  }
16574
16581
  if (endAnchor) {
16575
16582
  const found = text.indexOf(endAnchor, rangeStart);
16576
16583
  if (found === -1) {
16577
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD\uFFFD?: ${endAnchor}`);
16584
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD?: ${endAnchor}`);
16578
16585
  }
16579
16586
  rangeEnd = found;
16580
16587
  }
@@ -16627,9 +16634,9 @@ function getNearestContext(content, oldString) {
16627
16634
  }
16628
16635
  function buildNoMatchMessage(content, oldString) {
16629
16636
  return [
16630
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16631
- "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD\uFFFD?",
16632
- "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD\uFFFD?",
16637
+ "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD?",
16638
+ "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD?",
16639
+ "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD?",
16633
16640
  getNearestContext(content, oldString)
16634
16641
  ].join("\n");
16635
16642
  }
@@ -16752,10 +16759,10 @@ async function resolveReadableGbkFile(input) {
16752
16759
  try {
16753
16760
  stat = await fs2.stat(candidatePath);
16754
16761
  } catch (error45) {
16755
- throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD\uFFFD?: ${candidatePath}`, error45);
16762
+ throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD?: ${candidatePath}`, error45);
16756
16763
  }
16757
16764
  if (stat.isDirectory()) {
16758
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16765
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16759
16766
  }
16760
16767
  return {
16761
16768
  filePath: candidatePath,
@@ -16960,7 +16967,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16960
16967
  } else if (occurrencesBefore === 0) {
16961
16968
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16962
16969
  } else if (occurrencesBefore > 1) {
16963
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${oldString}`);
16970
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${oldString}`);
16964
16971
  }
16965
16972
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16966
16973
  return {
@@ -17065,7 +17072,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17065
17072
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17066
17073
  if (alreadyExists) {
17067
17074
  if (input.ifExists === "error") {
17068
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17075
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17069
17076
  }
17070
17077
  if (input.ifExists === "skip") {
17071
17078
  skipped = true;
@@ -17112,7 +17119,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17112
17119
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17113
17120
  if (alreadyExists) {
17114
17121
  if (input.ifExists === "error") {
17115
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17122
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17116
17123
  }
17117
17124
  if (input.ifExists === "skip") {
17118
17125
  skipped = true;
@@ -17126,10 +17133,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17126
17133
  }
17127
17134
  }
17128
17135
  if (!inserted && totalMatches === 0) {
17129
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${input.anchor}`);
17136
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${input.anchor}`);
17130
17137
  }
17131
17138
  if (!inserted && totalMatches > 0) {
17132
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${input.occurrence} \u5904`);
17139
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${input.occurrence} \u5904`);
17133
17140
  }
17134
17141
  await finalizeInserted();
17135
17142
  await handle.close();
@@ -17192,10 +17199,10 @@ async function replaceLargeGbkFileText(input) {
17192
17199
  });
17193
17200
  await flushText("", true);
17194
17201
  if (occurrencesBefore === 0) {
17195
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD\uFFFD?: ${input.oldString}`);
17202
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD?: ${input.oldString}`);
17196
17203
  }
17197
17204
  if (!input.replaceAll && occurrencesBefore > 1) {
17198
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${input.oldString}`);
17205
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${input.oldString}`);
17199
17206
  }
17200
17207
  await handle.close();
17201
17208
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17421,7 +17428,7 @@ async function replaceGbkFileText(input) {
17421
17428
  } else if (occurrencesBefore === 0) {
17422
17429
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17423
17430
  } else if (occurrencesBefore > 1) {
17424
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${effectiveOldString}`);
17431
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${effectiveOldString}`);
17425
17432
  }
17426
17433
  const fileNewlineStyle = detectNewlineStyle(current.content);
17427
17434
  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;
@@ -17554,10 +17561,10 @@ async function writeGbkFile(input) {
17554
17561
  try {
17555
17562
  const stat = await fs2.stat(candidatePath);
17556
17563
  if (stat.isDirectory()) {
17557
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
17564
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
17558
17565
  }
17559
17566
  if (!overwrite) {
17560
- throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD\uFFFD?: ${candidatePath}`);
17567
+ throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD?: ${candidatePath}`);
17561
17568
  }
17562
17569
  } catch (error45) {
17563
17570
  if (error45 instanceof Error && "code" in error45) {
@@ -17603,7 +17610,25 @@ async function writeGbkFile(input) {
17603
17610
  }
17604
17611
  }
17605
17612
 
17613
+ // src/lib/model-context.ts
17614
+ var _currentContextTokens = null;
17615
+ function setCurrentContextTokens(tokens) {
17616
+ _currentContextTokens = tokens;
17617
+ }
17618
+ function getMaxOutputChars(fallback = 8e3) {
17619
+ if (_currentContextTokens === null) return fallback;
17620
+ const computed = Math.round(_currentContextTokens * 0.01 * 4);
17621
+ return Math.max(4e3, Math.min(32e3, computed));
17622
+ }
17623
+
17606
17624
  // src/tools/gbk_edit.ts
17625
+ function truncateToolOutput(content) {
17626
+ const maxChars = getMaxOutputChars();
17627
+ if (content.length <= maxChars) return content;
17628
+ const truncated = content.slice(0, maxChars);
17629
+ return truncated + `
17630
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
17631
+ }
17607
17632
  var gbk_edit_default = tool({
17608
17633
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17609
17634
 
@@ -17673,8 +17698,8 @@ Insert mode:
17673
17698
  diffPreview
17674
17699
  }
17675
17700
  });
17676
- if (diffPreview) return diffPreview;
17677
- return JSON.stringify(result, null, 2);
17701
+ if (diffPreview) return truncateToolOutput(diffPreview);
17702
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17678
17703
  }
17679
17704
  });
17680
17705
 
@@ -18059,29 +18084,36 @@ var ANSI_RED2 = "\x1B[31m";
18059
18084
  var ANSI_GREEN2 = "\x1B[32m";
18060
18085
  var ANSI_DIM2 = "\x1B[2m";
18061
18086
  var ANSI_RESET2 = "\x1B[0m";
18087
+ var MAX_DIFF_LINES2 = 80;
18062
18088
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
18063
18089
  const beforeLines = normalizeNewlines2(beforeText).split("\n");
18064
18090
  const afterLines = normalizeNewlines2(afterText).split("\n");
18065
18091
  const maxLines = Math.max(beforeLines.length, afterLines.length);
18066
- const lines = [
18092
+ const header = [
18067
18093
  `${ANSI_DIM2}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`,
18068
18094
  `${ANSI_DIM2}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`
18069
18095
  ];
18096
+ const body = [];
18070
18097
  for (let index = 0; index < maxLines; index += 1) {
18071
18098
  const before = beforeLines[index];
18072
18099
  const after = afterLines[index];
18073
18100
  if (before !== void 0 && after !== void 0 && before === after) {
18074
- lines.push(` ${before}`);
18101
+ body.push(` ${before}`);
18075
18102
  continue;
18076
18103
  }
18077
18104
  if (before !== void 0) {
18078
- lines.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18105
+ body.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18079
18106
  }
18080
18107
  if (after !== void 0) {
18081
- lines.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18108
+ body.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18082
18109
  }
18083
18110
  }
18084
- return "\n" + lines.join("\n");
18111
+ if (header.length + body.length <= MAX_DIFF_LINES2) {
18112
+ return "\n" + [...header, ...body].join("\n");
18113
+ }
18114
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED2) || l.startsWith(ANSI_GREEN2));
18115
+ const notice = `${ANSI_DIM2}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET2}`;
18116
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
18085
18117
  }
18086
18118
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
18087
18119
  const alignedContent = alignTextToNewlineStyle2(content, newlineStyle);
@@ -18728,11 +18760,18 @@ async function replaceTextFileText(input) {
18728
18760
  bytesRead: loaded.bytesRead,
18729
18761
  bytesWritten: buffer.byteLength,
18730
18762
  newlineStyle: detectNewlineStyle(outputText),
18731
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
18763
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
18732
18764
  };
18733
18765
  }
18734
18766
 
18735
18767
  // src/tools/text_edit.ts
18768
+ function truncateToolOutput2(content) {
18769
+ const maxChars = getMaxOutputChars();
18770
+ if (content.length <= maxChars) return content;
18771
+ const truncated = content.slice(0, maxChars);
18772
+ return truncated + `
18773
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
18774
+ }
18736
18775
  var text_edit_default = tool({
18737
18776
  description: `Edit text files with automatic encoding detection and preservation.
18738
18777
 
@@ -18783,8 +18822,8 @@ var text_edit_default = tool({
18783
18822
  diffPreview
18784
18823
  }
18785
18824
  });
18786
- if (diffPreview) return diffPreview;
18787
- return JSON.stringify(result, null, 2);
18825
+ if (diffPreview) return truncateToolOutput2(diffPreview);
18826
+ return truncateToolOutput2(JSON.stringify(result, null, 2));
18788
18827
  }
18789
18828
  });
18790
18829
 
@@ -18876,6 +18915,12 @@ function createOpencodeGbkHooks() {
18876
18915
  text_write: text_write_default,
18877
18916
  text_edit: text_edit_default
18878
18917
  },
18918
+ async "chat.params"(input) {
18919
+ const contextTokens = input.model?.limit?.context;
18920
+ if (typeof contextTokens === "number" && contextTokens > 0) {
18921
+ setCurrentContextTokens(contextTokens);
18922
+ }
18923
+ },
18879
18924
  async "experimental.chat.system.transform"(_input, output) {
18880
18925
  appendTextToolSystemPrompt(output.system);
18881
18926
  }
@@ -16312,29 +16312,36 @@ var ANSI_RED = "\x1B[31m";
16312
16312
  var ANSI_GREEN = "\x1B[32m";
16313
16313
  var ANSI_DIM = "\x1B[2m";
16314
16314
  var ANSI_RESET = "\x1B[0m";
16315
+ var MAX_DIFF_LINES = 80;
16315
16316
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16316
16317
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16317
16318
  const afterLines = normalizeNewlines(afterText).split("\n");
16318
16319
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16319
- const lines = [
16320
+ const header = [
16320
16321
  `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16321
16322
  `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16322
16323
  ];
16324
+ const body = [];
16323
16325
  for (let index = 0; index < maxLines; index += 1) {
16324
16326
  const before = beforeLines[index];
16325
16327
  const after = afterLines[index];
16326
16328
  if (before !== void 0 && after !== void 0 && before === after) {
16327
- lines.push(` ${before}`);
16329
+ body.push(` ${before}`);
16328
16330
  continue;
16329
16331
  }
16330
16332
  if (before !== void 0) {
16331
- lines.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16333
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16332
16334
  }
16333
16335
  if (after !== void 0) {
16334
- lines.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16336
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16335
16337
  }
16336
16338
  }
16337
- return "\n" + lines.join("\n");
16339
+ if (header.length + body.length <= MAX_DIFF_LINES) {
16340
+ return "\n" + [...header, ...body].join("\n");
16341
+ }
16342
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED) || l.startsWith(ANSI_GREEN));
16343
+ const notice = `${ANSI_DIM}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET}`;
16344
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
16338
16345
  }
16339
16346
  function toSafeNumber(value) {
16340
16347
  return typeof value === "bigint" ? Number(value) : value;
@@ -16358,10 +16365,10 @@ function assertInsertArguments(input) {
16358
16365
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16359
16366
  }
16360
16367
  if (input.replaceAll !== void 0) {
16361
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? replaceAll");
16368
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? replaceAll");
16362
16369
  }
16363
16370
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16364
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD\uFFFD? startLine/endLine/startAnchor/endAnchor");
16371
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\uFFFD? startLine/endLine/startAnchor/endAnchor");
16365
16372
  }
16366
16373
  }
16367
16374
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16385,9 +16392,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16385
16392
  searchFrom = index + token.length;
16386
16393
  }
16387
16394
  if (count === 0) {
16388
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16395
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16389
16396
  }
16390
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${occurrence} \u5904`);
16397
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${token} \u53EA\u627E\uFFFD? ${count} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${occurrence} \u5904`);
16391
16398
  }
16392
16399
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16393
16400
  const alignedContent = newlineStyle === "crlf" ? content.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n") : newlineStyle === "lf" ? content.replace(/\r\n/g, "\n") : content;
@@ -16396,7 +16403,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16396
16403
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16397
16404
  if (alreadyExists) {
16398
16405
  if (ifExists === "error") {
16399
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
16406
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
16400
16407
  }
16401
16408
  if (ifExists === "skip") {
16402
16409
  return {
@@ -16437,7 +16444,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16437
16444
  }
16438
16445
  function assertNotBinary(buffer) {
16439
16446
  if (buffer.includes(0)) {
16440
- throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD\uFFFD? GBK \u6587\u672C\u5904\u7406");
16447
+ throw createGbkError("GBK_BINARY_FILE", "\u7591\u4F3C\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\uFFFD? GBK \u6587\u672C\u5904\u7406");
16441
16448
  }
16442
16449
  }
16443
16450
  function splitLinesWithNumbers(text, offset = 1, limit = 2e3) {
@@ -16567,14 +16574,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16567
16574
  if (startAnchor) {
16568
16575
  const found = text.indexOf(startAnchor);
16569
16576
  if (found === -1) {
16570
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD\uFFFD?: ${startAnchor}`);
16577
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8D77\u59CB\u951A\uFFFD?: ${startAnchor}`);
16571
16578
  }
16572
16579
  rangeStart = found + startAnchor.length;
16573
16580
  }
16574
16581
  if (endAnchor) {
16575
16582
  const found = text.indexOf(endAnchor, rangeStart);
16576
16583
  if (found === -1) {
16577
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD\uFFFD?: ${endAnchor}`);
16584
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u7ED3\u675F\u951A\uFFFD?: ${endAnchor}`);
16578
16585
  }
16579
16586
  rangeEnd = found;
16580
16587
  }
@@ -16627,9 +16634,9 @@ function getNearestContext(content, oldString) {
16627
16634
  }
16628
16635
  function buildNoMatchMessage(content, oldString) {
16629
16636
  return [
16630
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16631
- "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD\uFFFD?",
16632
- "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD\uFFFD?",
16637
+ "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD?",
16638
+ "oldString \u5FC5\u987B\u4E0E\u6587\u4EF6\u5B9E\u9645\u5185\u5BB9\u5B8C\u5168\u5BF9\u5E94\uFF0C\u6216\u4EC5\u5728\u6362\uFFFD?/\u5C3E\u968F\u7A7A\u884C\u4E0A\u5B58\u5728\u8F7B\u5FAE\u5DEE\u5F02\uFFFD?",
16639
+ "\u6700\u63A5\u8FD1\u7684\u4E0A\u4E0B\u6587\uFFFD?",
16633
16640
  getNearestContext(content, oldString)
16634
16641
  ].join("\n");
16635
16642
  }
@@ -16752,10 +16759,10 @@ async function resolveReadableGbkFile(input) {
16752
16759
  try {
16753
16760
  stat = await fs2.stat(candidatePath);
16754
16761
  } catch (error45) {
16755
- throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD\uFFFD?: ${candidatePath}`, error45);
16762
+ throw createGbkError("GBK_FILE_NOT_FOUND", `\u6587\u4EF6\u4E0D\u5B58\uFFFD?: ${candidatePath}`, error45);
16756
16763
  }
16757
16764
  if (stat.isDirectory()) {
16758
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
16765
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
16759
16766
  }
16760
16767
  return {
16761
16768
  filePath: candidatePath,
@@ -16960,7 +16967,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16960
16967
  } else if (occurrencesBefore === 0) {
16961
16968
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16962
16969
  } else if (occurrencesBefore > 1) {
16963
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${oldString}`);
16970
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${oldString}`);
16964
16971
  }
16965
16972
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16966
16973
  return {
@@ -17065,7 +17072,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17065
17072
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17066
17073
  if (alreadyExists) {
17067
17074
  if (input.ifExists === "error") {
17068
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17075
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17069
17076
  }
17070
17077
  if (input.ifExists === "skip") {
17071
17078
  skipped = true;
@@ -17112,7 +17119,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17112
17119
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17113
17120
  if (alreadyExists) {
17114
17121
  if (input.ifExists === "error") {
17115
- throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD\uFFFD?");
17122
+ throw createGbkError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u4F4D\u7F6E\u5DF2\u5B58\u5728\u76F8\u540C\u5185\uFFFD?");
17116
17123
  }
17117
17124
  if (input.ifExists === "skip") {
17118
17125
  skipped = true;
@@ -17126,10 +17133,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17126
17133
  }
17127
17134
  }
17128
17135
  if (!inserted && totalMatches === 0) {
17129
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${input.anchor}`);
17136
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${input.anchor}`);
17130
17137
  }
17131
17138
  if (!inserted && totalMatches > 0) {
17132
- throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD\uFFFD? ${input.occurrence} \u5904`);
17139
+ throw createGbkError("GBK_NO_MATCH", `\u951A\u70B9 ${input.anchor} \u53EA\u627E\uFFFD? ${totalMatches} \u5904\uFF0C\u65E0\u6CD5\u4F7F\u7528\uFFFD? ${input.occurrence} \u5904`);
17133
17140
  }
17134
17141
  await finalizeInserted();
17135
17142
  await handle.close();
@@ -17192,10 +17199,10 @@ async function replaceLargeGbkFileText(input) {
17192
17199
  });
17193
17200
  await flushText("", true);
17194
17201
  if (occurrencesBefore === 0) {
17195
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD\uFFFD?: ${input.oldString}`);
17202
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u8981\u66FF\u6362\u7684\u5185\uFFFD?: ${input.oldString}`);
17196
17203
  }
17197
17204
  if (!input.replaceAll && occurrencesBefore > 1) {
17198
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${input.oldString}`);
17205
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${input.oldString}`);
17199
17206
  }
17200
17207
  await handle.close();
17201
17208
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17421,7 +17428,7 @@ async function replaceGbkFileText(input) {
17421
17428
  } else if (occurrencesBefore === 0) {
17422
17429
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17423
17430
  } else if (occurrencesBefore > 1) {
17424
- throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD\uFFFD?: ${effectiveOldString}`);
17431
+ throw createGbkError("GBK_MULTIPLE_MATCHES", `\u627E\u5230\u591A\u4E2A\u5339\u914D\uFFFD?: ${effectiveOldString}`);
17425
17432
  }
17426
17433
  const fileNewlineStyle = detectNewlineStyle(current.content);
17427
17434
  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;
@@ -17554,10 +17561,10 @@ async function writeGbkFile(input) {
17554
17561
  try {
17555
17562
  const stat = await fs2.stat(candidatePath);
17556
17563
  if (stat.isDirectory()) {
17557
- throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD\uFFFD?: ${candidatePath}`);
17564
+ throw createGbkError("GBK_IS_DIRECTORY", `\u76EE\u6807\u8DEF\u5F84\u662F\u76EE\uFFFD?: ${candidatePath}`);
17558
17565
  }
17559
17566
  if (!overwrite) {
17560
- throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD\uFFFD?: ${candidatePath}`);
17567
+ throw createGbkError("GBK_FILE_EXISTS", `\u76EE\u6807\u6587\u4EF6\u5DF2\u5B58\uFFFD?: ${candidatePath}`);
17561
17568
  }
17562
17569
  } catch (error45) {
17563
17570
  if (error45 instanceof Error && "code" in error45) {
@@ -17603,7 +17610,25 @@ async function writeGbkFile(input) {
17603
17610
  }
17604
17611
  }
17605
17612
 
17613
+ // src/lib/model-context.ts
17614
+ var _currentContextTokens = null;
17615
+ function setCurrentContextTokens(tokens) {
17616
+ _currentContextTokens = tokens;
17617
+ }
17618
+ function getMaxOutputChars(fallback = 8e3) {
17619
+ if (_currentContextTokens === null) return fallback;
17620
+ const computed = Math.round(_currentContextTokens * 0.01 * 4);
17621
+ return Math.max(4e3, Math.min(32e3, computed));
17622
+ }
17623
+
17606
17624
  // src/tools/gbk_edit.ts
17625
+ function truncateToolOutput(content) {
17626
+ const maxChars = getMaxOutputChars();
17627
+ if (content.length <= maxChars) return content;
17628
+ const truncated = content.slice(0, maxChars);
17629
+ return truncated + `
17630
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
17631
+ }
17607
17632
  var gbk_edit_default = tool({
17608
17633
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17609
17634
 
@@ -17673,8 +17698,8 @@ Insert mode:
17673
17698
  diffPreview
17674
17699
  }
17675
17700
  });
17676
- if (diffPreview) return diffPreview;
17677
- return JSON.stringify(result, null, 2);
17701
+ if (diffPreview) return truncateToolOutput(diffPreview);
17702
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17678
17703
  }
17679
17704
  });
17680
17705
 
@@ -18059,29 +18084,36 @@ var ANSI_RED2 = "\x1B[31m";
18059
18084
  var ANSI_GREEN2 = "\x1B[32m";
18060
18085
  var ANSI_DIM2 = "\x1B[2m";
18061
18086
  var ANSI_RESET2 = "\x1B[0m";
18087
+ var MAX_DIFF_LINES2 = 80;
18062
18088
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
18063
18089
  const beforeLines = normalizeNewlines2(beforeText).split("\n");
18064
18090
  const afterLines = normalizeNewlines2(afterText).split("\n");
18065
18091
  const maxLines = Math.max(beforeLines.length, afterLines.length);
18066
- const lines = [
18092
+ const header = [
18067
18093
  `${ANSI_DIM2}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`,
18068
18094
  `${ANSI_DIM2}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`
18069
18095
  ];
18096
+ const body = [];
18070
18097
  for (let index = 0; index < maxLines; index += 1) {
18071
18098
  const before = beforeLines[index];
18072
18099
  const after = afterLines[index];
18073
18100
  if (before !== void 0 && after !== void 0 && before === after) {
18074
- lines.push(` ${before}`);
18101
+ body.push(` ${before}`);
18075
18102
  continue;
18076
18103
  }
18077
18104
  if (before !== void 0) {
18078
- lines.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18105
+ body.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18079
18106
  }
18080
18107
  if (after !== void 0) {
18081
- lines.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18108
+ body.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18082
18109
  }
18083
18110
  }
18084
- return "\n" + lines.join("\n");
18111
+ if (header.length + body.length <= MAX_DIFF_LINES2) {
18112
+ return "\n" + [...header, ...body].join("\n");
18113
+ }
18114
+ const changedOnly = body.filter((l) => l.startsWith(ANSI_RED2) || l.startsWith(ANSI_GREEN2));
18115
+ const notice = `${ANSI_DIM2}... (\u4E0A\u4E0B\u6587\u5DF2\u7701\u7565\uFF0C\u4EC5\u663E\u793A\u53D8\u66F4\u884C)${ANSI_RESET2}`;
18116
+ return "\n" + [...header, notice, ...changedOnly].join("\n");
18085
18117
  }
18086
18118
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
18087
18119
  const alignedContent = alignTextToNewlineStyle2(content, newlineStyle);
@@ -18728,11 +18760,18 @@ async function replaceTextFileText(input) {
18728
18760
  bytesRead: loaded.bytesRead,
18729
18761
  bytesWritten: buffer.byteLength,
18730
18762
  newlineStyle: detectNewlineStyle(outputText),
18731
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
18763
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
18732
18764
  };
18733
18765
  }
18734
18766
 
18735
18767
  // src/tools/text_edit.ts
18768
+ function truncateToolOutput2(content) {
18769
+ const maxChars = getMaxOutputChars();
18770
+ if (content.length <= maxChars) return content;
18771
+ const truncated = content.slice(0, maxChars);
18772
+ return truncated + `
18773
+ \x1B[2m... (\u8F93\u51FA\u5DF2\u622A\u65AD\uFF0C\u8D85\u51FA ${maxChars} \u5B57\u7B26\u4E0A\u9650)\x1B[0m`;
18774
+ }
18736
18775
  var text_edit_default = tool({
18737
18776
  description: `Edit text files with automatic encoding detection and preservation.
18738
18777
 
@@ -18783,8 +18822,8 @@ var text_edit_default = tool({
18783
18822
  diffPreview
18784
18823
  }
18785
18824
  });
18786
- if (diffPreview) return diffPreview;
18787
- return JSON.stringify(result, null, 2);
18825
+ if (diffPreview) return truncateToolOutput2(diffPreview);
18826
+ return truncateToolOutput2(JSON.stringify(result, null, 2));
18788
18827
  }
18789
18828
  });
18790
18829
 
@@ -18876,6 +18915,12 @@ function createOpencodeGbkHooks() {
18876
18915
  text_write: text_write_default,
18877
18916
  text_edit: text_edit_default
18878
18917
  },
18918
+ async "chat.params"(input) {
18919
+ const contextTokens = input.model?.limit?.context;
18920
+ if (typeof contextTokens === "number" && contextTokens > 0) {
18921
+ setCurrentContextTokens(contextTokens);
18922
+ }
18923
+ },
18879
18924
  async "experimental.chat.system.transform"(_input, output) {
18880
18925
  appendTextToolSystemPrompt(output.system);
18881
18926
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "manifestVersion": 1,
3
3
  "packageName": "opencode-gbk-tools",
4
- "packageVersion": "0.1.19",
4
+ "packageVersion": "0.1.23",
5
5
  "artifacts": [
6
6
  {
7
7
  "relativePath": "plugins/opencode-gbk-tools.js",
8
8
  "kind": "plugin",
9
- "expectedHash": "481836ea0b68a3b872d46346cfa1bc270966d57d566b66846df882aa4885972e",
9
+ "expectedHash": "28e00665097934ea390c85f9a991f9074f77d31ff7ffe6f118035e834b911d2d",
10
10
  "hashAlgorithm": "sha256"
11
11
  }
12
12
  ]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-gbk-tools",
3
- "version": "0.1.21",
3
+ "version": "0.1.23",
4
4
  "description": "Auto-encoding text tools plus GBK/GB18030 tools for OpenCode",
5
5
  "type": "module",
6
6
  "main": "./dist/plugin/index.js",