opencode-gbk-tools 0.1.29 → 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/plugin/index.js +194 -110
- package/dist/plugins/opencode-gbk-tools.js +193 -109
- package/dist/release-manifest.json +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -208,7 +208,8 @@ A:不要。现在优先用 `mode="insertAfter"` 或 `mode="insertBefore"`,
|
|
|
208
208
|
|
|
209
209
|
| 版本 | 说明 |
|
|
210
210
|
|------|------|
|
|
211
|
-
| 0.1.
|
|
211
|
+
| 0.1.30 | 补强 `text_*` 针对真实 GBK 样本与大体积重复样本的高强度测试:新增 `QFunction-0.txt` 真实样本读取、编辑、追加与端到端工作流覆盖,并补充大文件重复样本下的 `text_read` / `text_edit` 行为验证;同时保留现有“首次检测或已记忆为 GBK/GB18030 的文件直接拦截到 `gbk_*`”的路由策略 |
|
|
212
|
+
| 0.1.29 | 在方案 B 基础上新增 GBK 文件持久记忆:普通 agent 继续按编码分流,UTF-8 优先使用 OpenCode 内置工具;对现有文件,已确认或首次检测到是 GBK/GB18030 的路径会按完整路径 + mtime/size 持久记忆;后续再次操作同一路径时,`text_*` 与内置 `read` / `write` / `edit` 都会被直接拦截并提示改用 `gbk_*`,避免先误走 `text_edit` / `edit` 再失败;`gbk-engine` 继续保持为强制 GBK 专属模式 |
|
|
212
213
|
| 0.1.28 | 调整为方案 B:普通 agent 按编码分流,UTF-8 优先使用 OpenCode 内置工具,GBK/乱码风险优先使用 `gbk_*`;`gbk-engine` 保持为强制 GBK 专属模式;同时修复 `src/lib/gbk-file.ts` 中多处用户可见乱码错误文案,并增强 `text_*` 的匹配容错与大文件流式替换稳定性 |
|
|
213
214
|
| 0.1.27 | 重新发布当前稳定内容,包含:新建 `.txt` 文件在 `encoding=auto` 下默认使用 `gbk`;保留已有文件的原编码检测与保持逻辑;修复 plugin 追加 system 规则时可能触发部分 Jinja/chat template 的“System message must be at the beginning”异常;同步更新工具提示、README 与测试覆盖 |
|
|
214
215
|
| 0.1.26 | 仅提升发布版本号,用于重新发布当时的稳定内容 |
|
package/dist/plugin/index.js
CHANGED
|
@@ -3816,6 +3816,9 @@ var require_lib = __commonJS({
|
|
|
3816
3816
|
}
|
|
3817
3817
|
});
|
|
3818
3818
|
|
|
3819
|
+
// src/plugin/index.ts
|
|
3820
|
+
import path5 from "path";
|
|
3821
|
+
|
|
3819
3822
|
// src/lib/session-pressure.ts
|
|
3820
3823
|
var AUTO_SUMMARIZE_PRESSURE_RATIO = 0.85;
|
|
3821
3824
|
var AUTO_SUMMARIZE_COOLDOWN_MS = 6e4;
|
|
@@ -4043,6 +4046,67 @@ async function getRememberedGbkEncoding(filePath) {
|
|
|
4043
4046
|
return entry;
|
|
4044
4047
|
}
|
|
4045
4048
|
|
|
4049
|
+
// src/lib/path-sandbox.ts
|
|
4050
|
+
import fs2 from "fs/promises";
|
|
4051
|
+
import path2 from "path";
|
|
4052
|
+
|
|
4053
|
+
// src/lib/errors.ts
|
|
4054
|
+
var GbkToolError = class extends Error {
|
|
4055
|
+
code;
|
|
4056
|
+
cause;
|
|
4057
|
+
constructor(code, message, cause) {
|
|
4058
|
+
super(message);
|
|
4059
|
+
this.name = "GbkToolError";
|
|
4060
|
+
this.code = code;
|
|
4061
|
+
this.cause = cause;
|
|
4062
|
+
}
|
|
4063
|
+
};
|
|
4064
|
+
function createGbkError(code, message, cause) {
|
|
4065
|
+
return new GbkToolError(code, message, cause);
|
|
4066
|
+
}
|
|
4067
|
+
var createTextError = createGbkError;
|
|
4068
|
+
|
|
4069
|
+
// src/lib/path-sandbox.ts
|
|
4070
|
+
function resolveBaseDirectory(context) {
|
|
4071
|
+
const value = context.directory ?? process.cwd();
|
|
4072
|
+
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
4073
|
+
}
|
|
4074
|
+
function resolveWorkspaceRoot(context) {
|
|
4075
|
+
const value = context.worktree ?? context.directory ?? process.cwd();
|
|
4076
|
+
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
4077
|
+
}
|
|
4078
|
+
function resolveCandidatePath(filePath, context) {
|
|
4079
|
+
return path2.resolve(resolveBaseDirectory(context), filePath);
|
|
4080
|
+
}
|
|
4081
|
+
async function resolveExistingAnchor(filePath) {
|
|
4082
|
+
let current = filePath;
|
|
4083
|
+
while (true) {
|
|
4084
|
+
try {
|
|
4085
|
+
return await fs2.realpath(current);
|
|
4086
|
+
} catch {
|
|
4087
|
+
const parent = path2.dirname(current);
|
|
4088
|
+
if (parent === current) {
|
|
4089
|
+
throw createGbkError("GBK_IO_ERROR", `\u65E0\u6CD5\u89E3\u6790\u8DEF\u5F84\u951A\u70B9: ${filePath}`);
|
|
4090
|
+
}
|
|
4091
|
+
current = parent;
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
async function assertPathAllowed(filePath, context, allowExternal = false) {
|
|
4096
|
+
const candidatePath = resolveCandidatePath(filePath, context);
|
|
4097
|
+
const workspaceRoot = resolveWorkspaceRoot(context);
|
|
4098
|
+
if (!allowExternal) {
|
|
4099
|
+
const realWorkspaceRoot = await fs2.realpath(workspaceRoot);
|
|
4100
|
+
const realCandidateAnchor = await resolveExistingAnchor(candidatePath);
|
|
4101
|
+
const relative = path2.relative(realWorkspaceRoot, realCandidateAnchor);
|
|
4102
|
+
if (relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative)) {
|
|
4103
|
+
return { candidatePath, workspaceRoot };
|
|
4104
|
+
}
|
|
4105
|
+
throw createGbkError("GBK_PATH_OUTSIDE_ROOT", `\u76EE\u6807\u8DEF\u5F84\u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4: ${candidatePath}`);
|
|
4106
|
+
}
|
|
4107
|
+
return { candidatePath, workspaceRoot };
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4046
4110
|
// node_modules/zod/v4/classic/external.js
|
|
4047
4111
|
var external_exports = {};
|
|
4048
4112
|
__export(external_exports, {
|
|
@@ -4774,10 +4838,10 @@ function mergeDefs(...defs) {
|
|
|
4774
4838
|
function cloneDef(schema) {
|
|
4775
4839
|
return mergeDefs(schema._zod.def);
|
|
4776
4840
|
}
|
|
4777
|
-
function getElementAtPath(obj,
|
|
4778
|
-
if (!
|
|
4841
|
+
function getElementAtPath(obj, path6) {
|
|
4842
|
+
if (!path6)
|
|
4779
4843
|
return obj;
|
|
4780
|
-
return
|
|
4844
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
4781
4845
|
}
|
|
4782
4846
|
function promiseAllObject(promisesObj) {
|
|
4783
4847
|
const keys = Object.keys(promisesObj);
|
|
@@ -5138,11 +5202,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5138
5202
|
}
|
|
5139
5203
|
return false;
|
|
5140
5204
|
}
|
|
5141
|
-
function prefixIssues(
|
|
5205
|
+
function prefixIssues(path6, issues) {
|
|
5142
5206
|
return issues.map((iss) => {
|
|
5143
5207
|
var _a;
|
|
5144
5208
|
(_a = iss).path ?? (_a.path = []);
|
|
5145
|
-
iss.path.unshift(
|
|
5209
|
+
iss.path.unshift(path6);
|
|
5146
5210
|
return iss;
|
|
5147
5211
|
});
|
|
5148
5212
|
}
|
|
@@ -5310,7 +5374,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5310
5374
|
return issue2.message;
|
|
5311
5375
|
};
|
|
5312
5376
|
const result = { errors: [] };
|
|
5313
|
-
const processError = (error46,
|
|
5377
|
+
const processError = (error46, path6 = []) => {
|
|
5314
5378
|
var _a, _b;
|
|
5315
5379
|
for (const issue2 of error46.issues) {
|
|
5316
5380
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -5320,7 +5384,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5320
5384
|
} else if (issue2.code === "invalid_element") {
|
|
5321
5385
|
processError({ issues: issue2.issues }, issue2.path);
|
|
5322
5386
|
} else {
|
|
5323
|
-
const fullpath = [...
|
|
5387
|
+
const fullpath = [...path6, ...issue2.path];
|
|
5324
5388
|
if (fullpath.length === 0) {
|
|
5325
5389
|
result.errors.push(mapper(issue2));
|
|
5326
5390
|
continue;
|
|
@@ -5352,8 +5416,8 @@ function treeifyError(error45, _mapper) {
|
|
|
5352
5416
|
}
|
|
5353
5417
|
function toDotPath(_path) {
|
|
5354
5418
|
const segs = [];
|
|
5355
|
-
const
|
|
5356
|
-
for (const seg of
|
|
5419
|
+
const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5420
|
+
for (const seg of path6) {
|
|
5357
5421
|
if (typeof seg === "number")
|
|
5358
5422
|
segs.push(`[${seg}]`);
|
|
5359
5423
|
else if (typeof seg === "symbol")
|
|
@@ -16597,67 +16661,6 @@ import crypto from "crypto";
|
|
|
16597
16661
|
import { createReadStream } from "fs";
|
|
16598
16662
|
import fs3 from "fs/promises";
|
|
16599
16663
|
import path3 from "path";
|
|
16600
|
-
|
|
16601
|
-
// src/lib/errors.ts
|
|
16602
|
-
var GbkToolError = class extends Error {
|
|
16603
|
-
code;
|
|
16604
|
-
cause;
|
|
16605
|
-
constructor(code, message, cause) {
|
|
16606
|
-
super(message);
|
|
16607
|
-
this.name = "GbkToolError";
|
|
16608
|
-
this.code = code;
|
|
16609
|
-
this.cause = cause;
|
|
16610
|
-
}
|
|
16611
|
-
};
|
|
16612
|
-
function createGbkError(code, message, cause) {
|
|
16613
|
-
return new GbkToolError(code, message, cause);
|
|
16614
|
-
}
|
|
16615
|
-
var createTextError = createGbkError;
|
|
16616
|
-
|
|
16617
|
-
// src/lib/path-sandbox.ts
|
|
16618
|
-
import fs2 from "fs/promises";
|
|
16619
|
-
import path2 from "path";
|
|
16620
|
-
function resolveBaseDirectory(context) {
|
|
16621
|
-
const value = context.directory ?? process.cwd();
|
|
16622
|
-
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
16623
|
-
}
|
|
16624
|
-
function resolveWorkspaceRoot(context) {
|
|
16625
|
-
const value = context.worktree ?? context.directory ?? process.cwd();
|
|
16626
|
-
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
16627
|
-
}
|
|
16628
|
-
function resolveCandidatePath(filePath, context) {
|
|
16629
|
-
return path2.resolve(resolveBaseDirectory(context), filePath);
|
|
16630
|
-
}
|
|
16631
|
-
async function resolveExistingAnchor(filePath) {
|
|
16632
|
-
let current = filePath;
|
|
16633
|
-
while (true) {
|
|
16634
|
-
try {
|
|
16635
|
-
return await fs2.realpath(current);
|
|
16636
|
-
} catch {
|
|
16637
|
-
const parent = path2.dirname(current);
|
|
16638
|
-
if (parent === current) {
|
|
16639
|
-
throw createGbkError("GBK_IO_ERROR", `\u65E0\u6CD5\u89E3\u6790\u8DEF\u5F84\u951A\u70B9: ${filePath}`);
|
|
16640
|
-
}
|
|
16641
|
-
current = parent;
|
|
16642
|
-
}
|
|
16643
|
-
}
|
|
16644
|
-
}
|
|
16645
|
-
async function assertPathAllowed(filePath, context, allowExternal = false) {
|
|
16646
|
-
const candidatePath = resolveCandidatePath(filePath, context);
|
|
16647
|
-
const workspaceRoot = resolveWorkspaceRoot(context);
|
|
16648
|
-
if (!allowExternal) {
|
|
16649
|
-
const realWorkspaceRoot = await fs2.realpath(workspaceRoot);
|
|
16650
|
-
const realCandidateAnchor = await resolveExistingAnchor(candidatePath);
|
|
16651
|
-
const relative = path2.relative(realWorkspaceRoot, realCandidateAnchor);
|
|
16652
|
-
if (relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative)) {
|
|
16653
|
-
return { candidatePath, workspaceRoot };
|
|
16654
|
-
}
|
|
16655
|
-
throw createGbkError("GBK_PATH_OUTSIDE_ROOT", `\u76EE\u6807\u8DEF\u5F84\u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4: ${candidatePath}`);
|
|
16656
|
-
}
|
|
16657
|
-
return { candidatePath, workspaceRoot };
|
|
16658
|
-
}
|
|
16659
|
-
|
|
16660
|
-
// src/lib/gbk-file.ts
|
|
16661
16664
|
var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16662
16665
|
var STREAM_READ_CHUNK_SIZE_BYTES = 1024 * 1024;
|
|
16663
16666
|
var gbkLineIndexCache = /* @__PURE__ */ new Map();
|
|
@@ -18243,35 +18246,6 @@ var gbk_write_default = tool({
|
|
|
18243
18246
|
}
|
|
18244
18247
|
});
|
|
18245
18248
|
|
|
18246
|
-
// src/plugin/text-guidance.ts
|
|
18247
|
-
var TEXT_TOOL_SYSTEM_MARKER = "[opencode-gbk-tools:text-rules]";
|
|
18248
|
-
var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
18249
|
-
TEXT_TOOL_SYSTEM_MARKER,
|
|
18250
|
-
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
18251
|
-
"- \u666E\u901A UTF-8 / UTF-8 BOM / UTF-16 \u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 OpenCode \u5185\u7F6E read\u3001write\u3001edit\u3002",
|
|
18252
|
-
"- \u9047\u5230 GBK / GB18030 \u6587\u4EF6\u3001\u4E2D\u6587\u4E71\u7801\u3001\u975E UTF-8 \u65E7\u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\u3002",
|
|
18253
|
-
"- \u65B0\u5EFA .txt \u6587\u4EF6\u5728 encoding=auto \u4E0B\u9ED8\u8BA4\u4F7F\u7528 GBK\uFF1B\u5176\u4ED6\u65B0\u6587\u4EF6\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
18254
|
-
"- \u65E0\u6CD5\u786E\u5B9A\u7F16\u7801\u65F6\uFF0C\u5148\u5C1D\u8BD5\u5185\u7F6E read\uFF1B\u82E5\u51FA\u73B0\u4E2D\u6587\u4E71\u7801\u3001\u66FF\u6362\u5931\u8D25\u6216\u7F16\u7801\u98CE\u9669\uFF0C\u518D\u5207\u6362\u5230 gbk_*\u3002",
|
|
18255
|
-
"- \u5DF2\u786E\u8BA4\u662F GBK/GB18030 \u7684\u6587\u4EF6\u4F1A\u88AB\u63D2\u4EF6\u6301\u4E45\u8BB0\u5FC6\uFF1B\u518D\u6B21\u64CD\u4F5C\u540C\u4E00\u8DEF\u5F84\u65F6\uFF0C\u4F18\u5148\u7EE7\u7EED\u6309 GBK \u5904\u7406\u3002",
|
|
18256
|
-
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
18257
|
-
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
18258
|
-
"- anchor\u3001startAnchor\u3001endAnchor\u3001oldString \u82E5\u76F4\u63A5\u590D\u5236\u81EA\u8BFB\u53D6\u7ED3\u679C\uFF0C\u53EF\u4FDD\u7559 LF \u6362\u884C\uFF1Bgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u6309\u6587\u4EF6\u6362\u884C\u98CE\u683C\u81EA\u52A8\u5BF9\u9F50\u3002",
|
|
18259
|
-
'- \u82E5\u8BFB\u53D6\u7ED3\u679C\u5E26\u6709 "N: " \u884C\u53F7\u524D\u7F00\uFF0Cgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u81EA\u52A8\u5265\u79BB\u8FD9\u4E9B\u524D\u7F00\u540E\u518D\u5339\u914D\u3002',
|
|
18260
|
-
"- gbk-engine \u662F\u5F3A\u5236 GBK \u4E13\u5C5E\u6A21\u5F0F\uFF1A\u53EA\u5141\u8BB8 gbk_*\uFF0C\u4E0D\u8D70\u5185\u7F6E\u8BFB\u5199\u7F16\u8F91\u5DE5\u5177\u3002"
|
|
18261
|
-
].join("\n");
|
|
18262
|
-
function appendTextToolSystemPrompt(system) {
|
|
18263
|
-
if (system.some((item) => item.includes(TEXT_TOOL_SYSTEM_MARKER))) {
|
|
18264
|
-
return;
|
|
18265
|
-
}
|
|
18266
|
-
if (system.length === 0) {
|
|
18267
|
-
system.push(TEXT_TOOL_SYSTEM_PROMPT);
|
|
18268
|
-
return;
|
|
18269
|
-
}
|
|
18270
|
-
system[0] = `${system[0]}
|
|
18271
|
-
|
|
18272
|
-
${TEXT_TOOL_SYSTEM_PROMPT}`;
|
|
18273
|
-
}
|
|
18274
|
-
|
|
18275
18249
|
// src/lib/text-file.ts
|
|
18276
18250
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
18277
18251
|
import crypto2 from "crypto";
|
|
@@ -19220,6 +19194,20 @@ async function ensureParentDirectory(parent, createDirectories) {
|
|
|
19220
19194
|
throw error45;
|
|
19221
19195
|
}
|
|
19222
19196
|
}
|
|
19197
|
+
async function detectTextFileEncoding(input) {
|
|
19198
|
+
const detected = await detectReadableTextFile(input);
|
|
19199
|
+
const fileSize = typeof detected.stat.size === "bigint" ? Number(detected.stat.size) : detected.stat.size;
|
|
19200
|
+
return {
|
|
19201
|
+
filePath: detected.filePath,
|
|
19202
|
+
encoding: detected.encoding,
|
|
19203
|
+
requestedEncoding: detected.requestedEncoding,
|
|
19204
|
+
detectedEncoding: detected.detectedEncoding,
|
|
19205
|
+
confidence: detected.confidence,
|
|
19206
|
+
hasBom: detected.hasBom,
|
|
19207
|
+
bytesRead: Math.min(fileSize, TEXT_DETECTION_SAMPLE_BYTES),
|
|
19208
|
+
newlineStyle: "unknown"
|
|
19209
|
+
};
|
|
19210
|
+
}
|
|
19223
19211
|
async function readTextFile(input) {
|
|
19224
19212
|
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
19225
19213
|
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
@@ -19497,6 +19485,35 @@ async function replaceTextFileText(input) {
|
|
|
19497
19485
|
};
|
|
19498
19486
|
}
|
|
19499
19487
|
|
|
19488
|
+
// src/plugin/text-guidance.ts
|
|
19489
|
+
var TEXT_TOOL_SYSTEM_MARKER = "[opencode-gbk-tools:text-rules]";
|
|
19490
|
+
var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
19491
|
+
TEXT_TOOL_SYSTEM_MARKER,
|
|
19492
|
+
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
19493
|
+
"- \u666E\u901A UTF-8 / UTF-8 BOM / UTF-16 \u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 OpenCode \u5185\u7F6E read\u3001write\u3001edit\u3002",
|
|
19494
|
+
"- \u9047\u5230 GBK / GB18030 \u6587\u4EF6\u3001\u4E2D\u6587\u4E71\u7801\u3001\u975E UTF-8 \u65E7\u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\uFF0C\u4E0D\u8981\u5148\u5C1D\u8BD5 text_* \u6216\u5185\u7F6E read/write/edit\u3002",
|
|
19495
|
+
"- \u65B0\u5EFA .txt \u6587\u4EF6\u5728 encoding=auto \u4E0B\u9ED8\u8BA4\u4F7F\u7528 GBK\uFF1B\u5176\u4ED6\u65B0\u6587\u4EF6\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
19496
|
+
"- \u5BF9\u73B0\u6709 .txt / .cfg / .ini / .log \u7B49\u65E7\u6587\u672C\u6587\u4EF6\uFF0C\u53EA\u8981\u6000\u7591\u662F\u4E2D\u6587\u672C\u5730\u7F16\u7801\uFF0C\u4F18\u5148\u5148\u7528 gbk_read \u5224\u65AD\uFF0C\u4E0D\u8981\u5148 edit \u518D\u56E0\u4E3A\u5339\u914D\u5931\u8D25\u6216\u6587\u4EF6\u65F6\u95F4\u6233\u53D8\u5316\u800C\u56DE\u9000\u3002",
|
|
19497
|
+
"- \u5DF2\u786E\u8BA4\u6216\u9996\u6B21\u68C0\u6D4B\u5230\u662F GBK/GB18030 \u7684\u6587\u4EF6\u4F1A\u88AB\u63D2\u4EF6\u6301\u4E45\u8BB0\u5FC6\uFF1B\u518D\u6B21\u64CD\u4F5C\u540C\u4E00\u8DEF\u5F84\u65F6\uFF0C\u4F1A\u76F4\u63A5\u62E6\u622A\u5185\u7F6E read/write/edit \u548C text_*\uFF0C\u5E76\u8981\u6C42\u6539\u7528 gbk_*\u3002",
|
|
19498
|
+
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
19499
|
+
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
19500
|
+
"- anchor\u3001startAnchor\u3001endAnchor\u3001oldString \u82E5\u76F4\u63A5\u590D\u5236\u81EA\u8BFB\u53D6\u7ED3\u679C\uFF0C\u53EF\u4FDD\u7559 LF \u6362\u884C\uFF1Bgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u6309\u6587\u4EF6\u6362\u884C\u98CE\u683C\u81EA\u52A8\u5BF9\u9F50\u3002",
|
|
19501
|
+
'- \u82E5\u8BFB\u53D6\u7ED3\u679C\u5E26\u6709 "N: " \u884C\u53F7\u524D\u7F00\uFF0Cgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u81EA\u52A8\u5265\u79BB\u8FD9\u4E9B\u524D\u7F00\u540E\u518D\u5339\u914D\u3002',
|
|
19502
|
+
"- gbk-engine \u662F\u5F3A\u5236 GBK \u4E13\u5C5E\u6A21\u5F0F\uFF1A\u53EA\u5141\u8BB8 gbk_*\uFF0C\u4E0D\u8D70\u5185\u7F6E\u8BFB\u5199\u7F16\u8F91\u5DE5\u5177\u3002"
|
|
19503
|
+
].join("\n");
|
|
19504
|
+
function appendTextToolSystemPrompt(system) {
|
|
19505
|
+
if (system.some((item) => item.includes(TEXT_TOOL_SYSTEM_MARKER))) {
|
|
19506
|
+
return;
|
|
19507
|
+
}
|
|
19508
|
+
if (system.length === 0) {
|
|
19509
|
+
system.push(TEXT_TOOL_SYSTEM_PROMPT);
|
|
19510
|
+
return;
|
|
19511
|
+
}
|
|
19512
|
+
system[0] = `${system[0]}
|
|
19513
|
+
|
|
19514
|
+
${TEXT_TOOL_SYSTEM_PROMPT}`;
|
|
19515
|
+
}
|
|
19516
|
+
|
|
19500
19517
|
// src/tools/text_edit.ts
|
|
19501
19518
|
var text_edit_default = tool({
|
|
19502
19519
|
description: `Edit text files with automatic encoding detection and preservation.
|
|
@@ -19641,7 +19658,8 @@ var MANAGED_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
|
19641
19658
|
"text_edit"
|
|
19642
19659
|
]);
|
|
19643
19660
|
var BUILTIN_TEXT_TOOL_IDS = /* @__PURE__ */ new Set(["read", "write", "edit"]);
|
|
19644
|
-
var
|
|
19661
|
+
var TEXT_TOOL_IDS = /* @__PURE__ */ new Set(["text_read", "text_write", "text_edit"]);
|
|
19662
|
+
var ROUTED_TEXT_TOOL_IDS = /* @__PURE__ */ new Set([...BUILTIN_TEXT_TOOL_IDS, ...TEXT_TOOL_IDS]);
|
|
19645
19663
|
function getToolFilePath(args) {
|
|
19646
19664
|
if (!args || typeof args !== "object") {
|
|
19647
19665
|
return null;
|
|
@@ -19649,6 +19667,35 @@ function getToolFilePath(args) {
|
|
|
19649
19667
|
const filePath = args.filePath;
|
|
19650
19668
|
return typeof filePath === "string" ? filePath : null;
|
|
19651
19669
|
}
|
|
19670
|
+
function getToolAllowExternal(args) {
|
|
19671
|
+
if (!args || typeof args !== "object") {
|
|
19672
|
+
return false;
|
|
19673
|
+
}
|
|
19674
|
+
return args.allowExternal === true;
|
|
19675
|
+
}
|
|
19676
|
+
function normalizeSessionFilePath(filePath, directory, worktree) {
|
|
19677
|
+
const resolved = path5.normalize(resolveCandidatePath(filePath, { directory, worktree }));
|
|
19678
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
19679
|
+
}
|
|
19680
|
+
function buildGbkRoutingMessage(filePath, encoding) {
|
|
19681
|
+
return `\u6587\u4EF6\u68C0\u6D4B\u4E3A ${encoding.toUpperCase()} \u7F16\u7801\uFF0C\u8BF7\u76F4\u63A5\u6539\u7528 gbk_read\u3001gbk_write\u3001gbk_edit \u6216 gbk_search\uFF0C\u4E0D\u8981\u5148\u4F7F\u7528\u5185\u7F6E read/write/edit \u6216 text_*\uFF1A${filePath}`;
|
|
19682
|
+
}
|
|
19683
|
+
function buildTextEditSessionRoutingMessage(filePath) {
|
|
19684
|
+
return `\u5F53\u524D\u4F1A\u8BDD\u5DF2\u5BF9\u8BE5\u6587\u4EF6\u4F7F\u7528 text_edit\uFF0C\u8BF7\u7EE7\u7EED\u4F7F\u7528 text_read\u3001text_write\u3001text_edit \u6216\u76F4\u63A5\u6539\u7528 gbk_*\uFF1B\u4E0D\u8981\u518D\u5207\u56DE\u5185\u7F6E read/write/edit\uFF0C\u4EE5\u514D\u89E6\u53D1\u6587\u4EF6\u65B0\u9C9C\u5EA6\u68C0\u67E5\u51B2\u7A81\uFF1A${filePath}`;
|
|
19685
|
+
}
|
|
19686
|
+
async function detectExistingGbkEncoding(filePath, allowExternal, directory, worktree) {
|
|
19687
|
+
try {
|
|
19688
|
+
const detected = await detectTextFileEncoding({
|
|
19689
|
+
filePath,
|
|
19690
|
+
encoding: "auto",
|
|
19691
|
+
allowExternal,
|
|
19692
|
+
context: { directory, worktree }
|
|
19693
|
+
});
|
|
19694
|
+
return isRememberedGbkEncoding(detected.encoding) ? detected.encoding : null;
|
|
19695
|
+
} catch {
|
|
19696
|
+
return null;
|
|
19697
|
+
}
|
|
19698
|
+
}
|
|
19652
19699
|
async function maybePersistRememberedEncoding(metadata) {
|
|
19653
19700
|
const filePath = typeof metadata.filePath === "string" ? metadata.filePath : null;
|
|
19654
19701
|
const encoding = metadata.encoding;
|
|
@@ -19712,7 +19759,25 @@ async function maybeAutoSummarizeSession(client, directory, input) {
|
|
|
19712
19759
|
markAutoSummarizeFinished(input.sessionID, false);
|
|
19713
19760
|
}
|
|
19714
19761
|
}
|
|
19715
|
-
function createOpencodeGbkHooks(client, directory) {
|
|
19762
|
+
function createOpencodeGbkHooks(client, directory, worktree) {
|
|
19763
|
+
const sessionTextEditedFiles = /* @__PURE__ */ new Map();
|
|
19764
|
+
function rememberSessionTextEditFile(sessionID, normalizedFilePath) {
|
|
19765
|
+
if (!sessionID) {
|
|
19766
|
+
return;
|
|
19767
|
+
}
|
|
19768
|
+
let files = sessionTextEditedFiles.get(sessionID);
|
|
19769
|
+
if (!files) {
|
|
19770
|
+
files = /* @__PURE__ */ new Set();
|
|
19771
|
+
sessionTextEditedFiles.set(sessionID, files);
|
|
19772
|
+
}
|
|
19773
|
+
files.add(normalizedFilePath);
|
|
19774
|
+
}
|
|
19775
|
+
function hasSessionTextEditFile(sessionID, normalizedFilePath) {
|
|
19776
|
+
if (!sessionID) {
|
|
19777
|
+
return false;
|
|
19778
|
+
}
|
|
19779
|
+
return sessionTextEditedFiles.get(sessionID)?.has(normalizedFilePath) ?? false;
|
|
19780
|
+
}
|
|
19716
19781
|
return {
|
|
19717
19782
|
tool: {
|
|
19718
19783
|
gbk_read: gbk_read_default,
|
|
@@ -19743,19 +19808,32 @@ function createOpencodeGbkHooks(client, directory) {
|
|
|
19743
19808
|
if (!filePath) {
|
|
19744
19809
|
return;
|
|
19745
19810
|
}
|
|
19746
|
-
|
|
19747
|
-
if (!remembered) {
|
|
19811
|
+
if (!ROUTED_TEXT_TOOL_IDS.has(input.tool)) {
|
|
19748
19812
|
return;
|
|
19749
19813
|
}
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19814
|
+
const normalizedFilePath = normalizeSessionFilePath(filePath, directory, worktree);
|
|
19815
|
+
if (BUILTIN_TEXT_TOOL_IDS.has(input.tool) && hasSessionTextEditFile(input.sessionID, normalizedFilePath)) {
|
|
19816
|
+
throw new Error(buildTextEditSessionRoutingMessage(filePath));
|
|
19817
|
+
}
|
|
19818
|
+
const resolvedFilePath = resolveCandidatePath(filePath, { directory, worktree });
|
|
19819
|
+
const remembered = await getRememberedGbkEncoding(resolvedFilePath);
|
|
19820
|
+
if (remembered) {
|
|
19821
|
+
throw new Error(buildGbkRoutingMessage(filePath, remembered.encoding));
|
|
19822
|
+
}
|
|
19823
|
+
const detectedEncoding = await detectExistingGbkEncoding(
|
|
19824
|
+
resolvedFilePath,
|
|
19825
|
+
getToolAllowExternal(output.args),
|
|
19826
|
+
directory,
|
|
19827
|
+
worktree
|
|
19828
|
+
);
|
|
19829
|
+
if (!detectedEncoding) {
|
|
19754
19830
|
return;
|
|
19755
19831
|
}
|
|
19756
|
-
|
|
19757
|
-
|
|
19832
|
+
try {
|
|
19833
|
+
await rememberGbkEncoding(resolvedFilePath, detectedEncoding);
|
|
19834
|
+
} catch {
|
|
19758
19835
|
}
|
|
19836
|
+
throw new Error(buildGbkRoutingMessage(filePath, detectedEncoding));
|
|
19759
19837
|
},
|
|
19760
19838
|
async "tool.execute.after"(input, output) {
|
|
19761
19839
|
if (!MANAGED_TOOL_IDS.has(input.tool)) return;
|
|
@@ -19772,6 +19850,12 @@ function createOpencodeGbkHooks(client, directory) {
|
|
|
19772
19850
|
if (typeof metadata.diffPreview === "string") {
|
|
19773
19851
|
metadata.diffPreview = truncateMetadataPreview(metadata.diffPreview, input.sessionID);
|
|
19774
19852
|
}
|
|
19853
|
+
if (input.tool === "text_edit" && typeof metadata.filePath === "string") {
|
|
19854
|
+
rememberSessionTextEditFile(
|
|
19855
|
+
input.sessionID,
|
|
19856
|
+
normalizeSessionFilePath(metadata.filePath, directory, worktree)
|
|
19857
|
+
);
|
|
19858
|
+
}
|
|
19775
19859
|
try {
|
|
19776
19860
|
await maybePersistRememberedEncoding(metadata);
|
|
19777
19861
|
} catch {
|
|
@@ -19803,7 +19887,7 @@ function createOpencodeGbkHooks(client, directory) {
|
|
|
19803
19887
|
var pluginModule = {
|
|
19804
19888
|
id: "opencode-gbk-tools",
|
|
19805
19889
|
async server(ctx) {
|
|
19806
|
-
return createOpencodeGbkHooks(ctx.client, ctx.directory);
|
|
19890
|
+
return createOpencodeGbkHooks(ctx.client, ctx.directory, ctx.worktree);
|
|
19807
19891
|
}
|
|
19808
19892
|
};
|
|
19809
19893
|
var plugin_default = pluginModule;
|
|
@@ -3816,6 +3816,9 @@ var require_lib = __commonJS({
|
|
|
3816
3816
|
}
|
|
3817
3817
|
});
|
|
3818
3818
|
|
|
3819
|
+
// src/plugin/index.ts
|
|
3820
|
+
import path5 from "path";
|
|
3821
|
+
|
|
3819
3822
|
// src/lib/session-pressure.ts
|
|
3820
3823
|
var AUTO_SUMMARIZE_PRESSURE_RATIO = 0.85;
|
|
3821
3824
|
var AUTO_SUMMARIZE_COOLDOWN_MS = 6e4;
|
|
@@ -4043,6 +4046,67 @@ async function getRememberedGbkEncoding(filePath) {
|
|
|
4043
4046
|
return entry;
|
|
4044
4047
|
}
|
|
4045
4048
|
|
|
4049
|
+
// src/lib/path-sandbox.ts
|
|
4050
|
+
import fs2 from "fs/promises";
|
|
4051
|
+
import path2 from "path";
|
|
4052
|
+
|
|
4053
|
+
// src/lib/errors.ts
|
|
4054
|
+
var GbkToolError = class extends Error {
|
|
4055
|
+
code;
|
|
4056
|
+
cause;
|
|
4057
|
+
constructor(code, message, cause) {
|
|
4058
|
+
super(message);
|
|
4059
|
+
this.name = "GbkToolError";
|
|
4060
|
+
this.code = code;
|
|
4061
|
+
this.cause = cause;
|
|
4062
|
+
}
|
|
4063
|
+
};
|
|
4064
|
+
function createGbkError(code, message, cause) {
|
|
4065
|
+
return new GbkToolError(code, message, cause);
|
|
4066
|
+
}
|
|
4067
|
+
var createTextError = createGbkError;
|
|
4068
|
+
|
|
4069
|
+
// src/lib/path-sandbox.ts
|
|
4070
|
+
function resolveBaseDirectory(context) {
|
|
4071
|
+
const value = context.directory ?? process.cwd();
|
|
4072
|
+
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
4073
|
+
}
|
|
4074
|
+
function resolveWorkspaceRoot(context) {
|
|
4075
|
+
const value = context.worktree ?? context.directory ?? process.cwd();
|
|
4076
|
+
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
4077
|
+
}
|
|
4078
|
+
function resolveCandidatePath(filePath, context) {
|
|
4079
|
+
return path2.resolve(resolveBaseDirectory(context), filePath);
|
|
4080
|
+
}
|
|
4081
|
+
async function resolveExistingAnchor(filePath) {
|
|
4082
|
+
let current = filePath;
|
|
4083
|
+
while (true) {
|
|
4084
|
+
try {
|
|
4085
|
+
return await fs2.realpath(current);
|
|
4086
|
+
} catch {
|
|
4087
|
+
const parent = path2.dirname(current);
|
|
4088
|
+
if (parent === current) {
|
|
4089
|
+
throw createGbkError("GBK_IO_ERROR", `\u65E0\u6CD5\u89E3\u6790\u8DEF\u5F84\u951A\u70B9: ${filePath}`);
|
|
4090
|
+
}
|
|
4091
|
+
current = parent;
|
|
4092
|
+
}
|
|
4093
|
+
}
|
|
4094
|
+
}
|
|
4095
|
+
async function assertPathAllowed(filePath, context, allowExternal = false) {
|
|
4096
|
+
const candidatePath = resolveCandidatePath(filePath, context);
|
|
4097
|
+
const workspaceRoot = resolveWorkspaceRoot(context);
|
|
4098
|
+
if (!allowExternal) {
|
|
4099
|
+
const realWorkspaceRoot = await fs2.realpath(workspaceRoot);
|
|
4100
|
+
const realCandidateAnchor = await resolveExistingAnchor(candidatePath);
|
|
4101
|
+
const relative = path2.relative(realWorkspaceRoot, realCandidateAnchor);
|
|
4102
|
+
if (relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative)) {
|
|
4103
|
+
return { candidatePath, workspaceRoot };
|
|
4104
|
+
}
|
|
4105
|
+
throw createGbkError("GBK_PATH_OUTSIDE_ROOT", `\u76EE\u6807\u8DEF\u5F84\u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4: ${candidatePath}`);
|
|
4106
|
+
}
|
|
4107
|
+
return { candidatePath, workspaceRoot };
|
|
4108
|
+
}
|
|
4109
|
+
|
|
4046
4110
|
// node_modules/zod/v4/classic/external.js
|
|
4047
4111
|
var external_exports = {};
|
|
4048
4112
|
__export(external_exports, {
|
|
@@ -4774,10 +4838,10 @@ function mergeDefs(...defs) {
|
|
|
4774
4838
|
function cloneDef(schema) {
|
|
4775
4839
|
return mergeDefs(schema._zod.def);
|
|
4776
4840
|
}
|
|
4777
|
-
function getElementAtPath(obj,
|
|
4778
|
-
if (!
|
|
4841
|
+
function getElementAtPath(obj, path6) {
|
|
4842
|
+
if (!path6)
|
|
4779
4843
|
return obj;
|
|
4780
|
-
return
|
|
4844
|
+
return path6.reduce((acc, key) => acc?.[key], obj);
|
|
4781
4845
|
}
|
|
4782
4846
|
function promiseAllObject(promisesObj) {
|
|
4783
4847
|
const keys = Object.keys(promisesObj);
|
|
@@ -5138,11 +5202,11 @@ function aborted(x, startIndex = 0) {
|
|
|
5138
5202
|
}
|
|
5139
5203
|
return false;
|
|
5140
5204
|
}
|
|
5141
|
-
function prefixIssues(
|
|
5205
|
+
function prefixIssues(path6, issues) {
|
|
5142
5206
|
return issues.map((iss) => {
|
|
5143
5207
|
var _a;
|
|
5144
5208
|
(_a = iss).path ?? (_a.path = []);
|
|
5145
|
-
iss.path.unshift(
|
|
5209
|
+
iss.path.unshift(path6);
|
|
5146
5210
|
return iss;
|
|
5147
5211
|
});
|
|
5148
5212
|
}
|
|
@@ -5310,7 +5374,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5310
5374
|
return issue2.message;
|
|
5311
5375
|
};
|
|
5312
5376
|
const result = { errors: [] };
|
|
5313
|
-
const processError = (error46,
|
|
5377
|
+
const processError = (error46, path6 = []) => {
|
|
5314
5378
|
var _a, _b;
|
|
5315
5379
|
for (const issue2 of error46.issues) {
|
|
5316
5380
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -5320,7 +5384,7 @@ function treeifyError(error45, _mapper) {
|
|
|
5320
5384
|
} else if (issue2.code === "invalid_element") {
|
|
5321
5385
|
processError({ issues: issue2.issues }, issue2.path);
|
|
5322
5386
|
} else {
|
|
5323
|
-
const fullpath = [...
|
|
5387
|
+
const fullpath = [...path6, ...issue2.path];
|
|
5324
5388
|
if (fullpath.length === 0) {
|
|
5325
5389
|
result.errors.push(mapper(issue2));
|
|
5326
5390
|
continue;
|
|
@@ -5352,8 +5416,8 @@ function treeifyError(error45, _mapper) {
|
|
|
5352
5416
|
}
|
|
5353
5417
|
function toDotPath(_path) {
|
|
5354
5418
|
const segs = [];
|
|
5355
|
-
const
|
|
5356
|
-
for (const seg of
|
|
5419
|
+
const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
5420
|
+
for (const seg of path6) {
|
|
5357
5421
|
if (typeof seg === "number")
|
|
5358
5422
|
segs.push(`[${seg}]`);
|
|
5359
5423
|
else if (typeof seg === "symbol")
|
|
@@ -16597,67 +16661,6 @@ import crypto from "crypto";
|
|
|
16597
16661
|
import { createReadStream } from "fs";
|
|
16598
16662
|
import fs3 from "fs/promises";
|
|
16599
16663
|
import path3 from "path";
|
|
16600
|
-
|
|
16601
|
-
// src/lib/errors.ts
|
|
16602
|
-
var GbkToolError = class extends Error {
|
|
16603
|
-
code;
|
|
16604
|
-
cause;
|
|
16605
|
-
constructor(code, message, cause) {
|
|
16606
|
-
super(message);
|
|
16607
|
-
this.name = "GbkToolError";
|
|
16608
|
-
this.code = code;
|
|
16609
|
-
this.cause = cause;
|
|
16610
|
-
}
|
|
16611
|
-
};
|
|
16612
|
-
function createGbkError(code, message, cause) {
|
|
16613
|
-
return new GbkToolError(code, message, cause);
|
|
16614
|
-
}
|
|
16615
|
-
var createTextError = createGbkError;
|
|
16616
|
-
|
|
16617
|
-
// src/lib/path-sandbox.ts
|
|
16618
|
-
import fs2 from "fs/promises";
|
|
16619
|
-
import path2 from "path";
|
|
16620
|
-
function resolveBaseDirectory(context) {
|
|
16621
|
-
const value = context.directory ?? process.cwd();
|
|
16622
|
-
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
16623
|
-
}
|
|
16624
|
-
function resolveWorkspaceRoot(context) {
|
|
16625
|
-
const value = context.worktree ?? context.directory ?? process.cwd();
|
|
16626
|
-
return path2.isAbsolute(value) ? value : path2.resolve(process.cwd(), value);
|
|
16627
|
-
}
|
|
16628
|
-
function resolveCandidatePath(filePath, context) {
|
|
16629
|
-
return path2.resolve(resolveBaseDirectory(context), filePath);
|
|
16630
|
-
}
|
|
16631
|
-
async function resolveExistingAnchor(filePath) {
|
|
16632
|
-
let current = filePath;
|
|
16633
|
-
while (true) {
|
|
16634
|
-
try {
|
|
16635
|
-
return await fs2.realpath(current);
|
|
16636
|
-
} catch {
|
|
16637
|
-
const parent = path2.dirname(current);
|
|
16638
|
-
if (parent === current) {
|
|
16639
|
-
throw createGbkError("GBK_IO_ERROR", `\u65E0\u6CD5\u89E3\u6790\u8DEF\u5F84\u951A\u70B9: ${filePath}`);
|
|
16640
|
-
}
|
|
16641
|
-
current = parent;
|
|
16642
|
-
}
|
|
16643
|
-
}
|
|
16644
|
-
}
|
|
16645
|
-
async function assertPathAllowed(filePath, context, allowExternal = false) {
|
|
16646
|
-
const candidatePath = resolveCandidatePath(filePath, context);
|
|
16647
|
-
const workspaceRoot = resolveWorkspaceRoot(context);
|
|
16648
|
-
if (!allowExternal) {
|
|
16649
|
-
const realWorkspaceRoot = await fs2.realpath(workspaceRoot);
|
|
16650
|
-
const realCandidateAnchor = await resolveExistingAnchor(candidatePath);
|
|
16651
|
-
const relative = path2.relative(realWorkspaceRoot, realCandidateAnchor);
|
|
16652
|
-
if (relative === "" || !relative.startsWith("..") && !path2.isAbsolute(relative)) {
|
|
16653
|
-
return { candidatePath, workspaceRoot };
|
|
16654
|
-
}
|
|
16655
|
-
throw createGbkError("GBK_PATH_OUTSIDE_ROOT", `\u76EE\u6807\u8DEF\u5F84\u8D85\u51FA\u5DE5\u4F5C\u76EE\u5F55\u8303\u56F4: ${candidatePath}`);
|
|
16656
|
-
}
|
|
16657
|
-
return { candidatePath, workspaceRoot };
|
|
16658
|
-
}
|
|
16659
|
-
|
|
16660
|
-
// src/lib/gbk-file.ts
|
|
16661
16664
|
var STREAMING_FILE_SIZE_THRESHOLD_BYTES = 1024 * 1024;
|
|
16662
16665
|
var STREAM_READ_CHUNK_SIZE_BYTES = 1024 * 1024;
|
|
16663
16666
|
var gbkLineIndexCache = /* @__PURE__ */ new Map();
|
|
@@ -18243,35 +18246,6 @@ var gbk_write_default = tool({
|
|
|
18243
18246
|
}
|
|
18244
18247
|
});
|
|
18245
18248
|
|
|
18246
|
-
// src/plugin/text-guidance.ts
|
|
18247
|
-
var TEXT_TOOL_SYSTEM_MARKER = "[opencode-gbk-tools:text-rules]";
|
|
18248
|
-
var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
18249
|
-
TEXT_TOOL_SYSTEM_MARKER,
|
|
18250
|
-
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
18251
|
-
"- \u666E\u901A UTF-8 / UTF-8 BOM / UTF-16 \u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 OpenCode \u5185\u7F6E read\u3001write\u3001edit\u3002",
|
|
18252
|
-
"- \u9047\u5230 GBK / GB18030 \u6587\u4EF6\u3001\u4E2D\u6587\u4E71\u7801\u3001\u975E UTF-8 \u65E7\u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\u3002",
|
|
18253
|
-
"- \u65B0\u5EFA .txt \u6587\u4EF6\u5728 encoding=auto \u4E0B\u9ED8\u8BA4\u4F7F\u7528 GBK\uFF1B\u5176\u4ED6\u65B0\u6587\u4EF6\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
18254
|
-
"- \u65E0\u6CD5\u786E\u5B9A\u7F16\u7801\u65F6\uFF0C\u5148\u5C1D\u8BD5\u5185\u7F6E read\uFF1B\u82E5\u51FA\u73B0\u4E2D\u6587\u4E71\u7801\u3001\u66FF\u6362\u5931\u8D25\u6216\u7F16\u7801\u98CE\u9669\uFF0C\u518D\u5207\u6362\u5230 gbk_*\u3002",
|
|
18255
|
-
"- \u5DF2\u786E\u8BA4\u662F GBK/GB18030 \u7684\u6587\u4EF6\u4F1A\u88AB\u63D2\u4EF6\u6301\u4E45\u8BB0\u5FC6\uFF1B\u518D\u6B21\u64CD\u4F5C\u540C\u4E00\u8DEF\u5F84\u65F6\uFF0C\u4F18\u5148\u7EE7\u7EED\u6309 GBK \u5904\u7406\u3002",
|
|
18256
|
-
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
18257
|
-
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
18258
|
-
"- anchor\u3001startAnchor\u3001endAnchor\u3001oldString \u82E5\u76F4\u63A5\u590D\u5236\u81EA\u8BFB\u53D6\u7ED3\u679C\uFF0C\u53EF\u4FDD\u7559 LF \u6362\u884C\uFF1Bgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u6309\u6587\u4EF6\u6362\u884C\u98CE\u683C\u81EA\u52A8\u5BF9\u9F50\u3002",
|
|
18259
|
-
'- \u82E5\u8BFB\u53D6\u7ED3\u679C\u5E26\u6709 "N: " \u884C\u53F7\u524D\u7F00\uFF0Cgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u81EA\u52A8\u5265\u79BB\u8FD9\u4E9B\u524D\u7F00\u540E\u518D\u5339\u914D\u3002',
|
|
18260
|
-
"- gbk-engine \u662F\u5F3A\u5236 GBK \u4E13\u5C5E\u6A21\u5F0F\uFF1A\u53EA\u5141\u8BB8 gbk_*\uFF0C\u4E0D\u8D70\u5185\u7F6E\u8BFB\u5199\u7F16\u8F91\u5DE5\u5177\u3002"
|
|
18261
|
-
].join("\n");
|
|
18262
|
-
function appendTextToolSystemPrompt(system) {
|
|
18263
|
-
if (system.some((item) => item.includes(TEXT_TOOL_SYSTEM_MARKER))) {
|
|
18264
|
-
return;
|
|
18265
|
-
}
|
|
18266
|
-
if (system.length === 0) {
|
|
18267
|
-
system.push(TEXT_TOOL_SYSTEM_PROMPT);
|
|
18268
|
-
return;
|
|
18269
|
-
}
|
|
18270
|
-
system[0] = `${system[0]}
|
|
18271
|
-
|
|
18272
|
-
${TEXT_TOOL_SYSTEM_PROMPT}`;
|
|
18273
|
-
}
|
|
18274
|
-
|
|
18275
18249
|
// src/lib/text-file.ts
|
|
18276
18250
|
var import_iconv_lite2 = __toESM(require_lib(), 1);
|
|
18277
18251
|
import crypto2 from "crypto";
|
|
@@ -19220,6 +19194,20 @@ async function ensureParentDirectory(parent, createDirectories) {
|
|
|
19220
19194
|
throw error45;
|
|
19221
19195
|
}
|
|
19222
19196
|
}
|
|
19197
|
+
async function detectTextFileEncoding(input) {
|
|
19198
|
+
const detected = await detectReadableTextFile(input);
|
|
19199
|
+
const fileSize = typeof detected.stat.size === "bigint" ? Number(detected.stat.size) : detected.stat.size;
|
|
19200
|
+
return {
|
|
19201
|
+
filePath: detected.filePath,
|
|
19202
|
+
encoding: detected.encoding,
|
|
19203
|
+
requestedEncoding: detected.requestedEncoding,
|
|
19204
|
+
detectedEncoding: detected.detectedEncoding,
|
|
19205
|
+
confidence: detected.confidence,
|
|
19206
|
+
hasBom: detected.hasBom,
|
|
19207
|
+
bytesRead: Math.min(fileSize, TEXT_DETECTION_SAMPLE_BYTES),
|
|
19208
|
+
newlineStyle: "unknown"
|
|
19209
|
+
};
|
|
19210
|
+
}
|
|
19223
19211
|
async function readTextFile(input) {
|
|
19224
19212
|
const offset = normalizeOptionalPositiveInteger(input.offset, "offset") ?? 1;
|
|
19225
19213
|
const limit = normalizeOptionalPositiveInteger(input.limit, "limit") ?? 2e3;
|
|
@@ -19497,6 +19485,35 @@ async function replaceTextFileText(input) {
|
|
|
19497
19485
|
};
|
|
19498
19486
|
}
|
|
19499
19487
|
|
|
19488
|
+
// src/plugin/text-guidance.ts
|
|
19489
|
+
var TEXT_TOOL_SYSTEM_MARKER = "[opencode-gbk-tools:text-rules]";
|
|
19490
|
+
var TEXT_TOOL_SYSTEM_PROMPT = [
|
|
19491
|
+
TEXT_TOOL_SYSTEM_MARKER,
|
|
19492
|
+
"\u6587\u672C\u6587\u4EF6\u5904\u7406\u89C4\u5219\uFF1A",
|
|
19493
|
+
"- \u666E\u901A UTF-8 / UTF-8 BOM / UTF-16 \u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 OpenCode \u5185\u7F6E read\u3001write\u3001edit\u3002",
|
|
19494
|
+
"- \u9047\u5230 GBK / GB18030 \u6587\u4EF6\u3001\u4E2D\u6587\u4E71\u7801\u3001\u975E UTF-8 \u65E7\u6587\u672C\uFF0C\u4F18\u5148\u4F7F\u7528 gbk_read\u3001gbk_write\u3001gbk_edit\u3001gbk_search\uFF0C\u4E0D\u8981\u5148\u5C1D\u8BD5 text_* \u6216\u5185\u7F6E read/write/edit\u3002",
|
|
19495
|
+
"- \u65B0\u5EFA .txt \u6587\u4EF6\u5728 encoding=auto \u4E0B\u9ED8\u8BA4\u4F7F\u7528 GBK\uFF1B\u5176\u4ED6\u65B0\u6587\u4EF6\u8BF7\u663E\u5F0F\u6307\u5B9A encoding\u3002",
|
|
19496
|
+
"- \u5BF9\u73B0\u6709 .txt / .cfg / .ini / .log \u7B49\u65E7\u6587\u672C\u6587\u4EF6\uFF0C\u53EA\u8981\u6000\u7591\u662F\u4E2D\u6587\u672C\u5730\u7F16\u7801\uFF0C\u4F18\u5148\u5148\u7528 gbk_read \u5224\u65AD\uFF0C\u4E0D\u8981\u5148 edit \u518D\u56E0\u4E3A\u5339\u914D\u5931\u8D25\u6216\u6587\u4EF6\u65F6\u95F4\u6233\u53D8\u5316\u800C\u56DE\u9000\u3002",
|
|
19497
|
+
"- \u5DF2\u786E\u8BA4\u6216\u9996\u6B21\u68C0\u6D4B\u5230\u662F GBK/GB18030 \u7684\u6587\u4EF6\u4F1A\u88AB\u63D2\u4EF6\u6301\u4E45\u8BB0\u5FC6\uFF1B\u518D\u6B21\u64CD\u4F5C\u540C\u4E00\u8DEF\u5F84\u65F6\uFF0C\u4F1A\u76F4\u63A5\u62E6\u622A\u5185\u7F6E read/write/edit \u548C text_*\uFF0C\u5E76\u8981\u6C42\u6539\u7528 gbk_*\u3002",
|
|
19498
|
+
"- \u5982\u679C\u610F\u56FE\u662F\u2018\u5728\u67D0\u6807\u7B7E\u524D\u540E\u63D2\u5165\u5185\u5BB9\u2019\uFF0C\u4F18\u5148\u4F7F\u7528 mode=insertAfter \u6216 mode=insertBefore\uFF0C\u5E76\u4F20 anchor/content\u3002",
|
|
19499
|
+
"- \u53EA\u6709\u5728\u660E\u786E\u505A\u7CBE\u786E\u66FF\u6362\u65F6\uFF0C\u624D\u4F7F\u7528 oldString/newString\u3002",
|
|
19500
|
+
"- anchor\u3001startAnchor\u3001endAnchor\u3001oldString \u82E5\u76F4\u63A5\u590D\u5236\u81EA\u8BFB\u53D6\u7ED3\u679C\uFF0C\u53EF\u4FDD\u7559 LF \u6362\u884C\uFF1Bgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u6309\u6587\u4EF6\u6362\u884C\u98CE\u683C\u81EA\u52A8\u5BF9\u9F50\u3002",
|
|
19501
|
+
'- \u82E5\u8BFB\u53D6\u7ED3\u679C\u5E26\u6709 "N: " \u884C\u53F7\u524D\u7F00\uFF0Cgbk_edit / text_edit \u4F1A\u5C3D\u91CF\u81EA\u52A8\u5265\u79BB\u8FD9\u4E9B\u524D\u7F00\u540E\u518D\u5339\u914D\u3002',
|
|
19502
|
+
"- gbk-engine \u662F\u5F3A\u5236 GBK \u4E13\u5C5E\u6A21\u5F0F\uFF1A\u53EA\u5141\u8BB8 gbk_*\uFF0C\u4E0D\u8D70\u5185\u7F6E\u8BFB\u5199\u7F16\u8F91\u5DE5\u5177\u3002"
|
|
19503
|
+
].join("\n");
|
|
19504
|
+
function appendTextToolSystemPrompt(system) {
|
|
19505
|
+
if (system.some((item) => item.includes(TEXT_TOOL_SYSTEM_MARKER))) {
|
|
19506
|
+
return;
|
|
19507
|
+
}
|
|
19508
|
+
if (system.length === 0) {
|
|
19509
|
+
system.push(TEXT_TOOL_SYSTEM_PROMPT);
|
|
19510
|
+
return;
|
|
19511
|
+
}
|
|
19512
|
+
system[0] = `${system[0]}
|
|
19513
|
+
|
|
19514
|
+
${TEXT_TOOL_SYSTEM_PROMPT}`;
|
|
19515
|
+
}
|
|
19516
|
+
|
|
19500
19517
|
// src/tools/text_edit.ts
|
|
19501
19518
|
var text_edit_default = tool({
|
|
19502
19519
|
description: `Edit text files with automatic encoding detection and preservation.
|
|
@@ -19641,7 +19658,8 @@ var MANAGED_TOOL_IDS = /* @__PURE__ */ new Set([
|
|
|
19641
19658
|
"text_edit"
|
|
19642
19659
|
]);
|
|
19643
19660
|
var BUILTIN_TEXT_TOOL_IDS = /* @__PURE__ */ new Set(["read", "write", "edit"]);
|
|
19644
|
-
var
|
|
19661
|
+
var TEXT_TOOL_IDS = /* @__PURE__ */ new Set(["text_read", "text_write", "text_edit"]);
|
|
19662
|
+
var ROUTED_TEXT_TOOL_IDS = /* @__PURE__ */ new Set([...BUILTIN_TEXT_TOOL_IDS, ...TEXT_TOOL_IDS]);
|
|
19645
19663
|
function getToolFilePath(args) {
|
|
19646
19664
|
if (!args || typeof args !== "object") {
|
|
19647
19665
|
return null;
|
|
@@ -19649,6 +19667,35 @@ function getToolFilePath(args) {
|
|
|
19649
19667
|
const filePath = args.filePath;
|
|
19650
19668
|
return typeof filePath === "string" ? filePath : null;
|
|
19651
19669
|
}
|
|
19670
|
+
function getToolAllowExternal(args) {
|
|
19671
|
+
if (!args || typeof args !== "object") {
|
|
19672
|
+
return false;
|
|
19673
|
+
}
|
|
19674
|
+
return args.allowExternal === true;
|
|
19675
|
+
}
|
|
19676
|
+
function normalizeSessionFilePath(filePath, directory, worktree) {
|
|
19677
|
+
const resolved = path5.normalize(resolveCandidatePath(filePath, { directory, worktree }));
|
|
19678
|
+
return process.platform === "win32" ? resolved.toLowerCase() : resolved;
|
|
19679
|
+
}
|
|
19680
|
+
function buildGbkRoutingMessage(filePath, encoding) {
|
|
19681
|
+
return `\u6587\u4EF6\u68C0\u6D4B\u4E3A ${encoding.toUpperCase()} \u7F16\u7801\uFF0C\u8BF7\u76F4\u63A5\u6539\u7528 gbk_read\u3001gbk_write\u3001gbk_edit \u6216 gbk_search\uFF0C\u4E0D\u8981\u5148\u4F7F\u7528\u5185\u7F6E read/write/edit \u6216 text_*\uFF1A${filePath}`;
|
|
19682
|
+
}
|
|
19683
|
+
function buildTextEditSessionRoutingMessage(filePath) {
|
|
19684
|
+
return `\u5F53\u524D\u4F1A\u8BDD\u5DF2\u5BF9\u8BE5\u6587\u4EF6\u4F7F\u7528 text_edit\uFF0C\u8BF7\u7EE7\u7EED\u4F7F\u7528 text_read\u3001text_write\u3001text_edit \u6216\u76F4\u63A5\u6539\u7528 gbk_*\uFF1B\u4E0D\u8981\u518D\u5207\u56DE\u5185\u7F6E read/write/edit\uFF0C\u4EE5\u514D\u89E6\u53D1\u6587\u4EF6\u65B0\u9C9C\u5EA6\u68C0\u67E5\u51B2\u7A81\uFF1A${filePath}`;
|
|
19685
|
+
}
|
|
19686
|
+
async function detectExistingGbkEncoding(filePath, allowExternal, directory, worktree) {
|
|
19687
|
+
try {
|
|
19688
|
+
const detected = await detectTextFileEncoding({
|
|
19689
|
+
filePath,
|
|
19690
|
+
encoding: "auto",
|
|
19691
|
+
allowExternal,
|
|
19692
|
+
context: { directory, worktree }
|
|
19693
|
+
});
|
|
19694
|
+
return isRememberedGbkEncoding(detected.encoding) ? detected.encoding : null;
|
|
19695
|
+
} catch {
|
|
19696
|
+
return null;
|
|
19697
|
+
}
|
|
19698
|
+
}
|
|
19652
19699
|
async function maybePersistRememberedEncoding(metadata) {
|
|
19653
19700
|
const filePath = typeof metadata.filePath === "string" ? metadata.filePath : null;
|
|
19654
19701
|
const encoding = metadata.encoding;
|
|
@@ -19712,7 +19759,25 @@ async function maybeAutoSummarizeSession(client, directory, input) {
|
|
|
19712
19759
|
markAutoSummarizeFinished(input.sessionID, false);
|
|
19713
19760
|
}
|
|
19714
19761
|
}
|
|
19715
|
-
function createOpencodeGbkHooks(client, directory) {
|
|
19762
|
+
function createOpencodeGbkHooks(client, directory, worktree) {
|
|
19763
|
+
const sessionTextEditedFiles = /* @__PURE__ */ new Map();
|
|
19764
|
+
function rememberSessionTextEditFile(sessionID, normalizedFilePath) {
|
|
19765
|
+
if (!sessionID) {
|
|
19766
|
+
return;
|
|
19767
|
+
}
|
|
19768
|
+
let files = sessionTextEditedFiles.get(sessionID);
|
|
19769
|
+
if (!files) {
|
|
19770
|
+
files = /* @__PURE__ */ new Set();
|
|
19771
|
+
sessionTextEditedFiles.set(sessionID, files);
|
|
19772
|
+
}
|
|
19773
|
+
files.add(normalizedFilePath);
|
|
19774
|
+
}
|
|
19775
|
+
function hasSessionTextEditFile(sessionID, normalizedFilePath) {
|
|
19776
|
+
if (!sessionID) {
|
|
19777
|
+
return false;
|
|
19778
|
+
}
|
|
19779
|
+
return sessionTextEditedFiles.get(sessionID)?.has(normalizedFilePath) ?? false;
|
|
19780
|
+
}
|
|
19716
19781
|
return {
|
|
19717
19782
|
tool: {
|
|
19718
19783
|
gbk_read: gbk_read_default,
|
|
@@ -19743,19 +19808,32 @@ function createOpencodeGbkHooks(client, directory) {
|
|
|
19743
19808
|
if (!filePath) {
|
|
19744
19809
|
return;
|
|
19745
19810
|
}
|
|
19746
|
-
|
|
19747
|
-
if (!remembered) {
|
|
19811
|
+
if (!ROUTED_TEXT_TOOL_IDS.has(input.tool)) {
|
|
19748
19812
|
return;
|
|
19749
19813
|
}
|
|
19750
|
-
|
|
19751
|
-
|
|
19752
|
-
|
|
19753
|
-
|
|
19814
|
+
const normalizedFilePath = normalizeSessionFilePath(filePath, directory, worktree);
|
|
19815
|
+
if (BUILTIN_TEXT_TOOL_IDS.has(input.tool) && hasSessionTextEditFile(input.sessionID, normalizedFilePath)) {
|
|
19816
|
+
throw new Error(buildTextEditSessionRoutingMessage(filePath));
|
|
19817
|
+
}
|
|
19818
|
+
const resolvedFilePath = resolveCandidatePath(filePath, { directory, worktree });
|
|
19819
|
+
const remembered = await getRememberedGbkEncoding(resolvedFilePath);
|
|
19820
|
+
if (remembered) {
|
|
19821
|
+
throw new Error(buildGbkRoutingMessage(filePath, remembered.encoding));
|
|
19822
|
+
}
|
|
19823
|
+
const detectedEncoding = await detectExistingGbkEncoding(
|
|
19824
|
+
resolvedFilePath,
|
|
19825
|
+
getToolAllowExternal(output.args),
|
|
19826
|
+
directory,
|
|
19827
|
+
worktree
|
|
19828
|
+
);
|
|
19829
|
+
if (!detectedEncoding) {
|
|
19754
19830
|
return;
|
|
19755
19831
|
}
|
|
19756
|
-
|
|
19757
|
-
|
|
19832
|
+
try {
|
|
19833
|
+
await rememberGbkEncoding(resolvedFilePath, detectedEncoding);
|
|
19834
|
+
} catch {
|
|
19758
19835
|
}
|
|
19836
|
+
throw new Error(buildGbkRoutingMessage(filePath, detectedEncoding));
|
|
19759
19837
|
},
|
|
19760
19838
|
async "tool.execute.after"(input, output) {
|
|
19761
19839
|
if (!MANAGED_TOOL_IDS.has(input.tool)) return;
|
|
@@ -19772,6 +19850,12 @@ function createOpencodeGbkHooks(client, directory) {
|
|
|
19772
19850
|
if (typeof metadata.diffPreview === "string") {
|
|
19773
19851
|
metadata.diffPreview = truncateMetadataPreview(metadata.diffPreview, input.sessionID);
|
|
19774
19852
|
}
|
|
19853
|
+
if (input.tool === "text_edit" && typeof metadata.filePath === "string") {
|
|
19854
|
+
rememberSessionTextEditFile(
|
|
19855
|
+
input.sessionID,
|
|
19856
|
+
normalizeSessionFilePath(metadata.filePath, directory, worktree)
|
|
19857
|
+
);
|
|
19858
|
+
}
|
|
19775
19859
|
try {
|
|
19776
19860
|
await maybePersistRememberedEncoding(metadata);
|
|
19777
19861
|
} catch {
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifestVersion": 1,
|
|
3
3
|
"packageName": "opencode-gbk-tools",
|
|
4
|
-
"packageVersion": "0.1.
|
|
4
|
+
"packageVersion": "0.1.30",
|
|
5
5
|
"artifacts": [
|
|
6
6
|
{
|
|
7
7
|
"relativePath": "plugins/opencode-gbk-tools.js",
|
|
8
8
|
"kind": "plugin",
|
|
9
|
-
"expectedHash": "
|
|
9
|
+
"expectedHash": "ce0215b44f5166b283e083969533307a4fc54f62e594dc11c1ca669d6c9b495e",
|
|
10
10
|
"hashAlgorithm": "sha256"
|
|
11
11
|
},
|
|
12
12
|
{
|