opencode-gbk-tools 0.1.22 → 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.
@@ -16307,29 +16307,40 @@ async function assertPathAllowed(filePath, context, allowExternal = false) {
16307
16307
  var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
16308
16308
  var STREAM_READ_CHUNK_SIZE_BYTES = 1024 * 1024;
16309
16309
  var gbkLineIndexCache = /* @__PURE__ */ new Map();
16310
+ var ANSI_RED = "\x1B[31m";
16311
+ var ANSI_GREEN = "\x1B[32m";
16312
+ var ANSI_DIM = "\x1B[2m";
16313
+ var ANSI_RESET = "\x1B[0m";
16314
+ var MAX_DIFF_LINES = 80;
16310
16315
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16311
16316
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16312
16317
  const afterLines = normalizeNewlines(afterText).split("\n");
16313
16318
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16314
- const lines = [
16315
- `--- ${path2.basename(filePath)} (${encoding})`,
16316
- `+++ ${path2.basename(filePath)} (${encoding})`
16319
+ const header = [
16320
+ `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16321
+ `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16317
16322
  ];
16323
+ const body = [];
16318
16324
  for (let index = 0; index < maxLines; index += 1) {
16319
16325
  const before = beforeLines[index];
16320
16326
  const after = afterLines[index];
16321
16327
  if (before !== void 0 && after !== void 0 && before === after) {
16322
- lines.push(` ${before}`);
16328
+ body.push(` ${before}`);
16323
16329
  continue;
16324
16330
  }
16325
16331
  if (before !== void 0) {
16326
- lines.push(`-${before}`);
16332
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16327
16333
  }
16328
16334
  if (after !== void 0) {
16329
- lines.push(`+${after}`);
16335
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16330
16336
  }
16331
16337
  }
16332
- return 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");
16333
16344
  }
16334
16345
  function toSafeNumber(value) {
16335
16346
  return typeof value === "bigint" ? Number(value) : value;
@@ -16353,10 +16364,10 @@ function assertInsertArguments(input) {
16353
16364
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16354
16365
  }
16355
16366
  if (input.replaceAll !== void 0) {
16356
- 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");
16357
16368
  }
16358
16369
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16359
- 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");
16360
16371
  }
16361
16372
  }
16362
16373
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16380,9 +16391,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16380
16391
  searchFrom = index + token.length;
16381
16392
  }
16382
16393
  if (count === 0) {
16383
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16394
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16384
16395
  }
16385
- 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`);
16386
16397
  }
16387
16398
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16388
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;
@@ -16391,7 +16402,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16391
16402
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16392
16403
  if (alreadyExists) {
16393
16404
  if (ifExists === "error") {
16394
- 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?");
16395
16406
  }
16396
16407
  if (ifExists === "skip") {
16397
16408
  return {
@@ -16432,7 +16443,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16432
16443
  }
16433
16444
  function assertNotBinary(buffer) {
16434
16445
  if (buffer.includes(0)) {
16435
- 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");
16436
16447
  }
16437
16448
  }
16438
16449
  function detectNewlineStyle(text) {
@@ -16513,14 +16524,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16513
16524
  if (startAnchor) {
16514
16525
  const found = text.indexOf(startAnchor);
16515
16526
  if (found === -1) {
16516
- 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}`);
16517
16528
  }
16518
16529
  rangeStart = found + startAnchor.length;
16519
16530
  }
16520
16531
  if (endAnchor) {
16521
16532
  const found = text.indexOf(endAnchor, rangeStart);
16522
16533
  if (found === -1) {
16523
- 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}`);
16524
16535
  }
16525
16536
  rangeEnd = found;
16526
16537
  }
@@ -16573,9 +16584,9 @@ function getNearestContext(content, oldString) {
16573
16584
  }
16574
16585
  function buildNoMatchMessage(content, oldString) {
16575
16586
  return [
16576
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16577
- "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?",
16578
- "\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?",
16579
16590
  getNearestContext(content, oldString)
16580
16591
  ].join("\n");
16581
16592
  }
@@ -16698,10 +16709,10 @@ async function resolveReadableGbkFile(input) {
16698
16709
  try {
16699
16710
  stat = await fs2.stat(candidatePath);
16700
16711
  } catch (error45) {
16701
- 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);
16702
16713
  }
16703
16714
  if (stat.isDirectory()) {
16704
- 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}`);
16705
16716
  }
16706
16717
  return {
16707
16718
  filePath: candidatePath,
@@ -16898,7 +16909,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16898
16909
  } else if (occurrencesBefore === 0) {
16899
16910
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16900
16911
  } else if (occurrencesBefore > 1) {
16901
- 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}`);
16902
16913
  }
16903
16914
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16904
16915
  return {
@@ -17003,7 +17014,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17003
17014
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17004
17015
  if (alreadyExists) {
17005
17016
  if (input.ifExists === "error") {
17006
- 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?");
17007
17018
  }
17008
17019
  if (input.ifExists === "skip") {
17009
17020
  skipped = true;
@@ -17050,7 +17061,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17050
17061
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17051
17062
  if (alreadyExists) {
17052
17063
  if (input.ifExists === "error") {
17053
- 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?");
17054
17065
  }
17055
17066
  if (input.ifExists === "skip") {
17056
17067
  skipped = true;
@@ -17064,10 +17075,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17064
17075
  }
17065
17076
  }
17066
17077
  if (!inserted && totalMatches === 0) {
17067
- 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}`);
17068
17079
  }
17069
17080
  if (!inserted && totalMatches > 0) {
17070
- 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`);
17071
17082
  }
17072
17083
  await finalizeInserted();
17073
17084
  await handle.close();
@@ -17130,10 +17141,10 @@ async function replaceLargeGbkFileText(input) {
17130
17141
  });
17131
17142
  await flushText("", true);
17132
17143
  if (occurrencesBefore === 0) {
17133
- 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}`);
17134
17145
  }
17135
17146
  if (!input.replaceAll && occurrencesBefore > 1) {
17136
- 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}`);
17137
17148
  }
17138
17149
  await handle.close();
17139
17150
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17282,7 +17293,7 @@ async function replaceGbkFileText(input) {
17282
17293
  } else if (occurrencesBefore === 0) {
17283
17294
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17284
17295
  } else if (occurrencesBefore > 1) {
17285
- 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}`);
17286
17297
  }
17287
17298
  const fileNewlineStyle = detectNewlineStyle(current.content);
17288
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;
@@ -17303,7 +17314,22 @@ async function replaceGbkFileText(input) {
17303
17314
  };
17304
17315
  }
17305
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
+
17306
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
+ }
17307
17333
  var gbk_edit_default = tool({
17308
17334
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17309
17335
 
@@ -17373,8 +17399,8 @@ Insert mode:
17373
17399
  diffPreview
17374
17400
  }
17375
17401
  });
17376
- if (diffPreview) return diffPreview;
17377
- return JSON.stringify(result, null, 2);
17402
+ if (diffPreview) return truncateToolOutput(diffPreview);
17403
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17378
17404
  }
17379
17405
  });
17380
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) {
@@ -16577,29 +16577,40 @@ function assertInsertArguments(input) {
16577
16577
  throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
16578
16578
  }
16579
16579
  }
16580
+ var ANSI_RED = "\x1B[31m";
16581
+ var ANSI_GREEN = "\x1B[32m";
16582
+ var ANSI_DIM = "\x1B[2m";
16583
+ var ANSI_RESET = "\x1B[0m";
16584
+ var MAX_DIFF_LINES = 80;
16580
16585
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
16581
16586
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16582
16587
  const afterLines = normalizeNewlines(afterText).split("\n");
16583
16588
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16584
- const lines = [
16585
- `--- ${path3.basename(filePath)} (${encoding})`,
16586
- `+++ ${path3.basename(filePath)} (${encoding})`
16589
+ const header = [
16590
+ `${ANSI_DIM}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET}`,
16591
+ `${ANSI_DIM}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET}`
16587
16592
  ];
16593
+ const body = [];
16588
16594
  for (let index = 0; index < maxLines; index += 1) {
16589
16595
  const before = beforeLines[index];
16590
16596
  const after = afterLines[index];
16591
16597
  if (before !== void 0 && after !== void 0 && before === after) {
16592
- lines.push(` ${before}`);
16598
+ body.push(` ${before}`);
16593
16599
  continue;
16594
16600
  }
16595
16601
  if (before !== void 0) {
16596
- lines.push(`-${before}`);
16602
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16597
16603
  }
16598
16604
  if (after !== void 0) {
16599
- lines.push(`+${after}`);
16605
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16600
16606
  }
16601
16607
  }
16602
- return 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");
16603
16614
  }
16604
16615
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16605
16616
  const alignedContent = alignTextToNewlineStyle(content, newlineStyle);
@@ -16975,11 +16986,26 @@ async function replaceTextFileText(input) {
16975
16986
  bytesRead: loaded.bytesRead,
16976
16987
  bytesWritten: buffer.byteLength,
16977
16988
  newlineStyle: detectNewlineStyle(outputText),
16978
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
16989
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
16979
16990
  };
16980
16991
  }
16981
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
+
16982
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
+ }
16983
17009
  var text_edit_default = tool({
16984
17010
  description: `Edit text files with automatic encoding detection and preservation.
16985
17011
 
@@ -17030,8 +17056,8 @@ var text_edit_default = tool({
17030
17056
  diffPreview
17031
17057
  }
17032
17058
  });
17033
- if (diffPreview) return diffPreview;
17034
- return JSON.stringify(result, null, 2);
17059
+ if (diffPreview) return truncateToolOutput(diffPreview);
17060
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17035
17061
  }
17036
17062
  });
17037
17063
  export {
@@ -16308,29 +16308,40 @@ async function assertPathAllowed(filePath, context, allowExternal = false) {
16308
16308
  var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
16309
16309
  var STREAM_READ_CHUNK_SIZE_BYTES = 1024 * 1024;
16310
16310
  var gbkLineIndexCache = /* @__PURE__ */ new Map();
16311
+ var ANSI_RED = "\x1B[31m";
16312
+ var ANSI_GREEN = "\x1B[32m";
16313
+ var ANSI_DIM = "\x1B[2m";
16314
+ var ANSI_RESET = "\x1B[0m";
16315
+ var MAX_DIFF_LINES = 80;
16311
16316
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16312
16317
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16313
16318
  const afterLines = normalizeNewlines(afterText).split("\n");
16314
16319
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16315
- const lines = [
16316
- `--- ${path2.basename(filePath)} (${encoding})`,
16317
- `+++ ${path2.basename(filePath)} (${encoding})`
16320
+ const header = [
16321
+ `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16322
+ `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16318
16323
  ];
16324
+ const body = [];
16319
16325
  for (let index = 0; index < maxLines; index += 1) {
16320
16326
  const before = beforeLines[index];
16321
16327
  const after = afterLines[index];
16322
16328
  if (before !== void 0 && after !== void 0 && before === after) {
16323
- lines.push(` ${before}`);
16329
+ body.push(` ${before}`);
16324
16330
  continue;
16325
16331
  }
16326
16332
  if (before !== void 0) {
16327
- lines.push(`-${before}`);
16333
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16328
16334
  }
16329
16335
  if (after !== void 0) {
16330
- lines.push(`+${after}`);
16336
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16331
16337
  }
16332
16338
  }
16333
- return 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");
16334
16345
  }
16335
16346
  function toSafeNumber(value) {
16336
16347
  return typeof value === "bigint" ? Number(value) : value;
@@ -16354,10 +16365,10 @@ function assertInsertArguments(input) {
16354
16365
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16355
16366
  }
16356
16367
  if (input.replaceAll !== void 0) {
16357
- 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");
16358
16369
  }
16359
16370
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16360
- 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");
16361
16372
  }
16362
16373
  }
16363
16374
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16381,9 +16392,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16381
16392
  searchFrom = index + token.length;
16382
16393
  }
16383
16394
  if (count === 0) {
16384
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16395
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16385
16396
  }
16386
- 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`);
16387
16398
  }
16388
16399
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16389
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;
@@ -16392,7 +16403,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16392
16403
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16393
16404
  if (alreadyExists) {
16394
16405
  if (ifExists === "error") {
16395
- 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?");
16396
16407
  }
16397
16408
  if (ifExists === "skip") {
16398
16409
  return {
@@ -16433,7 +16444,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16433
16444
  }
16434
16445
  function assertNotBinary(buffer) {
16435
16446
  if (buffer.includes(0)) {
16436
- 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");
16437
16448
  }
16438
16449
  }
16439
16450
  function splitLinesWithNumbers(text, offset = 1, limit = 2e3) {
@@ -16563,14 +16574,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16563
16574
  if (startAnchor) {
16564
16575
  const found = text.indexOf(startAnchor);
16565
16576
  if (found === -1) {
16566
- 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}`);
16567
16578
  }
16568
16579
  rangeStart = found + startAnchor.length;
16569
16580
  }
16570
16581
  if (endAnchor) {
16571
16582
  const found = text.indexOf(endAnchor, rangeStart);
16572
16583
  if (found === -1) {
16573
- 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}`);
16574
16585
  }
16575
16586
  rangeEnd = found;
16576
16587
  }
@@ -16623,9 +16634,9 @@ function getNearestContext(content, oldString) {
16623
16634
  }
16624
16635
  function buildNoMatchMessage(content, oldString) {
16625
16636
  return [
16626
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16627
- "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?",
16628
- "\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?",
16629
16640
  getNearestContext(content, oldString)
16630
16641
  ].join("\n");
16631
16642
  }
@@ -16748,10 +16759,10 @@ async function resolveReadableGbkFile(input) {
16748
16759
  try {
16749
16760
  stat = await fs2.stat(candidatePath);
16750
16761
  } catch (error45) {
16751
- 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);
16752
16763
  }
16753
16764
  if (stat.isDirectory()) {
16754
- 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}`);
16755
16766
  }
16756
16767
  return {
16757
16768
  filePath: candidatePath,
@@ -16956,7 +16967,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16956
16967
  } else if (occurrencesBefore === 0) {
16957
16968
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16958
16969
  } else if (occurrencesBefore > 1) {
16959
- 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}`);
16960
16971
  }
16961
16972
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16962
16973
  return {
@@ -17061,7 +17072,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17061
17072
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17062
17073
  if (alreadyExists) {
17063
17074
  if (input.ifExists === "error") {
17064
- 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?");
17065
17076
  }
17066
17077
  if (input.ifExists === "skip") {
17067
17078
  skipped = true;
@@ -17108,7 +17119,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17108
17119
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17109
17120
  if (alreadyExists) {
17110
17121
  if (input.ifExists === "error") {
17111
- 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?");
17112
17123
  }
17113
17124
  if (input.ifExists === "skip") {
17114
17125
  skipped = true;
@@ -17122,10 +17133,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17122
17133
  }
17123
17134
  }
17124
17135
  if (!inserted && totalMatches === 0) {
17125
- 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}`);
17126
17137
  }
17127
17138
  if (!inserted && totalMatches > 0) {
17128
- 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`);
17129
17140
  }
17130
17141
  await finalizeInserted();
17131
17142
  await handle.close();
@@ -17188,10 +17199,10 @@ async function replaceLargeGbkFileText(input) {
17188
17199
  });
17189
17200
  await flushText("", true);
17190
17201
  if (occurrencesBefore === 0) {
17191
- 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}`);
17192
17203
  }
17193
17204
  if (!input.replaceAll && occurrencesBefore > 1) {
17194
- 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}`);
17195
17206
  }
17196
17207
  await handle.close();
17197
17208
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17417,7 +17428,7 @@ async function replaceGbkFileText(input) {
17417
17428
  } else if (occurrencesBefore === 0) {
17418
17429
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17419
17430
  } else if (occurrencesBefore > 1) {
17420
- 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}`);
17421
17432
  }
17422
17433
  const fileNewlineStyle = detectNewlineStyle(current.content);
17423
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;
@@ -17550,10 +17561,10 @@ async function writeGbkFile(input) {
17550
17561
  try {
17551
17562
  const stat = await fs2.stat(candidatePath);
17552
17563
  if (stat.isDirectory()) {
17553
- 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}`);
17554
17565
  }
17555
17566
  if (!overwrite) {
17556
- 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}`);
17557
17568
  }
17558
17569
  } catch (error45) {
17559
17570
  if (error45 instanceof Error && "code" in error45) {
@@ -17599,7 +17610,25 @@ async function writeGbkFile(input) {
17599
17610
  }
17600
17611
  }
17601
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
+
17602
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
+ }
17603
17632
  var gbk_edit_default = tool({
17604
17633
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17605
17634
 
@@ -17669,8 +17698,8 @@ Insert mode:
17669
17698
  diffPreview
17670
17699
  }
17671
17700
  });
17672
- if (diffPreview) return diffPreview;
17673
- return JSON.stringify(result, null, 2);
17701
+ if (diffPreview) return truncateToolOutput(diffPreview);
17702
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17674
17703
  }
17675
17704
  });
17676
17705
 
@@ -18051,29 +18080,40 @@ function assertInsertArguments2(input) {
18051
18080
  throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
18052
18081
  }
18053
18082
  }
18083
+ var ANSI_RED2 = "\x1B[31m";
18084
+ var ANSI_GREEN2 = "\x1B[32m";
18085
+ var ANSI_DIM2 = "\x1B[2m";
18086
+ var ANSI_RESET2 = "\x1B[0m";
18087
+ var MAX_DIFF_LINES2 = 80;
18054
18088
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
18055
18089
  const beforeLines = normalizeNewlines2(beforeText).split("\n");
18056
18090
  const afterLines = normalizeNewlines2(afterText).split("\n");
18057
18091
  const maxLines = Math.max(beforeLines.length, afterLines.length);
18058
- const lines = [
18059
- `--- ${path3.basename(filePath)} (${encoding})`,
18060
- `+++ ${path3.basename(filePath)} (${encoding})`
18092
+ const header = [
18093
+ `${ANSI_DIM2}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`,
18094
+ `${ANSI_DIM2}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`
18061
18095
  ];
18096
+ const body = [];
18062
18097
  for (let index = 0; index < maxLines; index += 1) {
18063
18098
  const before = beforeLines[index];
18064
18099
  const after = afterLines[index];
18065
18100
  if (before !== void 0 && after !== void 0 && before === after) {
18066
- lines.push(` ${before}`);
18101
+ body.push(` ${before}`);
18067
18102
  continue;
18068
18103
  }
18069
18104
  if (before !== void 0) {
18070
- lines.push(`-${before}`);
18105
+ body.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18071
18106
  }
18072
18107
  if (after !== void 0) {
18073
- lines.push(`+${after}`);
18108
+ body.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18074
18109
  }
18075
18110
  }
18076
- return 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");
18077
18117
  }
18078
18118
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
18079
18119
  const alignedContent = alignTextToNewlineStyle2(content, newlineStyle);
@@ -18720,11 +18760,18 @@ async function replaceTextFileText(input) {
18720
18760
  bytesRead: loaded.bytesRead,
18721
18761
  bytesWritten: buffer.byteLength,
18722
18762
  newlineStyle: detectNewlineStyle(outputText),
18723
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
18763
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
18724
18764
  };
18725
18765
  }
18726
18766
 
18727
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
+ }
18728
18775
  var text_edit_default = tool({
18729
18776
  description: `Edit text files with automatic encoding detection and preservation.
18730
18777
 
@@ -18775,8 +18822,8 @@ var text_edit_default = tool({
18775
18822
  diffPreview
18776
18823
  }
18777
18824
  });
18778
- if (diffPreview) return diffPreview;
18779
- return JSON.stringify(result, null, 2);
18825
+ if (diffPreview) return truncateToolOutput2(diffPreview);
18826
+ return truncateToolOutput2(JSON.stringify(result, null, 2));
18780
18827
  }
18781
18828
  });
18782
18829
 
@@ -18868,6 +18915,12 @@ function createOpencodeGbkHooks() {
18868
18915
  text_write: text_write_default,
18869
18916
  text_edit: text_edit_default
18870
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
+ },
18871
18924
  async "experimental.chat.system.transform"(_input, output) {
18872
18925
  appendTextToolSystemPrompt(output.system);
18873
18926
  }
@@ -16308,29 +16308,40 @@ async function assertPathAllowed(filePath, context, allowExternal = false) {
16308
16308
  var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
16309
16309
  var STREAM_READ_CHUNK_SIZE_BYTES = 1024 * 1024;
16310
16310
  var gbkLineIndexCache = /* @__PURE__ */ new Map();
16311
+ var ANSI_RED = "\x1B[31m";
16312
+ var ANSI_GREEN = "\x1B[32m";
16313
+ var ANSI_DIM = "\x1B[2m";
16314
+ var ANSI_RESET = "\x1B[0m";
16315
+ var MAX_DIFF_LINES = 80;
16311
16316
  function buildGbkLineDiffPreview(filePath, encoding, beforeText, afterText) {
16312
16317
  const beforeLines = normalizeNewlines(beforeText).split("\n");
16313
16318
  const afterLines = normalizeNewlines(afterText).split("\n");
16314
16319
  const maxLines = Math.max(beforeLines.length, afterLines.length);
16315
- const lines = [
16316
- `--- ${path2.basename(filePath)} (${encoding})`,
16317
- `+++ ${path2.basename(filePath)} (${encoding})`
16320
+ const header = [
16321
+ `${ANSI_DIM}--- ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`,
16322
+ `${ANSI_DIM}+++ ${path2.basename(filePath)} (${encoding})${ANSI_RESET}`
16318
16323
  ];
16324
+ const body = [];
16319
16325
  for (let index = 0; index < maxLines; index += 1) {
16320
16326
  const before = beforeLines[index];
16321
16327
  const after = afterLines[index];
16322
16328
  if (before !== void 0 && after !== void 0 && before === after) {
16323
- lines.push(` ${before}`);
16329
+ body.push(` ${before}`);
16324
16330
  continue;
16325
16331
  }
16326
16332
  if (before !== void 0) {
16327
- lines.push(`-${before}`);
16333
+ body.push(`${ANSI_RED}-${before}${ANSI_RESET}`);
16328
16334
  }
16329
16335
  if (after !== void 0) {
16330
- lines.push(`+${after}`);
16336
+ body.push(`${ANSI_GREEN}+${after}${ANSI_RESET}`);
16331
16337
  }
16332
16338
  }
16333
- return 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");
16334
16345
  }
16335
16346
  function toSafeNumber(value) {
16336
16347
  return typeof value === "bigint" ? Number(value) : value;
@@ -16354,10 +16365,10 @@ function assertInsertArguments(input) {
16354
16365
  throw createGbkError("GBK_INVALID_ARGUMENT", "content \u4E0D\u80FD\u4E3A\u7A7A");
16355
16366
  }
16356
16367
  if (input.replaceAll !== void 0) {
16357
- 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");
16358
16369
  }
16359
16370
  if (input.startLine !== void 0 || input.endLine !== void 0 || input.startAnchor !== void 0 || input.endAnchor !== void 0) {
16360
- 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");
16361
16372
  }
16362
16373
  }
16363
16374
  function findOccurrenceIndex(text, token, occurrence) {
@@ -16381,9 +16392,9 @@ function findOccurrenceIndex(text, token, occurrence) {
16381
16392
  searchFrom = index + token.length;
16382
16393
  }
16383
16394
  if (count === 0) {
16384
- throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD\uFFFD?: ${token}`);
16395
+ throw createGbkError("GBK_NO_MATCH", `\u672A\u627E\u5230\u951A\uFFFD?: ${token}`);
16385
16396
  }
16386
- 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`);
16387
16398
  }
16388
16399
  function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
16389
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;
@@ -16392,7 +16403,7 @@ function insertByAnchor(text, mode, anchor, content, occurrence, ifExists, newli
16392
16403
  const alreadyExists = mode === "insertAfter" ? text.slice(insertionPoint, insertionPoint + alignedContent.length) === alignedContent : text.slice(Math.max(0, insertionPoint - alignedContent.length), insertionPoint) === alignedContent;
16393
16404
  if (alreadyExists) {
16394
16405
  if (ifExists === "error") {
16395
- 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?");
16396
16407
  }
16397
16408
  if (ifExists === "skip") {
16398
16409
  return {
@@ -16433,7 +16444,7 @@ function normalizeOptionalPositiveInteger(value, name) {
16433
16444
  }
16434
16445
  function assertNotBinary(buffer) {
16435
16446
  if (buffer.includes(0)) {
16436
- 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");
16437
16448
  }
16438
16449
  }
16439
16450
  function splitLinesWithNumbers(text, offset = 1, limit = 2e3) {
@@ -16563,14 +16574,14 @@ function applyAnchors(text, startAnchor, endAnchor) {
16563
16574
  if (startAnchor) {
16564
16575
  const found = text.indexOf(startAnchor);
16565
16576
  if (found === -1) {
16566
- 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}`);
16567
16578
  }
16568
16579
  rangeStart = found + startAnchor.length;
16569
16580
  }
16570
16581
  if (endAnchor) {
16571
16582
  const found = text.indexOf(endAnchor, rangeStart);
16572
16583
  if (found === -1) {
16573
- 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}`);
16574
16585
  }
16575
16586
  rangeEnd = found;
16576
16587
  }
@@ -16623,9 +16634,9 @@ function getNearestContext(content, oldString) {
16623
16634
  }
16624
16635
  function buildNoMatchMessage(content, oldString) {
16625
16636
  return [
16626
- "\u672A\u627E\u5230\u9700\u8981\u66FF\u6362\u7684\u6587\u672C\uFFFD\uFFFD?",
16627
- "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?",
16628
- "\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?",
16629
16640
  getNearestContext(content, oldString)
16630
16641
  ].join("\n");
16631
16642
  }
@@ -16748,10 +16759,10 @@ async function resolveReadableGbkFile(input) {
16748
16759
  try {
16749
16760
  stat = await fs2.stat(candidatePath);
16750
16761
  } catch (error45) {
16751
- 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);
16752
16763
  }
16753
16764
  if (stat.isDirectory()) {
16754
- 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}`);
16755
16766
  }
16756
16767
  return {
16757
16768
  filePath: candidatePath,
@@ -16956,7 +16967,7 @@ function replaceScopedTextContent(scopeText, oldString, newString, replaceAll, n
16956
16967
  } else if (occurrencesBefore === 0) {
16957
16968
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scopeText, oldString));
16958
16969
  } else if (occurrencesBefore > 1) {
16959
- 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}`);
16960
16971
  }
16961
16972
  const alignedNewString = alignTextToNewlineStyle(newString, newlineStyle);
16962
16973
  return {
@@ -17061,7 +17072,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17061
17072
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17062
17073
  if (alreadyExists) {
17063
17074
  if (input.ifExists === "error") {
17064
- 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?");
17065
17076
  }
17066
17077
  if (input.ifExists === "skip") {
17067
17078
  skipped = true;
@@ -17108,7 +17119,7 @@ async function replaceLargeGbkFileByAnchor(input) {
17108
17119
  const alreadyExists = input.mode === "insertAfter" ? after.startsWith(alignedContent) : before.endsWith(alignedContent);
17109
17120
  if (alreadyExists) {
17110
17121
  if (input.ifExists === "error") {
17111
- 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?");
17112
17123
  }
17113
17124
  if (input.ifExists === "skip") {
17114
17125
  skipped = true;
@@ -17122,10 +17133,10 @@ async function replaceLargeGbkFileByAnchor(input) {
17122
17133
  }
17123
17134
  }
17124
17135
  if (!inserted && totalMatches === 0) {
17125
- 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}`);
17126
17137
  }
17127
17138
  if (!inserted && totalMatches > 0) {
17128
- 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`);
17129
17140
  }
17130
17141
  await finalizeInserted();
17131
17142
  await handle.close();
@@ -17188,10 +17199,10 @@ async function replaceLargeGbkFileText(input) {
17188
17199
  });
17189
17200
  await flushText("", true);
17190
17201
  if (occurrencesBefore === 0) {
17191
- 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}`);
17192
17203
  }
17193
17204
  if (!input.replaceAll && occurrencesBefore > 1) {
17194
- 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}`);
17195
17206
  }
17196
17207
  await handle.close();
17197
17208
  const mode = typeof input.stat.mode === "bigint" ? Number(input.stat.mode) : input.stat.mode;
@@ -17417,7 +17428,7 @@ async function replaceGbkFileText(input) {
17417
17428
  } else if (occurrencesBefore === 0) {
17418
17429
  throw createGbkError("GBK_NO_MATCH", buildNoMatchMessage(scope.selectedText, effectiveOldString));
17419
17430
  } else if (occurrencesBefore > 1) {
17420
- 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}`);
17421
17432
  }
17422
17433
  const fileNewlineStyle = detectNewlineStyle(current.content);
17423
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;
@@ -17550,10 +17561,10 @@ async function writeGbkFile(input) {
17550
17561
  try {
17551
17562
  const stat = await fs2.stat(candidatePath);
17552
17563
  if (stat.isDirectory()) {
17553
- 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}`);
17554
17565
  }
17555
17566
  if (!overwrite) {
17556
- 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}`);
17557
17568
  }
17558
17569
  } catch (error45) {
17559
17570
  if (error45 instanceof Error && "code" in error45) {
@@ -17599,7 +17610,25 @@ async function writeGbkFile(input) {
17599
17610
  }
17600
17611
  }
17601
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
+
17602
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
+ }
17603
17632
  var gbk_edit_default = tool({
17604
17633
  description: `Edit GBK/GB18030 encoded text files with exact string replacement.
17605
17634
 
@@ -17669,8 +17698,8 @@ Insert mode:
17669
17698
  diffPreview
17670
17699
  }
17671
17700
  });
17672
- if (diffPreview) return diffPreview;
17673
- return JSON.stringify(result, null, 2);
17701
+ if (diffPreview) return truncateToolOutput(diffPreview);
17702
+ return truncateToolOutput(JSON.stringify(result, null, 2));
17674
17703
  }
17675
17704
  });
17676
17705
 
@@ -18051,29 +18080,40 @@ function assertInsertArguments2(input) {
18051
18080
  throw createTextError("GBK_INVALID_ARGUMENT", "\u63D2\u5165\u6A21\u5F0F\u4E0D\u652F\u6301 startLine/endLine/startAnchor/endAnchor");
18052
18081
  }
18053
18082
  }
18083
+ var ANSI_RED2 = "\x1B[31m";
18084
+ var ANSI_GREEN2 = "\x1B[32m";
18085
+ var ANSI_DIM2 = "\x1B[2m";
18086
+ var ANSI_RESET2 = "\x1B[0m";
18087
+ var MAX_DIFF_LINES2 = 80;
18054
18088
  function buildLineDiffPreview(filePath, encoding, beforeText, afterText) {
18055
18089
  const beforeLines = normalizeNewlines2(beforeText).split("\n");
18056
18090
  const afterLines = normalizeNewlines2(afterText).split("\n");
18057
18091
  const maxLines = Math.max(beforeLines.length, afterLines.length);
18058
- const lines = [
18059
- `--- ${path3.basename(filePath)} (${encoding})`,
18060
- `+++ ${path3.basename(filePath)} (${encoding})`
18092
+ const header = [
18093
+ `${ANSI_DIM2}--- ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`,
18094
+ `${ANSI_DIM2}+++ ${path3.basename(filePath)} (${encoding})${ANSI_RESET2}`
18061
18095
  ];
18096
+ const body = [];
18062
18097
  for (let index = 0; index < maxLines; index += 1) {
18063
18098
  const before = beforeLines[index];
18064
18099
  const after = afterLines[index];
18065
18100
  if (before !== void 0 && after !== void 0 && before === after) {
18066
- lines.push(` ${before}`);
18101
+ body.push(` ${before}`);
18067
18102
  continue;
18068
18103
  }
18069
18104
  if (before !== void 0) {
18070
- lines.push(`-${before}`);
18105
+ body.push(`${ANSI_RED2}-${before}${ANSI_RESET2}`);
18071
18106
  }
18072
18107
  if (after !== void 0) {
18073
- lines.push(`+${after}`);
18108
+ body.push(`${ANSI_GREEN2}+${after}${ANSI_RESET2}`);
18074
18109
  }
18075
18110
  }
18076
- return 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");
18077
18117
  }
18078
18118
  function buildInsertOutput(text, mode, anchor, content, occurrence, ifExists, newlineStyle) {
18079
18119
  const alignedContent = alignTextToNewlineStyle2(content, newlineStyle);
@@ -18720,11 +18760,18 @@ async function replaceTextFileText(input) {
18720
18760
  bytesRead: loaded.bytesRead,
18721
18761
  bytesWritten: buffer.byteLength,
18722
18762
  newlineStyle: detectNewlineStyle(outputText),
18723
- diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, scope.selectedText, replaced)
18763
+ diffPreview: buildLineDiffPreview(loaded.filePath, targetEncoding, input.oldString, alignedNewString)
18724
18764
  };
18725
18765
  }
18726
18766
 
18727
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
+ }
18728
18775
  var text_edit_default = tool({
18729
18776
  description: `Edit text files with automatic encoding detection and preservation.
18730
18777
 
@@ -18775,8 +18822,8 @@ var text_edit_default = tool({
18775
18822
  diffPreview
18776
18823
  }
18777
18824
  });
18778
- if (diffPreview) return diffPreview;
18779
- return JSON.stringify(result, null, 2);
18825
+ if (diffPreview) return truncateToolOutput2(diffPreview);
18826
+ return truncateToolOutput2(JSON.stringify(result, null, 2));
18780
18827
  }
18781
18828
  });
18782
18829
 
@@ -18868,6 +18915,12 @@ function createOpencodeGbkHooks() {
18868
18915
  text_write: text_write_default,
18869
18916
  text_edit: text_edit_default
18870
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
+ },
18871
18924
  async "experimental.chat.system.transform"(_input, output) {
18872
18925
  appendTextToolSystemPrompt(output.system);
18873
18926
  }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "manifestVersion": 1,
3
3
  "packageName": "opencode-gbk-tools",
4
- "packageVersion": "0.1.21",
4
+ "packageVersion": "0.1.23",
5
5
  "artifacts": [
6
6
  {
7
7
  "relativePath": "plugins/opencode-gbk-tools.js",
8
8
  "kind": "plugin",
9
- "expectedHash": "d0407b86c21d2515bf4642c3bbede9bc26083224d316f19c3d40decb7fea4461",
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.22",
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",