opencode-gbk-tools 0.1.0
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/LICENSE +21 -0
- package/README.md +78 -0
- package/bin/opencode-gbk.js +9 -0
- package/dist/agents/gbk-engine.md +25 -0
- package/dist/cli/index.js +162 -0
- package/dist/opencode-tools/gbk_edit.js +16448 -0
- package/dist/opencode-tools/gbk_read.js +16395 -0
- package/dist/opencode-tools/gbk_write.js +16386 -0
- package/dist/release-manifest.json +31 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# opencode-gbk-tools
|
|
2
|
+
|
|
3
|
+
为 OpenCode 提供一套面向 `GBK` / `GB18030` 文本工程的自定义工具与专用 agent。
|
|
4
|
+
|
|
5
|
+
## 功能
|
|
6
|
+
|
|
7
|
+
- `gbk_read`
|
|
8
|
+
- `gbk_write`
|
|
9
|
+
- `gbk_edit`
|
|
10
|
+
- `gbk-engine` agent
|
|
11
|
+
|
|
12
|
+
## 安装
|
|
13
|
+
|
|
14
|
+
一次性使用:
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npx opencode-gbk-tools install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
全局安装后使用:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm install -g opencode-gbk-tools
|
|
24
|
+
opencode-gbk install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
安装到当前项目:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
opencode-gbk install --project
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## 目录
|
|
34
|
+
|
|
35
|
+
- 全局安装目标:`~/.config/opencode`
|
|
36
|
+
- 项目安装目标:当前命令执行目录下的 `.opencode`
|
|
37
|
+
|
|
38
|
+
## CLI
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
opencode-gbk install
|
|
42
|
+
opencode-gbk install --project
|
|
43
|
+
opencode-gbk install --force
|
|
44
|
+
|
|
45
|
+
opencode-gbk uninstall
|
|
46
|
+
opencode-gbk uninstall --project
|
|
47
|
+
|
|
48
|
+
opencode-gbk doctor
|
|
49
|
+
opencode-gbk doctor --project
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Agent
|
|
53
|
+
|
|
54
|
+
安装后会生成 `gbk-engine`。
|
|
55
|
+
|
|
56
|
+
它的目标是:
|
|
57
|
+
|
|
58
|
+
- 文件内容读取必须走 `gbk_read`
|
|
59
|
+
- 文件内容写入必须走 `gbk_write`
|
|
60
|
+
- 文件内容修改必须走 `gbk_edit`
|
|
61
|
+
- 内置 `read`、`edit`、`grep` 被限制
|
|
62
|
+
|
|
63
|
+
## 已知限制
|
|
64
|
+
|
|
65
|
+
- 只支持文本文件,不支持二进制文件
|
|
66
|
+
- 首版不做自动编码识别
|
|
67
|
+
- 只支持 `gbk` 和 `gb18030`
|
|
68
|
+
- `edit: deny` 在 OpenCode 中会一起限制内置 `write`、`patch`、`multiedit`
|
|
69
|
+
- 对无法映射的字符沿用 `iconv-lite` 默认替代行为
|
|
70
|
+
|
|
71
|
+
## 发布
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm run check
|
|
75
|
+
npm test
|
|
76
|
+
npm run build
|
|
77
|
+
npm pack --dry-run
|
|
78
|
+
```
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 处理 GBK/GB18030 编码文件的专用代理,只使用 gbk_read、gbk_write、gbk_edit
|
|
3
|
+
mode: primary
|
|
4
|
+
permission:
|
|
5
|
+
read: deny
|
|
6
|
+
edit: deny
|
|
7
|
+
grep: deny
|
|
8
|
+
webfetch: ask
|
|
9
|
+
bash:
|
|
10
|
+
"*": deny
|
|
11
|
+
"git status*": allow
|
|
12
|
+
"npm view *": allow
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
你是一个专门处理 GBK/GB18030 文本文件的代理。
|
|
16
|
+
|
|
17
|
+
规则:
|
|
18
|
+
- 读取文件时优先使用 `gbk_read`
|
|
19
|
+
- 创建或覆盖文件时优先使用 `gbk_write`
|
|
20
|
+
- 修改已有文件时优先使用 `gbk_edit`
|
|
21
|
+
- 文件发现可使用 `glob`,但文件内容读取必须使用 `gbk_read`
|
|
22
|
+
- 禁止依赖内置 `read`、`grep`、`write`、`edit`、`patch`
|
|
23
|
+
- `edit: deny` 同时覆盖内置 `write`、`patch`、`multiedit`
|
|
24
|
+
- 如果用户请求涉及 UTF-8 文件或二进制文件,先明确说明不适用
|
|
25
|
+
- 若目标文件编码不确定,先提醒用户确认是 `gbk` 还是 `gb18030`
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
// src/cli/index.ts
|
|
2
|
+
import path6 from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
|
|
5
|
+
// src/cli/install.ts
|
|
6
|
+
import fs3 from "fs/promises";
|
|
7
|
+
import path3 from "path";
|
|
8
|
+
|
|
9
|
+
// src/cli/fs.ts
|
|
10
|
+
import crypto from "crypto";
|
|
11
|
+
import fs from "fs/promises";
|
|
12
|
+
async function ensureDir(directory) {
|
|
13
|
+
await fs.mkdir(directory, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
async function computeFileHash(filePath) {
|
|
16
|
+
const buffer = await fs.readFile(filePath);
|
|
17
|
+
return crypto.createHash("sha256").update(buffer).digest("hex");
|
|
18
|
+
}
|
|
19
|
+
async function pathExists(filePath) {
|
|
20
|
+
try {
|
|
21
|
+
await fs.access(filePath);
|
|
22
|
+
return true;
|
|
23
|
+
} catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/cli/manifest.ts
|
|
29
|
+
import fs2 from "fs/promises";
|
|
30
|
+
import path from "path";
|
|
31
|
+
function getInstalledManifestPath(targetBase) {
|
|
32
|
+
return path.join(targetBase, "opencode-gbk-tools.manifest.json");
|
|
33
|
+
}
|
|
34
|
+
async function loadInstalledManifest(targetBase) {
|
|
35
|
+
try {
|
|
36
|
+
const content = await fs2.readFile(getInstalledManifestPath(targetBase), "utf8");
|
|
37
|
+
return JSON.parse(content);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
async function writeInstalledManifest(targetBase, manifest) {
|
|
43
|
+
await fs2.mkdir(targetBase, { recursive: true });
|
|
44
|
+
await fs2.writeFile(getInstalledManifestPath(targetBase), JSON.stringify(manifest, null, 2), "utf8");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/cli/paths.ts
|
|
48
|
+
import os from "os";
|
|
49
|
+
import path2 from "path";
|
|
50
|
+
function getGlobalTargetBase() {
|
|
51
|
+
return path2.join(os.homedir(), ".config", "opencode");
|
|
52
|
+
}
|
|
53
|
+
function getProjectTargetBase(cwd) {
|
|
54
|
+
return path2.join(cwd, ".opencode");
|
|
55
|
+
}
|
|
56
|
+
function resolveTargetBase(target, cwd) {
|
|
57
|
+
return target === "global" ? getGlobalTargetBase() : getProjectTargetBase(cwd);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// src/cli/install.ts
|
|
61
|
+
async function installCommand(args) {
|
|
62
|
+
const targetBase = resolveTargetBase(args.target, args.cwd);
|
|
63
|
+
const releaseManifest = JSON.parse(await fs3.readFile(path3.join(args.packageRoot, "dist", "release-manifest.json"), "utf8"));
|
|
64
|
+
const existingManifest = await loadInstalledManifest(targetBase);
|
|
65
|
+
for (const artifact of releaseManifest.artifacts) {
|
|
66
|
+
const targetPath = path3.join(targetBase, artifact.relativePath);
|
|
67
|
+
if (await pathExists(targetPath) && !existingManifest && !args.force) {
|
|
68
|
+
throw new Error(`\u68C0\u6D4B\u5230\u672A\u53D7\u7BA1\u6587\u4EF6\u51B2\u7A81: ${targetPath}`);
|
|
69
|
+
}
|
|
70
|
+
await ensureDir(path3.dirname(targetPath));
|
|
71
|
+
const sourceRoot = artifact.kind === "tool" ? path3.join(args.packageRoot, "dist", "opencode-tools") : path3.join(args.packageRoot, "dist", "agents");
|
|
72
|
+
await fs3.copyFile(path3.join(sourceRoot, path3.basename(artifact.relativePath)), targetPath);
|
|
73
|
+
}
|
|
74
|
+
const installedManifest = {
|
|
75
|
+
manifestVersion: 1,
|
|
76
|
+
packageName: releaseManifest.packageName,
|
|
77
|
+
packageVersion: releaseManifest.packageVersion,
|
|
78
|
+
targetType: args.target,
|
|
79
|
+
targetBase,
|
|
80
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81
|
+
files: releaseManifest.artifacts.map((artifact) => ({
|
|
82
|
+
...artifact,
|
|
83
|
+
installedByVersion: releaseManifest.packageVersion
|
|
84
|
+
}))
|
|
85
|
+
};
|
|
86
|
+
await writeInstalledManifest(targetBase, installedManifest);
|
|
87
|
+
return { targetBase };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/cli/uninstall.ts
|
|
91
|
+
import fs4 from "fs/promises";
|
|
92
|
+
import path4 from "path";
|
|
93
|
+
async function uninstallCommand(args) {
|
|
94
|
+
const targetBase = resolveTargetBase(args.target, args.cwd);
|
|
95
|
+
const manifest = await loadInstalledManifest(targetBase);
|
|
96
|
+
if (!manifest) {
|
|
97
|
+
return { targetBase, removed: 0 };
|
|
98
|
+
}
|
|
99
|
+
let removed = 0;
|
|
100
|
+
for (const file of manifest.files) {
|
|
101
|
+
const targetPath = path4.join(targetBase, file.relativePath);
|
|
102
|
+
await fs4.rm(targetPath, { force: true });
|
|
103
|
+
removed += 1;
|
|
104
|
+
}
|
|
105
|
+
await fs4.rm(path4.join(targetBase, "opencode-gbk-tools.manifest.json"), { force: true });
|
|
106
|
+
return { targetBase, removed };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// src/cli/doctor.ts
|
|
110
|
+
import fs5 from "fs/promises";
|
|
111
|
+
import path5 from "path";
|
|
112
|
+
async function doctorCommand(args) {
|
|
113
|
+
const targetBase = resolveTargetBase(args.target, args.cwd);
|
|
114
|
+
const manifest = await loadInstalledManifest(targetBase);
|
|
115
|
+
if (!manifest) {
|
|
116
|
+
return { ok: false, issues: ["\u672A\u627E\u5230 installed manifest"] };
|
|
117
|
+
}
|
|
118
|
+
const issues = [];
|
|
119
|
+
for (const file of manifest.files) {
|
|
120
|
+
const targetPath = path5.join(targetBase, file.relativePath);
|
|
121
|
+
try {
|
|
122
|
+
const actual = await computeFileHash(targetPath);
|
|
123
|
+
if (actual !== file.expectedHash) {
|
|
124
|
+
issues.push(`\u6587\u4EF6\u5185\u5BB9\u4E0E manifest \u4E0D\u4E00\u81F4: ${file.relativePath}`);
|
|
125
|
+
}
|
|
126
|
+
} catch {
|
|
127
|
+
issues.push(`\u6587\u4EF6\u7F3A\u5931: ${file.relativePath}`);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
await fs5.access(path5.join(args.packageRoot, "dist", "release-manifest.json"));
|
|
131
|
+
return { ok: issues.length === 0, issues };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/cli/index.ts
|
|
135
|
+
function resolvePackageRoot(env = process.env, moduleUrl = import.meta.url) {
|
|
136
|
+
if (env.OPENCODE_GBK_PACKAGE_ROOT) {
|
|
137
|
+
return env.OPENCODE_GBK_PACKAGE_ROOT;
|
|
138
|
+
}
|
|
139
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
140
|
+
return path6.resolve(path6.dirname(modulePath), "..", "..");
|
|
141
|
+
}
|
|
142
|
+
async function main(argv, env = process.env) {
|
|
143
|
+
const [command, ...rest] = argv;
|
|
144
|
+
const target = rest.includes("--project") ? "project" : "global";
|
|
145
|
+
const force = rest.includes("--force");
|
|
146
|
+
const packageRoot = resolvePackageRoot(env);
|
|
147
|
+
const cwd = process.cwd();
|
|
148
|
+
if (command === "install") {
|
|
149
|
+
return await installCommand({ packageRoot, target, cwd, force });
|
|
150
|
+
}
|
|
151
|
+
if (command === "uninstall") {
|
|
152
|
+
return await uninstallCommand({ target, cwd });
|
|
153
|
+
}
|
|
154
|
+
if (command === "doctor") {
|
|
155
|
+
return await doctorCommand({ packageRoot, target, cwd });
|
|
156
|
+
}
|
|
157
|
+
throw new Error(`\u672A\u77E5\u547D\u4EE4: ${command ?? ""}`);
|
|
158
|
+
}
|
|
159
|
+
export {
|
|
160
|
+
main,
|
|
161
|
+
resolvePackageRoot
|
|
162
|
+
};
|