opencode-gbk-tools 0.1.9 → 0.1.10

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # opencode-gbk-tools
2
2
 
3
- 为 OpenCode 提供一套面向 `GBK` / `GB18030` 编码文本文件的自定义工具与专用 agent。
3
+ 为 OpenCode 提供一套自动识别编码的文本工具,以及面向 `GBK` / `GB18030` 的专用工具。
4
4
 
5
- 解决 OpenCode 内置工具无法正确读写 GBK 编码文件的问题。
5
+ 解决 OpenCode 内置工具难以稳定处理非 UTF-8 文本文件的问题,并让所有 agents 默认优先使用可保留原编码的 `text_*` 工具。
6
6
 
7
7
  ---
8
8
 
@@ -10,11 +10,14 @@
10
10
 
11
11
  | 工具 | 用途 |
12
12
  |------|------|
13
+ | `text_read` | 自动识别文件编码并按行读取,优先用于通用文本文件 |
14
+ | `text_write` | 自动保持已有文件编码、BOM 和换行风格后写入 |
15
+ | `text_edit` | 自动保持已有文件编码、BOM 和换行风格后编辑 |
13
16
  | `gbk_read` | 读取 GBK/GB18030 文件,支持分页、尾部预览 |
14
17
  | `gbk_write` | 写入或追加内容到 GBK 文件(`append=true` 支持追加) |
15
18
  | `gbk_edit` | 精确替换 GBK 文件中的指定文本块 |
16
19
  | `gbk_search` | 在 GBK 文件中搜索关键词,返回行号和上下文 |
17
- | `gbk-engine` | 专用 agent,引导 AI 正确使用上述工具 |
20
+ | 本地/全局 plugin 规则 | 给所有 agents 注入“优先使用 `text_*`”的系统提示 |
18
21
 
19
22
  ---
20
23
 
@@ -42,6 +45,28 @@ npx opencode-gbk-tools uninstall
42
45
 
43
46
  ## 工具使用说明
44
47
 
48
+ ### text_read / text_write / text_edit
49
+
50
+ - 默认 `encoding=auto`
51
+ - 已有文件会尽量保持:
52
+ - 原编码
53
+ - BOM
54
+ - 换行风格
55
+ - 当前支持:
56
+ - `utf8`
57
+ - `utf8-bom`
58
+ - `utf16le`
59
+ - `utf16be`
60
+ - `gbk`
61
+ - `gb18030`
62
+
63
+ 优先使用建议:
64
+
65
+ ```text
66
+ 通用文本文件:text_read / text_write / text_edit
67
+ 明确 GBK 文件:gbk_read / gbk_write / gbk_edit / gbk_search
68
+ ```
69
+
45
70
  ### gbk_read — 读取文件
46
71
 
47
72
  ```
@@ -108,7 +133,8 @@ gbk_write(filePath="文件路径", content="\r\n新增内容", append=true) #
108
133
  ## 已知限制
109
134
 
110
135
  - 只支持文本文件,不支持二进制文件
111
- - 只支持 `gbk` 和 `gb18030` 编码
136
+ - 自动识别目前只覆盖 `utf8`、`utf8-bom`、`utf16le`、`utf16be`、`gbk`、`gb18030`
137
+ - 编码检测在歧义场景下可能返回 `TEXT_UNKNOWN_ENCODING`,此时应显式指定 `encoding`
112
138
  - 对无法映射的字符沿用 `iconv-lite` 默认替代行为
113
139
 
114
140
  ---
@@ -133,6 +159,7 @@ A:检查 `oldString` 是否包含了行号前缀(如 `"3787: "`),去掉
133
159
 
134
160
  | 版本 | 说明 |
135
161
  |------|------|
162
+ | 0.1.10 | 新增 `text_read` / `text_write` / `text_edit`,自动识别并保持原编码、BOM 与换行风格;同时通过 plugin 规则让所有 agents 默认优先使用 `text_*` |
136
163
  | 0.1.9 | `gbk_edit` 修复精确匹配路径写入 CRLF 文件时换行风格变 mixed 的 bug |
137
164
  | 0.1.8 | `gbk_write` 新增 `append=true` 追加模式 |
138
165
  | 0.1.7 | `gbk_edit` 自动剥离行号前缀后重试匹配 |
package/dist/cli/index.js CHANGED
@@ -60,7 +60,7 @@ function resolveTargetBase(target, cwd) {
60
60
  // src/cli/install.ts
61
61
  async function installCommand(args) {
62
62
  const targetBase = resolveTargetBase(args.target, args.cwd);
63
- const allowedArtifacts = new Set(args.artifacts ?? ["tool", "agent"]);
63
+ const allowedArtifacts = new Set(args.artifacts ?? ["tool", "agent", "plugin"]);
64
64
  const releaseManifest = JSON.parse(await fs3.readFile(path3.join(args.packageRoot, "dist", "release-manifest.json"), "utf8"));
65
65
  const selectedArtifacts = releaseManifest.artifacts.filter((artifact) => allowedArtifacts.has(artifact.kind));
66
66
  const existingManifest = await loadInstalledManifest(targetBase);
@@ -70,7 +70,7 @@ async function installCommand(args) {
70
70
  throw new Error(`\u68C0\u6D4B\u5230\u672A\u53D7\u7BA1\u6587\u4EF6\u51B2\u7A81: ${targetPath}`);
71
71
  }
72
72
  await ensureDir(path3.dirname(targetPath));
73
- const sourceRoot = artifact.kind === "tool" ? path3.join(args.packageRoot, "dist", "opencode-tools") : path3.join(args.packageRoot, "dist", "agents");
73
+ const sourceRoot = artifact.kind === "tool" ? path3.join(args.packageRoot, "dist", "opencode-tools") : artifact.kind === "agent" ? path3.join(args.packageRoot, "dist", "agents") : path3.join(args.packageRoot, "dist", "plugins");
74
74
  await fs3.copyFile(path3.join(sourceRoot, path3.basename(artifact.relativePath)), targetPath);
75
75
  }
76
76
  const installedManifest = {
@@ -181,7 +181,7 @@ async function setupCommand(args) {
181
181
  await fs6.mkdir(configBase, { recursive: true });
182
182
  const configPath = await resolveConfigFile(configBase);
183
183
  await ensurePluginConfigured(configPath, pluginName);
184
- const installResult = await installCommand({ ...args, force: true, artifacts: ["agent"] });
184
+ const installResult = await installCommand({ ...args, force: true, artifacts: ["agent", "plugin"] });
185
185
  return {
186
186
  configPath,
187
187
  targetBase: installResult.targetBase
@@ -16779,6 +16779,17 @@ and avoid false matches. Scoped edits also improve performance on very large fil
16779
16779
  },
16780
16780
  async execute(args, context) {
16781
16781
  const result = await replaceGbkFileText({ ...args, context });
16782
+ context.metadata({
16783
+ title: `GBK \u7F16\u8F91 ${result.encoding.toUpperCase()} x${result.replacements}`,
16784
+ metadata: {
16785
+ filePath: result.filePath,
16786
+ encoding: result.encoding,
16787
+ replacements: result.replacements,
16788
+ occurrencesBefore: result.occurrencesBefore,
16789
+ bytesRead: result.bytesRead,
16790
+ bytesWritten: result.bytesWritten
16791
+ }
16792
+ });
16782
16793
  return JSON.stringify(result, null, 2);
16783
16794
  }
16784
16795
  });
@@ -16632,6 +16632,19 @@ Workflow when truncated=true:
16632
16632
  },
16633
16633
  async execute(args, context) {
16634
16634
  const result = await readGbkFile({ ...args, context });
16635
+ const lineRange = `${result.startLine}-${result.endLine}`;
16636
+ context.metadata({
16637
+ title: `GBK \u8BFB\u53D6 ${result.encoding.toUpperCase()} ${lineRange}`,
16638
+ metadata: {
16639
+ filePath: result.filePath,
16640
+ encoding: result.encoding,
16641
+ lineRange,
16642
+ totalLines: result.totalLines,
16643
+ newlineStyle: result.newlineStyle,
16644
+ truncated: result.truncated,
16645
+ tail: result.tail
16646
+ }
16647
+ });
16635
16648
  return JSON.stringify(result, null, 2);
16636
16649
  }
16637
16650
  });
@@ -16454,6 +16454,15 @@ Workflow for large files:
16454
16454
  },
16455
16455
  async execute(args, context) {
16456
16456
  const result = await searchGbkFile({ ...args, context });
16457
+ context.metadata({
16458
+ title: `GBK \u641C\u7D22 ${result.encoding.toUpperCase()} ${result.matchCount} \u547D\u4E2D`,
16459
+ metadata: {
16460
+ filePath: result.filePath,
16461
+ encoding: result.encoding,
16462
+ totalLines: result.totalLines,
16463
+ matchCount: result.matchCount
16464
+ }
16465
+ });
16457
16466
  return JSON.stringify(result, null, 2);
16458
16467
  }
16459
16468
  });
@@ -16432,6 +16432,18 @@ var gbk_write_default = tool({
16432
16432
  },
16433
16433
  async execute(args, context) {
16434
16434
  const result = await writeGbkFile({ ...args, context });
16435
+ const action = result.appended ? "\u8FFD\u52A0" : result.overwritten ? "\u8986\u76D6" : "\u5199\u5165";
16436
+ context.metadata({
16437
+ title: `GBK ${action} ${result.encoding.toUpperCase()}`,
16438
+ metadata: {
16439
+ filePath: result.filePath,
16440
+ encoding: result.encoding,
16441
+ created: result.created,
16442
+ overwritten: result.overwritten,
16443
+ appended: result.appended ?? false,
16444
+ bytesWritten: result.bytesWritten
16445
+ }
16446
+ });
16435
16447
  return JSON.stringify(result, null, 2);
16436
16448
  }
16437
16449
  });